From 220b2afb3c4596b875624a5024fff134234cb102 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 1 Jun 2013 23:16:06 -0400 Subject: [PATCH 001/703] Client side first shot --- .gitignore | 3 + Makefile.am | 59 ++++----- cmd-break-pane.c | 5 + cmd-join-pane.c | 5 + configure.ac | 3 + resize.c | 12 ++ server-client.c | 4 + server.c | 4 + session.c | 6 + tmate-decoder.c | 118 ++++++++++++++++++ tmate-encoder.c | 89 ++++++++++++++ tmate-ssh-client.c | 301 +++++++++++++++++++++++++++++++++++++++++++++ tmate.1 | 1 + tmate.c | 18 +++ tmate.h | 94 ++++++++++++++ tmux.c | 7 +- tmux.h | 4 + window.c | 17 +++ 18 files changed, 713 insertions(+), 37 deletions(-) create mode 100644 tmate-decoder.c create mode 100644 tmate-encoder.c create mode 100644 tmate-ssh-client.c create mode 120000 tmate.1 create mode 100644 tmate.c create mode 100644 tmate.h diff --git a/.gitignore b/.gitignore index c3906ada..7721edac 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ tmux Makefile Makefile.in configure +tmate +cscope.* +ctags diff --git a/Makefile.am b/Makefile.am index 2ce54b1a..b17ca578 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,8 +1,8 @@ # $Id$ # Obvious program stuff. -bin_PROGRAMS = tmux -dist_man1_MANS = tmux.1 +bin_PROGRAMS = tmate +dist_man1_MANS = tmate.1 # Distribution tarball options. EXTRA_DIST = \ @@ -21,6 +21,8 @@ if IS_GLIBC CFLAGS += -D_GNU_SOURCE endif +CFLAGS += -Wno-unused-parameter -Wno-unused-variable + # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. if IS_GCC @@ -57,7 +59,7 @@ CFLAGS += -erroff=E_EMPTY_DECLARATION endif # List of sources. -dist_tmux_SOURCES = \ +dist_tmate_SOURCES = \ arguments.c \ attributes.c \ cfg.c \ @@ -171,6 +173,10 @@ dist_tmux_SOURCES = \ session.c \ signal.c \ status.c \ + tmate-ssh-client.c \ + tmate-encoder.c \ + tmate-decoder.c \ + tmate.c \ tmux.c \ tty-acs.c \ tty-keys.c \ @@ -183,66 +189,51 @@ dist_tmux_SOURCES = \ window.c \ xmalloc.c \ xterm-keys.c -nodist_tmux_SOURCES = osdep-@PLATFORM@.c +nodist_tmate_SOURCES = osdep-@PLATFORM@.c # Pile in all the compat/ stuff that is needed. if NO_FORKPTY -nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c +nodist_tmate_SOURCES += compat/forkpty-@PLATFORM@.c endif if NO_IMSG -nodist_tmux_SOURCES += compat/imsg.c compat/imsg-buffer.c +nodist_tmate_SOURCES += compat/imsg.c compat/imsg-buffer.c endif if NO_CLOSEFROM -nodist_tmux_SOURCES += compat/closefrom.c +nodist_tmate_SOURCES += compat/closefrom.c endif if NO_DAEMON -nodist_tmux_SOURCES += compat/daemon.c +nodist_tmate_SOURCES += compat/daemon.c endif if NO_SETENV -nodist_tmux_SOURCES += compat/setenv.c +nodist_tmate_SOURCES += compat/setenv.c endif if NO_STRLCAT -nodist_tmux_SOURCES += compat/strlcat.c +nodist_tmate_SOURCES += compat/strlcat.c endif if NO_STRLCPY -nodist_tmux_SOURCES += compat/strlcpy.c +nodist_tmate_SOURCES += compat/strlcpy.c endif if NO_ASPRINTF -nodist_tmux_SOURCES += compat/asprintf.c +nodist_tmate_SOURCES += compat/asprintf.c endif if NO_FGETLN -nodist_tmux_SOURCES += compat/fgetln.c +nodist_tmate_SOURCES += compat/fgetln.c endif if NO_GETOPT -nodist_tmux_SOURCES += compat/getopt.c +nodist_tmate_SOURCES += compat/getopt.c endif if NO_STRCASESTR -nodist_tmux_SOURCES += compat/strcasestr.c +nodist_tmate_SOURCES += compat/strcasestr.c endif if NO_STRSEP -nodist_tmux_SOURCES += compat/strsep.c +nodist_tmate_SOURCES += compat/strsep.c endif if NO_VIS -nodist_tmux_SOURCES += compat/vis.c compat/unvis.c +nodist_tmate_SOURCES += compat/vis.c compat/unvis.c endif if NO_STRTONUM -nodist_tmux_SOURCES += compat/strtonum.c +nodist_tmate_SOURCES += compat/strtonum.c endif if NO_B64_NTOP -nodist_tmux_SOURCES += compat/b64_ntop.c +nodist_tmate_SOURCES += compat/b64_ntop.c endif - -# Update SF web site. -upload-index.html: update-index.html - scp www/index.html www/main.css www/images/*.png \ - ${USER},tmux@web.sf.net:/home/groups/t/tm/tmux/htdocs - rm -f www/index.html www/images/small-* - -update-index.html: - (cd www/images && \ - rm -f small-* && \ - for i in *.png; do \ - convert "$$i" -resize 200x150 "small-$$i"; \ - done \ - ) - sed "s/%%VERSION%%/${VERSION}/g" www/index.html.in >www/index.html diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 8ed9a1a6..a0e85513 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -41,6 +41,10 @@ const struct cmd_entry cmd_break_pane_entry = { enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE + cmdq_error(cmdq, "break pane is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct winlink *wl; struct session *s; @@ -112,4 +116,5 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 2cf587e0..971bc0b9 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -76,6 +76,10 @@ cmd_join_pane_exec(struct cmd *self, struct cmd_q *cmdq) enum cmd_retval join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) { +#ifdef TMATE + cmdq_error(cmdq, "join pane is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct session *dst_s; struct winlink *src_wl, *dst_wl; @@ -170,4 +174,5 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) notify_window_layout_changed(dst_w); return (CMD_RETURN_NORMAL); +#endif } diff --git a/configure.ac b/configure.ac index c0ef34c6..bfffefa1 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,9 @@ AC_MSG_RESULT($found_glibc) # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) +AC_SEARCH_LIBS(ssh_new, ssh) +AC_SEARCH_LIBS(msgpack_object_print, msgpack) + # Look for libevent. PKG_CHECK_MODULES( LIBEVENT, diff --git a/resize.c b/resize.c index 5c365dfe..7f2ed236 100644 --- a/resize.c +++ b/resize.c @@ -21,6 +21,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Recalculate window and session sizes. @@ -71,6 +72,17 @@ recalculate_sizes(void) ssy = c->tty.sy; } } + +#ifdef TMATE + /* We assume a single session */ + if (tmate_sx > 0 && tmate_sy > 0) { + if ((u_int)tmate_sx < ssx) + ssx = tmate_sx; + if ((u_int)tmate_sy < ssy) + ssy = tmate_sy; + } +#endif + if (ssx == UINT_MAX || ssy == UINT_MAX) { s->flags |= SESSION_UNATTACHED; continue; diff --git a/server-client.c b/server-client.c index 77e6de78..241e1bed 100644 --- a/server-client.c +++ b/server-client.c @@ -27,6 +27,7 @@ #include #include "tmux.h" +#include "tmate.h" void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); @@ -511,6 +512,9 @@ server_client_loop(void) if (w == NULL) continue; + if (w->flags & WINDOW_REDRAW) + tmate_sync_window(w); + w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { server_client_check_focus(wp); diff --git a/server.c b/server.c index 4bfa9185..352255f7 100644 --- a/server.c +++ b/server.c @@ -36,6 +36,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Main server functions. @@ -195,6 +196,9 @@ server_start(int lockfd, char *lockfile) evtimer_add(&server_ev_second, &tv); set_signals(server_signal_callback); + + tmate_client_start(); + server_loop(); exit(0); } diff --git a/session.c b/session.c index 74eb06a5..d3d82ea2 100644 --- a/session.c +++ b/session.c @@ -25,6 +25,7 @@ #include #include "tmux.h" +#include "tmate.h" /* Global session list. */ struct sessions sessions; @@ -90,6 +91,11 @@ session_create(const char *name, const char *cmd, const char *cwd, { struct session *s; + if (next_session_id != 0) { + xasprintf(cause, "multi sessions is not supported with tmate"); + return NULL; + } + s = xmalloc(sizeof *s); s->references = 0; s->flags = 0; diff --git a/tmate-decoder.c b/tmate-decoder.c new file mode 100644 index 00000000..476077af --- /dev/null +++ b/tmate-decoder.c @@ -0,0 +1,118 @@ +#include "tmate.h" + +int tmate_sx = -1; +int tmate_sy = -1; + +struct tmate_unpacker { + msgpack_object *argv; + int argc; +}; + +static void decoder_error(void) +{ + tmate_fatal("Received a bad message"); +} + +static void init_unpacker(struct tmate_unpacker *uk, + msgpack_object obj) +{ + if (obj.type != MSGPACK_OBJECT_ARRAY) + decoder_error(); + + uk->argv = obj.via.array.ptr; + uk->argc = obj.via.array.size; +} + +static int64_t unpack_int(struct tmate_unpacker *uk) +{ + int64_t val; + + if (uk->argc == 0) + decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER && + uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER) + decoder_error(); + + val = uk->argv[0].via.i64; + + uk->argv++; + uk->argc--; + + return val; +} + +static void tmate_client_key(struct tmate_unpacker *uk) +{ + struct client *c; + int key = unpack_int(uk); + + /* Very gross. other clients cannot even detach */ + + if (ARRAY_LENGTH(&clients) > 0) { + c = ARRAY_ITEM(&clients, 0); + server_client_handle_key(c, key); + } +} + +static void tmate_client_resize(struct tmate_unpacker *uk) +{ + /* A bit gross as well */ + tmate_sx = unpack_int(uk); + tmate_sy = unpack_int(uk); + recalculate_sizes(); + + /* TODO Handle reconnection cases */ +} + +static void handle_message(msgpack_object obj) +{ + struct tmate_unpacker _uk; + struct tmate_unpacker *uk = &_uk; + int cmd; + + init_unpacker(uk, obj); + + switch (unpack_int(uk)) { + case TMATE_CLIENT_KEY: tmate_client_key(uk); break; + case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; + default: decoder_error(); + } +} + +void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) +{ + msgpack_unpacked result; + + msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); + + msgpack_unpacked_init(&result); + while (msgpack_unpacker_next(&decoder->unpacker, &result)) { + handle_message(result.data); + } + msgpack_unpacked_destroy(&result); + + if (msgpack_unpacker_message_size(&decoder->unpacker) > + TMATE_MAX_MESSAGE_SIZE) { + tmate_fatal("Message too big"); + } +} + +void tmate_decoder_get_buffer(struct tmate_decoder *decoder, + char **buf, size_t *len) +{ + /* rewind the buffer if possible */ + if (msgpack_unpacker_buffer_capacity(&decoder->unpacker) < + TMATE_MAX_MESSAGE_SIZE) { + msgpack_unpacker_expand_buffer(&decoder->unpacker, 0); + } + + *buf = msgpack_unpacker_buffer(&decoder->unpacker); + *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); +} + +void tmate_decoder_init(struct tmate_decoder *decoder) +{ + if (!msgpack_unpacker_init(&decoder->unpacker, 2*TMATE_MAX_MESSAGE_SIZE)) + tmate_fatal("cannot initialize the unpacker"); +} diff --git a/tmate-encoder.c b/tmate-encoder.c new file mode 100644 index 00000000..a4449e5c --- /dev/null +++ b/tmate-encoder.c @@ -0,0 +1,89 @@ +#include + +#include "tmate.h" + +static int msgpack_write(void *data, const char *buf, unsigned int len) +{ + struct tmate_encoder *encoder = data; + + evbuffer_add(encoder->buffer, buf, len); + + if ((encoder->ev_readable.ev_flags & EVLIST_INSERTED) && + !(encoder->ev_readable.ev_flags & EVLIST_ACTIVE)) { + event_active(&encoder->ev_readable, EV_READ, 0); + } + + return 0; +} + +void tmate_encoder_init(struct tmate_encoder *encoder) +{ + msgpack_packer_init(&encoder->pk, encoder, &msgpack_write); + encoder->buffer = evbuffer_new(); +} + +#define msgpack_pack_string(pk, str) do { \ + int __strlen = strlen(str); \ + msgpack_pack_raw(pk, __strlen); \ + msgpack_pack_raw_body(pk, str, __strlen); \ +} while(0) + +#define pack(what, ...) msgpack_pack_##what(&tmate_encoder->pk, __VA_ARGS__) + +void tmate_write_header(void) +{ + pack(array, 2); + pack(int, TMATE_HEADER); + pack(int, TMATE_PROTOCOL_VERSION); +} + +void tmate_sync_window(struct window *w) +{ + struct window_pane *wp; + int num_panes = 0; + int active_pane_id = -1; + + pack(array, 7); + pack(int, TMATE_SYNC_WINDOW); + + pack(int, w->id); + pack(string, w->name); + pack(int, w->sx); + pack(int, w->sy); + + TAILQ_FOREACH(wp, &w->panes, entry) + num_panes++; + + pack(array, num_panes); + TAILQ_FOREACH(wp, &w->panes, entry) { + pack(array, 5); + pack(int, wp->id); + pack(int, wp->sx); + pack(int, wp->sy); + pack(int, wp->xoff); + pack(int, wp->yoff); + + if (wp == w->active) + active_pane_id = wp->id; + } + pack(int, active_pane_id); +} + +void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) +{ + size_t max_write, to_write; + + max_write = TMATE_MAX_MESSAGE_SIZE - 4; + while (len > 0) { + to_write = len < max_write ? len : max_write; + + pack(array, 3); + pack(int, TMATE_PTY_DATA); + pack(int, wp->id); + pack(raw, to_write); + pack(raw_body, buf, to_write); + + buf += to_write; + len -= to_write; + } +} diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c new file mode 100644 index 00000000..581ca772 --- /dev/null +++ b/tmate-ssh-client.c @@ -0,0 +1,301 @@ +#include +#include +#include +#include +#include +#include + +#include "tmate.h" + +static void consume_channel(struct tmate_ssh_client *client); +static void flush_input_stream(struct tmate_ssh_client *client); +static void __flush_input_stream(evutil_socket_t fd, short what, void *arg); +static void __on_session_event(evutil_socket_t fd, short what, void *arg); +static void disconnect_session(struct tmate_ssh_client *client); +static void reconnect_session(struct tmate_ssh_client *client); + +static void log_function(ssh_session session, int priority, + const char *message, void *userdata) +{ + tmate_debug("[%d] %s", priority, message); +} + +static struct ssh_callbacks_struct ssh_session_callbacks = { + .log_function = log_function +}; + + +static void register_session_fd_event(struct tmate_ssh_client *client) +{ + if (!event_initialized(&client->ev_ssh)) { + int flag = 1; + setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, + TCP_NODELAY, &flag, sizeof(flag)); + + event_assign(&client->ev_ssh, ev_base, ssh_get_fd(client->session), + EV_READ | EV_PERSIST, __on_session_event, client); + event_add(&client->ev_ssh, NULL); + } +} + +static void register_input_stream_event(struct tmate_ssh_client *client) +{ + if (!event_initialized(&client->encoder->ev_readable)) { + event_assign(&client->encoder->ev_readable, ev_base, -1, + EV_READ | EV_PERSIST, __flush_input_stream, client); + event_add(&client->encoder->ev_readable, NULL); + } +} + +static int __ssh_userauth_autopubkey(ssh_session session, const char *passphrase) +{ + int ret; + + /* For some reason, auth doesn't work in blocking mode :( */ + ssh_set_blocking(session, 1); + ret = ssh_userauth_autopubkey(session, passphrase); + ssh_set_blocking(session, 0); + + return ret; +} + +static void consume_channel(struct tmate_ssh_client *client) +{ + char *buf; + ssize_t len; + + for (;;) { + tmate_decoder_get_buffer(client->decoder, &buf, &len); + len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); + if (len < 0) { + tmate_debug("Error reading from channel: %s", + ssh_get_error(client->session)); + reconnect_session(client); + break; + } + + if (len == 0) + break; + + tmate_decoder_commit(client->decoder, len); + } +} + +static void on_session_event(struct tmate_ssh_client *client) +{ + int verbosity = SSH_LOG_RARE; + int port = 2200; + + ssh_session session = client->session; + ssh_channel channel = client->channel; + + switch (client->state) { + case SSH_INIT: + client->session = session = ssh_new(); + if (!session) { + tmate_fatal("cannot initialize"); + return; + } + + ssh_set_callbacks(session, &ssh_session_callbacks); + + client->channel = channel = ssh_channel_new(session); + if (!channel) { + tmate_fatal("cannot initialize"); + return; + } + + ssh_set_blocking(session, 0); + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + ssh_options_set(session, SSH_OPTIONS_PORT, &port); + ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); + + tmate_debug("Connecting..."); + client->state = SSH_CONNECT; + /* fall through */ + + case SSH_CONNECT: + switch (ssh_connect(session)) { + case SSH_AGAIN: + register_session_fd_event(client); + return; + case SSH_ERROR: + tmate_debug("Error connecting: %s", ssh_get_error(session)); + reconnect_session(client); + return; + case SSH_OK: + register_session_fd_event(client); + tmate_debug("Connected"); + client->state = SSH_AUTH; + /* fall through */ + } + + /* TODO Authenticate server */ + + case SSH_AUTH: + switch (__ssh_userauth_autopubkey(session, NULL)) { + case SSH_AUTH_AGAIN: + return; + case SSH_AUTH_PARTIAL: + case SSH_AUTH_INFO: + case SSH_AUTH_DENIED: + tmate_debug("Access denied. Try again later."); + disconnect_session(client); + return; + case SSH_AUTH_ERROR: + tmate_debug("Auth error: %s", ssh_get_error(session)); + reconnect_session(client); + return; + case SSH_AUTH_SUCCESS: + tmate_debug("Auth successful"); + client->state = SSH_OPEN_CHANNEL; + /* fall through */ + } + + case SSH_OPEN_CHANNEL: + switch (ssh_channel_open_session(channel)) { + case SSH_AGAIN: + return; + case SSH_ERROR: + tmate_debug("Error opening session: %s", ssh_get_error(session)); + reconnect_session(client); + return; + case SSH_OK: + tmate_debug("Session opened, initalizing tmate"); + client->state = SSH_BOOTSTRAP; + /* fall through */ + } + + case SSH_BOOTSTRAP: + switch (ssh_channel_request_subsystem(channel, "tmate")) { + case SSH_AGAIN: + return; + case SSH_ERROR: + tmate_debug("Error initializing tmate: %s", ssh_get_error(session)); + reconnect_session(client); + return; + case SSH_OK: + tmate_debug("Ready"); + + /* Writes are now performed in a blocking fashion */ + ssh_set_blocking(session, 1); + + client->state = SSH_READY; + register_input_stream_event(client); + flush_input_stream(client); + /* fall through */ + } + + case SSH_READY: + consume_channel(client); + if (!ssh_is_connected(session)) { + tmate_debug("Disconnected"); + reconnect_session(client); + return; + } + } +} + +static void flush_input_stream(struct tmate_ssh_client *client) +{ + struct evbuffer *evb = client->encoder->buffer; + ssize_t len, written; + char *buf; + + if (client->state < SSH_READY) + return; + + for (;;) { + len = evbuffer_get_length(evb); + if (!len) + break; + + buf = evbuffer_pullup(evb, -1); + + written = ssh_channel_write(client->channel, buf, len); + if (written < 0) { + tmate_debug("Error writing to channel: %s", + ssh_get_error(client->session)); + reconnect_session(client); + return; + } + + evbuffer_drain(evb, written); + } +} + +static void __flush_input_stream(evutil_socket_t fd, short what, void *arg) +{ + flush_input_stream(arg); +} + +static void __on_session_event(evutil_socket_t fd, short what, void *arg) +{ + on_session_event(arg); +} + +static void disconnect_session(struct tmate_ssh_client *client) +{ + if (event_initialized(&client->ev_ssh)) { + event_del(&client->ev_ssh); + client->ev_ssh.ev_flags = 0; + } + + if (event_initialized(&client->encoder->ev_readable)) { + event_del(&client->encoder->ev_readable); + client->encoder->ev_readable.ev_flags = 0; + } + + if (client->session) { + /* ssh_free() also frees the associated channels. */ + ssh_free(client->session); + client->session = NULL; + client->channel = NULL; + } + + client->state = SSH_NONE; +} + +static void connect_session(struct tmate_ssh_client *client) +{ + if (!client->session) { + client->state = SSH_INIT; + on_session_event(client); + } +} + +static void on_reconnect_timer(evutil_socket_t fd, short what, void *arg) +{ + connect_session(arg); +} + +static void reconnect_session(struct tmate_ssh_client *client) +{ + struct timeval tv; + + disconnect_session(client); + + tv.tv_sec = 1; + tv.tv_usec = 0; + evtimer_add(&client->ev_ssh_reconnect, &tv); +} + +void tmate_ssh_client_init(struct tmate_ssh_client *client, + struct tmate_encoder *encoder, + struct tmate_decoder *decoder) +{ + ssh_callbacks_init(&ssh_session_callbacks); + + client->state = SSH_NONE; + client->session = NULL; + client->channel = NULL; + + client->encoder = encoder; + client->decoder = decoder; + + evtimer_assign(&client->ev_ssh_reconnect, ev_base, + on_reconnect_timer, client); + + connect_session(client); +} diff --git a/tmate.1 b/tmate.1 new file mode 120000 index 00000000..44d7405b --- /dev/null +++ b/tmate.1 @@ -0,0 +1 @@ +tmux.1 \ No newline at end of file diff --git a/tmate.c b/tmate.c new file mode 100644 index 00000000..c670d39c --- /dev/null +++ b/tmate.c @@ -0,0 +1,18 @@ +#include "tmate.h" + +struct tmate_encoder *tmate_encoder; + +static struct tmate_ssh_client client; +static struct tmate_encoder encoder; +static struct tmate_decoder decoder; + +void tmate_client_start(void) +{ + tmate_encoder_init(&encoder); + tmate_decoder_init(&decoder); + tmate_encoder = &encoder; + + tmate_ssh_client_init(&client, &encoder, &decoder); + + tmate_write_header(); +} diff --git a/tmate.h b/tmate.h new file mode 100644 index 00000000..78d0e422 --- /dev/null +++ b/tmate.h @@ -0,0 +1,94 @@ +#ifndef TMATE_H +#define TMATE_H + +#include +#include +#include + +#include "tmux.h" + +#define tmate_debug(...) log_debug("[tmate] " __VA_ARGS__) +#define tmate_warn(...) log_warn("[tmate] " __VA_ARGS__) +#define tmate_info(...) log_info("[tmate] " __VA_ARGS__) +#define tmate_fatal(...) log_fatal("[tmate] " __VA_ARGS__) + +/* tmate-encoder.c */ + +#define TMATE_MAX_MESSAGE_SIZE (16*1024) + +#define TMATE_PROTOCOL_VERSION 1 + +enum tmate_commands { + TMATE_HEADER, + TMATE_SYNC_WINDOW, + TMATE_PTY_DATA, +}; + +struct tmate_encoder { + msgpack_packer pk; + struct evbuffer *buffer; + struct event ev_readable; +}; + +extern void tmate_encoder_init(struct tmate_encoder *encoder); + +extern void tmate_write_header(void); +extern void tmate_sync_window(struct window *w); +extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); + +/* tmate-decoder.c */ + +enum tmate_notifications { + TMATE_CLIENT_KEY, + TMATE_CLIENT_RESIZE, +}; + +struct tmate_decoder { + struct msgpack_unpacker unpacker; +}; + +extern int tmate_sx; +extern int tmate_sy; + +extern void tmate_decoder_init(struct tmate_decoder *decoder); +extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder, + char **buf, size_t *len); +extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); + +/* tmate-ssh-client.c */ + +typedef struct ssh_session_struct* ssh_session; +typedef struct ssh_channel_struct* ssh_channel; + +enum tmate_ssh_client_state_types { + SSH_NONE, + SSH_INIT, + SSH_CONNECT, + SSH_AUTH, + SSH_OPEN_CHANNEL, + SSH_BOOTSTRAP, + SSH_READY, +}; + +struct tmate_ssh_client { + int state; + ssh_session session; + ssh_channel channel; + + struct tmate_encoder *encoder; + struct tmate_decoder *decoder; + + struct event ev_ssh; + struct event ev_ssh_reconnect; +}; + +extern void tmate_ssh_client_init(struct tmate_ssh_client *client, + struct tmate_encoder *encoder, + struct tmate_decoder *decoder); + +/* tmate.c */ + +extern struct tmate_encoder *tmate_encoder; +extern void tmate_client_start(void); + +#endif diff --git a/tmux.c b/tmux.c index 8ea91ebe..65c94389 100644 --- a/tmux.c +++ b/tmux.c @@ -168,9 +168,9 @@ makesocketpath(const char *label) uid = getuid(); if ((s = getenv("TMPDIR")) == NULL || *s == '\0') - xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); + xsnprintf(base, sizeof base, "%s/tmate-%u", _PATH_TMP, uid); else - xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); + xsnprintf(base, sizeof base, "%s/tmate-%u", s, uid); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) return (NULL); @@ -244,7 +244,8 @@ main(int argc, char **argv) malloc_options = (char *) "AFGJPX"; #endif - quiet = flags = 0; + flags = IDENTIFY_256COLOURS | IDENTIFY_UTF8; + quiet = 0; label = path = NULL; login_shell = (**argv == '-'); while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUvV")) != -1) { diff --git a/tmux.h b/tmux.h index 9c91d6a4..c8afa93e 100644 --- a/tmux.h +++ b/tmux.h @@ -19,6 +19,8 @@ #ifndef TMUX_H #define TMUX_H +#define TMATE + #define PROTOCOL_VERSION 7 #include @@ -959,6 +961,8 @@ struct window_pane { struct bufferevent *pipe_event; size_t pipe_off; + size_t tmate_off; + struct screen *screen; struct screen base; diff --git a/window.c b/window.c index 7678adc6..f4bccadb 100644 --- a/window.c +++ b/window.c @@ -30,6 +30,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Each window is attached to a number of panes, each of which is a pty. This @@ -328,6 +329,8 @@ window_create(const char *name, const char *cmd, const char *shell, } else w->name = default_window_name(w); + tmate_sync_window(w); + return (w); } @@ -374,6 +377,7 @@ window_set_name(struct window *w, const char *new_name) free(w->name); w->name = xstrdup(new_name); notify_window_renamed(w); + tmate_sync_window(w); } void @@ -691,6 +695,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->pipe_off = 0; wp->pipe_event = NULL; + wp->tmate_off = 0; + wp->saved_grid = NULL; screen_init(&wp->base, sx, sy, hlimit); @@ -872,13 +878,24 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { + /* FIXME tmux: + * - new_data = EVBUFFER_DATA(wp->event->input); + * + new_data = EVBUFFER_DATA(wp->event->input) + wp->pipe_off; + * also, can the buffer be too small? + */ new_data = EVBUFFER_DATA(wp->event->input); bufferevent_write(wp->pipe_event, new_data, new_size); } + new_size = EVBUFFER_LENGTH(wp->event->input) - wp->tmate_off; + new_data = EVBUFFER_DATA(wp->event->input) + wp->tmate_off; + if (new_size > 0) + tmate_pty_data(wp, new_data, new_size); + input_parse(wp); wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + wp->tmate_off = EVBUFFER_LENGTH(wp->event->input); /* * If we get here, we're not outputting anymore, so set the silence From 0f7ccda4fb34685d5bbd62eba3ff8d5f63bdc71b Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 10 Jun 2013 00:58:12 -0400 Subject: [PATCH 002/703] Adding libssh --- Makefile.am | 7 + configure.ac | 8 +- libssh/.gitignore | 8 + libssh/AUTHORS | 15 + libssh/BSD | 24 + libssh/CMakeLists.txt | 136 + libssh/COPYING | 460 +++ libssh/CPackConfig.cmake | 53 + libssh/CTestConfig.cmake | 9 + libssh/ChangeLog | 298 ++ libssh/CodingStyle | 59 + libssh/ConfigureChecks.cmake | 179 + libssh/DefineOptions.cmake | 27 + libssh/INSTALL | 99 + libssh/README | 163 + libssh/SubmittingPatches | 118 + libssh/build/build_make.sh | 197 + libssh/cmake/Modules/AddCMockaTest.cmake | 23 + libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS | 22 + .../cmake/Modules/CheckCCompilerFlagSSP.cmake | 26 + .../cmake/Modules/DefineCMakeDefaults.cmake | 27 + .../cmake/Modules/DefineCompilerFlags.cmake | 77 + .../Modules/DefineInstallationPaths.cmake | 109 + .../Modules/DefinePlatformDefaults.cmake | 28 + libssh/cmake/Modules/FindArgp.cmake | 60 + libssh/cmake/Modules/FindCMocka.cmake | 49 + libssh/cmake/Modules/FindGCrypt.cmake | 75 + libssh/cmake/Modules/FindNSIS.cmake | 39 + libssh/cmake/Modules/FindOpenSSL.cmake | 208 + libssh/cmake/Modules/FindZLIB.cmake | 120 + .../cmake/Modules/MacroAddCompileFlags.cmake | 21 + libssh/cmake/Modules/MacroAddLinkFlags.cmake | 20 + libssh/cmake/Modules/MacroAddPlugin.cmake | 30 + libssh/cmake/Modules/MacroCopyFile.cmake | 33 + .../Modules/MacroEnsureOutOfSourceBuild.cmake | 17 + libssh/cmake/Modules/UseDoxygen.cmake | 100 + libssh/config.h.cmake | 148 + libssh/doc/CMakeLists.txt | 5 + libssh/doc/TracFooter.html | 1 + libssh/doc/TracHeader.html | 4 + libssh/doc/authentication.dox | 375 ++ libssh/doc/command.dox | 94 + libssh/doc/doxy.config.in | 1835 +++++++++ libssh/doc/doxy.trac.in | 1546 ++++++++ libssh/doc/forwarding.dox | 229 ++ libssh/doc/guided_tour.dox | 455 +++ libssh/doc/introduction.dox | 49 + libssh/doc/linking.dox | 24 + libssh/doc/mainpage.dox | 211 + libssh/doc/scp.dox | 268 ++ libssh/doc/sftp.dox | 415 ++ libssh/doc/shell.dox | 361 ++ libssh/doc/tbd.dox | 14 + libssh/doc/threading.dox | 65 + libssh/examples/CMakeLists.txt | 57 + libssh/examples/authentication.c | 167 + libssh/examples/connect_ssh.c | 67 + libssh/examples/examples_common.h | 22 + libssh/examples/exec.c | 66 + libssh/examples/knownhosts.c | 98 + libssh/examples/libssh_scp.c | 304 ++ libssh/examples/libsshpp.cpp | 33 + libssh/examples/libsshpp_noexcept.cpp | 41 + libssh/examples/sample.c | 519 +++ libssh/examples/samplesftp.c | 266 ++ libssh/examples/samplesshd-kbdint.c | 413 ++ libssh/examples/samplesshd-tty.c | 453 +++ libssh/examples/samplesshd.c | 314 ++ libssh/examples/scp_download.c | 165 + libssh/examples/senddata.c | 64 + libssh/examples/sshnetcat.c | 258 ++ libssh/include/CMakeLists.txt | 3 + libssh/include/libssh/CMakeLists.txt | 39 + libssh/include/libssh/agent.h | 117 + libssh/include/libssh/auth.h | 106 + libssh/include/libssh/bind.h | 53 + libssh/include/libssh/buffer.h | 72 + libssh/include/libssh/callbacks.h | 447 +++ libssh/include/libssh/channels.h | 118 + libssh/include/libssh/crc32.h | 28 + libssh/include/libssh/crypto.h | 113 + libssh/include/libssh/dh.h | 55 + libssh/include/libssh/ecdh.h | 41 + libssh/include/libssh/kex.h | 50 + libssh/include/libssh/keys.h | 56 + libssh/include/libssh/legacy.h | 120 + libssh/include/libssh/libcrypto.h | 86 + libssh/include/libssh/libgcrypt.h | 71 + libssh/include/libssh/libssh.h | 596 +++ libssh/include/libssh/libsshpp.hpp | 596 +++ libssh/include/libssh/messages.h | 104 + libssh/include/libssh/misc.h | 92 + libssh/include/libssh/options.h | 28 + libssh/include/libssh/packet.h | 87 + libssh/include/libssh/pcap.h | 46 + libssh/include/libssh/pki.h | 121 + libssh/include/libssh/pki_priv.h | 88 + libssh/include/libssh/poll.h | 159 + libssh/include/libssh/priv.h | 247 ++ libssh/include/libssh/scp.h | 55 + libssh/include/libssh/server.h | 393 ++ libssh/include/libssh/session.h | 197 + libssh/include/libssh/sftp.h | 979 +++++ libssh/include/libssh/socket.h | 68 + libssh/include/libssh/ssh1.h | 82 + libssh/include/libssh/ssh2.h | 73 + libssh/include/libssh/string.h | 41 + libssh/include/libssh/threads.h | 31 + libssh/include/libssh/wrapper.h | 71 + libssh/libssh-build-tree-settings.cmake.in | 1 + libssh/libssh-config-version.cmake.in | 11 + libssh/libssh-config.cmake.in | 11 + libssh/libssh.pc.cmake | 6 + libssh/libssh_threads.pc.cmake | 6 + libssh/src/CMakeLists.txt | 254 ++ libssh/src/agent.c | 516 +++ libssh/src/auth.c | 2116 ++++++++++ libssh/src/auth1.c | 235 ++ libssh/src/base64.c | 287 ++ libssh/src/bind.c | 466 +++ libssh/src/buffer.c | 626 +++ libssh/src/callbacks.c | 61 + libssh/src/channels.c | 3461 +++++++++++++++++ libssh/src/channels1.c | 385 ++ libssh/src/client.c | 696 ++++ libssh/src/config.c | 366 ++ libssh/src/connect.c | 484 +++ libssh/src/crc32.c | 95 + libssh/src/dh.c | 1136 ++++++ libssh/src/ecdh.c | 278 ++ libssh/src/error.c | 139 + libssh/src/gcrypt_missing.c | 101 + libssh/src/getpass.c | 282 ++ libssh/src/gzip.c | 221 ++ libssh/src/init.c | 85 + libssh/src/kex.c | 501 +++ libssh/src/kex1.c | 496 +++ libssh/src/known_hosts.c | 666 ++++ libssh/src/legacy.c | 731 ++++ libssh/src/libcrypto.c | 600 +++ libssh/src/libgcrypt.c | 526 +++ libssh/src/log.c | 152 + libssh/src/match.c | 189 + libssh/src/messages.c | 1319 +++++++ libssh/src/misc.c | 1036 +++++ libssh/src/options.c | 1424 +++++++ libssh/src/packet.c | 531 +++ libssh/src/packet1.c | 370 ++ libssh/src/packet_cb.c | 244 ++ libssh/src/packet_crypt.c | 186 + libssh/src/pcap.c | 554 +++ libssh/src/pki.c | 1410 +++++++ libssh/src/pki_crypto.c | 1403 +++++++ libssh/src/pki_gcrypt.c | 1699 ++++++++ libssh/src/poll.c | 926 +++++ libssh/src/scp.c | 809 ++++ libssh/src/server.c | 1205 ++++++ libssh/src/session.c | 737 ++++ libssh/src/sftp.c | 3199 +++++++++++++++ libssh/src/sftpserver.c | 518 +++ libssh/src/socket.c | 846 ++++ libssh/src/string.c | 270 ++ libssh/src/threads.c | 176 + libssh/src/threads/CMakeLists.txt | 125 + libssh/src/threads/pthread.c | 99 + libssh/src/wrapper.c | 345 ++ libssh/tests/CMakeLists.txt | 48 + libssh/tests/authentication.c | 74 + libssh/tests/benchmarks/CMakeLists.txt | 21 + libssh/tests/benchmarks/bench1.sh | 13 + libssh/tests/benchmarks/bench2.sh | 13 + libssh/tests/benchmarks/bench_raw.c | 285 ++ libssh/tests/benchmarks/bench_scp.c | 150 + libssh/tests/benchmarks/bench_sftp.c | 239 ++ libssh/tests/benchmarks/benchmarks.c | 400 ++ libssh/tests/benchmarks/benchmarks.h | 99 + libssh/tests/benchmarks/latency.c | 148 + libssh/tests/chmodtest.c | 33 + libssh/tests/client/CMakeLists.txt | 12 + libssh/tests/client/torture_algorithms.c | 245 ++ libssh/tests/client/torture_auth.c | 365 ++ libssh/tests/client/torture_connect.c | 132 + libssh/tests/client/torture_knownhosts.c | 108 + libssh/tests/client/torture_proxycommand.c | 57 + libssh/tests/client/torture_session.c | 108 + libssh/tests/client/torture_sftp_dir.c | 74 + libssh/tests/client/torture_sftp_static.c | 32 + libssh/tests/cmdline.c | 71 + libssh/tests/connection.c | 31 + libssh/tests/ctest-default.cmake | 71 + libssh/tests/generate.py | 10 + libssh/tests/sftp_stress/main.c | 174 + libssh/tests/test_exec.c | 62 + libssh/tests/test_pcap.c | 50 + libssh/tests/test_socket.c | 93 + libssh/tests/test_tunnel.c | 76 + libssh/tests/tests.h | 8 + libssh/tests/torture.c | 316 ++ libssh/tests/torture.h | 76 + libssh/tests/unittests/CMakeLists.txt | 16 + libssh/tests/unittests/torture_buffer.c | 132 + libssh/tests/unittests/torture_callbacks.c | 71 + libssh/tests/unittests/torture_init.c | 23 + libssh/tests/unittests/torture_isipaddr.c | 56 + libssh/tests/unittests/torture_keyfiles.c | 261 ++ libssh/tests/unittests/torture_list.c | 91 + libssh/tests/unittests/torture_misc.c | 210 + libssh/tests/unittests/torture_options.c | 197 + libssh/tests/unittests/torture_pki.c | 1037 +++++ libssh/tests/unittests/torture_rand.c | 68 + libssh/tests/valgrind.supp | 106 + 211 files changed, 59656 insertions(+), 2 deletions(-) create mode 100644 libssh/.gitignore create mode 100644 libssh/AUTHORS create mode 100644 libssh/BSD create mode 100644 libssh/CMakeLists.txt create mode 100644 libssh/COPYING create mode 100644 libssh/CPackConfig.cmake create mode 100644 libssh/CTestConfig.cmake create mode 100644 libssh/ChangeLog create mode 100644 libssh/CodingStyle create mode 100644 libssh/ConfigureChecks.cmake create mode 100644 libssh/DefineOptions.cmake create mode 100644 libssh/INSTALL create mode 100644 libssh/README create mode 100644 libssh/SubmittingPatches create mode 100755 libssh/build/build_make.sh create mode 100644 libssh/cmake/Modules/AddCMockaTest.cmake create mode 100644 libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS create mode 100644 libssh/cmake/Modules/CheckCCompilerFlagSSP.cmake create mode 100644 libssh/cmake/Modules/DefineCMakeDefaults.cmake create mode 100644 libssh/cmake/Modules/DefineCompilerFlags.cmake create mode 100644 libssh/cmake/Modules/DefineInstallationPaths.cmake create mode 100644 libssh/cmake/Modules/DefinePlatformDefaults.cmake create mode 100644 libssh/cmake/Modules/FindArgp.cmake create mode 100644 libssh/cmake/Modules/FindCMocka.cmake create mode 100644 libssh/cmake/Modules/FindGCrypt.cmake create mode 100644 libssh/cmake/Modules/FindNSIS.cmake create mode 100644 libssh/cmake/Modules/FindOpenSSL.cmake create mode 100644 libssh/cmake/Modules/FindZLIB.cmake create mode 100644 libssh/cmake/Modules/MacroAddCompileFlags.cmake create mode 100644 libssh/cmake/Modules/MacroAddLinkFlags.cmake create mode 100644 libssh/cmake/Modules/MacroAddPlugin.cmake create mode 100644 libssh/cmake/Modules/MacroCopyFile.cmake create mode 100644 libssh/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake create mode 100644 libssh/cmake/Modules/UseDoxygen.cmake create mode 100644 libssh/config.h.cmake create mode 100644 libssh/doc/CMakeLists.txt create mode 100644 libssh/doc/TracFooter.html create mode 100644 libssh/doc/TracHeader.html create mode 100644 libssh/doc/authentication.dox create mode 100644 libssh/doc/command.dox create mode 100644 libssh/doc/doxy.config.in create mode 100644 libssh/doc/doxy.trac.in create mode 100644 libssh/doc/forwarding.dox create mode 100644 libssh/doc/guided_tour.dox create mode 100644 libssh/doc/introduction.dox create mode 100644 libssh/doc/linking.dox create mode 100644 libssh/doc/mainpage.dox create mode 100644 libssh/doc/scp.dox create mode 100644 libssh/doc/sftp.dox create mode 100644 libssh/doc/shell.dox create mode 100644 libssh/doc/tbd.dox create mode 100644 libssh/doc/threading.dox create mode 100644 libssh/examples/CMakeLists.txt create mode 100644 libssh/examples/authentication.c create mode 100644 libssh/examples/connect_ssh.c create mode 100644 libssh/examples/examples_common.h create mode 100644 libssh/examples/exec.c create mode 100644 libssh/examples/knownhosts.c create mode 100644 libssh/examples/libssh_scp.c create mode 100644 libssh/examples/libsshpp.cpp create mode 100644 libssh/examples/libsshpp_noexcept.cpp create mode 100644 libssh/examples/sample.c create mode 100644 libssh/examples/samplesftp.c create mode 100644 libssh/examples/samplesshd-kbdint.c create mode 100644 libssh/examples/samplesshd-tty.c create mode 100644 libssh/examples/samplesshd.c create mode 100644 libssh/examples/scp_download.c create mode 100644 libssh/examples/senddata.c create mode 100644 libssh/examples/sshnetcat.c create mode 100644 libssh/include/CMakeLists.txt create mode 100644 libssh/include/libssh/CMakeLists.txt create mode 100644 libssh/include/libssh/agent.h create mode 100644 libssh/include/libssh/auth.h create mode 100644 libssh/include/libssh/bind.h create mode 100644 libssh/include/libssh/buffer.h create mode 100644 libssh/include/libssh/callbacks.h create mode 100644 libssh/include/libssh/channels.h create mode 100644 libssh/include/libssh/crc32.h create mode 100644 libssh/include/libssh/crypto.h create mode 100644 libssh/include/libssh/dh.h create mode 100644 libssh/include/libssh/ecdh.h create mode 100644 libssh/include/libssh/kex.h create mode 100644 libssh/include/libssh/keys.h create mode 100644 libssh/include/libssh/legacy.h create mode 100644 libssh/include/libssh/libcrypto.h create mode 100644 libssh/include/libssh/libgcrypt.h create mode 100644 libssh/include/libssh/libssh.h create mode 100644 libssh/include/libssh/libsshpp.hpp create mode 100644 libssh/include/libssh/messages.h create mode 100644 libssh/include/libssh/misc.h create mode 100644 libssh/include/libssh/options.h create mode 100644 libssh/include/libssh/packet.h create mode 100644 libssh/include/libssh/pcap.h create mode 100644 libssh/include/libssh/pki.h create mode 100644 libssh/include/libssh/pki_priv.h create mode 100644 libssh/include/libssh/poll.h create mode 100644 libssh/include/libssh/priv.h create mode 100644 libssh/include/libssh/scp.h create mode 100644 libssh/include/libssh/server.h create mode 100644 libssh/include/libssh/session.h create mode 100644 libssh/include/libssh/sftp.h create mode 100644 libssh/include/libssh/socket.h create mode 100644 libssh/include/libssh/ssh1.h create mode 100644 libssh/include/libssh/ssh2.h create mode 100644 libssh/include/libssh/string.h create mode 100644 libssh/include/libssh/threads.h create mode 100644 libssh/include/libssh/wrapper.h create mode 100644 libssh/libssh-build-tree-settings.cmake.in create mode 100644 libssh/libssh-config-version.cmake.in create mode 100644 libssh/libssh-config.cmake.in create mode 100644 libssh/libssh.pc.cmake create mode 100644 libssh/libssh_threads.pc.cmake create mode 100644 libssh/src/CMakeLists.txt create mode 100644 libssh/src/agent.c create mode 100644 libssh/src/auth.c create mode 100644 libssh/src/auth1.c create mode 100644 libssh/src/base64.c create mode 100644 libssh/src/bind.c create mode 100644 libssh/src/buffer.c create mode 100644 libssh/src/callbacks.c create mode 100644 libssh/src/channels.c create mode 100644 libssh/src/channels1.c create mode 100644 libssh/src/client.c create mode 100644 libssh/src/config.c create mode 100644 libssh/src/connect.c create mode 100644 libssh/src/crc32.c create mode 100644 libssh/src/dh.c create mode 100644 libssh/src/ecdh.c create mode 100644 libssh/src/error.c create mode 100644 libssh/src/gcrypt_missing.c create mode 100644 libssh/src/getpass.c create mode 100644 libssh/src/gzip.c create mode 100644 libssh/src/init.c create mode 100644 libssh/src/kex.c create mode 100644 libssh/src/kex1.c create mode 100644 libssh/src/known_hosts.c create mode 100644 libssh/src/legacy.c create mode 100644 libssh/src/libcrypto.c create mode 100644 libssh/src/libgcrypt.c create mode 100644 libssh/src/log.c create mode 100644 libssh/src/match.c create mode 100644 libssh/src/messages.c create mode 100644 libssh/src/misc.c create mode 100644 libssh/src/options.c create mode 100644 libssh/src/packet.c create mode 100644 libssh/src/packet1.c create mode 100644 libssh/src/packet_cb.c create mode 100644 libssh/src/packet_crypt.c create mode 100644 libssh/src/pcap.c create mode 100644 libssh/src/pki.c create mode 100644 libssh/src/pki_crypto.c create mode 100644 libssh/src/pki_gcrypt.c create mode 100644 libssh/src/poll.c create mode 100644 libssh/src/scp.c create mode 100644 libssh/src/server.c create mode 100644 libssh/src/session.c create mode 100644 libssh/src/sftp.c create mode 100644 libssh/src/sftpserver.c create mode 100644 libssh/src/socket.c create mode 100644 libssh/src/string.c create mode 100644 libssh/src/threads.c create mode 100644 libssh/src/threads/CMakeLists.txt create mode 100644 libssh/src/threads/pthread.c create mode 100644 libssh/src/wrapper.c create mode 100644 libssh/tests/CMakeLists.txt create mode 100644 libssh/tests/authentication.c create mode 100644 libssh/tests/benchmarks/CMakeLists.txt create mode 100644 libssh/tests/benchmarks/bench1.sh create mode 100755 libssh/tests/benchmarks/bench2.sh create mode 100644 libssh/tests/benchmarks/bench_raw.c create mode 100644 libssh/tests/benchmarks/bench_scp.c create mode 100644 libssh/tests/benchmarks/bench_sftp.c create mode 100644 libssh/tests/benchmarks/benchmarks.c create mode 100644 libssh/tests/benchmarks/benchmarks.h create mode 100644 libssh/tests/benchmarks/latency.c create mode 100644 libssh/tests/chmodtest.c create mode 100644 libssh/tests/client/CMakeLists.txt create mode 100644 libssh/tests/client/torture_algorithms.c create mode 100644 libssh/tests/client/torture_auth.c create mode 100644 libssh/tests/client/torture_connect.c create mode 100644 libssh/tests/client/torture_knownhosts.c create mode 100644 libssh/tests/client/torture_proxycommand.c create mode 100644 libssh/tests/client/torture_session.c create mode 100644 libssh/tests/client/torture_sftp_dir.c create mode 100644 libssh/tests/client/torture_sftp_static.c create mode 100644 libssh/tests/cmdline.c create mode 100644 libssh/tests/connection.c create mode 100644 libssh/tests/ctest-default.cmake create mode 100755 libssh/tests/generate.py create mode 100644 libssh/tests/sftp_stress/main.c create mode 100644 libssh/tests/test_exec.c create mode 100644 libssh/tests/test_pcap.c create mode 100644 libssh/tests/test_socket.c create mode 100644 libssh/tests/test_tunnel.c create mode 100644 libssh/tests/tests.h create mode 100644 libssh/tests/torture.c create mode 100644 libssh/tests/torture.h create mode 100644 libssh/tests/unittests/CMakeLists.txt create mode 100644 libssh/tests/unittests/torture_buffer.c create mode 100644 libssh/tests/unittests/torture_callbacks.c create mode 100644 libssh/tests/unittests/torture_init.c create mode 100644 libssh/tests/unittests/torture_isipaddr.c create mode 100644 libssh/tests/unittests/torture_keyfiles.c create mode 100644 libssh/tests/unittests/torture_list.c create mode 100644 libssh/tests/unittests/torture_misc.c create mode 100644 libssh/tests/unittests/torture_options.c create mode 100644 libssh/tests/unittests/torture_pki.c create mode 100644 libssh/tests/unittests/torture_rand.c create mode 100644 libssh/tests/valgrind.supp diff --git a/Makefile.am b/Makefile.am index b17ca578..7f9192df 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,6 +22,7 @@ CFLAGS += -D_GNU_SOURCE endif CFLAGS += -Wno-unused-parameter -Wno-unused-variable +CFLAGS += -Ilibssh/include/ # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. @@ -237,3 +238,9 @@ endif if NO_B64_NTOP nodist_tmate_SOURCES += compat/b64_ntop.c endif + +tmate_LDADD = libssh/build/src/libssh.a + +libssh/build/src/libssh.a: + cd libssh/build && cmake .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON + +make -C libssh/build ssh_static diff --git a/configure.ac b/configure.ac index bfffefa1..013cd655 100644 --- a/configure.ac +++ b/configure.ac @@ -107,8 +107,12 @@ AC_MSG_RESULT($found_glibc) # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) -AC_SEARCH_LIBS(ssh_new, ssh) -AC_SEARCH_LIBS(msgpack_object_print, msgpack) +AC_CHECK_LIB(z, inflate, [], + [AC_MSG_ERROR([zlib library required])]) +AC_CHECK_LIB(crypto,CRYPTO_new_ex_data, [], + [AC_MSG_ERROR([OpenSSL library required])]) +AC_CHECK_LIB(ssl, SSL_library_init, [], + [AC_MSG_ERROR([OpenSSL library required])]) # Look for libevent. PKG_CHECK_MODULES( diff --git a/libssh/.gitignore b/libssh/.gitignore new file mode 100644 index 00000000..c3c0e572 --- /dev/null +++ b/libssh/.gitignore @@ -0,0 +1,8 @@ +*.a +*.o +.* +*.swp +*~$ +build +cscope.* +tags diff --git a/libssh/AUTHORS b/libssh/AUTHORS new file mode 100644 index 00000000..fd753860 --- /dev/null +++ b/libssh/AUTHORS @@ -0,0 +1,15 @@ +Author(s): +Aris Adamantiadis (project initiator) + +Andreas Schneider (developer) + +Nick Zitzmann (mostly client SFTP stuff) + +Norbert Kiesel (getaddrinfo and other patches) + +Jean-Philippe Garcia Ballester (Port to libgcrypt and configure.in voodoo, debian packaging) + +Contributor(s): + +Laurent Bigonville (debian packaging) + diff --git a/libssh/BSD b/libssh/BSD new file mode 100644 index 00000000..b8dba0d2 --- /dev/null +++ b/libssh/BSD @@ -0,0 +1,24 @@ +Some parts are under the BSDv2 License : + + +Copyright (c) 2000 Markus Friedl. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt new file mode 100644 index 00000000..81ac5884 --- /dev/null +++ b/libssh/CMakeLists.txt @@ -0,0 +1,136 @@ +project(libssh C) + +# Required cmake version +cmake_minimum_required(VERSION 2.6.0) + +# global needed variables +set(APPLICATION_NAME ${PROJECT_NAME}) + +set(APPLICATION_VERSION_MAJOR "0") +set(APPLICATION_VERSION_MINOR "5") +set(APPLICATION_VERSION_PATCH "90") + +set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}") + +# SOVERSION scheme: CURRENT.AGE.REVISION +# If there was an incompatible interface change: +# Increment CURRENT. Set AGE and REVISION to 0 +# If there was a compatible interface change: +# Increment AGE. Set REVISION to 0 +# If the source code was changed, but there were no interface changes: +# Increment REVISION. +set(LIBRARY_VERSION "4.3.0") +set(LIBRARY_SOVERSION "4") + +# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked +set(CMAKE_MODULE_PATH + ${CMAKE_SOURCE_DIR}/cmake/Modules +) + +# add definitions +include(DefineCMakeDefaults) +include(DefinePlatformDefaults) +include(DefineCompilerFlags) +include(DefineInstallationPaths) +include(DefineOptions.cmake) +include(CPackConfig.cmake) + +# disallow in-source build +include(MacroEnsureOutOfSourceBuild) +macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source build. Please create a separate build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there.") + +# add macros +include(MacroAddPlugin) +include(MacroCopyFile) + +# search for libraries +if (WITH_ZLIB) + find_package(ZLIB REQUIRED) +endif (WITH_ZLIB) + +if (WITH_GCRYPT) + find_package(GCrypt 1.5.0 REQUIRED) + if (NOT GCRYPT_FOUND) + message(FATAL_ERROR "Could not find GCrypt") + endif (NOT GCRYPT_FOUND) +else (WITH_GCRYPT) + find_package(OpenSSL) + if (NOT OPENSSL_FOUND) + find_package(GCrypt) + if (NOT GCRYPT_FOUND) + message(FATAL_ERROR "Could not find OpenSSL or GCrypt") + endif (NOT GCRYPT_FOUND) + endif (NOT OPENSSL_FOUND) +endif(WITH_GCRYPT) + +# Find out if we have threading available +set(CMAKE_THREAD_PREFER_PTHREADS ON) +find_package(Threads) + +# config.h checks +include(ConfigureChecks.cmake) +configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +# check subdirectories +add_subdirectory(doc) +add_subdirectory(include) +add_subdirectory(src) + +# pkg-config file +configure_file(libssh.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc) +configure_file(libssh_threads.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libssh_threads.pc) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc + ${CMAKE_CURRENT_BINARY_DIR}/libssh_threads.pc + DESTINATION + ${LIB_INSTALL_DIR}/pkgconfig + COMPONENT + pkgconfig +) + +# cmake config files +configure_file(libssh-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-config.cmake @ONLY) +configure_file(libssh-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-config-version.cmake @ONLY) +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/libssh-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libssh-config-version.cmake + DESTINATION + ${CMAKE_INSTALL_DIR} + COMPONENT + devel +) + +# in tree build settings +configure_file(libssh-build-tree-settings.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-build-tree-settings.cmake @ONLY) + +add_subdirectory(examples) + +if (WITH_TESTING) + find_package(CMocka REQUIRED) + include(AddCMockaTest) + add_subdirectory(tests) +endif (WITH_TESTING) + + +message(STATUS "********************************************") +message(STATUS "********** ${PROJECT_NAME} build options : **********") + +message(STATUS "zlib support: ${WITH_ZLIB}") +message(STATUS "libgcrypt support: ${WITH_GCRYPT}") +message(STATUS "SSH-1 support: ${WITH_SSH1}") +message(STATUS "SFTP support: ${WITH_SFTP}") +message(STATUS "Server support : ${WITH_SERVER}") +message(STATUS "Pcap debugging support : ${WITH_PCAP}") +message(STATUS "With static library: ${WITH_STATIC_LIB}") +message(STATUS "Unit testing: ${WITH_TESTING}") +message(STATUS "Client code Unit testing: ${WITH_CLIENT_TESTING}") +if (WITH_INTERNAL_DOC) + message(STATUS "Internal documentation generation") +else (WITH_INTERNAL_DOC) + message(STATUS "Public API documentation generation") +endif (WITH_INTERNAL_DOC) +message(STATUS "Benchmarks: ${WITH_BENCHMARKS}") +message(STATUS "********************************************") + diff --git a/libssh/COPYING b/libssh/COPYING new file mode 100644 index 00000000..cb7d9b73 --- /dev/null +++ b/libssh/COPYING @@ -0,0 +1,460 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + Linking with OpenSSL +17. In addition, as a special exception, we give permission to link the code of its release of libssh with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU Lesser General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. + END OF TERMS AND CONDITIONS diff --git a/libssh/CPackConfig.cmake b/libssh/CPackConfig.cmake new file mode 100644 index 00000000..2e4c9fee --- /dev/null +++ b/libssh/CPackConfig.cmake @@ -0,0 +1,53 @@ +# For help take a look at: +# http://www.cmake.org/Wiki/CMake:CPackConfiguration + +### general settings +set(CPACK_PACKAGE_NAME ${APPLICATION_NAME}) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The SSH library") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README") +set(CPACK_PACKAGE_VENDOR "The SSH Library Development Team") +set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING") + + +### versions +set(CPACK_PACKAGE_VERSION_MAJOR "0") +set(CPACK_PACKAGE_VERSION_MINOR "5") +set(CPACK_PACKAGE_VERSION_PATCH "90") +set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") + + +### source generator +set(CPACK_SOURCE_GENERATOR "TGZ") +set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build/;tags;cscope.*") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") + +if (WIN32) + set(CPACK_GENERATOR "ZIP") + + ### nsis generator + find_package(NSIS) + if (NSIS_MAKE) + set(CPACK_GENERATOR "${CPACK_GENERATOR};NSIS") + set(CPACK_NSIS_DISPLAY_NAME "The SSH Library") + set(CPACK_NSIS_COMPRESSOR "/SOLID zlib") + set(CPACK_NSIS_MENU_LINKS "http://www.libssh.org/" "libssh homepage") + endif (NSIS_MAKE) +endif (WIN32) + +set(CPACK_PACKAGE_INSTALL_DIRECTORY "libssh") + +set(CPACK_PACKAGE_FILE_NAME ${APPLICATION_NAME}-${CPACK_PACKAGE_VERSION}) + +set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") +set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C/C++ Headers") +set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION + "Libraries used to build programs which use libssh") +set(CPACK_COMPONENT_HEADERS_DESCRIPTION + "C/C++ header files for use with libssh") +set(CPACK_COMPONENT_HEADERS_DEPENDS libraries) +#set(CPACK_COMPONENT_APPLICATIONS_GROUP "Runtime") +set(CPACK_COMPONENT_LIBRARIES_GROUP "Development") +set(CPACK_COMPONENT_HEADERS_GROUP "Development") + +include(CPack) diff --git a/libssh/CTestConfig.cmake b/libssh/CTestConfig.cmake new file mode 100644 index 00000000..d8a41831 --- /dev/null +++ b/libssh/CTestConfig.cmake @@ -0,0 +1,9 @@ +set(UPDATE_TYPE "true") + +set(CTEST_PROJECT_NAME "libssh") +set(CTEST_NIGHTLY_START_TIME "01:00:00 CET") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "test.libssh.org") +set(CTEST_DROP_LOCATION "/submit.php?project=libssh") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/libssh/ChangeLog b/libssh/ChangeLog new file mode 100644 index 00000000..5bc0784a --- /dev/null +++ b/libssh/ChangeLog @@ -0,0 +1,298 @@ +ChangeLog +========== + +version 0.5.x (released 2012-xx-xx) + * Added new PKI infrastructure. + * Added simplified user auth functions. + * Added ECDSA pubkey support. + * Added ECDSA hostkey support. + * Added diffie-hellman-group14-sha1 support. + * Fixed a ton of bugs. + +version 0.5.0 (released 2011-06-01) + * Added ssh_ prefix to all functions. + * Added complete Windows support. + * Added improved server support. + * Added unit tests for a lot of functions. + * Added asynchronous service request. + * Added a multiplatform ssh_getpass() function. + * Added a tutorial. + * Added a lot of documentation. + * Fixed a lot of bugs. + * Fixed several memory leaks. + +version 0.4.8 (released 2011-01-15) + * Fixed memory leaks in session signing. + * Fixed memory leak in ssh_print_hexa. + * Fixed problem with ssh_connect w/ timeout and fd > 1024. + * Fixed some warnings on OS/2. + * Fixed installation path for OS/2. + +version 0.4.7 (released 2010-12-28) + * Fixed a possible memory leak in ssh_get_user_home(). + * Fixed a memory leak in sftp_xstat. + * Fixed uninitialized fd->revents member. + * Fixed timout value in ssh_channel_accept(). + * Fixed length checks in ssh_analyze_banner(). + * Fixed a possible data overread and crash bug. + * Fixed setting max_fd which breaks ssh_select(). + * Fixed some pedantic build warnings. + * Fixed a memory leak with session->bindaddr. + +version 0.4.6 (released 2010-09-03) + * Added a cleanup function to free the ws2_32 library. + * Fixed build with gcc 3.4. + * Fixed the Windows build on Vista and newer. + * Fixed the usage of WSAPoll() on Windows. + * Fixed "@deprecated" in doxygen + * Fixed some mingw warnings. + * Fixed handling of opened channels. + * Fixed keepalive problem on older openssh servers. + * Fixed testing for big endian on Windows. + * Fixed the Windows preprocessor macros and defines. + +version 0.4.5 (released 2010-07-13) + * Added option to bind a client to an ip address. + * Fixed the ssh socket polling function. + * Fixed Windows related bugs in bsd_poll(). + * Fixed serveral build warnings. + +version 0.4.4 (released 2010-06-01) + * Fixed a bug in the expand function for escape sequences. + * Fixed a bug in the tilde expand function. + * Fixed a bug in setting the options. + +version 0.4.3 (released 2010-05-18) + * Added global/keepalive responses. + * Added runtime detection of WSAPoll(). + * Added a select(2) based poll-emulation if poll(2) is not available. + * Added a function to expand an escaped string. + * Added a function to expand the tilde from a path. + * Added a proxycommand support. + * Added ssh_privatekey_type public function + * Added the possibility to define _OPENSSL_DIR and _ZLIB_DIR. + * Fixed sftp_chown. + * Fixed sftp_rename on protocol version 3. + * Fixed a blocking bug in channel_poll. + * Fixed config parsing wich has overwritten user specified values. + * Fixed hashed [host]:port format in knownhosts + * Fixed Windows build. + * Fixed doublefree happening after a negociation error. + * Fixed aes*-ctr with <= OpenSSL 0.9.7b. + * Fixed some documentation. + * Fixed exec example which has broken read usage. + * Fixed broken algorithm choice for server. + * Fixed a typo that we don't export all symbols. + * Removed the unneeded dependency to doxygen. + * Build examples only on the Linux plattform. + +version 0.4.2 (released 2010-03-15) + * Added owner and group information in sftp attributes. + * Added missing SSH_OPTIONS_FD option. + * Added printout of owner and group in the sftp example. + * Added a prepend function for ssh_list. + * Added send back replies to openssh's keepalives. + * Fixed documentation in scp code + * Fixed longname parsing, this only workings with readdir. + * Fixed and added support for several identity files. + * Fixed sftp_parse_longname() on Windows. + * Fixed a race condition bug in ssh_scp_close() + * Remove config support for SSHv1 Cipher variable. + * Rename ssh_list_add to ssh_list_append. + * Rename ssh_list_get_head to ssh_list_pop_head + +version 0.4.1 (released 2010-02-13) + * Added support for aes128-ctr, aes192-ctr and aes256-ctr encryption. + * Added an example for exec. + * Added private key type detection feature in privatekey_from_file(). + * Fixed zlib compression fallback. + * Fixed kex bug that client preference should be prioritary + * Fixed known_hosts file set by the user. + * Fixed a memleak in channel_accept(). + * Fixed underflow when leave_function() are unbalanced + * Fixed memory corruption in handle_channel_request_open(). + * Fixed closing of a file handle case of errors in privatekey_from_file(). + * Fixed ssh_get_user_home_dir() to be thread safe. + * Fixed the doxygen documentation. + +version 0.4.0 (released 2009-12-10) + * Added scp support. + * Added support for sending signals (RFC 4254, section 6.9). + * Added MSVC support. + * Added support for ~/.ssh/config. + * Added sftp extension support. + * Added X11 forwarding support for client. + * Added forward listening. + * Added support for openssh extensions (statvfs, fstatvfs). + * Added a cleaned up interface for setting options. + * Added a generic way to handle sockets asynchronously. + * Added logging of the sftp flags used to open a file. + * Added full poll() support and poll-emulation for win32. + * Added missing 64bit functions in sftp. + * Added support for ~/ and SSH_DIR/ in filenames instead of %s/. + * Fixed Fix channel_get_exit_status bug. + * Fixed calltrace logging to make it optional. + * Fixed compilation on Solaris. + * Fixed resolving of ip addresses. + * Fixed libssh compilation without server support. + * Fixed possible memory corruptions (ticket #14). + +version 0.3.4 (released 2009-09-14) + * Added ssh_basename and ssh_dirname. + * Added a portable ssh_mkdir function. + * Added a sftp_tell64() function. + * Added missing NULL pointer checks to crypt_set_algorithms_server. + * Fixed ssh_write_knownhost if ~/.ssh doesn't exist. + * Fixed a possible integer overflow in buffer_get_data(). + * Fixed possible security bug in packet_decrypt(). + * Fixed a possible stack overflow in agent code. + +version 0.3.3 (released 2009-08-18) + * Fixed double free pointer crash in dsa_public_to_string. + * Fixed channel_get_exit_status bug. + * Fixed ssh_finalize which didn't clear the flag. + * Fixed memory leak introduced by previous bugfix. + * Fixed channel_poll broken when delayed EOF recvd. + * Fixed stupid "can't parse known host key" bug. + * Fixed possible memory corruption (ticket #14). + +version 0.3.2 (released 2009-08-05) + * Added ssh_init() function. + * Added sftp_readlink() function. + * Added sftp_symlink() function. + * Fixed ssh_write_knownhost(). + * Fixed compilation on Solaris. + * Fixed SSHv1 compilation. + +version 0.3.1 (released 2009-07-14) + * Added return code SSH_SERVER_FILE_NOT_FOUND. + * Fixed compilation of SSHv1. + * Fixed several memory leaks. + * Fixed possible infinite loops. + * Fixed a possible crash bug. + * Fixed build warnings. + * Fixed cmake on BSD. +version 0.3.1 (released 2009-07-14) + * Added return code SSH_SERVER_FILE_NOT_FOUND. + * Fixed compilation of SSHv1. + * Fixed several memory leaks. + * Fixed possible infinite loops. + * Fixed a possible crash bug. + * Fixed build warnings. + * Fixed cmake on BSD. + +version 0.3 (released 2009-05-21) + * Added support for ssh-agent authentication. + * Added POSIX like sftp implementation. + * Added error checking to all functions. + * Added const to arguments where it was needed. + * Added a channel_get_exit_status() function. + * Added a channel_read_buffer() function, channel_read() is now + a POSIX like function. + * Added a more generic auth callback function. + * Added printf attribute checking for log and error functions. + * Added runtime function tracer support. + * Added NSIS build support with CPack. + * Added openssh hashed host support. + * Added API documentation for all public functions. + * Added asynchronous SFTP read function. + * Added a ssh_bind_set_fd() function. + * Fixed known_hosts parsing. + * Fixed a lot of build warnings. + * Fixed the Windows build. + * Fixed a lot of memory leaks. + * Fixed a double free corruption in the server support. + * Fixed the "ssh_accept:" bug in server support. + * Fixed important channel bugs. + * Refactored the socket handling. + * Switched to CMake build system. + * Improved performance. + +version 0.2 (released 2007-11-29) + * General cleanup + * More comprehensive API + * Up-to-date Doxygen documentation of each public function + * Basic server-based support + * Libgcrypt support (alternative to openssl and its license) + * SSH1 support (disabled by default) + * Added 3des-cbc + * A lot of bugfixes + +version 0.11-dev + * Server implementation development. + * Small bug corrected when connecting to sun ssh servers. + * Channel wierdness corrected (writing huge data packets) + * Channel_read_nonblocking added + * Channel bug where stderr wasn't correctly read fixed. + * Added sftp_file_set_nonblocking(), which is nonblocking SFTP IO + * Connect_status callback. + * Priv.h contains the internal functions, libssh.h the public interface + * Options_set_timeout (thx marcelo) really working. + * Tcp tunneling through channel_open_forward. + * Channel_request_exec() + * Channel_request_env() + * Ssh_get_pubkey_hash() + * Ssh_is_server_known() + * Ssh_write_known_host() + * Options_set_ssh_dir + * How could this happen ! there weren't any channel_close ! + * Nasty channel_free bug resolved. + * Removed the unsigned long all around the code. use only u8,u32 & u64. + * It now compiles and runs under amd64 ! + * Channel_request_pty_size + * Channel_change_pty_size + * Options_copy() + * Ported the doc to an HTML file. + * Small bugfix in packet.c + * Prefixed error constants with SSH_ + * Sftp_stat, sftp_lstat, sftp_fstat. thanks Michel Bardiaux for the patch. + * Again channel number mismatch fixed. + * Fixed a bug in ssh_select making the select fail when a signal has been + caught. + * Keyboard-interactive authentication working. + +version 0.1 (released 2004-03-05) + * Begining of sftp subsystem implementation. + * Some cleanup into channels implementation + * Now every channel functions is called by its CHANNEL handler. + * Added channel_poll() and channel_read(). + * Changed the client so it uses the new channel_poll and channel_read interface + * Small use-after-free bug with channels resolved + * Changed stupidities in lot of function names. + * Removed a debug output file opened by default. + * Added API.txt, the libssh programmer handbook. + * Various bug fixes from Nick Zitzmann. + * Developed a cryptographic structure for handling protocols. + * An autoconf script which took me half of a day to set up. + * A ssh_select wrapper has been written. + +version 0.0.4 (released 2003-10-10) + * Some terminal code (eof handling) added + * Channels bugfix (it still needs some tweaking though) + * Zlib support + * Added a wrapper.c file. The goal is to provide a similar API to every + cryptographic functions. bignums and sha/md5 are wrapped now. + * More work than it first looks. + * Support for other crypto libs planed (lighter libs) + * Fixed stupid select() bug. + * Libssh now compiles and links with openssl 0.9.6 + * RSA pubkey authentication code now works ! + +version 0.0.3 (released 2003-09-15) + * Added install target in makefile + * Some cleanup in headers files and source code + * Change default banner and project name to libssh. + * New file auth.c to support more and more authentication ways + * Bugfix(read offbyone) in send_kex + * A base64 parser. don't read the source, it's awful. pure 0xbadc0de. + * Changed the client filename to "ssh". logic isn't it ? + * Dss publickey authentication ! still need to wait for the rsa one + * Bugfix in packet.c + * New misc.c contains misc functions + +version 0.0.2 (released 2003-09-03) + * Initial release. + * Client supports both ssh and dss hostkey verification, but doesn't compare them to openssh's files. (~/.ssh/known_hosts) + * The only supported authentication method is password. + * Compiles on linux and openbsd. freebsd and netbsd should work, too + * Lot of work which hasn't been discussed here. diff --git a/libssh/CodingStyle b/libssh/CodingStyle new file mode 100644 index 00000000..93cc382c --- /dev/null +++ b/libssh/CodingStyle @@ -0,0 +1,59 @@ +Coding Style Conventions +======================== + +Coding style guidelines are about reducing the number of unnecessary +reformatting patches and making things easier for developers to work together. + +You don't have to like them or even agree with them, but once put in place we +all have to abide by them (or vote to change them). However, coding style +should never outweigh coding itself and so the guidelines described here are +hopefully easy enough to follow as they are very common and supported by tools +and editors. + +The basic style for C code is the Linux kernel coding style [1] with one +excecption, we use 4 spaces instead of tabs. This closely matches what most +libssh developers use already anyways, with a few exceptions as mentioned +below. + +To shorthen this here are the highlights: + +* Maximum line width is 80 characters + + The reason is not about people with low-res screens but rather sticking + to 80 columns prevents you from easily nesting more than one level of + if statements or other code blocks. + +* Use 4 spaces to indent + +* No trailing whitespaces + +* Follow the K&R guidelines. We won't go through all of them here. Do you + have a copy of "The C Programming Language" anyways right? + +Editors +======== + +VIM +---- + +set ts=4 sw=4 et cindent + +For Vim, the following settings in $HOME/.vimrc will also deal with +displaying trailing whitespace: + + if has("syntax") && (&t_Co > 2 || has("gui_running")) + syntax on + function! ActivateInvisibleCharIndicator() + syntax match TrailingSpace "[ \t]\+$" display containedin=ALL + highlight TrailingSpace ctermbg=Red + endf + autocmd BufNewFile,BufRead * call ActivateInvisibleCharIndicator() + endif + " Show tabs, trailing whitespace, and continued lines visually + set list listchars=tab:»·,trail:·,extends:… + + " highlight overly long lines same as TODOs. + set textwidth=80 + autocmd BufNewFile,BufRead *.c,*.h exec 'match Todo /\%>' . &textwidth . 'v.\+/' + +[1] https://www.kernel.org/doc/Documentation/CodingStyle diff --git a/libssh/ConfigureChecks.cmake b/libssh/ConfigureChecks.cmake new file mode 100644 index 00000000..b0485d98 --- /dev/null +++ b/libssh/ConfigureChecks.cmake @@ -0,0 +1,179 @@ +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckFunctionExists) +include(CheckLibraryExists) +include(CheckTypeSize) +include(CheckCXXSourceCompiles) +include(TestBigEndian) + +set(PACKAGE ${APPLICATION_NAME}) +set(VERSION ${APPLICATION_VERSION}) +set(DATADIR ${DATA_INSTALL_DIR}) +set(LIBDIR ${LIB_INSTALL_DIR}) +set(PLUGINDIR "${PLUGIN_INSTALL_DIR}-${LIBRARY_SOVERSION}") +set(SYSCONFDIR ${SYSCONF_INSTALL_DIR}) + +set(BINARYDIR ${CMAKE_BINARY_DIR}) +set(SOURCEDIR ${CMAKE_SOURCE_DIR}) + +function(COMPILER_DUMPVERSION _OUTPUT_VERSION) + # Remove whitespaces from the argument. + # This is needed for CC="ccache gcc" cmake .. + string(REPLACE " " "" _C_COMPILER_ARG "${CMAKE_C_COMPILER_ARG1}") + + execute_process( + COMMAND + ${CMAKE_C_COMPILER} ${_C_COMPILER_ARG} -dumpversion + OUTPUT_VARIABLE _COMPILER_VERSION + ) + + string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" + _COMPILER_VERSION "${_COMPILER_VERSION}") + + set(${_OUTPUT_VERSION} ${_COMPILER_VERSION} PARENT_SCOPE) +endfunction() + +if(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) + compiler_dumpversion(GNUCC_VERSION) + if (NOT GNUCC_VERSION EQUAL 34) + set(CMAKE_REQUIRED_FLAGS "-fvisibility=hidden") + check_c_source_compiles( +"void __attribute__((visibility(\"default\"))) test() {} +int main(void){ return 0; } +" WITH_VISIBILITY_HIDDEN) + set(CMAKE_REQUIRED_FLAGS "") + endif (NOT GNUCC_VERSION EQUAL 34) +endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) + +# HEADER FILES +check_include_file(argp.h HAVE_ARGP_H) +check_include_file(pty.h HAVE_PTY_H) +check_include_file(termios.h HAVE_TERMIOS_H) + +if (WIN32) + check_include_files("winsock2.h;ws2tcpip.h;wspiapi.h" HAVE_WSPIAPI_H) + if (NOT HAVE_WSPIAPI_H) + message(STATUS "WARNING: Without wspiapi.h, this build will only work on Windows XP and newer versions") + endif (NOT HAVE_WSPIAPI_H) + check_include_files("winsock2.h;ws2tcpip.h" HAVE_WS2TCPIP_H) + if (HAVE_WSPIAPI_H OR HAVE_WS2TCPIP_H) + set(HAVE_GETADDRINFO TRUE) + set(HAVE_GETHOSTBYNAME TRUE) + endif (HAVE_WSPIAPI_H OR HAVE_WS2TCPIP_H) + + set(HAVE_SELECT TRUE) +endif (WIN32) + +set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) +check_include_file(openssl/aes.h HAVE_OPENSSL_AES_H) + +set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) +check_include_file(openssl/blowfish.h HAVE_OPENSSL_BLOWFISH_H) + +set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) +check_include_file(openssl/des.h HAVE_OPENSSL_DES_H) + +set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) +check_include_file(openssl/ecdh.h HAVE_OPENSSL_ECDH_H) + +set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) +check_include_file(openssl/ec.h HAVE_OPENSSL_EC_H) + +set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) +check_include_file(openssl/ecdsa.h HAVE_OPENSSL_ECDSA_H) + +if (CMAKE_HAVE_PTHREAD_H) + set(HAVE_PTHREAD_H 1) +endif (CMAKE_HAVE_PTHREAD_H) + +if (NOT WITH_GCRYPT) + if (HAVE_OPENSSL_EC_H AND HAVE_OPENSSL_ECDSA_H) + set(HAVE_OPENSSL_ECC 1) + endif (HAVE_OPENSSL_EC_H AND HAVE_OPENSSL_ECDSA_H) + + if (HAVE_OPENSSL_ECC) + set(HAVE_ECC 1) + endif (HAVE_OPENSSL_ECC) +endif (NOT WITH_GCRYPT) + +# FUNCTIONS + +check_function_exists(strncpy HAVE_STRNCPY) +check_function_exists(vsnprintf HAVE_VSNPRINTF) +check_function_exists(snprintf HAVE_SNPRINTF) + +if (WIN32) + check_function_exists(_vsnprintf_s HAVE__VSNPRINTF_S) + check_function_exists(_vsnprintf HAVE__VSNPRINTF) + check_function_exists(_snprintf HAVE__SNPRINTF) + check_function_exists(_snprintf_s HAVE__SNPRINTF_S) +endif (WIN32) + +if (UNIX) + if (NOT LINUX) + # libsocket (Solaris) + check_library_exists(socket getaddrinfo "" HAVE_LIBSOCKET) + if (HAVE_LIBSOCKET) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} socket) + endif (HAVE_LIBSOCKET) + + # libnsl/inet_pton (Solaris) + check_library_exists(nsl inet_pton "" HAVE_LIBNSL) + if (HAVE_LIBNSL) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} nsl) + endif (HAVE_LIBNSL) + + # librt + check_library_exists(rt nanosleep "" HAVE_LIBRT) + endif (NOT LINUX) + + check_library_exists(rt clock_gettime "" HAVE_CLOCK_GETTIME) + if (HAVE_LIBRT OR HAVE_CLOCK_GETTIME) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} rt) + endif (HAVE_LIBRT OR HAVE_CLOCK_GETTIME) + + check_library_exists(util forkpty "" HAVE_LIBUTIL) + check_function_exists(getaddrinfo HAVE_GETADDRINFO) + check_function_exists(poll HAVE_POLL) + check_function_exists(select HAVE_SELECT) + check_function_exists(cfmakeraw HAVE_CFMAKERAW) + check_function_exists(ntohll HAVE_NTOHLL) + check_function_exists(htonll HAVE_HTONLL) + check_function_exists(strtoull HAVE_STRTOULL) + check_function_exists(__strtoull HAVE___STRTOULL) +endif (UNIX) + +set(LIBSSH_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CACHE INTERNAL "libssh required system libraries") + +# LIBRARIES +if (OPENSSL_FOUND) + set(HAVE_LIBCRYPTO 1) +endif (OPENSSL_FOUND) + +if (GCRYPT_FOUND) + set(HAVE_LIBGCRYPT 1) + if (GCRYPT_VERSION VERSION_GREATER "1.4.6") + #set(HAVE_GCRYPT_ECC 1) + #set(HAVE_ECC 1) + endif (GCRYPT_VERSION VERSION_GREATER "1.4.6") +endif (GCRYPT_FOUND) + +if (CMAKE_HAVE_THREADS_LIBRARY) + if (CMAKE_USE_PTHREADS_INIT) + set(HAVE_PTHREAD 1) + endif (CMAKE_USE_PTHREADS_INIT) +endif (CMAKE_HAVE_THREADS_LIBRARY) + +# OPTIONS +if (WITH_DEBUG_CRYPTO) + set(DEBUG_CRYPTO 1) +endif (WITH_DEBUG_CRYPTO) + +if (WITH_DEBUG_CALLTRACE) + set(DEBUG_CALLTRACE 1) +endif (WITH_DEBUG_CALLTRACE) + +# ENDIAN +if (NOT WIN32) + test_big_endian(WORDS_BIGENDIAN) +endif (NOT WIN32) diff --git a/libssh/DefineOptions.cmake b/libssh/DefineOptions.cmake new file mode 100644 index 00000000..ea8265c0 --- /dev/null +++ b/libssh/DefineOptions.cmake @@ -0,0 +1,27 @@ +option(WITH_ZLIB "Build with ZLIB support" ON) +option(WITH_SSH1 "Build with SSH1 support" OFF) +option(WITH_SFTP "Build with SFTP support" ON) +option(WITH_SERVER "Build with SSH server support" ON) +option(WITH_STATIC_LIB "Build with a static library" OFF) +option(WITH_DEBUG_CRYPTO "Build with cryto debug output" OFF) +option(WITH_DEBUG_CALLTRACE "Build with calltrace debug output" ON) +option(WITH_GCRYPT "Compile against libgcrypt" OFF) +option(WITH_PCAP "Compile with Pcap generation support" ON) +option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF) +option(WITH_TESTING "Build with unit tests" OFF) +option(WITH_CLIENT_TESTING "Build with client tests; requires a running sshd" OFF) +option(WITH_BENCHMARKS "Build benchmarks tools" OFF) + +if (WITH_ZLIB) + set(WITH_LIBZ ON) +else (WITH_ZLIB) + set(WITH_LIBZ OFF) +endif (WITH_ZLIB) + +if(WITH_BENCHMARKS) + set(WITH_TESTING ON) +endif(WITH_BENCHMARKS) + +if (WITH_TESTING) + set(WITH_STATIC_LIB ON) +endif (WITH_TESTING) diff --git a/libssh/INSTALL b/libssh/INSTALL new file mode 100644 index 00000000..a772b824 --- /dev/null +++ b/libssh/INSTALL @@ -0,0 +1,99 @@ +# How to build from source + +## Requirements + +### Common requirements + +In order to build libssh, you need to install several components: + +- A C compiler +- [CMake](http://www.cmake.org) >= 2.6.0. +- [openssl](http://www.openssl.org) >= 0.9.8 +or +- [gcrypt](http://www.gnu.org/directory/Security/libgcrypt.html) >= 1.4 + +optional: +- [libz](http://www.zlib.net) >= 1.2 + +Note that these version numbers are version we know works correctly. If you +build and run libssh successfully with an older version, please let us know. + +Windows binaries known to be working: + +- http://www.slproweb.com/products/Win32OpenSSL.html +- http://www.winimage.com/zLibDll/index.html + +We installed them in C:\Program Files + +## Building +First, you need to configure the compilation, using CMake. Go inside the +`build` dir. Create it if it doesn't exist. + +GNU/Linux, MacOS X, MSYS/MinGW: + + cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug .. + make + +On Windows you should choose a makefile gernerator with -G. + +### CMake standard options +Here is a list of the most interesting options provided out of the box by +CMake. + +- CMAKE_BUILD_TYPE: The type of build (can be Debug Release MinSizeRel + RelWithDebInfo) +- CMAKE_INSTALL_PREFIX: The prefix to use when running make install (Default + to /usr/local on GNU/Linux and MacOS X) +- CMAKE_C_COMPILER: The path to the C compiler +- CMAKE_CXX_COMPILER: The path to the C++ compiler + +### CMake options defined for libssh + +Options are defined in the following files: + +- DefineOptions.cmake + +They can be changed with the -D option: + +`cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DWITH_ZLIB=OFF ..` + +### Browsing/editing CMake options + +In addition to passing options on the command line, you can browse and edit +CMake options using `cmakesetup` (Windows), `cmake-gui` or `ccmake` (GNU/Linux +and MacOS X). + +- Go to the build dir +- On Windows: run `cmakesetup` +- On GNU/Linux and MacOS X: run `ccmake ..` + +### Useful Windows options: + +If you have installed OpenSSL or ZLIB in non standard directories, maybe you +want to set: + +OPENSSL_ROOT_DIR + +and + +ZLIB_ROOT_DIR + +## Installing + +If you want to install libssh after compilation run: + + make install + +## Running + +The libssh binary can be found in the `build/libssh` directory. +You can use `build/examples/samplessh` which is a sample client to +test libssh on UNIX. + +## About this document + +This document is written using [Markdown][] syntax, making it possible to +provide usable information in both plain text and HTML format. Whenever +modifying this document please use [Markdown][] syntax. + +[markdown]: http://www.daringfireball.net/projects/markdown diff --git a/libssh/README b/libssh/README new file mode 100644 index 00000000..e3170ce3 --- /dev/null +++ b/libssh/README @@ -0,0 +1,163 @@ + _ _ _ _ + (_) (_) (_) (_) + (_) _ (_) _ _ _ _ _ (_) _ + (_) (_) (_)(_) _ (_)(_) (_)(_) (_)(_) _ + (_) (_) (_) (_) _ (_) _ (_) (_) (_) + (_) (_) (_)(_)(_) (_)(_) (_)(_) (_) (_).org + + The SSH library +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1* Why ? +-_-_-_-_-_ + +Why not ? :) I've began to work on my own implementation of the ssh protocol +because i didn't like the currently public ones. +Not any allowed you to import and use the functions as a powerful library, +and so i worked on a library-based SSH implementation which was non-existing +in the free and open source software world. + + +2* How/Who ? +-_-_-_-_-_-_-_ + +If you downloaded this file, you must know what it is : a library for +accessing ssh client services through C libraries calls in a simple manner. +Everybody can use this software under the terms of the LGPL - see the COPYING +file + +If you ask yourself how to compile libssh, please read INSTALL before anything. + +3* Where ? +-_-_-_-_-_-_ + +http://www.libssh.org + +4* API Changes ! +-_-_-_-_-_-_-_-_-_ + +Changes between 0.4 and 0.5 +--------------------------- + +We use the ssh_ prefix as namespace for every function now. There is a legacy.h +which could be used to get the old function names. + +Changes between 0.3 and 0.4 +--------------------------- + +We changed libssh to be typesafe now: + +SSH_SESSION *session -> ssh_session session +SFTP_SESSION *sftp -> sftp_session sftp +CHANNEL *channel -> ssh_channel channel +STRING *string -> ssh_string string +... + +The options structure has been removed and there is a new function. This +function can set all available options now. You can find the enum in the +header file and it is documented. Example: + +ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + +5* Copyright policy +-_-_-_-_-_-_-_-_-_-_ + +libssh is a project with distributed copyright ownership, which means we prefer +the copyright on parts of libssh to be held by individuals rather than +corporations if possible. There are historical legal reasons for this, but one +of the best ways to explain it is that it’s much easier to work with +individuals who have ownership than corporate legal departments if we ever need +to make reasonable compromises with people using and working with libssh. + +We track the ownership of every part of libssh via git, our source code control +system, so we know the provenance of every piece of code that is committed to +libssh. + +So if possible, if you’re doing libssh changes on behalf of a company who +normally owns all the work you do please get them to assign personal copyright +ownership of your changes to you as an individual, that makes things very easy +for us to work with and avoids bringing corporate legal departments into the +picture. + +If you can’t do this we can still accept patches from you owned by your +employer under a standard employment contract with corporate copyright +ownership. It just requires a simple set-up process first. + +We use a process very similar to the way things are done in the Linux Kernel +community, so it should be very easy to get a sign off from your corporate +legal department. The only changes we’ve made are to accommodate the license we +use, which is LGPLv2 (or later) whereas the Linux kernel uses GPLv2. + +The process is called signing. + +How to sign your work +---------------------- + +Once you have permission to contribute to libssh from your employer, simply +email a copy of the following text from your corporate email address to: + +contributing@libssh.org + +-------------------------------------------------------------------------- +libssh Developer's Certificate of Origin. Version 1.0 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the appropriate + version of the GNU General Public License; or + +(b) The contribution is based upon previous work that, to the best of + my knowledge, is covered under an appropriate open source license + and I have the right under that license to submit that work with + modifications, whether created in whole or in part by me, under + the GNU General Public License, in the appropriate version; or + +(c) The contribution was provided directly to me by some other + person who certified (a) or (b) and I have not modified it. + +(d) I understand and agree that this project and the contribution are + public and that a record of the contribution (including all + metadata and personal information I submit with it, including my + sign-off) is maintained indefinitely and may be redistributed + consistent with the libssh Team's policies and the requirements of + the GNU GPL where they are relevant. + +(e) I am granting this work to this project under the terms of the + GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of + the License, or (at the option of the project) any later version. + +http://www.gnu.org/licenses/lgpl-2.1.html +-------------------------------------------------------------------------- + +We will maintain a copy of that email as a record that you have the rights to +contribute code to libssh under the required licenses whilst working for the +company where the email came from. + +Then when sending in a patch via the normal mechanisms described above, add a +line that states: + + + Signed-off-by: Random J Developer + + +using your real name and the email address you sent the original email you used +to send the libssh Developer’s Certificate of Origin to us (sorry, no +pseudonyms or anonymous contributions.) + +That’s it! Such code can then quite happily contain changes that have copyright +messages such as: + + + (c) Example Corporation. + + +and can be merged into the libssh codebase in the same way as patches from any +other individual. You don’t need to send in a copy of the libssh Developer’s +Certificate of Origin for each patch, or inside each patch. Just the sign-off +message is all that is required once we’ve received the initial email. + +Have fun and happy libssh hacking! + +The libssh Team diff --git a/libssh/SubmittingPatches b/libssh/SubmittingPatches new file mode 100644 index 00000000..66b54e76 --- /dev/null +++ b/libssh/SubmittingPatches @@ -0,0 +1,118 @@ +How to contribute a patch to libssh +==================================== + +Simple, just make the code change, and email it as either a "diff -u" +change, or as a "git format-patch" change against the original source +code to libssh@libssh.org, or attach it to a bug report at +https://red.libssh.org/ + +For larger code changes, breaking the changes up into a set of simple +patches, each of which does a single thing, are much easier to review. +Patch sets like that will most likely have an easier time being merged +into the libssh code than large single patches that make lots of +changes in one large diff. + +Ownership of the contributed code +================================== + +libssh is a project with distributed copyright ownership, which means +we prefer the copyright on parts of libssh to be held by individuals +rather than corporations if possible. There are historical legal +reasons for this, but one of the best ways to explain it is that it's +much easier to work with individuals who have ownership than corporate +legal departments if we ever need to make reasonable compromises with +people using and working with libssh. + +We track the ownership of every part of libssh via http://git.libssh.org, +our source code control system, so we know the provenance of every piece +of code that is committed to libssh. + +So if possible, if you're doing libssh changes on behalf of a company +who normally owns all the work you do please get them to assign +personal copyright ownership of your changes to you as an individual, +that makes things very easy for us to work with and avoids bringing +corporate legal departments into the picture. + +If you can't do this we can still accept patches from you owned by +your employer under a standard employment contract with corporate +copyright ownership. It just requires a simple set-up process first. + +We use a process very similar to the way things are done in the Linux +Kernel community, so it should be very easy to get a sign off from +your corporate legal department. The only changes we've made are to +accommodate the license we use, which is LGPLv2 (or later) whereas the +Linux kernel uses GPLv2. + +The process is called signing. + +How to sign your work +---------------------- + +Once you have permission to contribute to libssh from your employer, simply +email a copy of the following text from your corporate email address to: + +contributing@libssh.org + + + +libssh Developer's Certificate of Origin. Version 1.0 + + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the appropriate + version of the GNU General Public License; or + +(b) The contribution is based upon previous work that, to the best of + my knowledge, is covered under an appropriate open source license + and I have the right under that license to submit that work with + modifications, whether created in whole or in part by me, under + the GNU General Public License, in the appropriate version; or + +(c) The contribution was provided directly to me by some other + person who certified (a) or (b) and I have not modified it. + +(d) I understand and agree that this project and the contribution are + public and that a record of the contribution (including all + metadata and personal information I submit with it, including my + sign-off) is maintained indefinitely and may be redistributed + consistent with the libssh Team's policies and the requirements of + the GNU GPL where they are relevant. + +(e) I am granting this work to this project under the terms of the + GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of + the License, or (at the option of the project) any later version. + + http://www.gnu.org/licenses/lgpl-2.1.html + + +We will maintain a copy of that email as a record that you have the +rights to contribute code to libssh under the required licenses whilst +working for the company where the email came from. + +Then when sending in a patch via the normal mechanisms described +above, add a line that states: + + Signed-off-by: Random J Developer + +using your real name and the email address you sent the original email +you used to send the libssh Developer's Certificate of Origin to us +(sorry, no pseudonyms or anonymous contributions.) + +That's it! Such code can then quite happily contain changes that have +copyright messages such as: + + (c) Example Corporation. + +and can be merged into the libssh codebase in the same way as patches +from any other individual. You don't need to send in a copy of the +libssh Developer's Certificate of Origin for each patch, or inside each +patch. Just the sign-off message is all that is required once we've +received the initial email. + +Have fun and happy libssh hacking ! + +The libssh Team + diff --git a/libssh/build/build_make.sh b/libssh/build/build_make.sh new file mode 100755 index 00000000..ba03c99c --- /dev/null +++ b/libssh/build/build_make.sh @@ -0,0 +1,197 @@ +#!/bin/bash +# +# Last Change: 2008-06-18 14:13:46 +# +# Script to build libssh on UNIX. +# +# Copyright (c) 2006-2007 Andreas Schneider +# + +SOURCE_DIR=".." + +LANG=C +export LANG + +SCRIPT="$0" +COUNT=0 +while [ -L "${SCRIPT}" ] +do + SCRIPT=$(readlink ${SCRIPT}) + COUNT=$(expr ${COUNT} + 1) + if [ ${COUNT} -gt 100 ]; then + echo "Too many symbolic links" + exit 1 + fi +done +BUILDDIR=$(dirname ${SCRIPT}) + +cleanup_and_exit () { + if test "$1" = 0 -o -z "$1" ; then + exit 0 + else + exit $1 + fi +} + +function configure() { + if [ -n "${CMAKEDIR}" ]; then + ${CMAKEDIR}/bin/cmake "$@" ${SOURCE_DIR} || cleanup_and_exit $? + else + cmake "$@" ${SOURCE_DIR} || cleanup_and_exit $? + fi +} + +function compile() { + if [ -f /proc/cpuinfo ]; then + CPUCOUNT=$(grep -c processor /proc/cpuinfo) + elif test `uname` = "SunOS" ; then + CPUCOUNT=$(psrinfo -p) + else + CPUCOUNT="1" + fi + + if [ "${CPUCOUNT}" -gt "1" ]; then + ${MAKE} -j${CPUCOUNT} $1 || cleanup_and_exit $? + else + ${MAKE} $1 || exit $? + fi +} + +function clean_build_dir() { + find ! -path "*.svn*" ! -name "*.bat" ! -name "*.sh" ! -name "." -print0 | xargs -0 rm -rf +} + +function usage () { +echo "Usage: `basename $0` [--prefix /install_prefix|--build [debug|final]|--clean|--verbose|--libsuffix (32|64)|--help|--clang|--cmakedir /directory|--make +(gmake|make)|--ccompiler (gcc|cc)|--withstaticlib|--unittesting|--clientunittesting|--withssh1|--withserver]" + cleanup_and_exit +} + +cd ${BUILDDIR} + +# the default CMake options: +OPTIONS="--graphviz=${BUILDDIR}/libssh.dot" + +# the default 'make' utility: +MAKE="make" + +while test -n "$1"; do + PARAM="$1" + ARG="$2" + shift + case ${PARAM} in + *-*=*) + ARG=${PARAM#*=} + PARAM=${PARAM%%=*} + set -- "----noarg=${PARAM}" "$@" + esac + case ${PARAM} in + *-help|-h) + #echo_help + usage + cleanup_and_exit + ;; + *-build) + DOMAKE="1" + BUILD_TYPE="${ARG}" + test -n "${BUILD_TYPE}" && shift + ;; + *-clean) + clean_build_dir + cleanup_and_exit + ;; + *-clang) + OPTIONS="${OPTIONS} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++" + ;; + *-verbose) + DOVERBOSE="1" + ;; + *-memtest) + OPTIONS="${OPTIONS} -DMEM_NULL_TESTS=ON" + ;; + *-libsuffix) + OPTIONS="${OPTIONS} -DLIB_SUFFIX=${ARG}" + shift + ;; + *-prefix) + OPTIONS="${OPTIONS} -DCMAKE_INSTALL_PREFIX=${ARG}" + shift + ;; + *-sysconfdir) + OPTIONS="${OPTIONS} -DSYSCONF_INSTALL_DIR=${ARG}" + shift + ;; + *-cmakedir) + CMAKEDIR="${ARG}" + shift + ;; + *-make) + MAKE="${ARG}" + shift + ;; + *-ccompiler) + OPTIONS="${OPTIONS} -DCMAKE_C_COMPILER=${ARG}" + shift + ;; + *-withstaticlib) + OPTIONS="${OPTIONS} -DWITH_STATIC_LIB=ON" + ;; + *-unittesting) + OPTIONS="${OPTIONS} -DWITH_TESTING=ON" + ;; + *-clientunittesting) + OPTIONS="${OPTIONS} -DWITH_CLIENT_TESTING=ON" + ;; + *-withssh1) + OPTIONS="${OPTIONS} -DWITH_SSH1=ON" + ;; + *-withserver) + OPTIONS="${OPTIONS} -DWITH_SERVER=ON" + ;; + ----noarg) + echo "$ARG does not take an argument" + cleanup_and_exit + ;; + -*) + echo Unknown Option "$PARAM". Exit. + cleanup_and_exit 1 + ;; + *) + usage + ;; + esac +done + +if [ "${DOMAKE}" == "1" ]; then + OPTIONS="${OPTIONS} -DCMAKE_BUILD_TYPE=${BUILD_TYPE}" +fi + +if [ -n "${DOVERBOSE}" ]; then + OPTIONS="${OPTIONS} -DCMAKE_VERBOSE_MAKEFILE=1" +else + OPTIONS="${OPTIONS} -DCMAKE_VERBOSE_MAKEFILE=0" +fi + +test -f "${BUILDDIR}/.build.log" && rm -f ${BUILDDIR}/.build.log +touch ${BUILDDIR}/.build.log +# log everything from here to .build.log +exec 1> >(exec -a 'build logging tee' tee -a ${BUILDDIR}/.build.log) 2>&1 +echo "${HOST} started build at $(date)." +echo + +configure ${OPTIONS} "$@" + +if [ -n "${DOMAKE}" ]; then + test -n "${DOVERBOSE}" && compile VERBOSE=1 || compile +fi + +DOT=$(which dot 2>/dev/null) +if [ -n "${DOT}" ]; then + ${DOT} -Tpng -o${BUILDDIR}/libssh.png ${BUILDDIR}/libssh.dot + ${DOT} -Tsvg -o${BUILDDIR}/libssh.svg ${BUILDDIR}/libssh.dot +fi + +exec >&0 2>&0 # so that the logging tee finishes +sleep 1 # wait till tee terminates + +cleanup_and_exit 0 diff --git a/libssh/cmake/Modules/AddCMockaTest.cmake b/libssh/cmake/Modules/AddCMockaTest.cmake new file mode 100644 index 00000000..b2d1ca8a --- /dev/null +++ b/libssh/cmake/Modules/AddCMockaTest.cmake @@ -0,0 +1,23 @@ +# - ADD_CHECK_TEST(test_name test_source linklib1 ... linklibN) + +# Copyright (c) 2007 Daniel Gollub +# Copyright (c) 2007-2010 Andreas Schneider +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +enable_testing() +include(CTest) + +if(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW) + set(CMAKE_C_FLAGS_PROFILING "-g -O0 -Wall -W -Wshadow -Wunused-variable -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers -Wwrite-strings -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Compiler Flags") + set(CMAKE_SHARED_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") + set(CMAKE_MODULE_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") + set(CMAKE_EXEC_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") +endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW) + +function (ADD_CMOCKA_TEST _testName _testSource) + add_executable(${_testName} ${_testSource}) + target_link_libraries(${_testName} ${ARGN}) + add_test(${_testName} ${CMAKE_CURRENT_BINARY_DIR}/${_testName}) +endfunction (ADD_CMOCKA_TEST) diff --git a/libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS b/libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS new file mode 100644 index 00000000..4b417765 --- /dev/null +++ b/libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libssh/cmake/Modules/CheckCCompilerFlagSSP.cmake b/libssh/cmake/Modules/CheckCCompilerFlagSSP.cmake new file mode 100644 index 00000000..2fe43954 --- /dev/null +++ b/libssh/cmake/Modules/CheckCCompilerFlagSSP.cmake @@ -0,0 +1,26 @@ +# - Check whether the C compiler supports a given flag in the +# context of a stack checking compiler option. + +# CHECK_C_COMPILER_FLAG_SSP(FLAG VARIABLE) +# +# FLAG - the compiler flag +# VARIABLE - variable to store the result +# +# This actually calls check_c_source_compiles. +# See help for CheckCSourceCompiles for a listing of variables +# that can modify the build. + +# Copyright (c) 2006, Alexander Neundorf, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +include(CheckCSourceCompiles) + +function(CHECK_C_COMPILER_FLAG_SSP _FLAG _RESULT) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") + check_c_source_compiles("int main(int argc, char **argv) { char buffer[256]; return buffer[argc]=0;}" ${_RESULT}) + set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") +endfunction(CHECK_C_COMPILER_FLAG_SSP) diff --git a/libssh/cmake/Modules/DefineCMakeDefaults.cmake b/libssh/cmake/Modules/DefineCMakeDefaults.cmake new file mode 100644 index 00000000..72893c3c --- /dev/null +++ b/libssh/cmake/Modules/DefineCMakeDefaults.cmake @@ -0,0 +1,27 @@ +# Always include srcdir and builddir in include path +# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY} in +# about every subdir +# since cmake 2.4.0 +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Put the include dirs which are in the source or build tree +# before all other include dirs, so the headers in the sources +# are prefered over the already installed ones +# since cmake 2.4.1 +set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) + +# Use colored output +# since cmake 2.4.0 +set(CMAKE_COLOR_MAKEFILE ON) + +# Define the generic version of the libraries here +set(GENERIC_LIB_VERSION "0.1.0") +set(GENERIC_LIB_SOVERSION "0") + +# Set the default build type to release with debug info +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE RelWithDebInfo + CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." + ) +endif (NOT CMAKE_BUILD_TYPE) diff --git a/libssh/cmake/Modules/DefineCompilerFlags.cmake b/libssh/cmake/Modules/DefineCompilerFlags.cmake new file mode 100644 index 00000000..582ea1ca --- /dev/null +++ b/libssh/cmake/Modules/DefineCompilerFlags.cmake @@ -0,0 +1,77 @@ +# define system dependent compiler flags + +include(CheckCCompilerFlag) +include(CheckCCompilerFlagSSP) + +if (UNIX AND NOT WIN32) + # + # Define GNUCC compiler flags + # + if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") + + # add -Wconversion ? + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes -Wdeclaration-after-statement") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused -Wfloat-equal -Wpointer-arith -Wwrite-strings -Wformat-security") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-format-attribute") + + # with -fPIC + check_c_compiler_flag("-fPIC" WITH_FPIC) + if (WITH_FPIC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + endif (WITH_FPIC) + + check_c_compiler_flag_ssp("-fstack-protector" WITH_STACK_PROTECTOR) + if (WITH_STACK_PROTECTOR) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector") + endif (WITH_STACK_PROTECTOR) + + if (CMAKE_BUILD_TYPE) + string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) + if (NOT CMAKE_BUILD_TYPE_LOWER MATCHES debug) + check_c_compiler_flag("-D_FORTIFY_SOURCE=2" WITH_FORTIFY_SOURCE) + if (WITH_FORTIFY_SOURCE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2") + endif (WITH_FORTIFY_SOURCE) + endif() + endif() + endif (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") + + # + # Check for large filesystem support + # + if (CMAKE_SIZEOF_VOID_P MATCHES "8") + # with large file support + execute_process( + COMMAND + getconf LFS64_CFLAGS + OUTPUT_VARIABLE + _lfs_CFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + else (CMAKE_SIZEOF_VOID_P MATCHES "8") + # with large file support + execute_process( + COMMAND + getconf LFS_CFLAGS + OUTPUT_VARIABLE + _lfs_CFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif (CMAKE_SIZEOF_VOID_P MATCHES "8") + if (_lfs_CFLAGS) + string(REGEX REPLACE "[\r\n]" " " "${_lfs_CFLAGS}" "${${_lfs_CFLAGS}}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_lfs_CFLAGS}") + endif (_lfs_CFLAGS) + +endif (UNIX AND NOT WIN32) + +if (MSVC) + # Use secure functions by defaualt and suppress warnings about + #"deprecated" functions + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") +endif (MSVC) diff --git a/libssh/cmake/Modules/DefineInstallationPaths.cmake b/libssh/cmake/Modules/DefineInstallationPaths.cmake new file mode 100644 index 00000000..88e08cad --- /dev/null +++ b/libssh/cmake/Modules/DefineInstallationPaths.cmake @@ -0,0 +1,109 @@ +if (UNIX OR OS2) + IF (NOT APPLICATION_NAME) + MESSAGE(STATUS "${PROJECT_NAME} is used as APPLICATION_NAME") + SET(APPLICATION_NAME ${PROJECT_NAME}) + ENDIF (NOT APPLICATION_NAME) + + # Suffix for Linux + SET(LIB_SUFFIX + CACHE STRING "Define suffix of directory name (32/64)" + ) + + SET(EXEC_INSTALL_PREFIX + "${CMAKE_INSTALL_PREFIX}" + CACHE PATH "Base directory for executables and libraries" + ) + SET(SHARE_INSTALL_PREFIX + "${CMAKE_INSTALL_PREFIX}/share" + CACHE PATH "Base directory for files which go to share/" + ) + SET(DATA_INSTALL_PREFIX + "${SHARE_INSTALL_PREFIX}/${APPLICATION_NAME}" + CACHE PATH "The parent directory where applications can install their data") + + # The following are directories where stuff will be installed to + SET(BIN_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/bin" + CACHE PATH "The ${APPLICATION_NAME} binary install dir (default prefix/bin)" + ) + SET(SBIN_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/sbin" + CACHE PATH "The ${APPLICATION_NAME} sbin install dir (default prefix/sbin)" + ) + SET(LIB_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" + CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/lib)" + ) + SET(LIBEXEC_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/libexec" + CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/libexec)" + ) + SET(PLUGIN_INSTALL_DIR + "${LIB_INSTALL_DIR}/${APPLICATION_NAME}" + CACHE PATH "The subdirectory relative to the install prefix where plugins will be installed (default is prefix/lib/${APPLICATION_NAME})" + ) + SET(INCLUDE_INSTALL_DIR + "${CMAKE_INSTALL_PREFIX}/include" + CACHE PATH "The subdirectory to the header prefix (default prefix/include)" + ) + + set(CMAKE_INSTALL_DIR + "${LIB_INSTALL_DIR}/cmake" + CACHE PATH "The subdirectory to install cmake config files") + + SET(DATA_INSTALL_DIR + "${DATA_INSTALL_PREFIX}" + CACHE PATH "The parent directory where applications can install their data (default prefix/share/${APPLICATION_NAME})" + ) + SET(HTML_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/doc/HTML" + CACHE PATH "The HTML install dir for documentation (default data/doc/html)" + ) + SET(ICON_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/icons" + CACHE PATH "The icon install dir (default data/icons/)" + ) + SET(SOUND_INSTALL_DIR + "${DATA_INSTALL_PREFIX}/sounds" + CACHE PATH "The install dir for sound files (default data/sounds)" + ) + + SET(LOCALE_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/locale" + CACHE PATH "The install dir for translations (default prefix/share/locale)" + ) + + SET(XDG_APPS_DIR + "${SHARE_INSTALL_PREFIX}/applications/" + CACHE PATH "The XDG apps dir" + ) + SET(XDG_DIRECTORY_DIR + "${SHARE_INSTALL_PREFIX}/desktop-directories" + CACHE PATH "The XDG directory" + ) + + SET(SYSCONF_INSTALL_DIR + "${EXEC_INSTALL_PREFIX}/etc" + CACHE PATH "The ${APPLICATION_NAME} sysconfig install dir (default prefix/etc)" + ) + SET(MAN_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/man" + CACHE PATH "The ${APPLICATION_NAME} man install dir (default prefix/man)" + ) + SET(INFO_INSTALL_DIR + "${SHARE_INSTALL_PREFIX}/info" + CACHE PATH "The ${APPLICATION_NAME} info install dir (default prefix/info)" + ) +else() + # Same same + set(BIN_INSTALL_DIR "bin" CACHE PATH "-") + set(SBIN_INSTALL_DIR "sbin" CACHE PATH "-") + set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "-") + set(INCLUDE_INSTALL_DIR "include" CACHE PATH "-") + set(CMAKE_INSTALL_DIR "CMake" CACHE PATH "-") + set(PLUGIN_INSTALL_DIR "plugins" CACHE PATH "-") + set(HTML_INSTALL_DIR "doc/HTML" CACHE PATH "-") + set(ICON_INSTALL_DIR "icons" CACHE PATH "-") + set(SOUND_INSTALL_DIR "soudns" CACHE PATH "-") + set(LOCALE_INSTALL_DIR "lang" CACHE PATH "-") +endif () diff --git a/libssh/cmake/Modules/DefinePlatformDefaults.cmake b/libssh/cmake/Modules/DefinePlatformDefaults.cmake new file mode 100644 index 00000000..502d936b --- /dev/null +++ b/libssh/cmake/Modules/DefinePlatformDefaults.cmake @@ -0,0 +1,28 @@ +# Set system vars + +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + set(LINUX TRUE) +endif(CMAKE_SYSTEM_NAME MATCHES "Linux") + +if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + set(FREEBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + set(OPENBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + set(NETBSD TRUE) + set(BSD TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") + +if (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + set(SOLARIS TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + +if (CMAKE_SYSTEM_NAME MATCHES "OS2") + set(OS2 TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "OS2") diff --git a/libssh/cmake/Modules/FindArgp.cmake b/libssh/cmake/Modules/FindArgp.cmake new file mode 100644 index 00000000..aa228557 --- /dev/null +++ b/libssh/cmake/Modules/FindArgp.cmake @@ -0,0 +1,60 @@ +# - Try to find Argp +# Once done this will define +# +# ARGP_FOUND - system has Argp +# ARGP_INCLUDE_DIRS - the Argp include directory +# ARGP_LIBRARIES - Link these to use Argp +# ARGP_DEFINITIONS - Compiler switches required for using Argp +# +# Copyright (c) 2010 Andreas Schneider +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + + +if (ARGP_LIBRARIES AND ARGP_INCLUDE_DIRS) + # in cache already + set(ARGP_FOUND TRUE) +else (ARGP_LIBRARIES AND ARGP_INCLUDE_DIRS) + + find_path(ARGP_INCLUDE_DIR + NAMES + argp.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ) + + find_library(ARGP_LIBRARY + NAMES + argp + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + + set(ARGP_INCLUDE_DIRS + ${ARGP_INCLUDE_DIR} + ) + + if (ARGP_LIBRARY) + set(ARGP_LIBRARIES + ${ARGP_LIBRARIES} + ${ARGP_LIBRARY} + ) + endif (ARGP_LIBRARY) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Argp DEFAULT_MSG ARGP_LIBRARIES ARGP_INCLUDE_DIRS) + + # show the ARGP_INCLUDE_DIRS and ARGP_LIBRARIES variables only in the advanced view + mark_as_advanced(ARGP_INCLUDE_DIRS ARGP_LIBRARIES) + +endif (ARGP_LIBRARIES AND ARGP_INCLUDE_DIRS) + diff --git a/libssh/cmake/Modules/FindCMocka.cmake b/libssh/cmake/Modules/FindCMocka.cmake new file mode 100644 index 00000000..2dd9fc5f --- /dev/null +++ b/libssh/cmake/Modules/FindCMocka.cmake @@ -0,0 +1,49 @@ +# - Try to find CMocka +# Once done this will define +# +# CMOCKA_ROOT_DIR - Set this variable to the root installation of CMocka +# +# Read-Only variables: +# CMOCKA_FOUND - system has CMocka +# CMOCKA_INCLUDE_DIR - the CMocka include directory +# CMOCKA_LIBRARIES - Link these to use CMocka +# CMOCKA_DEFINITIONS - Compiler switches required for using CMocka +# +#============================================================================= +# Copyright (c) 2011-2012 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# + +find_path(CMOCKA_INCLUDE_DIR + NAMES + cmocka.h + PATHS + ${CMOCKA_ROOT_DIR}/include +) + +find_library(CMOCKA_LIBRARY + NAMES + cmocka + PATHS + ${CMOCKA_ROOT_DIR}/include +) + +if (CMOCKA_LIBRARY) + set(CMOCKA_LIBRARIES + ${CMOCKA_LIBRARIES} + ${CMOCKA_LIBRARY} + ) +endif (CMOCKA_LIBRARY) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CMocka DEFAULT_MSG CMOCKA_LIBRARIES CMOCKA_INCLUDE_DIR) + +# show the CMOCKA_INCLUDE_DIR and CMOCKA_LIBRARIES variables only in the advanced view +mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARIES) diff --git a/libssh/cmake/Modules/FindGCrypt.cmake b/libssh/cmake/Modules/FindGCrypt.cmake new file mode 100644 index 00000000..5f1fe40b --- /dev/null +++ b/libssh/cmake/Modules/FindGCrypt.cmake @@ -0,0 +1,75 @@ +# - Try to find GCrypt +# Once done this will define +# +# GCRYPT_FOUND - system has GCrypt +# GCRYPT_INCLUDE_DIRS - the GCrypt include directory +# GCRYPT_LIBRARIES - Link these to use GCrypt +# GCRYPT_DEFINITIONS - Compiler switches required for using GCrypt +# +#============================================================================= +# Copyright (c) 2009-2012 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# + +set(_GCRYPT_ROOT_HINTS + $ENV{GCRYTPT_ROOT_DIR} + ${GCRYPT_ROOT_DIR}) + +set(_GCRYPT_ROOT_PATHS + "$ENV{PROGRAMFILES}/libgcrypt") + +set(_GCRYPT_ROOT_HINTS_AND_PATHS + HINTS ${_GCRYPT_ROOT_HINTS} + PATHS ${_GCRYPT_ROOT_PATHS}) + + +find_path(GCRYPT_INCLUDE_DIR + NAMES + gcrypt.h + HINTS + ${_GCRYPT_ROOT_HINTS_AND_PATHS} +) + +find_library(GCRYPT_LIBRARY + NAMES + gcrypt + gcrypt11 + libgcrypt-11 + HINTS + ${_GCRYPT_ROOT_HINTS_AND_PATHS} +) +set(GCRYPT_LIBRARIES ${GCRYPT_LIBRARY}) + +if (GCRYPT_INCLUDE_DIR) + file(STRINGS "${GCRYPT_INCLUDE_DIR}/gcrypt.h" _gcrypt_version_str REGEX "^#define GCRYPT_VERSION \"[0-9]+.[0-9]+.[0-9]+\"") + + string(REGEX REPLACE "^.*GCRYPT_VERSION.*([0-9]+.[0-9]+.[0-9]+).*" "\\1" GCRYPT_VERSION "${_gcrypt_version_str}") +endif (GCRYPT_INCLUDE_DIR) + +include(FindPackageHandleStandardArgs) +if (GCRYPT_VERSION) + find_package_handle_standard_args(GCrypt + REQUIRED_VARS + GCRYPT_INCLUDE_DIR + GCRYPT_LIBRARIES + VERSION_VAR + GCRYPT_VERSION + FAIL_MESSAGE + "Could NOT find GCrypt, try to set the path to GCrypt root folder in the system variable GCRYPT_ROOT_DIR" + ) +else (GCRYPT_VERSION) + find_package_handle_standard_args(GCrypt + "Could NOT find GCrypt, try to set the path to GCrypt root folder in the system variable GCRYPT_ROOT_DIR" + GCRYPT_INCLUDE_DIR + GCRYPT_LIBRARIES) +endif (GCRYPT_VERSION) + +# show the GCRYPT_INCLUDE_DIRS and GCRYPT_LIBRARIES variables only in the advanced view +mark_as_advanced(GCRYPT_INCLUDE_DIR GCRYPT_LIBRARIES) diff --git a/libssh/cmake/Modules/FindNSIS.cmake b/libssh/cmake/Modules/FindNSIS.cmake new file mode 100644 index 00000000..98a17c78 --- /dev/null +++ b/libssh/cmake/Modules/FindNSIS.cmake @@ -0,0 +1,39 @@ +# - Try to find NSIS +# Once done this will define +# +# NSIS_ROOT_DIR - Set this variable to the root installation of ZLIB +# +# Read-Only variables: +# NSIS_FOUND - system has NSIS +# NSIS_MAKE - NSIS creator executable +# +#============================================================================= +# Copyright (c) 2010-2011 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# + +set(_NSIS_ROOT_PATHS + C:/NSIS/Bin + "$ENV{PROGRAMFILES}/NSIS" +) + +find_program(NSIS_MAKE + NAMES + makensis + PATHS + ${NSIS_ROOT_PATH} + ${NSIS_ROOT_PATH}/Bin + ${_NSIS_ROOT_PATHS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(NSIS DEFAULT_MSG NSIS_MAKE) + +mark_as_advanced(NSIS_MAKE) diff --git a/libssh/cmake/Modules/FindOpenSSL.cmake b/libssh/cmake/Modules/FindOpenSSL.cmake new file mode 100644 index 00000000..565190c6 --- /dev/null +++ b/libssh/cmake/Modules/FindOpenSSL.cmake @@ -0,0 +1,208 @@ +# - Try to find OpenSSL +# Once done this will define +# +# OPENSSL_ROOT_DIR - Set this variable to the root installation of OpenSSL +# +# Read-Only variables: +# OPENSSL_FOUND - system has OpenSSL +# OPENSSL_INCLUDE_DIRS - the OpenSSL include directory +# OPENSSL_LIBRARIES - Link these to use OpenSSL +# OPENSSL_DEFINITIONS - Compiler switches required for using OpenSSL +# +#============================================================================= +# Copyright (c) 2006-2009 Kitware, Inc. +# Copyright (c) 2006 Alexander Neundorf +# Copyright (c) 2009-2010 Mathieu Malaterre +# Copyright (c) 2011 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# + +if (OPENSSL_LIBRARIES AND OPENSSL_INCLUDE_DIRS) + # in cache already + set(OPENSSL_FOUND TRUE) +else (OPENSSL_LIBRARIES AND OPENSSL_INCLUDE_DIRS) + + if (UNIX) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(_OPENSSL openssl) + endif (PKG_CONFIG_FOUND) + endif (UNIX) + + # http://www.slproweb.com/products/Win32OpenSSL.html + set(_OPENSSL_ROOT_HINTS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" + ) + + set(_OPENSSL_ROOT_PATHS + "C:/OpenSSL/" + "C:/OpenSSL-Win32/" + "C:/OpenSSL-Win64/" + "$ENV{PROGRAMFILES}/OpenSSL" + "$ENV{PROGRAMFILES}/OpenSSL-Win32" + "$ENV{PROGRAMFILES}/OpenSSL-Win64" + ) + + find_path(OPENSSL_ROOT_DIR + NAMES + include/openssl/ssl.h + HINTS + ${_OPENSSL_ROOT_HINTS} + PATHS + ${_OPENSSL_ROOT_PATHS} + ) + mark_as_advanced(OPENSSL_ROOT_DIR) + + find_path(OPENSSL_INCLUDE_DIR + NAMES + openssl/ssl.h + PATHS + /usr/local/include + /opt/local/include + /sw/include + /usr/lib/sfw/include + ${OPENSSL_ROOT_DIR}/include + ) + + set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) + mark_as_advanced(OPENSSL_INCLUDE_DIRS) + + if (WIN32 AND NOT CYGWIN) + # MINGW should go here too + if (MSVC) + # /MD and /MDd are the standard values - if someone wants to use + # others, the libnames have to change here too + # use also ssl and ssleay32 in debug as fallback for openssl < 0.9.8b + # TODO: handle /MT and static lib + # In Visual C++ naming convention each of these four kinds of Windows libraries has it's standard suffix: + # * MD for dynamic-release + # * MDd for dynamic-debug + # * MT for static-release + # * MTd for static-debug + + # Implementation details: + # We are using the libraries located in the VC subdir instead of the parent directory eventhough : + # libeay32MD.lib is identical to ../libeay32.lib, and + # ssleay32MD.lib is identical to ../ssleay32.lib + find_library(LIB_EAY_DEBUG + NAMES + libeay32MDd + libeay32 + PATHS + ${OPENSSL_ROOT_DIR}/lib/VC + ) + + find_library(LIB_EAY_RELEASE + NAMES + libeay32MD + libeay32 + PATHS + ${OPENSSL_ROOT_DIR}/lib/VC + ) + + find_library(SSL_EAY_DEBUG + NAMES + ssleay32MDd + ssleay32 + ssl + PATHS ${OPENSSL_ROOT_DIR}/lib/VC + ) + + find_library(SSL_EAY_RELEASE + NAMES + ssleay32MD + ssleay32 + ssl + PATHS + ${OPENSSL_ROOT_DIR}/lib/VC + ) + + if (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) + set(OPENSSL_LIBRARIES + optimized ${SSL_EAY_RELEASE} debug ${SSL_EAY_DEBUG} + optimized ${LIB_EAY_RELEASE} debug ${LIB_EAY_DEBUG} + ) + else (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) + set( OPENSSL_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} ) + endif (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) + + mark_as_advanced(SSL_EAY_DEBUG SSL_EAY_RELEASE) + mark_as_advanced(LIB_EAY_DEBUG LIB_EAY_RELEASE) + elseif (MINGW) + # same player, for MingW + find_library(LIB_EAY + NAMES + libeay32 + PATHS + ${OPENSSL_ROOT_DIR}/lib/MinGW + ) + + find_library(SSL_EAY + NAMES + ssleay32 + PATHS + ${OPENSSL_ROOT_DIR}/lib/MinGW + ) + + mark_as_advanced(SSL_EAY LIB_EAY) + set(OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY}) + else(MSVC) + # Not sure what to pick for -say- intel, let's use the toplevel ones and hope someone report issues: + find_library(LIB_EAY + NAMES + libeay32 + PATHS + ${OPENSSL_ROOT_DIR}/lib + ) + + find_library(SSL_EAY + NAMES + ssleay32 + PATHS + ${OPENSSL_ROOT_DIR}/lib + ) + + mark_as_advanced(SSL_EAY LIB_EAY) + set(OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY}) + endif(MSVC) + else (WIN32 AND NOT CYGWIN) + find_library(OPENSSL_SSL_LIBRARIES + NAMES + ssl + ssleay32 + ssleay32MD + PATHS + ${_OPENSSL_LIBDIR} + /opt/local/lib + /sw/lib + /usr/sfw/lib/64 + /usr/sfw/lib + ) + + find_library(OPENSSL_CRYPTO_LIBRARIES + NAMES + crypto + PATHS + ${_OPENSSL_LIBDIR} + /opt/local/lib + /sw/lib + /usr/sfw/lib/64 + /usr/sfw/lib + ) + + mark_as_advanced(OPENSSL_CRYPTO_LIBRARIES OPENSSL_SSL_LIBRARIES) + set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES}) + endif (WIN32 AND NOT CYGWIN) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(OpenSSL DEFAULT_MSG OPENSSL_LIBRARIES OPENSSL_INCLUDE_DIRS) + +endif (OPENSSL_LIBRARIES AND OPENSSL_INCLUDE_DIRS) diff --git a/libssh/cmake/Modules/FindZLIB.cmake b/libssh/cmake/Modules/FindZLIB.cmake new file mode 100644 index 00000000..f68207e4 --- /dev/null +++ b/libssh/cmake/Modules/FindZLIB.cmake @@ -0,0 +1,120 @@ +# - Try to find ZLIB +# Once done this will define +# +# ZLIB_ROOT_DIR - Set this variable to the root installation of ZLIB +# +# Read-Only variables: +# ZLIB_FOUND - system has ZLIB +# ZLIB_INCLUDE_DIRS - the ZLIB include directory +# ZLIB_LIBRARIES - Link these to use ZLIB +# +# ZLIB_VERSION_STRING - The version of zlib found (x.y.z) +# ZLIB_VERSION_MAJOR - The major version of zlib +# ZLIB_VERSION_MINOR - The minor version of zlib +# ZLIB_VERSION_PATCH - The patch version of zlib +# ZLIB_VERSION_TWEAK - The tweak version of zlib +# +# The following variable are provided for backward compatibility +# +# ZLIB_MAJOR_VERSION - The major version of zlib +# ZLIB_MINOR_VERSION - The minor version of zlib +# ZLIB_PATCH_VERSION - The patch version of zlib +# +#============================================================================= +# Copyright (c) 2001-2009 Kitware, Inc. +# Copyright (c) 2011 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# + +if (ZLIB_LIBRARIES AND ZLIB_INCLUDE_DIRS) + # in cache already + set(ZLIB_FOUND TRUE) +else (ZLIB_LIBRARIES AND ZLIB_INCLUDE_DIRS) + + set(_ZLIB_ROOT_HINTS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]/include" + ) + + set(_ZLIB_ROOT_PATHS + "$ENV{PROGRAMFILES}/zlib" + ) + + find_path(ZLIB_ROOT_DIR + NAMES + include/zlib.h + HINTS + ${_ZLIB_ROOT_HINTS} + PATHS + ${_ZLIB_ROOT_PATHS} + ) + mark_as_advanced(ZLIB_ROOT_DIR) + + # check for header file + find_path(ZLIB_INCLUDE_DIR + NAMES + zlib.h + PATHS + /usr/local/include + /opt/local/include + /sw/include + /usr/lib/sfw/include + ${ZLIB_ROOT_DIR}/include + ) + mark_as_advanced(ZLIB_INCLUDE_DIR) + + # check version number + if (ZLIB_INCLUDE_DIR AND EXISTS "${ZLIB_INCLUDE_DIR}/zlib.h") + file(STRINGS "${ZLIB_INCLUDE_DIR}/zlib.h" ZLIB_H REGEX "^#define ZLIB_VERSION \"[^\"]*\"$") + + string(REGEX REPLACE "^.*ZLIB_VERSION \"([0-9]+).*$" "\\1" ZLIB_VERSION_MAJOR "${ZLIB_H}") + string(REGEX REPLACE "^.*ZLIB_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" ZLIB_VERSION_MINOR "${ZLIB_H}") + string(REGEX REPLACE "^.*ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" ZLIB_VERSION_PATCH "${ZLIB_H}") + + set(ZLIB_VERSION_STRING "${ZLIB_VERSION_MAJOR}.${ZLIB_VERSION_MINOR}.${ZLIB_VERSION_PATCH}") + + # only append a TWEAK version if it exists: + set(ZLIB_VERSION_TWEAK "") + if ("${ZLIB_H}" MATCHES "^.*ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+).*$") + set(ZLIB_VERSION_TWEAK "${CMAKE_MATCH_1}") + set(ZLIB_VERSION_STRING "${ZLIB_VERSION_STRING}.${ZLIB_VERSION_TWEAK}") + endif ("${ZLIB_H}" MATCHES "^.*ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+).*$") + + set(ZLIB_MAJOR_VERSION "${ZLIB_VERSION_MAJOR}") + set(ZLIB_MINOR_VERSION "${ZLIB_VERSION_MINOR}") + set(ZLIB_PATCH_VERSION "${ZLIB_VERSION_PATCH}") + endif (ZLIB_INCLUDE_DIR AND EXISTS "${ZLIB_INCLUDE_DIR}/zlib.h") + + find_library(ZLIB_LIBRARY + NAMES + z + zdll + zlib + zlib1 + zlibd + PATHS + /usr/local/lib + /opt/local/lib + /sw/lib + /usr/sfw/lib/64 + /usr/sfw/lib + ${ZLIB_ROOT_DIR}/lib + ) + mark_as_advanced(ZLIB_LIBRARY) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(ZLIB DEFAULT_MSG ZLIB_INCLUDE_DIR ZLIB_LIBRARY) + #find_package_handle_standard_args(ZLIB REQUIRED_VARS ZLIB_INCLUDE_DIR ZLIB_LIBRARY + # VERSION_VAR ZLIB_VERSION_STRING) + + if (ZLIB_FOUND) + set(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR}) + set(ZLIB_LIBRARIES ${ZLIB_LIBRARY}) + endif (ZLIB_FOUND) +endif (ZLIB_LIBRARIES AND ZLIB_INCLUDE_DIRS) diff --git a/libssh/cmake/Modules/MacroAddCompileFlags.cmake b/libssh/cmake/Modules/MacroAddCompileFlags.cmake new file mode 100644 index 00000000..a866689d --- /dev/null +++ b/libssh/cmake/Modules/MacroAddCompileFlags.cmake @@ -0,0 +1,21 @@ +# - MACRO_ADD_COMPILE_FLAGS(target_name flag1 ... flagN) + +# Copyright (c) 2006, Oswald Buddenhagen, +# Copyright (c) 2006, Andreas Schneider, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +macro (MACRO_ADD_COMPILE_FLAGS _target) + + get_target_property(_flags ${_target} COMPILE_FLAGS) + if (_flags) + set(_flags ${_flags} ${ARGN}) + else (_flags) + set(_flags ${ARGN}) + endif (_flags) + + set_target_properties(${_target} PROPERTIES COMPILE_FLAGS ${_flags}) + +endmacro (MACRO_ADD_COMPILE_FLAGS) diff --git a/libssh/cmake/Modules/MacroAddLinkFlags.cmake b/libssh/cmake/Modules/MacroAddLinkFlags.cmake new file mode 100644 index 00000000..91cad306 --- /dev/null +++ b/libssh/cmake/Modules/MacroAddLinkFlags.cmake @@ -0,0 +1,20 @@ +# - MACRO_ADD_LINK_FLAGS(target_name flag1 ... flagN) + +# Copyright (c) 2006, Oswald Buddenhagen, +# Copyright (c) 2006, Andreas Schneider, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +macro (MACRO_ADD_LINK_FLAGS _target) + + get_target_property(_flags ${_target} LINK_FLAGS) + if (_flags) + set(_flags "${_flags} ${ARGN}") + else (_flags) + set(_flags "${ARGN}") + endif (_flags) + + set_target_properties(${_target} PROPERTIES LINK_FLAGS "${_flags}") + +endmacro (MACRO_ADD_LINK_FLAGS) diff --git a/libssh/cmake/Modules/MacroAddPlugin.cmake b/libssh/cmake/Modules/MacroAddPlugin.cmake new file mode 100644 index 00000000..36b5e57e --- /dev/null +++ b/libssh/cmake/Modules/MacroAddPlugin.cmake @@ -0,0 +1,30 @@ +# - MACRO_ADD_PLUGIN(name [WITH_PREFIX] file1 .. fileN) +# +# Create a plugin from the given source files. +# If WITH_PREFIX is given, the resulting plugin will have the +# prefix "lib", otherwise it won't. +# +# Copyright (c) 2006, Alexander Neundorf, +# Copyright (c) 2006, Laurent Montel, +# Copyright (c) 2006, Andreas Schneider, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +macro (MACRO_ADD_PLUGIN _target_NAME _with_PREFIX) + + if (${_with_PREFIX} STREQUAL "WITH_PREFIX") + set(_first_SRC) + else (${_with_PREFIX} STREQUAL "WITH_PREFIX") + set(_first_SRC ${_with_PREFIX}) + endif (${_with_PREFIX} STREQUAL "WITH_PREFIX") + + add_library(${_target_NAME} MODULE ${_first_SRC} ${ARGN}) + + if (_first_SRC) + set_target_properties(${_target_NAME} PROPERTIES PREFIX "") + endif (_first_SRC) + +endmacro (MACRO_ADD_PLUGIN _name _sources) + diff --git a/libssh/cmake/Modules/MacroCopyFile.cmake b/libssh/cmake/Modules/MacroCopyFile.cmake new file mode 100644 index 00000000..cee1cae3 --- /dev/null +++ b/libssh/cmake/Modules/MacroCopyFile.cmake @@ -0,0 +1,33 @@ +# - macro_copy_file(_src _dst) +# Copies a file to ${_dst} only if ${_src} is different (newer) than ${_dst} +# +# Example: +# macro_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/icon.png ${CMAKE_CURRENT_BINARY_DIR}/.) +# Copies file icon.png to ${CMAKE_CURRENT_BINARY_DIR} directory +# +# Copyright (c) 2006-2007 Wengo +# Copyright (c) 2006-2008 Andreas Schneider +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING file. + + +macro (macro_copy_file _src _dst) + # Removes all path containing .svn or CVS or CMakeLists.txt during the copy + if (NOT ${_src} MATCHES ".*\\.svn|CVS|CMakeLists\\.txt.*") + + if (CMAKE_VERBOSE_MAKEFILE) + message(STATUS "Copy file from ${_src} to ${_dst}") + endif (CMAKE_VERBOSE_MAKEFILE) + + # Creates directory if necessary + get_filename_component(_path ${_dst} PATH) + file(MAKE_DIRECTORY ${_path}) + + execute_process( + COMMAND + ${CMAKE_COMMAND} -E copy_if_different ${_src} ${_dst} + OUTPUT_QUIET + ) + endif (NOT ${_src} MATCHES ".*\\.svn|CVS|CMakeLists\\.txt.*") +endmacro (macro_copy_file) diff --git a/libssh/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake b/libssh/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake new file mode 100644 index 00000000..a2e94809 --- /dev/null +++ b/libssh/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake @@ -0,0 +1,17 @@ +# - MACRO_ENSURE_OUT_OF_SOURCE_BUILD() +# MACRO_ENSURE_OUT_OF_SOURCE_BUILD() + +# Copyright (c) 2006, Alexander Neundorf, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +macro (MACRO_ENSURE_OUT_OF_SOURCE_BUILD _errorMessage) + + string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" _insource) + if (_insource) + message(SEND_ERROR "${_errorMessage}") + message(FATAL_ERROR "Remove the file CMakeCache.txt in ${CMAKE_SOURCE_DIR} first.") + endif (_insource) + +endmacro (MACRO_ENSURE_OUT_OF_SOURCE_BUILD) diff --git a/libssh/cmake/Modules/UseDoxygen.cmake b/libssh/cmake/Modules/UseDoxygen.cmake new file mode 100644 index 00000000..c4ab7ccc --- /dev/null +++ b/libssh/cmake/Modules/UseDoxygen.cmake @@ -0,0 +1,100 @@ +# - Run Doxygen +# +# Adds a doxygen target that runs doxygen to generate the html +# and optionally the LaTeX API documentation. +# The doxygen target is added to the doc target as dependency. +# i.e.: the API documentation is built with: +# make doc +# +# USAGE: INCLUDE IN PROJECT +# +# set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +# include(UseDoxygen) +# Add the Doxyfile.in and UseDoxygen.cmake files to the projects source directory. +# +# +# Variables you may define are: +# DOXYFILE_OUTPUT_DIR - Path where the Doxygen output is stored. Defaults to "doc". +# +# DOXYFILE_LATEX_DIR - Directory where the Doxygen LaTeX output is stored. Defaults to "latex". +# +# DOXYFILE_HTML_DIR - Directory where the Doxygen html output is stored. Defaults to "html". +# + +# +# Copyright (c) 2009-2010 Tobias Rautenkranz +# Copyright (c) 2010 Andreas Schneider +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +macro(usedoxygen_set_default name value) + if(NOT DEFINED "${name}") + set("${name}" "${value}") + endif() +endmacro() + +find_package(Doxygen) + +if(DOXYGEN_FOUND) + find_file(DOXYFILE_IN + NAMES + doxy.config.in + PATHS + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_ROOT}/Modules/ + NO_DEFAULT_PATH) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(DOXYFILE_IN DEFAULT_MSG "DOXYFILE_IN") +endif() + +if(DOXYGEN_FOUND AND DOXYFILE_IN_FOUND) + add_custom_target(doxygen ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.config) + + usedoxygen_set_default(DOXYFILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") + usedoxygen_set_default(DOXYFILE_HTML_DIR "html") + + set_property(DIRECTORY APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_HTML_DIR}") + + set(DOXYFILE_LATEX FALSE) + set(DOXYFILE_PDFLATEX FALSE) + set(DOXYFILE_DOT FALSE) + + find_package(LATEX) + if(LATEX_COMPILER AND MAKEINDEX_COMPILER) + set(DOXYFILE_LATEX TRUE) + usedoxygen_set_default(DOXYFILE_LATEX_DIR "latex") + + set_property(DIRECTORY APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES + "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") + + if(PDFLATEX_COMPILER) + set(DOXYFILE_PDFLATEX TRUE) + endif() + if(DOXYGEN_DOT_EXECUTABLE) + set(DOXYFILE_DOT TRUE) + endif() + + add_custom_command(TARGET doxygen + POST_BUILD + COMMAND ${CMAKE_MAKE_PROGRAM} + WORKING_DIRECTORY "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") + endif() + + configure_file(${DOXYFILE_IN} ${CMAKE_CURRENT_BINARY_DIR}/doxy.config ESCAPE_QUOTES IMMEDIATE @ONLY) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/doxy.trac.in) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxy.trac.in ${CMAKE_CURRENT_BINARY_DIR}/doxy.trac ESCAPE_QUOTES IMMEDIATE @ONLY) + add_custom_target(doxygen-trac ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.trac) + endif() + + get_target_property(DOC_TARGET doc TYPE) + if(NOT DOC_TARGET) + add_custom_target(doc) + endif() + + add_dependencies(doc doxygen) +endif() diff --git a/libssh/config.h.cmake b/libssh/config.h.cmake new file mode 100644 index 00000000..2014e8d9 --- /dev/null +++ b/libssh/config.h.cmake @@ -0,0 +1,148 @@ +/* Name of package */ +#cmakedefine PACKAGE "${APPLICATION_NAME}" + +/* Version number of package */ +#cmakedefine VERSION "${APPLICATION_VERSION}" + +#cmakedefine LOCALEDIR "${LOCALE_INSTALL_DIR}" +#cmakedefine DATADIR "${DATADIR}" +#cmakedefine LIBDIR "${LIBDIR}" +#cmakedefine PLUGINDIR "${PLUGINDIR}" +#cmakedefine SYSCONFDIR "${SYSCONFDIR}" +#cmakedefine BINARYDIR "${BINARYDIR}" +#cmakedefine SOURCEDIR "${SOURCEDIR}" + +/************************** HEADER FILES *************************/ + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_ARGP_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PTY_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_TERMIOS_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_OPENSSL_AES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_WSPIAPI_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_OPENSSL_BLOWFISH_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_OPENSSL_DES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_OPENSSL_ECDH_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_OPENSSL_EC_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_OPENSSL_ECDSA_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PTHREAD_H 1 + +/* Define to 1 if you have eliptic curve cryptography in openssl */ +#cmakedefine HAVE_OPENSSL_ECC 1 + +/* Define to 1 if you have eliptic curve cryptography in gcrypt */ +#cmakedefine HAVE_GCRYPT_ECC 1 + +/* Define to 1 if you have eliptic curve cryptography */ +#cmakedefine HAVE_ECC 1 + +/*************************** FUNCTIONS ***************************/ + +/* Define to 1 if you have the `snprintf' function. */ +#cmakedefine HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `_snprintf' function. */ +#cmakedefine HAVE__SNPRINTF 1 + +/* Define to 1 if you have the `_snprintf_s' function. */ +#cmakedefine HAVE__SNPRINTF_S 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#cmakedefine HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the `_vsnprintf' function. */ +#cmakedefine HAVE__VSNPRINTF 1 + +/* Define to 1 if you have the `_vsnprintf_s' function. */ +#cmakedefine HAVE__VSNPRINTF_S 1 + +/* Define to 1 if you have the `strncpy' function. */ +#cmakedefine HAVE_STRNCPY 1 + +/* Define to 1 if you have the `cfmakeraw' function. */ +#cmakedefine HAVE_CFMAKERAW 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +#cmakedefine HAVE_GETADDRINFO 1 + +/* Define to 1 if you have the `poll' function. */ +#cmakedefine HAVE_POLL 1 + +/* Define to 1 if you have the `select' function. */ +#cmakedefine HAVE_SELECT 1 + +/* Define to 1 if you have the `clock_gettime' function. */ +#cmakedefine HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the `ntohll' function. */ +#cmakedefine HAVE_NTOHLL 1 + +/* Define to 1 if you have the `htonll' function. */ +#cmakedefine HAVE_HTONLL 1 + +/* Define to 1 if you have the `strtoull' function. */ +#cmakedefine HAVE_STRTOULL 1 + +/* Define to 1 if you have the `__strtoull' function. */ +#cmakedefine HAVE___STRTOULL 1 + +/*************************** LIBRARIES ***************************/ + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +#cmakedefine HAVE_LIBCRYPTO 1 + +/* Define to 1 if you have the `gcrypt' library (-lgcrypt). */ +#cmakedefine HAVE_LIBGCRYPT 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#cmakedefine HAVE_PTHREAD 1 + + +/**************************** OPTIONS ****************************/ + +/* Define to 1 if you want to enable ZLIB */ +#cmakedefine WITH_ZLIB 1 + +/* Define to 1 if you want to enable SFTP */ +#cmakedefine WITH_SFTP 1 + +/* Define to 1 if you want to enable SSH1 */ +#cmakedefine WITH_SSH1 1 + +/* Define to 1 if you want to enable server support */ +#cmakedefine WITH_SERVER 1 + +/* Define to 1 if you want to enable debug output for crypto functions */ +#cmakedefine DEBUG_CRYPTO 1 + +/* Define to 1 if you want to enable pcap output support (experimental) */ +#cmakedefine WITH_PCAP 1 + +/* Define to 1 if you want to enable calltrace debug output */ +#cmakedefine DEBUG_CALLTRACE 1 + +/*************************** ENDIAN *****************************/ + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#cmakedefine WORDS_BIGENDIAN 1 diff --git a/libssh/doc/CMakeLists.txt b/libssh/doc/CMakeLists.txt new file mode 100644 index 00000000..31242811 --- /dev/null +++ b/libssh/doc/CMakeLists.txt @@ -0,0 +1,5 @@ +# +# Build the documentation +# +include(UseDoxygen OPTIONAL) + diff --git a/libssh/doc/TracFooter.html b/libssh/doc/TracFooter.html new file mode 100644 index 00000000..867baf0e --- /dev/null +++ b/libssh/doc/TracFooter.html @@ -0,0 +1 @@ + diff --git a/libssh/doc/TracHeader.html b/libssh/doc/TracHeader.html new file mode 100644 index 00000000..280a869b --- /dev/null +++ b/libssh/doc/TracHeader.html @@ -0,0 +1,4 @@ + + + + diff --git a/libssh/doc/authentication.dox b/libssh/doc/authentication.dox new file mode 100644 index 00000000..fbc2103b --- /dev/null +++ b/libssh/doc/authentication.dox @@ -0,0 +1,375 @@ +/** +@page libssh_tutor_authentication Chapter 2: A deeper insight on authentication +@section authentication_details A deeper insight on authentication + +In our guided tour, we merely mentioned that the user needed to authenticate. +We didn't explain much in detail how that was supposed to happen. +This chapter explains better the four authentication methods: with public keys, +with a password, with challenges and responses (keyboard-interactive), and with +no authentication at all. + +If your software is supposed to connect to an arbitrary server, then you +might need to support all authentication methods. If your software will +connect only to a given server, then it might be enough for your software +to support only the authentication methods used by that server. If you are +the administrator of the server, it might be your call to choose those +authentication methods. + +It is not the purpose of this document to review in detail the advantages +and drawbacks of each authentication method. You are therefore invited +to read the abundant documentation on this topic to fully understand the +advantages and security risks linked to each method. + + +@subsection pubkeys Authenticating with public keys + +libssh is fully compatible with the openssh public and private keys. You +can either use the automatic public key authentication method provided by +libssh, or roll your own using the public key functions. + +The process of authenticating by public key to a server is the following: + - you scan a list of files that contain public keys. each key is sent to + the SSH server, until the server acknowledges a key (a key it knows can be + used to authenticate the user). + - then, you retrieve the private key for this key and send a message + proving that you know that private key. + +The function ssh_userauth_autopubkey() does this using the available keys in +"~/.ssh/". The return values are the following: + - SSH_AUTH_ERROR: some serious error happened during authentication + - SSH_AUTH_DENIED: no key matched + - SSH_AUTH_SUCCESS: you are now authenticated + - SSH_AUTH_PARTIAL: some key matched but you still have to provide an other + mean of authentication (like a password). + +The ssh_userauth_publickey_auto() function also tries to authenticate using the +SSH agent, if you have one running, or the "none" method otherwise. + +If you wish to authenticate with public key by your own, follow these steps: + - Retrieve the public key with ssh_import_pubkey_file(). + - Offer the public key to the SSH server using ssh_userauth_try_publickey(). + If the return value is SSH_AUTH_SUCCESS, the SSH server accepts to + authenticate using the public key and you can go to the next step. + - Retrieve the private key, using the ssh_pki_import_privkey_file() function. + If a passphrase is needed, either the passphrase specified as argument or + a callback will be used. + - Authenticate using ssh_userauth_publickey() with your private key. + - Do not forget cleaning up memory using ssh_key_free(). + +Here is a minimalistic example of public key authentication: + +@code +int authenticate_pubkey(ssh_session session) +{ + int rc; + + rc = ssh_userauth_publickey_auto(session, NULL); + + if (rc == SSH_AUTH_ERROR) + { + fprintf(stderr, "Authentication failed: %s\n", + ssh_get_error(session)); + return SSH_AUTH_ERROR; + } + + return rc; +} +@endcode + +@see ssh_userauth_publickey_auto() +@see ssh_userauth_try_publickey() +@see ssh_userauth_publickey() +@see ssh_pki_import_pubkey_file() +@see ssh_pki_import_privkey_file() +@see ssh_key_free() + + +@subsection password Authenticating with a password + +The function ssh_userauth_password() serves the purpose of authenticating +using a password. It will return SSH_AUTH_SUCCESS if the password worked, +or one of other constants otherwise. It's your work to ask the password +and to deallocate it in a secure manner. + +If your server complains that the password is wrong, but you can still +authenticate using openssh's client (issuing password), it's probably +because openssh only accept keyboard-interactive. Switch to +keyboard-interactive authentication, or try to configure plain text passwords +on the SSH server. + +Here is a small example of password authentication: + +@code +int authenticate_password(ssh_session session) +{ + char *password; + int rc; + + password = getpass("Enter your password: "); + rc = ssh_userauth_password(session, NULL, password); + if (rc == SSH_AUTH_ERROR) + { + fprintf(stderr, "Authentication failed: %s\n", + ssh_get_error(session)); + return SSH_AUTH_ERROR; + } + + return rc; +} +@endcode + +@see ssh_userauth_password + + +@subsection keyb_int The keyboard-interactive authentication method + +The keyboard-interactive method is, as its name tells, interactive. The +server will issue one or more challenges that the user has to answer, +until the server takes an authentication decision. + +ssh_userauth_kbdint() is the the main keyboard-interactive function. +It will return SSH_AUTH_SUCCESS,SSH_AUTH_DENIED, SSH_AUTH_PARTIAL, +SSH_AUTH_ERROR, or SSH_AUTH_INFO, depending on the result of the request. + +The keyboard-interactive authentication method of SSH2 is a feature that +permits the server to ask a certain number of questions in an interactive +manner to the client, until it decides to accept or deny the login. + +To begin, you call ssh_userauth_kbdint() (just set user and submethods to +NULL) and store the answer. + +If the answer is SSH_AUTH_INFO, it means that the server has sent a few +questions that you should ask the user. You can retrieve these questions +with the following functions: ssh_userauth_kbdint_getnprompts(), +ssh_userauth_kbdint_getname(), ssh_userauth_kbdint_getinstruction(), and +ssh_userauth_kbdint_getprompt(). + +Set the answer for each question in the challenge using +ssh_userauth_kbdint_setanswer(). + +Then, call again ssh_userauth_kbdint() and start the process again until +these functions returns something else than SSH_AUTH_INFO. + +Here are a few remarks: + - Even the first call can return SSH_AUTH_DENIED or SSH_AUTH_SUCCESS. + - The server can send an empty question set (this is the default behavior + on my system) after you have sent the answers to the first questions. + You must still parse the answer, it might contain some + message from the server saying hello or such things. Just call + ssh_userauth_kbdint() until needed. + - The meaning of "name", "prompt", "instruction" may be a little + confusing. An explanation is given in the RFC section that follows. + +Here is a little note about how to use the information from +keyboard-interactive authentication, coming from the RFC itself (rfc4256): + +@verbatim + + 3.3 User Interface Upon receiving a request message, the client SHOULD + prompt the user as follows: A command line interface (CLI) client SHOULD + print the name and instruction (if non-empty), adding newlines. Then for + each prompt in turn, the client SHOULD display the prompt and read the + user input. + + A graphical user interface (GUI) client has many choices on how to prompt + the user. One possibility is to use the name field (possibly prefixed + with the application's name) as the title of a dialog window in which + the prompt(s) are presented. In that dialog window, the instruction field + would be a text message, and the prompts would be labels for text entry + fields. All fields SHOULD be presented to the user, for example an + implementation SHOULD NOT discard the name field because its windows lack + titles; it SHOULD instead find another way to display this information. If + prompts are presented in a dialog window, then the client SHOULD NOT + present each prompt in a separate window. + + All clients MUST properly handle an instruction field with embedded + newlines. They SHOULD also be able to display at least 30 characters for + the name and prompts. If the server presents names or prompts longer than 30 + characters, the client MAY truncate these fields to the length it can + display. If the client does truncate any fields, there MUST be an obvious + indication that such truncation has occured. + + The instruction field SHOULD NOT be truncated. Clients SHOULD use control + character filtering as discussed in [SSH-ARCH] to avoid attacks by + including terminal control characters in the fields to be displayed. + + For each prompt, the corresponding echo field indicates whether or not + the user input should be echoed as characters are typed. Clients SHOULD + correctly echo/mask user input for each prompt independently of other + prompts in the request message. If a client does not honor the echo field + for whatever reason, then the client MUST err on the side of + masking input. A GUI client might like to have a checkbox toggling + echo/mask. Clients SHOULD NOT add any additional characters to the prompt + such as ": " (colon-space); the server is responsible for supplying all + text to be displayed to the user. Clients MUST also accept empty responses + from the user and pass them on as empty strings. +@endverbatim + +The following example shows how to perform keyboard-interactive authentication: + +@code +int authenticate_kbdint(ssh_session session) +{ + int rc; + + rc = ssh_userauth_kbdint(session, NULL, NULL); + while (rc == SSH_AUTH_INFO) + { + const char *name, *instruction; + int nprompts, iprompt; + + name = ssh_userauth_kbdint_getname(session); + instruction = ssh_userauth_kbdint_getinstruction(session); + nprompts = ssh_userauth_kbdint_getnprompts(session); + + if (strlen(name) > 0) + printf("%s\n", name); + if (strlen(instruction) > 0) + printf("%s\n", instruction); + for (iprompt = 0; iprompt < nprompts; iprompt++) + { + const char *prompt; + char echo; + + prompt = ssh_userauth_kbdint_getprompt(session, iprompt, &echo); + if (echo) + { + char buffer[128], *ptr; + + printf("%s", prompt); + if (fgets(buffer, sizeof(buffer), stdin) == NULL) + return SSH_AUTH_ERROR; + buffer[sizeof(buffer) - 1] = '\0'; + if ((ptr = strchr(buffer, '\n')) != NULL) + *ptr = '\0'; + if (ssh_userauth_kbdint_setanswer(session, iprompt, buffer) < 0) + return SSH_AUTH_ERROR; + memset(buffer, 0, strlen(buffer)); + } + else + { + char *ptr; + + ptr = getpass(prompt); + if (ssh_userauth_kbdint_setanswer(session, iprompt, ptr) < 0) + return SSH_AUTH_ERROR; + } + } + rc = ssh_userauth_kbdint(session, NULL, NULL); + } + return rc; +} +@endcode + +@see ssh_userauth_kbdint() +@see ssh_userauth_kbdint_getnprompts() +@see ssh_userauth_kbdint_getname() +@see ssh_userauth_kbdint_getinstruction() +@see ssh_userauth_kbdint_getprompt() +@see ssh_userauth_kbdint_setanswer() + + +@subsection none Authenticating with "none" method + +The primary purpose of the "none" method is to get authenticated **without** +any credential. Don't do that, use one of the other authentication methods, +unless you really want to grant anonymous access. + +If the account has no password, and if the server is configured to let you +pass, ssh_userauth_none() might answer SSH_AUTH_SUCCESS. + +The following example shows how to perform "none" authentication: + +@code +int authenticate_kbdint(ssh_session session) +{ + int rc; + + rc = ssh_userauth_none(session, NULL, NULL); + return rc; +} +@endcode + +@subsection auth_list Getting the list of supported authentications + +You are not meant to choose a given authentication method, you can +let the server tell you which methods are available. Once you know them, +you try them one after the other. + +The following example shows how to get the list of available authentication +methods with ssh_userauth_list() and how to use the result: + +@code +int test_several_auth_methods(ssh_session session) +{ + int method, rc; + + rc = ssh_userauth_none(session, NULL, NULL); + if (rc != SSH_AUTH_SUCCESS) { + return rc; + } + + method = ssh_userauth_list(session, NULL); + + if (method & SSH_AUTH_METHOD_NONE) + { // For the source code of function authenticate_none(), + // refer to the corresponding example + rc = authenticate_none(session); + if (rc == SSH_AUTH_SUCCESS) return rc; + } + if (method & SSH_AUTH_METHOD_PUBLICKEY) + { // For the source code of function authenticate_pubkey(), + // refer to the corresponding example + rc = authenticate_pubkey(session); + if (rc == SSH_AUTH_SUCCESS) return rc; + } + if (method & SSH_AUTH_METHOD_INTERACTIVE) + { // For the source code of function authenticate_kbdint(), + // refer to the corresponding example + rc = authenticate_kbdint(session); + if (rc == SSH_AUTH_SUCCESS) return rc; + } + if (method & SSH_AUTH_METHOD_PASSWORD) + { // For the source code of function authenticate_password(), + // refer to the corresponding example + rc = authenticate_password(session); + if (rc == SSH_AUTH_SUCCESS) return rc; + } + return SSH_AUTH_ERROR; +} +@endcode + + +@subsection banner Getting the banner + +The SSH server might send a banner, which you can retrieve with +ssh_get_issue_banner(), then display to the user. + +The following example shows how to retrieve and dispose the issue banner: + +@code +int display_banner(ssh_session session) +{ + int rc; + char *banner; + +/* + *** Does not work without calling ssh_userauth_none() first *** + *** That will be fixed *** +*/ + rc = ssh_userauth_none(session, NULL); + if (rc == SSH_AUTH_ERROR) + return rc; + + banner = ssh_get_issue_banner(session); + if (banner) + { + printf("%s\n", banner); + free(banner); + } + + return rc; +} +@endcode + +*/ diff --git a/libssh/doc/command.dox b/libssh/doc/command.dox new file mode 100644 index 00000000..76113543 --- /dev/null +++ b/libssh/doc/command.dox @@ -0,0 +1,94 @@ +/** +@page libssh_tutor_command Chapter 4: Passing a remote command +@section remote_command Passing a remote command + +Previous chapter has shown how to open a full shell session, with an attached +terminal or not. If you only need to execute a command on the remote end, +you don't need all that complexity. + +The method described here is suited for executing only one remote command. +If you need to issue several commands in a row, you should consider using +a non-interactive remote shell, as explained in previous chapter. + +@see shell + + +@subsection exec_remote Executing a remote command + +The first steps for executing a remote command are identical to those +for opening remote shells. You first need a SSH channel, and then +a SSH session that uses this channel: + +@code +int show_remote_files(ssh_session session) +{ + ssh_channel channel; + int rc; + + channel = ssh_channel_new(session); + if (channel == NULL) return SSH_ERROR; + + rc = ssh_channel_open_session(channel); + if (rc != SSH_OK) + { + ssh_channel_free(channel); + return rc; + } +@endcode + +Once a session is open, you can start the remote command with +ssh_channel_request_exec(): + +@code + rc = ssh_channel_request_exec(channel, "ls -l"); + if (rc != SSH_OK) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return rc; + } +@endcode + +If the remote command displays data, you get them with ssh_channel_read(). +This function returns the number of bytes read. If there is no more +data to read on the channel, this function returns 0, and you can go to next step. +If an error has been encountered, it returns a negative value: + +@code + char buffer[256]; + unsigned int nbytes; + + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + while (nbytes > 0) + { + if (fwrite(buffer, 1, nbytes, stdout) != nbytes) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + } + + if (nbytes < 0) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } +@endcode + +Once you read the result of the remote command, you send an +end-of-file to the channel, close it, and free the memory +that it used: + +@code + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + ssh_channel_free(channel); + + return SSH_OK; +} +@endcode + +*/ diff --git a/libssh/doc/doxy.config.in b/libssh/doc/doxy.config.in new file mode 100644 index 00000000..9810518f --- /dev/null +++ b/libssh/doc/doxy.config.in @@ -0,0 +1,1835 @@ +# Doxyfile 1.8.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = @APPLICATION_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @APPLICATION_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = @CMAKE_SOURCE_DIR@ + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = @CMAKE_INTERNAL_DOC@ + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @CMAKE_SOURCE_DIR@/include/libssh \ + @CMAKE_SOURCE_DIR@/src \ + @CMAKE_SOURCE_DIR@/doc + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.cpp \ + *.cc \ + *.c \ + *.h \ + *.hh \ + *.hpp \ + *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.git/* \ + */.svn/* \ + */cmake/* \ + */build/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = *.c \ + *.h \ + INSTALL \ + DEPENDENCIES \ + CHANGELOG \ + LICENSE \ + LGPL + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 2 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = @DOXYFILE_LATEX@ + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = @LATEX_COMPILER@ + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = @MAKEINDEX_COMPILER@ + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = YES + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = @CMAKE_CURRENT_BINARY_DIR@/html/@PROJECT_NAME@.TAGFILE + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = YES + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = @DOXYGEN_DOT_FOUND@ + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = @DOXYGEN_DOT_EXECUTABLE_PATH@ + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/libssh/doc/doxy.trac.in b/libssh/doc/doxy.trac.in new file mode 100644 index 00000000..dbd719aa --- /dev/null +++ b/libssh/doc/doxy.trac.in @@ -0,0 +1,1546 @@ +# Doxyfile 1.6.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = @APPLICATION_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @APPLICATION_VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = @CMAKE_SOURCE_DIR@ + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = @CMAKE_INTERNAL_DOC@ + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @CMAKE_SOURCE_DIR@/include \ + @CMAKE_SOURCE_DIR@/libssh \ + @CMAKE_SOURCE_DIR@/doc + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.cpp \ + *.cc \ + *.c \ + *.h \ + *.hh \ + *.hpp \ + *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.git/* \ + */.svn/* \ + */cmake/* \ + */build/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = *.c \ + *.h \ + INSTALL \ + DEPENDENCIES \ + CHANGELOG \ + LICENSE \ + LGPL + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 2 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = trac + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = @CMAKE_CURRENT_SOURCE_DIR@/TracHeader.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = @CMAKE_CURRENT_SOURCE_DIR@/TracFooter.html + +# If the HTML_TIMESTAMP tag is set to YES then the generated HTML +# documentation will contain the timesstamp. + +HTML_TIMESTAMP = NO + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP) +# there is already a search function so this one should typically +# be disabled. + +SEARCHENGINE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = YES + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = @CMAKE_CURRENT_BINARY_DIR@/trac/@PROJECT_NAME@.TAGFILE + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = YES + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = @DOXYGEN_DOT_FOUND@ + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = @DOXYGEN_DOT_EXECUTABLE_PATH@ + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/libssh/doc/forwarding.dox b/libssh/doc/forwarding.dox new file mode 100644 index 00000000..9dc0df36 --- /dev/null +++ b/libssh/doc/forwarding.dox @@ -0,0 +1,229 @@ +/** +@page libssh_tutor_forwarding Chapter 7: Forwarding connections (tunnel) +@section forwarding_connections Forwarding connections + +Port forwarding comes in SSH protocol in two different flavours: +direct or reverse port forwarding. Direct port forwarding is also +named local port forwardind, and reverse port forwarding is also called +remote port forwarding. SSH also allows X11 tunnels. + + + +@subsection forwarding_direct Direct port forwarding + +Direct port forwarding is from client to server. The client opens a tunnel, +and forwards whatever data to the server. Then, the server connects to an +end point. The end point can reside on another machine or on the SSH +server itself. + +Example of use of direct port forwarding: +@verbatim +Mail client application Google Mail + | ^ + 5555 (arbitrary) | + | 143 (IMAP2) + V | + SSH client =====> SSH server + +Legend: +--P-->: port connexion through port P +=====>: SSH tunnel +@endverbatim +A mail client connects to port 5555 of a client. An encrypted tunnel is +established to the server. The server connects to port 143 of Google Mail (the +end point). Now the local mail client can retreive mail. + + +@subsection forwarding_reverse Reverse port forwarding + +The reverse forwarding is slightly different. It goes from server to client, +even though the client has the initiative of establishing the tunnel. +Once the tunnel is established, the server will listen on a port. Whenever +a connection to this port is made, the server forwards the data to the client. + +Example of use of reverse port forwarding: +@verbatim + Local mail server Mail client application + ^ | + | 5555 (arbitrary) + 143 (IMAP2) | + | V + SSH client <===== SSH server + +Legend: +--P-->: port connexion through port P +=====>: SSH tunnel +@endverbatim +In this example, the SSH client establishes the tunnel, +but it is used to forward the connections established at +the server to the client. + + +@subsection forwarding_x11 X11 tunnels + +X11 tunnels allow a remote application to display locally. + +Example of use of X11 tunnels: +@verbatim + Local display Graphical application + (X11 server) (X11 client) + ^ | + | V + SSH client <===== SSH server + +Legend: +----->: X11 connection through X11 display number +=====>: SSH tunnel +@endverbatim +The SSH tunnel is established by the client. + +How to establish X11 tunnels with libssh has already been described in +this tutorial. + +@see x11 + + +@subsection libssh_direct Doing direct port forwarding with libssh + +To do direct port forwarding, call function ssh_channel_open_forward(): + - you need a separate channel for the tunnel as first parameter; + - second and third parameters are the remote endpoint; + - fourth and fifth parameters are sent to the remote server + so that they can be logged on that server. + +If you don't plan to forward the data you will receive to any local port, +just put fake values like "localhost" and 5555 as your local host and port. + +The example below shows how to open a direct channel that would be +used to retrieve google's home page from the remote SSH server. + +@code +int direct_forwarding(ssh_session session) +{ + ssh_channel forwarding_channel; + int rc; + char *http_get = "GET / HTTP/1.1\nHost: www.google.com\n\n"; + int nbytes, nwritten; + + forwarding_channel = ssh_channel_new(session); + if (forwarding_channel == NULL) { + return rc; + } + + rc = ssh_channel_open_forward(forwarding_channel, + "www.google.com", 80, + "localhost", 5555); + if (rc != SSH_OK) + { + ssh_channel_free(forwarding_channel); + return rc; + } + + nbytes = strlen(http_get); + nwritten = ssh_channel_write(forwarding_channel, + http_get, + nbytes); + if (nbytes != nwritten) + { + ssh_channel_free(forwarding_channel); + return SSH_ERROR; + } + + ... + + ssh_channel_free(forwarding_channel); + return SSH_OK; +} +@endcode + +The data sent by Google can be retrieved for example with ssh_select() +and ssh_channel_read(). Goggle's home page can then be displayed on the +local SSH client, saved into a local file, made available on a local port, +or whatever use you have for it. + + +@subsection libssh_reverse Doing reverse port forwarding with libssh + +To do reverse port forwarding, call ssh_forward_listen(), +then ssh_forward_accept(). + +When you call ssh_forward_listen(), you can let the remote server +chose the non-priviledged port it should listen to. Otherwise, you can chose +your own priviledged or non-priviledged port. Beware that you should have +administrative priviledges on the remote server to open a priviledged port +(port number < 1024). + +Below is an example of a very rough web server waiting for connections on port +8080 of remote SSH server. The incoming connections are passed to the +local libssh application, which handles them: + +@code +int web_server(ssh_session session) +{ + int rc; + ssh_channel channel; + char buffer[256]; + int nbytes, nwritten; + char *helloworld = "" +"HTTP/1.1 200 OK\n" +"Content-Type: text/html\n" +"Content-Length: 113\n" +"\n" +"\n" +" \n" +" Hello, World!\n" +" \n" +" \n" +"

Hello, World!

\n" +" \n" +"\n"; + + rc = ssh_forward_listen(session, NULL, 8080, NULL); + if (rc != SSH_OK) + { + fprintf(stderr, "Error opening remote port: %s\n", + ssh_get_error(session)); + return rc; + } + + channel = ssh_forward_accept(session, 60000); + if (channel == NULL) + { + fprintf(stderr, "Error waiting for incoming connection: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + while (1) + { + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + if (nbytes < 0) + { + fprintf(stderr, "Error reading incoming data: %s\n", + ssh_get_error(session)); + ssh_channel_send_eof(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + if (strncmp(buffer, "GET /", 5)) continue; + + nbytes = strlen(helloworld); + nwritten = ssh_channel_write(channel, helloworld, nbytes); + if (nwritten != nbytes) + { + fprintf(stderr, "Error sending answer: %s\n", + ssh_get_error(session)); + ssh_channel_send_eof(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + printf("Sent answer\n"); + } + + ssh_channel_send_eof(channel); + ssh_channel_free(channel); + return SSH_OK; +} +@endcode + +*/ diff --git a/libssh/doc/guided_tour.dox b/libssh/doc/guided_tour.dox new file mode 100644 index 00000000..2fa906e0 --- /dev/null +++ b/libssh/doc/guided_tour.dox @@ -0,0 +1,455 @@ +/** +@page libssh_tutor_guided_tour Chapter 1: A typical SSH session +@section ssh_session A typical SSH session + +A SSH session goes through the following steps: + + - Before connecting to the server, you can set up if you wish one or other + server public key authentication, i.e. DSA or RSA. You can choose + cryptographic algorithms you trust and compression algorithms if any. You + must of course set up the hostname. + + - The connection is established. A secure handshake is made, and resulting from + it, a public key from the server is gained. You MUST verify that the public + key is legitimate, using for instance the MD5 fingerprint or the known hosts + file. + + - The client must authenticate: the classical ways are password, or + public keys (from dsa and rsa key-pairs generated by openssh). + If a SSH agent is running, it is possible to use it. + + - Now that the user has been authenticated, you must open one or several + channels. Channels are different subways for information into a single ssh + connection. Each channel has a standard stream (stdout) and an error stream + (stderr). You can theoretically open an infinity of channels. + + - With the channel you opened, you can do several things: + - Execute a single command. + - Open a shell. You may want to request a pseudo-terminal before. + - Invoke the sftp subsystem to transfer files. + - Invoke the scp subsystem to transfer files. + - Invoke your own subsystem. This is outside the scope of this document, + but can be done. + + - When everything is finished, just close the channels, and then the connection. + +The sftp and scp subsystems use channels, but libssh hides them to +the programmer. If you want to use those subsystems, instead of a channel, +you'll usually open a "sftp session" or a "scp session". + + +@subsection setup Creating the session and setting options + +The most important object in a SSH connection is the SSH session. In order +to allocate a new SSH session, you use ssh_new(). Don't forget to +always verify that the allocation successed. +@code +#include +#include + +int main() +{ + ssh_session my_ssh_session = ssh_new(); + if (my_ssh_session == NULL) + exit(-1); + ... + ssh_free(my_ssh_session); +} +@endcode + +libssh follows the allocate-it-deallocate-it pattern. Each object that you allocate +using xxxxx_new() must be deallocated using xxxxx_free(). In this case, ssh_new() +does the allocation and ssh_free() does the contrary. + +The ssh_options_set() function sets the options of the session. The most important options are: + - SSH_OPTIONS_HOST: the name of the host you want to connect to + - SSH_OPTIONS_PORT: the used port (default is port 22) + - SSH_OPTIONS_USER: the system user under which you want to connect + - SSH_OPTIONS_LOG_VERBOSITY: the quantity of messages that are printed + +The complete list of options can be found in the documentation of ssh_options_set(). +The only mandatory option is SSH_OPTIONS_HOST. If you don't use SSH_OPTIONS_USER, +the local username of your account will be used. + +Here is a small example of how to use it: + +@code +#include +#include + +int main() +{ + ssh_session my_ssh_session; + int verbosity = SSH_LOG_PROTOCOL; + int port = 22; + + my_ssh_session = ssh_new(); + if (my_ssh_session == NULL) + exit(-1); + + ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT, &port); + + ... + + ssh_free(my_ssh_session); +} +@endcode + +Please notice that all parameters are passed to ssh_options_set() as pointers, +even if you need to set an integer value. + +@see ssh_new +@see ssh_free +@see ssh_options_set +@see ssh_options_parse_config +@see ssh_options_copy +@see ssh_options_getopt + + +@subsection connect Connecting to the server + +Once all settings have been made, you can connect using ssh_connect(). That +function will return SSH_OK if the connection worked, SSH_ERROR otherwise. + +You can get the English error string with ssh_get_error() in order to show the +user what went wrong. Then, use ssh_disconnect() when you want to stop +the session. + +Here's an example: + +@code +#include +#include +#include + +int main() +{ + ssh_session my_ssh_session; + int rc; + + my_ssh_session = ssh_new(); + if (my_ssh_session == NULL) + exit(-1); + + ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost"); + + rc = ssh_connect(my_ssh_session); + if (rc != SSH_OK) + { + fprintf(stderr, "Error connecting to localhost: %s\n", + ssh_get_error(my_ssh_session)); + exit(-1); + } + + ... + + ssh_disconnect(my_ssh_session); + ssh_free(my_ssh_session); +} +@endcode + + +@subsection serverauth Authenticating the server + +Once you're connected, the following step is mandatory: you must check that the server +you just connected to is known and safe to use (remember, SSH is about security and +authentication). + +There are two ways of doing this: + - The first way (recommended) is to use the ssh_is_server_known() + function. This function will look into the known host file + (~/.ssh/known_hosts on UNIX), look for the server hostname's pattern, + and determine whether this host is present or not in the list. + - The second way is to use ssh_get_pubkey_hash() to get a binary version + of the public key hash value. You can then use your own database to check + if this public key is known and secure. + +You can also use the ssh_get_pubkey_hash() to show the public key hash +value to the user, in case he knows what the public key hash value is +(some paranoid people write their public key hash values on paper before +going abroad, just in case ...). + +If the remote host is being used to for the first time, you can ask the user whether +he/she trusts it. Once he/she concluded that the host is valid and worth being +added in the known hosts file, you use ssh_write_knownhost() to register it in +the known hosts file, or any other way if you use your own database. + +The following example is part of the examples suite available in the +examples/ directory: + +@code +#include +#include + +int verify_knownhost(ssh_session session) +{ + int state, hlen; + unsigned char *hash = NULL; + char *hexa; + char buf[10]; + + state = ssh_is_server_known(session); + + hlen = ssh_get_pubkey_hash(session, &hash); + if (hlen < 0) + return -1; + + switch (state) + { + case SSH_SERVER_KNOWN_OK: + break; /* ok */ + + case SSH_SERVER_KNOWN_CHANGED: + fprintf(stderr, "Host key for server changed: it is now:\n"); + ssh_print_hexa("Public key hash", hash, hlen); + fprintf(stderr, "For security reasons, connection will be stopped\n"); + free(hash); + return -1; + + case SSH_SERVER_FOUND_OTHER: + fprintf(stderr, "The host key for this server was not found but an other" + "type of key exists.\n"); + fprintf(stderr, "An attacker might change the default server key to" + "confuse your client into thinking the key does not exist\n"); + free(hash); + return -1; + + case SSH_SERVER_FILE_NOT_FOUND: + fprintf(stderr, "Could not find known host file.\n"); + fprintf(stderr, "If you accept the host key here, the file will be" + "automatically created.\n"); + /* fallback to SSH_SERVER_NOT_KNOWN behavior */ + + case SSH_SERVER_NOT_KNOWN: + hexa = ssh_get_hexa(hash, hlen); + fprintf(stderr,"The server is unknown. Do you trust the host key?\n"); + fprintf(stderr, "Public key hash: %s\n", hexa); + free(hexa); + if (fgets(buf, sizeof(buf), stdin) == NULL) + { + free(hash); + return -1; + } + if (strncasecmp(buf, "yes", 3) != 0) + { + free(hash); + return -1; + } + if (ssh_write_knownhost(session) < 0) + { + fprintf(stderr, "Error %s\n", strerror(errno)); + free(hash); + return -1; + } + break; + + case SSH_SERVER_ERROR: + fprintf(stderr, "Error %s", ssh_get_error(session)); + free(hash); + return -1; + } + + free(hash); + return 0; +} +@endcode + +@see ssh_connect +@see ssh_disconnect +@see ssh_get_error +@see ssh_get_error_code +@see ssh_get_pubkey_hash +@see ssh_is_server_known +@see ssh_write_knownhost + + +@subsection auth Authenticating the user + +The authentication process is the way a service provider can identify a +user and verify his/her identity. The authorization process is about enabling +the authenticated user the access to ressources. In SSH, the two concepts +are linked. After authentication, the server can grant the user access to +several ressources such as port forwarding, shell, sftp subsystem, and so on. + +libssh supports several methods of authentication: + - "none" method. This method allows to get the available authentications + methods. It also gives the server a chance to authenticate the user with + just his/her login. Some very old hardware uses this feature to fallback + the user on a "telnet over SSH" style of login. + - password method. A password is sent to the server, which accepts it or not. + - keyboard-interactive method. The server sends several challenges to the + user, who must answer correctly. This makes possible the authentication + via a codebook for instance ("give code at 23:R on page 3"). + - public key method. The host knows the public key of the user, and the + user must prove he knows the associated private key. This can be done + manually, or delegated to the SSH agent as we'll see later. + +All these methods can be combined. You can for instance force the user to +authenticate with at least two of the authentication methods. In that case, +one speaks of "Partial authentication". A partial authentication is a +response from authentication functions stating that your credential was +accepted, but yet another one is required to get in. + +The example below shows an authentication with password: + +@code +#include +#include +#include + +int main() +{ + ssh_session my_ssh_session; + int rc; + char *password; + + // Open session and set options + my_ssh_session = ssh_new(); + if (my_ssh_session == NULL) + exit(-1); + ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost"); + + // Connect to server + rc = ssh_connect(my_ssh_session); + if (rc != SSH_OK) + { + fprintf(stderr, "Error connecting to localhost: %s\n", + ssh_get_error(my_ssh_session)); + ssh_free(my_ssh_session); + exit(-1); + } + + // Verify the server's identity + // For the source code of verify_knowhost(), check previous example + if (verify_knownhost(my_ssh_session) < 0) + { + ssh_disconnect(my_ssh_session); + ssh_free(my_ssh_session); + exit(-1); + } + + // Authenticate ourselves + password = getpass("Password: "); + rc = ssh_userauth_password(my_ssh_session, NULL, password); + if (rc != SSH_AUTH_SUCCESS) + { + fprintf(stderr, "Error authenticating with password: %s\n", + ssh_get_error(my_ssh_session)); + ssh_disconnect(my_ssh_session); + ssh_free(my_ssh_session); + exit(-1); + } + + ... + + ssh_disconnect(my_ssh_session); + ssh_free(my_ssh_session); +} +@endcode + +@see @ref authentication_details + + +@subsection using_ssh Doing something + +At this point, the authenticity of both server and client is established. +Time has come to take advantage of the many possibilities offered by the SSH +protocol: execute a remote command, open remote shells, transfer files, +forward ports, etc. + +The example below shows how to execute a remote command: + +@code +int show_remote_processes(ssh_session session) +{ + ssh_channel channel; + int rc; + char buffer[256]; + unsigned int nbytes; + + channel = ssh_channel_new(session); + if (channel == NULL) + return SSH_ERROR; + + rc = ssh_channel_open_session(channel); + if (rc != SSH_OK) + { + ssh_channel_free(channel); + return rc; + } + + rc = ssh_channel_request_exec(channel, "ps aux"); + if (rc != SSH_OK) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return rc; + } + + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + while (nbytes > 0) + { + if (write(1, buffer, nbytes) != nbytes) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + } + + if (nbytes < 0) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return SSH_ERROR; + } + + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + ssh_channel_free(channel); + + return SSH_OK; +} +@endcode + +@see @ref opening_shell +@see @ref remote_command +@see @ref sftp_subsystem +@see @ref scp_subsystem + + +@subsection errors Handling the errors + +All the libssh functions which return an error value also set an English error message +describing the problem. + +Error values are typically SSH_ERROR for integer values, or NULL for pointers. + +The function ssh_get_error() returns a pointer to the static error message. + +ssh_error_code() returns the error code number : SSH_NO_ERROR, +SSH_REQUEST_DENIED, SSH_INVALID_REQUEST, SSH_CONNECTION_LOST, SSH_FATAL, +or SSH_INVALID_DATA. SSH_REQUEST_DENIED means the ssh server refused your +request, but the situation is recoverable. The others mean something happened +to the connection (some encryption problems, server problems, ...). +SSH_INVALID_REQUEST means the library got some garbage from server, but +might be recoverable. SSH_FATAL means the connection has an important +problem and isn't probably recoverable. + +Most of time, the error returned are SSH_FATAL, but some functions +(generaly the ssh_request_xxx ones) may fail because of server denying request. +In these cases, SSH_REQUEST_DENIED is returned. + +ssh_get_error() and ssh_get_error_code() take a ssh_session as a parameter. +That's for thread safety, error messages that can be attached to a session +aren't static anymore. Any error that happens during ssh_options_xxx() +or ssh_connect() (i.e., outside of any session) can be retrieved by +giving NULL as argument. + +The SFTP subsystem has its own error codes, in addition to libssh ones. + + +*/ diff --git a/libssh/doc/introduction.dox b/libssh/doc/introduction.dox new file mode 100644 index 00000000..cd786497 --- /dev/null +++ b/libssh/doc/introduction.dox @@ -0,0 +1,49 @@ +/** +@page libssh_tutorial The Tutorial +@section introduction Introduction + +libssh is a C library that enables you to write a program that uses the +SSH protocol. With it, you can remotely execute programs, transfer +files, or use a secure and transparent tunnel for your remote programs. +The SSH protocol is encrypted, ensures data integrity, and provides strong +means of authenticating both the server of the client. The library hides +a lot of technical details from the SSH protocol, but this does not +mean that you should not try to know about and understand these details. + +libssh is a Free Software / Open Source project. The libssh library +is distributed under LGPL license. The libssh project has nothing to do with +"libssh2", which is a completly different and independant project. + +libssh can run on top of either libgcrypt or libcrypto, +two general-purpose cryptographic libraries. + +This tutorial concentrates for its main part on the "client" side of libssh. +To learn how to accept incoming SSH connexions (how to write a SSH server), +you'll have to jump to the end of this document. + +This tutorial describes libssh version 0.5.0. This version is a little different +from the 0.4.X series. However, the examples should work with +little changes on versions like 0.4.2 and later. + + +Table of contents: + +@subpage libssh_tutor_guided_tour + +@subpage libssh_tutor_authentication + +@subpage libssh_tutor_shell + +@subpage libssh_tutor_command + +@subpage libssh_tutor_sftp + +@subpage libssh_tutor_scp + +@subpage libssh_tutor_forwarding + +@subpage libssh_tutor_threads + +@subpage libssh_tutor_todo + +*/ diff --git a/libssh/doc/linking.dox b/libssh/doc/linking.dox new file mode 100644 index 00000000..16dfab98 --- /dev/null +++ b/libssh/doc/linking.dox @@ -0,0 +1,24 @@ +/** + +@page libssh_linking The Linking HowTo + +@section dynamic Dynamic Linking + +On UNIX and Windows systems its the same, you need at least the libssh.h +header file and the libssh shared library. + +@section static Static Linking + +@warning The libssh library is licensed under the LGPL! Make sure you +understand what this means to your codebase if you want to distribute +binaries and link statically against LGPL code! + +On UNIX systems linking against the static version of the library is the +same as linking against the shared library. Both have the same name. Some +build system require to use the full path to the static library. + +On Windows you need to define LIBSSH_STATIC in the compiler command +line. This is required cause the dynamic library needs to specify the +dllimport attribute. + +*/ diff --git a/libssh/doc/mainpage.dox b/libssh/doc/mainpage.dox new file mode 100644 index 00000000..fc65e413 --- /dev/null +++ b/libssh/doc/mainpage.dox @@ -0,0 +1,211 @@ +/** + +@mainpage + +This is the online reference for developing with the libssh library. It +documents the libssh C API and the C++ wrapper. + +@section main-linking Linking + +We created a small howto how to link libssh against your application, read +@subpage libssh_linking. + +@section main-tutorial Tutorial + +You should start by reading @subpage libssh_tutorial, then reading the documentation of +the interesting functions as you go. + +@section main-features Features + +The libssh library provides: + + - Full C library functions for manipulating a client-side SSH connection + - SSH2 and SSH1 protocol compliant + - Fully configurable sessions + - Server support + - SSH agent authentication support + - Support for AES-128, AES-192, AES-256, Blowfish, 3DES in CBC mode, and AES in CTR mode + - Supports OpenSSL and GCrypt + - Use multiple SSH connections in a same process, at same time + - Use multiple channels in the same connection + - Thread safety when using different sessions at same time + - POSIX-like SFTP (Secure File Transfer) implementation with openssh extension support + - SCP implementation + - Large file system support (files bigger than 4GB) + - RSA and DSS server public key supported + - Compression support (with zlib) + - Public key (RSA and DSS), password and keyboard-interactive authentication + - Full poll()/WSAPoll() support and a poll-emulation for Win32. + - Runs and tested under x86_64, x86, ARM, Sparc32, PPC under Linux, BSD, MacOSX, Solaris and Windows + +@section main-copyright Copyright Policy + +libssh is a project with distributed copyright ownership, which means we prefer +the copyright on parts of libssh to be held by individuals rather than +corporations if possible. There are historical legal reasons for this, but one +of the best ways to explain it is that it’s much easier to work with +individuals who have ownership than corporate legal departments if we ever need +to make reasonable compromises with people using and working with libssh. + +We track the ownership of every part of libssh via git, our source code control +system, so we know the provenance of every piece of code that is committed to +libssh. + +So if possible, if you’re doing libssh changes on behalf of a company who +normally owns all the work you do please get them to assign personal copyright +ownership of your changes to you as an individual, that makes things very easy +for us to work with and avoids bringing corporate legal departments into the +picture. + +If you can’t do this we can still accept patches from you owned by your +employer under a standard employment contract with corporate copyright +ownership. It just requires a simple set-up process first. + +We use a process very similar to the way things are done in the Linux Kernel +community, so it should be very easy to get a sign off from your corporate +legal department. The only changes we’ve made are to accommodate the license we +use, which is LGPLv2 (or later) whereas the Linux kernel uses GPLv2. + +The process is called signing. + +How to sign your work +---------------------- + +Once you have permission to contribute to libssh from your employer, simply +email a copy of the following text from your corporate email address to: + +contributing@libssh.org + +@verbatim +libssh Developer's Certificate of Origin. Version 1.0 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the appropriate + version of the GNU General Public License; or + +(b) The contribution is based upon previous work that, to the best of + my knowledge, is covered under an appropriate open source license + and I have the right under that license to submit that work with + modifications, whether created in whole or in part by me, under + the GNU General Public License, in the appropriate version; or + +(c) The contribution was provided directly to me by some other + person who certified (a) or (b) and I have not modified it. + +(d) I understand and agree that this project and the contribution are + public and that a record of the contribution (including all + metadata and personal information I submit with it, including my + sign-off) is maintained indefinitely and may be redistributed + consistent with the libssh Team's policies and the requirements of + the GNU GPL where they are relevant. + +(e) I am granting this work to this project under the terms of the + GNU Lesser General Public License as published by the + Free Software Foundation; either version 2.1 of + the License, or (at the option of the project) any later version. + +http://www.gnu.org/licenses/lgpl-2.1.html +@endverbatim + +We will maintain a copy of that email as a record that you have the rights to +contribute code to libssh under the required licenses whilst working for the +company where the email came from. + +Then when sending in a patch via the normal mechanisms described above, add a +line that states: + +@verbatim + Signed-off-by: Random J Developer +@endverbatim + +using your real name and the email address you sent the original email you used +to send the libssh Developer’s Certificate of Origin to us (sorry, no +pseudonyms or anonymous contributions.) + +That’s it! Such code can then quite happily contain changes that have copyright +messages such as: + +@verbatim + (c) Example Corporation. +@endverbatim + +and can be merged into the libssh codebase in the same way as patches from any +other individual. You don’t need to send in a copy of the libssh Developer’s +Certificate of Origin for each patch, or inside each patch. Just the sign-off +message is all that is required once we’ve received the initial email. + +Have fun and happy libssh hacking! + +The libssh Team + +@section main-rfc Internet standard + +@subsection main-rfc-secsh Secure Shell (SSH) + +The following RFC documents described SSH-2 protcol as an Internet standard. + + - RFC 4250, + The Secure Shell (SSH) Protocol Assigned Numbers + - RFC 4251, + The Secure Shell (SSH) Protocol Architecture + - RFC 4252, + The Secure Shell (SSH) Authentication Protocol + - RFC 4253, + The Secure Shell (SSH) Transport Layer Protocol + - RFC 4254, + The Secure Shell (SSH) Connection Protocol + - RFC 4255, + Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints + - RFC 4256, + Generic Message Exchange Authentication for the Secure Shell Protocol (SSH) + - RFC 4335, + The Secure Shell (SSH) Session Channel Break Extension + - RFC 4344, + The Secure Shell (SSH) Transport Layer Encryption Modes + - RFC 4345, + Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol + +It was later modified and expanded by the following RFCs. + + - RFC 4419, + Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer + Protocol + - RFC 4432, + RSA Key Exchange for the Secure Shell (SSH) Transport Layer Protocol + - RFC 4462, + Generic Security Service Application Program Interface (GSS-API) + Authentication and Key Exchange for the Secure Shell (SSH) Protocol + - RFC 4716, + The Secure Shell (SSH) Public Key File Format + - RFC 5656, + Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer + +Interesting cryptography documents: + + - PKCS #11, PKCS #11 reference documents, describing interface with smartcards. + +@subsection main-rfc-sftp Secure Shell File Transfer Protocol (SFTP) + +The protocol is not an Internet standard but it is still widely implemented. +OpenSSH and most other implementation implement Version 3 of the protocol. We +do the same in libssh. + + - + draft-ietf-secsh-filexfer-02.txt, + SSH File Transfer Protocol + +@subsection main-rfc-extensions Secure Shell Extensions + +The OpenSSH project has defined some extensions to the protocol. We support some of +them like the statvfs calls in SFTP or the ssh-agent. + + - + OpenSSH's deviations and extensions + - + OpenSSH's ssh-agent + - + OpenSSH's pubkey certificate authentication + +*/ diff --git a/libssh/doc/scp.dox b/libssh/doc/scp.dox new file mode 100644 index 00000000..1e7db780 --- /dev/null +++ b/libssh/doc/scp.dox @@ -0,0 +1,268 @@ +/** +@page libssh_tutor_scp Chapter 6: The SCP subsystem +@section scp_subsystem The SCP subsystem + +The SCP subsystem has far less functionnality than the SFTP subsystem. +However, if you only need to copy files from and to the remote system, +it does its job. + + +@subsection scp_session Opening and closing a SCP session + +Like in the SFTP subsystem, you don't handle the SSH channels directly. +Instead, you open a "SCP session". + +When you open your SCP session, you have to choose between read or write mode. +You can't do both in the same session. So you specify either SSH_SCP_READ or +SSH_SCP_WRITE as the second parameter of function ssh_scp_new(). + +Another important mode flag for opening your SCP session is SSH_SCP_RECURSIVE. +When you use SSH_SCP_RECURSIVE, you declare that you are willing to emulate +the behaviour of "scp -r" command in your program, no matter it is for +reading or for writing. + +Once your session is created, you initialize it with ssh_scp_init(). When +you have finished transferring files, you terminate the SCP connection with +ssh_scp_close(). Finally, you can dispose the SCP connection with +ssh_scp_free(). + +The example below does the maintenance work to open a SCP connection for writing in +recursive mode: + +@code +int scp_write(ssh_session session) +{ + ssh_scp scp; + int rc; + + scp = ssh_scp_new + (session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, "."); + if (scp == NULL) + { + fprintf(stderr, "Error allocating scp session: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + rc = ssh_scp_init(scp); + if (rc != SSH_OK) + { + fprintf(stderr, "Error initializing scp session: %s\n", + ssh_get_error(session)); + ssh_scp_free(scp); + return rc; + } + + ... + + ssh_scp_close(scp); + ssh_scp_free(scp); + return SSH_OK; +} +@endcode + +The example below shows how to open a connection to read a single file: + +@code +int scp_read(ssh_session session) +{ + ssh_scp scp; + int rc; + + scp = ssh_scp_new + (session, SSH_SCP_READ, "helloworld/helloworld.txt"); + if (scp == NULL) + { + fprintf(stderr, "Error allocating scp session: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + rc = ssh_scp_init(scp); + if (rc != SSH_OK) + { + fprintf(stderr, "Error initializing scp session: %s\n", + ssh_get_error(session)); + ssh_scp_free(scp); + return rc; + } + + ... + + ssh_scp_close(scp); + ssh_scp_free(scp); + return SSH_OK; +} + +@endcode + + +@subsection scp_write Creating files and directories + +You create directories with ssh_scp_push_directory(). In recursive mode, +you are placed in this directory once it is created. If the directory +already exists and if you are in recursive mode, you simply enter that +directory. + +Creating files is done in two steps. First, you prepare the writing with +ssh_scp_push_file(). Then, you write the data with ssh_scp_write(). +The length of the data to write must be identical between both function calls. +There's no need to "open" nor "close" the file, this is done automatically +on the remote end. If the file already exists, it is overwritten and truncated. + +The following example creates a new directory named "helloworld/", then creates +a file named "helloworld.txt" in that directory: + +@code +int scp_helloworld(ssh_session session, ssh_scp scp) +{ + int rc; + const char *helloworld = "Hello, world!\n"; + int length = strlen(helloworld); + + rc = ssh_scp_push_directory(scp, "helloworld", S_IRWXU); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't create remote directory: %s\n", + ssh_get_error(session)); + return rc; + } + + rc = ssh_scp_push_file + (scp, "helloworld.txt", length, S_IRUSR | S_IWUSR); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't open remote file: %s\n", + ssh_get_error(session)); + return rc; + } + + rc = ssh_scp_write(scp, helloworld, length); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't write to remote file: %s\n", + ssh_get_error(session)); + return rc; + } + + return SSH_OK; +} +@endcode + + +@subsection scp_recursive_write Copying full directory trees to the remote server + +Let's say you want to copy the following tree of files to the remote site: + +@verbatim + +-- file1 + +-- B --+ + | +-- file2 +-- A --+ + | +-- file3 + +-- C --+ + +-- file4 +@endverbatim + +You would do it that way: + - open the session in recursive mode + - enter directory A + - enter its subdirectory B + - create file1 in B + - create file2 in B + - leave directory B + - enter subdirectory C + - create file3 in C + - create file4 in C + - leave directory C + - leave directory A + +To leave a directory, call ssh_scp_leave_directory(). + + +@subsection scp_read Reading files and directories + + +To receive files, you pull requests from the other side with ssh_scp_pull_request(). +If this function returns SSH_SCP_REQUEST_NEWFILE, then you must get ready for +the reception. You can get the size of the data to receive with ssh_scp_request_get_size() +and allocate a buffer accordingly. When you are ready, you accept the request with +ssh_scp_accept_request(), then read the data with ssh_scp_read(). + +The following example receives a single file. The name of the file to +receive has been given earlier, when the scp session was opened: + +@code +int scp_receive(ssh_session session, ssh_scp scp) +{ + int rc; + int size, mode; + char *filename, *buffer; + + rc = ssh_scp_pull_request(scp); + if (rc != SSH_SCP_REQUEST_NEWFILE) + { + fprintf(stderr, "Error receiving information about file: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + size = ssh_scp_request_get_size(scp); + filename = strdup(ssh_scp_request_get_filename(scp)); + mode = ssh_scp_request_get_permissions(scp); + printf("Receiving file %s, size %d, permisssions 0%o\n", + filename, size, mode); + free(filename); + + buffer = malloc(size); + if (buffer == NULL) + { + fprintf(stderr, "Memory allocation error\n"); + return SSH_ERROR; + } + + ssh_scp_accept_request(scp); + rc = ssh_scp_read(scp, buffer, size); + if (rc == SSH_ERROR) + { + fprintf(stderr, "Error receiving file data: %s\n", + ssh_get_error(session)); + free(buffer); + return rc; + } + printf("Done\n"); + + write(1, buffer, size); + free(buffer); + + rc = ssh_scp_pull_request(scp); + if (rc != SSH_SCP_REQUEST_EOF) + { + fprintf(stderr, "Unexpected request: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + return SSH_OK; +} +@endcode + +In this example, since we just requested a single file, we expect ssh_scp_request() +to return SSH_SCP_REQUEST_NEWFILE first, then SSH_SCP_REQUEST_EOF. That's quite a +naive approach; for example, the remote server might send a warning as well +(return code SSH_SCP_REQUEST_WARNING) and the example would fail. A more comprehensive +reception program would receive the requests in a loop and analyze them carefully +until SSH_SCP_REQUEST_EOF has been received. + + +@subsection scp_recursive_read Receiving full directory trees from the remote server + +If you opened the SCP session in recursive mode, the remote end will be +telling you when to change directory. + +In that case, when ssh_scp_pull_request() answers +SSH_SCP_REQUEST_NEWDIRECTORY, you should make that local directory (if +it does not exist yet) and enter it. When ssh_scp_pull_request() answers +SSH_SCP_REQUEST_ENDDIRECTORY, you should leave the current directory. + +*/ diff --git a/libssh/doc/sftp.dox b/libssh/doc/sftp.dox new file mode 100644 index 00000000..97f9afbb --- /dev/null +++ b/libssh/doc/sftp.dox @@ -0,0 +1,415 @@ +/** +@page libssh_tutor_sftp Chapter 5: The SFTP subsystem +@section sftp_subsystem The SFTP subsystem + +SFTP stands for "Secure File Transfer Protocol". It enables you to safely +transfer files between the local and the remote computer. It reminds a lot +of the old FTP protocol. + +SFTP is a rich protocol. It lets you do over the network almost everything +that you can do with local files: + - send files + - modify only a portion of a file + - receive files + - receive only a portion of a file + - get file owner and group + - get file permissions + - set file owner and group + - set file permissions + - remove files + - rename files + - create a directory + - remove a directory + - retrieve the list of files in a directory + - get the target of a symbolic link + - create symbolic links + - get information about mounted filesystems. + +The current implemented version of the SFTP protocol is version 3. All functions +aren't implemented yet, but the most important are. + + +@subsection sftp_session Opening and closing a SFTP session + +Unlike with remote shells and remote commands, when you use the SFTP subsystem, +you don't handle directly the SSH channels. Instead, you open a "SFTP session". + +The function sftp_new() creates a new SFTP session. The function sftp_init() +initializes it. The function sftp_free() deletes it. + +As you see, all the SFTP-related functions start with the "sftp_" prefix +instead of the usual "ssh_" prefix. + +The example below shows how to use these functions: + +@code +#include + +int sftp_helloworld(ssh_session session) +{ + sftp_session sftp; + int rc; + + sftp = sftp_new(session); + if (sftp == NULL) + { + fprintf(stderr, "Error allocating SFTP session: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + rc = sftp_init(sftp); + if (rc != SSH_OK) + { + fprintf(stderr, "Error initializing SFTP session: %s.\n", + sftp_get_error(sftp)); + sftp_free(sftp); + return rc; + } + + ... + + sftp_free(sftp); + return SSH_OK; +} +@endcode + + +@subsection sftp_errors Analyzing SFTP errors + +In case of a problem, the function sftp_get_error() returns a SFTP-specific +error number, in addition to the regular SSH error number returned by +ssh_get_error_number(). + +Possible errors are: + - SSH_FX_OK: no error + - SSH_FX_EOF: end-of-file encountered + - SSH_FX_NO_SUCH_FILE: file does not exist + - SSH_FX_PERMISSION_DENIED: permission denied + - SSH_FX_FAILURE: generic failure + - SSH_FX_BAD_MESSAGE: garbage received from server + - SSH_FX_NO_CONNECTION: no connection has been set up + - SSH_FX_CONNECTION_LOST: there was a connection, but we lost it + - SSH_FX_OP_UNSUPPORTED: operation not supported by libssh yet + - SSH_FX_INVALID_HANDLE: invalid file handle + - SSH_FX_NO_SUCH_PATH: no such file or directory path exists + - SSH_FX_FILE_ALREADY_EXISTS: an attempt to create an already existing file or directory has been made + - SSH_FX_WRITE_PROTECT: write-protected filesystem + - SSH_FX_NO_MEDIA: no media was in remote drive + + +@subsection sftp_mkdir Creating a directory + +The function sftp_mkdir() tahes the "SFTP session" we juste created as +its first argument. It also needs the name of the file to create, and the +desired permissions. The permissions are the same as for the usual mkdir() +function. To get a comprehensive list of the available permissions, use the +"man 2 stat" command. The desired permissions are combined with the remote +user's mask to determine the effective permissions. + +The code below creates a directory named "helloworld" in the current directory that +can be read and written only by its owner: + +@code +#include +#include + +int sftp_helloworld(ssh_session session, sftp_session sftp) +{ + int rc; + + rc = sftp_mkdir(sftp, "helloworld", S_IRWXU); + if (rc != SSH_OK) + { + if (sftp_get_error(sftp) != SSH_FX_FILE_ALREADY_EXISTS) + { + fprintf(stderr, "Can't create directory: %s\n", + ssh_get_error(session)); + return rc; + } + } + + ... + + return SSH_OK; +} +@endcode + +Unlike its equivalent in the SCP subsystem, this function does NOT change the +current directory to the newly created subdirectory. + + +@subsection sftp_write Copying a file to the remote computer + +You handle the contents of a remote file just like you would do with a +local file: you open the file in a given mode, move the file pointer in it, +read or write data, and close the file. + +The sftp_open() function is very similar to the regular open() function, +excepted that it returns a file handle of type sftp_file. This file handle +is then used by the other file manipulation functions and remains valid +until you close the remote file with sftp_close(). + +The example below creates a new file named "helloworld.txt" in the +newly created "helloworld" directory. If the file already exists, it will +be truncated. It then writes the famous "Hello, World!" sentence to the +file, followed by a new line character. Finally, the file is closed: + +@code +#include +#include +#include + +int sftp_helloworld(ssh_session session, sftp_session sftp) +{ + int access_type = O_WRONLY | O_CREAT | O_TRUNC; + sftp_file file; + const char *helloworld = "Hello, World!\n"; + int length = strlen(helloworld); + int rc, nwritten; + + ... + + file = sftp_open(sftp, "helloworld/helloworld.txt", + access_type, S_IRWXU); + if (file == NULL) + { + fprintf(stderr, "Can't open file for writing: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + nwritten = sftp_write(file, helloworld, length); + if (nwritten != length) + { + fprintf(stderr, "Can't write data to file: %s\n", + ssh_get_error(session)); + sftp_close(file); + return SSH_ERROR; + } + + rc = sftp_close(file); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't close the written file: %s\n", + ssh_get_error(session)); + return rc; + } + + return SSH_OK; +} +@endcode + + +@subsection sftp_read Reading a file from the remote computer + +The nice thing with reading a file over the network through SFTP is that it +can be done both in a synchronous way or an asynchronous way. If you read the file +asynchronously, your program can do something else while it waits for the +results to come. + +Synchronous read is done with sftp_read(). + +The following example prints the contents of remote file "/etc/profile". For +each 1024 bytes of information read, it waits until the end of the read operation: + +@code +int sftp_read_sync(ssh_session session, sftp_session sftp) +{ + int access_type; + sftp_file file; + char buffer[1024]; + int nbytes, rc; + + access_type = O_RDONLY; + file = sftp_open(sftp, "/etc/profile", + access_type, 0); + if (file == NULL) + { + fprintf(stderr, "Can't open file for reading: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + nbytes = sftp_read(file, buffer, sizeof(buffer)); + while (nbytes > 0) + { + if (write(1, buffer, nbytes) != nbytes) + { + sftp_close(file); + return SSH_ERROR; + } + nbytes = sftp_read(file, buffer, sizeof(buffer)); + } + + if (nbytes < 0) + { + fprintf(stderr, "Error while reading file: %s\n", + ssh_get_error(session)); + sftp_close(file); + return SSH_ERROR; + } + + rc = sftp_close(file); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't close the read file: %s\n", + ssh_get_error(session)); + return rc; + } + + return SSH_OK; +} +@endcode + +Asynchronous read is done in two steps, first sftp_async_read_begin(), which +returns a "request handle", and then sftp_async_read(), which uses that request handle. +If the file has been opened in nonblocking mode, then sftp_async_read() +might return SSH_AGAIN, which means that the request hasn't completed yet +and that the function should be called again later on. Otherwise, +sftp_async_read() waits for the data to come. To open a file in nonblocking mode, +call sftp_file_set_nonblocking() right after you opened it. Default is blocking mode. + +The example below reads a very big file in asynchronous, nonblocking, mode. Each +time the data are not ready yet, a counter is incrementer. + +@code +int sftp_read_async(ssh_session session, sftp_session sftp) +{ + int access_type; + sftp_file file; + char buffer[1024]; + int async_request; + int nbytes; + long counter; + int rc; + + access_type = O_RDONLY; + file = sftp_open(sftp, "some_very_big_file", + access_type, 0); + if (file == NULL) + { + fprintf(stderr, "Can't open file for reading: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + sftp_file_set_nonblocking(file); + + async_request = sftp_async_read_begin(file, sizeof(buffer)); + counter = 0L; + usleep(10000); + if (async_request >= 0) + nbytes = sftp_async_read(file, buffer, sizeof(buffer), + async_request); + else nbytes = -1; + while (nbytes > 0 || nbytes == SSH_AGAIN) + { + if (nbytes > 0) + { + write(1, buffer, nbytes); + async_request = sftp_async_read_begin(file, sizeof(buffer)); + } + else counter++; + usleep(10000); + if (async_request >= 0) + nbytes = sftp_async_read(file, buffer, sizeof(buffer), + async_request); + else nbytes = -1; + } + + if (nbytes < 0) + { + fprintf(stderr, "Error while reading file: %s\n", + ssh_get_error(session)); + sftp_close(file); + return SSH_ERROR; + } + + printf("The counter has reached value: %ld\n", counter); + + rc = sftp_close(file); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't close the read file: %s\n", + ssh_get_error(session)); + return rc; + } + + return SSH_OK; +} +@endcode + +@subsection sftp_ls Listing the contents of a directory + +The functions sftp_opendir(), sftp_readdir(), sftp_dir_eof(), +and sftp_closedir() enable to list the contents of a directory. +They use a new handle_type, "sftp_dir", which gives access to the +directory being read. + +In addition, sftp_readdir() returns a "sftp_attributes" which is a pointer +to a structure with informations about a directory entry: + - name: the name of the file or directory + - size: its size in bytes + - etc. + +sftp_readdir() might return NULL under two conditions: + - when the end of the directory has been met + - when an error occured + +To tell the difference, call sftp_dir_eof(). + +The attributes must be freed with sftp_attributes_free() when no longer +needed. + +The following example reads the contents of some remote directory: + +@code +int sftp_list_dir(ssh_session session, sftp_session sftp) +{ + sftp_dir dir; + sftp_attributes attributes; + int rc; + + dir = sftp_opendir(sftp, "/var/log"); + if (!dir) + { + fprintf(stderr, "Directory not opened: %s\n", + ssh_get_error(session)); + return SSH_ERROR; + } + + printf("Name Size Perms Owner\tGroup\n"); + + while ((attributes = sftp_readdir(sftp, dir)) != NULL) + { + printf("%-20s %10llu %.8o %s(%d)\t%s(%d)\n", + attributes->name, + (long long unsigned int) attributes->size, + attributes->permissions, + attributes->owner, + attributes->uid, + attributes->group, + attributes->gid); + + sftp_attributes_free(attributes); + } + + if (!sftp_dir_eof(dir)) + { + fprintf(stderr, "Can't list directory: %s\n", + ssh_get_error(session)); + sftp_closedir(dir); + return SSH_ERROR; + } + + rc = sftp_closedir(dir); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't close directory: %s\n", + ssh_get_error(session)); + return rc; + } +} +@endcode + +*/ diff --git a/libssh/doc/shell.dox b/libssh/doc/shell.dox new file mode 100644 index 00000000..35fdfc82 --- /dev/null +++ b/libssh/doc/shell.dox @@ -0,0 +1,361 @@ +/** +@page libssh_tutor_shell Chapter 3: Opening a remote shell +@section opening_shell Opening a remote shell + +We already mentioned that a single SSH connection can be shared +between several "channels". Channels can be used for different purposes. + +This chapter shows how to open one of these channels, and how to use it to +start a command interpreter on a remote computer. + + +@subsection open_channel Opening and closing a channel + +The ssh_channel_new() function creates a channel. It returns the channel as +a variable of type ssh_channel. + +Once you have this channel, you open a SSH session that uses it with +ssh_channel_open_session(). + +Once you don't need the channel anymore, you can send an end-of-file +to it with ssh_channel_close(). At this point, you can destroy the channel +with ssh_channel_free(). + +The code sample below achieves these tasks: + +@code +int shell_session(ssh_session session) +{ + ssh_channel channel; + int rc; + + channel = ssh_channel_new(session); + if (channel == NULL) + return SSH_ERROR; + + rc = ssh_channel_open_session(channel); + if (rc != SSH_OK) + { + ssh_channel_free(channel); + return rc; + } + + ... + + ssh_channel_close(channel); + ssh_channel_send_eof(channel); + ssh_channel_free(channel); + + return SSH_OK; +} +@endcode + + +@subsection interactive Interactive and non-interactive sessions + +A "shell" is a command interpreter. It is said to be "interactive" +if there is a human user typing the commands, one after the +other. The contrary, a non-interactive shell, is similar to +the execution of commands in the background: there is no attached +terminal. + +If you plan using an interactive shell, you need to create a +pseud-terminal on the remote side. A remote terminal is usually referred +to as a "pty", for "pseudo-teletype". The remote processes won't see the +difference with a real text-oriented terminal. + +If needed, you request the pty with the function ssh_channel_request_pty(). +Then you define its dimensions (number of rows and columns) +with ssh_channel_change_pty_size(). + +Be your session interactive or not, the next step is to request a +shell with ssh_channel_request_shell(). + +@code +int interactive_shell_session(ssh_channel channel) +{ + int rc; + + rc = ssh_channel_request_pty(channel); + if (rc != SSH_OK) return rc; + + rc = ssh_channel_change_pty_size(channel, 80, 24); + if (rc != SSH_OK) return rc; + + rc = ssh_channel_request_shell(channel); + if (rc != SSH_OK) return rc; + + ... + + return rc; +} +@endcode + + +@subsection read_data Displaying the data sent by the remote computer + +In your program, you will usually need to receive all the data "displayed" +into the remote pty. You will usually analyse, log, or display this data. + +ssh_channel_read() and ssh_channel_read_nonblocking() are the simplest +way to read data from a channel. If you only need to read from a single +channel, they should be enough. + +The example below shows how to wait for remote data using ssh_channel_read(): + +@code +int interactive_shell_session(ssh_channel channel) +{ + int rc; + char buffer[256]; + int nbytes; + + rc = ssh_channel_request_pty(channel); + if (rc != SSH_OK) return rc; + + rc = ssh_channel_change_pty_size(channel, 80, 24); + if (rc != SSH_OK) return rc; + + rc = ssh_channel_request_shell(channel); + if (rc != SSH_OK) return rc; + + while (ssh_channel_is_open(channel) && + !ssh_channel_is_eof(channel)) + { + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + if (nbytes < 0) + return SSH_ERROR; + + if (nbytes > 0) + write(1, buffer, nbytes); + } + + return rc; +} +@endcode + +Unlike ssh_channel_read(), ssh_channel_read_nonblocking() never waits for +remote data to be ready. It returns immediately. + +If you plan to use ssh_channel_read_nonblocking() repeatedly in a loop, +you should use a "passive wait" function like usleep(3) in the same +loop. Otherwise, your program will consume all the CPU time, and your +computer might become unresponsive. + + +@subsection write_data Sending user input to the remote computer + +User's input is sent to the remote site with ssh_channel_write(). + +The following example shows how to combine a nonblocking read from a SSH +channel with a nonblocking read from the keyboard. The local input is then +sent to the remote computer: + +@code +/* Under Linux, this function determines whether a key has been pressed. + Under Windows, it is a standard function, so you need not redefine it. +*/ +int kbhit() +{ + struct timeval tv = { 0L, 0L }; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(0, &fds); + + return select(1, &fds, NULL, NULL, &tv); +} + +/* A very simple terminal emulator: + - print data received from the remote computer + - send keyboard input to the remote computer +*/ +int interactive_shell_session(ssh_channel channel) +{ + /* Session and terminal initialization skipped */ + ... + + char buffer[256]; + int nbytes, nwritten; + + while (ssh_channel_is_open(channel) && + !ssh_channel_is_eof(channel)) + { + nbytes = ssh_channel_read_nonblocking(channel, buffer, sizeof(buffer), 0); + if (nbytes < 0) return SSH_ERROR; + if (nbytes > 0) + { + nwritten = write(1, buffer, nbytes); + if (nwritten != nbytes) return SSH_ERROR; + + if (!kbhit()) + { + usleep(50000L); // 0.05 second + continue; + } + + nbytes = read(0, buffer, sizeof(buffer)); + if (nbytes < 0) return SSH_ERROR; + if (nbytes > 0) + { + nwritten = ssh_channel_write(channel, buffer, nbytes); + if (nwritten != nbytes) return SSH_ERROR; + } + } + + return rc; +} +@endcode + +Of course, this is a poor terminal emulator, since the echo from the keys +pressed should not be done locally, but should be done by the remote side. +Also, user's input should not be sent once "Enter" key is pressed, but +immediately after each key is pressed. This can be accomplished +by setting the local terminal to "raw" mode with the cfmakeraw(3) function. +cfmakeraw() is a standard function under Linux, on other systems you can +recode it with: + +@code +static void cfmakeraw(struct termios *termios_p) +{ + termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + termios_p->c_oflag &= ~OPOST; + termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + termios_p->c_cflag &= ~(CSIZE|PARENB); + termios_p->c_cflag |= CS8; +} +@endcode + +If you are not using a local terminal, but some kind of graphical +environment, the solution to this kind of "echo" problems will be different. + + +@subsection select_loop A more elaborate way to get the remote data + +*** Warning: ssh_select() and ssh_channel_select() are not relevant anymore, + since libssh is about to provide an easier system for asynchronous + communications. This subsection should be removed then. *** + +ssh_channel_read() and ssh_channel_read_nonblocking() functions are simple, +but they are not adapted when you expect data from more than one SSH channel, +or from other file descriptors. Last example showed how getting data from +the standard input (the keyboard) at the same time as data from the SSH +channel was complicated. The functions ssh_select() and ssh_channel_select() +provide a more elegant way to wait for data coming from many sources. + +The functions ssh_select() and ssh_channel_select() remind of the standard +UNIX select(2) function. The idea is to wait for "something" to happen: +incoming data to be read, outcoming data to block, or an exception to +occur. Both these functions do a "passive wait", i.e. you can safely use +them repeatedly in a loop, it will not consume exaggerate processor time +and make your computer unresponsive. It is quite common to use these +functions in your application's main loop. + +The difference between ssh_select() and ssh_channel_select() is that +ssh_channel_select() is simpler, but allows you only to watch SSH channels. +ssh_select() is more complete and enables watching regular file descriptors +as well, in the same function call. + +Below is an example of a function that waits both for remote SSH data to come, +as well as standard input from the keyboard: + +@code +int interactive_shell_session(ssh_session session, ssh_channel channel) +{ + /* Session and terminal initialization skipped */ + ... + + char buffer[256]; + int nbytes, nwritten; + + while (ssh_channel_is_open(channel) && + !ssh_channel_is_eof(channel)) + { + struct timeval timeout; + ssh_channel in_channels[2], out_channels[2]; + fd_set fds; + int maxfd; + + timeout.tv_sec = 30; + timeout.tv_usec = 0; + in_channels[0] = channel; + in_channels[1] = NULL; + FD_ZERO(&fds); + FD_SET(0, &fds); + FD_SET(ssh_get_fd(session), &fds); + maxfd = ssh_get_fd(session) + 1; + + ssh_select(in_channels, out_channels, maxfd, &fds, &timeout); + + if (out_channels[0] != NULL) + { + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + if (nbytes < 0) return SSH_ERROR; + if (nbytes > 0) + { + nwritten = write(1, buffer, nbytes); + if (nwritten != nbytes) return SSH_ERROR; + } + } + + if (FD_ISSET(0, &fds)) + { + nbytes = read(0, buffer, sizeof(buffer)); + if (nbytes < 0) return SSH_ERROR; + if (nbytes > 0) + { + nwritten = ssh_channel_write(channel, buffer, nbytes); + if (nbytes != nwritten) return SSH_ERROR; + } + } + } + + return rc; +} +@endcode + + +@subsection x11 Using graphical applications on the remote side + +If your remote application is graphical, you can forward the X11 protocol to +your local computer. + +To do that, you first declare that you accept X11 connections with +ssh_channel_accept_x11(). Then you create the forwarding tunnel for +the X11 protocol with ssh_channel_request_x11(). + +The following code performs channel initialization and shell session +opening, and handles a parallel X11 connection: + +@code +int interactive_shell_session(ssh_channel channel) +{ + int rc; + ssh_channel x11channel; + + rc = ssh_channel_request_pty(channel); + if (rc != SSH_OK) return rc; + + rc = ssh_channel_change_pty_size(channel, 80, 24); + if (rc != SSH_OK) return rc; + + rc = ssh_channel_request_x11(channel, 0, NULL, NULL, 0); + if (rc != SSH_OK) return rc; + + rc = ssh_channel_request_shell(channel); + if (rc != SSH_OK) return rc; + + /* Read the data sent by the remote computer here */ + ... +} +@endcode + +Don't forget to set the $DISPLAY environment variable on the remote +side, or the remote applications won't try using the X11 tunnel: + +@code +$ export DISPLAY=:0 +$ xclock & +@endcode + +*/ diff --git a/libssh/doc/tbd.dox b/libssh/doc/tbd.dox new file mode 100644 index 00000000..921337ed --- /dev/null +++ b/libssh/doc/tbd.dox @@ -0,0 +1,14 @@ +/** +@page libssh_tutor_todo To be done + +*** To be written *** + +@section sshd Writing a libssh-based server + +*** To be written *** + +@section cpp The libssh C++ wrapper + +*** To be written *** + +*/ diff --git a/libssh/doc/threading.dox b/libssh/doc/threading.dox new file mode 100644 index 00000000..a11c82f7 --- /dev/null +++ b/libssh/doc/threading.dox @@ -0,0 +1,65 @@ +/** +@page libssh_tutor_threads Chapter 8: Threads with libssh +@section threads_with_libssh How to use libssh with threads + +libssh may be used in multithreaded applications, but under several conditions : + - Threading must be initialized during the initialization of libssh. This + initialization must be done outside of any threading context. + - If pthreads is being used by your application (or your framework's backend), + you must link with libssh_threads dynamic library and initialize + threading with the ssh_threads_pthreads threading object. + - If an other threading library is being used by your application, you must + implement all the methods of the ssh_threads_callbacks_struct structure + and initialize libssh with it. + - At all times, you may use different sessions inside threads, make parallel + connections, read/write on different sessions and so on. You *cannot* use a + single session (or channels for a single session) in several threads at the same + time. This will most likely lead to internal state corruption. This limitation is + being worked out and will maybe disappear later. + +@subsection threads_init Initialization of threads + +To initialize threading, you must first select the threading model you want to +use, using ssh_threads_set_callbacks(), then call ssh_init(). + +@code +#include +... +ssh_threads_set_callbacks(ssh_threads_get_noop()); +ssh_init(); +@endcode + +ssh_threads_noop is the threading structure that does nothing. It's the +threading callbacks being used by default when you're not using threading. + +@subsection threads_pthread Using libpthread with libssh + +If your application is using libpthread, you may simply use the libpthread +threading backend: + +@code +#include +... +ssh_threads_set_callbacks(ssh_threads_get_pthread()); +ssh_init(); +@endcode + +However, you must be sure to link with the library ssh_threads. If +you're using gcc, you must use the commandline +@code +gcc -o output input.c -lssh -lssh_threads +@endcode + + +@subsection threads_other Using another threading library + +You must find your way in the ssh_threads_callbacks_struct structure. You must +implement the following methods : +- mutex_lock +- mutex_unlock +- mutex_init +- mutex_destroy +- thread_id + +Good luck ! +*/ diff --git a/libssh/examples/CMakeLists.txt b/libssh/examples/CMakeLists.txt new file mode 100644 index 00000000..5513b758 --- /dev/null +++ b/libssh/examples/CMakeLists.txt @@ -0,0 +1,57 @@ +project(libssh-examples C CXX) + +set(examples_SRCS + authentication.c + knownhosts.c + connect_ssh.c +) + +include_directories( + ${LIBSSH_PUBLIC_INCLUDE_DIRS} + ${CMAKE_BINARY_DIR} +) + +if (LINUX) + add_executable(libssh_scp libssh_scp.c ${examples_SRCS}) + target_link_libraries(libssh_scp ${LIBSSH_SHARED_LIBRARY}) + + add_executable(scp_download scp_download.c ${examples_SRCS}) + target_link_libraries(scp_download ${LIBSSH_SHARED_LIBRARY}) + + add_executable(samplessh sample.c ${examples_SRCS}) + target_link_libraries(samplessh ${LIBSSH_SHARED_LIBRARY}) + + add_executable(sshnetcat sshnetcat.c ${examples_SRCS}) + target_link_libraries(sshnetcat ${LIBSSH_SHARED_LIBRARY}) + + if (WITH_SFTP) + add_executable(samplesftp samplesftp.c ${examples_SRCS}) + target_link_libraries(samplesftp ${LIBSSH_SHARED_LIBRARY}) + endif (WITH_SFTP) + + if (WITH_SERVER) + add_executable(samplesshd samplesshd.c) + target_link_libraries(samplesshd ${LIBSSH_SHARED_LIBRARY}) + + add_executable(samplesshd-kbdint samplesshd-kbdint.c) + target_link_libraries(samplesshd-kbdint ${LIBSSH_SHARED_LIBRARY}) + + if (HAVE_LIBUTIL) + add_executable(samplesshd-tty samplesshd-tty.c) + target_link_libraries(samplesshd-tty ${LIBSSH_SHARED_LIBRARY} util) + endif (HAVE_LIBUTIL) + + endif (WITH_SERVER) +endif (LINUX) + +add_executable(exec exec.c ${examples_SRCS}) +target_link_libraries(exec ${LIBSSH_SHARED_LIBRARY}) + +add_executable(senddata senddata.c ${examples_SRCS}) +target_link_libraries(senddata ${LIBSSH_SHARED_LIBRARY}) + +add_executable(libsshpp libsshpp.cpp) +target_link_libraries(libsshpp ${LIBSSH_SHARED_LIBRARY}) + +add_executable(libsshpp_noexcept libsshpp_noexcept.cpp) +target_link_libraries(libsshpp_noexcept ${LIBSSH_SHARED_LIBRARY}) diff --git a/libssh/examples/authentication.c b/libssh/examples/authentication.c new file mode 100644 index 00000000..0e749e54 --- /dev/null +++ b/libssh/examples/authentication.c @@ -0,0 +1,167 @@ +/* + * authentication.c + * This file contains an example of how to do an authentication to a + * SSH server using libssh + */ + +/* +Copyright 2003-2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. + */ + +#include +#include +#include + +#include +#include "examples_common.h" + +int authenticate_kbdint(ssh_session session, const char *password) { + int err; + + err = ssh_userauth_kbdint(session, NULL, NULL); + while (err == SSH_AUTH_INFO) { + const char *instruction; + const char *name; + char buffer[128]; + int i, n; + + name = ssh_userauth_kbdint_getname(session); + instruction = ssh_userauth_kbdint_getinstruction(session); + n = ssh_userauth_kbdint_getnprompts(session); + + if (name && strlen(name) > 0) { + printf("%s\n", name); + } + + if (instruction && strlen(instruction) > 0) { + printf("%s\n", instruction); + } + + for (i = 0; i < n; i++) { + const char *answer; + const char *prompt; + char echo; + + prompt = ssh_userauth_kbdint_getprompt(session, i, &echo); + if (prompt == NULL) { + break; + } + + if (echo) { + char *p; + + printf("%s", prompt); + + if (fgets(buffer, sizeof(buffer), stdin) == NULL) { + return SSH_AUTH_ERROR; + } + + buffer[sizeof(buffer) - 1] = '\0'; + if ((p = strchr(buffer, '\n'))) { + *p = '\0'; + } + + if (ssh_userauth_kbdint_setanswer(session, i, buffer) < 0) { + return SSH_AUTH_ERROR; + } + + memset(buffer, 0, strlen(buffer)); + } else { + if (password && strstr(prompt, "Password:")) { + answer = password; + } else { + buffer[0] = '\0'; + + if (ssh_getpass(prompt, buffer, sizeof(buffer), 0, 0) < 0) { + return SSH_AUTH_ERROR; + } + answer = buffer; + } + err = ssh_userauth_kbdint_setanswer(session, i, answer); + memset(buffer, 0, sizeof(buffer)); + if (err < 0) { + return SSH_AUTH_ERROR; + } + } + } + err=ssh_userauth_kbdint(session,NULL,NULL); + } + + return err; +} + +static void error(ssh_session session){ + fprintf(stderr,"Authentication failed: %s\n",ssh_get_error(session)); +} + +int authenticate_console(ssh_session session){ + int rc; + int method; + char password[128] = {0}; + char *banner; + + // Try to authenticate + rc = ssh_userauth_none(session, NULL); + if (rc == SSH_AUTH_ERROR) { + error(session); + return rc; + } + + method = ssh_auth_list(session); + while (rc != SSH_AUTH_SUCCESS) { + // Try to authenticate with public key first + if (method & SSH_AUTH_METHOD_PUBLICKEY) { + rc = ssh_userauth_autopubkey(session, NULL); + if (rc == SSH_AUTH_ERROR) { + error(session); + return rc; + } else if (rc == SSH_AUTH_SUCCESS) { + break; + } + } + + // Try to authenticate with keyboard interactive"; + if (method & SSH_AUTH_METHOD_INTERACTIVE) { + rc = authenticate_kbdint(session, NULL); + if (rc == SSH_AUTH_ERROR) { + error(session); + return rc; + } else if (rc == SSH_AUTH_SUCCESS) { + break; + } + } + + if (ssh_getpass("Password: ", password, sizeof(password), 0, 0) < 0) { + return SSH_AUTH_ERROR; + } + + // Try to authenticate with password + if (method & SSH_AUTH_METHOD_PASSWORD) { + rc = ssh_userauth_password(session, NULL, password); + if (rc == SSH_AUTH_ERROR) { + error(session); + return rc; + } else if (rc == SSH_AUTH_SUCCESS) { + break; + } + } + memset(password, 0, sizeof(password)); + } + + banner = ssh_get_issue_banner(session); + if (banner) { + printf("%s\n",banner); + ssh_string_free_char(banner); + } + + return rc; +} diff --git a/libssh/examples/connect_ssh.c b/libssh/examples/connect_ssh.c new file mode 100644 index 00000000..c9e4ef6e --- /dev/null +++ b/libssh/examples/connect_ssh.c @@ -0,0 +1,67 @@ +/* + * connect_ssh.c + * This file contains an example of how to connect to a + * SSH server using libssh + */ + +/* +Copyright 2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. + */ + +#include +#include "examples_common.h" +#include + +ssh_session connect_ssh(const char *host, const char *user,int verbosity){ + ssh_session session; + int auth=0; + + session=ssh_new(); + if (session == NULL) { + return NULL; + } + + if(user != NULL){ + if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) { + ssh_free(session); + return NULL; + } + } + + if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) { + ssh_free(session); + return NULL; + } + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + if(ssh_connect(session)){ + fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session)); + ssh_disconnect(session); + ssh_free(session); + return NULL; + } + if(verify_knownhost(session)<0){ + ssh_disconnect(session); + ssh_free(session); + return NULL; + } + auth=authenticate_console(session); + if(auth==SSH_AUTH_SUCCESS){ + return session; + } else if(auth==SSH_AUTH_DENIED){ + fprintf(stderr,"Authentication failed\n"); + } else { + fprintf(stderr,"Error while authenticating : %s\n",ssh_get_error(session)); + } + ssh_disconnect(session); + ssh_free(session); + return NULL; +} diff --git a/libssh/examples/examples_common.h b/libssh/examples/examples_common.h new file mode 100644 index 00000000..13eb455c --- /dev/null +++ b/libssh/examples/examples_common.h @@ -0,0 +1,22 @@ +/* +Copyright 2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ +#ifndef EXAMPLES_COMMON_H_ +#define EXAMPLES_COMMON_H_ + +#include +int authenticate_console(ssh_session session); +int authenticate_kbdint(ssh_session session, const char *password); +int verify_knownhost(ssh_session session); +ssh_session connect_ssh(const char *hostname, const char *user, int verbosity); + +#endif /* EXAMPLES_COMMON_H_ */ diff --git a/libssh/examples/exec.c b/libssh/examples/exec.c new file mode 100644 index 00000000..4d5e0c1a --- /dev/null +++ b/libssh/examples/exec.c @@ -0,0 +1,66 @@ +/* simple exec example */ +#include + +#include +#include "examples_common.h" + +int main(void) { + ssh_session session; + ssh_channel channel; + char buffer[256]; + int nbytes; + int rc; + + session = connect_ssh("localhost", NULL, 0); + if (session == NULL) { + ssh_finalize(); + return 1; + } + + channel = ssh_channel_new(session);; + if (channel == NULL) { + ssh_disconnect(session); + ssh_free(session); + ssh_finalize(); + return 1; + } + + rc = ssh_channel_open_session(channel); + if (rc < 0) { + goto failed; + } + + rc = ssh_channel_request_exec(channel, "lsof"); + if (rc < 0) { + goto failed; + } + + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + while (nbytes > 0) { + if (fwrite(buffer, 1, nbytes, stdout) != (unsigned int) nbytes) { + goto failed; + } + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + } + + if (nbytes < 0) { + goto failed; + } + + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + ssh_channel_free(channel); + ssh_disconnect(session); + ssh_free(session); + ssh_finalize(); + + return 0; +failed: + ssh_channel_close(channel); + ssh_channel_free(channel); + ssh_disconnect(session); + ssh_free(session); + ssh_finalize(); + + return 1; +} diff --git a/libssh/examples/knownhosts.c b/libssh/examples/knownhosts.c new file mode 100644 index 00000000..37c0ba4e --- /dev/null +++ b/libssh/examples/knownhosts.c @@ -0,0 +1,98 @@ +/* + * knownhosts.c + * This file contains an example of how verify the identity of a + * SSH server using libssh + */ + +/* +Copyright 2003-2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. + */ + +#include +#include +#include +#include + +#include +#include "examples_common.h" + +#ifdef _WIN32 +#define strncasecmp _strnicmp +#endif + +int verify_knownhost(ssh_session session){ + char *hexa; + int state; + char buf[10]; + unsigned char *hash = NULL; + int hlen; + + state=ssh_is_server_known(session); + + hlen = ssh_get_pubkey_hash(session, &hash); + if (hlen < 0) { + return -1; + } + switch(state){ + case SSH_SERVER_KNOWN_OK: + break; /* ok */ + case SSH_SERVER_KNOWN_CHANGED: + fprintf(stderr,"Host key for server changed : server's one is now :\n"); + ssh_print_hexa("Public key hash",hash, hlen); + ssh_clean_pubkey_hash(&hash); + fprintf(stderr,"For security reason, connection will be stopped\n"); + return -1; + case SSH_SERVER_FOUND_OTHER: + fprintf(stderr,"The host key for this server was not found but an other type of key exists.\n"); + fprintf(stderr,"An attacker might change the default server key to confuse your client" + "into thinking the key does not exist\n" + "We advise you to rerun the client with -d or -r for more safety.\n"); + return -1; + case SSH_SERVER_FILE_NOT_FOUND: + fprintf(stderr,"Could not find known host file. If you accept the host key here,\n"); + fprintf(stderr,"the file will be automatically created.\n"); + /* fallback to SSH_SERVER_NOT_KNOWN behavior */ + case SSH_SERVER_NOT_KNOWN: + hexa = ssh_get_hexa(hash, hlen); + fprintf(stderr,"The server is unknown. Do you trust the host key ?\n"); + fprintf(stderr, "Public key hash: %s\n", hexa); + ssh_string_free_char(hexa); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + ssh_clean_pubkey_hash(&hash); + return -1; + } + if(strncasecmp(buf,"yes",3)!=0){ + ssh_clean_pubkey_hash(&hash); + return -1; + } + fprintf(stderr,"This new key will be written on disk for further usage. do you agree ?\n"); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + ssh_clean_pubkey_hash(&hash); + return -1; + } + if(strncasecmp(buf,"yes",3)==0){ + if (ssh_write_knownhost(session) < 0) { + ssh_clean_pubkey_hash(&hash); + fprintf(stderr, "error %s\n", strerror(errno)); + return -1; + } + } + + break; + case SSH_SERVER_ERROR: + ssh_clean_pubkey_hash(&hash); + fprintf(stderr,"%s",ssh_get_error(session)); + return -1; + } + ssh_clean_pubkey_hash(&hash); + return 0; +} diff --git a/libssh/examples/libssh_scp.c b/libssh/examples/libssh_scp.c new file mode 100644 index 00000000..d443f8f2 --- /dev/null +++ b/libssh/examples/libssh_scp.c @@ -0,0 +1,304 @@ +/* libssh_scp.c + * Sample implementation of a SCP client + */ + +/* +Copyright 2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. + */ + +#include +#include +#include +#include +#include + +#include +#include "examples_common.h" + +static char **sources; +static int nsources; +static char *destination; +static int verbosity=0; + +struct location { + int is_ssh; + char *user; + char *host; + char *path; + ssh_session session; + ssh_scp scp; + FILE *file; +}; + +enum { + READ, + WRITE +}; + +static void usage(const char *argv0){ + fprintf(stderr,"Usage : %s [options] [[user@]host1:]file1 ... \n" + " [[user@]host2:]destination\n" + "sample scp client - libssh-%s\n", +// "Options :\n", +// " -r : use RSA to verify host public key\n", + argv0, + ssh_version(0)); + exit(0); +} + +static int opts(int argc, char **argv){ + int i; + while((i=getopt(argc,argv,"v"))!=-1){ + switch(i){ + case 'v': + verbosity++; + break; + default: + fprintf(stderr,"unknown option %c\n",optopt); + usage(argv[0]); + return -1; + } + } + nsources=argc-optind-1; + if(nsources < 1){ + usage(argv[0]); + return -1; + } + sources=malloc((nsources + 1) * sizeof(char *)); + if(sources == NULL) + return -1; + for(i=0;ihost=location->user=NULL; + ptr=strchr(loc,':'); + if(ptr != NULL){ + location->is_ssh=1; + location->path=strdup(ptr+1); + *ptr='\0'; + ptr=strchr(loc,'@'); + if(ptr != NULL){ + location->host=strdup(ptr+1); + *ptr='\0'; + location->user=strdup(loc); + } else { + location->host=strdup(loc); + } + } else { + location->is_ssh=0; + location->path=strdup(loc); + } + return location; +} + +static int open_location(struct location *loc, int flag){ + if(loc->is_ssh && flag==WRITE){ + loc->session=connect_ssh(loc->host,loc->user,verbosity); + if(!loc->session){ + fprintf(stderr,"Couldn't connect to %s\n",loc->host); + return -1; + } + loc->scp=ssh_scp_new(loc->session,SSH_SCP_WRITE,loc->path); + if(!loc->scp){ + fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); + return -1; + } + if(ssh_scp_init(loc->scp)==SSH_ERROR){ + fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); + ssh_scp_free(loc->scp); + return -1; + } + return 0; + } else if(loc->is_ssh && flag==READ){ + loc->session=connect_ssh(loc->host, loc->user,verbosity); + if(!loc->session){ + fprintf(stderr,"Couldn't connect to %s\n",loc->host); + return -1; + } + loc->scp=ssh_scp_new(loc->session,SSH_SCP_READ,loc->path); + if(!loc->scp){ + fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); + return -1; + } + if(ssh_scp_init(loc->scp)==SSH_ERROR){ + fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); + ssh_scp_free(loc->scp); + return -1; + } + return 0; + } else { + loc->file=fopen(loc->path,flag==READ ? "r":"w"); + if(!loc->file){ + if(errno==EISDIR){ + if(chdir(loc->path)){ + fprintf(stderr,"Error changing directory to %s: %s\n",loc->path,strerror(errno)); + return -1; + } + return 0; + } + fprintf(stderr,"Error opening %s: %s\n",loc->path,strerror(errno)); + return -1; + } + return 0; + } + return -1; +} + +/** @brief copies files from source location to destination + * @param src source location + * @param dest destination location + * @param recursive Copy also directories + */ +static int do_copy(struct location *src, struct location *dest, int recursive){ + int size; + socket_t fd; + struct stat s; + int w,r; + char buffer[16384]; + int total=0; + int mode; + char *filename; + /* recursive mode doesn't work yet */ + (void)recursive; + /* Get the file name and size*/ + if(!src->is_ssh){ + fd=fileno(src->file); + fstat(fd,&s); + size=s.st_size; + mode = s.st_mode & ~S_IFMT; + filename=ssh_basename(src->path); + } else { + size=0; + do { + r=ssh_scp_pull_request(src->scp); + if(r==SSH_SCP_REQUEST_NEWDIR){ + ssh_scp_deny_request(src->scp,"Not in recursive mode"); + continue; + } + if(r==SSH_SCP_REQUEST_NEWFILE){ + size=ssh_scp_request_get_size(src->scp); + filename=strdup(ssh_scp_request_get_filename(src->scp)); + mode=ssh_scp_request_get_permissions(src->scp); + //ssh_scp_accept_request(src->scp); + break; + } + if(r==SSH_ERROR){ + fprintf(stderr,"Error: %s\n",ssh_get_error(src->session)); + return -1; + } + } while(r != SSH_SCP_REQUEST_NEWFILE); + } + + if(dest->is_ssh){ + r=ssh_scp_push_file(dest->scp,src->path, size, mode); + // snprintf(buffer,sizeof(buffer),"C0644 %d %s\n",size,src->path); + if(r==SSH_ERROR){ + fprintf(stderr,"error: %s\n",ssh_get_error(dest->session)); + ssh_scp_free(dest->scp); + return -1; + } + } else { + if(!dest->file){ + dest->file=fopen(filename,"w"); + if(!dest->file){ + fprintf(stderr,"Cannot open %s for writing: %s\n",filename,strerror(errno)); + if(src->is_ssh) + ssh_scp_deny_request(src->scp,"Cannot open local file"); + return -1; + } + } + if(src->is_ssh){ + ssh_scp_accept_request(src->scp); + } + } + do { + if(src->is_ssh){ + r=ssh_scp_read(src->scp,buffer,sizeof(buffer)); + if(r==SSH_ERROR){ + fprintf(stderr,"Error reading scp: %s\n",ssh_get_error(src->session)); + return -1; + } + if(r==0) + break; + } else { + r=fread(buffer,1,sizeof(buffer),src->file); + if(r==0) + break; + if(r<0){ + fprintf(stderr,"Error reading file: %s\n",strerror(errno)); + return -1; + } + } + if(dest->is_ssh){ + w=ssh_scp_write(dest->scp,buffer,r); + if(w == SSH_ERROR){ + fprintf(stderr,"Error writing in scp: %s\n",ssh_get_error(dest->session)); + ssh_scp_free(dest->scp); + dest->scp=NULL; + return -1; + } + } else { + w=fwrite(buffer,r,1,dest->file); + if(w<=0){ + fprintf(stderr,"Error writing in local file: %s\n",strerror(errno)); + return -1; + } + } + total+=r; + + } while(total < size); + printf("wrote %d bytes\n",total); + return 0; +} + +int main(int argc, char **argv){ + struct location *dest, *src; + int i; + int r; + if(opts(argc,argv)<0) + return EXIT_FAILURE; + dest=parse_location(destination); + if(open_location(dest,WRITE)<0) + return EXIT_FAILURE; + for(i=0;iis_ssh){ + r=ssh_scp_close(dest->scp); + if(r == SSH_ERROR){ + fprintf(stderr,"Error closing scp: %s\n",ssh_get_error(dest->session)); + ssh_scp_free(dest->scp); + dest->scp=NULL; + return -1; + } + } else { + fclose(dest->file); + dest->file=NULL; + } + ssh_disconnect(dest->session); + ssh_finalize(); + return 0; +} diff --git a/libssh/examples/libsshpp.cpp b/libssh/examples/libsshpp.cpp new file mode 100644 index 00000000..8f042a45 --- /dev/null +++ b/libssh/examples/libsshpp.cpp @@ -0,0 +1,33 @@ +/* +Copyright 2010 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +*/ + +/* This file demonstrates the use of the C++ wrapper to libssh */ + +#include +#include +#include + +int main(int argc, const char **argv){ + ssh::Session session; + try { + if(argc>1) + session.setOption(SSH_OPTIONS_HOST,argv[1]); + else + session.setOption(SSH_OPTIONS_HOST,"localhost"); + session.connect(); + session.userauthPublickeyAuto(); + session.disconnect(); + } catch (ssh::SshException e){ + std::cout << "Error during connection : "; + std::cout << e.getError() << std::endl; + } + return 0; +} diff --git a/libssh/examples/libsshpp_noexcept.cpp b/libssh/examples/libsshpp_noexcept.cpp new file mode 100644 index 00000000..eff8cc19 --- /dev/null +++ b/libssh/examples/libsshpp_noexcept.cpp @@ -0,0 +1,41 @@ +/* +Copyright 2010 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +*/ + +/* This file demonstrates the use of the C++ wrapper to libssh + * specifically, without C++ exceptions + */ + +#include +#define SSH_NO_CPP_EXCEPTIONS +#include + +int main(int argc, const char **argv){ + ssh::Session session,s2; + int err; + if(argc>1) + err=session.setOption(SSH_OPTIONS_HOST,argv[1]); + else + err=session.setOption(SSH_OPTIONS_HOST,"localhost"); + if(err==SSH_ERROR) + goto error; + err=session.connect(); + if(err==SSH_ERROR) + goto error; + err=session.userauthPublickeyAuto(); + if(err==SSH_ERROR) + goto error; + + return 0; + error: + std::cout << "Error during connection : "; + std::cout << session.getError() << std::endl; + return 1; +} diff --git a/libssh/examples/sample.c b/libssh/examples/sample.c new file mode 100644 index 00000000..cfe4b3a6 --- /dev/null +++ b/libssh/examples/sample.c @@ -0,0 +1,519 @@ +/* client.c */ +/* +Copyright 2003-2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ + +#include "config.h" +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_PTY_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include + +#include "examples_common.h" +#define MAXCMD 10 + +static char *host; +static char *user; +static char *cmds[MAXCMD]; +static struct termios terminal; + +static char *pcap_file=NULL; + +static char *proxycommand; + +static int auth_callback(const char *prompt, char *buf, size_t len, + int echo, int verify, void *userdata) { + (void) verify; + (void) userdata; + + return ssh_getpass(prompt, buf, len, echo, verify); +} + +struct ssh_callbacks_struct cb = { + .auth_function=auth_callback, + .userdata=NULL +}; + +static void add_cmd(char *cmd){ + int n; + for(n=0;cmds[n] && (nc_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + termios_p->c_oflag &= ~OPOST; + termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + termios_p->c_cflag &= ~(CSIZE|PARENB); + termios_p->c_cflag |= CS8; +} +#endif + + +static void do_cleanup(int i) { + /* unused variable */ + (void) i; + + tcsetattr(0,TCSANOW,&terminal); +} + +static void do_exit(int i) { + /* unused variable */ + (void) i; + + do_cleanup(0); + exit(0); +} + +ssh_channel chan; +int signal_delayed=0; + +static void sigwindowchanged(int i){ + (void) i; + signal_delayed=1; +} + +static void setsignal(void){ + signal(SIGWINCH, sigwindowchanged); + signal_delayed=0; +} + +static void sizechanged(void){ + struct winsize win = { 0, 0, 0, 0 }; + ioctl(1, TIOCGWINSZ, &win); + ssh_channel_change_pty_size(chan,win.ws_col, win.ws_row); +// printf("Changed pty size\n"); + setsignal(); +} + +/* There are two flavors of select loop: the one based on + * ssh_select and the one based on channel_select. + * The ssh_select one permits you to give your own file descriptors to + * follow. It is thus a complete select loop. + * The second one only selects on channels. It is simplier to use + * but doesn't permit you to fill in your own file descriptor. It is + * more adapted if you can't use ssh_select as a main loop (because + * you already have another main loop system). + */ + +#ifdef USE_CHANNEL_SELECT + +/* channel_select base main loop, with a standard select(2) + */ +static void select_loop(ssh_session session,ssh_channel channel){ + fd_set fds; + struct timeval timeout; + char buffer[4096]; + ssh_buffer readbuf=ssh_buffer_new(); + ssh_channel channels[2]; + int lus; + int eof=0; + int maxfd; + int ret; + while(channel){ + /* when a signal is caught, ssh_select will return + * with SSH_EINTR, which means it should be started + * again. It lets you handle the signal the faster you + * can, like in this window changed example. Of course, if + * your signal handler doesn't call libssh at all, you're + * free to handle signals directly in sighandler. + */ + do{ + FD_ZERO(&fds); + if(!eof) + FD_SET(0,&fds); + timeout.tv_sec=30; + timeout.tv_usec=0; + FD_SET(ssh_get_fd(session),&fds); + maxfd=ssh_get_fd(session)+1; + ret=select(maxfd,&fds,NULL,NULL,&timeout); + if(ret==EINTR) + continue; + if(FD_ISSET(0,&fds)){ + lus=read(0,buffer,sizeof(buffer)); + if(lus) + ssh_channel_write(channel,buffer,lus); + else { + eof=1; + ssh_channel_send_eof(channel); + } + } + if(FD_ISSET(ssh_get_fd(session),&fds)){ + ssh_set_fd_toread(session); + } + channels[0]=channel; // set the first channel we want to read from + channels[1]=NULL; + ret=ssh_channel_select(channels,NULL,NULL,NULL); // no specific timeout - just poll + if(signal_delayed) + sizechanged(); + } while (ret==EINTR || ret==SSH_EINTR); + + // we already looked for input from stdin. Now, we are looking for input from the channel + + if(channel && ssh_channel_is_closed(channel)){ + ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); + + ssh_channel_free(channel); + channel=NULL; + channels[0]=NULL; + } + if(channels[0]){ + while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,0)>0){ + lus=channel_read_buffer(channel,readbuf,0,0); + if(lus==-1){ + fprintf(stderr, "Error reading channel: %s\n", + ssh_get_error(session)); + return; + } + if(lus==0){ + ssh_log(session,SSH_LOG_RARE,"EOF received"); + ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); + + ssh_channel_free(channel); + channel=channels[0]=NULL; + } else + if (write(1,ssh_buffer_get_begin(readbuf),lus) < 0) { + fprintf(stderr, "Error writing to buffer\n"); + return; + } + } + while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,1)>0){ /* stderr */ + lus=channel_read_buffer(channel,readbuf,0,1); + if(lus==-1){ + fprintf(stderr, "Error reading channel: %s\n", + ssh_get_error(session)); + return; + } + if(lus==0){ + ssh_log(session,SSH_LOG_RARE,"EOF received"); + ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); + ssh_channel_free(channel); + channel=channels[0]=NULL; + } else + if (write(2,ssh_buffer_get_begin(readbuf),lus) < 0) { + fprintf(stderr, "Error writing to buffer\n"); + return; + } + } + } + if(channel && ssh_channel_is_closed(channel)){ + ssh_channel_free(channel); + channel=NULL; + } + } + ssh_buffer_free(readbuf); +} +#else /* CHANNEL_SELECT */ + +static void select_loop(ssh_session session,ssh_channel channel){ + fd_set fds; + struct timeval timeout; + char buffer[4096]; + /* channels will be set to the channels to poll. + * outchannels will contain the result of the poll + */ + ssh_channel channels[2], outchannels[2]; + int lus; + int eof=0; + int maxfd; + unsigned int r; + int ret; + while(channel){ + do{ + FD_ZERO(&fds); + if(!eof) + FD_SET(0,&fds); + timeout.tv_sec=30; + timeout.tv_usec=0; + FD_SET(ssh_get_fd(session),&fds); + maxfd=ssh_get_fd(session)+1; + channels[0]=channel; // set the first channel we want to read from + channels[1]=NULL; + ret=ssh_select(channels,outchannels,maxfd,&fds,&timeout); + if(signal_delayed) + sizechanged(); + if(ret==EINTR) + continue; + if(FD_ISSET(0,&fds)){ + lus=read(0,buffer,sizeof(buffer)); + if(lus) + ssh_channel_write(channel,buffer,lus); + else { + eof=1; + ssh_channel_send_eof(channel); + } + } + if(channel && ssh_channel_is_closed(channel)){ + ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); + + ssh_channel_free(channel); + channel=NULL; + channels[0]=NULL; + } + if(outchannels[0]){ + while(channel && ssh_channel_is_open(channel) && (r = ssh_channel_poll(channel,0))!=0){ + lus=ssh_channel_read(channel,buffer,sizeof(buffer) > r ? r : sizeof(buffer),0); + if(lus==-1){ + fprintf(stderr, "Error reading channel: %s\n", + ssh_get_error(session)); + return; + } + if(lus==0){ + ssh_log(session,SSH_LOG_RARE,"EOF received"); + ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); + + ssh_channel_free(channel); + channel=channels[0]=NULL; + } else + if (write(1,buffer,lus) < 0) { + fprintf(stderr, "Error writing to buffer\n"); + return; + } + } + while(channel && ssh_channel_is_open(channel) && (r = ssh_channel_poll(channel,1))!=0){ /* stderr */ + lus=ssh_channel_read(channel,buffer,sizeof(buffer) > r ? r : sizeof(buffer),1); + if(lus==-1){ + fprintf(stderr, "Error reading channel: %s\n", + ssh_get_error(session)); + return; + } + if(lus==0){ + ssh_log(session,SSH_LOG_RARE,"EOF received"); + ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); + ssh_channel_free(channel); + channel=channels[0]=NULL; + } else + if (write(2,buffer,lus) < 0) { + fprintf(stderr, "Error writing to buffer\n"); + return; + } + } + } + if(channel && ssh_channel_is_closed(channel)){ + ssh_channel_free(channel); + channel=NULL; + } + } while (ret==EINTR || ret==SSH_EINTR); + + } +} + +#endif + +static void shell(ssh_session session){ + ssh_channel channel; + struct termios terminal_local; + int interactive=isatty(0); + channel = ssh_channel_new(session); + if(interactive){ + tcgetattr(0,&terminal_local); + memcpy(&terminal,&terminal_local,sizeof(struct termios)); + } + if(ssh_channel_open_session(channel)){ + printf("error opening channel : %s\n",ssh_get_error(session)); + return; + } + chan=channel; + if(interactive){ + ssh_channel_request_pty(channel); + sizechanged(); + } + if(ssh_channel_request_shell(channel)){ + printf("Requesting shell : %s\n",ssh_get_error(session)); + return; + } + if(interactive){ + cfmakeraw(&terminal_local); + tcsetattr(0,TCSANOW,&terminal_local); + setsignal(); + } + signal(SIGTERM,do_cleanup); + select_loop(session,channel); + if(interactive) + do_cleanup(0); +} + +static void batch_shell(ssh_session session){ + ssh_channel channel; + char buffer[1024]; + int i,s=0; + for(i=0;i +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "examples_common.h" +#ifdef WITH_SFTP + +static int verbosity; +static char *destination; + +#define DATALEN 65536 +static void do_sftp(ssh_session session){ + sftp_session sftp=sftp_new(session); + sftp_dir dir; + sftp_attributes file; + sftp_statvfs_t sftpstatvfs; + struct statvfs sysstatvfs; + sftp_file fichier; + sftp_file to; + int len=1; + unsigned int i; + char data[DATALEN]={0}; + char *lnk; + + unsigned int count; + + if(!sftp){ + fprintf(stderr, "sftp error initialising channel: %s\n", + ssh_get_error(session)); + return; + } + if(sftp_init(sftp)){ + fprintf(stderr, "error initialising sftp: %s\n", + ssh_get_error(session)); + return; + } + + printf("Additional SFTP extensions provided by the server:\n"); + count = sftp_extensions_get_count(sftp); + for (i = 0; i < count; i++) { + printf("\t%s, version: %s\n", + sftp_extensions_get_name(sftp, i), + sftp_extensions_get_data(sftp, i)); + } + + /* test symlink and readlink */ + if (sftp_symlink(sftp, "/tmp/this_is_the_link", + "/tmp/sftp_symlink_test") < 0) { + fprintf(stderr, "Could not create link (%s)\n", ssh_get_error(session)); + return; + } + + lnk = sftp_readlink(sftp, "/tmp/sftp_symlink_test"); + if (lnk == NULL) { + fprintf(stderr, "Could not read link (%s)\n", ssh_get_error(session)); + return; + } + printf("readlink /tmp/sftp_symlink_test: %s\n", lnk); + + sftp_unlink(sftp, "/tmp/sftp_symlink_test"); + + if (sftp_extension_supported(sftp, "statvfs@openssh.com", "2")) { + sftpstatvfs = sftp_statvfs(sftp, "/tmp"); + if (sftpstatvfs == NULL) { + fprintf(stderr, "statvfs failed (%s)\n", ssh_get_error(session)); + return; + } + + printf("sftp statvfs:\n" + "\tfile system block size: %llu\n" + "\tfundamental fs block size: %llu\n" + "\tnumber of blocks (unit f_frsize): %llu\n" + "\tfree blocks in file system: %llu\n" + "\tfree blocks for non-root: %llu\n" + "\ttotal file inodes: %llu\n" + "\tfree file inodes: %llu\n" + "\tfree file inodes for to non-root: %llu\n" + "\tfile system id: %llu\n" + "\tbit mask of f_flag values: %llu\n" + "\tmaximum filename length: %llu\n", + (unsigned long long) sftpstatvfs->f_bsize, + (unsigned long long) sftpstatvfs->f_frsize, + (unsigned long long) sftpstatvfs->f_blocks, + (unsigned long long) sftpstatvfs->f_bfree, + (unsigned long long) sftpstatvfs->f_bavail, + (unsigned long long) sftpstatvfs->f_files, + (unsigned long long) sftpstatvfs->f_ffree, + (unsigned long long) sftpstatvfs->f_favail, + (unsigned long long) sftpstatvfs->f_fsid, + (unsigned long long) sftpstatvfs->f_flag, + (unsigned long long) sftpstatvfs->f_namemax); + + sftp_statvfs_free(sftpstatvfs); + + if (statvfs("/tmp", &sysstatvfs) < 0) { + fprintf(stderr, "statvfs failed (%s)\n", strerror(errno)); + return; + } + + printf("sys statvfs:\n" + "\tfile system block size: %llu\n" + "\tfundamental fs block size: %llu\n" + "\tnumber of blocks (unit f_frsize): %llu\n" + "\tfree blocks in file system: %llu\n" + "\tfree blocks for non-root: %llu\n" + "\ttotal file inodes: %llu\n" + "\tfree file inodes: %llu\n" + "\tfree file inodes for to non-root: %llu\n" + "\tfile system id: %llu\n" + "\tbit mask of f_flag values: %llu\n" + "\tmaximum filename length: %llu\n", + (unsigned long long) sysstatvfs.f_bsize, + (unsigned long long) sysstatvfs.f_frsize, + (unsigned long long) sysstatvfs.f_blocks, + (unsigned long long) sysstatvfs.f_bfree, + (unsigned long long) sysstatvfs.f_bavail, + (unsigned long long) sysstatvfs.f_files, + (unsigned long long) sysstatvfs.f_ffree, + (unsigned long long) sysstatvfs.f_favail, + (unsigned long long) sysstatvfs.f_fsid, + (unsigned long long) sysstatvfs.f_flag, + (unsigned long long) sysstatvfs.f_namemax); + } + + /* the connection is made */ + /* opening a directory */ + dir=sftp_opendir(sftp,"./"); + if(!dir) { + fprintf(stderr, "Directory not opened(%s)\n", ssh_get_error(session)); + return ; + } + /* reading the whole directory, file by file */ + while((file=sftp_readdir(sftp,dir))){ + fprintf(stderr, "%30s(%.8o) : %s(%.5d) %s(%.5d) : %.10llu bytes\n", + file->name, + file->permissions, + file->owner, + file->uid, + file->group, + file->gid, + (long long unsigned int) file->size); + sftp_attributes_free(file); + } + /* when file=NULL, an error has occured OR the directory listing is end of file */ + if(!sftp_dir_eof(dir)){ + fprintf(stderr, "Error: %s\n", ssh_get_error(session)); + return; + } + if(sftp_closedir(dir)){ + fprintf(stderr, "Error: %s\n", ssh_get_error(session)); + return; + } + /* this will open a file and copy it into your /home directory */ + /* the small buffer size was intended to stress the library. of course, you can use a buffer till 20kbytes without problem */ + + fichier=sftp_open(sftp,"/usr/bin/ssh",O_RDONLY, 0); + if(!fichier){ + fprintf(stderr, "Error opening /usr/bin/ssh: %s\n", + ssh_get_error(session)); + return; + } + /* open a file for writing... */ + to=sftp_open(sftp,"ssh-copy",O_WRONLY | O_CREAT, 0700); + if(!to){ + fprintf(stderr, "Error opening ssh-copy for writing: %s\n", + ssh_get_error(session)); + return; + } + while((len=sftp_read(fichier,data,4096)) > 0){ + if(sftp_write(to,data,len)!=len){ + fprintf(stderr, "Error writing %d bytes: %s\n", + len, ssh_get_error(session)); + return; + } + } + printf("finished\n"); + if(len<0) + fprintf(stderr, "Error reading file: %s\n", ssh_get_error(session)); + sftp_close(fichier); + sftp_close(to); + printf("fichiers ferm\n"); + to=sftp_open(sftp,"/tmp/grosfichier",O_WRONLY|O_CREAT, 0644); + for(i=0;i<1000;++i){ + len=sftp_write(to,data,DATALEN); + printf("wrote %d bytes\n",len); + if(len != DATALEN){ + printf("chunk %d : %d (%s)\n",i,len,ssh_get_error(session)); + } + } + sftp_close(to); + + /* close the sftp session */ + sftp_free(sftp); + printf("sftp session terminated\n"); +} + +static void usage(const char *argv0){ + fprintf(stderr,"Usage : %s [-v] remotehost\n" + "sample sftp test client - libssh-%s\n" + "Options :\n" + " -v : increase log verbosity\n", + argv0, + ssh_version(0)); + exit(0); +} + +static int opts(int argc, char **argv){ + int i; + while((i=getopt(argc,argv,"v"))!=-1){ + switch(i){ + case 'v': + verbosity++; + break; + default: + fprintf(stderr,"unknown option %c\n",optopt); + usage(argv[0]); + return -1; + } + } + + destination=argv[optind]; + if(destination == NULL){ + usage(argv[0]); + return -1; + } + return 0; +} + +int main(int argc, char **argv){ + ssh_session session; + if(opts(argc,argv)<0) + return EXIT_FAILURE; + session=connect_ssh(destination,NULL,verbosity); + if(session == NULL) + return EXIT_FAILURE; + do_sftp(session); + ssh_disconnect(session); + ssh_free(session); + return 0; +} + + + +#endif diff --git a/libssh/examples/samplesshd-kbdint.c b/libssh/examples/samplesshd-kbdint.c new file mode 100644 index 00000000..faecfd9b --- /dev/null +++ b/libssh/examples/samplesshd-kbdint.c @@ -0,0 +1,413 @@ +/* This is a sample implementation of a libssh based SSH server */ +/* +Copyright 2003-2011 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ + +#include "config.h" + +#include +#include + +#ifdef HAVE_ARGP_H +#include +#endif +#include +#include +#include + +#define SSHD_USER "libssh" +#define SSHD_PASSWORD "libssh" + +#ifndef KEYS_FOLDER +#ifdef _WIN32 +#define KEYS_FOLDER +#else +#define KEYS_FOLDER "/etc/ssh/" +#endif +#endif + +#ifdef WITH_PCAP +static const char *pcap_file = "debug.server.pcap"; +static ssh_pcap_file pcap; + +static void set_pcap(ssh_session session){ + if(!pcap_file) + return; + pcap=ssh_pcap_file_new(); + if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ + printf("Error opening pcap file\n"); + ssh_pcap_file_free(pcap); + pcap=NULL; + return; + } + ssh_set_pcap_file(session,pcap); +} + +static void cleanup_pcap(void) { + ssh_pcap_file_free(pcap); + pcap=NULL; +} +#endif + + +static int auth_password(const char *user, const char *password){ + if(strcmp(user, SSHD_USER)) + return 0; + if(strcmp(password, SSHD_PASSWORD)) + return 0; + return 1; // authenticated +} +#ifdef HAVE_ARGP_H +const char *argp_program_version = "libssh server example " + SSH_STRINGIFY(LIBSSH_VERSION); +const char *argp_program_bug_address = ""; + +/* Program documentation. */ +static char doc[] = "libssh -- a Secure Shell protocol implementation"; + +/* A description of the arguments we accept. */ +static char args_doc[] = "BINDADDR"; + +static int port = 22; + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "port", + .key = 'p', + .arg = "PORT", + .flags = 0, + .doc = "Set the port to bind.", + .group = 0 + }, + { + .name = "hostkey", + .key = 'k', + .arg = "FILE", + .flags = 0, + .doc = "Set the host key.", + .group = 0 + }, + { + .name = "dsakey", + .key = 'd', + .arg = "FILE", + .flags = 0, + .doc = "Set the dsa key.", + .group = 0 + }, + { + .name = "rsakey", + .key = 'r', + .arg = "FILE", + .flags = 0, + .doc = "Set the rsa key.", + .group = 0 + }, + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Get verbose output.", + .group = 0 + }, + {NULL, 0, 0, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + ssh_bind sshbind = state->input; + + switch (key) { + case 'p': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); + port = atoi(arg); + break; + case 'd': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); + break; + case 'k': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); + break; + case 'r': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); + break; + case 'v': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + /* Too many arguments. */ + argp_usage (state); + } + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); + break; + case ARGP_KEY_END: + if (state->arg_num < 1) { + /* Not enough arguments. */ + argp_usage (state); + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Our argp parser. */ +static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; +#endif /* HAVE_ARGP_H */ + +static const char *name; +static const char *instruction; +static const char *prompts[2]; +static char echo[] = { 1, 0 }; + +static int kbdint_check_response(ssh_session session) { + int count; + + count = ssh_userauth_kbdint_getnanswers(session); + if(count != 2) { + instruction = "Something weird happened :("; + return 0; + } + if(strcasecmp("Arthur Dent", + ssh_userauth_kbdint_getanswer(session, 0)) != 0) { + instruction = "OK, this is not YOUR name, " + "but it's a reference to the HGTG..."; + prompts[0] = "The main character's full name: "; + return 0; + } + if(strcmp("42", ssh_userauth_kbdint_getanswer(session, 1)) != 0) { + instruction = "Make an effort !!! What is the Answer to the Ultimate " + "Question of Life, the Universe, and Everything ?"; + prompts[1] = "Answer to the Ultimate Question of Life, the Universe, " + "and Everything: "; + return 0; + } + + return 1; +} + +static int authenticate(ssh_session session) { + ssh_message message; + + name = "\n\nKeyboard-Interactive Fancy Authentication\n"; + instruction = "Please enter your real name and your password"; + prompts[0] = "Real name: "; + prompts[1] = "Password: "; + + do { + message=ssh_message_get(session); + if(!message) + break; + switch(ssh_message_type(message)){ + case SSH_REQUEST_AUTH: + switch(ssh_message_subtype(message)){ + case SSH_AUTH_METHOD_PASSWORD: + printf("User %s wants to auth with pass %s\n", + ssh_message_auth_user(message), + ssh_message_auth_password(message)); + if(auth_password(ssh_message_auth_user(message), + ssh_message_auth_password(message))){ + ssh_message_auth_reply_success(message,0); + ssh_message_free(message); + return 1; + } + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + // not authenticated, send default message + ssh_message_reply_default(message); + break; + + case SSH_AUTH_METHOD_INTERACTIVE: + if(!ssh_message_auth_kbdint_is_response(message)) { + printf("User %s wants to auth with kbdint\n", + ssh_message_auth_user(message)); + ssh_message_auth_interactive_request(message, name, + instruction, 2, prompts, echo); + } else { + if(kbdint_check_response(session)) { + ssh_message_auth_reply_success(message,0); + ssh_message_free(message); + return 1; + } + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + ssh_message_reply_default(message); + } + break; + case SSH_AUTH_METHOD_NONE: + default: + printf("User %s wants to auth with unknown auth %d\n", + ssh_message_auth_user(message), + ssh_message_subtype(message)); + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + ssh_message_reply_default(message); + break; + } + break; + default: + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + ssh_message_reply_default(message); + } + ssh_message_free(message); + } while (1); + return 0; +} + +int main(int argc, char **argv){ + ssh_session session; + ssh_bind sshbind; + ssh_message message; + ssh_channel chan=0; + char buf[2048]; + int auth=0; + int shell=0; + int i; + int r; + + sshbind=ssh_bind_new(); + session=ssh_new(); + + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, + KEYS_FOLDER "ssh_host_dsa_key"); + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, + KEYS_FOLDER "ssh_host_rsa_key"); + +#ifdef HAVE_ARGP_H + /* + * Parse our arguments; every option seen by parse_opt will + * be reflected in arguments. + */ + argp_parse (&argp, argc, argv, 0, 0, sshbind); +#else + (void) argc; + (void) argv; +#endif +#ifdef WITH_PCAP + set_pcap(session); +#endif + + if(ssh_bind_listen(sshbind)<0){ + printf("Error listening to socket: %s\n", ssh_get_error(sshbind)); + return 1; + } + printf("Started sample libssh sshd on port %d\n", port); + printf("You can login as the user %s with the password %s\n", SSHD_USER, + SSHD_PASSWORD); + r = ssh_bind_accept(sshbind, session); + if(r==SSH_ERROR){ + printf("Error accepting a connection: %s\n", ssh_get_error(sshbind)); + return 1; + } + if (ssh_handle_key_exchange(session)) { + printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); + return 1; + } + + /* proceed to authentication */ + auth = authenticate(session); + if(!auth){ + printf("Authentication error: %s\n", ssh_get_error(session)); + ssh_disconnect(session); + return 1; + } + + + /* wait for a channel session */ + do { + message = ssh_message_get(session); + if(message){ + if(ssh_message_type(message) == SSH_REQUEST_CHANNEL_OPEN && + ssh_message_subtype(message) == SSH_CHANNEL_SESSION) { + chan = ssh_message_channel_request_open_reply_accept(message); + ssh_message_free(message); + break; + } else { + ssh_message_reply_default(message); + ssh_message_free(message); + } + } else { + break; + } + } while(!chan); + + if(!chan) { + printf("Error: cleint did not ask for a channel session (%s)\n", + ssh_get_error(session)); + ssh_finalize(); + return 1; + } + + + /* wait for a shell */ + do { + message = ssh_message_get(session); + if(message != NULL) { + if(ssh_message_type(message) == SSH_REQUEST_CHANNEL && + ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_SHELL) { + shell = 1; + ssh_message_channel_request_reply_success(message); + ssh_message_free(message); + break; + } + ssh_message_reply_default(message); + ssh_message_free(message); + } else { + break; + } + } while(!shell); + + if(!shell) { + printf("Error: No shell requested (%s)\n", ssh_get_error(session)); + return 1; + } + + + printf("it works !\n"); + do{ + i=ssh_channel_read(chan,buf, 2048, 0); + if(i>0) { + if(*buf == '' || *buf == '') + break; + if(i == 1 && *buf == '\r') + ssh_channel_write(chan, "\r\n", 2); + else + ssh_channel_write(chan, buf, i); + if (write(1,buf,i) < 0) { + printf("error writing to buffer\n"); + return 1; + } + } + } while (i>0); + ssh_channel_close(chan); + ssh_disconnect(session); + ssh_bind_free(sshbind); +#ifdef WITH_PCAP + cleanup_pcap(); +#endif + ssh_finalize(); + return 0; +} + diff --git a/libssh/examples/samplesshd-tty.c b/libssh/examples/samplesshd-tty.c new file mode 100644 index 00000000..373ec079 --- /dev/null +++ b/libssh/examples/samplesshd-tty.c @@ -0,0 +1,453 @@ +/* This is a sample implementation of a libssh based SSH server */ +/* +Copyright 2003-2011 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_ARGP_H +#include +#endif +#include +#include +#include +#include +#include + +#define SSHD_USER "libssh" +#define SSHD_PASSWORD "libssh" + +#ifndef KEYS_FOLDER +#ifdef _WIN32 +#define KEYS_FOLDER +#else +#define KEYS_FOLDER "/etc/ssh/" +#endif +#endif + +#ifdef WITH_PCAP +const char *pcap_file="debug.server.pcap"; +ssh_pcap_file pcap; + +static void set_pcap(ssh_session session){ + if(!pcap_file) + return; + pcap=ssh_pcap_file_new(); + if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ + printf("Error opening pcap file\n"); + ssh_pcap_file_free(pcap); + pcap=NULL; + return; + } + ssh_set_pcap_file(session,pcap); +} + +static void cleanup_pcap(){ + ssh_pcap_file_free(pcap); + pcap=NULL; +} +#endif + + +static int auth_password(const char *user, const char *password){ + if(strcmp(user, SSHD_USER)) + return 0; + if(strcmp(password, SSHD_PASSWORD)) + return 0; + return 1; // authenticated +} +#ifdef HAVE_ARGP_H +const char *argp_program_version = "libssh server example " + SSH_STRINGIFY(LIBSSH_VERSION); +const char *argp_program_bug_address = ""; + +/* Program documentation. */ +static char doc[] = "libssh -- a Secure Shell protocol implementation"; + +/* A description of the arguments we accept. */ +static char args_doc[] = "BINDADDR"; + +static int port = 22; + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "port", + .key = 'p', + .arg = "PORT", + .flags = 0, + .doc = "Set the port to bind.", + .group = 0 + }, + { + .name = "hostkey", + .key = 'k', + .arg = "FILE", + .flags = 0, + .doc = "Set the host key.", + .group = 0 + }, + { + .name = "dsakey", + .key = 'd', + .arg = "FILE", + .flags = 0, + .doc = "Set the dsa key.", + .group = 0 + }, + { + .name = "rsakey", + .key = 'r', + .arg = "FILE", + .flags = 0, + .doc = "Set the rsa key.", + .group = 0 + }, + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Get verbose output.", + .group = 0 + }, + {NULL, 0, 0, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + ssh_bind sshbind = state->input; + + switch (key) { + case 'p': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); + port = atoi(arg); + break; + case 'd': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); + break; + case 'k': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); + break; + case 'r': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); + break; + case 'v': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + /* Too many arguments. */ + argp_usage (state); + } + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); + break; + case ARGP_KEY_END: + if (state->arg_num < 1) { + /* Not enough arguments. */ + argp_usage (state); + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Our argp parser. */ +static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; +#endif /* HAVE_ARGP_H */ + +static int authenticate(ssh_session session) { + ssh_message message; + + do { + message=ssh_message_get(session); + if(!message) + break; + switch(ssh_message_type(message)){ + case SSH_REQUEST_AUTH: + switch(ssh_message_subtype(message)){ + case SSH_AUTH_METHOD_PASSWORD: + printf("User %s wants to auth with pass %s\n", + ssh_message_auth_user(message), + ssh_message_auth_password(message)); + if(auth_password(ssh_message_auth_user(message), + ssh_message_auth_password(message))){ + ssh_message_auth_reply_success(message,0); + ssh_message_free(message); + return 1; + } + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + // not authenticated, send default message + ssh_message_reply_default(message); + break; + + case SSH_AUTH_METHOD_NONE: + default: + printf("User %s wants to auth with unknown auth %d\n", + ssh_message_auth_user(message), + ssh_message_subtype(message)); + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + ssh_message_reply_default(message); + break; + } + break; + default: + ssh_message_auth_set_methods(message, + SSH_AUTH_METHOD_PASSWORD | + SSH_AUTH_METHOD_INTERACTIVE); + ssh_message_reply_default(message); + } + ssh_message_free(message); + } while (1); + return 0; +} + +static int copy_fd_to_chan(socket_t fd, int revents, void *userdata) { + ssh_channel chan = (ssh_channel)userdata; + char buf[2048]; + int sz = 0; + + if(!chan) { + close(fd); + return -1; + } + if(revents & POLLIN) { + sz = read(fd, buf, 2048); + if(sz > 0) { + ssh_channel_write(chan, buf, sz); + } + } + if(revents & POLLHUP) { + ssh_channel_close(chan); + sz = -1; + } + return sz; +} + +static int copy_chan_to_fd(ssh_session session, + ssh_channel channel, + void *data, + uint32_t len, + int is_stderr, + void *userdata) { + int fd = *(int*)userdata; + int sz; + (void)session; + (void)channel; + (void)is_stderr; + + sz = write(fd, data, len); + return sz; +} + +static void chan_close(ssh_session session, ssh_channel channel, void *userdata) { + int fd = *(int*)userdata; + (void)session; + (void)channel; + + close(fd); +} + +struct ssh_channel_callbacks_struct cb = { + .channel_data_function = copy_chan_to_fd, + .channel_eof_function = chan_close, + .channel_close_function = chan_close, + .userdata = NULL +}; + +static int main_loop(ssh_channel chan) { + ssh_session session = ssh_channel_get_session(chan); + socket_t fd; + struct termios *term = NULL; + struct winsize *win = NULL; + pid_t childpid; + ssh_event event; + short events; + + + childpid = forkpty(&fd, NULL, term, win); + if(childpid == 0) { + execl("/bin/bash", "/bin/bash", (char *)NULL); + abort(); + } + + cb.userdata = &fd; + ssh_callbacks_init(&cb); + ssh_set_channel_callbacks(chan, &cb); + + events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; + + event = ssh_event_new(); + if(event == NULL) { + printf("Couldn't get a event\n"); + return -1; + } + if(ssh_event_add_fd(event, fd, events, copy_fd_to_chan, chan) != SSH_OK) { + printf("Couldn't add an fd to the event\n"); + return -1; + } + if(ssh_event_add_session(event, session) != SSH_OK) { + printf("Couldn't add the session to the event\n"); + return -1; + } + + do { + ssh_event_dopoll(event, 1000); + } while(!ssh_channel_is_closed(chan)); + + ssh_event_remove_fd(event, fd); + + ssh_event_remove_session(event, session); + + ssh_event_free(event); + return 0; +} + + +int main(int argc, char **argv){ + ssh_session session; + ssh_bind sshbind; + ssh_message message; + ssh_channel chan=0; + int auth=0; + int shell=0; + int r; + + sshbind=ssh_bind_new(); + session=ssh_new(); + + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, + KEYS_FOLDER "ssh_host_dsa_key"); + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, + KEYS_FOLDER "ssh_host_rsa_key"); + +#ifdef HAVE_ARGP_H + /* + * Parse our arguments; every option seen by parse_opt will + * be reflected in arguments. + */ + argp_parse (&argp, argc, argv, 0, 0, sshbind); +#else + (void) argc; + (void) argv; +#endif +#ifdef WITH_PCAP + set_pcap(session); +#endif + + if(ssh_bind_listen(sshbind)<0){ + printf("Error listening to socket: %s\n", ssh_get_error(sshbind)); + return 1; + } + printf("Started sample libssh sshd on port %d\n", port); + printf("You can login as the user %s with the password %s\n", SSHD_USER, + SSHD_PASSWORD); + r = ssh_bind_accept(sshbind, session); + if(r==SSH_ERROR){ + printf("Error accepting a connection: %s\n", ssh_get_error(sshbind)); + return 1; + } + if (ssh_handle_key_exchange(session)) { + printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); + return 1; + } + + /* proceed to authentication */ + auth = authenticate(session); + if(!auth){ + printf("Authentication error: %s\n", ssh_get_error(session)); + ssh_disconnect(session); + return 1; + } + + + /* wait for a channel session */ + do { + message = ssh_message_get(session); + if(message){ + if(ssh_message_type(message) == SSH_REQUEST_CHANNEL_OPEN && + ssh_message_subtype(message) == SSH_CHANNEL_SESSION) { + chan = ssh_message_channel_request_open_reply_accept(message); + ssh_message_free(message); + break; + } else { + ssh_message_reply_default(message); + ssh_message_free(message); + } + } else { + break; + } + } while(!chan); + + if(!chan) { + printf("Error: cleint did not ask for a channel session (%s)\n", + ssh_get_error(session)); + ssh_finalize(); + return 1; + } + + + /* wait for a shell */ + do { + message = ssh_message_get(session); + if(message != NULL) { + if(ssh_message_type(message) == SSH_REQUEST_CHANNEL) { + if(ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_SHELL) { + shell = 1; + ssh_message_channel_request_reply_success(message); + ssh_message_free(message); + break; + } else if(ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_PTY) { + ssh_message_channel_request_reply_success(message); + ssh_message_free(message); + continue; + } + } + ssh_message_reply_default(message); + ssh_message_free(message); + } else { + break; + } + } while(!shell); + + if(!shell) { + printf("Error: No shell requested (%s)\n", ssh_get_error(session)); + return 1; + } + + printf("it works !\n"); + + main_loop(chan); + + ssh_disconnect(session); + ssh_bind_free(sshbind); +#ifdef WITH_PCAP + cleanup_pcap(); +#endif + ssh_finalize(); + return 0; +} + diff --git a/libssh/examples/samplesshd.c b/libssh/examples/samplesshd.c new file mode 100644 index 00000000..f9e0dc8c --- /dev/null +++ b/libssh/examples/samplesshd.c @@ -0,0 +1,314 @@ +/* This is a sample implementation of a libssh based SSH server */ +/* +Copyright 2003-2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ + +#include "config.h" + +#include +#include + +#ifdef HAVE_ARGP_H +#include +#endif +#include +#include +#include + +#ifndef KEYS_FOLDER +#ifdef _WIN32 +#define KEYS_FOLDER +#else +#define KEYS_FOLDER "/etc/ssh/" +#endif +#endif + +#ifdef WITH_PCAP +static const char *pcap_file="debug.server.pcap"; +static ssh_pcap_file pcap; + +static void set_pcap(ssh_session session) { + if(!pcap_file) + return; + pcap=ssh_pcap_file_new(); + if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ + printf("Error opening pcap file\n"); + ssh_pcap_file_free(pcap); + pcap=NULL; + return; + } + ssh_set_pcap_file(session,pcap); +} + +static void cleanup_pcap(void) { + ssh_pcap_file_free(pcap); + pcap=NULL; +} +#endif + + +static int auth_password(const char *user, const char *password){ + if(strcmp(user,"aris")) + return 0; + if(strcmp(password,"lala")) + return 0; + return 1; // authenticated +} +#ifdef HAVE_ARGP_H +const char *argp_program_version = "libssh server example " + SSH_STRINGIFY(LIBSSH_VERSION); +const char *argp_program_bug_address = ""; + +/* Program documentation. */ +static char doc[] = "libssh -- a Secure Shell protocol implementation"; + +/* A description of the arguments we accept. */ +static char args_doc[] = "BINDADDR"; + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "port", + .key = 'p', + .arg = "PORT", + .flags = 0, + .doc = "Set the port to bind.", + .group = 0 + }, + { + .name = "hostkey", + .key = 'k', + .arg = "FILE", + .flags = 0, + .doc = "Set the host key.", + .group = 0 + }, + { + .name = "dsakey", + .key = 'd', + .arg = "FILE", + .flags = 0, + .doc = "Set the dsa key.", + .group = 0 + }, + { + .name = "rsakey", + .key = 'r', + .arg = "FILE", + .flags = 0, + .doc = "Set the rsa key.", + .group = 0 + }, + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Get verbose output.", + .group = 0 + }, + {NULL, 0, NULL, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + ssh_bind sshbind = state->input; + + switch (key) { + case 'p': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); + break; + case 'd': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); + break; + case 'k': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); + break; + case 'r': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); + break; + case 'v': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + /* Too many arguments. */ + argp_usage (state); + } + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); + break; + case ARGP_KEY_END: + if (state->arg_num < 1) { + /* Not enough arguments. */ + argp_usage (state); + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Our argp parser. */ +static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; +#endif /* HAVE_ARGP_H */ + +int main(int argc, char **argv){ + ssh_session session; + ssh_bind sshbind; + ssh_message message; + ssh_channel chan=0; + char buf[2048]; + int auth=0; + int sftp=0; + int i; + int r; + + sshbind=ssh_bind_new(); + session=ssh_new(); + + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key"); + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key"); + +#ifdef HAVE_ARGP_H + /* + * Parse our arguments; every option seen by parse_opt will + * be reflected in arguments. + */ + argp_parse (&argp, argc, argv, 0, 0, sshbind); +#else + (void) argc; + (void) argv; +#endif +#ifdef WITH_PCAP + set_pcap(session); +#endif + + if(ssh_bind_listen(sshbind)<0){ + printf("Error listening to socket: %s\n",ssh_get_error(sshbind)); + return 1; + } + r=ssh_bind_accept(sshbind,session); + if(r==SSH_ERROR){ + printf("error accepting a connection : %s\n",ssh_get_error(sshbind)); + return 1; + } + if (ssh_handle_key_exchange(session)) { + printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); + return 1; + } + do { + message=ssh_message_get(session); + if(!message) + break; + switch(ssh_message_type(message)){ + case SSH_REQUEST_AUTH: + switch(ssh_message_subtype(message)){ + case SSH_AUTH_METHOD_PASSWORD: + printf("User %s wants to auth with pass %s\n", + ssh_message_auth_user(message), + ssh_message_auth_password(message)); + if(auth_password(ssh_message_auth_user(message), + ssh_message_auth_password(message))){ + auth=1; + ssh_message_auth_reply_success(message,0); + break; + } + // not authenticated, send default message + case SSH_AUTH_METHOD_NONE: + default: + ssh_message_auth_set_methods(message,SSH_AUTH_METHOD_PASSWORD); + ssh_message_reply_default(message); + break; + } + break; + default: + ssh_message_reply_default(message); + } + ssh_message_free(message); + } while (!auth); + if(!auth){ + printf("auth error: %s\n",ssh_get_error(session)); + ssh_disconnect(session); + return 1; + } + do { + message=ssh_message_get(session); + if(message){ + switch(ssh_message_type(message)){ + case SSH_REQUEST_CHANNEL_OPEN: + if(ssh_message_subtype(message)==SSH_CHANNEL_SESSION){ + chan=ssh_message_channel_request_open_reply_accept(message); + break; + } + default: + ssh_message_reply_default(message); + } + ssh_message_free(message); + } + } while(message && !chan); + if(!chan){ + printf("error : %s\n",ssh_get_error(session)); + ssh_finalize(); + return 1; + } + do { + message=ssh_message_get(session); + if(message && ssh_message_type(message)==SSH_REQUEST_CHANNEL && + (ssh_message_subtype(message)==SSH_CHANNEL_REQUEST_SHELL || + ssh_message_subtype(message)==SSH_CHANNEL_REQUEST_PTY)) { +// if(!strcmp(ssh_message_channel_request_subsystem(message),"sftp")){ + sftp=1; + ssh_message_channel_request_reply_success(message); + break; + // } + } + if(!sftp){ + ssh_message_reply_default(message); + } + ssh_message_free(message); + } while (message && !sftp); + if(!sftp){ + printf("error : %s\n",ssh_get_error(session)); + return 1; + } + printf("it works !\n"); + do{ + i=ssh_channel_read(chan,buf, 2048, 0); + if(i>0) { + ssh_channel_write(chan, buf, i); + if (write(1,buf,i) < 0) { + printf("error writing to buffer\n"); + return 1; + } + if (buf[0] == '\x0d') { + if (write(1, "\n", 1) < 0) { + printf("error writing to buffer\n"); + return 1; + } + ssh_channel_write(chan, "\n", 1); + } + } + } while (i>0); + ssh_disconnect(session); + ssh_bind_free(sshbind); +#ifdef WITH_PCAP + cleanup_pcap(); +#endif + ssh_finalize(); + return 0; +} + diff --git a/libssh/examples/scp_download.c b/libssh/examples/scp_download.c new file mode 100644 index 00000000..bfd8dc4e --- /dev/null +++ b/libssh/examples/scp_download.c @@ -0,0 +1,165 @@ +/* scp_download.c + * Sample implementation of a tiny SCP downloader client + */ + +/* +Copyright 2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. + */ + +#include +#include +#include +#include +#include + +#include +#include "examples_common.h" + +int verbosity=0; +const char *createcommand="rm -fr /tmp/libssh_tests && mkdir /tmp/libssh_tests && cd /tmp/libssh_tests && date > a && date > b && mkdir c && date > d"; +char *host=NULL; +static void usage(const char *argv0){ + fprintf(stderr,"Usage : %s [options] host\n" + "sample tiny scp downloader client - libssh-%s\n" + "This program will create files in /tmp and try to fetch them\n", +// "Options :\n", +// " -r : use RSA to verify host public key\n", + argv0, + ssh_version(0)); + exit(0); +} + +static int opts(int argc, char **argv){ + int i; + while((i=getopt(argc,argv,"v"))!=-1){ + switch(i){ + case 'v': + verbosity++; + break; + default: + fprintf(stderr,"unknown option %c\n",optopt); + usage(argv[0]); + return -1; + } + } + host = argv[optind]; + if(host == NULL) + usage(argv[0]); + return 0; +} + +static void create_files(ssh_session session){ + ssh_channel channel=ssh_channel_new(session); + char buffer[1]; + if(channel == NULL){ + fprintf(stderr,"Error creating channel: %s\n",ssh_get_error(session)); + exit(EXIT_FAILURE); + } + if(ssh_channel_open_session(channel) != SSH_OK){ + fprintf(stderr,"Error creating channel: %s\n",ssh_get_error(session)); + ssh_channel_free(channel); + exit(EXIT_FAILURE); + } + if(ssh_channel_request_exec(channel,createcommand) != SSH_OK){ + fprintf(stderr,"Error executing command: %s\n",ssh_get_error(session)); + ssh_channel_close(channel); + ssh_channel_free(channel); + exit(EXIT_FAILURE); + } + while(!ssh_channel_is_eof(channel)){ + ssh_channel_read(channel,buffer,1,1); + if (write(1,buffer,1) < 0) { + fprintf(stderr, "Error writing to buffer\n"); + ssh_channel_close(channel); + ssh_channel_free(channel); + return; + } + } + ssh_channel_close(channel); + ssh_channel_free(channel); +} + + +static int fetch_files(ssh_session session){ + int size; + char buffer[16384]; + int mode; + char *filename; + int r; + ssh_scp scp=ssh_scp_new(session, SSH_SCP_READ | SSH_SCP_RECURSIVE, "/tmp/libssh_tests/*"); + if(ssh_scp_init(scp) != SSH_OK){ + fprintf(stderr,"error initializing scp: %s\n",ssh_get_error(session)); + ssh_scp_free(scp); + return -1; + } + printf("Trying to download 3 files (a,b,d) and 1 directory (c)\n"); + do { + + r=ssh_scp_pull_request(scp); + switch(r){ + case SSH_SCP_REQUEST_NEWFILE: + size=ssh_scp_request_get_size(scp); + filename=strdup(ssh_scp_request_get_filename(scp)); + mode=ssh_scp_request_get_permissions(scp); + printf("downloading file %s, size %d, perms 0%o\n",filename,size,mode); + free(filename); + ssh_scp_accept_request(scp); + r=ssh_scp_read(scp,buffer,sizeof(buffer)); + if(r==SSH_ERROR){ + fprintf(stderr,"Error reading scp: %s\n",ssh_get_error(session)); + ssh_scp_close(scp); + ssh_scp_free(scp); + return -1; + } + printf("done\n"); + break; + case SSH_ERROR: + fprintf(stderr,"Error: %s\n",ssh_get_error(session)); + ssh_scp_close(scp); + ssh_scp_free(scp); + return -1; + case SSH_SCP_REQUEST_WARNING: + fprintf(stderr,"Warning: %s\n",ssh_scp_request_get_warning(scp)); + break; + case SSH_SCP_REQUEST_NEWDIR: + filename=strdup(ssh_scp_request_get_filename(scp)); + mode=ssh_scp_request_get_permissions(scp); + printf("downloading directory %s, perms 0%o\n",filename,mode); + free(filename); + ssh_scp_accept_request(scp); + break; + case SSH_SCP_REQUEST_ENDDIR: + printf("End of directory\n"); + break; + case SSH_SCP_REQUEST_EOF: + printf("End of requests\n"); + goto end; + } + } while (1); + end: + ssh_scp_close(scp); + ssh_scp_free(scp); + return 0; +} + +int main(int argc, char **argv){ + ssh_session session; + if(opts(argc,argv)<0) + return EXIT_FAILURE; + session=connect_ssh(host,NULL,verbosity); + if(session == NULL) + return EXIT_FAILURE; + create_files(session); + fetch_files(session); + ssh_disconnect(session); + ssh_free(session); + ssh_finalize(); + return 0; +} diff --git a/libssh/examples/senddata.c b/libssh/examples/senddata.c new file mode 100644 index 00000000..acc1bebc --- /dev/null +++ b/libssh/examples/senddata.c @@ -0,0 +1,64 @@ +#include + +#include +#include "examples_common.h" + +#define LIMIT 0x100000000 + +int main(void) { + ssh_session session; + ssh_channel channel; + char buffer[1024*1024]; + int rc; + uint64_t total=0; + uint64_t lastshown=4096; + session = connect_ssh("localhost", NULL, 0); + if (session == NULL) { + return 1; + } + + channel = ssh_channel_new(session);; + if (channel == NULL) { + ssh_disconnect(session); + return 1; + } + + rc = ssh_channel_open_session(channel); + if (rc < 0) { + ssh_channel_close(channel); + ssh_disconnect(session); + return 1; + } + + rc = ssh_channel_request_exec(channel, "cat > /dev/null"); + if (rc < 0) { + ssh_channel_close(channel); + ssh_disconnect(session); + return 1; + } + + + while ((rc = ssh_channel_write(channel, buffer, sizeof(buffer))) > 0) { + total += rc; + if(total/2 >= lastshown){ + printf("written %llx\n", (long long unsigned int) total); + lastshown=total; + } + if(total > LIMIT) + break; + } + + if (rc < 0) { + printf("error : %s\n",ssh_get_error(session)); + ssh_channel_close(channel); + ssh_disconnect(session); + return 1; + } + + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + + ssh_disconnect(session); + + return 0; +} diff --git a/libssh/examples/sshnetcat.c b/libssh/examples/sshnetcat.c new file mode 100644 index 00000000..cad777bb --- /dev/null +++ b/libssh/examples/sshnetcat.c @@ -0,0 +1,258 @@ +/* +Copyright 2010 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ + +#include "config.h" +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "examples_common.h" +char *host; +const char *desthost="localhost"; +const char *port="22"; + +#ifdef WITH_PCAP +#include +char *pcap_file=NULL; +#endif + +static void usage(){ + fprintf(stderr,"Usage : sshnetcat [user@]host forwarded_host forwarded_port\n"); + exit(1); +} + +static int opts(int argc, char **argv){ + int i; + while((i=getopt(argc,argv,"P:"))!=-1){ + switch(i){ +#ifdef WITH_PCAP + case 'P': + pcap_file=optarg; + break; +#endif + default: + fprintf(stderr,"unknown option %c\n",optopt); + usage(); + } + } + if(optind < argc) + host=argv[optind++]; + if(optind < argc) + desthost=argv[optind++]; + if(optind < argc) + port=argv[optind++]; + if(host==NULL) + usage(); + return 0; +} + +static void select_loop(ssh_session session,ssh_channel channel){ + fd_set fds; + struct timeval timeout; + char buffer[4096]; + /* channels will be set to the channels to poll. + * outchannels will contain the result of the poll + */ + ssh_channel channels[2], outchannels[2]; + int lus; + int eof=0; + int maxfd; + int ret; + while(channel){ + do{ + FD_ZERO(&fds); + if(!eof) + FD_SET(0,&fds); + timeout.tv_sec=30; + timeout.tv_usec=0; + FD_SET(ssh_get_fd(session),&fds); + maxfd=ssh_get_fd(session)+1; + channels[0]=channel; // set the first channel we want to read from + channels[1]=NULL; + ret=ssh_select(channels,outchannels,maxfd,&fds,&timeout); + if(ret==EINTR) + continue; + if(FD_ISSET(0,&fds)){ + lus=read(0,buffer,sizeof(buffer)); + if(lus) + channel_write(channel,buffer,lus); + else { + eof=1; + channel_send_eof(channel); + } + } + if(channel && channel_is_closed(channel)){ + ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel)); + + channel_free(channel); + channel=NULL; + channels[0]=NULL; + } + if(outchannels[0]){ + while(channel && channel_is_open(channel) && channel_poll(channel,0)){ + lus=channel_read(channel,buffer,sizeof(buffer),0); + if(lus==-1){ + fprintf(stderr, "Error reading channel: %s\n", + ssh_get_error(session)); + return; + } + if(lus==0){ + ssh_log(session,SSH_LOG_RARE,"EOF received\n"); + ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel)); + + channel_free(channel); + channel=channels[0]=NULL; + } else { + ret = write(1, buffer, lus); + if (ret < 0) { + fprintf(stderr, "Error writing to stdin: %s", + strerror(errno)); + return; + } + } + } + while(channel && channel_is_open(channel) && channel_poll(channel,1)){ /* stderr */ + lus=channel_read(channel,buffer,sizeof(buffer),1); + if(lus==-1){ + fprintf(stderr, "Error reading channel: %s\n", + ssh_get_error(session)); + return; + } + if(lus==0){ + ssh_log(session,SSH_LOG_RARE,"EOF received\n"); + ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel)); + channel_free(channel); + channel=channels[0]=NULL; + } else + ret = write(2, buffer, lus); + if (ret < 0) { + fprintf(stderr, "Error writing to stderr: %s", + strerror(errno)); + return; + } + } + } + if(channel && channel_is_closed(channel)){ + channel_free(channel); + channel=NULL; + } + } while (ret==EINTR || ret==SSH_EINTR); + + } +} + +static void forwarding(ssh_session session){ + ssh_channel channel; + int r; + channel=channel_new(session); + r=channel_open_forward(channel,desthost,atoi(port),"localhost",22); + if(r<0) { + printf("error forwarding port : %s\n",ssh_get_error(session)); + return; + } + select_loop(session,channel); +} + +static int client(ssh_session session){ + int auth=0; + char *banner; + int state; + + if (ssh_options_set(session, SSH_OPTIONS_HOST ,host) < 0) + return -1; + ssh_options_parse_config(session, NULL); + + if(ssh_connect(session)){ + fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session)); + return -1; + } + state=verify_knownhost(session); + if (state != 0) + return -1; + ssh_userauth_none(session, NULL); + banner=ssh_get_issue_banner(session); + if(banner){ + printf("%s\n",banner); + free(banner); + } + auth=authenticate_console(session); + if(auth != SSH_AUTH_SUCCESS){ + return -1; + } + ssh_log(session, SSH_LOG_FUNCTIONS, "Authentication success"); + forwarding(session); + return 0; +} + +#ifdef WITH_PCAP +ssh_pcap_file pcap; +void set_pcap(ssh_session session); +void set_pcap(ssh_session session){ + if(!pcap_file) + return; + pcap=ssh_pcap_file_new(); + if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ + printf("Error opening pcap file\n"); + ssh_pcap_file_free(pcap); + pcap=NULL; + return; + } + ssh_set_pcap_file(session,pcap); +} + +void cleanup_pcap(void); +void cleanup_pcap(){ + ssh_pcap_file_free(pcap); + pcap=NULL; +} +#endif + +int main(int argc, char **argv){ + ssh_session session; + + session = ssh_new(); + + if(ssh_options_getopt(session, &argc, argv)) { + fprintf(stderr, "error parsing command line :%s\n", + ssh_get_error(session)); + usage(); + } + opts(argc,argv); +#ifdef WITH_PCAP + set_pcap(session); +#endif + client(session); + + ssh_disconnect(session); + ssh_free(session); +#ifdef WITH_PCAP + cleanup_pcap(); +#endif + + ssh_finalize(); + + return 0; +} diff --git a/libssh/include/CMakeLists.txt b/libssh/include/CMakeLists.txt new file mode 100644 index 00000000..cb3bd962 --- /dev/null +++ b/libssh/include/CMakeLists.txt @@ -0,0 +1,3 @@ +project(headers C) + +add_subdirectory(libssh) diff --git a/libssh/include/libssh/CMakeLists.txt b/libssh/include/libssh/CMakeLists.txt new file mode 100644 index 00000000..78ee1c61 --- /dev/null +++ b/libssh/include/libssh/CMakeLists.txt @@ -0,0 +1,39 @@ +project(libssh-headers C) + +set(libssh_HDRS + callbacks.h + libssh.h + ssh2.h + legacy.h +) + +if (WITH_SFTP) + set(libssh_HDRS + ${libssh_HDRS} + sftp.h + ) +endif (WITH_SFTP) + +if (WITH_SSH1) + set(libssh_HDRS + ${libssh_HDRS} + ssh1.h + ) +endif (WITH_SSH1) + +if (WITH_SERVER) + set(libssh_HDRS + ${libssh_HDRS} + server.h + ) +endif (WITH_SERVER) + +install( + FILES + ${libssh_HDRS} + DESTINATION + ${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME} + COMPONENT + headers +) + diff --git a/libssh/include/libssh/agent.h b/libssh/include/libssh/agent.h new file mode 100644 index 00000000..4641c9e2 --- /dev/null +++ b/libssh/include/libssh/agent.h @@ -0,0 +1,117 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2008-2009 Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __AGENT_H +#define __AGENT_H + +#include "libssh/libssh.h" + +/* Messages for the authentication agent connection. */ +#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 +#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 +#define SSH_AGENTC_RSA_CHALLENGE 3 +#define SSH_AGENT_RSA_RESPONSE 4 +#define SSH_AGENT_FAILURE 5 +#define SSH_AGENT_SUCCESS 6 +#define SSH_AGENTC_ADD_RSA_IDENTITY 7 +#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 +#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 + +/* private OpenSSH extensions for SSH2 */ +#define SSH2_AGENTC_REQUEST_IDENTITIES 11 +#define SSH2_AGENT_IDENTITIES_ANSWER 12 +#define SSH2_AGENTC_SIGN_REQUEST 13 +#define SSH2_AGENT_SIGN_RESPONSE 14 +#define SSH2_AGENTC_ADD_IDENTITY 17 +#define SSH2_AGENTC_REMOVE_IDENTITY 18 +#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 + +/* smartcard */ +#define SSH_AGENTC_ADD_SMARTCARD_KEY 20 +#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21 + +/* lock/unlock the agent */ +#define SSH_AGENTC_LOCK 22 +#define SSH_AGENTC_UNLOCK 23 + +/* add key with constraints */ +#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24 +#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 +#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 + +#define SSH_AGENT_CONSTRAIN_LIFETIME 1 +#define SSH_AGENT_CONSTRAIN_CONFIRM 2 + +/* extended failure messages */ +#define SSH2_AGENT_FAILURE 30 + +/* additional error code for ssh.com's ssh-agent2 */ +#define SSH_COM_AGENT2_FAILURE 102 + +#define SSH_AGENT_OLD_SIGNATURE 0x01 + +struct ssh_agent_struct { + struct ssh_socket_struct *sock; + ssh_buffer ident; + unsigned int count; +}; + +#ifndef _WIN32 +/* agent.c */ +/** + * @brief Create a new ssh agent structure. + * + * @return An allocated ssh agent structure or NULL on error. + */ +struct ssh_agent_struct *agent_new(struct ssh_session_struct *session); + +void agent_close(struct ssh_agent_struct *agent); + +/** + * @brief Free an allocated ssh agent structure. + * + * @param agent The ssh agent structure to free. + */ +void agent_free(struct ssh_agent_struct *agent); + +/** + * @brief Check if the ssh agent is running. + * + * @param session The ssh session to check for the agent. + * + * @return 1 if it is running, 0 if not. + */ +int agent_is_running(struct ssh_session_struct *session); + +int ssh_agent_get_ident_count(struct ssh_session_struct *session); + +ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, + char **comment); + +ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session, + char **comment); + +ssh_string ssh_agent_sign_data(ssh_session session, + const ssh_key pubkey, + struct ssh_buffer_struct *data); +#endif + +#endif /* __AGENT_H */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/include/libssh/auth.h b/libssh/include/libssh/auth.h new file mode 100644 index 00000000..3a6012ec --- /dev/null +++ b/libssh/include/libssh/auth.h @@ -0,0 +1,106 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AUTH_H_ +#define AUTH_H_ +#include "config.h" +#include "libssh/callbacks.h" + +SSH_PACKET_CALLBACK(ssh_packet_userauth_banner); +SSH_PACKET_CALLBACK(ssh_packet_userauth_failure); +SSH_PACKET_CALLBACK(ssh_packet_userauth_success); +SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok); +SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request); +SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response); + +/** @internal + * kdbint structure must be shared with message.c + * and server.c + */ +struct ssh_kbdint_struct { + uint32_t nprompts; + uint32_t nanswers; + char *name; + char *instruction; + char **prompts; + unsigned char *echo; /* bool array */ + char **answers; +}; +typedef struct ssh_kbdint_struct* ssh_kbdint; + +ssh_kbdint ssh_kbdint_new(void); +void ssh_kbdint_clean(ssh_kbdint kbd); +void ssh_kbdint_free(ssh_kbdint kbd); + + +#ifdef WITH_SSH1 +void ssh_auth1_handler(ssh_session session, uint8_t type); + +/* auth1.c */ +int ssh_userauth1_none(ssh_session session, const char *username); +int ssh_userauth1_offer_pubkey(ssh_session session, const char *username, + int type, ssh_string pubkey); +int ssh_userauth1_password(ssh_session session, const char *username, + const char *password); + + +#endif + +/** @internal + * States of authentication in the client-side. They describe + * what was the last response from the server + */ +enum ssh_auth_state_e { + /** No authentication asked */ + SSH_AUTH_STATE_NONE=0, + /** Last authentication response was a partial success */ + SSH_AUTH_STATE_PARTIAL, + /** Last authentication response was a success */ + SSH_AUTH_STATE_SUCCESS, + /** Last authentication response was failed */ + SSH_AUTH_STATE_FAILED, + /** Last authentication was erroneous */ + SSH_AUTH_STATE_ERROR, + /** Last state was a keyboard-interactive ask for info */ + SSH_AUTH_STATE_INFO, + /** Last state was a public key accepted for authentication */ + SSH_AUTH_STATE_PK_OK, + /** We asked for a keyboard-interactive authentication */ + SSH_AUTH_STATE_KBDINT_SENT + +}; + +/** @internal + * @brief states of the authentication service request + */ +enum ssh_auth_service_state_e { + /** initial state */ + SSH_AUTH_SERVICE_NONE=0, + /** Authentication service request packet sent */ + SSH_AUTH_SERVICE_SENT, + /** Service accepted */ + SSH_AUTH_SERVICE_ACCEPTED, + /** Access to service denied (fatal) */ + SSH_AUTH_SERVICE_DENIED, + /** Specific to SSH1 */ + SSH_AUTH_SERVICE_USER_SENT +}; + +#endif /* AUTH_H_ */ diff --git a/libssh/include/libssh/bind.h b/libssh/include/libssh/bind.h new file mode 100644 index 00000000..ced1c494 --- /dev/null +++ b/libssh/include/libssh/bind.h @@ -0,0 +1,53 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BIND_H_ +#define BIND_H_ + +#include "libssh/priv.h" +#include "libssh/session.h" + +struct ssh_bind_struct { + struct ssh_common_struct common; /* stuff common to ssh_bind and ssh_session */ + struct ssh_bind_callbacks_struct *bind_callbacks; + void *bind_callbacks_userdata; + + struct ssh_poll_handle_struct *poll; + /* options */ + char *wanted_methods[10]; + char *banner; + char *ecdsakey; + char *dsakey; + char *rsakey; + ssh_key ecdsa; + ssh_key dsa; + ssh_key rsa; + char *bindaddr; + socket_t bindfd; + unsigned int bindport; + int blocking; + int toaccept; +}; + +struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct + *sshbind); + + +#endif /* BIND_H_ */ diff --git a/libssh/include/libssh/buffer.h b/libssh/include/libssh/buffer.h new file mode 100644 index 00000000..d7cdfbf4 --- /dev/null +++ b/libssh/include/libssh/buffer.h @@ -0,0 +1,72 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BUFFER_H_ +#define BUFFER_H_ + +#include "libssh/libssh.h" +/* + * Describes a buffer state + * [XXXXXXXXXXXXDATA PAYLOAD XXXXXXXXXXXXXXXXXXXXXXXX] + * ^ ^ ^ ^] + * \_data points\_pos points here \_used points here | / + * here Allocated + */ +struct ssh_buffer_struct { + char *data; + uint32_t used; + uint32_t allocated; + uint32_t pos; +}; + +LIBSSH_API void ssh_buffer_free(ssh_buffer buffer); +LIBSSH_API void *ssh_buffer_get_begin(ssh_buffer buffer); +LIBSSH_API uint32_t ssh_buffer_get_len(ssh_buffer buffer); +LIBSSH_API ssh_buffer ssh_buffer_new(void); +int buffer_add_ssh_string(ssh_buffer buffer, ssh_string string); +int buffer_add_u8(ssh_buffer buffer, uint8_t data); +int buffer_add_u16(ssh_buffer buffer, uint16_t data); +int buffer_add_u32(ssh_buffer buffer, uint32_t data); +int buffer_add_u64(ssh_buffer buffer, uint64_t data); +int buffer_add_data(ssh_buffer buffer, const void *data, uint32_t len); +int buffer_prepend_data(ssh_buffer buffer, const void *data, uint32_t len); +int buffer_add_buffer(ssh_buffer buffer, ssh_buffer source); +int buffer_reinit(ssh_buffer buffer); + +/* buffer_get_rest returns a pointer to the current position into the buffer */ +void *buffer_get_rest(ssh_buffer buffer); +/* buffer_get_rest_len returns the number of bytes which can be read */ +uint32_t buffer_get_rest_len(ssh_buffer buffer); + +/* buffer_read_*() returns the number of bytes read, except for ssh strings */ +int buffer_get_u8(ssh_buffer buffer, uint8_t *data); +int buffer_get_u32(ssh_buffer buffer, uint32_t *data); +int buffer_get_u64(ssh_buffer buffer, uint64_t *data); + +uint32_t buffer_get_data(ssh_buffer buffer, void *data, uint32_t requestedlen); +/* buffer_get_ssh_string() is an exception. if the String read is too large or invalid, it will answer NULL. */ +ssh_string buffer_get_ssh_string(ssh_buffer buffer); +/* gets a string out of a SSH-1 mpint */ +ssh_string buffer_get_mpint(ssh_buffer buffer); +/* buffer_pass_bytes acts as if len bytes have been read (used for padding) */ +uint32_t buffer_pass_bytes_end(ssh_buffer buffer, uint32_t len); +uint32_t buffer_pass_bytes(ssh_buffer buffer, uint32_t len); + +#endif /* BUFFER_H_ */ diff --git a/libssh/include/libssh/callbacks.h b/libssh/include/libssh/callbacks.h new file mode 100644 index 00000000..e15a0bd8 --- /dev/null +++ b/libssh/include/libssh/callbacks.h @@ -0,0 +1,447 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* callback.h + * This file includes the public declarations for the libssh callback mechanism + */ + +#ifndef _SSH_CALLBACK_H +#define _SSH_CALLBACK_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup libssh_callbacks The libssh callbacks + * @ingroup libssh + * + * Callback which can be replaced in libssh. + * + * @{ + */ + +/** @internal + * @brief callback to process simple codes + * @param code value to transmit + * @param user Userdata to pass in callback + */ +typedef void (*ssh_callback_int) (int code, void *user); + +/** @internal + * @brief callback for data received messages. + * @param data data retrieved from the socket or stream + * @param len number of bytes available from this stream + * @param user user-supplied pointer sent along with all callback messages + * @returns number of bytes processed by the callee. The remaining bytes will + * be sent in the next callback message, when more data is available. + */ +typedef int (*ssh_callback_data) (const void *data, size_t len, void *user); + +typedef void (*ssh_callback_int_int) (int code, int errno_code, void *user); + +typedef int (*ssh_message_callback) (ssh_session, ssh_message message, void *user); +typedef int (*ssh_channel_callback_int) (ssh_channel channel, int code, void *user); +typedef int (*ssh_channel_callback_data) (ssh_channel channel, int code, void *data, size_t len, void *user); + +/** + * @brief SSH log callback. All logging messages will go through this callback + * @param session Current session handler + * @param priority Priority of the log, the smaller being the more important + * @param message the actual message + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_log_callback) (ssh_session session, int priority, + const char *message, void *userdata); + +/** + * @brief SSH Connection status callback. + * @param session Current session handler + * @param status Percentage of connection status, going from 0.0 to 1.0 + * once connection is done. + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_status_callback) (ssh_session session, float status, + void *userdata); + +/** + * @brief SSH global request callback. All global request will go through this + * callback. + * @param session Current session handler + * @param message the actual message + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_global_request_callback) (ssh_session session, + ssh_message message, void *userdata); + +/** + * The structure to replace libssh functions with appropriate callbacks. + */ +struct ssh_callbacks_struct { + /** DON'T SET THIS use ssh_callbacks_init() instead. */ + size_t size; + /** + * User-provided data. User is free to set anything he wants here + */ + void *userdata; + /** + * This functions will be called if e.g. a keyphrase is needed. + */ + ssh_auth_callback auth_function; + /** + * This function will be called each time a loggable event happens. + */ + ssh_log_callback log_function; + /** + * This function gets called during connection time to indicate the + * percentage of connection steps completed. + */ + void (*connect_status_function)(void *userdata, float status); + /** + * This function will be called each time a global request is received. + */ + ssh_global_request_callback global_request_function; +}; +typedef struct ssh_callbacks_struct *ssh_callbacks; + +/** + * These are the callbacks exported by the socket structure + * They are called by the socket module when a socket event appears + */ +struct ssh_socket_callbacks_struct { + /** + * User-provided data. User is free to set anything he wants here + */ + void *userdata; + /** + * This function will be called each time data appears on socket. The data + * not consumed will appear on the next data event. + */ + ssh_callback_data data; + /** This function will be called each time a controlflow state changes, i.e. + * the socket is available for reading or writing. + */ + ssh_callback_int controlflow; + /** This function will be called each time an exception appears on socket. An + * exception can be a socket problem (timeout, ...) or an end-of-file. + */ + ssh_callback_int_int exception; + /** This function is called when the ssh_socket_connect was used on the socket + * on nonblocking state, and the connection successed. + */ + ssh_callback_int_int connected; +}; +typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks; + +#define SSH_SOCKET_FLOW_WRITEWILLBLOCK 1 +#define SSH_SOCKET_FLOW_WRITEWONTBLOCK 2 + +#define SSH_SOCKET_EXCEPTION_EOF 1 +#define SSH_SOCKET_EXCEPTION_ERROR 2 + +#define SSH_SOCKET_CONNECTED_OK 1 +#define SSH_SOCKET_CONNECTED_ERROR 2 +#define SSH_SOCKET_CONNECTED_TIMEOUT 3 + +/** + * @brief Initializes an ssh_callbacks_struct + * A call to this macro is mandatory when you have set a new + * ssh_callback_struct structure. Its goal is to maintain the binary + * compatibility with future versions of libssh as the structure + * evolves with time. + */ +#define ssh_callbacks_init(p) do {\ + (p)->size=sizeof(*(p)); \ +} while(0); + +/** + * @internal + * @brief tests if a callback can be called without crash + * verifies that the struct size if big enough + * verifies that the callback pointer exists + * @param p callback pointer + * @param c callback name + * @returns nonzero if callback can be called + */ +#define ssh_callbacks_exists(p,c) (\ + (p != NULL) && ( (char *)&((p)-> c) < (char *)(p) + (p)->size ) && \ + ((p)-> c != NULL) \ + ) + +/** @brief Prototype for a packet callback, to be called when a new packet arrives + * @param session The current session of the packet + * @param type packet type (see ssh2.h) + * @param packet buffer containing the packet, excluding size, type and padding fields + * @param user user argument to the callback + * and are called each time a packet shows up + * @returns SSH_PACKET_USED Packet was parsed and used + * @returns SSH_PACKET_NOT_USED Packet was not used or understood, processing must continue + */ +typedef int (*ssh_packet_callback) (ssh_session session, uint8_t type, ssh_buffer packet, void *user); + +/** return values for a ssh_packet_callback */ +/** Packet was used and should not be parsed by another callback */ +#define SSH_PACKET_USED 1 +/** Packet was not used and should be passed to any other callback + * available */ +#define SSH_PACKET_NOT_USED 2 + + +/** @brief This macro declares a packet callback handler + * @code + * SSH_PACKET_CALLBACK(mycallback){ + * ... + * } + * @endcode + */ +#define SSH_PACKET_CALLBACK(name) \ + int name (ssh_session session, uint8_t type, ssh_buffer packet, void *user) + +struct ssh_packet_callbacks_struct { + /** Index of the first packet type being handled */ + uint8_t start; + /** Number of packets being handled by this callback struct */ + uint8_t n_callbacks; + /** A pointer to n_callbacks packet callbacks */ + ssh_packet_callback *callbacks; + /** + * User-provided data. User is free to set anything he wants here + */ + void *user; +}; + +typedef struct ssh_packet_callbacks_struct *ssh_packet_callbacks; + +/** + * @brief Set the session callback functions. + * + * This functions sets the callback structure to use your own callback + * functions for auth, logging and status. + * + * @code + * struct ssh_callbacks_struct cb = { + * .userdata = data, + * .auth_function = my_auth_function + * }; + * ssh_callbacks_init(&cb); + * ssh_set_callbacks(session, &cb); + * @endcode + * + * @param session The session to set the callback structure. + * + * @param cb The callback structure itself. + * + * @return SSH_OK on success, SSH_ERROR on error. + */ +LIBSSH_API int ssh_set_callbacks(ssh_session session, ssh_callbacks cb); + +/** + * @brief SSH channel data callback. Called when data is available on a channel + * @param session Current session handler + * @param channel the actual channel + * @param data the data that has been read on the channel + * @param len the length of the data + * @param is_stderr is 0 for stdout or 1 for stderr + * @param userdata Userdata to be passed to the callback function. + */ +typedef int (*ssh_channel_data_callback) (ssh_session session, + ssh_channel channel, + void *data, + uint32_t len, + int is_stderr, + void *userdata); + +/** + * @brief SSH channel eof callback. Called when a channel receives EOF + * @param session Current session handler + * @param channel the actual channel + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_channel_eof_callback) (ssh_session session, + ssh_channel channel, + void *userdata); + +/** + * @brief SSH channel close callback. Called when a channel is closed by remote peer + * @param session Current session handler + * @param channel the actual channel + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_channel_close_callback) (ssh_session session, + ssh_channel channel, + void *userdata); + +/** + * @brief SSH channel signal callback. Called when a channel has received a signal + * @param session Current session handler + * @param channel the actual channel + * @param signal the signal name (without the SIG prefix) + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_channel_signal_callback) (ssh_session session, + ssh_channel channel, + const char *signal, + void *userdata); + +/** + * @brief SSH channel exit status callback. Called when a channel has received an exit status + * @param session Current session handler + * @param channel the actual channel + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_channel_exit_status_callback) (ssh_session session, + ssh_channel channel, + int exit_status, + void *userdata); + +/** + * @brief SSH channel exit signal callback. Called when a channel has received an exit signal + * @param session Current session handler + * @param channel the actual channel + * @param signal the signal name (without the SIG prefix) + * @param core a boolean telling wether a core has been dumped or not + * @param errmsg the description of the exception + * @param lang the language of the description (format: RFC 3066) + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_channel_exit_signal_callback) (ssh_session session, + ssh_channel channel, + const char *signal, + int core, + const char *errmsg, + const char *lang, + void *userdata); + +struct ssh_channel_callbacks_struct { + /** DON'T SET THIS use ssh_callbacks_init() instead. */ + size_t size; + /** + * User-provided data. User is free to set anything he wants here + */ + void *userdata; + /** + * This functions will be called when there is data available. + */ + ssh_channel_data_callback channel_data_function; + /** + * This functions will be called when the channel has received an EOF. + */ + ssh_channel_eof_callback channel_eof_function; + /** + * This functions will be called when the channel has been closed by remote + */ + ssh_channel_close_callback channel_close_function; + /** + * This functions will be called when a signal has been received + */ + ssh_channel_signal_callback channel_signal_function; + /** + * This functions will be called when an exit status has been received + */ + ssh_channel_exit_status_callback channel_exit_status_function; + /** + * This functions will be called when an exit signal has been received + */ + ssh_channel_exit_signal_callback channel_exit_signal_function; +}; +typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks; + +/** + * @brief Set the channel callback functions. + * + * This functions sets the callback structure to use your own callback + * functions for channel data and exceptions + * + * @code + * struct ssh_channel_callbacks_struct cb = { + * .userdata = data, + * .channel_data = my_channel_data_function + * }; + * ssh_callbacks_init(&cb); + * ssh_set_channel_callbacks(channel, &cb); + * @endcode + * + * @param channel The channel to set the callback structure. + * + * @param cb The callback structure itself. + * + * @return SSH_OK on success, SSH_ERROR on error. + */ +LIBSSH_API int ssh_set_channel_callbacks(ssh_channel channel, + ssh_channel_callbacks cb); + +/** @} */ + +/** @group libssh_threads + * @{ + */ + +typedef int (*ssh_thread_callback) (void **lock); + +typedef unsigned long (*ssh_thread_id_callback) (void); +struct ssh_threads_callbacks_struct { + const char *type; + ssh_thread_callback mutex_init; + ssh_thread_callback mutex_destroy; + ssh_thread_callback mutex_lock; + ssh_thread_callback mutex_unlock; + ssh_thread_id_callback thread_id; +}; + +/** + * @brief sets the thread callbacks necessary if your program is using + * libssh in a multithreaded fashion. This function must be called first, + * outside of any threading context (in your main() for instance), before + * ssh_init(). + * @param cb pointer to a ssh_threads_callbacks_struct structure, which contains + * the different callbacks to be set. + * @see ssh_threads_callbacks_struct + * @see SSH_THREADS_PTHREAD + */ +LIBSSH_API int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct + *cb); + +/** + * @brief returns a pointer on the pthread threads callbacks, to be used with + * ssh_threads_set_callbacks. + * @warning you have to link with the library ssh_threads. + * @see ssh_threads_set_callbacks + */ +LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void); + +/** + * @brief returns a pointer on the noop threads callbacks, to be used with + * ssh_threads_set_callbacks. These callbacks do nothing and are being used by + * default. + * @see ssh_threads_set_callbacks + */ +LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void); + +/** @} */ +#ifdef __cplusplus +} +#endif + +#endif /*_SSH_CALLBACK_H */ + +/* @} */ diff --git a/libssh/include/libssh/channels.h b/libssh/include/libssh/channels.h new file mode 100644 index 00000000..45152236 --- /dev/null +++ b/libssh/include/libssh/channels.h @@ -0,0 +1,118 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CHANNELS_H_ +#define CHANNELS_H_ +#include "libssh/priv.h" + +/** @internal + * Describes the different possible states in a + * outgoing (client) channel request + */ +enum ssh_channel_request_state_e { + /** No request has been made */ + SSH_CHANNEL_REQ_STATE_NONE = 0, + /** A request has been made and answer is pending */ + SSH_CHANNEL_REQ_STATE_PENDING, + /** A request has been replied and accepted */ + SSH_CHANNEL_REQ_STATE_ACCEPTED, + /** A request has been replied and refused */ + SSH_CHANNEL_REQ_STATE_DENIED, + /** A request has been replied and an error happend */ + SSH_CHANNEL_REQ_STATE_ERROR +}; + +enum ssh_channel_state_e { + SSH_CHANNEL_STATE_NOT_OPEN = 0, + SSH_CHANNEL_STATE_OPENING, + SSH_CHANNEL_STATE_OPEN_DENIED, + SSH_CHANNEL_STATE_OPEN, + SSH_CHANNEL_STATE_CLOSED +}; + +/* The channel has been closed by the remote side */ +#define SSH_CHANNEL_FLAG_CLOSED_REMOTE 0x1 +/* The channel has been freed by the calling program */ +#define SSH_CHANNEL_FLAG_FREED_LOCAL 0x2 +/* the channel has not yet been bound to a remote one */ +#define SSH_CHANNEL_FLAG_NOT_BOUND 0x4 + +struct ssh_channel_struct { + ssh_session session; /* SSH_SESSION pointer */ + uint32_t local_channel; + uint32_t local_window; + int local_eof; + uint32_t local_maxpacket; + + uint32_t remote_channel; + uint32_t remote_window; + int remote_eof; /* end of file received */ + uint32_t remote_maxpacket; + enum ssh_channel_state_e state; + int delayed_close; + int flags; + ssh_buffer stdout_buffer; + ssh_buffer stderr_buffer; + void *userarg; + int version; + int exit_status; + enum ssh_channel_request_state_e request_state; + ssh_channel_callbacks callbacks; +}; + +SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf); +SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail); +SSH_PACKET_CALLBACK(ssh_packet_channel_success); +SSH_PACKET_CALLBACK(ssh_packet_channel_failure); +SSH_PACKET_CALLBACK(ssh_request_success); +SSH_PACKET_CALLBACK(ssh_request_denied); + +SSH_PACKET_CALLBACK(channel_rcv_change_window); +SSH_PACKET_CALLBACK(channel_rcv_eof); +SSH_PACKET_CALLBACK(channel_rcv_close); +SSH_PACKET_CALLBACK(channel_rcv_request); +SSH_PACKET_CALLBACK(channel_rcv_data); + +ssh_channel ssh_channel_new(ssh_session session); +int channel_default_bufferize(ssh_channel channel, void *data, int len, + int is_stderr); +int ssh_channel_flush(ssh_channel channel); +uint32_t ssh_channel_new_id(ssh_session session); +ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id); +int channel_write_common(ssh_channel channel, const void *data, + uint32_t len, int is_stderr); +void ssh_channel_do_free(ssh_channel channel); +#ifdef WITH_SSH1 +SSH_PACKET_CALLBACK(ssh_packet_data1); +SSH_PACKET_CALLBACK(ssh_packet_close1); +SSH_PACKET_CALLBACK(ssh_packet_exist_status1); + +/* channels1.c */ +int channel_open_session1(ssh_channel channel); +int channel_request_pty_size1(ssh_channel channel, const char *terminal, + int cols, int rows); +int channel_change_pty_size1(ssh_channel channel, int cols, int rows); +int channel_request_shell1(ssh_channel channel); +int channel_request_exec1(ssh_channel channel, const char *cmd); +int channel_write1(ssh_channel channel, const void *data, int len); +ssh_channel ssh_get_channel1(ssh_session session); +#endif + +#endif /* CHANNELS_H_ */ diff --git a/libssh/include/libssh/crc32.h b/libssh/include/libssh/crc32.h new file mode 100644 index 00000000..07c0cafc --- /dev/null +++ b/libssh/include/libssh/crc32.h @@ -0,0 +1,28 @@ +/* + * crc32.c - simple CRC32 code + * + * This file is part of the SSH Library + * + * Copyright (c) 2005 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _CRC32_H +#define _CRC32_H + +uint32_t ssh_crc32(const char *buf, uint32_t len); + +#endif /* _CRC32_H */ diff --git a/libssh/include/libssh/crypto.h b/libssh/include/libssh/crypto.h new file mode 100644 index 00000000..5376ca61 --- /dev/null +++ b/libssh/include/libssh/crypto.h @@ -0,0 +1,113 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * crypto.h is an include file for internal cryptographic structures of libssh + */ + +#ifndef _CRYPTO_H_ +#define _CRYPTO_H_ + +#include "config.h" + +#ifdef HAVE_LIBGCRYPT +#include +#endif +#include "libssh/wrapper.h" + +#ifdef cbc_encrypt +#undef cbc_encrypt +#endif +#ifdef cbc_decrypt +#undef cbc_decrypt +#endif + +#ifdef HAVE_OPENSSL_ECDH_H +#include +#endif +#include "libssh/ecdh.h" +#include "libssh/kex.h" + +enum ssh_key_exchange_e { + /* diffie-hellman-group1-sha1 */ + SSH_KEX_DH_GROUP1_SHA1=1, + /* diffie-hellman-group14-sha1 */ + SSH_KEX_DH_GROUP14_SHA1, + /* ecdh-sha2-nistp256 */ + SSH_KEX_ECDH_SHA2_NISTP256 +}; + +struct ssh_crypto_struct { + bignum e,f,x,k,y; +#ifdef HAVE_ECDH + EC_KEY *ecdh_privkey; + ssh_string ecdh_client_pubkey; + ssh_string ecdh_server_pubkey; +#endif + ssh_string dh_server_signature; /* information used by dh_handshake. */ + size_t digest_len; /* len of all the fields below */ + unsigned char *session_id; + unsigned char *secret_hash; /* Secret hash is same as session id until re-kex */ + unsigned char *encryptIV; + unsigned char *decryptIV; + unsigned char *decryptkey; + unsigned char *encryptkey; + unsigned char *encryptMAC; + unsigned char *decryptMAC; + unsigned char hmacbuf[EVP_MAX_MD_SIZE]; + struct ssh_cipher_struct *in_cipher, *out_cipher; /* the cipher structures/objects */ + ssh_string server_pubkey; + const char *server_pubkey_type; + int do_compress_out; /* idem */ + int do_compress_in; /* don't set them, set the option instead */ + int delayed_compress_in; /* Use of zlib@openssh.org */ + int delayed_compress_out; + void *compress_out_ctx; /* don't touch it */ + void *compress_in_ctx; /* really, don't */ + /* kex sent by server, client, and mutually elected methods */ + struct ssh_kex_struct server_kex; + struct ssh_kex_struct client_kex; + char *kex_methods[SSH_KEX_METHODS]; + enum ssh_key_exchange_e kex_type; + enum ssh_mac_e mac_type; /* Mac operations to use for key gen */ +}; + +struct ssh_cipher_struct { + const char *name; /* ssh name of the algorithm */ + unsigned int blocksize; /* blocksize of the algo */ + unsigned int keylen; /* length of the key structure */ +#ifdef HAVE_LIBGCRYPT + gcry_cipher_hd_t *key; +#elif defined HAVE_LIBCRYPTO + void *key; /* a key buffer allocated for the algo */ + void *IV; +#endif + unsigned int keysize; /* bytes of key used. != keylen */ + /* sets the new key for immediate use */ + int (*set_encrypt_key)(struct ssh_cipher_struct *cipher, void *key, void *IV); + int (*set_decrypt_key)(struct ssh_cipher_struct *cipher, void *key, void *IV); + void (*cbc_encrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len); + void (*cbc_decrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len); +}; + +/* vim: set ts=2 sw=2 et cindent: */ +#endif /* _CRYPTO_H_ */ diff --git a/libssh/include/libssh/dh.h b/libssh/include/libssh/dh.h new file mode 100644 index 00000000..e1039e24 --- /dev/null +++ b/libssh/include/libssh/dh.h @@ -0,0 +1,55 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DH_H_ +#define DH_H_ + +#include "config.h" + +#include "libssh/crypto.h" + +void ssh_print_bignum(const char *which,bignum num); +int dh_generate_e(ssh_session session); +int dh_generate_f(ssh_session session); +int dh_generate_x(ssh_session session); +int dh_generate_y(ssh_session session); + +int ssh_crypto_init(void); +void ssh_crypto_finalize(void); + +ssh_string dh_get_e(ssh_session session); +ssh_string dh_get_f(ssh_session session); +int dh_import_f(ssh_session session,ssh_string f_string); +int dh_import_e(ssh_session session, ssh_string e_string); +void dh_import_pubkey(ssh_session session,ssh_string pubkey_string); +int dh_build_k(ssh_session session); +int ssh_client_dh_init(ssh_session session); +int ssh_client_dh_reply(ssh_session session, ssh_buffer packet); + +int make_sessionid(ssh_session session); +/* add data for the final cookie */ +int hashbufin_add_cookie(ssh_session session, unsigned char *cookie); +int hashbufout_add_cookie(ssh_session session); +int generate_session_keys(ssh_session session); +bignum make_string_bn(ssh_string string); +ssh_string make_bignum_string(bignum num); + + +#endif /* DH_H_ */ diff --git a/libssh/include/libssh/ecdh.h b/libssh/include/libssh/ecdh.h new file mode 100644 index 00000000..b35b2af7 --- /dev/null +++ b/libssh/include/libssh/ecdh.h @@ -0,0 +1,41 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2011 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ECDH_H_ +#define ECDH_H_ + +#include "config.h" + +#ifdef HAVE_LIBCRYPTO +#ifdef HAVE_OPENSSL_ECDH_H + +#define HAVE_ECDH + +#endif /* HAVE_OPENSSL_ECDH_H */ +#endif /* HAVE_LIBCRYPTO */ + +int ssh_client_ecdh_init(ssh_session session); +int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet); + +#ifdef WITH_SERVER +int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet); +#endif /* WITH_SERVER */ + +#endif /* ECDH_H_ */ diff --git a/libssh/include/libssh/kex.h b/libssh/include/libssh/kex.h new file mode 100644 index 00000000..37d4be8e --- /dev/null +++ b/libssh/include/libssh/kex.h @@ -0,0 +1,50 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef KEX_H_ +#define KEX_H_ + +#include "libssh/priv.h" +#include "libssh/callbacks.h" + +#define SSH_KEX_METHODS 10 + +struct ssh_kex_struct { + unsigned char cookie[16]; + char *methods[SSH_KEX_METHODS]; +}; + +SSH_PACKET_CALLBACK(ssh_packet_kexinit); +#ifdef WITH_SSH1 +SSH_PACKET_CALLBACK(ssh_packet_publickey1); +#endif + +int ssh_send_kex(ssh_session session, int server_kex); +void ssh_list_kex(ssh_session session, struct ssh_kex_struct *kex); +int set_client_kex(ssh_session session); +int ssh_kex_select_methods(ssh_session session); +int verify_existing_algo(int algo, const char *name); +char **space_tokenize(const char *chain); +int ssh_get_kex1(ssh_session session); +char *ssh_find_matching(const char *in_d, const char *what_d); +const char *ssh_kex_get_supported_method(uint32_t algo); +const char *ssh_kex_get_description(uint32_t algo); + +#endif /* KEX_H_ */ diff --git a/libssh/include/libssh/keys.h b/libssh/include/libssh/keys.h new file mode 100644 index 00000000..6f08e070 --- /dev/null +++ b/libssh/include/libssh/keys.h @@ -0,0 +1,56 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef KEYS_H_ +#define KEYS_H_ + +#include "config.h" +#include "libssh/libssh.h" +#include "libssh/wrapper.h" + +struct ssh_public_key_struct { + int type; + const char *type_c; /* Don't free it ! it is static */ +#ifdef HAVE_LIBGCRYPT + gcry_sexp_t dsa_pub; + gcry_sexp_t rsa_pub; +#elif HAVE_LIBCRYPTO + DSA *dsa_pub; + RSA *rsa_pub; +#endif +}; + +struct ssh_private_key_struct { + int type; +#ifdef HAVE_LIBGCRYPT + gcry_sexp_t dsa_priv; + gcry_sexp_t rsa_priv; +#elif defined HAVE_LIBCRYPTO + DSA *dsa_priv; + RSA *rsa_priv; +#endif +}; + +const char *ssh_type_to_char(int type); +int ssh_type_from_name(const char *name); + +ssh_public_key publickey_from_string(ssh_session session, ssh_string pubkey_s); + +#endif /* KEYS_H_ */ diff --git a/libssh/include/libssh/legacy.h b/libssh/include/libssh/legacy.h new file mode 100644 index 00000000..771fe56f --- /dev/null +++ b/libssh/include/libssh/legacy.h @@ -0,0 +1,120 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Since libssh.h includes legacy.h, it's important that libssh.h is included + * first. we don't define LEGACY_H now because we want it to be defined when + * included from libssh.h + * All function calls declared in this header are deprecated and meant to be + * removed in future. + */ + +#ifndef LEGACY_H_ +#define LEGACY_H_ + +typedef struct ssh_private_key_struct* ssh_private_key; +typedef struct ssh_public_key_struct* ssh_public_key; + +LIBSSH_API int ssh_auth_list(ssh_session session); +LIBSSH_API int ssh_userauth_offer_pubkey(ssh_session session, const char *username, int type, ssh_string publickey); +LIBSSH_API int ssh_userauth_pubkey(ssh_session session, const char *username, ssh_string publickey, ssh_private_key privatekey); +#ifndef _WIN32 +LIBSSH_API int ssh_userauth_agent_pubkey(ssh_session session, const char *username, + ssh_public_key publickey); +#endif +LIBSSH_API int ssh_userauth_autopubkey(ssh_session session, const char *passphrase); +LIBSSH_API int ssh_userauth_privatekey_file(ssh_session session, const char *username, + const char *filename, const char *passphrase); + +LIBSSH_API void buffer_free(ssh_buffer buffer); +LIBSSH_API void *buffer_get(ssh_buffer buffer); +LIBSSH_API uint32_t buffer_get_len(ssh_buffer buffer); +LIBSSH_API ssh_buffer buffer_new(void); + +LIBSSH_API ssh_channel channel_accept_x11(ssh_channel channel, int timeout_ms); +LIBSSH_API int channel_change_pty_size(ssh_channel channel,int cols,int rows); +LIBSSH_API ssh_channel channel_forward_accept(ssh_session session, int timeout_ms); +LIBSSH_API int channel_close(ssh_channel channel); +LIBSSH_API int channel_forward_cancel(ssh_session session, const char *address, int port); +LIBSSH_API int channel_forward_listen(ssh_session session, const char *address, int port, int *bound_port); +LIBSSH_API void channel_free(ssh_channel channel); +LIBSSH_API int channel_get_exit_status(ssh_channel channel); +LIBSSH_API ssh_session channel_get_session(ssh_channel channel); +LIBSSH_API int channel_is_closed(ssh_channel channel); +LIBSSH_API int channel_is_eof(ssh_channel channel); +LIBSSH_API int channel_is_open(ssh_channel channel); +LIBSSH_API ssh_channel channel_new(ssh_session session); +LIBSSH_API int channel_open_forward(ssh_channel channel, const char *remotehost, + int remoteport, const char *sourcehost, int localport); +LIBSSH_API int channel_open_session(ssh_channel channel); +LIBSSH_API int channel_poll(ssh_channel channel, int is_stderr); +LIBSSH_API int channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); + +LIBSSH_API int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, + int is_stderr); + +LIBSSH_API int channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, + int is_stderr); +LIBSSH_API int channel_request_env(ssh_channel channel, const char *name, const char *value); +LIBSSH_API int channel_request_exec(ssh_channel channel, const char *cmd); +LIBSSH_API int channel_request_pty(ssh_channel channel); +LIBSSH_API int channel_request_pty_size(ssh_channel channel, const char *term, + int cols, int rows); +LIBSSH_API int channel_request_shell(ssh_channel channel); +LIBSSH_API int channel_request_send_signal(ssh_channel channel, const char *signum); +LIBSSH_API int channel_request_sftp(ssh_channel channel); +LIBSSH_API int channel_request_subsystem(ssh_channel channel, const char *subsystem); +LIBSSH_API int channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, + const char *cookie, int screen_number); +LIBSSH_API int channel_send_eof(ssh_channel channel); +LIBSSH_API int channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct + timeval * timeout); +LIBSSH_API void channel_set_blocking(ssh_channel channel, int blocking); +LIBSSH_API int channel_write(ssh_channel channel, const void *data, uint32_t len); + +LIBSSH_API void privatekey_free(ssh_private_key prv); +LIBSSH_API ssh_private_key privatekey_from_file(ssh_session session, const char *filename, + int type, const char *passphrase); +LIBSSH_API void publickey_free(ssh_public_key key); +LIBSSH_API int ssh_publickey_to_file(ssh_session session, const char *file, + ssh_string pubkey, int type); +LIBSSH_API ssh_string publickey_from_file(ssh_session session, const char *filename, + int *type); +LIBSSH_API ssh_public_key publickey_from_privatekey(ssh_private_key prv); +LIBSSH_API ssh_string publickey_to_string(ssh_public_key key); +LIBSSH_API int ssh_try_publickey_from_file(ssh_session session, const char *keyfile, + ssh_string *publickey, int *type); +LIBSSH_API enum ssh_keytypes_e ssh_privatekey_type(ssh_private_key privatekey); + +LIBSSH_API ssh_string ssh_get_pubkey(ssh_session session); + +LIBSSH_API ssh_message ssh_message_retrieve(ssh_session session, uint32_t packettype); +LIBSSH_API ssh_public_key ssh_message_auth_publickey(ssh_message msg); + +LIBSSH_API void string_burn(ssh_string str); +LIBSSH_API ssh_string string_copy(ssh_string str); +LIBSSH_API void *string_data(ssh_string str); +LIBSSH_API int string_fill(ssh_string str, const void *data, size_t len); +LIBSSH_API void string_free(ssh_string str); +LIBSSH_API ssh_string string_from_char(const char *what); +LIBSSH_API size_t string_len(ssh_string str); +LIBSSH_API ssh_string string_new(size_t size); +LIBSSH_API char *string_to_char(ssh_string str); + +#endif /* LEGACY_H_ */ diff --git a/libssh/include/libssh/libcrypto.h b/libssh/include/libssh/libcrypto.h new file mode 100644 index 00000000..54c78b16 --- /dev/null +++ b/libssh/include/libssh/libcrypto.h @@ -0,0 +1,86 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBCRYPTO_H_ +#define LIBCRYPTO_H_ + +#include "config.h" + +#ifdef HAVE_LIBCRYPTO + +#include +#include +#include +#include +#include +#ifdef HAVE_OPENSSL_ECC +#include +#endif + +typedef SHA_CTX* SHACTX; +typedef SHA256_CTX* SHA256CTX; +typedef MD5_CTX* MD5CTX; +typedef HMAC_CTX* HMACCTX; + +#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH +#ifdef MD5_DIGEST_LEN + #undef MD5_DIGEST_LEN +#endif +#define MD5_DIGEST_LEN MD5_DIGEST_LENGTH + +#ifdef HAVE_OPENSSL_ECC +#define EVP_DIGEST_LEN EVP_MAX_MD_SIZE +#endif + +#include +#include +#define OPENSSL_0_9_7b 0x0090702fL +#if (OPENSSL_VERSION_NUMBER <= OPENSSL_0_9_7b) +#define BROKEN_AES_CTR +#endif +typedef BIGNUM* bignum; +typedef BN_CTX* bignum_CTX; + +#define bignum_new() BN_new() +#define bignum_free(num) BN_clear_free(num) +#define bignum_set_word(bn,n) BN_set_word(bn,n) +#define bignum_bin2bn(bn,datalen,data) BN_bin2bn(bn,datalen,data) +#define bignum_bn2dec(num) BN_bn2dec(num) +#define bignum_dec2bn(bn,data) BN_dec2bn(data,bn) +#define bignum_bn2hex(num) BN_bn2hex(num) +#define bignum_rand(rnd, bits, top, bottom) BN_rand(rnd,bits,top,bottom) +#define bignum_ctx_new() BN_CTX_new() +#define bignum_ctx_free(num) BN_CTX_free(num) +#define bignum_mod_exp(dest,generator,exp,modulo,ctx) BN_mod_exp(dest,generator,exp,modulo,ctx) +#define bignum_num_bytes(num) BN_num_bytes(num) +#define bignum_num_bits(num) BN_num_bits(num) +#define bignum_is_bit_set(num,bit) BN_is_bit_set(num,bit) +#define bignum_bn2bin(num,ptr) BN_bn2bin(num,ptr) +#define bignum_cmp(num1,num2) BN_cmp(num1,num2) + +SHA256CTX sha256_init(void); +void sha256_update(SHA256CTX c, const void *data, unsigned long len); +void sha256_final(unsigned char *md, SHA256CTX c); + +struct ssh_cipher_struct *ssh_get_ciphertab(void); + +#endif /* HAVE_LIBCRYPTO */ + +#endif /* LIBCRYPTO_H_ */ diff --git a/libssh/include/libssh/libgcrypt.h b/libssh/include/libssh/libgcrypt.h new file mode 100644 index 00000000..54982063 --- /dev/null +++ b/libssh/include/libssh/libgcrypt.h @@ -0,0 +1,71 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBGCRYPT_H_ +#define LIBGCRYPT_H_ + +#include "config.h" + +#ifdef HAVE_LIBGCRYPT + +#include +typedef gcry_md_hd_t SHACTX; +typedef gcry_md_hd_t MD5CTX; +typedef gcry_md_hd_t HMACCTX; +#define SHA_DIGEST_LENGTH 20 +#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH +#define MD5_DIGEST_LEN 16 +#define SHA256_DIGEST_LENGTH 32 +#define SHA384_DIGEST_LENGTH 48 +#define SHA512_DIGEST_LENGTH 64 + +#ifndef EVP_MAX_MD_SIZE +#define EVP_MAX_MD_SIZE 36 +#endif + +#define EVP_DIGEST_LEN EVP_MAX_MD_SIZE + +typedef gcry_mpi_t bignum; + +/* missing gcrypt functions */ +int my_gcry_dec2bn(bignum *bn, const char *data); +char *my_gcry_bn2dec(bignum bn); + +#define bignum_new() gcry_mpi_new(0) +#define bignum_free(num) gcry_mpi_release(num) +#define bignum_set_word(bn,n) gcry_mpi_set_ui(bn,n) +#define bignum_bin2bn(bn,datalen,data) gcry_mpi_scan(data,GCRYMPI_FMT_USG,bn,datalen,NULL) +#define bignum_bn2dec(num) my_gcry_bn2dec(num) +#define bignum_dec2bn(num, data) my_gcry_dec2bn(data, num) +#define bignum_bn2hex(num,data) gcry_mpi_aprint(GCRYMPI_FMT_HEX,data,NULL,num) +#define bignum_hex2bn(num,datalen,data) gcry_mpi_scan(num,GCRYMPI_FMT_HEX,data,datalen,NULL) +#define bignum_rand(num,bits) gcry_mpi_randomize(num,bits,GCRY_STRONG_RANDOM),gcry_mpi_set_bit(num,bits-1),gcry_mpi_set_bit(num,0) +#define bignum_mod_exp(dest,generator,exp,modulo) gcry_mpi_powm(dest,generator,exp,modulo) +#define bignum_num_bits(num) gcry_mpi_get_nbits(num) +#define bignum_num_bytes(num) ((gcry_mpi_get_nbits(num)+7)/8) +#define bignum_is_bit_set(num,bit) gcry_mpi_test_bit(num,bit) +#define bignum_bn2bin(num,datalen,data) gcry_mpi_print(GCRYMPI_FMT_USG,data,datalen,NULL,num) +#define bignum_cmp(num1,num2) gcry_mpi_cmp(num1,num2) + +#endif /* HAVE_LIBGCRYPT */ + +struct ssh_cipher_struct *ssh_get_ciphertab(void); + +#endif /* LIBGCRYPT_H_ */ diff --git a/libssh/include/libssh/libssh.h b/libssh/include/libssh/libssh.h new file mode 100644 index 00000000..d9cc8478 --- /dev/null +++ b/libssh/include/libssh/libssh.h @@ -0,0 +1,596 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _LIBSSH_H +#define _LIBSSH_H + +#if defined _WIN32 || defined __CYGWIN__ + #ifdef LIBSSH_STATIC + #define LIBSSH_API + #else + #ifdef LIBSSH_EXPORTS + #ifdef __GNUC__ + #define LIBSSH_API __attribute__((dllexport)) + #else + #define LIBSSH_API __declspec(dllexport) + #endif + #else + #ifdef __GNUC__ + #define LIBSSH_API __attribute__((dllimport)) + #else + #define LIBSSH_API __declspec(dllimport) + #endif + #endif + #endif +#else + #if __GNUC__ >= 4 && !defined(__OS2__) + #define LIBSSH_API __attribute__((visibility("default"))) + #else + #define LIBSSH_API + #endif +#endif + +#ifdef _MSC_VER + /* Visual Studio hasn't inttypes.h so it doesn't know uint32_t */ + typedef int int32_t; + typedef unsigned int uint32_t; + typedef unsigned short uint16_t; + typedef unsigned char uint8_t; + typedef unsigned long long uint64_t; + typedef int mode_t; +#else /* _MSC_VER */ + #include + #include +#endif /* _MSC_VER */ + +#ifdef _WIN32 + #include +#else /* _WIN32 */ + #include /* for fd_set * */ + #include +#endif /* _WIN32 */ + +#define SSH_STRINGIFY(s) SSH_TOSTRING(s) +#define SSH_TOSTRING(s) #s + +/* libssh version macros */ +#define SSH_VERSION_INT(a, b, c) ((a) << 16 | (b) << 8 | (c)) +#define SSH_VERSION_DOT(a, b, c) a ##.## b ##.## c +#define SSH_VERSION(a, b, c) SSH_VERSION_DOT(a, b, c) + +/* libssh version */ +#define LIBSSH_VERSION_MAJOR 0 +#define LIBSSH_VERSION_MINOR 6 +#define LIBSSH_VERSION_MICRO 0 + +#define LIBSSH_VERSION_INT SSH_VERSION_INT(LIBSSH_VERSION_MAJOR, \ + LIBSSH_VERSION_MINOR, \ + LIBSSH_VERSION_MICRO) +#define LIBSSH_VERSION SSH_VERSION(LIBSSH_VERSION_MAJOR, \ + LIBSSH_VERSION_MINOR, \ + LIBSSH_VERSION_MICRO) + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif /* __GNUC__ */ + +#ifdef __GNUC__ +#define SSH_DEPRECATED __attribute__ ((deprecated)) +#else +#define SSH_DEPRECATED +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct ssh_agent_struct* ssh_agent; +typedef struct ssh_buffer_struct* ssh_buffer; +typedef struct ssh_channel_struct* ssh_channel; +typedef struct ssh_message_struct* ssh_message; +typedef struct ssh_pcap_file_struct* ssh_pcap_file; +typedef struct ssh_key_struct* ssh_key; +typedef struct ssh_scp_struct* ssh_scp; +typedef struct ssh_session_struct* ssh_session; +typedef struct ssh_string_struct* ssh_string; +typedef struct ssh_event_struct* ssh_event; + +/* Socket type */ +#ifdef _WIN32 +#ifndef socket_t +typedef SOCKET socket_t; +#endif /* socket_t */ +#else /* _WIN32 */ +#ifndef socket_t +typedef int socket_t; +#endif +#endif /* _WIN32 */ + +#define SSH_INVALID_SOCKET ((socket_t) -1) + +/* the offsets of methods */ +enum ssh_kex_types_e { + SSH_KEX=0, + SSH_HOSTKEYS, + SSH_CRYPT_C_S, + SSH_CRYPT_S_C, + SSH_MAC_C_S, + SSH_MAC_S_C, + SSH_COMP_C_S, + SSH_COMP_S_C, + SSH_LANG_C_S, + SSH_LANG_S_C +}; + +#define SSH_CRYPT 2 +#define SSH_MAC 3 +#define SSH_COMP 4 +#define SSH_LANG 5 + +enum ssh_auth_e { + SSH_AUTH_SUCCESS=0, + SSH_AUTH_DENIED, + SSH_AUTH_PARTIAL, + SSH_AUTH_INFO, + SSH_AUTH_AGAIN, + SSH_AUTH_ERROR=-1 +}; + +/* auth flags */ +#define SSH_AUTH_METHOD_UNKNOWN 0 +#define SSH_AUTH_METHOD_NONE 0x0001 +#define SSH_AUTH_METHOD_PASSWORD 0x0002 +#define SSH_AUTH_METHOD_PUBLICKEY 0x0004 +#define SSH_AUTH_METHOD_HOSTBASED 0x0008 +#define SSH_AUTH_METHOD_INTERACTIVE 0x0010 + +/* messages */ +enum ssh_requests_e { + SSH_REQUEST_AUTH=1, + SSH_REQUEST_CHANNEL_OPEN, + SSH_REQUEST_CHANNEL, + SSH_REQUEST_SERVICE, + SSH_REQUEST_GLOBAL +}; + +enum ssh_channel_type_e { + SSH_CHANNEL_UNKNOWN=0, + SSH_CHANNEL_SESSION, + SSH_CHANNEL_DIRECT_TCPIP, + SSH_CHANNEL_FORWARDED_TCPIP, + SSH_CHANNEL_X11 +}; + +enum ssh_channel_requests_e { + SSH_CHANNEL_REQUEST_UNKNOWN=0, + SSH_CHANNEL_REQUEST_PTY, + SSH_CHANNEL_REQUEST_EXEC, + SSH_CHANNEL_REQUEST_SHELL, + SSH_CHANNEL_REQUEST_ENV, + SSH_CHANNEL_REQUEST_SUBSYSTEM, + SSH_CHANNEL_REQUEST_WINDOW_CHANGE, + SSH_CHANNEL_REQUEST_X11 +}; + +enum ssh_global_requests_e { + SSH_GLOBAL_REQUEST_UNKNOWN=0, + SSH_GLOBAL_REQUEST_TCPIP_FORWARD, + SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD, +}; + +enum ssh_publickey_state_e { + SSH_PUBLICKEY_STATE_ERROR=-1, + SSH_PUBLICKEY_STATE_NONE=0, + SSH_PUBLICKEY_STATE_VALID=1, + SSH_PUBLICKEY_STATE_WRONG=2 +}; + +/* status flags */ +#define SSH_CLOSED 0x01 +#define SSH_READ_PENDING 0x02 +#define SSH_CLOSED_ERROR 0x04 +#define SSH_WRITE_PENDING 0x08 + +enum ssh_server_known_e { + SSH_SERVER_ERROR=-1, + SSH_SERVER_NOT_KNOWN=0, + SSH_SERVER_KNOWN_OK, + SSH_SERVER_KNOWN_CHANGED, + SSH_SERVER_FOUND_OTHER, + SSH_SERVER_FILE_NOT_FOUND +}; + +#ifndef MD5_DIGEST_LEN + #define MD5_DIGEST_LEN 16 +#endif +/* errors */ + +enum ssh_error_types_e { + SSH_NO_ERROR=0, + SSH_REQUEST_DENIED, + SSH_FATAL, + SSH_EINTR +}; + +/* some types for keys */ +enum ssh_keytypes_e{ + SSH_KEYTYPE_UNKNOWN=0, + SSH_KEYTYPE_DSS=1, + SSH_KEYTYPE_RSA, + SSH_KEYTYPE_RSA1, + SSH_KEYTYPE_ECDSA +}; + +enum ssh_keycmp_e { + SSH_KEY_CMP_PUBLIC = 0, + SSH_KEY_CMP_PRIVATE +}; + +/* Error return codes */ +#define SSH_OK 0 /* No error */ +#define SSH_ERROR -1 /* Error of some kind */ +#define SSH_AGAIN -2 /* The nonblocking call must be repeated */ +#define SSH_EOF -127 /* We have already a eof */ + +/** + * @addtogroup libssh_log + * + * @{ + */ + +enum { + /** No logging at all + */ + SSH_LOG_NOLOG=0, + /** Only warnings + */ + SSH_LOG_WARNING, + /** High level protocol information + */ + SSH_LOG_PROTOCOL, + /** Lower level protocol infomations, packet level + */ + SSH_LOG_PACKET, + /** Every function path + */ + SSH_LOG_FUNCTIONS +}; +/** @} */ +#define SSH_LOG_RARE SSH_LOG_WARNING + +/** + * @name Logging levels + * + * @brief Debug levels for logging. + * @{ + */ + +/** No logging at all */ +#define SSH_LOG_NONE 0 +/** Show only warnings */ +#define SSH_LOG_WARN 1 +/** Get some information what's going on */ +#define SSH_LOG_INFO 2 +/** Get detailed debuging information **/ +#define SSH_LOG_DEBUG 3 +/** Get trace output, packet information, ... */ +#define SSH_LOG_TRACE 4 + +/** @} */ + +enum ssh_options_e { + SSH_OPTIONS_HOST, + SSH_OPTIONS_PORT, + SSH_OPTIONS_PORT_STR, + SSH_OPTIONS_FD, + SSH_OPTIONS_USER, + SSH_OPTIONS_SSH_DIR, + SSH_OPTIONS_IDENTITY, + SSH_OPTIONS_ADD_IDENTITY, + SSH_OPTIONS_KNOWNHOSTS, + SSH_OPTIONS_TIMEOUT, + SSH_OPTIONS_TIMEOUT_USEC, + SSH_OPTIONS_SSH1, + SSH_OPTIONS_SSH2, + SSH_OPTIONS_LOG_VERBOSITY, + SSH_OPTIONS_LOG_VERBOSITY_STR, + SSH_OPTIONS_CIPHERS_C_S, + SSH_OPTIONS_CIPHERS_S_C, + SSH_OPTIONS_COMPRESSION_C_S, + SSH_OPTIONS_COMPRESSION_S_C, + SSH_OPTIONS_PROXYCOMMAND, + SSH_OPTIONS_BINDADDR, + SSH_OPTIONS_STRICTHOSTKEYCHECK, + SSH_OPTIONS_COMPRESSION, + SSH_OPTIONS_COMPRESSION_LEVEL, + SSH_OPTIONS_KEY_EXCHANGE, + SSH_OPTIONS_HOSTKEYS +}; + +enum { + /** Code is going to write/create remote files */ + SSH_SCP_WRITE, + /** Code is going to read remote files */ + SSH_SCP_READ, + SSH_SCP_RECURSIVE=0x10 +}; + +enum ssh_scp_request_types { + /** A new directory is going to be pulled */ + SSH_SCP_REQUEST_NEWDIR=1, + /** A new file is going to be pulled */ + SSH_SCP_REQUEST_NEWFILE, + /** End of requests */ + SSH_SCP_REQUEST_EOF, + /** End of directory */ + SSH_SCP_REQUEST_ENDDIR, + /** Warning received */ + SSH_SCP_REQUEST_WARNING +}; + +LIBSSH_API int ssh_blocking_flush(ssh_session session, int timeout); +LIBSSH_API ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms); +LIBSSH_API int ssh_channel_change_pty_size(ssh_channel channel,int cols,int rows); +LIBSSH_API int ssh_channel_close(ssh_channel channel); +LIBSSH_API void ssh_channel_free(ssh_channel channel); +LIBSSH_API int ssh_channel_get_exit_status(ssh_channel channel); +LIBSSH_API ssh_session ssh_channel_get_session(ssh_channel channel); +LIBSSH_API int ssh_channel_is_closed(ssh_channel channel); +LIBSSH_API int ssh_channel_is_eof(ssh_channel channel); +LIBSSH_API int ssh_channel_is_open(ssh_channel channel); +LIBSSH_API ssh_channel ssh_channel_new(ssh_session session); +LIBSSH_API int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, + int remoteport, const char *sourcehost, int localport); +LIBSSH_API int ssh_channel_open_session(ssh_channel channel); +LIBSSH_API int ssh_channel_poll(ssh_channel channel, int is_stderr); +LIBSSH_API int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr); +LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); +LIBSSH_API int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, + int is_stderr); +LIBSSH_API int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value); +LIBSSH_API int ssh_channel_request_exec(ssh_channel channel, const char *cmd); +LIBSSH_API int ssh_channel_request_pty(ssh_channel channel); +LIBSSH_API int ssh_channel_request_pty_size(ssh_channel channel, const char *term, + int cols, int rows); +LIBSSH_API int ssh_channel_request_shell(ssh_channel channel); +LIBSSH_API int ssh_channel_request_send_signal(ssh_channel channel, const char *signum); +LIBSSH_API int ssh_channel_request_sftp(ssh_channel channel); +LIBSSH_API int ssh_channel_request_subsystem(ssh_channel channel, const char *subsystem); +LIBSSH_API int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, + const char *cookie, int screen_number); +LIBSSH_API int ssh_channel_send_eof(ssh_channel channel); +LIBSSH_API int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct + timeval * timeout); +LIBSSH_API void ssh_channel_set_blocking(ssh_channel channel, int blocking); +LIBSSH_API int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len); +LIBSSH_API uint32_t ssh_channel_window_size(ssh_channel channel); + +LIBSSH_API char *ssh_basename (const char *path); +LIBSSH_API void ssh_clean_pubkey_hash(unsigned char **hash); +LIBSSH_API int ssh_connect(ssh_session session); +LIBSSH_API const char *ssh_copyright(void); +LIBSSH_API void ssh_disconnect(ssh_session session); +LIBSSH_API char *ssh_dirname (const char *path); +LIBSSH_API int ssh_finalize(void); +LIBSSH_API ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms); +LIBSSH_API int ssh_forward_cancel(ssh_session session, const char *address, int port); +LIBSSH_API int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port); +LIBSSH_API void ssh_free(ssh_session session); +LIBSSH_API const char *ssh_get_disconnect_message(ssh_session session); +LIBSSH_API const char *ssh_get_error(void *error); +LIBSSH_API int ssh_get_error_code(void *error); +LIBSSH_API socket_t ssh_get_fd(ssh_session session); +LIBSSH_API char *ssh_get_hexa(const unsigned char *what, size_t len); +LIBSSH_API char *ssh_get_issue_banner(ssh_session session); +LIBSSH_API int ssh_get_openssh_version(ssh_session session); +LIBSSH_API int ssh_get_publickey(ssh_session session, ssh_key *key); +LIBSSH_API int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash); +LIBSSH_API int ssh_get_random(void *where,int len,int strong); +LIBSSH_API int ssh_get_version(ssh_session session); +LIBSSH_API int ssh_get_status(ssh_session session); +LIBSSH_API int ssh_init(void); +LIBSSH_API int ssh_is_blocking(ssh_session session); +LIBSSH_API int ssh_is_connected(ssh_session session); +LIBSSH_API int ssh_is_server_known(ssh_session session); + +/* legacy */ +LIBSSH_API void ssh_log(ssh_session session, + int prioriry, + const char *format, ...) PRINTF_ATTRIBUTE(3, 4); + +LIBSSH_API ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg); +LIBSSH_API int ssh_message_channel_request_reply_success(ssh_message msg); +LIBSSH_API void ssh_message_free(ssh_message msg); +LIBSSH_API ssh_message ssh_message_get(ssh_session session); +LIBSSH_API int ssh_message_subtype(ssh_message msg); +LIBSSH_API int ssh_message_type(ssh_message msg); +LIBSSH_API int ssh_mkdir (const char *pathname, mode_t mode); +LIBSSH_API ssh_session ssh_new(void); + +LIBSSH_API int ssh_options_copy(ssh_session src, ssh_session *dest); +LIBSSH_API int ssh_options_getopt(ssh_session session, int *argcptr, char **argv); +LIBSSH_API int ssh_options_parse_config(ssh_session session, const char *filename); +LIBSSH_API int ssh_options_set(ssh_session session, enum ssh_options_e type, + const void *value); +LIBSSH_API int ssh_options_get(ssh_session session, enum ssh_options_e type, + char **value); +LIBSSH_API int ssh_options_get_port(ssh_session session, unsigned int * port_target); +LIBSSH_API int ssh_pcap_file_close(ssh_pcap_file pcap); +LIBSSH_API void ssh_pcap_file_free(ssh_pcap_file pcap); +LIBSSH_API ssh_pcap_file ssh_pcap_file_new(void); +LIBSSH_API int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename); + +/** + * @brief SSH authentication callback. + * + * @param prompt Prompt to be displayed. + * @param buf Buffer to save the password. You should null-terminate it. + * @param len Length of the buffer. + * @param echo Enable or disable the echo of what you type. + * @param verify Should the password be verified? + * @param userdata Userdata to be passed to the callback function. Useful + * for GUI applications. + * + * @return 0 on success, < 0 on error. + */ +typedef int (*ssh_auth_callback) (const char *prompt, char *buf, size_t len, + int echo, int verify, void *userdata); + +LIBSSH_API ssh_key ssh_key_new(void); +LIBSSH_API void ssh_key_free (ssh_key key); +LIBSSH_API enum ssh_keytypes_e ssh_key_type(const ssh_key key); +LIBSSH_API const char *ssh_key_type_to_char(enum ssh_keytypes_e type); +LIBSSH_API enum ssh_keytypes_e ssh_key_type_from_name(const char *name); +LIBSSH_API int ssh_key_is_public(const ssh_key k); +LIBSSH_API int ssh_key_is_private(const ssh_key k); +LIBSSH_API int ssh_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what); + +LIBSSH_API int ssh_pki_generate(enum ssh_keytypes_e type, int parameter, + ssh_key *pkey); +LIBSSH_API int ssh_pki_import_privkey_base64(const char *b64_key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + ssh_key *pkey); +LIBSSH_API int ssh_pki_import_privkey_file(const char *filename, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + ssh_key *pkey); + +LIBSSH_API int ssh_pki_import_pubkey_base64(const char *b64_key, + enum ssh_keytypes_e type, + ssh_key *pkey); +LIBSSH_API int ssh_pki_import_pubkey_file(const char *filename, + ssh_key *pkey); + +LIBSSH_API int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey, + ssh_key *pkey); +LIBSSH_API int ssh_pki_export_pubkey_base64(const ssh_key key, + char **b64_key); +LIBSSH_API int ssh_pki_export_pubkey_file(const ssh_key key, + const char *filename); + +LIBSSH_API void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len); +LIBSSH_API int ssh_send_ignore (ssh_session session, const char *data); +LIBSSH_API int ssh_send_debug (ssh_session session, const char *message, int always_display); +LIBSSH_API int ssh_scp_accept_request(ssh_scp scp); +LIBSSH_API int ssh_scp_close(ssh_scp scp); +LIBSSH_API int ssh_scp_deny_request(ssh_scp scp, const char *reason); +LIBSSH_API void ssh_scp_free(ssh_scp scp); +LIBSSH_API int ssh_scp_init(ssh_scp scp); +LIBSSH_API int ssh_scp_leave_directory(ssh_scp scp); +LIBSSH_API ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location); +LIBSSH_API int ssh_scp_pull_request(ssh_scp scp); +LIBSSH_API int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode); +LIBSSH_API int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int perms); +LIBSSH_API int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int perms); +LIBSSH_API int ssh_scp_read(ssh_scp scp, void *buffer, size_t size); +LIBSSH_API const char *ssh_scp_request_get_filename(ssh_scp scp); +LIBSSH_API int ssh_scp_request_get_permissions(ssh_scp scp); +LIBSSH_API size_t ssh_scp_request_get_size(ssh_scp scp); +LIBSSH_API uint64_t ssh_scp_request_get_size64(ssh_scp scp); +LIBSSH_API const char *ssh_scp_request_get_warning(ssh_scp scp); +LIBSSH_API int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len); +LIBSSH_API int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, + fd_set *readfds, struct timeval *timeout); +LIBSSH_API int ssh_service_request(ssh_session session, const char *service); +LIBSSH_API void ssh_set_blocking(ssh_session session, int blocking); +LIBSSH_API void ssh_set_fd_except(ssh_session session); +LIBSSH_API void ssh_set_fd_toread(ssh_session session); +LIBSSH_API void ssh_set_fd_towrite(ssh_session session); +LIBSSH_API void ssh_silent_disconnect(ssh_session session); +LIBSSH_API int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile); + +/* USERAUTH */ +LIBSSH_API int ssh_userauth_none(ssh_session session, const char *username); +LIBSSH_API int ssh_userauth_list(ssh_session session, const char *username); +LIBSSH_API int ssh_userauth_try_publickey(ssh_session session, + const char *username, + const ssh_key pubkey); +LIBSSH_API int ssh_userauth_publickey(ssh_session session, + const char *username, + const ssh_key privkey); +#ifndef _WIN32 +LIBSSH_API int ssh_userauth_agent(ssh_session session, + const char *username); +#endif +LIBSSH_API int ssh_userauth_publickey_auto(ssh_session session, + const char *username, + const char *passphrase); +LIBSSH_API int ssh_userauth_password(ssh_session session, + const char *username, + const char *password); + +LIBSSH_API int ssh_userauth_kbdint(ssh_session session, const char *user, const char *submethods); +LIBSSH_API const char *ssh_userauth_kbdint_getinstruction(ssh_session session); +LIBSSH_API const char *ssh_userauth_kbdint_getname(ssh_session session); +LIBSSH_API int ssh_userauth_kbdint_getnprompts(ssh_session session); +LIBSSH_API const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, char *echo); +LIBSSH_API int ssh_userauth_kbdint_getnanswers(ssh_session session); +LIBSSH_API const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i); +LIBSSH_API int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i, + const char *answer); +LIBSSH_API const char *ssh_version(int req_version); +LIBSSH_API int ssh_write_knownhost(ssh_session session); + +LIBSSH_API void ssh_string_burn(ssh_string str); +LIBSSH_API ssh_string ssh_string_copy(ssh_string str); +LIBSSH_API void *ssh_string_data(ssh_string str); +LIBSSH_API int ssh_string_fill(ssh_string str, const void *data, size_t len); +LIBSSH_API void ssh_string_free(ssh_string str); +LIBSSH_API ssh_string ssh_string_from_char(const char *what); +LIBSSH_API size_t ssh_string_len(ssh_string str); +LIBSSH_API ssh_string ssh_string_new(size_t size); +LIBSSH_API const char *ssh_string_get_char(ssh_string str); +LIBSSH_API char *ssh_string_to_char(ssh_string str); +LIBSSH_API void ssh_string_free_char(char *s); + +LIBSSH_API int ssh_getpass(const char *prompt, char *buf, size_t len, int echo, + int verify); + + +typedef int (*ssh_event_callback)(socket_t fd, int revents, void *userdata); + +LIBSSH_API ssh_event ssh_event_new(void); +LIBSSH_API int ssh_event_add_fd(ssh_event event, socket_t fd, short events, + ssh_event_callback cb, void *userdata); +LIBSSH_API int ssh_event_add_session(ssh_event event, ssh_session session); +LIBSSH_API int ssh_event_dopoll(ssh_event event, int timeout); +LIBSSH_API int ssh_event_remove_fd(ssh_event event, socket_t fd); +LIBSSH_API int ssh_event_remove_session(ssh_event event, ssh_session session); +LIBSSH_API void ssh_event_free(ssh_event event); +LIBSSH_API const char* ssh_get_serverbanner(ssh_session session); + +#ifndef LIBSSH_LEGACY_0_4 +#include "libssh/legacy.h" +#endif + +#ifdef __cplusplus +} +#endif +#endif /* _LIBSSH_H */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/include/libssh/libsshpp.hpp b/libssh/include/libssh/libsshpp.hpp new file mode 100644 index 00000000..16e27dd8 --- /dev/null +++ b/libssh/include/libssh/libsshpp.hpp @@ -0,0 +1,596 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBSSHPP_HPP_ +#define LIBSSHPP_HPP_ + +/** + * @defgroup ssh_cpp The libssh C++ wrapper + * + * The C++ bindings for libssh are completely embedded in a single .hpp file, and + * this for two reasons: + * - C++ is hard to keep binary compatible, C is easy. We try to keep libssh C version + * as much as possible binary compatible between releases, while this would be hard for + * C++. If you compile your program with these headers, you will only link to the C version + * of libssh which will be kept ABI compatible. No need to recompile your C++ program + * each time a new binary-compatible version of libssh is out + * - Most of the functions in this file are really short and are probably worth the "inline" + * linking mode, which the compiler can decide to do in some case. There would be nearly no + * performance penalty of using the wrapper rather than native calls. + * + * Please visit the documentation of ssh::Session and ssh::Channel + * @see ssh::Session + * @see ssh::Channel + * + * If you wish not to use C++ exceptions, please define SSH_NO_CPP_EXCEPTIONS: + * @code + * #define SSH_NO_CPP_EXCEPTIONS + * #include + * @endcode + * All functions will then return SSH_ERROR in case of error. + * @{ + */ + +/* do not use deprecated functions */ +#define LIBSSH_LEGACY_0_4 + +#include +#include +#include +#include +#include + +namespace ssh { + +class Channel; +/** Some people do not like C++ exceptions. With this define, we give + * the choice to use or not exceptions. + * @brief if defined, disable C++ exceptions for libssh c++ wrapper + */ +#ifndef SSH_NO_CPP_EXCEPTIONS + +/** @brief This class describes a SSH Exception object. This object can be thrown + * by several SSH functions that interact with the network, and may fail because of + * socket, protocol or memory errors. + */ +class SshException{ +public: + SshException(ssh_session csession){ + code=ssh_get_error_code(csession); + description=std::string(ssh_get_error(csession)); + } + SshException(const SshException &e){ + code=e.code; + description=e.description; + } + /** @brief returns the Error code + * @returns SSH_FATAL Fatal error happened (not recoverable) + * @returns SSH_REQUEST_DENIED Request was denied by remote host + * @see ssh_get_error_code + */ + int getCode(){ + return code; + } + /** @brief returns the error message of the last exception + * @returns pointer to a c string containing the description of error + * @see ssh_get_error + */ + std::string getError(){ + return description; + } +private: + int code; + std::string description; +}; + +/** @internal + * @brief Macro to throw exception if there was an error + */ +#define ssh_throw(x) if((x)==SSH_ERROR) throw SshException(getCSession()) +#define ssh_throw_null(CSession,x) if((x)==NULL) throw SshException(CSession) +#define void_throwable void +#define return_throwable return + +#else + +/* No exception at all. All functions will return an error code instead + * of an exception + */ +#define ssh_throw(x) if((x)==SSH_ERROR) return SSH_ERROR +#define ssh_throw_null(CSession,x) if((x)==NULL) return NULL +#define void_throwable int +#define return_throwable return SSH_OK +#endif + +/** + * The ssh::Session class contains the state of a SSH connection. + */ +class Session { + friend class Channel; +public: + Session(){ + c_session=ssh_new(); + } + ~Session(){ + ssh_free(c_session); + c_session=NULL; + } + /** @brief sets an SSH session options + * @param type Type of option + * @param option cstring containing the value of option + * @throws SshException on error + * @see ssh_options_set + */ + void_throwable setOption(enum ssh_options_e type, const char *option){ + ssh_throw(ssh_options_set(c_session,type,option)); + return_throwable; + } + /** @brief sets an SSH session options + * @param type Type of option + * @param option long integer containing the value of option + * @throws SshException on error + * @see ssh_options_set + */ + void_throwable setOption(enum ssh_options_e type, long int option){ + ssh_throw(ssh_options_set(c_session,type,&option)); + return_throwable; + } + /** @brief sets an SSH session options + * @param type Type of option + * @param option void pointer containing the value of option + * @throws SshException on error + * @see ssh_options_set + */ + void_throwable setOption(enum ssh_options_e type, void *option){ + ssh_throw(ssh_options_set(c_session,type,option)); + return_throwable; + } + /** @brief connects to the remote host + * @throws SshException on error + * @see ssh_connect + */ + void_throwable connect(){ + int ret=ssh_connect(c_session); + ssh_throw(ret); + return_throwable; + } + /** @brief Authenticates automatically using public key + * @throws SshException on error + * @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED + * @see ssh_userauth_autopubkey + */ + int userauthPublickeyAuto(void){ + int ret=ssh_userauth_publickey_auto(c_session, NULL, NULL); + ssh_throw(ret); + return ret; + } + /** @brief Authenticates using the "none" method. Prefer using autopubkey if + * possible. + * @throws SshException on error + * @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED + * @see ssh_userauth_none + * @see Session::userauthAutoPubkey + */ + int userauthNone(){ + int ret=ssh_userauth_none(c_session,NULL); + ssh_throw(ret); + return ret; + } + /** @brief Authenticates using the password method. + * @param[in] password password to use for authentication + * @throws SshException on error + * @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED + * @see ssh_userauth_password + */ + int userauthPassword(const char *password){ + int ret=ssh_userauth_password(c_session,NULL,password); + ssh_throw(ret); + return ret; + } + /** @brief Try to authenticate using the publickey method. + * @param[in] pubkey public key to use for authentication + * @throws SshException on error + * @returns SSH_AUTH_SUCCESS if the pubkey is accepted, + * @returns SSH_AUTH_DENIED if the pubkey is denied + * @see ssh_userauth_try_pubkey + */ + int userauthTryPublickey(ssh_key pubkey){ + int ret=ssh_userauth_try_publickey(c_session, NULL, pubkey); + ssh_throw(ret); + return ret; + } + /** @brief Authenticates using the publickey method. + * @param[in] privkey private key to use for authentication + * @throws SshException on error + * @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED + * @see ssh_userauth_pubkey + */ + int userauthPublickey(ssh_key privkey){ + int ret=ssh_userauth_publickey(c_session, NULL, privkey); + ssh_throw(ret); + return ret; + } + int userauthPrivatekeyFile(const char *filename, + const char *passphrase); + /** @brief Returns the available authentication methods from the server + * @throws SshException on error + * @returns Bitfield of available methods. + * @see ssh_userauth_list + */ + int getAuthList(){ + int ret=ssh_userauth_list(c_session, NULL); + ssh_throw(ret); + return ret; + } + /** @brief Disconnects from the SSH server and closes connection + * @see ssh_disconnect + */ + void disconnect(){ + ssh_disconnect(c_session); + } + /** @brief Returns the disconnect message from the server, if any + * @returns pointer to the message, or NULL. Do not attempt to free + * the pointer. + */ + const char *getDisconnectMessage(){ + const char *msg=ssh_get_disconnect_message(c_session); + return msg; + } + /** @internal + * @brief gets error message + */ + const char *getError(){ + return ssh_get_error(c_session); + } + /** @internal + * @brief returns error code + */ + int getErrorCode(){ + return ssh_get_error_code(c_session); + } + /** @brief returns the file descriptor used for the communication + * @returns the file descriptor + * @warning if a proxycommand is used, this function will only return + * one of the two file descriptors being used + * @see ssh_get_fd + */ + socket_t getSocket(){ + return ssh_get_fd(c_session); + } + /** @brief gets the Issue banner from the ssh server + * @returns the issue banner. This is generally a MOTD from server + * @see ssh_get_issue_banner + */ + std::string getIssueBanner(){ + char *banner=ssh_get_issue_banner(c_session); + std::string ret= std::string(banner); + ::free(banner); + return ret; + } + /** @brief returns the OpenSSH version (server) if possible + * @returns openssh version code + * @see ssh_get_openssh_version + */ + int getOpensshVersion(){ + return ssh_get_openssh_version(c_session); + } + /** @brief returns the version of the SSH protocol being used + * @returns the SSH protocol version + * @see ssh_get_version + */ + int getVersion(){ + return ssh_get_version(c_session); + } + /** @brief verifies that the server is known + * @throws SshException on error + * @returns Integer value depending on the knowledge of the + * server key + * @see ssh_is_server_known + */ + int isServerKnown(){ + int ret=ssh_is_server_known(c_session); + ssh_throw(ret); + return ret; + } + void log(int priority, const char *format, ...){ + char buffer[1024]; + va_list va; + + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + ssh_log(c_session,priority, "%s", buffer); + } + + /** @brief copies options from a session to another + * @throws SshException on error + * @see ssh_options_copy + */ + void_throwable optionsCopy(const Session &source){ + ssh_throw(ssh_options_copy(source.c_session,&c_session)); + return_throwable; + } + /** @brief parses a configuration file for options + * @throws SshException on error + * @param[in] file configuration file name + * @see ssh_options_parse_config + */ + void_throwable optionsParseConfig(const char *file){ + ssh_throw(ssh_options_parse_config(c_session,file)); + return_throwable; + } + /** @brief silently disconnect from remote host + * @see ssh_silent_disconnect + */ + void silentDisconnect(){ + ssh_silent_disconnect(c_session); + } + /** @brief Writes the known host file with current + * host key + * @throws SshException on error + * @see ssh_write_knownhost + */ + int writeKnownhost(){ + int ret = ssh_write_knownhost(c_session); + ssh_throw(ret); + return ret; + } + + /** @brief accept an incoming forward connection + * @param[in] timeout_ms timeout for waiting, in ms + * @returns new Channel pointer on the forward connection + * @returns NULL in case of error + * @warning you have to delete this pointer after use + * @see ssh_channel_forward_accept + * @see Session::listenForward + */ + Channel *acceptForward(int timeout_ms); + /* acceptForward is implemented later in this file */ + + void_throwable cancelForward(const char *address, int port){ + int err=ssh_forward_cancel(c_session, address, port); + ssh_throw(err); + return_throwable; + } + + void_throwable listenForward(const char *address, int port, + int &boundport){ + int err=ssh_forward_listen(c_session, address, port, &boundport); + ssh_throw(err); + return_throwable; + } + +private: + ssh_session c_session; + ssh_session getCSession(){ + return c_session; + } + /* No copy constructor, no = operator */ + Session(const Session &); + Session& operator=(const Session &); +}; + +/** @brief the ssh::Channel class describes the state of an SSH + * channel. + * @see ssh_channel + */ +class Channel { + friend class Session; +public: + Channel(Session &session){ + channel=ssh_channel_new(session.getCSession()); + this->session=&session; + } + ~Channel(){ + ssh_channel_free(channel); + channel=NULL; + } + + /** @brief accept an incoming X11 connection + * @param[in] timeout_ms timeout for waiting, in ms + * @returns new Channel pointer on the X11 connection + * @returns NULL in case of error + * @warning you have to delete this pointer after use + * @see ssh_channel_accept_x11 + * @see Channel::requestX11 + */ + Channel *acceptX11(int timeout_ms){ + ssh_channel x11chan = ssh_channel_accept_x11(channel,timeout_ms); + ssh_throw_null(getCSession(),x11chan); + Channel *newchan = new Channel(getSession(),x11chan); + return newchan; + } + /** @brief change the size of a pseudoterminal + * @param[in] cols number of columns + * @param[in] rows number of rows + * @throws SshException on error + * @see ssh_channel_change_pty_size + */ + void_throwable changePtySize(int cols, int rows){ + int err=ssh_channel_change_pty_size(channel,cols,rows); + ssh_throw(err); + return_throwable; + } + + /** @brief closes a channel + * @throws SshException on error + * @see ssh_channel_close + */ + void_throwable close(){ + ssh_throw(ssh_channel_close(channel)); + return_throwable; + } + + int getExitStatus(){ + return ssh_channel_get_exit_status(channel); + } + Session &getSession(){ + return *session; + } + /** @brief returns true if channel is in closed state + * @see ssh_channel_is_closed + */ + bool isClosed(){ + return ssh_channel_is_closed(channel) != 0; + } + /** @brief returns true if channel is in EOF state + * @see ssh_channel_is_eof + */ + bool isEof(){ + return ssh_channel_is_eof(channel) != 0; + } + /** @brief returns true if channel is in open state + * @see ssh_channel_is_open + */ + bool isOpen(){ + return ssh_channel_is_open(channel) != 0; + } + int openForward(const char *remotehost, int remoteport, + const char *sourcehost=NULL, int localport=0){ + int err=ssh_channel_open_forward(channel,remotehost,remoteport, + sourcehost, localport); + ssh_throw(err); + return err; + } + /* TODO: completely remove this ? */ + void_throwable openSession(){ + int err=ssh_channel_open_session(channel); + ssh_throw(err); + return_throwable; + } + int poll(bool is_stderr=false){ + int err=ssh_channel_poll(channel,is_stderr); + ssh_throw(err); + return err; + } + int read(void *dest, size_t count, bool is_stderr=false){ + int err; + /* handle int overflow */ + if(count > 0x7fffffff) + count = 0x7fffffff; + err=ssh_channel_read(channel,dest,count,is_stderr); + ssh_throw(err); + return err; + } + int readNonblocking(void *dest, size_t count, bool is_stderr=false){ + int err; + /* handle int overflow */ + if(count > 0x7fffffff) + count = 0x7fffffff; + err=ssh_channel_read_nonblocking(channel,dest,count,is_stderr); + ssh_throw(err); + return err; + } + void_throwable requestEnv(const char *name, const char *value){ + int err=ssh_channel_request_env(channel,name,value); + ssh_throw(err); + return_throwable; + } + + void_throwable requestExec(const char *cmd){ + int err=ssh_channel_request_exec(channel,cmd); + ssh_throw(err); + return_throwable; + } + void_throwable requestPty(const char *term=NULL, int cols=0, int rows=0){ + int err; + if(term != NULL && cols != 0 && rows != 0) + err=ssh_channel_request_pty_size(channel,term,cols,rows); + else + err=ssh_channel_request_pty(channel); + ssh_throw(err); + return_throwable; + } + + void_throwable requestShell(){ + int err=ssh_channel_request_shell(channel); + ssh_throw(err); + return_throwable; + } + void_throwable requestSendSignal(const char *signum){ + int err=ssh_channel_request_send_signal(channel, signum); + ssh_throw(err); + return_throwable; + } + void_throwable requestSubsystem(const char *subsystem){ + int err=ssh_channel_request_subsystem(channel,subsystem); + ssh_throw(err); + return_throwable; + } + int requestX11(bool single_connection, + const char *protocol, const char *cookie, int screen_number){ + int err=ssh_channel_request_x11(channel,single_connection, + protocol, cookie, screen_number); + ssh_throw(err); + return err; + } + void_throwable sendEof(){ + int err=ssh_channel_send_eof(channel); + ssh_throw(err); + return_throwable; + } + /** @brief Writes on a channel + * @param data data to write. + * @param len number of bytes to write. + * @param is_stderr write should be done on the stderr channel (server only) + * @returns number of bytes written + * @throws SshException in case of error + * @see channel_write + * @see channel_write_stderr + */ + int write(const void *data, size_t len, bool is_stderr=false){ + int ret; + if(is_stderr){ + ret=ssh_channel_write_stderr(channel,data,len); + } else { + ret=ssh_channel_write(channel,data,len); + } + ssh_throw(ret); + return ret; + } +private: + ssh_session getCSession(){ + return session->getCSession(); + } + Channel (Session &session, ssh_channel c_channel){ + this->channel=c_channel; + this->session=&session; + } + Session *session; + ssh_channel channel; + /* No copy and no = operator */ + Channel(const Channel &); + Channel &operator=(const Channel &); +}; + + +/* This code cannot be put inline due to references to Channel */ +Channel *Session::acceptForward(int timeout_ms){ + ssh_channel forward = ssh_forward_accept(c_session, + timeout_ms); + ssh_throw_null(c_session,forward); + Channel *newchan = new Channel(*this,forward); + return newchan; + } + +} // namespace ssh + +/** @} */ +#endif /* LIBSSHPP_HPP_ */ diff --git a/libssh/include/libssh/messages.h b/libssh/include/libssh/messages.h new file mode 100644 index 00000000..f196c6f7 --- /dev/null +++ b/libssh/include/libssh/messages.h @@ -0,0 +1,104 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MESSAGES_H_ +#define MESSAGES_H_ + +#include "config.h" + +struct ssh_auth_request { + char *username; + int method; + char *password; + struct ssh_key_struct *pubkey; + char signature_state; + char kbdint_response; +}; + +struct ssh_channel_request_open { + int type; + uint32_t sender; + uint32_t window; + uint32_t packet_size; + char *originator; + uint16_t originator_port; + char *destination; + uint16_t destination_port; +}; + +struct ssh_service_request { + char *service; +}; + +struct ssh_global_request { + int type; + uint8_t want_reply; + char *bind_address; + uint16_t bind_port; +}; + +struct ssh_channel_request { + int type; + ssh_channel channel; + uint8_t want_reply; + /* pty-req type specifics */ + char *TERM; + uint32_t width; + uint32_t height; + uint32_t pxwidth; + uint32_t pxheight; + ssh_string modes; + + /* env type request */ + char *var_name; + char *var_value; + /* exec type request */ + char *command; + /* subsystem */ + char *subsystem; + + /* X11 */ + uint8_t x11_single_connection; + const char *x11_auth_protocol; + const char *x11_auth_cookie; + uint32_t x11_screen_number; +}; + +struct ssh_message_struct { + ssh_session session; + int type; + struct ssh_auth_request auth_request; + struct ssh_channel_request_open channel_request_open; + struct ssh_channel_request channel_request; + struct ssh_service_request service_request; + struct ssh_global_request global_request; +}; + +SSH_PACKET_CALLBACK(ssh_packet_channel_open); +SSH_PACKET_CALLBACK(ssh_packet_service_request); +SSH_PACKET_CALLBACK(ssh_packet_userauth_request); +SSH_PACKET_CALLBACK(ssh_packet_global_request); + +int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet, + const char *request, uint8_t want_reply); +void ssh_message_queue(ssh_session session, ssh_message message); +ssh_message ssh_message_pop_head(ssh_session session); + +#endif /* MESSAGES_H_ */ diff --git a/libssh/include/libssh/misc.h b/libssh/include/libssh/misc.h new file mode 100644 index 00000000..1e69b069 --- /dev/null +++ b/libssh/include/libssh/misc.h @@ -0,0 +1,92 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MISC_H_ +#define MISC_H_ + +/* in misc.c */ +/* gets the user home dir. */ +char *ssh_get_user_home_dir(void); +char *ssh_get_local_username(void); +int ssh_file_readaccess_ok(const char *file); + +char *ssh_path_expand_tilde(const char *d); +char *ssh_path_expand_escape(ssh_session session, const char *s); +int ssh_analyze_banner(ssh_session session, int server, int *ssh1, int *ssh2); +int ssh_is_ipaddr_v4(const char *str); +int ssh_is_ipaddr(const char *str); + +#ifndef HAVE_NTOHLL +/* macro for byte ordering */ +uint64_t ntohll(uint64_t); +#endif + +#ifndef HAVE_HTONLL +#define htonll(x) ntohll((x)) +#endif + +/* list processing */ + +struct ssh_list { + struct ssh_iterator *root; + struct ssh_iterator *end; +}; + +struct ssh_iterator { + struct ssh_iterator *next; + const void *data; +}; + +struct ssh_timestamp { + long seconds; + long useconds; +}; + +struct ssh_list *ssh_list_new(void); +void ssh_list_free(struct ssh_list *list); +struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list); +struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value); +int ssh_list_append(struct ssh_list *list, const void *data); +int ssh_list_prepend(struct ssh_list *list, const void *data); +void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator); +char *ssh_lowercase(const char* str); +char *ssh_hostport(const char *host, int port); + +const void *_ssh_list_pop_head(struct ssh_list *list); + +#define ssh_iterator_value(type, iterator)\ + ((type)((iterator)->data)) + +/** @brief fetch the head element of a list and remove it from list + * @param type type of the element to return + * @param list the ssh_list to use + * @return the first element of the list, or NULL if the list is empty + */ +#define ssh_list_pop_head(type, ssh_list)\ + ((type)_ssh_list_pop_head(ssh_list)) + +int ssh_make_milliseconds(long sec, long usec); +void ssh_timestamp_init(struct ssh_timestamp *ts); +int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout); +int ssh_timeout_update(struct ssh_timestamp *ts, int timeout); + +int ssh_match_group(const char *group, const char *object); + +#endif /* MISC_H_ */ diff --git a/libssh/include/libssh/options.h b/libssh/include/libssh/options.h new file mode 100644 index 00000000..4078ad08 --- /dev/null +++ b/libssh/include/libssh/options.h @@ -0,0 +1,28 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2011 Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _OPTIONS_H +#define _OPTIONS_H + +int ssh_config_parse_file(ssh_session session, const char *filename); +int ssh_options_set_algo(ssh_session session, int algo, const char *list); +int ssh_options_apply(ssh_session session); + +#endif /* _OPTIONS_H */ diff --git a/libssh/include/libssh/packet.h b/libssh/include/libssh/packet.h new file mode 100644 index 00000000..513eaa81 --- /dev/null +++ b/libssh/include/libssh/packet.h @@ -0,0 +1,87 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PACKET_H_ +#define PACKET_H_ + +struct ssh_socket_struct; + +/* this structure should go someday */ +typedef struct packet_struct { + int valid; + uint32_t len; + uint8_t type; +} PACKET; + +/** different state of packet reading. */ +enum ssh_packet_state_e { + /** Packet not initialized, must read the size of packet */ + PACKET_STATE_INIT, + /** Size was read, waiting for the rest of data */ + PACKET_STATE_SIZEREAD, + /** Full packet was read and callbacks are being called. Future packets + * should wait for the end of the callback. */ + PACKET_STATE_PROCESSING +}; + +int packet_send(ssh_session session); + +#ifdef WITH_SSH1 +int packet_send1(ssh_session session) ; +void ssh_packet_set_default_callbacks1(ssh_session session); + +SSH_PACKET_CALLBACK(ssh_packet_disconnect1); +SSH_PACKET_CALLBACK(ssh_packet_smsg_success1); +SSH_PACKET_CALLBACK(ssh_packet_smsg_failure1); +int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user); + +#endif + +SSH_PACKET_CALLBACK(ssh_packet_unimplemented); +SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback); +SSH_PACKET_CALLBACK(ssh_packet_ignore_callback); +SSH_PACKET_CALLBACK(ssh_packet_dh_reply); +SSH_PACKET_CALLBACK(ssh_packet_newkeys); +SSH_PACKET_CALLBACK(ssh_packet_service_accept); + +#ifdef WITH_SERVER +SSH_PACKET_CALLBACK(ssh_packet_kexdh_init); +#endif + +int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum); +int ssh_packet_parse_type(ssh_session session); +//int packet_flush(ssh_session session, int enforce_blocking); + +int ssh_packet_socket_callback(const void *data, size_t len, void *user); +void ssh_packet_register_socket_callback(ssh_session session, struct ssh_socket_struct *s); +void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks); +void ssh_packet_set_default_callbacks(ssh_session session); +void ssh_packet_process(ssh_session session, uint8_t type); + +/* PACKET CRYPT */ +uint32_t packet_decrypt_len(ssh_session session, char *crypted); +int packet_decrypt(ssh_session session, void *packet, unsigned int len); +unsigned char *packet_encrypt(ssh_session session, + void *packet, + unsigned int len); +int packet_hmac_verify(ssh_session session,ssh_buffer buffer, + unsigned char *mac); + +#endif /* PACKET_H_ */ diff --git a/libssh/include/libssh/pcap.h b/libssh/include/libssh/pcap.h new file mode 100644 index 00000000..ca57156b --- /dev/null +++ b/libssh/include/libssh/pcap.h @@ -0,0 +1,46 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PCAP_H_ +#define PCAP_H_ + +#include "config.h" +#include "libssh/libssh.h" + +#ifdef WITH_PCAP +typedef struct ssh_pcap_context_struct* ssh_pcap_context; + +int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len); + +ssh_pcap_context ssh_pcap_context_new(ssh_session session); +void ssh_pcap_context_free(ssh_pcap_context ctx); + +enum ssh_pcap_direction{ + SSH_PCAP_DIR_IN, + SSH_PCAP_DIR_OUT +}; +void ssh_pcap_context_set_file(ssh_pcap_context, ssh_pcap_file); +int ssh_pcap_context_write(ssh_pcap_context,enum ssh_pcap_direction direction, void *data, + uint32_t len, uint32_t origlen); + + +#endif /* WITH_PCAP */ +#endif /* PCAP_H_ */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/include/libssh/pki.h b/libssh/include/libssh/pki.h new file mode 100644 index 00000000..d7fa5e57 --- /dev/null +++ b/libssh/include/libssh/pki.h @@ -0,0 +1,121 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PKI_H_ +#define PKI_H_ + +#ifdef HAVE_OPENSSL_EC_H +#include +#endif +#ifdef HAVE_OPENSSL_ECDSA_H +#include +#endif + +#include "libssh/crypto.h" + +#define MAX_PUBKEY_SIZE 0x100000 /* 1M */ + +#define SSH_KEY_FLAG_EMPTY 0x0 +#define SSH_KEY_FLAG_PUBLIC 0x0001 +#define SSH_KEY_FLAG_PRIVATE 0x0002 + +struct ssh_key_struct { + enum ssh_keytypes_e type; + int flags; + const char *type_c; /* Don't free it ! it is static */ + int ecdsa_nid; +#ifdef HAVE_LIBGCRYPT + gcry_sexp_t dsa; + gcry_sexp_t rsa; + void *ecdsa; +#elif HAVE_LIBCRYPTO + DSA *dsa; + RSA *rsa; +#ifdef HAVE_OPENSSL_ECC + EC_KEY *ecdsa; +#else + void *ecdsa; +#endif /* HAVE_OPENSSL_EC_H */ +#endif + void *cert; +}; + +struct ssh_signature_struct { + enum ssh_keytypes_e type; +#ifdef HAVE_LIBGCRYPT + gcry_sexp_t dsa_sig; + gcry_sexp_t rsa_sig; + void *ecdsa_sig; +#elif defined HAVE_LIBCRYPTO + DSA_SIG *dsa_sig; + ssh_string rsa_sig; +# ifdef HAVE_OPENSSL_ECC + ECDSA_SIG *ecdsa_sig; +# else + void *ecdsa_sig; +# endif +#endif +}; + +typedef struct ssh_signature_struct *ssh_signature; + +/* SSH Key Functions */ +ssh_key ssh_key_dup(const ssh_key key); +void ssh_key_clean (ssh_key key); + +/* SSH Signature Functions */ +ssh_signature ssh_signature_new(void); +void ssh_signature_free(ssh_signature sign); + +int ssh_pki_export_signature_blob(const ssh_signature sign, + ssh_string *sign_blob); +int ssh_pki_import_signature_blob(const ssh_string sig_blob, + const ssh_key pubkey, + ssh_signature *psig); +int ssh_pki_signature_verify_blob(ssh_session session, + ssh_string sig_blob, + const ssh_key key, + unsigned char *digest, + size_t dlen); + +/* SSH Public Key Functions */ +int ssh_pki_export_pubkey_blob(const ssh_key key, + ssh_string *pblob); +int ssh_pki_import_pubkey_blob(const ssh_string key_blob, + ssh_key *pkey); +int ssh_pki_export_pubkey_rsa1(const ssh_key key, + const char *host, + char *rsa1, + size_t rsa1_len); + +/* SSH Signing Functions */ +ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf, + ssh_key privatekey); +ssh_string ssh_pki_do_sign_agent(ssh_session session, + struct ssh_buffer_struct *buf, + const ssh_key pubkey); +ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, + const ssh_key privkey); + +/* Temporary functions, to be removed after migration to ssh_key */ +ssh_public_key ssh_pki_convert_key_to_publickey(ssh_key key); +ssh_private_key ssh_pki_convert_key_to_privatekey(ssh_key key); + +#endif /* PKI_H_ */ diff --git a/libssh/include/libssh/pki_priv.h b/libssh/include/libssh/pki_priv.h new file mode 100644 index 00000000..2c361e43 --- /dev/null +++ b/libssh/include/libssh/pki_priv.h @@ -0,0 +1,88 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PKI_PRIV_H_ +#define PKI_PRIV_H_ + +#define RSA_HEADER_BEGIN "-----BEGIN RSA PRIVATE KEY-----" +#define RSA_HEADER_END "-----END RSA PRIVATE KEY-----" +#define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----" +#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----" +#define ECDSA_HEADER_BEGIN "-----BEGIN EC PRIVATE KEY-----" +#define ECDSA_HEADER_END "-----END EC PRIVATE KEY-----" + +#define ssh_pki_log(...) \ + _ssh_pki_log(__FUNCTION__, __VA_ARGS__) +void _ssh_pki_log(const char *function, + const char *format, ...) PRINTF_ATTRIBUTE(2, 3); + +int pki_key_ecdsa_nid_from_name(const char *name); + +/* SSH Key Functions */ +ssh_key pki_key_dup(const ssh_key key, int demote); +int pki_key_generate_rsa(ssh_key key, int parameter); +int pki_key_generate_dss(ssh_key key, int parameter); +int pki_key_generate_ecdsa(ssh_key key, int parameter); +int pki_key_compare(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what); + +/* SSH Private Key Functions */ +enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey); +ssh_key pki_private_key_from_base64(const char *b64_key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data); + +/* SSH Public Key Functions */ +int pki_pubkey_build_dss(ssh_key key, + ssh_string p, + ssh_string q, + ssh_string g, + ssh_string pubkey); +int pki_pubkey_build_rsa(ssh_key key, + ssh_string e, + ssh_string n); +int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e); +ssh_string pki_publickey_to_blob(const ssh_key key); +int pki_export_pubkey_rsa1(const ssh_key key, + const char *host, + char *rsa1, + size_t rsa1_len); + +/* SSH Signature Functions */ +ssh_string pki_signature_to_blob(const ssh_signature sign); +ssh_signature pki_signature_from_blob(const ssh_key pubkey, + const ssh_string sig_blob, + enum ssh_keytypes_e type); +int pki_signature_verify(ssh_session session, + const ssh_signature sig, + const ssh_key key, + const unsigned char *hash, + size_t hlen); + +/* SSH Signing Functions */ +ssh_signature pki_do_sign(const ssh_key privkey, + const unsigned char *hash, + size_t hlen); +ssh_signature pki_do_sign_sessionid(const ssh_key key, + const unsigned char *hash, + size_t hlen); +#endif /* PKI_PRIV_H_ */ diff --git a/libssh/include/libssh/poll.h b/libssh/include/libssh/poll.h new file mode 100644 index 00000000..bbc03a95 --- /dev/null +++ b/libssh/include/libssh/poll.h @@ -0,0 +1,159 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef POLL_H_ +#define POLL_H_ + +#include "config.h" + +#ifdef HAVE_POLL + +#include +typedef struct pollfd ssh_pollfd_t; + +#else /* HAVE_POLL */ + +/* poll emulation support */ + +typedef struct ssh_pollfd_struct { + socket_t fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +} ssh_pollfd_t; + +typedef unsigned long int nfds_t; + +#ifdef _WIN32 + +#ifndef POLLRDNORM +#define POLLRDNORM 0x0100 +#endif +#ifndef POLLRDBAND +#define POLLRDBAND 0x0200 +#endif +#ifndef POLLIN +#define POLLIN (POLLRDNORM | POLLRDBAND) +#endif +#ifndef POLLPRI +#define POLLPRI 0x0400 +#endif + +#ifndef POLLWRNORM +#define POLLWRNORM 0x0010 +#endif +#ifndef POLLOUT +#define POLLOUT (POLLWRNORM) +#endif +#ifndef POLLWRBAND +#define POLLWRBAND 0x0020 +#endif + +#ifndef POLLERR +#define POLLERR 0x0001 +#endif +#ifndef POLLHUP +#define POLLHUP 0x0002 +#endif +#ifndef POLLNVAL +#define POLLNVAL 0x0004 +#endif + +#else /* _WIN32 */ + +/* poll.c */ +#ifndef POLLIN +#define POLLIN 0x001 /* There is data to read. */ +#endif +#ifndef POLLPRI +#define POLLPRI 0x002 /* There is urgent data to read. */ +#endif +#ifndef POLLOUT +#define POLLOUT 0x004 /* Writing now will not block. */ +#endif + +#ifndef POLLERR +#define POLLERR 0x008 /* Error condition. */ +#endif +#ifndef POLLHUP +#define POLLHUP 0x010 /* Hung up. */ +#endif +#ifndef POLLNVAL +#define POLLNVAL 0x020 /* Invalid polling request. */ +#endif + +#ifndef POLLRDNORM +#define POLLRDNORM 0x040 /* mapped to read fds_set */ +#endif +#ifndef POLLRDBAND +#define POLLRDBAND 0x080 /* mapped to exception fds_set */ +#endif +#ifndef POLLWRNORM +#define POLLWRNORM 0x100 /* mapped to write fds_set */ +#endif +#ifndef POLLWRBAND +#define POLLWRBAND 0x200 /* mapped to write fds_set */ +#endif + +#endif /* WIN32 */ +#endif /* HAVE_POLL */ + +void ssh_poll_init(void); +void ssh_poll_cleanup(void); +int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout); +typedef struct ssh_poll_ctx_struct *ssh_poll_ctx; +typedef struct ssh_poll_handle_struct *ssh_poll_handle; + +/** + * @brief SSH poll callback. This callback will be used when an event + * caught on the socket. + * + * @param p Poll object this callback belongs to. + * @param fd The raw socket. + * @param revents The current poll events on the socket. + * @param userdata Userdata to be passed to the callback function. + * + * @return 0 on success, < 0 if you removed the poll object from + * its poll context. + */ +typedef int (*ssh_poll_callback)(ssh_poll_handle p, socket_t fd, int revents, + void *userdata); + +struct ssh_socket_struct; + +ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb, + void *userdata); +void ssh_poll_free(ssh_poll_handle p); +ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p); +short ssh_poll_get_events(ssh_poll_handle p); +void ssh_poll_set_events(ssh_poll_handle p, short events); +void ssh_poll_add_events(ssh_poll_handle p, short events); +void ssh_poll_remove_events(ssh_poll_handle p, short events); +socket_t ssh_poll_get_fd(ssh_poll_handle p); +void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd); +void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata); +ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size); +void ssh_poll_ctx_free(ssh_poll_ctx ctx); +int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p); +int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, struct ssh_socket_struct *s); +void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p); +int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout); +ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session); + +#endif /* POLL_H_ */ diff --git a/libssh/include/libssh/priv.h b/libssh/include/libssh/priv.h new file mode 100644 index 00000000..912a1918 --- /dev/null +++ b/libssh/include/libssh/priv.h @@ -0,0 +1,247 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * priv.h file + * This include file contains everything you shouldn't deal with in + * user programs. Consider that anything in this file might change + * without notice; libssh.h file will keep backward compatibility + * on binary & source + */ + +#ifndef _LIBSSH_PRIV_H +#define _LIBSSH_PRIV_H + +#include "config.h" + +#ifdef _WIN32 + +/* Imitate define of inttypes.h */ +# ifndef PRIdS +# define PRIdS "Id" +# endif + +# ifndef PRIu64 +# if __WORDSIZE == 64 +# define PRIu64 "lu" +# else +# define PRIu64 "llu" +# endif /* __WORDSIZE */ +# endif /* PRIu64 */ + +#if !defined(HAVE_STRTOULL) +# if defined(HAVE___STRTOULL) +# define strtoull __strtoull +# elif defined(__hpux) && defined(__LP64__) +# define strtoull strtoul +# else +# error "no strtoull function found" +# endif +#endif /* !defined(HAVE_STRTOULL) */ + +# ifdef _MSC_VER +# include + +/* On Microsoft compilers define inline to __inline on all others use inline */ +# undef inline +# define inline __inline + +# define strcasecmp _stricmp +# define strncasecmp _strnicmp +# if !defined(HAVE_STRTOULL) +# define strtoull _strtoui64 +# endif +# define isblank(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n' || (ch) == '\r') + +# define usleep(X) Sleep(((X)+1000)/1000) + +# undef strtok_r +# define strtok_r strtok_s + +# if defined(HAVE__SNPRINTF_S) +# undef snprintf +# define snprintf(d, n, ...) _snprintf_s((d), (n), _TRUNCATE, __VA_ARGS__) +# else /* HAVE__SNPRINTF_S */ +# if defined(HAVE__SNPRINTF) +# undef snprintf +# define snprintf _snprintf +# else /* HAVE__SNPRINTF */ +# if !defined(HAVE_SNPRINTF) +# error "no snprintf compatible function found" +# endif /* HAVE_SNPRINTF */ +# endif /* HAVE__SNPRINTF */ +# endif /* HAVE__SNPRINTF_S */ + +# if defined(HAVE__VSNPRINTF_S) +# undef vsnprintf +# define vsnprintf(s, n, f, v) _vsnprintf_s((s), (n), _TRUNCATE, (f), (v)) +# else /* HAVE__VSNPRINTF_S */ +# if defined(HAVE__VSNPRINTF) +# undef vsnprintf +# define vsnprintf _vsnprintf +# else +# if !defined(HAVE_VSNPRINTF) +# error "No vsnprintf compatible function found" +# endif /* HAVE_VSNPRINTF */ +# endif /* HAVE__VSNPRINTF */ +# endif /* HAVE__VSNPRINTF_S */ + +# endif /* _MSC_VER */ + +int gettimeofday(struct timeval *__p, void *__t); + +#else /* _WIN32 */ + +#include +#define PRIdS "zd" + +#endif /* _WIN32 */ + +#include "libssh/libssh.h" +#include "libssh/callbacks.h" + +/* some constants */ +#define MAX_PACKET_LEN 262144 +#define ERROR_BUFFERLEN 1024 +#define CLIENTBANNER1 "SSH-1.5-libssh-" SSH_STRINGIFY(LIBSSH_VERSION) +#define CLIENTBANNER2 "SSH-2.0-libssh-" SSH_STRINGIFY(LIBSSH_VERSION) +#define KBDINT_MAX_PROMPT 256 /* more than openssh's :) */ + +#ifndef __FUNCTION__ +#if defined(__SUNPRO_C) +#define __FUNCTION__ __func__ +#endif +#endif + +#define enter_function() (void)session +#define leave_function() (void)session + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +/* forward declarations */ +struct ssh_common_struct; +struct ssh_kex_struct; + +int ssh_get_key_params(ssh_session session, ssh_key *privkey); + +/* LOGGING */ +#define SSH_LOG(session, priority, ...) \ + ssh_log_common(&session->common, priority, __FUNCTION__, __VA_ARGS__) +void ssh_log_common(struct ssh_common_struct *common, + int verbosity, + const char *function, + const char *format, ...) PRINTF_ATTRIBUTE(4, 5); +void ssh_log_function(int verbosity, + const char *function, + const char *buffer); + + +/* ERROR HANDLING */ + +/* error handling structure */ +struct error_struct { + int error_code; + char error_buffer[ERROR_BUFFERLEN]; +}; + +#define ssh_set_error(error, code, ...) \ + _ssh_set_error(error, code, __FUNCTION__, __VA_ARGS__) +void _ssh_set_error(void *error, + int code, + const char *function, + const char *descr, ...) PRINTF_ATTRIBUTE(4, 5); + +#define ssh_set_error_oom(error) \ + _ssh_set_error_oom(error, __FUNCTION__) +void _ssh_set_error_oom(void *error, const char *function); + +#define ssh_set_error_invalid(error) \ + _ssh_set_error_invalid(error, __FUNCTION__) +void _ssh_set_error_invalid(void *error, const char *function); + + + + + +/* client.c */ + +int ssh_send_banner(ssh_session session, int is_server); + +/* connect.c */ +socket_t ssh_connect_host(ssh_session session, const char *host,const char + *bind_addr, int port, long timeout, long usec); +socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, + const char *bind_addr, int port); + +/* in base64.c */ +ssh_buffer base64_to_bin(const char *source); +unsigned char *bin_to_base64(const unsigned char *source, int len); + +/* gzip.c */ +int compress_buffer(ssh_session session,ssh_buffer buf); +int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen); + +/* match.c */ +int match_hostname(const char *host, const char *pattern, unsigned int len); + + + + +/** Free memory space */ +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0) + +/** Zero a structure */ +#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) + +/** Zero a structure given a pointer to the structure */ +#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0) + +/** Get the size of an array */ +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) + +/** Overwrite a string with '\0' */ +#define BURN_STRING(x) do { if ((x) != NULL) memset((x), '\0', strlen((x))); __asm__ volatile ("" : : : "memory"); } while(0) + +/** Overwrite the buffer with '\0' */ +#define BURN_BUFFER(x, size) do { if ((x) != NULL) memset((x), '\0', (size))); __asm__ volatile ("") : : : "memory"; } while(0) + +/** + * This is a hack to fix warnings. The idea is to use this everywhere that we + * get the "discarding const" warning by the compiler. That doesn't actually + * fix the real issue, but marks the place and you can search the code for + * discard_const. + * + * Please use this macro only when there is no other way to fix the warning. + * We should use this function in only in a very few places. + * + * Also, please call this via the discard_const_p() macro interface, as that + * makes the return type safe. + */ +#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) + +/** + * Type-safe version of discard_const + */ +#define discard_const_p(type, ptr) ((type *)discard_const(ptr)) + +#endif /* _LIBSSH_PRIV_H */ +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/include/libssh/scp.h b/libssh/include/libssh/scp.h new file mode 100644 index 00000000..d356d89b --- /dev/null +++ b/libssh/include/libssh/scp.h @@ -0,0 +1,55 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _SCP_H +#define _SCP_H + +enum ssh_scp_states { + SSH_SCP_NEW, //Data structure just created + SSH_SCP_WRITE_INITED, //Gave our intention to write + SSH_SCP_WRITE_WRITING,//File was opened and currently writing + SSH_SCP_READ_INITED, //Gave our intention to read + SSH_SCP_READ_REQUESTED, //We got a read request + SSH_SCP_READ_READING, //File is opened and reading + SSH_SCP_ERROR, //Something bad happened + SSH_SCP_TERMINATED //Transfer finished +}; + +struct ssh_scp_struct { + ssh_session session; + int mode; + int recursive; + ssh_channel channel; + char *location; + enum ssh_scp_states state; + uint64_t filelen; + uint64_t processed; + enum ssh_scp_request_types request_type; + char *request_name; + char *warning; + int request_mode; +}; + +int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len); +int ssh_scp_integer_mode(const char *mode); +char *ssh_scp_string_mode(int mode); +int ssh_scp_response(ssh_scp scp, char **response); + +#endif diff --git a/libssh/include/libssh/server.h b/libssh/include/libssh/server.h new file mode 100644 index 00000000..6ed8002a --- /dev/null +++ b/libssh/include/libssh/server.h @@ -0,0 +1,393 @@ +/* Public include file for server support */ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @defgroup libssh_server The libssh server API + * + * @{ + */ + +#ifndef SERVER_H +#define SERVER_H + +#include "libssh/libssh.h" +#define SERVERBANNER CLIENTBANNER + +#ifdef __cplusplus +extern "C" { +#endif + +enum ssh_bind_options_e { + SSH_BIND_OPTIONS_BINDADDR, + SSH_BIND_OPTIONS_BINDPORT, + SSH_BIND_OPTIONS_BINDPORT_STR, + SSH_BIND_OPTIONS_HOSTKEY, + SSH_BIND_OPTIONS_DSAKEY, + SSH_BIND_OPTIONS_RSAKEY, + SSH_BIND_OPTIONS_BANNER, + SSH_BIND_OPTIONS_LOG_VERBOSITY, + SSH_BIND_OPTIONS_LOG_VERBOSITY_STR +}; + +typedef struct ssh_bind_struct* ssh_bind; + +/* Callback functions */ + +/** + * @brief Incoming connection callback. This callback is called when a ssh_bind + * has a new incoming connection. + * @param sshbind Current sshbind session handler + * @param message the actual message + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_bind_incoming_connection_callback) (ssh_bind sshbind, + void *userdata); + +/** + * @brief These are the callbacks exported by the ssh_bind structure. + * + * They are called by the server module when events appear on the network. + */ +struct ssh_bind_callbacks_struct { + /** DON'T SET THIS use ssh_callbacks_init() instead. */ + size_t size; + /** A new connection is available. */ + ssh_bind_incoming_connection_callback incoming_connection; +}; +typedef struct ssh_bind_callbacks_struct *ssh_bind_callbacks; + +/** + * @brief Creates a new SSH server bind. + * + * @return A newly allocated ssh_bind session pointer. + */ +LIBSSH_API ssh_bind ssh_bind_new(void); + +/** + * @brief Set the options for the current SSH server bind. + * + * @param sshbind The ssh server bind to configure. + * + * @param type The option type to set. This could be one of the + * following: + * + * - SSH_BIND_OPTIONS_BINDADDR + * The ip address to bind (const char *). + * + * - SSH_BIND_OPTIONS_BINDPORT + * The port to bind (unsigned int). + * + * - SSH_BIND_OPTIONS_BINDPORT_STR + * The port to bind (const char *). + * + * - SSH_BIND_OPTIONS_HOSTKEY + * This specifies the file containing the private host key used + * by SSHv1. (const char *). + * + * - SSH_BIND_OPTIONS_DSAKEY + * This specifies the file containing the private host dsa key + * used by SSHv2. (const char *). + * + * - SSH_BIND_OPTIONS_RSAKEY + * This specifies the file containing the private host dsa key + * used by SSHv2. (const char *). + * + * - SSH_BIND_OPTIONS_BANNER + * That the server banner (version string) for SSH. + * (const char *). + * + * - SSH_BIND_OPTIONS_LOG_VERBOSITY + * Set the session logging verbosity (int).\n + * \n + * The verbosity of the messages. Every log smaller or + * equal to verbosity will be shown. + * - SSH_LOG_NOLOG: No logging + * - SSH_LOG_RARE: Rare conditions or warnings + * - SSH_LOG_ENTRY: API-accessible entrypoints + * - SSH_LOG_PACKET: Packet id and size + * - SSH_LOG_FUNCTIONS: Function entering and leaving + * + * - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR + * Set the session logging verbosity (const char *).\n + * \n + * The verbosity of the messages. Every log smaller or + * equal to verbosity will be shown. + * - SSH_LOG_NOLOG: No logging + * - SSH_LOG_RARE: Rare conditions or warnings + * - SSH_LOG_ENTRY: API-accessible entrypoints + * - SSH_LOG_PACKET: Packet id and size + * - SSH_LOG_FUNCTIONS: Function entering and leaving + * \n + * See the corresponding numbers in libssh.h. + * + * @param value The value to set. This is a generic pointer and the + * datatype which is used should be set according to the + * type set. + * + * @returns SSH_OK on success, SSH_ERROR on invalid option or parameter. + */ +LIBSSH_API int ssh_bind_options_set(ssh_bind sshbind, + enum ssh_bind_options_e type, const void *value); + +/** + * @brief Start listening to the socket. + * + * @param ssh_bind_o The ssh server bind to use. + * + * @return 0 on success, < 0 on error. + */ +LIBSSH_API int ssh_bind_listen(ssh_bind ssh_bind_o); + +/** + * @brief Set the callback for this bind. + * + * @param[in] sshbind The bind to set the callback on. + * + * @param[in] callbacks An already set up ssh_bind_callbacks instance. + * + * @param[in] userdata A pointer to private data to pass to the callbacks. + * + * @return SSH_OK on success, SSH_ERROR if an error occured. + * + * @code + * struct ssh_callbacks_struct cb = { + * .userdata = data, + * .auth_function = my_auth_function + * }; + * ssh_callbacks_init(&cb); + * ssh_bind_set_callbacks(session, &cb); + * @endcode + */ +LIBSSH_API int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, + void *userdata); + +/** + * @brief Set the session to blocking/nonblocking mode. + * + * @param ssh_bind_o The ssh server bind to use. + * + * @param blocking Zero for nonblocking mode. + */ +LIBSSH_API void ssh_bind_set_blocking(ssh_bind ssh_bind_o, int blocking); + +/** + * @brief Recover the file descriptor from the session. + * + * @param ssh_bind_o The ssh server bind to get the fd from. + * + * @return The file descriptor. + */ +LIBSSH_API socket_t ssh_bind_get_fd(ssh_bind ssh_bind_o); + +/** + * @brief Set the file descriptor for a session. + * + * @param ssh_bind_o The ssh server bind to set the fd. + * + * @param fd The file descriptssh_bind B + */ +LIBSSH_API void ssh_bind_set_fd(ssh_bind ssh_bind_o, socket_t fd); + +/** + * @brief Allow the file descriptor to accept new sessions. + * + * @param ssh_bind_o The ssh server bind to use. + */ +LIBSSH_API void ssh_bind_fd_toaccept(ssh_bind ssh_bind_o); + +/** + * @brief Accept an incoming ssh connection and initialize the session. + * + * @param ssh_bind_o The ssh server bind to accept a connection. + * @param session A preallocated ssh session + * @see ssh_new + * @return SSH_OK when a connection is established + */ +LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session); + +/** + * @brief Accept an incoming ssh connection on the given file descriptor + * and initialize the session. + * + * @param ssh_bind_o The ssh server bind to accept a connection. + * @param session A preallocated ssh session + * @param fd A file descriptor of an already established TCP + * inbound connection + * @see ssh_new + * @see ssh_bind_accept + * @return SSH_OK when a connection is established + */ +LIBSSH_API int ssh_bind_accept_fd(ssh_bind ssh_bind_o, ssh_session session, + socket_t fd); + +/** + * @brief Handles the key exchange and set up encryption + * + * @param session A connected ssh session + * @see ssh_bind_accept + * @return SSH_OK if the key exchange was successful + */ +LIBSSH_API int ssh_handle_key_exchange(ssh_session session); + +/** + * @brief Free a ssh servers bind. + * + * @param ssh_bind_o The ssh server bind to free. + */ +LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o); + +/********************************************************** + * SERVER MESSAGING + **********************************************************/ + +/** + * @brief Reply with a standard reject message. + * + * Use this function if you don't know what to respond or if you want to reject + * a request. + * + * @param[in] msg The message to use for the reply. + * + * @return 0 on success, -1 on error. + * + * @see ssh_message_get() + */ +LIBSSH_API int ssh_message_reply_default(ssh_message msg); + +/** + * @brief Get the name of the authenticated user. + * + * @param[in] msg The message to get the username from. + * + * @return The username or NULL if an error occured. + * + * @see ssh_message_get() + * @see ssh_message_type() + */ +LIBSSH_API const char *ssh_message_auth_user(ssh_message msg); + +/** + * @brief Get the password of the authenticated user. + * + * @param[in] msg The message to get the password from. + * + * @return The username or NULL if an error occured. + * + * @see ssh_message_get() + * @see ssh_message_type() + */ +LIBSSH_API const char *ssh_message_auth_password(ssh_message msg); + +/** + * @brief Get the publickey of the authenticated user. + * + * If you need the key for later user you should duplicate it. + * + * @param[in] msg The message to get the public key from. + * + * @return The public key or NULL. + * + * @see ssh_key_dup() + * @see ssh_key_cmp() + * @see ssh_message_get() + * @see ssh_message_type() + */ +LIBSSH_API ssh_key ssh_message_auth_pubkey(ssh_message msg); + +LIBSSH_API int ssh_message_auth_kbdint_is_response(ssh_message msg); +LIBSSH_API enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg); +LIBSSH_API int ssh_message_auth_reply_success(ssh_message msg,int partial); +LIBSSH_API int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey); +LIBSSH_API int ssh_message_auth_reply_pk_ok_simple(ssh_message msg); + +LIBSSH_API int ssh_message_auth_set_methods(ssh_message msg, int methods); + +LIBSSH_API int ssh_message_auth_interactive_request(ssh_message msg, + const char *name, const char *instruction, + unsigned int num_prompts, const char **prompts, char *echo); + +LIBSSH_API int ssh_message_service_reply_success(ssh_message msg); +LIBSSH_API const char *ssh_message_service_service(ssh_message msg); + +LIBSSH_API int ssh_message_global_request_reply_success(ssh_message msg, + uint16_t bound_port); + +LIBSSH_API void ssh_set_message_callback(ssh_session session, + int(*ssh_bind_message_callback)(ssh_session session, ssh_message msg, void *data), + void *data); +LIBSSH_API int ssh_execute_message_callbacks(ssh_session session); + +LIBSSH_API const char *ssh_message_channel_request_open_originator(ssh_message msg); +LIBSSH_API int ssh_message_channel_request_open_originator_port(ssh_message msg); +LIBSSH_API const char *ssh_message_channel_request_open_destination(ssh_message msg); +LIBSSH_API int ssh_message_channel_request_open_destination_port(ssh_message msg); + +LIBSSH_API ssh_channel ssh_message_channel_request_channel(ssh_message msg); + +LIBSSH_API const char *ssh_message_channel_request_pty_term(ssh_message msg); +LIBSSH_API int ssh_message_channel_request_pty_width(ssh_message msg); +LIBSSH_API int ssh_message_channel_request_pty_height(ssh_message msg); +LIBSSH_API int ssh_message_channel_request_pty_pxwidth(ssh_message msg); +LIBSSH_API int ssh_message_channel_request_pty_pxheight(ssh_message msg); + +LIBSSH_API const char *ssh_message_channel_request_env_name(ssh_message msg); +LIBSSH_API const char *ssh_message_channel_request_env_value(ssh_message msg); + +LIBSSH_API const char *ssh_message_channel_request_command(ssh_message msg); + +LIBSSH_API const char *ssh_message_channel_request_subsystem(ssh_message msg); + +LIBSSH_API int ssh_message_channel_request_x11_single_connection(ssh_message msg); +LIBSSH_API const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg); +LIBSSH_API const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg); +LIBSSH_API int ssh_message_channel_request_x11_screen_number(ssh_message msg); + +LIBSSH_API const char *ssh_message_global_request_address(ssh_message msg); +LIBSSH_API int ssh_message_global_request_port(ssh_message msg); + +LIBSSH_API int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost, + int remoteport, const char *sourcehost, int localport); +LIBSSH_API int ssh_channel_open_x11(ssh_channel channel, + const char *orig_addr, int orig_port); + +LIBSSH_API int ssh_channel_request_send_exit_status(ssh_channel channel, + int exit_status); +LIBSSH_API int ssh_channel_request_send_exit_signal(ssh_channel channel, + const char *signum, + int core, + const char *errmsg, + const char *lang); +LIBSSH_API int ssh_channel_write_stderr(ssh_channel channel, + const void *data, + uint32_t len); + +/* deprecated functions */ +SSH_DEPRECATED LIBSSH_API int ssh_accept(ssh_session session); +SSH_DEPRECATED LIBSSH_API int channel_write_stderr(ssh_channel channel, + const void *data, uint32_t len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SERVER_H */ + +/** @} */ diff --git a/libssh/include/libssh/session.h b/libssh/include/libssh/session.h new file mode 100644 index 00000000..6edf9e51 --- /dev/null +++ b/libssh/include/libssh/session.h @@ -0,0 +1,197 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SESSION_H_ +#define SESSION_H_ +#include "libssh/priv.h" +#include "libssh/kex.h" +#include "libssh/packet.h" +#include "libssh/pcap.h" +#include "libssh/auth.h" +#include "libssh/channels.h" +#include "libssh/poll.h" + +/* These are the different states a SSH session can be into its life */ +enum ssh_session_state_e { + SSH_SESSION_STATE_NONE=0, + SSH_SESSION_STATE_CONNECTING, + SSH_SESSION_STATE_SOCKET_CONNECTED, + SSH_SESSION_STATE_BANNER_RECEIVED, + SSH_SESSION_STATE_INITIAL_KEX, + SSH_SESSION_STATE_KEXINIT_RECEIVED, + SSH_SESSION_STATE_DH, + SSH_SESSION_STATE_AUTHENTICATING, + SSH_SESSION_STATE_AUTHENTICATED, + SSH_SESSION_STATE_ERROR, + SSH_SESSION_STATE_DISCONNECTED +}; + +enum ssh_dh_state_e { + DH_STATE_INIT=0, + DH_STATE_INIT_SENT, + DH_STATE_NEWKEYS_SENT, + DH_STATE_FINISHED +}; + +enum ssh_pending_call_e { + SSH_PENDING_CALL_NONE = 0, + SSH_PENDING_CALL_CONNECT, + SSH_PENDING_CALL_AUTH_NONE, + SSH_PENDING_CALL_AUTH_PASSWORD, + SSH_PENDING_CALL_AUTH_OFFER_PUBKEY, + SSH_PENDING_CALL_AUTH_PUBKEY, + SSH_PENDING_CALL_AUTH_AGENT, + SSH_PENDING_CALL_AUTH_KBDINT_INIT, + SSH_PENDING_CALL_AUTH_KBDINT_SEND +}; + +/* libssh calls may block an undefined amount of time */ +#define SSH_SESSION_FLAG_BLOCKING 1 + +/* Client successfully authenticated */ +#define SSH_SESSION_FLAG_AUTHENTICATED 2 + +/* codes to use with ssh_handle_packets*() */ +#define SSH_TIMEOUT_INFINITE -1 +#define SSH_TIMEOUT_USER -2 +#define SSH_TIMEOUT_NONBLOCKING 0 + +/* members that are common to ssh_session and ssh_bind */ +struct ssh_common_struct { + struct error_struct error; + ssh_callbacks callbacks; /* Callbacks to user functions */ + int log_verbosity; /* verbosity of the log functions */ + int log_indent; /* indentation level in enter_function logs */ +}; + +struct ssh_session_struct { + struct ssh_common_struct common; + struct ssh_socket_struct *socket; + char *serverbanner; + char *clientbanner; + int protoversion; + int server; + int client; + int openssh; + uint32_t send_seq; + uint32_t recv_seq; +/* status flags */ + int closed; + int closed_by_except; + + int connected; + /* !=0 when the user got a session handle */ + int alive; + /* two previous are deprecated */ + /* int auth_service_asked; */ + + /* session flags (SSH_SESSION_FLAG_*) */ + int flags; + + ssh_string banner; /* that's the issue banner from + the server */ + char *discon_msg; /* disconnect message from + the remote host */ + ssh_buffer in_buffer; + PACKET in_packet; + ssh_buffer out_buffer; + + /* the states are used by the nonblocking stuff to remember */ + /* where it was before being interrupted */ + enum ssh_pending_call_e pending_call_state; + enum ssh_session_state_e session_state; + int packet_state; + enum ssh_dh_state_e dh_handshake_state; + enum ssh_auth_service_state_e auth_service_state; + enum ssh_auth_state_e auth_state; + enum ssh_channel_request_state_e global_req_state; + struct ssh_agent_state_struct *agent_state; + struct ssh_auth_auto_state_struct *auth_auto_state; + + ssh_buffer in_hashbuf; + ssh_buffer out_hashbuf; + struct ssh_crypto_struct *current_crypto; + struct ssh_crypto_struct *next_crypto; /* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */ + + struct ssh_list *channels; /* linked list of channels */ + int maxchannel; + int exec_channel_opened; /* version 1 only. more + info in channels1.c */ + ssh_agent agent; /* ssh agent */ + +/* keyb interactive data */ + struct ssh_kbdint_struct *kbdint; + int version; /* 1 or 2 */ + /* server host keys */ + struct { + ssh_key rsa_key; + ssh_key dsa_key; + ssh_key ecdsa_key; + + /* The type of host key wanted by client */ + enum ssh_keytypes_e hostkey; + } srv; + /* auths accepted by server */ + int auth_methods; + struct ssh_list *ssh_message_list; /* list of delayed SSH messages */ + int (*ssh_message_callback)( struct ssh_session_struct *session, ssh_message msg, void *userdata); + void *ssh_message_callback_data; + + void (*ssh_connection_callback)( struct ssh_session_struct *session); + struct ssh_packet_callbacks_struct default_packet_callbacks; + struct ssh_list *packet_callbacks; + struct ssh_socket_callbacks_struct socket_callbacks; + ssh_poll_ctx default_poll_ctx; + /* options */ +#ifdef WITH_PCAP + ssh_pcap_context pcap_ctx; /* pcap debugging context */ +#endif + struct { + struct ssh_list *identity; + char *username; + char *host; + char *bindaddr; /* bind the client to an ip addr */ + char *sshdir; + char *knownhosts; + char *wanted_methods[10]; + char *ProxyCommand; + unsigned long timeout; /* seconds */ + unsigned long timeout_usec; + unsigned int port; + socket_t fd; + int StrictHostKeyChecking; + int ssh2; + int ssh1; + char compressionlevel; + } opts; +}; + +/** @internal + * @brief a termination function evaluates the status of an object + * @param user[in] object to evaluate + * @returns 1 if the polling routine should terminate, 0 instead + */ +typedef int (*ssh_termination_function)(void *user); +int ssh_handle_packets(ssh_session session, int timeout); +int ssh_handle_packets_termination(ssh_session session, int timeout, + ssh_termination_function fct, void *user); +void ssh_socket_exception_callback(int code, int errno_code, void *user); + +#endif /* SESSION_H_ */ diff --git a/libssh/include/libssh/sftp.h b/libssh/include/libssh/sftp.h new file mode 100644 index 00000000..462e04c5 --- /dev/null +++ b/libssh/include/libssh/sftp.h @@ -0,0 +1,979 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @defgroup libssh_sftp The libssh SFTP API + * + * @brief SFTP handling functions + * + * SFTP commands are channeled by the ssh sftp subsystem. Every packet is + * sent/read using a sftp_packet type structure. Related to these packets, + * most of the server answers are messages having an ID and a message + * specific part. It is described by sftp_message when reading a message, + * the sftp system puts it into the queue, so the process having asked for + * it can fetch it, while continuing to read for other messages (it is + * unspecified in which order messages may be sent back to the client + * + * @{ + */ + +#ifndef SFTP_H +#define SFTP_H + +#include + +#include "libssh.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#ifndef uid_t + typedef uint32_t uid_t; +#endif /* uid_t */ +#ifndef gid_t + typedef uint32_t gid_t; +#endif /* gid_t */ +#ifdef _MSC_VER +#ifndef ssize_t + typedef _W64 SSIZE_T ssize_t; +#endif /* ssize_t */ +#endif /* _MSC_VER */ +#endif /* _WIN32 */ + +#define LIBSFTP_VERSION 3 + +typedef struct sftp_attributes_struct* sftp_attributes; +typedef struct sftp_client_message_struct* sftp_client_message; +typedef struct sftp_dir_struct* sftp_dir; +typedef struct sftp_ext_struct *sftp_ext; +typedef struct sftp_file_struct* sftp_file; +typedef struct sftp_message_struct* sftp_message; +typedef struct sftp_packet_struct* sftp_packet; +typedef struct sftp_request_queue_struct* sftp_request_queue; +typedef struct sftp_session_struct* sftp_session; +typedef struct sftp_status_message_struct* sftp_status_message; +typedef struct sftp_statvfs_struct* sftp_statvfs_t; + +struct sftp_session_struct { + ssh_session session; + ssh_channel channel; + int server_version; + int client_version; + int version; + sftp_request_queue queue; + uint32_t id_counter; + int errnum; + void **handles; + sftp_ext ext; +}; + +struct sftp_packet_struct { + sftp_session sftp; + uint8_t type; + ssh_buffer payload; +}; + +/* file handler */ +struct sftp_file_struct { + sftp_session sftp; + char *name; + uint64_t offset; + ssh_string handle; + int eof; + int nonblocking; +}; + +struct sftp_dir_struct { + sftp_session sftp; + char *name; + ssh_string handle; /* handle to directory */ + ssh_buffer buffer; /* contains raw attributes from server which haven't been parsed */ + uint32_t count; /* counts the number of following attributes structures into buffer */ + int eof; /* end of directory listing */ +}; + +struct sftp_message_struct { + sftp_session sftp; + uint8_t packet_type; + ssh_buffer payload; + uint32_t id; +}; + +/* this is a bunch of all data that could be into a message */ +struct sftp_client_message_struct { + sftp_session sftp; + uint8_t type; + uint32_t id; + char *filename; /* can be "path" */ + uint32_t flags; + sftp_attributes attr; + ssh_string handle; + uint64_t offset; + uint32_t len; + int attr_num; + ssh_buffer attrbuf; /* used by sftp_reply_attrs */ + ssh_string data; /* can be newpath of rename() */ +}; + +struct sftp_request_queue_struct { + sftp_request_queue next; + sftp_message message; +}; + +/* SSH_FXP_MESSAGE described into .7 page 26 */ +struct sftp_status_message_struct { + uint32_t id; + uint32_t status; + ssh_string error; + ssh_string lang; + char *errormsg; + char *langmsg; +}; + +struct sftp_attributes_struct { + char *name; + char *longname; /* ls -l output on openssh, not reliable else */ + uint32_t flags; + uint8_t type; + uint64_t size; + uint32_t uid; + uint32_t gid; + char *owner; /* set if openssh and version 4 */ + char *group; /* set if openssh and version 4 */ + uint32_t permissions; + uint64_t atime64; + uint32_t atime; + uint32_t atime_nseconds; + uint64_t createtime; + uint32_t createtime_nseconds; + uint64_t mtime64; + uint32_t mtime; + uint32_t mtime_nseconds; + ssh_string acl; + uint32_t extended_count; + ssh_string extended_type; + ssh_string extended_data; +}; + +/** + * @brief SFTP statvfs structure. + */ +struct sftp_statvfs_struct { + uint64_t f_bsize; /** file system block size */ + uint64_t f_frsize; /** fundamental fs block size */ + uint64_t f_blocks; /** number of blocks (unit f_frsize) */ + uint64_t f_bfree; /** free blocks in file system */ + uint64_t f_bavail; /** free blocks for non-root */ + uint64_t f_files; /** total file inodes */ + uint64_t f_ffree; /** free file inodes */ + uint64_t f_favail; /** free file inodes for to non-root */ + uint64_t f_fsid; /** file system id */ + uint64_t f_flag; /** bit mask of f_flag values */ + uint64_t f_namemax; /** maximum filename length */ +}; + +/** + * @brief Start a new sftp session. + * + * @param session The ssh session to use. + * + * @return A new sftp session or NULL on error. + * + * @see sftp_free() + */ +LIBSSH_API sftp_session sftp_new(ssh_session session); + +/** + * @brief Close and deallocate a sftp session. + * + * @param sftp The sftp session handle to free. + */ +LIBSSH_API void sftp_free(sftp_session sftp); + +/** + * @brief Initialize the sftp session with the server. + * + * @param sftp The sftp session to initialize. + * + * @return 0 on success, < 0 on error with ssh error set. + * + * @see sftp_new() + */ +LIBSSH_API int sftp_init(sftp_session sftp); + +/** + * @brief Get the last sftp error. + * + * Use this function to get the latest error set by a posix like sftp function. + * + * @param sftp The sftp session where the error is saved. + * + * @return The saved error (see server responses), < 0 if an error + * in the function occured. + * + * @see Server responses + */ +LIBSSH_API int sftp_get_error(sftp_session sftp); + +/** + * @brief Get the count of extensions provided by the server. + * + * @param sftp The sftp session to use. + * + * @return The count of extensions provided by the server, 0 on error or + * not available. + */ +LIBSSH_API unsigned int sftp_extensions_get_count(sftp_session sftp); + +/** + * @brief Get the name of the extension provided by the server. + * + * @param sftp The sftp session to use. + * + * @param indexn The index number of the extension name you want. + * + * @return The name of the extension. + */ +LIBSSH_API const char *sftp_extensions_get_name(sftp_session sftp, unsigned int indexn); + +/** + * @brief Get the data of the extension provided by the server. + * + * This is normally the version number of the extension. + * + * @param sftp The sftp session to use. + * + * @param indexn The index number of the extension data you want. + * + * @return The data of the extension. + */ +LIBSSH_API const char *sftp_extensions_get_data(sftp_session sftp, unsigned int indexn); + +/** + * @brief Check if the given extension is supported. + * + * @param sftp The sftp session to use. + * + * @param name The name of the extension. + * + * @param data The data of the extension. + * + * @return 1 if supported, 0 if not. + * + * Example: + * + * @code + * sftp_extension_supported(sftp, "statvfs@openssh.com", "2"); + * @endcode + */ +LIBSSH_API int sftp_extension_supported(sftp_session sftp, const char *name, + const char *data); + +/** + * @brief Open a directory used to obtain directory entries. + * + * @param session The sftp session handle to open the directory. + * @param path The path of the directory to open. + * + * @return A sftp directory handle or NULL on error with ssh and + * sftp error set. + * + * @see sftp_readdir + * @see sftp_closedir + */ +LIBSSH_API sftp_dir sftp_opendir(sftp_session session, const char *path); + +/** + * @brief Get a single file attributes structure of a directory. + * + * @param session The sftp session handle to read the directory entry. + * @param dir The opened sftp directory handle to read from. + * + * @return A file attribute structure or NULL at the end of the + * directory. + * + * @see sftp_opendir() + * @see sftp_attribute_free() + * @see sftp_closedir() + */ +LIBSSH_API sftp_attributes sftp_readdir(sftp_session session, sftp_dir dir); + +/** + * @brief Tell if the directory has reached EOF (End Of File). + * + * @param dir The sftp directory handle. + * + * @return 1 if the directory is EOF, 0 if not. + * + * @see sftp_readdir() + */ +LIBSSH_API int sftp_dir_eof(sftp_dir dir); + +/** + * @brief Get information about a file or directory. + * + * @param session The sftp session handle. + * @param path The path to the file or directory to obtain the + * information. + * + * @return The sftp attributes structure of the file or directory, + * NULL on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API sftp_attributes sftp_stat(sftp_session session, const char *path); + +/** + * @brief Get information about a file or directory. + * + * Identical to sftp_stat, but if the file or directory is a symbolic link, + * then the link itself is stated, not the file that it refers to. + * + * @param session The sftp session handle. + * @param path The path to the file or directory to obtain the + * information. + * + * @return The sftp attributes structure of the file or directory, + * NULL on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API sftp_attributes sftp_lstat(sftp_session session, const char *path); + +/** + * @brief Get information about a file or directory from a file handle. + * + * @param file The sftp file handle to get the stat information. + * + * @return The sftp attributes structure of the file or directory, + * NULL on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API sftp_attributes sftp_fstat(sftp_file file); + +/** + * @brief Free a sftp attribute structure. + * + * @param file The sftp attribute structure to free. + */ +LIBSSH_API void sftp_attributes_free(sftp_attributes file); + +/** + * @brief Close a directory handle opened by sftp_opendir(). + * + * @param dir The sftp directory handle to close. + * + * @return Returns SSH_NO_ERROR or SSH_ERROR if an error occured. + */ +LIBSSH_API int sftp_closedir(sftp_dir dir); + +/** + * @brief Close an open file handle. + * + * @param file The open sftp file handle to close. + * + * @return Returns SSH_NO_ERROR or SSH_ERROR if an error occured. + * + * @see sftp_open() + */ +LIBSSH_API int sftp_close(sftp_file file); + +/** + * @brief Open a file on the server. + * + * @param session The sftp session handle. + * + * @param file The file to be opened. + * + * @param accesstype Is one of O_RDONLY, O_WRONLY or O_RDWR which request + * opening the file read-only,write-only or read/write. + * Acesss may also be bitwise-or'd with one or more of + * the following: + * O_CREAT - If the file does not exist it will be + * created. + * O_EXCL - When used with O_CREAT, if the file already + * exists it is an error and the open will fail. + * O_TRUNC - If the file already exists it will be + * truncated. + * + * @param mode Mode specifies the permissions to use if a new file is + * created. It is modified by the process's umask in + * the usual way: The permissions of the created file are + * (mode & ~umask) + * + * @return A sftp file handle, NULL on error with ssh and sftp + * error set. + * + * @see sftp_get_error() + */ +LIBSSH_API sftp_file sftp_open(sftp_session session, const char *file, int accesstype, + mode_t mode); + +/** + * @brief Make the sftp communication for this file handle non blocking. + * + * @param[in] handle The file handle to set non blocking. + */ +LIBSSH_API void sftp_file_set_nonblocking(sftp_file handle); + +/** + * @brief Make the sftp communication for this file handle blocking. + * + * @param[in] handle The file handle to set blocking. + */ +LIBSSH_API void sftp_file_set_blocking(sftp_file handle); + +/** + * @brief Read from a file using an opened sftp file handle. + * + * @param file The opened sftp file handle to be read from. + * + * @param buf Pointer to buffer to recieve read data. + * + * @param count Size of the buffer in bytes. + * + * @return Number of bytes written, < 0 on error with ssh and sftp + * error set. + * + * @see sftp_get_error() + */ +LIBSSH_API ssize_t sftp_read(sftp_file file, void *buf, size_t count); + +/** + * @brief Start an asynchronous read from a file using an opened sftp file handle. + * + * Its goal is to avoid the slowdowns related to the request/response pattern + * of a synchronous read. To do so, you must call 2 functions: + * + * sftp_async_read_begin() and sftp_async_read(). + * + * The first step is to call sftp_async_read_begin(). This function returns a + * request identifier. The second step is to call sftp_async_read() using the + * returned identifier. + * + * @param file The opened sftp file handle to be read from. + * + * @param len Size to read in bytes. + * + * @return An identifier corresponding to the sent request, < 0 on + * error. + * + * @warning When calling this function, the internal offset is + * updated corresponding to the len parameter. + * + * @warning A call to sftp_async_read_begin() sends a request to + * the server. When the server answers, libssh allocates + * memory to store it until sftp_async_read() is called. + * Not calling sftp_async_read() will lead to memory + * leaks. + * + * @see sftp_async_read() + * @see sftp_open() + */ +LIBSSH_API int sftp_async_read_begin(sftp_file file, uint32_t len); + +/** + * @brief Wait for an asynchronous read to complete and save the data. + * + * @param file The opened sftp file handle to be read from. + * + * @param data Pointer to buffer to recieve read data. + * + * @param len Size of the buffer in bytes. It should be bigger or + * equal to the length parameter of the + * sftp_async_read_begin() call. + * + * @param id The identifier returned by the sftp_async_read_begin() + * function. + * + * @return Number of bytes read, 0 on EOF, SSH_ERROR if an error + * occured, SSH_AGAIN if the file is opened in nonblocking + * mode and the request hasn't been executed yet. + * + * @warning A call to this function with an invalid identifier + * will never return. + * + * @see sftp_async_read_begin() + */ +LIBSSH_API int sftp_async_read(sftp_file file, void *data, uint32_t len, uint32_t id); + +/** + * @brief Write to a file using an opened sftp file handle. + * + * @param file Open sftp file handle to write to. + * + * @param buf Pointer to buffer to write data. + * + * @param count Size of buffer in bytes. + * + * @return Number of bytes written, < 0 on error with ssh and sftp + * error set. + * + * @see sftp_open() + * @see sftp_read() + * @see sftp_close() + */ +LIBSSH_API ssize_t sftp_write(sftp_file file, const void *buf, size_t count); + +/** + * @brief Seek to a specific location in a file. + * + * @param file Open sftp file handle to seek in. + * + * @param new_offset Offset in bytes to seek. + * + * @return 0 on success, < 0 on error. + */ +LIBSSH_API int sftp_seek(sftp_file file, uint32_t new_offset); + +/** + * @brief Seek to a specific location in a file. This is the + * 64bit version. + * + * @param file Open sftp file handle to seek in. + * + * @param new_offset Offset in bytes to seek. + * + * @return 0 on success, < 0 on error. + */ +LIBSSH_API int sftp_seek64(sftp_file file, uint64_t new_offset); + +/** + * @brief Report current byte position in file. + * + * @param file Open sftp file handle. + * + * @return The offset of the current byte relative to the beginning + * of the file associated with the file descriptor. < 0 on + * error. + */ +LIBSSH_API unsigned long sftp_tell(sftp_file file); + +/** + * @brief Report current byte position in file. + * + * @param file Open sftp file handle. + * + * @return The offset of the current byte relative to the beginning + * of the file associated with the file descriptor. < 0 on + * error. + */ +LIBSSH_API uint64_t sftp_tell64(sftp_file file); + +/** + * @brief Rewinds the position of the file pointer to the beginning of the + * file. + * + * @param file Open sftp file handle. + */ +LIBSSH_API void sftp_rewind(sftp_file file); + +/** + * @brief Unlink (delete) a file. + * + * @param sftp The sftp session handle. + * + * @param file The file to unlink/delete. + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_unlink(sftp_session sftp, const char *file); + +/** + * @brief Remove a directoy. + * + * @param sftp The sftp session handle. + * + * @param directory The directory to remove. + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_rmdir(sftp_session sftp, const char *directory); + +/** + * @brief Create a directory. + * + * @param sftp The sftp session handle. + * + * @param directory The directory to create. + * + * @param mode Specifies the permissions to use. It is modified by the + * process's umask in the usual way: + * The permissions of the created file are (mode & ~umask) + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode); + +/** + * @brief Rename or move a file or directory. + * + * @param sftp The sftp session handle. + * + * @param original The original url (source url) of file or directory to + * be moved. + * + * @param newname The new url (destination url) of the file or directory + * after the move. + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_rename(sftp_session sftp, const char *original, const char *newname); + +/** + * @brief Set file attributes on a file, directory or symbolic link. + * + * @param sftp The sftp session handle. + * + * @param file The file which attributes should be changed. + * + * @param attr The file attributes structure with the attributes set + * which should be changed. + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr); + +/** + * @brief Change the file owner and group + * + * @param sftp The sftp session handle. + * + * @param file The file which owner and group should be changed. + * + * @param owner The new owner which should be set. + * + * @param group The new group which should be set. + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_chown(sftp_session sftp, const char *file, uid_t owner, gid_t group); + +/** + * @brief Change permissions of a file + * + * @param sftp The sftp session handle. + * + * @param file The file which owner and group should be changed. + * + * @param mode Specifies the permissions to use. It is modified by the + * process's umask in the usual way: + * The permissions of the created file are (mode & ~umask) + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_chmod(sftp_session sftp, const char *file, mode_t mode); + +/** + * @brief Change the last modification and access time of a file. + * + * @param sftp The sftp session handle. + * + * @param file The file which owner and group should be changed. + * + * @param times A timeval structure which contains the desired access + * and modification time. + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_utimes(sftp_session sftp, const char *file, const struct timeval *times); + +/** + * @brief Create a symbolic link. + * + * @param sftp The sftp session handle. + * + * @param target Specifies the target of the symlink. + * + * @param dest Specifies the path name of the symlink to be created. + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int sftp_symlink(sftp_session sftp, const char *target, const char *dest); + +/** + * @brief Read the value of a symbolic link. + * + * @param sftp The sftp session handle. + * + * @param path Specifies the path name of the symlink to be read. + * + * @return The target of the link, NULL on error. + * + * @see sftp_get_error() + */ +LIBSSH_API char *sftp_readlink(sftp_session sftp, const char *path); + +/** + * @brief Get information about a mounted file system. + * + * @param sftp The sftp session handle. + * + * @param path The pathname of any file within the mounted file system. + * + * @return A statvfs structure or NULL on error. + * + * @see sftp_get_error() + */ +LIBSSH_API sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path); + +/** + * @brief Get information about a mounted file system. + * + * @param file An opened file. + * + * @return A statvfs structure or NULL on error. + * + * @see sftp_get_error() + */ +LIBSSH_API sftp_statvfs_t sftp_fstatvfs(sftp_file file); + +/** + * @brief Free the memory of an allocated statvfs. + * + * @param statvfs_o The statvfs to free. + */ +LIBSSH_API void sftp_statvfs_free(sftp_statvfs_t statvfs_o); + +/** + * @brief Canonicalize a sftp path. + * + * @param sftp The sftp session handle. + * + * @param path The path to be canonicalized. + * + * @return The canonicalize path, NULL on error. + */ +LIBSSH_API char *sftp_canonicalize_path(sftp_session sftp, const char *path); + +/** + * @brief Get the version of the SFTP protocol supported by the server + * + * @param sftp The sftp session handle. + * + * @return The server version. + */ +LIBSSH_API int sftp_server_version(sftp_session sftp); + +#ifdef WITH_SERVER +/** + * @brief Create a new sftp server session. + * + * @param session The ssh session to use. + * + * @param chan The ssh channel to use. + * + * @return A new sftp server session. + */ +LIBSSH_API sftp_session sftp_server_new(ssh_session session, ssh_channel chan); + +/** + * @brief Intialize the sftp server. + * + * @param sftp The sftp session to init. + * + * @return 0 on success, < 0 on error. + */ +LIBSSH_API int sftp_server_init(sftp_session sftp); +#endif /* WITH_SERVER */ + +/* this is not a public interface */ +#define SFTP_HANDLES 256 +sftp_packet sftp_packet_read(sftp_session sftp); +int sftp_packet_write(sftp_session sftp,uint8_t type, ssh_buffer payload); +void sftp_packet_free(sftp_packet packet); +int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr); +sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf,int expectname); +/* sftpserver.c */ + +sftp_client_message sftp_get_client_message(sftp_session sftp); +void sftp_client_message_free(sftp_client_message msg); +int sftp_reply_name(sftp_client_message msg, const char *name, + sftp_attributes attr); +int sftp_reply_handle(sftp_client_message msg, ssh_string handle); +ssh_string sftp_handle_alloc(sftp_session sftp, void *info); +int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr); +void *sftp_handle(sftp_session sftp, ssh_string handle); +int sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message); +int sftp_reply_names_add(sftp_client_message msg, const char *file, + const char *longname, sftp_attributes attr); +int sftp_reply_names(sftp_client_message msg); +int sftp_reply_data(sftp_client_message msg, const void *data, int len); +void sftp_handle_remove(sftp_session sftp, void *handle); + +/* SFTP commands and constants */ +#define SSH_FXP_INIT 1 +#define SSH_FXP_VERSION 2 +#define SSH_FXP_OPEN 3 +#define SSH_FXP_CLOSE 4 +#define SSH_FXP_READ 5 +#define SSH_FXP_WRITE 6 +#define SSH_FXP_LSTAT 7 +#define SSH_FXP_FSTAT 8 +#define SSH_FXP_SETSTAT 9 +#define SSH_FXP_FSETSTAT 10 +#define SSH_FXP_OPENDIR 11 +#define SSH_FXP_READDIR 12 +#define SSH_FXP_REMOVE 13 +#define SSH_FXP_MKDIR 14 +#define SSH_FXP_RMDIR 15 +#define SSH_FXP_REALPATH 16 +#define SSH_FXP_STAT 17 +#define SSH_FXP_RENAME 18 +#define SSH_FXP_READLINK 19 +#define SSH_FXP_SYMLINK 20 + +#define SSH_FXP_STATUS 101 +#define SSH_FXP_HANDLE 102 +#define SSH_FXP_DATA 103 +#define SSH_FXP_NAME 104 +#define SSH_FXP_ATTRS 105 + +#define SSH_FXP_EXTENDED 200 +#define SSH_FXP_EXTENDED_REPLY 201 + +/* attributes */ +/* sftp draft is completely braindead : version 3 and 4 have different flags for same constants */ +/* and even worst, version 4 has same flag for 2 different constants */ +/* follow up : i won't develop any sftp4 compliant library before having a clarification */ + +#define SSH_FILEXFER_ATTR_SIZE 0x00000001 +#define SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004 +#define SSH_FILEXFER_ATTR_ACCESSTIME 0x00000008 +#define SSH_FILEXFER_ATTR_ACMODTIME 0x00000008 +#define SSH_FILEXFER_ATTR_CREATETIME 0x00000010 +#define SSH_FILEXFER_ATTR_MODIFYTIME 0x00000020 +#define SSH_FILEXFER_ATTR_ACL 0x00000040 +#define SSH_FILEXFER_ATTR_OWNERGROUP 0x00000080 +#define SSH_FILEXFER_ATTR_SUBSECOND_TIMES 0x00000100 +#define SSH_FILEXFER_ATTR_EXTENDED 0x80000000 +#define SSH_FILEXFER_ATTR_UIDGID 0x00000002 + +/* types */ +#define SSH_FILEXFER_TYPE_REGULAR 1 +#define SSH_FILEXFER_TYPE_DIRECTORY 2 +#define SSH_FILEXFER_TYPE_SYMLINK 3 +#define SSH_FILEXFER_TYPE_SPECIAL 4 +#define SSH_FILEXFER_TYPE_UNKNOWN 5 + +/** + * @name Server responses + * + * @brief Responses returned by the sftp server. + * @{ + */ + +/** No error */ +#define SSH_FX_OK 0 +/** End-of-file encountered */ +#define SSH_FX_EOF 1 +/** File doesn't exist */ +#define SSH_FX_NO_SUCH_FILE 2 +/** Permission denied */ +#define SSH_FX_PERMISSION_DENIED 3 +/** Generic failure */ +#define SSH_FX_FAILURE 4 +/** Garbage received from server */ +#define SSH_FX_BAD_MESSAGE 5 +/** No connection has been set up */ +#define SSH_FX_NO_CONNECTION 6 +/** There was a connection, but we lost it */ +#define SSH_FX_CONNECTION_LOST 7 +/** Operation not supported by the server */ +#define SSH_FX_OP_UNSUPPORTED 8 +/** Invalid file handle */ +#define SSH_FX_INVALID_HANDLE 9 +/** No such file or directory path exists */ +#define SSH_FX_NO_SUCH_PATH 10 +/** An attempt to create an already existing file or directory has been made */ +#define SSH_FX_FILE_ALREADY_EXISTS 11 +/** We are trying to write on a write-protected filesystem */ +#define SSH_FX_WRITE_PROTECT 12 +/** No media in remote drive */ +#define SSH_FX_NO_MEDIA 13 + +/** @} */ + +/* file flags */ +#define SSH_FXF_READ 0x01 +#define SSH_FXF_WRITE 0x02 +#define SSH_FXF_APPEND 0x04 +#define SSH_FXF_CREAT 0x08 +#define SSH_FXF_TRUNC 0x10 +#define SSH_FXF_EXCL 0x20 +#define SSH_FXF_TEXT 0x40 + +/* rename flags */ +#define SSH_FXF_RENAME_OVERWRITE 0x00000001 +#define SSH_FXF_RENAME_ATOMIC 0x00000002 +#define SSH_FXF_RENAME_NATIVE 0x00000004 + +#define SFTP_OPEN SSH_FXP_OPEN +#define SFTP_CLOSE SSH_FXP_CLOSE +#define SFTP_READ SSH_FXP_READ +#define SFTP_WRITE SSH_FXP_WRITE +#define SFTP_LSTAT SSH_FXP_LSTAT +#define SFTP_FSTAT SSH_FXP_FSTAT +#define SFTP_SETSTAT SSH_FXP_SETSTAT +#define SFTP_FSETSTAT SSH_FXP_FSETSTAT +#define SFTP_OPENDIR SSH_FXP_OPENDIR +#define SFTP_READDIR SSH_FXP_READDIR +#define SFTP_REMOVE SSH_FXP_REMOVE +#define SFTP_MKDIR SSH_FXP_MKDIR +#define SFTP_RMDIR SSH_FXP_RMDIR +#define SFTP_REALPATH SSH_FXP_REALPATH +#define SFTP_STAT SSH_FXP_STAT +#define SFTP_RENAME SSH_FXP_RENAME +#define SFTP_READLINK SSH_FXP_READLINK +#define SFTP_SYMLINK SSH_FXP_SYMLINK + +/* openssh flags */ +#define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */ +#define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */ + +#ifdef __cplusplus +} ; +#endif + +#endif /* SFTP_H */ + +/** @} */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/include/libssh/socket.h b/libssh/include/libssh/socket.h new file mode 100644 index 00000000..e25bb0c9 --- /dev/null +++ b/libssh/include/libssh/socket.h @@ -0,0 +1,68 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SOCKET_H_ +#define SOCKET_H_ + +#include "libssh/callbacks.h" +struct ssh_poll_handle_struct; +/* socket.c */ + +struct ssh_socket_struct; +typedef struct ssh_socket_struct* ssh_socket; + +int ssh_socket_init(void); +void ssh_socket_cleanup(void); +ssh_socket ssh_socket_new(ssh_session session); +void ssh_socket_reset(ssh_socket s); +void ssh_socket_free(ssh_socket s); +void ssh_socket_set_fd(ssh_socket s, socket_t fd); +socket_t ssh_socket_get_fd_in(ssh_socket s); +#ifndef _WIN32 +int ssh_socket_unix(ssh_socket s, const char *path); +void ssh_execute_command(const char *command, socket_t in, socket_t out); +int ssh_socket_connect_proxycommand(ssh_socket s, const char *command); +#endif +void ssh_socket_close(ssh_socket s); +int ssh_socket_write(ssh_socket s,const void *buffer, int len); +int ssh_socket_is_open(ssh_socket s); +int ssh_socket_fd_isset(ssh_socket s, fd_set *set); +void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd); +void ssh_socket_set_fd_in(ssh_socket s, socket_t fd); +void ssh_socket_set_fd_out(ssh_socket s, socket_t fd); +int ssh_socket_nonblocking_flush(ssh_socket s); +void ssh_socket_set_write_wontblock(ssh_socket s); +void ssh_socket_set_read_wontblock(ssh_socket s); +void ssh_socket_set_except(ssh_socket s); +int ssh_socket_get_status(ssh_socket s); +int ssh_socket_buffered_write_bytes(ssh_socket s); +int ssh_socket_data_available(ssh_socket s); +int ssh_socket_data_writable(ssh_socket s); +void ssh_socket_set_nonblocking(socket_t fd); +void ssh_socket_set_blocking(socket_t fd); + +void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks); +int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s); +struct ssh_poll_handle_struct * ssh_socket_get_poll_handle_in(ssh_socket s); +struct ssh_poll_handle_struct * ssh_socket_get_poll_handle_out(ssh_socket s); + +int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr); + +#endif /* SOCKET_H_ */ diff --git a/libssh/include/libssh/ssh1.h b/libssh/include/libssh/ssh1.h new file mode 100644 index 00000000..ce67f20b --- /dev/null +++ b/libssh/include/libssh/ssh1.h @@ -0,0 +1,82 @@ +#ifndef __SSH1_H +#define __SSH1_H + +#define SSH_MSG_NONE 0 /* no message */ +#define SSH_MSG_DISCONNECT 1 /* cause (string) */ +#define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ +#define SSH_CMSG_SESSION_KEY 3 /* key (BIGNUM) */ +#define SSH_CMSG_USER 4 /* user (string) */ +#define SSH_CMSG_AUTH_RHOSTS 5 /* user (string) */ +#define SSH_CMSG_AUTH_RSA 6 /* modulus (BIGNUM) */ +#define SSH_SMSG_AUTH_RSA_CHALLENGE 7 /* int (BIGNUM) */ +#define SSH_CMSG_AUTH_RSA_RESPONSE 8 /* int (BIGNUM) */ +#define SSH_CMSG_AUTH_PASSWORD 9 /* pass (string) */ +#define SSH_CMSG_REQUEST_PTY 10 /* TERM, tty modes */ +#define SSH_CMSG_WINDOW_SIZE 11 /* row,col,xpix,ypix */ +#define SSH_CMSG_EXEC_SHELL 12 /* */ +#define SSH_CMSG_EXEC_CMD 13 /* cmd (string) */ +#define SSH_SMSG_SUCCESS 14 /* */ +#define SSH_SMSG_FAILURE 15 /* */ +#define SSH_CMSG_STDIN_DATA 16 /* data (string) */ +#define SSH_SMSG_STDOUT_DATA 17 /* data (string) */ +#define SSH_SMSG_STDERR_DATA 18 /* data (string) */ +#define SSH_CMSG_EOF 19 /* */ +#define SSH_SMSG_EXITSTATUS 20 /* status (int) */ +#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* channel (int) */ +#define SSH_MSG_CHANNEL_OPEN_FAILURE 22 /* channel (int) */ +#define SSH_MSG_CHANNEL_DATA 23 /* ch,data (int,str) */ +#define SSH_MSG_CHANNEL_CLOSE 24 /* channel (int) */ +#define SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* channel (int) */ +/* SSH_CMSG_X11_REQUEST_FORWARDING 26 OBSOLETE */ +#define SSH_SMSG_X11_OPEN 27 /* channel (int) */ +#define SSH_CMSG_PORT_FORWARD_REQUEST 28 /* p,host,hp (i,s,i) */ +#define SSH_MSG_PORT_OPEN 29 /* ch,h,p (i,s,i) */ +#define SSH_CMSG_AGENT_REQUEST_FORWARDING 30 /* */ +#define SSH_SMSG_AGENT_OPEN 31 /* port (int) */ +#define SSH_MSG_IGNORE 32 /* string */ +#define SSH_CMSG_EXIT_CONFIRMATION 33 /* */ +#define SSH_CMSG_X11_REQUEST_FORWARDING 34 /* proto,data (s,s) */ +#define SSH_CMSG_AUTH_RHOSTS_RSA 35 /* user,mod (s,mpi) */ +#define SSH_MSG_DEBUG 36 /* string */ +#define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */ +#define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k (int) */ +#define SSH_CMSG_AUTH_TIS 39 /* we use this for s/key */ +#define SSH_SMSG_AUTH_TIS_CHALLENGE 40 /* challenge (string) */ +#define SSH_CMSG_AUTH_TIS_RESPONSE 41 /* response (string) */ +#define SSH_CMSG_AUTH_KERBEROS 42 /* (KTEXT) */ +#define SSH_SMSG_AUTH_KERBEROS_RESPONSE 43 /* (KTEXT) */ +#define SSH_CMSG_HAVE_KERBEROS_TGT 44 /* credentials (s) */ +#define SSH_CMSG_HAVE_AFS_TOKEN 65 /* token (s) */ + +/* protocol version 1.5 overloads some version 1.3 message types */ +#define SSH_MSG_CHANNEL_INPUT_EOF SSH_MSG_CHANNEL_CLOSE +#define SSH_MSG_CHANNEL_OUTPUT_CLOSE SSH_MSG_CHANNEL_CLOSE_CONFIRMATION + +/* + * Authentication methods. New types can be added, but old types should not + * be removed for compatibility. The maximum allowed value is 31. + */ +#define SSH_AUTH_RHOSTS 1 +#define SSH_AUTH_RSA 2 +#define SSH_AUTH_PASSWORD 3 +#define SSH_AUTH_RHOSTS_RSA 4 +#define SSH_AUTH_TIS 5 +#define SSH_AUTH_KERBEROS 6 +#define SSH_PASS_KERBEROS_TGT 7 + /* 8 to 15 are reserved */ +#define SSH_PASS_AFS_TOKEN 21 + +/* Protocol flags. These are bit masks. */ +#define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */ +#define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */ + +/* cipher flags. they are bit numbers */ +#define SSH_CIPHER_NONE 0 /* No encryption */ +#define SSH_CIPHER_IDEA 1 /* IDEA in CFB mode */ +#define SSH_CIPHER_DES 2 /* DES in CBC mode */ +#define SSH_CIPHER_3DES 3 /* Triple-DES in CBC mode */ +#define SSH_CIPHER_RC4 5 /* RC4 */ +#define SSH_CIPHER_BLOWFISH 6 + +#endif + diff --git a/libssh/include/libssh/ssh2.h b/libssh/include/libssh/ssh2.h new file mode 100644 index 00000000..f66dd2a9 --- /dev/null +++ b/libssh/include/libssh/ssh2.h @@ -0,0 +1,73 @@ +#ifndef __SSH2_H +#define __SSH2_H + +#define SSH2_MSG_DISCONNECT 1 +#define SSH2_MSG_IGNORE 2 +#define SSH2_MSG_UNIMPLEMENTED 3 +#define SSH2_MSG_DEBUG 4 +#define SSH2_MSG_SERVICE_REQUEST 5 +#define SSH2_MSG_SERVICE_ACCEPT 6 + +#define SSH2_MSG_KEXINIT 20 +#define SSH2_MSG_NEWKEYS 21 + +#define SSH2_MSG_KEXDH_INIT 30 +#define SSH2_MSG_KEXDH_REPLY 31 +#define SSH2_MSG_KEX_ECDH_INIT 30 +#define SSH2_MSG_KEX_ECDH_REPLY 31 +#define SSH2_MSG_ECMQV_INIT 30 +#define SSH2_MSG_ECMQV_REPLY 31 + +#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 +#define SSH2_MSG_KEX_DH_GEX_GROUP 31 +#define SSH2_MSG_KEX_DH_GEX_INIT 32 +#define SSH2_MSG_KEX_DH_GEX_REPLY 33 +#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 +#define SSH2_MSG_USERAUTH_REQUEST 50 +#define SSH2_MSG_USERAUTH_FAILURE 51 +#define SSH2_MSG_USERAUTH_SUCCESS 52 +#define SSH2_MSG_USERAUTH_BANNER 53 +#define SSH2_MSG_USERAUTH_PK_OK 60 +#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 +#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 +#define SSH2_MSG_GLOBAL_REQUEST 80 +#define SSH2_MSG_REQUEST_SUCCESS 81 +#define SSH2_MSG_REQUEST_FAILURE 82 +#define SSH2_MSG_CHANNEL_OPEN 90 +#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH2_MSG_CHANNEL_DATA 94 +#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH2_MSG_CHANNEL_EOF 96 +#define SSH2_MSG_CHANNEL_CLOSE 97 +#define SSH2_MSG_CHANNEL_REQUEST 98 +#define SSH2_MSG_CHANNEL_SUCCESS 99 +#define SSH2_MSG_CHANNEL_FAILURE 100 + +#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 +#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 +#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 +#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 +#define SSH2_DISCONNECT_RESERVED 4 +#define SSH2_DISCONNECT_MAC_ERROR 5 +#define SSH2_DISCONNECT_COMPRESSION_ERROR 6 +#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 +#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 +#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 +#define SSH2_DISCONNECT_CONNECTION_LOST 10 +#define SSH2_DISCONNECT_BY_APPLICATION 11 +#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 +#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 +#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 +#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 + +#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 +#define SSH2_OPEN_CONNECT_FAILED 2 +#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 +#define SSH2_OPEN_RESOURCE_SHORTAGE 4 + +#define SSH2_EXTENDED_DATA_STDERR 1 + +#endif diff --git a/libssh/include/libssh/string.h b/libssh/include/libssh/string.h new file mode 100644 index 00000000..8c7db1df --- /dev/null +++ b/libssh/include/libssh/string.h @@ -0,0 +1,41 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef STRING_H_ +#define STRING_H_ +#include "libssh/priv.h" + +/* must be 32 bits number + immediately our data */ +#ifdef _MSC_VER +#pragma pack(1) +#endif +struct ssh_string_struct { + uint32_t size; + unsigned char data[1]; +} +#if defined(__GNUC__) +__attribute__ ((packed)) +#endif +#ifdef _MSC_VER +#pragma pack() +#endif +; + +#endif /* STRING_H_ */ diff --git a/libssh/include/libssh/threads.h b/libssh/include/libssh/threads.h new file mode 100644 index 00000000..70072648 --- /dev/null +++ b/libssh/include/libssh/threads.h @@ -0,0 +1,31 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef THREADS_H_ +#define THREADS_H_ + +#include +#include + +int ssh_threads_init(void); +void ssh_threads_finalize(void); +const char *ssh_threads_get_type(void); + +#endif /* THREADS_H_ */ diff --git a/libssh/include/libssh/wrapper.h b/libssh/include/libssh/wrapper.h new file mode 100644 index 00000000..90c268d9 --- /dev/null +++ b/libssh/include/libssh/wrapper.h @@ -0,0 +1,71 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef WRAPPER_H_ +#define WRAPPER_H_ + +#include "config.h" +#include "libssh/libcrypto.h" +#include "libssh/libgcrypt.h" + +enum ssh_mac_e { + SSH_MAC_SHA1=1, + SSH_MAC_SHA256, + SSH_MAC_SHA384, + SSH_MAC_SHA512 +}; + +enum ssh_hmac_e { + SSH_HMAC_SHA1 = 1, + SSH_HMAC_MD5 +}; + +enum ssh_des_e { + SSH_3DES, + SSH_DES +}; + +typedef struct ssh_mac_ctx_struct *ssh_mac_ctx; +MD5CTX md5_init(void); +void md5_update(MD5CTX c, const void *data, unsigned long len); +void md5_final(unsigned char *md,MD5CTX c); +SHACTX sha1_init(void); +void sha1_update(SHACTX c, const void *data, unsigned long len); +void sha1_final(unsigned char *md,SHACTX c); +void sha1(unsigned char *digest,int len,unsigned char *hash); +void sha256(unsigned char *digest, int len, unsigned char *hash); + +void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen); + +ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type); +void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len); +void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx); + +HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type); +void hmac_update(HMACCTX c, const void *data, unsigned long len); +void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len); + +int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type); +int crypt_set_algorithms_server(ssh_session session); +struct ssh_crypto_struct *crypto_new(void); +void crypto_free(struct ssh_crypto_struct *crypto); + + +#endif /* WRAPPER_H_ */ diff --git a/libssh/libssh-build-tree-settings.cmake.in b/libssh/libssh-build-tree-settings.cmake.in new file mode 100644 index 00000000..16b406aa --- /dev/null +++ b/libssh/libssh-build-tree-settings.cmake.in @@ -0,0 +1 @@ +set(LIBSSH_INLUDE_DIR @PROJECT_SOURCE_DIR@/include) diff --git a/libssh/libssh-config-version.cmake.in b/libssh/libssh-config-version.cmake.in new file mode 100644 index 00000000..98f292c0 --- /dev/null +++ b/libssh/libssh-config-version.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION @APPLICATION_VERSION@) + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/libssh/libssh-config.cmake.in b/libssh/libssh-config.cmake.in new file mode 100644 index 00000000..1e287fae --- /dev/null +++ b/libssh/libssh-config.cmake.in @@ -0,0 +1,11 @@ +get_filename_component(LIBSSH_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +if (EXISTS "${LIBSSH_CMAKE_DIR}/CMakeCache.txt") + # In build tree + include(${LIBSSH_CMAKE_DIR}/libssh-build-tree-settings.cmake) +else() + set(LIBSSH_INCLUDE_DIR @INCLUDE_INSTALL_DIR@) +endif() + +set(LIBSSH_LIRBARY @LIB_INSTALL_DIR@/libssh.so) +set(LIBSSH_LIRBARIES @LIB_INSTALL_DIR@/libssh.so) diff --git a/libssh/libssh.pc.cmake b/libssh/libssh.pc.cmake new file mode 100644 index 00000000..3b1cb8dc --- /dev/null +++ b/libssh/libssh.pc.cmake @@ -0,0 +1,6 @@ +Name: ${APPLICATION_NAME} +Description: The SSH Library +Version: ${APPLICATION_VERSION} +Libs: -L${LIB_INSTALL_DIR} -lssh +Cflags: -I${INCLUDE_INSTALL_DIR} + diff --git a/libssh/libssh_threads.pc.cmake b/libssh/libssh_threads.pc.cmake new file mode 100644 index 00000000..5479c34f --- /dev/null +++ b/libssh/libssh_threads.pc.cmake @@ -0,0 +1,6 @@ +Name: ${APPLICATION_NAME}_threads +Description: The SSH Library Thread Extension +Version: ${APPLICATION_VERSION} +Libs: -L${LIB_INSTALL_DIR} -lssh_threads +Cflags: -I${INCLUDE_INSTALL_DIR} + diff --git a/libssh/src/CMakeLists.txt b/libssh/src/CMakeLists.txt new file mode 100644 index 00000000..1171c90d --- /dev/null +++ b/libssh/src/CMakeLists.txt @@ -0,0 +1,254 @@ +project(libssh-library C) + +set(LIBSSH_PUBLIC_INCLUDE_DIRS + ${CMAKE_SOURCE_DIR}/include + CACHE INTERNAL "libssh public include directories" +) + +set(LIBSSH_PRIVATE_INCLUDE_DIRS + ${CMAKE_BINARY_DIR} + ${OPENSSL_INCLUDE_DIRS} +) + +set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_REQUIRED_LIBRARIES} +) + +if (WIN32) + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + ws2_32 + ) +endif (WIN32) + +if (HAVE_LIBSOCKET) + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + socket + ) +endif (HAVE_LIBSOCKET) + +if (OPENSSL_LIBRARIES) + set(LIBSSH_PRIVATE_INCLUDE_DIRS + ${LIBSSH_PRIVATE_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIRS} + ) + + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + ${OPENSSL_LIBRARIES} + ) +endif (OPENSSL_LIBRARIES) + +if (GCRYPT_LIBRARY) + set(LIBSSH_PRIVATE_INCLUDE_DIRS + ${LIBSSH_PRIVATE_INCLUDE_DIRS} + ${GCRYPT_INCLUDE_DIR} + ) + + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + ${GCRYPT_LIBRARY} + ) +endif (GCRYPT_LIBRARY) + +if (WITH_ZLIB) + set(LIBSSH_PRIVATE_INCLUDE_DIRS + ${LIBSSH_PRIVATE_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} + ) + + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + ${ZLIB_LIBRARY} + ) +endif (WITH_ZLIB) + +set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + CACHE INTERNAL "libssh link libraries" +) + +set(LIBSSH_SHARED_LIBRARY + ssh_shared + CACHE INTERNAL "libssh shared library" +) + +if (WITH_STATIC_LIB) + set(LIBSSH_STATIC_LIBRARY + ssh_static + CACHE INTERNAL "libssh static library" + ) +endif (WITH_STATIC_LIB) + +set(libssh_SRCS + agent.c + auth.c + base64.c + buffer.c + callbacks.c + channels.c + client.c + config.c + connect.c + dh.c + ecdh.c + error.c + getpass.c + init.c + kex.c + known_hosts.c + legacy.c + libcrypto.c + log.c + match.c + messages.c + misc.c + options.c + packet.c + packet_cb.c + packet_crypt.c + pcap.c + pki.c + poll.c + session.c + scp.c + socket.c + string.c + threads.c + wrapper.c +) + +if (WITH_GCRYPT) + set(libssh_SRCS + ${libssh_SRCS} + libgcrypt.c + gcrypt_missing.c + pki_gcrypt.c + ) +else (WITH_GCRYPT) + set(libssh_SRCS + ${libssh_SRCS} + pki_crypto.c + ) +endif (WITH_GCRYPT) + +if (WITH_SFTP) + set(libssh_SRCS + ${libssh_SRCS} + sftp.c + ) + + if (WITH_SERVER) + set(libssh_SRCS + ${libssh_SRCS} + sftpserver.c + ) + endif (WITH_SERVER) +endif (WITH_SFTP) + +if (WITH_SSH1) + set(libssh_SRCS + ${libssh_SRCS} + auth1.c + channels1.c + crc32.c + kex1.c + packet1.c + ) +endif (WITH_SSH1) + +if (WITH_SERVER) + set(libssh_SRCS + ${libssh_SRCS} + server.c + bind.c + ) +endif (WITH_SERVER) + +if (WITH_ZLIB) + set(libssh_SRCS + ${libssh_SRCS} + gzip.c + ) +endif(WITH_ZLIB) + +include_directories( + ${LIBSSH_PUBLIC_INCLUDE_DIRS} + ${LIBSSH_PRIVATE_INCLUDE_DIRS} +) + +add_library(${LIBSSH_SHARED_LIBRARY} SHARED ${libssh_SRCS}) + +target_link_libraries(${LIBSSH_SHARED_LIBRARY} ${LIBSSH_LINK_LIBRARIES}) + +set_target_properties( + ${LIBSSH_SHARED_LIBRARY} + PROPERTIES + VERSION + ${LIBRARY_VERSION} + SOVERSION + ${LIBRARY_SOVERSION} + OUTPUT_NAME + ssh + DEFINE_SYMBOL + LIBSSH_EXPORTS +) + +if (WITH_VISIBILITY_HIDDEN) + set_target_properties(${LIBSSH_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") +endif (WITH_VISIBILITY_HIDDEN) + + +install( + TARGETS + ${LIBSSH_SHARED_LIBRARY} + RUNTIME DESTINATION ${BIN_INSTALL_DIR} + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR} + COMPONENT libraries +) + +if (WITH_STATIC_LIB) + add_library(${LIBSSH_STATIC_LIBRARY} STATIC ${libssh_SRCS}) + + if (MSVC) + set(OUTPUT_SUFFIX static) + else (MSVC) + set(OUTPUT_SUFFIX ) + endif (MSVC) + set_target_properties( + ${LIBSSH_STATIC_LIBRARY} + PROPERTIES + VERSION + ${LIBRARY_VERSION} + SOVERSION + ${LIBRARY_SOVERSION} + OUTPUT_NAME + ssh + ARCHIVE_OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX} + ) + + if (WIN32) + set_target_properties( + ${LIBSSH_STATIC_LIBRARY} + PROPERTIES + COMPILE_FLAGS + "-DLIBSSH_STATIC" + ) + endif (WIN32) + + install( + TARGETS + ${LIBSSH_STATIC_LIBRARY} + DESTINATION + ${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX} + COMPONENT + libraries + ) +endif (WITH_STATIC_LIB) + +if (CMAKE_HAVE_THREADS_LIBRARY) + add_subdirectory(threads) +endif (CMAKE_HAVE_THREADS_LIBRARY) diff --git a/libssh/src/agent.c b/libssh/src/agent.c new file mode 100644 index 00000000..95cf6a12 --- /dev/null +++ b/libssh/src/agent.c @@ -0,0 +1,516 @@ +/* + * agent.c - ssh agent functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2008-2009 by Andreas Schneider + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* This file is based on authfd.c from OpenSSH */ + +/* + * How does the ssh-agent work? + * + * a) client sends a request to get a list of all keys + * the agent returns the count and all public keys + * b) iterate over them to check if the server likes one + * c) the client sends a sign request to the agent + * type, pubkey as blob, data to sign, flags + * the agent returns the signed data + */ + +#ifndef _WIN32 + +#include "config.h" + +#include +#include +#include +#include + +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/agent.h" +#include "libssh/priv.h" +#include "libssh/socket.h" +#include "libssh/buffer.h" +#include "libssh/session.h" +#include "libssh/poll.h" +#include "libssh/pki.h" + +/* macro to check for "agent failure" message */ +#define agent_failed(x) \ + (((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \ + ((x) == SSH2_AGENT_FAILURE)) + +static uint32_t agent_get_u32(const void *vp) { + const uint8_t *p = (const uint8_t *)vp; + uint32_t v; + + v = (uint32_t)p[0] << 24; + v |= (uint32_t)p[1] << 16; + v |= (uint32_t)p[2] << 8; + v |= (uint32_t)p[3]; + + return v; +} + +static void agent_put_u32(void *vp, uint32_t v) { + uint8_t *p = (uint8_t *)vp; + + p[0] = (uint8_t)(v >> 24) & 0xff; + p[1] = (uint8_t)(v >> 16) & 0xff; + p[2] = (uint8_t)(v >> 8) & 0xff; + p[3] = (uint8_t)v & 0xff; +} + +static size_t atomicio(ssh_socket s, void *buf, size_t n, int do_read) { + char *b = buf; + size_t pos = 0; + ssize_t res; + ssh_pollfd_t pfd; + socket_t fd = ssh_socket_get_fd_in(s); + + pfd.fd = fd; + pfd.events = do_read ? POLLIN : POLLOUT; + + while (n > pos) { + if (do_read) { + res = read(fd, b + pos, n - pos); + } else { + res = write(fd, b + pos, n - pos); + } + switch (res) { + case -1: + if (errno == EINTR) { + continue; + } +#ifdef EWOULDBLOCK + if (errno == EAGAIN || errno == EWOULDBLOCK) { +#else + if (errno == EAGAIN) { +#endif + (void) ssh_poll(&pfd, 1, -1); + continue; + } + return 0; + case 0: + /* read returns 0 on end-of-file */ + errno = do_read ? 0 : EPIPE; + return pos; + default: + pos += (size_t) res; + } + } + + return pos; +} + +ssh_agent agent_new(struct ssh_session_struct *session) { + ssh_agent agent = NULL; + + agent = malloc(sizeof(struct ssh_agent_struct)); + if (agent == NULL) { + return NULL; + } + ZERO_STRUCTP(agent); + + agent->count = 0; + agent->sock = ssh_socket_new(session); + if (agent->sock == NULL) { + SAFE_FREE(agent); + return NULL; + } + + return agent; +} + +void agent_close(struct ssh_agent_struct *agent) { + if (agent == NULL) { + return; + } + + if (getenv("SSH_AUTH_SOCK")) { + ssh_socket_close(agent->sock); + } +} + +void agent_free(ssh_agent agent) { + if (agent) { + if (agent->ident) { + ssh_buffer_free(agent->ident); + } + if (agent->sock) { + agent_close(agent); + ssh_socket_free(agent->sock); + } + SAFE_FREE(agent); + } +} + +static int agent_connect(ssh_session session) { + const char *auth_sock = NULL; + + if (session == NULL || session->agent == NULL) { + return -1; + } + + auth_sock = getenv("SSH_AUTH_SOCK"); + + if (auth_sock && *auth_sock) { + if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) { + return -1; + } + return 0; + } + + return -1; +} + +#if 0 +static int agent_decode_reply(struct ssh_session_struct *session, int type) { + switch (type) { + case SSH_AGENT_FAILURE: + case SSH2_AGENT_FAILURE: + case SSH_COM_AGENT2_FAILURE: + ssh_log(session, SSH_LOG_RARE, "SSH_AGENT_FAILURE"); + return 0; + case SSH_AGENT_SUCCESS: + return 1; + default: + ssh_set_error(session, SSH_FATAL, + "Bad response from authentication agent: %d", type); + break; + } + + return -1; +} +#endif + +static int agent_talk(struct ssh_session_struct *session, + struct ssh_buffer_struct *request, struct ssh_buffer_struct *reply) { + uint32_t len = 0; + uint8_t payload[1024] = {0}; + + len = buffer_get_rest_len(request); + SSH_LOG(session, SSH_LOG_TRACE, "Request length: %u", len); + agent_put_u32(payload, len); + + /* send length and then the request packet */ + if (atomicio(session->agent->sock, payload, 4, 0) == 4) { + if (atomicio(session->agent->sock, buffer_get_rest(request), len, 0) + != len) { + SSH_LOG(session, SSH_LOG_WARN, "atomicio sending request failed: %s", + strerror(errno)); + return -1; + } + } else { + SSH_LOG(session, SSH_LOG_WARN, + "atomicio sending request length failed: %s", + strerror(errno)); + return -1; + } + + /* wait for response, read the length of the response packet */ + if (atomicio(session->agent->sock, payload, 4, 1) != 4) { + SSH_LOG(session, SSH_LOG_WARN, "atomicio read response length failed: %s", + strerror(errno)); + return -1; + } + + len = agent_get_u32(payload); + if (len > 256 * 1024) { + ssh_set_error(session, SSH_FATAL, + "Authentication response too long: %u", len); + return -1; + } + SSH_LOG(session, SSH_LOG_TRACE, "Response length: %u", len); + + while (len > 0) { + size_t n = len; + if (n > sizeof(payload)) { + n = sizeof(payload); + } + if (atomicio(session->agent->sock, payload, n, 1) != n) { + SSH_LOG(session, SSH_LOG_WARN, + "Error reading response from authentication socket."); + return -1; + } + if (buffer_add_data(reply, payload, n) < 0) { + SSH_LOG(session, SSH_LOG_WARN, "Not enough space"); + return -1; + } + len -= n; + } + + return 0; +} + +int ssh_agent_get_ident_count(struct ssh_session_struct *session) { + ssh_buffer request = NULL; + ssh_buffer reply = NULL; + unsigned int type = 0; + unsigned int c1 = 0, c2 = 0; + uint8_t buf[4] = {0}; + + switch (session->version) { + case 1: + c1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; + c2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; + break; + case 2: + c1 = SSH2_AGENTC_REQUEST_IDENTITIES; + c2 = SSH2_AGENT_IDENTITIES_ANSWER; + break; + default: + return 0; + } + + /* send message to the agent requesting the list of identities */ + request = ssh_buffer_new(); + if (buffer_add_u8(request, c1) < 0) { + ssh_set_error(session, SSH_FATAL, "Not enough space"); + return -1; + } + + reply = ssh_buffer_new(); + if (reply == NULL) { + ssh_buffer_free(request); + ssh_set_error(session, SSH_FATAL, "Not enough space"); + return -1; + } + + if (agent_talk(session, request, reply) < 0) { + ssh_buffer_free(request); + ssh_buffer_free(reply); + return 0; + } + ssh_buffer_free(request); + + /* get message type and verify the answer */ + buffer_get_u8(reply, (uint8_t *) &type); + SSH_LOG(session, SSH_LOG_WARN, + "Answer type: %d, expected answer: %d", + type, c2); + if (agent_failed(type)) { + return 0; + } else if (type != c2) { + ssh_set_error(session, SSH_FATAL, + "Bad authentication reply message type: %d", type); + return -1; + } + + buffer_get_u32(reply, (uint32_t *) buf); + session->agent->count = agent_get_u32(buf); + SSH_LOG(session, SSH_LOG_DEBUG, "Agent count: %d", + session->agent->count); + if (session->agent->count > 1024) { + ssh_set_error(session, SSH_FATAL, + "Too many identities in authentication reply: %d", + session->agent->count); + ssh_buffer_free(reply); + return -1; + } + + if (session->agent->ident) { + buffer_reinit(session->agent->ident); + } + session->agent->ident = reply; + + return session->agent->count; +} + +/* caller has to free commment */ +ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session, + char **comment) { + if (ssh_agent_get_ident_count(session) > 0) { + return ssh_agent_get_next_ident(session, comment); + } + + return NULL; +} + +/* caller has to free commment */ +ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, + char **comment) { + struct ssh_key_struct *key; + struct ssh_string_struct *blob = NULL; + struct ssh_string_struct *tmp = NULL; + int rc; + + if (session->agent->count == 0) { + return NULL; + } + + switch(session->version) { + case 1: + return NULL; + case 2: + /* get the blob */ + blob = buffer_get_ssh_string(session->agent->ident); + if (blob == NULL) { + return NULL; + } + + /* get the comment */ + tmp = buffer_get_ssh_string(session->agent->ident); + if (tmp == NULL) { + ssh_string_free(blob); + + return NULL; + } + + if (comment) { + *comment = ssh_string_to_char(tmp); + } else { + ssh_string_free(blob); + ssh_string_free(tmp); + + return NULL; + } + ssh_string_free(tmp); + + /* get key from blob */ + rc = ssh_pki_import_pubkey_blob(blob, &key); + ssh_string_free(blob); + if (rc == SSH_ERROR) { + return NULL; + } + break; + default: + return NULL; + } + + return key; +} + +int agent_is_running(ssh_session session) { + if (session == NULL || session->agent == NULL) { + return 0; + } + + if (ssh_socket_is_open(session->agent->sock)) { + return 1; + } else { + if (agent_connect(session) < 0) { + return 0; + } else { + return 1; + } + } + + return 0; +} + +ssh_string ssh_agent_sign_data(ssh_session session, + const ssh_key pubkey, + struct ssh_buffer_struct *data) +{ + ssh_buffer request; + ssh_buffer reply; + ssh_string key_blob; + ssh_string sig_blob; + int type = SSH2_AGENT_FAILURE; + int flags = 0; + uint32_t dlen; + int rc; + + request = ssh_buffer_new(); + if (request == NULL) { + return NULL; + } + + /* create request */ + if (buffer_add_u8(request, SSH2_AGENTC_SIGN_REQUEST) < 0) { + ssh_buffer_free(request); + return NULL; + } + + rc = ssh_pki_export_pubkey_blob(pubkey, &key_blob); + if (rc < 0) { + ssh_buffer_free(request); + return NULL; + } + + /* adds len + blob */ + rc = buffer_add_ssh_string(request, key_blob); + ssh_string_free(key_blob); + if (rc < 0) { + ssh_buffer_free(request); + return NULL; + } + + /* Add data */ + dlen = buffer_get_rest_len(data); + if (buffer_add_u32(request, htonl(dlen)) < 0) { + ssh_buffer_free(request); + return NULL; + } + if (buffer_add_data(request, buffer_get_rest(data), dlen) < 0) { + ssh_buffer_free(request); + return NULL; + } + + if (buffer_add_u32(request, htonl(flags)) < 0) { + ssh_buffer_free(request); + return NULL; + } + + reply = ssh_buffer_new(); + if (reply == NULL) { + ssh_buffer_free(request); + return NULL; + } + + /* send the request */ + if (agent_talk(session, request, reply) < 0) { + ssh_buffer_free(request); + ssh_buffer_free(reply); + return NULL; + } + ssh_buffer_free(request); + + /* check if reply is valid */ + if (buffer_get_u8(reply, (uint8_t *) &type) != sizeof(uint8_t)) { + ssh_buffer_free(reply); + return NULL; + } + + if (agent_failed(type)) { + SSH_LOG(session, SSH_LOG_WARN, "Agent reports failure in signing the key"); + ssh_buffer_free(reply); + return NULL; + } else if (type != SSH2_AGENT_SIGN_RESPONSE) { + ssh_set_error(session, SSH_FATAL, "Bad authentication response: %d", type); + ssh_buffer_free(reply); + return NULL; + } + + sig_blob = buffer_get_ssh_string(reply); + ssh_buffer_free(reply); + + return sig_blob; +} + +#endif /* _WIN32 */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/auth.c b/libssh/src/auth.c new file mode 100644 index 00000000..9d099a53 --- /dev/null +++ b/libssh/src/auth.c @@ -0,0 +1,2116 @@ +/* + * auth.c - Authentication with SSH protocols + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2011 by Aris Adamantiadis + * Copyright (c) 2008-2011 Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/crypto.h" +#include "libssh/ssh2.h" +#include "libssh/buffer.h" +#include "libssh/agent.h" +#include "libssh/misc.h" +#include "libssh/packet.h" +#include "libssh/session.h" +#include "libssh/keys.h" +#include "libssh/auth.h" +#include "libssh/pki.h" + +#include "libssh/legacy.h" + +/** + * @defgroup libssh_auth The SSH authentication functions. + * @ingroup libssh + * + * Functions to authenticate with a server. + * + * @{ + */ + +/** + * @internal + * + * @brief Ask access to the ssh-userauth service. + * + * @param[in] session The SSH session handle. + * + * @returns SSH_OK on success, SSH_ERROR on error. + * @returns SSH_AGAIN on nonblocking mode, if calling that function + * again is necessary + */ +static int ssh_userauth_request_service(ssh_session session) { + int rc; + + rc = ssh_service_request(session, "ssh-userauth"); + if (rc != SSH_OK) { + SSH_LOG(session, SSH_LOG_WARN, + "Failed to request \"ssh-userauth\" service"); + } + + return rc; +} + +static int ssh_auth_response_termination(void *user){ + ssh_session session=(ssh_session)user; + switch(session->auth_state){ + case SSH_AUTH_STATE_NONE: + case SSH_AUTH_STATE_KBDINT_SENT: + return 0; + default: + return 1; + } +} + +/** + * @internal + * @brief Wait for a response of an authentication function. + * + * @param[in] session The SSH session. + * + * @returns SSH_AUTH_SUCCESS Authentication success, or pubkey accepted + * SSH_AUTH_PARTIAL Authentication succeeded but another mean + * of authentication is needed. + * SSH_AUTH_INFO Data for keyboard-interactive + * SSH_AUTH_AGAIN In nonblocking mode, call has to be made again + * SSH_AUTH_ERROR Error during the process. + */ +static int ssh_userauth_get_response(ssh_session session) { + int rc = SSH_AUTH_ERROR; + + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + ssh_auth_response_termination, session); + if (rc == SSH_ERROR) { + leave_function(); + return SSH_AUTH_ERROR; + } + if (!ssh_auth_response_termination(session)){ + leave_function(); + return SSH_AUTH_AGAIN; + } + + switch(session->auth_state) { + case SSH_AUTH_STATE_ERROR: + rc = SSH_AUTH_ERROR; + break; + case SSH_AUTH_STATE_FAILED: + rc = SSH_AUTH_DENIED; + break; + case SSH_AUTH_STATE_INFO: + rc = SSH_AUTH_INFO; + break; + case SSH_AUTH_STATE_PARTIAL: + rc = SSH_AUTH_PARTIAL; + break; + case SSH_AUTH_STATE_PK_OK: + case SSH_AUTH_STATE_SUCCESS: + rc = SSH_AUTH_SUCCESS; + break; + case SSH_AUTH_STATE_KBDINT_SENT: + case SSH_AUTH_STATE_NONE: + /* not reached */ + rc = SSH_AUTH_ERROR; + break; + } + + return rc; +} + +/** + * @internal + * + * @brief Handles a SSH_USERAUTH_BANNER packet. + * + * This banner should be shown to user prior to authentication + */ +SSH_PACKET_CALLBACK(ssh_packet_userauth_banner){ + ssh_string banner; + (void)type; + (void)user; + enter_function(); + banner = buffer_get_ssh_string(packet); + if (banner == NULL) { + SSH_LOG(session, SSH_LOG_WARN, + "Invalid SSH_USERAUTH_BANNER packet"); + } else { + SSH_LOG(session, SSH_LOG_DEBUG, + "Received SSH_USERAUTH_BANNER packet"); + if(session->banner != NULL) + ssh_string_free(session->banner); + session->banner = banner; + } + leave_function(); + return SSH_PACKET_USED; +} + +/** + * @internal + * + * @brief Handles a SSH_USERAUTH_FAILURE packet. + * + * This handles the complete or partial authentication failure. + */ +SSH_PACKET_CALLBACK(ssh_packet_userauth_failure){ + char *auth_methods = NULL; + ssh_string auth; + uint8_t partial = 0; + (void) type; + (void) user; + enter_function(); + + auth = buffer_get_ssh_string(packet); + if (auth == NULL || buffer_get_u8(packet, &partial) != 1) { + ssh_set_error(session, SSH_FATAL, + "Invalid SSH_MSG_USERAUTH_FAILURE message"); + session->auth_state=SSH_AUTH_STATE_ERROR; + goto end; + } + + auth_methods = ssh_string_to_char(auth); + if (auth_methods == NULL) { + ssh_set_error_oom(session); + goto end; + } + + if (partial) { + session->auth_state=SSH_AUTH_STATE_PARTIAL; + SSH_LOG(session, SSH_LOG_INFO, + "Partial success. Authentication that can continue: %s", + auth_methods); + } else { + session->auth_state=SSH_AUTH_STATE_FAILED; + SSH_LOG(session, SSH_LOG_INFO, + "Access denied. Authentication that can continue: %s", + auth_methods); + ssh_set_error(session, SSH_REQUEST_DENIED, + "Access denied. Authentication that can continue: %s", + auth_methods); + + session->auth_methods = 0; + } + if (strstr(auth_methods, "password") != NULL) { + session->auth_methods |= SSH_AUTH_METHOD_PASSWORD; + } + if (strstr(auth_methods, "keyboard-interactive") != NULL) { + session->auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; + } + if (strstr(auth_methods, "publickey") != NULL) { + session->auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; + } + if (strstr(auth_methods, "hostbased") != NULL) { + session->auth_methods |= SSH_AUTH_METHOD_HOSTBASED; + } + +end: + ssh_string_free(auth); + SAFE_FREE(auth_methods); + leave_function(); + return SSH_PACKET_USED; +} + +/** + * @internal + * + * @brief Handles a SSH_USERAUTH_SUCCESS packet. + * + * It is also used to communicate the new to the upper levels. + */ +SSH_PACKET_CALLBACK(ssh_packet_userauth_success){ + enter_function(); + (void)packet; + (void)type; + (void)user; + + SSH_LOG(session, SSH_LOG_DEBUG, "Authentication successful"); + SSH_LOG(session, SSH_LOG_TRACE, "Received SSH_USERAUTH_SUCCESS"); + + session->auth_state=SSH_AUTH_STATE_SUCCESS; + session->session_state=SSH_SESSION_STATE_AUTHENTICATED; + session->flags |= SSH_SESSION_FLAG_AUTHENTICATED; + + if(session->current_crypto && session->current_crypto->delayed_compress_out){ + SSH_LOG(session, SSH_LOG_DEBUG, "Enabling delayed compression OUT"); + session->current_crypto->do_compress_out=1; + } + if(session->current_crypto && session->current_crypto->delayed_compress_in){ + SSH_LOG(session,SSH_LOG_DEBUG, "Enabling delayed compression IN"); + session->current_crypto->do_compress_in=1; + } + leave_function(); + return SSH_PACKET_USED; +} + +/** + * @internal + * + * @brief Handles a SSH_USERAUTH_PK_OK or SSH_USERAUTH_INFO_REQUEST packet. + * + * Since the two types of packets share the same code, additional work is done + * to understand if we are in a public key or keyboard-interactive context. + */ +SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok){ + int rc; + enter_function(); + + SSH_LOG(session, SSH_LOG_TRACE, "Received SSH_USERAUTH_PK_OK/INFO_REQUEST"); + + if(session->auth_state==SSH_AUTH_STATE_KBDINT_SENT){ + /* Assuming we are in keyboard-interactive context */ + SSH_LOG(session, SSH_LOG_TRACE, + "keyboard-interactive context, assuming SSH_USERAUTH_INFO_REQUEST"); + rc=ssh_packet_userauth_info_request(session,type,packet,user); + } else { + session->auth_state=SSH_AUTH_STATE_PK_OK; + SSH_LOG(session, SSH_LOG_TRACE, "Assuming SSH_USERAUTH_PK_OK"); + rc=SSH_PACKET_USED; + } + leave_function(); + return rc; +} + +/** + * @brief Get available authentication methods from the server. + * + * This requires the function ssh_userauth_none() to be called before the + * methods are available. The server MAY return a list of methods that may + * continue. + * + * @param[in] session The SSH session. + * + * @param[in] username Deprecated, set to NULL. + * + * @returns A bitfield of the fllowing values: + * - SSH_AUTH_METHOD_PASSWORD + * - SSH_AUTH_METHOD_PUBLICKEY + * - SSH_AUTH_METHOD_HOSTBASED + * - SSH_AUTH_METHOD_INTERACTIVE + * + * @warning Other reserved flags may appear in future versions. + * @see ssh_userauth_none() + */ +int ssh_userauth_list(ssh_session session, const char *username) +{ + (void) username; /* unused */ + + if (session == NULL) { + return 0; + } + +#ifdef WITH_SSH1 + if(session->version == 1) { + return SSH_AUTH_METHOD_PASSWORD; + } +#endif + + return session->auth_methods; +} + +/** + * @brief Try to authenticate through the "none" method. + * + * @param[in] session The ssh session to use. + * + * @param[in] username The username, this SHOULD be NULL. + * + * @returns SSH_AUTH_ERROR: A serious error happened.\n + * SSH_AUTH_DENIED: Authentication failed: use another method\n + * SSH_AUTH_PARTIAL: You've been partially authenticated, you still + * have to use another method\n + * SSH_AUTH_SUCCESS: Authentication success\n + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + * + * @note Most server implementations do not permit changing the username during + * authentication. The username should only be set with ssh_optoins_set() only + * before you connect to the server. + */ +int ssh_userauth_none(ssh_session session, const char *username) { + ssh_string str; + int rc; + +#ifdef WITH_SSH1 + if (session->version == 1) { + return ssh_userauth1_none(session, username); + } +#endif + + switch(session->pending_call_state){ + case SSH_PENDING_CALL_NONE: + break; + case SSH_PENDING_CALL_AUTH_NONE: + goto pending; + default: + ssh_set_error(session, SSH_FATAL, + "Wrong state during pending SSH call"); + return SSH_AUTH_ERROR; + } + + rc = ssh_userauth_request_service(session); + if (rc == SSH_AGAIN) { + return SSH_AUTH_AGAIN; + } else if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + + /* request */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + goto fail; + } + + /* username */ + if (username) { + str = ssh_string_from_char(username); + } else { + str = ssh_string_from_char(session->opts.username); + } + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* service */ + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* method */ + str = ssh_string_from_char("none"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + session->auth_state = SSH_AUTH_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_AUTH_NONE; + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + +pending: + rc = ssh_userauth_get_response(session); + if (rc != SSH_AUTH_AGAIN) { + session->pending_call_state = SSH_PENDING_CALL_NONE; + } + + return rc; +fail: + ssh_set_error_oom(session); + buffer_reinit(session->out_buffer); + + return SSH_AUTH_ERROR; +} + +/** + * @brief Try to authenticate with the given public key. + * + * To avoid unnecessary processing and user interaction, the following method + * is provided for querying whether authentication using the 'pubkey' would + * be possible. + * + * @param[in] session The SSH session. + * + * @param[in] username The username, this SHOULD be NULL. + * + * @param[in] pubkey The public key to try. + * + * @return SSH_AUTH_ERROR: A serious error happened.\n + * SSH_AUTH_DENIED: The server doesn't accept that public key as an + * authentication token. Try another key or another + * method.\n + * SSH_AUTH_PARTIAL: You've been partially authenticated, you still + * have to use another method.\n + * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use + * ssh_userauth_pubkey(). + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + * + * @note Most server implementations do not permit changing the username during + * authentication. The username should only be set with ssh_optoins_set() only + * before you connect to the server. + */ +int ssh_userauth_try_publickey(ssh_session session, + const char *username, + const ssh_key pubkey) +{ + ssh_string str; + int rc; + + if (session == NULL) { + return SSH_AUTH_ERROR; + } + + if (pubkey == NULL || !ssh_key_is_public(pubkey)) { + ssh_set_error(session, SSH_FATAL, "Invalid pubkey"); + return SSH_AUTH_ERROR; + } + +#ifdef WITH_SSH1 + if (session->version == 1) { + return SSH_AUTH_DENIED; + } +#endif + + switch(session->pending_call_state) { + case SSH_PENDING_CALL_NONE: + break; + case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY: + goto pending; + default: + ssh_set_error(session, + SSH_FATAL, + "Wrong state during pending SSH call"); + return SSH_ERROR; + } + + rc = ssh_userauth_request_service(session); + if (rc == SSH_AGAIN) { + return SSH_AUTH_AGAIN; + } else if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + + /* request */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + goto fail; + } + + /* username */ + if (username) { + str = ssh_string_from_char(username); + } else { + str = ssh_string_from_char(session->opts.username); + } + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* service */ + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* method */ + str = ssh_string_from_char("publickey"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* private key? */ + rc = buffer_add_u8(session->out_buffer, 0); + if (rc < 0) { + goto fail; + } + + /* algo */ + str = ssh_string_from_char(pubkey->type_c); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* public key */ + rc = ssh_pki_export_pubkey_blob(pubkey, &str); + if (rc < 0) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + session->auth_state = SSH_AUTH_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_AUTH_OFFER_PUBKEY; + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + +pending: + rc = ssh_userauth_get_response(session); + if (rc != SSH_AUTH_AGAIN) { + session->pending_call_state = SSH_PENDING_CALL_NONE; + } + + return rc; +fail: + ssh_set_error_oom(session); + buffer_reinit(session->out_buffer); + + return SSH_AUTH_ERROR; +} + +/** + * @brief Authenticate with public/private key. + * + * @param[in] session The SSH session. + * + * @param[in] username The username, this SHOULD be NULL. + * + * @param[in] privkey The private key for authentication. + * + * @return SSH_AUTH_ERROR: A serious error happened.\n + * SSH_AUTH_DENIED: The server doesn't accept that public key as an + * authentication token. Try another key or another + * method.\n + * SSH_AUTH_PARTIAL: You've been partially authenticated, you still + * have to use another method.\n + * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use + * ssh_userauth_pubkey(). + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + * + * @note Most server implementations do not permit changing the username during + * authentication. The username should only be set with ssh_optoins_set() only + * before you connect to the server. + */ +int ssh_userauth_publickey(ssh_session session, + const char *username, + const ssh_key privkey) +{ + ssh_string str; + int rc; + + if (session == NULL) { + return SSH_AUTH_ERROR; + } + + if (privkey == NULL || !ssh_key_is_private(privkey)) { + ssh_set_error(session, SSH_FATAL, "Invalid private key"); + return SSH_AUTH_ERROR; + } + +#ifdef WITH_SSH1 + if (session->version == 1) { + return SSH_AUTH_DENIED; + } +#endif + + switch(session->pending_call_state) { + case SSH_PENDING_CALL_NONE: + break; + case SSH_PENDING_CALL_AUTH_PUBKEY: + goto pending; + default: + ssh_set_error(session, + SSH_FATAL, + "Bad call during pending SSH call in ssh_userauth_try_pubkey"); + return SSH_AUTH_ERROR; + } + + rc = ssh_userauth_request_service(session); + if (rc == SSH_AGAIN) { + return SSH_AUTH_AGAIN; + } else if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + + /* request */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + goto fail; + } + + /* username */ + if (username) { + str = ssh_string_from_char(username); + } else { + str = ssh_string_from_char(session->opts.username); + } + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* service */ + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* method */ + str = ssh_string_from_char("publickey"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* private key? */ + rc = buffer_add_u8(session->out_buffer, 1); + if (rc < 0) { + goto fail; + } + + /* algo */ + str = ssh_string_from_char(privkey->type_c); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* public key */ + rc = ssh_pki_export_pubkey_blob(privkey, &str); + if (rc < 0) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* sign the buffer with the private key */ + str = ssh_pki_do_sign(session, session->out_buffer, privkey); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + session->auth_state = SSH_AUTH_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_AUTH_PUBKEY; + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + +pending: + rc = ssh_userauth_get_response(session); + if (rc != SSH_AUTH_AGAIN) { + session->pending_call_state = SSH_PENDING_CALL_NONE; + } + + return rc; +fail: + ssh_set_error_oom(session); + buffer_reinit(session->out_buffer); + + return SSH_AUTH_ERROR; +} + +#ifndef _WIN32 +static int ssh_userauth_agent_publickey(ssh_session session, + const char *username, + ssh_key pubkey) +{ + ssh_string str; + int rc; + + switch(session->pending_call_state) { + case SSH_PENDING_CALL_NONE: + break; + case SSH_PENDING_CALL_AUTH_AGENT: + goto pending; + default: + ssh_set_error(session, + SSH_FATAL, + "Bad call during pending SSH call in ssh_userauth_try_pubkey"); + return SSH_ERROR; + } + + rc = ssh_userauth_request_service(session); + if (rc == SSH_AGAIN) { + return SSH_AUTH_AGAIN; + } else if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + + /* request */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + goto fail; + } + + /* username */ + if (username) { + str = ssh_string_from_char(username); + } else { + str = ssh_string_from_char(session->opts.username); + } + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* service */ + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* method */ + str = ssh_string_from_char("publickey"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* private key? */ + rc = buffer_add_u8(session->out_buffer, 1); + if (rc < 0) { + goto fail; + } + + /* algo */ + str = ssh_string_from_char(pubkey->type_c); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* public key */ + rc = ssh_pki_export_pubkey_blob(pubkey, &str); + if (rc < 0) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* sign the buffer with the private key */ + str = ssh_pki_do_sign_agent(session, session->out_buffer, pubkey); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + session->auth_state = SSH_AUTH_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_AUTH_AGENT; + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + +pending: + rc = ssh_userauth_get_response(session); + if (rc != SSH_AUTH_AGAIN) { + session->pending_call_state = SSH_PENDING_CALL_NONE; + } + + return rc; +fail: + ssh_set_error_oom(session); + buffer_reinit(session->out_buffer); + + return SSH_AUTH_ERROR; +} + +enum ssh_agent_state_e { + SSH_AGENT_STATE_NONE = 0, + SSH_AGENT_STATE_PUBKEY, + SSH_AGENT_STATE_AUTH +}; + +struct ssh_agent_state_struct { + enum ssh_agent_state_e state; + ssh_key pubkey; + char *comment; +}; + + +/** + * @brief Try to do public key authentication with ssh agent. + * + * @param[in] session The ssh session to use. + * + * @param[in] username The username, this SHOULD be NULL. + * + * @return SSH_AUTH_ERROR: A serious error happened.\n + * SSH_AUTH_DENIED: The server doesn't accept that public key as an + * authentication token. Try another key or another + * method.\n + * SSH_AUTH_PARTIAL: You've been partially authenticated, you still + * have to use another method.\n + * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use + * ssh_userauth_pubkey(). + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + * + * @note Most server implementations do not permit changing the username during + * authentication. The username should only be set with ssh_optoins_set() only + * before you connect to the server. + */ +int ssh_userauth_agent(ssh_session session, + const char *username) { + int rc = SSH_AUTH_ERROR; + struct ssh_agent_state_struct *state; + if (session == NULL) { + return SSH_AUTH_ERROR; + } + + if (!agent_is_running(session)) { + return SSH_AUTH_DENIED; + } + if (!session->agent_state){ + session->agent_state = malloc(sizeof(struct ssh_agent_state_struct)); + if (!session->agent_state){ + ssh_set_error_oom(session); + return SSH_AUTH_ERROR; + } + ZERO_STRUCTP(session->agent_state); + session->agent_state->state=SSH_AGENT_STATE_NONE; + } + state = session->agent_state; + if (state->pubkey == NULL) + state->pubkey = ssh_agent_get_first_ident(session, &state->comment); + while (state->pubkey != NULL) { + if(state->state == SSH_AGENT_STATE_NONE){ + SSH_LOG(session, SSH_LOG_DEBUG, + "Trying identity %s", state->comment); + } + if(state->state == SSH_AGENT_STATE_NONE || + state->state == SSH_AGENT_STATE_PUBKEY){ + rc = ssh_userauth_try_publickey(session, username, state->pubkey); + if (rc == SSH_AUTH_ERROR) { + ssh_string_free_char(state->comment); + ssh_key_free(state->pubkey); + SAFE_FREE(session->agent_state); + return rc; + } else if (rc == SSH_AUTH_AGAIN) { + state->state = SSH_AGENT_STATE_PUBKEY; + return rc; + } else if (rc != SSH_AUTH_SUCCESS) { + SSH_LOG(session, SSH_LOG_DEBUG, + "Public key of %s refused by server", state->comment); + ssh_string_free_char(state->comment); + ssh_key_free(state->pubkey); + state->pubkey = ssh_agent_get_next_ident(session, &state->comment); + state->state = SSH_AGENT_STATE_NONE; + continue; + } + + SSH_LOG(session, SSH_LOG_DEBUG, + "Public key of %s accepted by server", state->comment); + state->state = SSH_AGENT_STATE_AUTH; + } + if (state->state == SSH_AGENT_STATE_AUTH){ + rc = ssh_userauth_agent_publickey(session, username, state->pubkey); + if (rc == SSH_AUTH_AGAIN) + return rc; + ssh_string_free_char(state->comment); + ssh_key_free(state->pubkey); + if (rc == SSH_AUTH_ERROR) { + SAFE_FREE(session->agent_state); + return rc; + } else if (rc != SSH_AUTH_SUCCESS) { + SSH_LOG(session, SSH_LOG_INFO, + "Server accepted public key but refused the signature"); + state->pubkey = ssh_agent_get_next_ident(session, &state->comment); + state->state = SSH_AGENT_STATE_NONE; + continue; + } + SAFE_FREE(session->agent_state); + return SSH_AUTH_SUCCESS; + } + } + + SAFE_FREE(session->agent_state); + return rc; +} +#endif + +enum ssh_auth_auto_state_e { + SSH_AUTH_AUTO_STATE_NONE=0, + SSH_AUTH_AUTO_STATE_PUBKEY, + SSH_AUTH_AUTO_STATE_KEY_IMPORTED, + SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED +}; + +struct ssh_auth_auto_state_struct { + enum ssh_auth_auto_state_e state; + struct ssh_iterator *it; + ssh_key privkey; + ssh_key pubkey; +}; + +/** + * @brief Tries to automatically authenticate with public key and "none" + * + * It may fail, for instance it doesn't ask for a password and uses a default + * asker for passphrases (in case the private key is encrypted). + * + * @param[in] session The SSH session. + * + * @param[in] username The username, this SHOULD be NULL. + * + * @param[in] passphrase Use this passphrase to unlock the privatekey. Use NULL + * if you don't want to use a passphrase or the user + * should be asked. + * + * @return SSH_AUTH_ERROR: A serious error happened.\n + * SSH_AUTH_DENIED: The server doesn't accept that public key as an + * authentication token. Try another key or another + * method.\n + * SSH_AUTH_PARTIAL: You've been partially authenticated, you still + * have to use another method.\n + * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use + * ssh_userauth_pubkey(). + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + * + * @note Most server implementations do not permit changing the username during + * authentication. The username should only be set with ssh_optoins_set() only + * before you connect to the server. + */ +int ssh_userauth_publickey_auto(ssh_session session, + const char *username, + const char *passphrase) +{ + ssh_auth_callback auth_fn = NULL; + void *auth_data = NULL; + struct ssh_auth_auto_state_struct *state; + int rc; + + if (session == NULL) { + return SSH_AUTH_ERROR; + } + + if (session->common.callbacks) { + auth_fn = session->common.callbacks->auth_function; + auth_data = session->common.callbacks->userdata; + } + if (!session->auth_auto_state){ + session->auth_auto_state = + malloc(sizeof(struct ssh_auth_auto_state_struct)); + if (!session->auth_auto_state){ + ssh_set_error_oom(session); + return SSH_AUTH_ERROR; + } + ZERO_STRUCTP(session->auth_auto_state); + } + state = session->auth_auto_state; + if (state->state == SSH_AUTH_AUTO_STATE_NONE) { +#ifndef _WIN32 + /* Try authentication with ssh-agent first */ + rc = ssh_userauth_agent(session, username); + if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_SUCCESS) { + return rc; + } + if (rc == SSH_AUTH_AGAIN) + return rc; +#endif + state->state = SSH_AUTH_AUTO_STATE_PUBKEY; + } + if (state->it == NULL) { + state->it = ssh_list_get_iterator(session->opts.identity); + } + + while (state->it != NULL){ + const char *privkey_file = state->it->data; + char pubkey_file[1024] = {0}; + if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY){ + SSH_LOG(session, SSH_LOG_DEBUG, + "Trying to authenticate with %s", privkey_file); + state->privkey = NULL; + state->pubkey = NULL; + snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file); + + rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey); + if (rc == SSH_ERROR) { + ssh_set_error(session, + SSH_FATAL, + "Failed to import public key: %s", + pubkey_file); + SAFE_FREE(session->auth_auto_state); + return SSH_AUTH_ERROR; + } else if (rc == SSH_EOF) { + /* Read the private key and save the public key to file */ + rc = ssh_pki_import_privkey_file(privkey_file, + passphrase, + auth_fn, + auth_data, + &state->privkey); + if (rc == SSH_ERROR) { + ssh_set_error(session, + SSH_FATAL, + "Failed to read private key: %s", + privkey_file); + state->it=state->it->next; + continue; + } else if (rc == SSH_EOF) { + /* If the file doesn't exist, continue */ + SSH_LOG(session, SSH_LOG_DEBUG, + "Private key %s doesn't exist.", + privkey_file); + state->it=state->it->next; + continue; + } + + rc = ssh_pki_export_privkey_to_pubkey(state->privkey, &state->pubkey); + if (rc == SSH_ERROR) { + ssh_key_free(state->privkey); + SAFE_FREE(session->auth_auto_state); + return SSH_AUTH_ERROR; + } + + rc = ssh_pki_export_pubkey_file(state->pubkey, pubkey_file); + if (rc == SSH_ERROR) { + SSH_LOG(session, + SSH_LOG_WARN, + "Could not write public key to file: %s", + pubkey_file); + } + } + state->state = SSH_AUTH_AUTO_STATE_KEY_IMPORTED; + } + if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED){ + rc = ssh_userauth_try_publickey(session, username, state->pubkey); + if (rc == SSH_AUTH_ERROR) { + SSH_LOG(session, + SSH_LOG_WARN, + "Public key authentication error for %s", + privkey_file); + ssh_key_free(state->privkey); + ssh_key_free(state->pubkey); + SAFE_FREE(session->auth_auto_state); + return rc; + } else if (rc == SSH_AUTH_AGAIN){ + return rc; + } else if (rc != SSH_AUTH_SUCCESS) { + SSH_LOG(session, + SSH_LOG_DEBUG, + "Public key for %s refused by server", + privkey_file); + ssh_key_free(state->privkey); + state->privkey = NULL; + ssh_key_free(state->pubkey); + state->pubkey = NULL; + state->it=state->it->next; + state->state = SSH_AUTH_AUTO_STATE_PUBKEY; + continue; + } + state->state = SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED; + } + if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED){ + /* Public key has been accepted by the server */ + if (state->privkey == NULL) { + rc = ssh_pki_import_privkey_file(privkey_file, + passphrase, + auth_fn, + auth_data, + &state->privkey); + if (rc == SSH_ERROR) { + ssh_key_free(state->pubkey); + state->pubkey=NULL; + ssh_set_error(session, + SSH_FATAL, + "Failed to read private key: %s", + privkey_file); + state->it=state->it->next; + state->state = SSH_AUTH_AUTO_STATE_PUBKEY; + continue; + } else if (rc == SSH_EOF) { + /* If the file doesn't exist, continue */ + ssh_key_free(state->pubkey); + state->pubkey=NULL; + SSH_LOG(session, + SSH_LOG_INFO, + "Private key %s doesn't exist.", + privkey_file); + state->it=state->it->next; + state->state = SSH_AUTH_AUTO_STATE_PUBKEY; + continue; + } + } + + rc = ssh_userauth_publickey(session, username, state->privkey); + if (rc != SSH_AUTH_AGAIN && rc != SSH_AUTH_DENIED) { + ssh_key_free(state->privkey); + ssh_key_free(state->pubkey); + SAFE_FREE(session->auth_auto_state); + } + if (rc == SSH_AUTH_ERROR) { + return rc; + } else if (rc == SSH_AUTH_SUCCESS) { + SSH_LOG(session, + SSH_LOG_INFO, + "Successfully authenticated using %s", + privkey_file); + return rc; + } else if (rc == SSH_AUTH_AGAIN){ + return rc; + } + + SSH_LOG(session, + SSH_LOG_WARN, + "The server accepted the public key but refused the signature"); + state->it=state->it->next; + state->state=SSH_AUTH_AUTO_STATE_PUBKEY; + /* continue */ + } + } + SSH_LOG(session, + SSH_LOG_INFO, + "Tried every public key, none matched"); + SAFE_FREE(session->auth_auto_state); + return SSH_AUTH_DENIED; +} + +/** + * @brief Try to authenticate by password. + * + * This authentication method is normally disabled on SSHv2 server. You should + * use keyboard-interactive mode. + * + * The 'password' value MUST be encoded UTF-8. It is up to the server how to + * interpret the password and validate it against the password database. + * However, if you read the password in some other encoding, you MUST convert + * the password to UTF-8. + * + * @param[in] session The ssh session to use. + * + * @param[in] username The username, this SHOULD be NULL. + * + * @param[in] password The password to authenticate in UTF-8. + * + * @returns SSH_AUTH_ERROR: A serious error happened.\n + * SSH_AUTH_DENIED: Authentication failed: use another method\n + * SSH_AUTH_PARTIAL: You've been partially authenticated, you still + * have to use another method\n + * SSH_AUTH_SUCCESS: Authentication success\n + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + * + * @note Most server implementations do not permit changing the username during + * authentication. The username should only be set with ssh_optoins_set() only + * before you connect to the server. + * + * @see ssh_userauth_none() + * @see ssh_userauth_kbdint() + */ +int ssh_userauth_password(ssh_session session, + const char *username, + const char *password) { + ssh_string str; + int rc; + +#ifdef WITH_SSH1 + if (session->version == 1) { + rc = ssh_userauth1_password(session, username, password); + return rc; + } +#endif + + switch(session->pending_call_state) { + case SSH_PENDING_CALL_NONE: + break; + case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY: + goto pending; + default: + ssh_set_error(session, + SSH_FATAL, + "Wrong state during pending SSH call"); + return SSH_ERROR; + } + + rc = ssh_userauth_request_service(session); + if (rc == SSH_AGAIN) { + return SSH_AUTH_AGAIN; + } else if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + + /* request */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + goto fail; + } + + /* username */ + if (username) { + str = ssh_string_from_char(username); + } else { + str = ssh_string_from_char(session->opts.username); + } + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* service */ + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* method */ + str = ssh_string_from_char("password"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* FALSE */ + rc = buffer_add_u8(session->out_buffer, 0); + if (rc < 0) { + goto fail; + } + + /* password */ + str = ssh_string_from_char(password); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + session->auth_state = SSH_AUTH_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_AUTH_OFFER_PUBKEY; + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + +pending: + rc = ssh_userauth_get_response(session); + if (rc != SSH_AUTH_AGAIN) { + session->pending_call_state = SSH_PENDING_CALL_NONE; + } + + return rc; +fail: + ssh_set_error_oom(session); + buffer_reinit(session->out_buffer); + + return SSH_AUTH_ERROR; +} + +#ifndef _WIN32 +/* LEGACY */ +int ssh_userauth_agent_pubkey(ssh_session session, + const char *username, + ssh_public_key publickey) +{ + ssh_key key; + int rc; + + key = ssh_key_new(); + if (key == NULL) { + return SSH_AUTH_ERROR; + } + + key->type = publickey->type; + key->type_c = ssh_key_type_to_char(key->type); + key->flags = SSH_KEY_FLAG_PUBLIC; + key->dsa = publickey->dsa_pub; + key->rsa = publickey->rsa_pub; + + rc = ssh_userauth_agent_publickey(session, username, key); + + key->dsa = NULL; + key->rsa = NULL; + ssh_key_free(key); + + return rc; +} +#endif /* _WIN32 */ + +ssh_kbdint ssh_kbdint_new(void) { + ssh_kbdint kbd; + + kbd = malloc(sizeof(struct ssh_kbdint_struct)); + if (kbd == NULL) { + return NULL; + } + ZERO_STRUCTP(kbd); + + return kbd; +} + + +void ssh_kbdint_free(ssh_kbdint kbd) { + int i, n; + + if (kbd == NULL) { + return; + } + + SAFE_FREE(kbd->name); + SAFE_FREE(kbd->instruction); + SAFE_FREE(kbd->echo); + + n = kbd->nprompts; + if (kbd->prompts) { + for (i = 0; i < n; i++) { + BURN_STRING(kbd->prompts[i]); + SAFE_FREE(kbd->prompts[i]); + } + SAFE_FREE(kbd->prompts); + } + + n = kbd->nanswers; + if (kbd->answers) { + for (i = 0; i < n; i++) { + BURN_STRING(kbd->answers[i]); + SAFE_FREE(kbd->answers[i]); + } + SAFE_FREE(kbd->answers); + } + + SAFE_FREE(kbd); +} + +void ssh_kbdint_clean(ssh_kbdint kbd) { + int i, n; + + if (kbd == NULL) { + return; + } + + SAFE_FREE(kbd->name); + SAFE_FREE(kbd->instruction); + SAFE_FREE(kbd->echo); + + n = kbd->nprompts; + if (kbd->prompts) { + for (i = 0; i < n; i++) { + BURN_STRING(kbd->prompts[i]); + SAFE_FREE(kbd->prompts[i]); + } + SAFE_FREE(kbd->prompts); + } + + n = kbd->nanswers; + + if (kbd->answers) { + for (i = 0; i < n; i++) { + BURN_STRING(kbd->answers[i]); + SAFE_FREE(kbd->answers[i]); + } + SAFE_FREE(kbd->answers); + } + + kbd->nprompts = 0; + kbd->nanswers = 0; +} + +/* + * This function sends the first packet as explained in RFC 3066 section 3.1. + */ +static int ssh_userauth_kbdint_init(ssh_session session, + const char *username, + const char *submethods) +{ + ssh_string str; + int rc; + if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_INIT) + goto pending; + if (session->pending_call_state != SSH_PENDING_CALL_NONE){ + ssh_set_error_invalid(session); + return SSH_ERROR; + } + rc = ssh_userauth_request_service(session); + if (rc == SSH_AGAIN) + return SSH_AUTH_AGAIN; + if (rc != SSH_OK) { + return SSH_AUTH_ERROR; + } + + /* request */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + goto fail; + } + + /* username */ + if (username) { + str = ssh_string_from_char(username); + } else { + str = ssh_string_from_char(session->opts.username); + } + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* service */ + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* method */ + str = ssh_string_from_char("keyboard-interactive"); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* lang string (ignore it) */ + str = ssh_string_from_char(""); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + /* submethods */ + if (submethods == NULL) { + submethods = ""; + } + + str = ssh_string_from_char(submethods); + if (str == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + session->auth_state = SSH_AUTH_STATE_KBDINT_SENT; + session->pending_call_state = SSH_PENDING_CALL_AUTH_KBDINT_INIT; + + SSH_LOG(session, SSH_LOG_DEBUG, + "Sending keyboard-interactive init request"); + + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } +pending: + rc = ssh_userauth_get_response(session); + if (rc != SSH_AUTH_AGAIN) + session->pending_call_state = SSH_PENDING_CALL_NONE; + return rc; +fail: + ssh_set_error_oom(session); + buffer_reinit(session->out_buffer); + + return SSH_AUTH_ERROR; +} + +/** + * @internal + * + * @brief Send the current challenge response and wait for a reply from the + * server. + * + * @returns SSH_AUTH_INFO if more info is needed + * @returns SSH_AUTH_SUCCESS + * @returns SSH_AUTH_FAILURE + * @returns SSH_AUTH_PARTIAL + */ +static int ssh_userauth_kbdint_send(ssh_session session) +{ + ssh_string answer; + uint32_t i; + int rc; + if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_SEND) + goto pending; + if (session->pending_call_state != SSH_PENDING_CALL_NONE){ + ssh_set_error_invalid(session); + return SSH_ERROR; + } + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_INFO_RESPONSE); + if (rc < 0) { + goto fail; + } + + rc = buffer_add_u32(session->out_buffer, htonl(session->kbdint->nprompts)); + if (rc < 0) { + goto fail; + } + + for (i = 0; i < session->kbdint->nprompts; i++) { + if (session->kbdint->answers && session->kbdint->answers[i]) { + answer = ssh_string_from_char(session->kbdint->answers[i]); + } else { + answer = ssh_string_from_char(""); + } + if (answer == NULL) { + goto fail; + } + + rc = buffer_add_ssh_string(session->out_buffer, answer); + string_burn(answer); + string_free(answer); + if (rc < 0) { + goto fail; + } + } + + session->auth_state = SSH_AUTH_STATE_KBDINT_SENT; + session->pending_call_state = SSH_PENDING_CALL_AUTH_KBDINT_SEND; + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + + SSH_LOG(session, SSH_LOG_DEBUG, + "Sending keyboard-interactive response packet"); + + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } +pending: + rc = ssh_userauth_get_response(session); + if (rc != SSH_AUTH_AGAIN) + session->pending_call_state = SSH_PENDING_CALL_NONE; + return rc; +fail: + ssh_set_error_oom(session); + buffer_reinit(session->out_buffer); + + return SSH_AUTH_ERROR; +} + +/** + * @internal + * @brief handles a SSH_USERAUTH_INFO_REQUEST packet, as used in + * keyboard-interactive authentication, and changes the + * authentication state. + */ +SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { + ssh_string name; /* name of the "asking" window showed to client */ + ssh_string instruction; + ssh_string tmp; + uint32_t nprompts; + uint32_t i; + (void)user; + (void)type; + enter_function(); + + name = buffer_get_ssh_string(packet); + instruction = buffer_get_ssh_string(packet); + tmp = buffer_get_ssh_string(packet); + buffer_get_u32(packet, &nprompts); + + /* We don't care about tmp */ + ssh_string_free(tmp); + + if (name == NULL || instruction == NULL) { + ssh_string_free(name); + ssh_string_free(instruction); + ssh_set_error(session, SSH_FATAL, "Invalid USERAUTH_INFO_REQUEST msg"); + leave_function(); + return SSH_PACKET_USED; + } + + if (session->kbdint == NULL) { + session->kbdint = ssh_kbdint_new(); + if (session->kbdint == NULL) { + ssh_set_error_oom(session); + ssh_string_free(name); + ssh_string_free(instruction); + + leave_function(); + return SSH_PACKET_USED; + } + } else { + ssh_kbdint_clean(session->kbdint); + } + + session->kbdint->name = ssh_string_to_char(name); + ssh_string_free(name); + if (session->kbdint->name == NULL) { + ssh_set_error_oom(session); + ssh_kbdint_free(session->kbdint); + ssh_string_free(instruction); + leave_function(); + return SSH_PACKET_USED; + } + + session->kbdint->instruction = ssh_string_to_char(instruction); + ssh_string_free(instruction); + if (session->kbdint->instruction == NULL) { + ssh_set_error_oom(session); + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + leave_function(); + return SSH_PACKET_USED; + } + + nprompts = ntohl(nprompts); + SSH_LOG(session, SSH_LOG_DEBUG, + "%d keyboard-interactive prompts", nprompts); + if (nprompts > KBDINT_MAX_PROMPT) { + ssh_set_error(session, SSH_FATAL, + "Too much prompts requested by the server: %u (0x%.4x)", + nprompts, nprompts); + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + leave_function(); + return SSH_PACKET_USED; + } + + session->kbdint->nprompts = nprompts; + session->kbdint->nanswers = nprompts; + session->kbdint->prompts = malloc(nprompts * sizeof(char *)); + if (session->kbdint->prompts == NULL) { + session->kbdint->nprompts = 0; + ssh_set_error_oom(session); + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + leave_function(); + return SSH_PACKET_USED; + } + memset(session->kbdint->prompts, 0, nprompts * sizeof(char *)); + + session->kbdint->echo = malloc(nprompts); + if (session->kbdint->echo == NULL) { + session->kbdint->nprompts = 0; + ssh_set_error_oom(session); + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + leave_function(); + return SSH_PACKET_USED; + } + memset(session->kbdint->echo, 0, nprompts); + + for (i = 0; i < nprompts; i++) { + tmp = buffer_get_ssh_string(packet); + buffer_get_u8(packet, &session->kbdint->echo[i]); + if (tmp == NULL) { + ssh_set_error(session, SSH_FATAL, "Short INFO_REQUEST packet"); + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + leave_function(); + return SSH_PACKET_USED; + } + session->kbdint->prompts[i] = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (session->kbdint->prompts[i] == NULL) { + ssh_set_error_oom(session); + session->kbdint->nprompts = i; + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + leave_function(); + return SSH_PACKET_USED; + } + } + session->auth_state=SSH_AUTH_STATE_INFO; + leave_function(); + return SSH_PACKET_USED; +} + +/** + * @brief Try to authenticate through the "keyboard-interactive" method. + * + * @param[in] session The ssh session to use. + * + * @param[in] user The username to authenticate. You can specify NULL if + * ssh_option_set_username() has been used. You cannot try + * two different logins in a row. + * + * @param[in] submethods Undocumented. Set it to NULL. + * + * @returns SSH_AUTH_ERROR: A serious error happened\n + * SSH_AUTH_DENIED: Authentication failed : use another method\n + * SSH_AUTH_PARTIAL: You've been partially authenticated, you still + * have to use another method\n + * SSH_AUTH_SUCCESS: Authentication success\n + * SSH_AUTH_INFO: The server asked some questions. Use + * ssh_userauth_kbdint_getnprompts() and such.\n + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + * + * @see ssh_userauth_kbdint_getnprompts() + * @see ssh_userauth_kbdint_getname() + * @see ssh_userauth_kbdint_getinstruction() + * @see ssh_userauth_kbdint_getprompt() + * @see ssh_userauth_kbdint_setanswer() + */ +int ssh_userauth_kbdint(ssh_session session, const char *user, + const char *submethods) { + int rc = SSH_AUTH_ERROR; + + if (session == NULL) { + return SSH_AUTH_ERROR; + } + +#ifdef WITH_SSH1 + if (session->version == 1) { + return SSH_AUTH_DENIED; + } +#endif + if ((session->pending_call_state == SSH_PENDING_CALL_NONE && session->kbdint == NULL) || + session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_INIT) + rc = ssh_userauth_kbdint_init(session, user, submethods); + else if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_SEND || + session->kbdint != NULL) { + /* + * If we are at this point, it is because session->kbdint exists. + * It means the user has set some information there we need to send + * the server and then we need to ack the status (new questions or ok + * pass in). + * It is possible that session->kbdint is NULL while we're waiting for + * a reply, hence the test for the pending call. + */ + rc = ssh_userauth_kbdint_send(session); + } else { + /* We are here because session->kbdint == NULL & state != NONE. + * This should not happen + */ + rc = SSH_AUTH_ERROR; + ssh_set_error(session,SSH_FATAL,"Invalid state in %s", __FUNCTION__); + } + return rc; +} + +/** + * @brief Get the number of prompts (questions) the server has given. + * + * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return + * code, this function can be used to retrieve information about the keyboard + * interactive authentication questions sent by the remote host. + * + * @param[in] session The ssh session to use. + * + * @returns The number of prompts. + */ +int ssh_userauth_kbdint_getnprompts(ssh_session session) { + if(session==NULL) + return SSH_ERROR; + if(session->kbdint == NULL) { + ssh_set_error_invalid(session); + return SSH_ERROR; + } + return session->kbdint->nprompts; +} + +/** + * @brief Get the "name" of the message block. + * + * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return + * code, this function can be used to retrieve information about the keyboard + * interactive authentication questions sent by the remote host. + * + * @param[in] session The ssh session to use. + * + * @returns The name of the message block. Do not free it. + */ +const char *ssh_userauth_kbdint_getname(ssh_session session) { + if(session==NULL) + return NULL; + if(session->kbdint == NULL) { + ssh_set_error_invalid(session); + return NULL; + } + return session->kbdint->name; +} + +/** + * @brief Get the "instruction" of the message block. + * + * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return + * code, this function can be used to retrieve information about the keyboard + * interactive authentication questions sent by the remote host. + * + * @param[in] session The ssh session to use. + * + * @returns The instruction of the message block. + */ + +const char *ssh_userauth_kbdint_getinstruction(ssh_session session) { + if(session==NULL) + return NULL; + if(session->kbdint == NULL) { + ssh_set_error_invalid(session); + return NULL; + } + return session->kbdint->instruction; +} + +/** + * @brief Get a prompt from a message block. + * + * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return + * code, this function can be used to retrieve information about the keyboard + * interactive authentication questions sent by the remote host. + * + * @param[in] session The ssh session to use. + * + * @param[in] i The index number of the i'th prompt. + * + * @param[out] echo This is an optional variable. You can obtain a + * boolean if the user input should be echoed or + * hidden. For passwords it is usually hidden. + * + * @returns A pointer to the prompt. Do not free it. + * + * @code + * const char prompt; + * char echo; + * + * prompt = ssh_userauth_kbdint_getprompt(session, 0, &echo); + * if (echo) ... + * @endcode + */ +const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, + char *echo) { + if(session==NULL) + return NULL; + if(session->kbdint == NULL) { + ssh_set_error_invalid(session); + return NULL; + } + if (i > session->kbdint->nprompts) { + ssh_set_error_invalid(session); + return NULL; + } + + if (echo) { + *echo = session->kbdint->echo[i]; + } + + return session->kbdint->prompts[i]; +} + +#ifdef WITH_SERVER +/** + * @brief Get the number of answers the client has given. + * + * @param[in] session The ssh session to use. + * + * @returns The number of answers. + */ +int ssh_userauth_kbdint_getnanswers(ssh_session session) { + if(session==NULL || session->kbdint == NULL) + return SSH_ERROR; + return session->kbdint->nanswers; +} + +/** + * @brief Get the answer for a question from a message block. + * + * @param[in] session The ssh session to use. + * + * @param[in] i index The number of the ith answer. + * + * @return 0 on success, < 0 on error. + */ +const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) { + if(session==NULL || session->kbdint == NULL + || session->kbdint->answers == NULL) { + return NULL; + } + if (i >= session->kbdint->nanswers) { + return NULL; + } + + return session->kbdint->answers[i]; +} +#endif + +/** + * @brief Set the answer for a question from a message block. + * + * If you have called ssh_userauth_kbdint() and got SSH_AUTH_INFO, this + * function returns the questions from the server. + * + * @param[in] session The ssh session to use. + * + * @param[in] i index The number of the ith prompt. + * + * @param[in] answer The answer to give to the server. The answer MUST be + * encoded UTF-8. It is up to the server how to interpret + * the value and validate it. However, if you read the + * answer in some other encoding, you MUST convert it to + * UTF-8. + * + * @return 0 on success, < 0 on error. + */ +int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i, + const char *answer) { + if (session == NULL) + return -1; + if (answer == NULL || session->kbdint == NULL || + i >= session->kbdint->nprompts) { + ssh_set_error_invalid(session); + return -1; + } + + if (session->kbdint->answers == NULL) { + session->kbdint->answers = malloc(sizeof(char*) * session->kbdint->nprompts); + if (session->kbdint->answers == NULL) { + ssh_set_error_oom(session); + return -1; + } + memset(session->kbdint->answers, 0, sizeof(char *) * session->kbdint->nprompts); + } + + if (session->kbdint->answers[i]) { + BURN_STRING(session->kbdint->answers[i]); + SAFE_FREE(session->kbdint->answers[i]); + } + + session->kbdint->answers[i] = strdup(answer); + if (session->kbdint->answers[i] == NULL) { + ssh_set_error_oom(session); + return -1; + } + + return 0; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/auth1.c b/libssh/src/auth1.c new file mode 100644 index 00000000..0f3f096d --- /dev/null +++ b/libssh/src/auth1.c @@ -0,0 +1,235 @@ +/* + * auth1.c - authentication with SSH-1 protocol + * + * This file is part of the SSH Library + * + * Copyright (c) 2005-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include "libssh/priv.h" +#include "libssh/ssh1.h" +#include "libssh/buffer.h" +#include "libssh/packet.h" +#include "libssh/session.h" +#include "libssh/string.h" + +#ifdef WITH_SSH1 + +static int ssh_auth_status_termination(void *s){ + ssh_session session=s; + if(session->auth_state != SSH_AUTH_STATE_NONE || + session->session_state == SSH_SESSION_STATE_ERROR) + return 1; + return 0; +} + +static int wait_auth1_status(ssh_session session) { + enter_function(); + /* wait for a packet */ + if (ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, + ssh_auth_status_termination, session) != SSH_OK){ + leave_function(); + return SSH_AUTH_ERROR; + } + ssh_log(session,SSH_LOG_PROTOCOL,"Auth state : %d",session->auth_state); + leave_function(); + switch(session->auth_state) { + case SSH_AUTH_STATE_SUCCESS: + return SSH_AUTH_SUCCESS; + case SSH_AUTH_STATE_FAILED: + return SSH_AUTH_DENIED; + default: + return SSH_AUTH_AGAIN; + } + return SSH_AUTH_ERROR; +} + +void ssh_auth1_handler(ssh_session session, uint8_t type){ + if(session->session_state != SSH_SESSION_STATE_AUTHENTICATING){ + ssh_set_error(session,SSH_FATAL,"SSH_SMSG_SUCCESS or FAILED received in wrong state"); + return; + } + if(type==SSH_SMSG_SUCCESS){ + session->auth_state=SSH_AUTH_STATE_SUCCESS; + session->session_state=SSH_SESSION_STATE_AUTHENTICATED; + } else if(type==SSH_SMSG_FAILURE) + session->auth_state=SSH_AUTH_STATE_FAILED; +} + +static int send_username(ssh_session session, const char *username) { + ssh_string user = NULL; + int rc; + /* returns SSH_AUTH_SUCCESS or SSH_AUTH_DENIED */ + if(session->auth_service_state == SSH_AUTH_SERVICE_USER_SENT) { + if(session->auth_state == SSH_AUTH_STATE_FAILED) + return SSH_AUTH_DENIED; + if(session->auth_state == SSH_AUTH_STATE_SUCCESS) + return SSH_AUTH_SUCCESS; + return SSH_AUTH_ERROR; + } + if (session->auth_service_state == SSH_AUTH_SERVICE_SENT) + goto pending; + if (!username) { + if(!(username = session->opts.username)) { + if (ssh_options_set(session, SSH_OPTIONS_USER, NULL) < 0) { + session->auth_service_state = SSH_AUTH_SERVICE_DENIED; + return SSH_ERROR; + } else { + username = session->opts.username; + } + } + } + user = ssh_string_from_char(username); + if (user == NULL) { + return SSH_AUTH_ERROR; + } + + if (buffer_add_u8(session->out_buffer, SSH_CMSG_USER) < 0) { + ssh_string_free(user); + return SSH_AUTH_ERROR; + } + if (buffer_add_ssh_string(session->out_buffer, user) < 0) { + ssh_string_free(user); + return SSH_AUTH_ERROR; + } + ssh_string_free(user); + session->auth_state=SSH_AUTH_STATE_NONE; + session->auth_service_state = SSH_AUTH_SERVICE_SENT; + if (packet_send(session) == SSH_ERROR) { + return SSH_AUTH_ERROR; + } +pending: + rc = wait_auth1_status(session); + switch (rc){ + case SSH_AUTH_SUCCESS: + session->auth_service_state=SSH_AUTH_SERVICE_USER_SENT; + session->auth_state=SSH_AUTH_STATE_SUCCESS; + ssh_set_error(session, SSH_NO_ERROR, "Authentication successful"); + return SSH_AUTH_SUCCESS; + case SSH_AUTH_DENIED: + session->auth_service_state=SSH_AUTH_SERVICE_USER_SENT; + ssh_set_error(session,SSH_REQUEST_DENIED,"Password authentication necessary for user %s",username); + return SSH_AUTH_DENIED; + case SSH_AUTH_AGAIN: + return SSH_AUTH_AGAIN; + default: + session->auth_service_state = SSH_AUTH_SERVICE_NONE; + session->auth_state=SSH_AUTH_STATE_ERROR; + return SSH_AUTH_ERROR; + } +} + +/* use the "none" authentication question */ +int ssh_userauth1_none(ssh_session session, const char *username){ + return send_username(session, username); +} + +/** \internal + * \todo implement ssh1 public key + */ +int ssh_userauth1_offer_pubkey(ssh_session session, const char *username, + int type, ssh_string pubkey) { + (void) session; + (void) username; + (void) type; + (void) pubkey; + enter_function(); + leave_function(); + return SSH_AUTH_DENIED; +} + +int ssh_userauth1_password(ssh_session session, const char *username, + const char *password) { + ssh_string pwd = NULL; + int rc; + enter_function(); + rc = send_username(session, username); + if (rc != SSH_AUTH_DENIED) { + leave_function(); + return rc; + } + if (session->pending_call_state == SSH_PENDING_CALL_AUTH_PASSWORD) + goto pending; + /* we trick a bit here. A known flaw in SSH1 protocol is that it's + * easy to guess password sizes. + * not that sure ... + */ + + /* XXX fix me here ! */ + /* cisco IOS doesn't like when a password is followed by zeroes and random pad. */ + if(1 || strlen(password) >= 128) { + /* not risky to disclose the size of such a big password .. */ + pwd = ssh_string_from_char(password); + if (pwd == NULL) { + leave_function(); + return SSH_AUTH_ERROR; + } + } else { + char buf[128] = {0}; + /* fill the password string from random things. the strcpy + * ensure there is at least a nul byte after the password. + * most implementation won't see the garbage at end. + * why garbage ? because nul bytes will be compressed by + * gzip and disclose password len. + */ + pwd = ssh_string_new(sizeof(buf)); + if (pwd == NULL) { + leave_function(); + return SSH_AUTH_ERROR; + } + ssh_get_random(buf, sizeof(buf), 0); + strcpy(buf, password); + ssh_string_fill(pwd, buf, sizeof(buf)); + } + + if (buffer_add_u8(session->out_buffer, SSH_CMSG_AUTH_PASSWORD) < 0) { + ssh_string_burn(pwd); + ssh_string_free(pwd); + leave_function(); + return SSH_AUTH_ERROR; + } + if (buffer_add_ssh_string(session->out_buffer, pwd) < 0) { + ssh_string_burn(pwd); + ssh_string_free(pwd); + leave_function(); + return SSH_AUTH_ERROR; + } + + ssh_string_burn(pwd); + ssh_string_free(pwd); + session->auth_state=SSH_AUTH_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_AUTH_PASSWORD; + if (packet_send(session) == SSH_ERROR) { + leave_function(); + return SSH_AUTH_ERROR; + } +pending: + rc = wait_auth1_status(session); + if (rc != SSH_AUTH_AGAIN) + session->pending_call_state = SSH_PENDING_CALL_NONE; + leave_function(); + return rc; +} + +#endif /* WITH_SSH1 */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/base64.c b/libssh/src/base64.c new file mode 100644 index 00000000..ff7671c1 --- /dev/null +++ b/libssh/src/base64.c @@ -0,0 +1,287 @@ +/* + * base64.c - support for base64 alphabet system, described in RFC1521 + * + * This file is part of the SSH Library + * + * Copyright (c) 2005-2005 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* just the dirtiest part of code i ever made */ +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/buffer.h" + +static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +/* Transformations */ +#define SET_A(n, i) do { (n) |= ((i) & 63) <<18; } while (0) +#define SET_B(n, i) do { (n) |= ((i) & 63) <<12; } while (0) +#define SET_C(n, i) do { (n) |= ((i) & 63) << 6; } while (0) +#define SET_D(n, i) do { (n) |= ((i) & 63); } while (0) + +#define GET_A(n) (unsigned char) (((n) & 0xff0000) >> 16) +#define GET_B(n) (unsigned char) (((n) & 0xff00) >> 8) +#define GET_C(n) (unsigned char) ((n) & 0xff) + +static int _base64_to_bin(unsigned char dest[3], const char *source, int num); +static int get_equals(char *string); + +/* First part: base64 to binary */ + +/** + * @internal + * + * @brief Translates a base64 string into a binary one. + * + * @returns A buffer containing the decoded string, NULL if something went + * wrong (e.g. incorrect char). + */ +ssh_buffer base64_to_bin(const char *source) { + ssh_buffer buffer = NULL; + unsigned char block[3]; + char *base64; + char *ptr; + size_t len; + int equals; + + base64 = strdup(source); + if (base64 == NULL) { + return NULL; + } + ptr = base64; + + /* Get the number of equals signs, which mirrors the padding */ + equals = get_equals(ptr); + if (equals > 2) { + SAFE_FREE(base64); + return NULL; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + SAFE_FREE(base64); + return NULL; + } + + len = strlen(ptr); + while (len > 4) { + if (_base64_to_bin(block, ptr, 3) < 0) { + goto error; + } + if (buffer_add_data(buffer, block, 3) < 0) { + goto error; + } + len -= 4; + ptr += 4; + } + + /* + * Depending on the number of bytes resting, there are 3 possibilities + * from the RFC. + */ + switch (len) { + /* + * (1) The final quantum of encoding input is an integral multiple of + * 24 bits. Here, the final unit of encoded output will be an integral + * multiple of 4 characters with no "=" padding + */ + case 4: + if (equals != 0) { + goto error; + } + if (_base64_to_bin(block, ptr, 3) < 0) { + goto error; + } + if (buffer_add_data(buffer, block, 3) < 0) { + goto error; + } + SAFE_FREE(base64); + + return buffer; + /* + * (2) The final quantum of encoding input is exactly 8 bits; here, the + * final unit of encoded output will be two characters followed by + * two "=" padding characters. + */ + case 2: + if (equals != 2){ + goto error; + } + + if (_base64_to_bin(block, ptr, 1) < 0) { + goto error; + } + if (buffer_add_data(buffer, block, 1) < 0) { + goto error; + } + SAFE_FREE(base64); + + return buffer; + /* + * The final quantum of encoding input is exactly 16 bits. Here, the final + * unit of encoded output will be three characters followed by one "=" + * padding character. + */ + case 3: + if (equals != 1) { + goto error; + } + if (_base64_to_bin(block, ptr, 2) < 0) { + goto error; + } + if (buffer_add_data(buffer,block,2) < 0) { + goto error; + } + SAFE_FREE(base64); + + return buffer; + default: + /* 4,3,2 are the only padding size allowed */ + goto error; + } + +error: + SAFE_FREE(base64); + ssh_buffer_free(buffer); + return NULL; +} + +#define BLOCK(letter, n) do {ptr = strchr(alphabet, source[n]); \ + if(!ptr) return -1; \ + i = ptr - alphabet; \ + SET_##letter(*block, i); \ + } while(0) + +/* Returns 0 if ok, -1 if not (ie invalid char into the stuff) */ +static int to_block4(unsigned long *block, const char *source, int num) { + char *ptr; + unsigned int i; + + *block = 0; + if (num < 1) { + return 0; + } + + BLOCK(A, 0); /* 6 bit */ + BLOCK(B,1); /* 12 bit */ + + if (num < 2) { + return 0; + } + + BLOCK(C, 2); /* 18 bit */ + + if (num < 3) { + return 0; + } + + BLOCK(D, 3); /* 24 bit */ + + return 0; +} + +/* num = numbers of final bytes to be decoded */ +static int _base64_to_bin(unsigned char dest[3], const char *source, int num) { + unsigned long block; + + if (to_block4(&block, source, num) < 0) { + return -1; + } + dest[0] = GET_A(block); + dest[1] = GET_B(block); + dest[2] = GET_C(block); + + return 0; +} + +/* Count the number of "=" signs and replace them by zeroes */ +static int get_equals(char *string) { + char *ptr = string; + int num = 0; + + while ((ptr=strchr(ptr,'=')) != NULL) { + num++; + *ptr = '\0'; + ptr++; + } + + return num; +} + +/* thanks sysk for debugging my mess :) */ +#define BITS(n) ((1 << (n)) - 1) +static void _bin_to_base64(unsigned char *dest, const unsigned char source[3], + int len) { + switch (len) { + case 1: + dest[0] = alphabet[(source[0] >> 2)]; + dest[1] = alphabet[((source[0] & BITS(2)) << 4)]; + dest[2] = '='; + dest[3] = '='; + break; + case 2: + dest[0] = alphabet[source[0] >> 2]; + dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)]; + dest[2] = alphabet[(source[1] & BITS(4)) << 2]; + dest[3] = '='; + break; + case 3: + dest[0] = alphabet[(source[0] >> 2)]; + dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)]; + dest[2] = alphabet[ (source[2] >> 6) | (source[1] & BITS(4)) << 2]; + dest[3] = alphabet[source[2] & BITS(6)]; + break; + } +} + +/** + * @internal + * + * @brief Converts binary data to a base64 string. + * + * @returns the converted string + */ +unsigned char *bin_to_base64(const unsigned char *source, int len) { + unsigned char *base64; + unsigned char *ptr; + int flen = len + (3 - (len % 3)); /* round to upper 3 multiple */ + flen = (4 * flen) / 3 + 1; + + base64 = malloc(flen); + if (base64 == NULL) { + return NULL; + } + ptr = base64; + + while(len > 0){ + _bin_to_base64(ptr, source, len > 3 ? 3 : len); + ptr += 4; + source += 3; + len -= 3; + } + ptr[0] = '\0'; + + return base64; +} + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/bind.c b/libssh/src/bind.c new file mode 100644 index 00000000..add5a702 --- /dev/null +++ b/libssh/src/bind.c @@ -0,0 +1,466 @@ +/* + * bind.c : all ssh_bind functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2004-2005 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/bind.h" +#include "libssh/libssh.h" +#include "libssh/server.h" +#include "libssh/pki.h" +#include "libssh/buffer.h" +#include "libssh/socket.h" +#include "libssh/session.h" + +/** + * @addtogroup libssh_server + * + * @{ + */ + + +#ifdef _WIN32 +#include +#include +#include + +/* + * is necessary for getaddrinfo before Windows XP, but it isn't + * available on some platforms like MinGW. + */ +#ifdef HAVE_WSPIAPI_H +# include +#endif + +#define SOCKOPT_TYPE_ARG4 char + +#else /* _WIN32 */ + +#include +#include +#include +#define SOCKOPT_TYPE_ARG4 int + +#endif /* _WIN32 */ + +static socket_t bind_socket(ssh_bind sshbind, const char *hostname, + int port) { + char port_c[6]; + struct addrinfo *ai; + struct addrinfo hints; + int opt = 1; + socket_t s; + int rc; + + ZERO_STRUCT(hints); + + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + + snprintf(port_c, 6, "%d", port); + rc = getaddrinfo(hostname, port_c, &hints, &ai); + if (rc != 0) { + ssh_set_error(sshbind, + SSH_FATAL, + "Resolving %s: %s", hostname, gai_strerror(rc)); + return -1; + } + + s = socket (ai->ai_family, + ai->ai_socktype, + ai->ai_protocol); + if (s == SSH_INVALID_SOCKET) { + ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno)); + freeaddrinfo (ai); + return -1; + } + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (char *)&opt, sizeof(opt)) < 0) { + ssh_set_error(sshbind, + SSH_FATAL, + "Setting socket options failed: %s", + strerror(errno)); + freeaddrinfo (ai); + close(s); + return -1; + } + + if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) { + ssh_set_error(sshbind, + SSH_FATAL, + "Binding to %s:%d: %s", + hostname, + port, + strerror(errno)); + freeaddrinfo (ai); + close(s); + return -1; + } + + freeaddrinfo (ai); + return s; +} + +ssh_bind ssh_bind_new(void) { + ssh_bind ptr; + + ptr = malloc(sizeof(struct ssh_bind_struct)); + if (ptr == NULL) { + return NULL; + } + ZERO_STRUCTP(ptr); + ptr->bindfd = SSH_INVALID_SOCKET; + ptr->bindport= 22; + ptr->common.log_verbosity = 0; + + return ptr; +} + +int ssh_bind_listen(ssh_bind sshbind) { + const char *host; + socket_t fd; + int rc; + + if (ssh_init() < 0) { + ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed"); + return -1; + } + + if (sshbind->ecdsakey == NULL && + sshbind->dsakey == NULL && + sshbind->rsakey == NULL) { + ssh_set_error(sshbind, SSH_FATAL, + "DSA or RSA host key file must be set before listen()"); + return SSH_ERROR; + } + +#ifdef HAVE_ECC + if (sshbind->ecdsakey) { + rc = ssh_pki_import_privkey_file(sshbind->ecdsakey, + NULL, + NULL, + NULL, + &sshbind->ecdsa); + if (rc == SSH_ERROR) { + ssh_set_error(sshbind, SSH_FATAL, + "Failed to import private ECDSA host key"); + return SSH_ERROR; + } + + if (ssh_key_type(sshbind->ecdsa) != SSH_KEYTYPE_ECDSA) { + ssh_set_error(sshbind, SSH_FATAL, + "The ECDSA host key has the wrong type"); + ssh_key_free(sshbind->ecdsa); + return SSH_ERROR; + } + } +#endif + + if (sshbind->dsakey) { + rc = ssh_pki_import_privkey_file(sshbind->dsakey, + NULL, + NULL, + NULL, + &sshbind->dsa); + if (rc == SSH_ERROR) { + ssh_set_error(sshbind, SSH_FATAL, + "Failed to import private DSA host key"); + return SSH_ERROR; + } + + if (ssh_key_type(sshbind->dsa) != SSH_KEYTYPE_DSS) { + ssh_set_error(sshbind, SSH_FATAL, + "The DSA host key has the wrong type: %d", + ssh_key_type(sshbind->dsa)); + ssh_key_free(sshbind->dsa); + return SSH_ERROR; + } + } + + if (sshbind->rsakey) { + rc = ssh_pki_import_privkey_file(sshbind->rsakey, + NULL, + NULL, + NULL, + &sshbind->rsa); + if (rc == SSH_ERROR) { + ssh_set_error(sshbind, SSH_FATAL, + "Failed to import private RSA host key"); + return SSH_ERROR; + } + + if (ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA && + ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA1) { + ssh_set_error(sshbind, SSH_FATAL, + "The RSA host key has the wrong type"); + ssh_key_free(sshbind->rsa); + return SSH_ERROR; + } + } + + if (sshbind->bindfd == SSH_INVALID_SOCKET) { + host = sshbind->bindaddr; + if (host == NULL) { + host = "0.0.0.0"; + } + + fd = bind_socket(sshbind, host, sshbind->bindport); + if (fd == SSH_INVALID_SOCKET) { + ssh_key_free(sshbind->dsa); + ssh_key_free(sshbind->rsa); + return -1; + } + sshbind->bindfd = fd; + + if (listen(fd, 10) < 0) { + ssh_set_error(sshbind, SSH_FATAL, + "Listening to socket %d: %s", + fd, strerror(errno)); + close(fd); + ssh_key_free(sshbind->dsa); + ssh_key_free(sshbind->rsa); + return -1; + } + } else { + SSH_LOG(sshbind, SSH_LOG_INFO, "Using app-provided bind socket"); + } + return 0; +} + +int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, + void *userdata){ + if (sshbind == NULL) { + return SSH_ERROR; + } + if (callbacks == NULL) { + ssh_set_error_invalid(sshbind); + return SSH_ERROR; + } + if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){ + ssh_set_error(sshbind,SSH_FATAL, + "Invalid callback passed in (badly initialized)"); + return SSH_ERROR; + } + sshbind->bind_callbacks = callbacks; + sshbind->bind_callbacks_userdata=userdata; + return 0; +} + +/** @internal + * @brief callback being called by poll when an event happens + * + */ +static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, + socket_t fd, int revents, void *user){ + ssh_bind sshbind=(ssh_bind)user; + (void)sshpoll; + (void)fd; + + if(revents & POLLIN){ + /* new incoming connection */ + if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){ + sshbind->bind_callbacks->incoming_connection(sshbind, + sshbind->bind_callbacks_userdata); + } + } + return 0; +} + +/** @internal + * @brief returns the current poll handle, or create it + * @param sshbind the ssh_bind object + * @returns a ssh_poll handle suitable for operation + */ +ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind){ + if(sshbind->poll) + return sshbind->poll; + sshbind->poll=ssh_poll_new(sshbind->bindfd,POLLIN, + ssh_bind_poll_callback,sshbind); + return sshbind->poll; +} + +void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) { + sshbind->blocking = blocking ? 1 : 0; +} + +socket_t ssh_bind_get_fd(ssh_bind sshbind) { + return sshbind->bindfd; +} + +void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) { + sshbind->bindfd = fd; +} + +void ssh_bind_fd_toaccept(ssh_bind sshbind) { + sshbind->toaccept = 1; +} + +void ssh_bind_free(ssh_bind sshbind){ + int i; + + if (sshbind == NULL) { + return; + } + + if (sshbind->bindfd >= 0) { +#ifdef _WIN32 + closesocket(sshbind->bindfd); +#else + close(sshbind->bindfd); +#endif + } + sshbind->bindfd = SSH_INVALID_SOCKET; + + /* options */ + SAFE_FREE(sshbind->banner); + SAFE_FREE(sshbind->dsakey); + SAFE_FREE(sshbind->rsakey); + SAFE_FREE(sshbind->dsa); + SAFE_FREE(sshbind->rsa); + SAFE_FREE(sshbind->bindaddr); + + for (i = 0; i < 10; i++) { + if (sshbind->wanted_methods[i]) { + SAFE_FREE(sshbind->wanted_methods[i]); + } + } + + SAFE_FREE(sshbind); +} + +int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ + int i; + + if (session == NULL){ + ssh_set_error(sshbind, SSH_FATAL,"session is null"); + return SSH_ERROR; + } + + session->server = 1; + session->version = 2; + + /* copy options */ + for (i = 0; i < 10; ++i) { + if (sshbind->wanted_methods[i]) { + session->opts.wanted_methods[i] = strdup(sshbind->wanted_methods[i]); + if (session->opts.wanted_methods[i] == NULL) { + return SSH_ERROR; + } + } + } + + if (sshbind->bindaddr == NULL) + session->opts.bindaddr = NULL; + else { + SAFE_FREE(session->opts.bindaddr); + session->opts.bindaddr = strdup(sshbind->bindaddr); + if (session->opts.bindaddr == NULL) { + return SSH_ERROR; + } + } + + session->common.log_verbosity = sshbind->common.log_verbosity; + + ssh_socket_free(session->socket); + session->socket = ssh_socket_new(session); + if (session->socket == NULL) { + /* perhaps it may be better to copy the error from session to sshbind */ + ssh_set_error_oom(sshbind); + return SSH_ERROR; + } + ssh_socket_set_fd(session->socket, fd); + ssh_socket_get_poll_handle_out(session->socket); + +#ifdef HAVE_ECC + if (sshbind->ecdsa) { + session->srv.ecdsa_key = ssh_key_dup(sshbind->ecdsa); + if (session->srv.ecdsa_key == NULL) { + ssh_set_error_oom(sshbind); + return SSH_ERROR; + } + } +#endif + if (sshbind->dsa) { + session->srv.dsa_key = ssh_key_dup(sshbind->dsa); + if (session->srv.dsa_key == NULL) { + ssh_set_error_oom(sshbind); + return SSH_ERROR; + } + } + if (sshbind->rsa) { + session->srv.rsa_key = ssh_key_dup(sshbind->rsa); + if (session->srv.rsa_key == NULL) { + ssh_set_error_oom(sshbind); + return SSH_ERROR; + } + } + return SSH_OK; +} + +int ssh_bind_accept(ssh_bind sshbind, ssh_session session) { + socket_t fd = SSH_INVALID_SOCKET; + int rc; + if (sshbind->bindfd == SSH_INVALID_SOCKET) { + ssh_set_error(sshbind, SSH_FATAL, + "Can't accept new clients on a not bound socket."); + return SSH_ERROR; + } + + if (session == NULL){ + ssh_set_error(sshbind, SSH_FATAL,"session is null"); + return SSH_ERROR; + } + + fd = accept(sshbind->bindfd, NULL, NULL); + if (fd == SSH_INVALID_SOCKET) { + ssh_set_error(sshbind, SSH_FATAL, + "Accepting a new connection: %s", + strerror(errno)); + return SSH_ERROR; + } + rc = ssh_bind_accept_fd(sshbind, session, fd); + + if(rc == SSH_ERROR){ +#ifdef _WIN32 + closesocket(fd); +#else + close(fd); +#endif + if (session->socket) + ssh_socket_close(session->socket); + } + return rc; +} + + +/** + * @} + */ diff --git a/libssh/src/buffer.c b/libssh/src/buffer.c new file mode 100644 index 00000000..ca120868 --- /dev/null +++ b/libssh/src/buffer.c @@ -0,0 +1,626 @@ +/* + * buffer.c - buffer functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/buffer.h" + +/** + * @defgroup libssh_buffer The SSH buffer functions. + * @ingroup libssh + * + * Functions to handle SSH buffers. + * + * @{ + */ + + +#ifdef DEBUG_BUFFER +/** + * @internal + * + * @brief Check that preconditions and postconditions are valid. + * + * @param[in] buf The buffer to check. + */ +static void buffer_verify(ssh_buffer buf){ + int doabort=0; + if(buf->data == NULL) + return; + if(buf->used > buf->allocated){ + fprintf(stderr,"Buffer error : allocated %u, used %u\n",buf->allocated, buf->used); + doabort=1; + } + if(buf->pos > buf->used){ + fprintf(stderr,"Buffer error : position %u, used %u\n",buf->pos, buf->used); + doabort=1; + } + if(buf->pos > buf->allocated){ + fprintf(stderr,"Buffer error : position %u, allocated %u\n",buf->pos, buf->allocated); + doabort=1; + } + if(doabort) + abort(); +} + +#else +#define buffer_verify(x) +#endif + +/** + * @brief Create a new SSH buffer. + * + * @return A newly initialized SSH buffer, NULL on error. + */ +struct ssh_buffer_struct *ssh_buffer_new(void) { + struct ssh_buffer_struct *buf = malloc(sizeof(struct ssh_buffer_struct)); + + if (buf == NULL) { + return NULL; + } + memset(buf, 0, sizeof(struct ssh_buffer_struct)); + buffer_verify(buf); + return buf; +} + +/** + * @brief Deallocate a SSH buffer. + * + * \param[in] buffer The buffer to free. + */ +void ssh_buffer_free(struct ssh_buffer_struct *buffer) { + if (buffer == NULL) { + return; + } + buffer_verify(buffer); + + if (buffer->data) { + /* burn the data */ + memset(buffer->data, 0, buffer->allocated); + SAFE_FREE(buffer->data); + } + memset(buffer, 'X', sizeof(*buffer)); + SAFE_FREE(buffer); +} + +static int realloc_buffer(struct ssh_buffer_struct *buffer, size_t needed) { + size_t smallest = 1; + char *new; + + buffer_verify(buffer); + + /* Find the smallest power of two which is greater or equal to needed */ + while(smallest <= needed) { + if (smallest == 0) { + return -1; + } + smallest <<= 1; + } + needed = smallest; + new = realloc(buffer->data, needed); + if (new == NULL) { + return -1; + } + buffer->data = new; + buffer->allocated = needed; + buffer_verify(buffer); + return 0; +} + +/** @internal + * @brief shifts a buffer to remove unused data in the beginning + * @param buffer SSH buffer + */ +static void buffer_shift(ssh_buffer buffer){ + buffer_verify(buffer); + if(buffer->pos==0) + return; + memmove(buffer->data, buffer->data + buffer->pos, buffer->used - buffer->pos); + buffer->used -= buffer->pos; + buffer->pos=0; + buffer_verify(buffer); +} + +/** + * @internal + * + * @brief Reinitialize a SSH buffer. + * + * @param[in] buffer The buffer to reinitialize. + * + * @return 0 on success, < 0 on error. + */ +int buffer_reinit(struct ssh_buffer_struct *buffer) { + buffer_verify(buffer); + memset(buffer->data, 0, buffer->used); + buffer->used = 0; + buffer->pos = 0; + if(buffer->allocated > 127) { + if (realloc_buffer(buffer, 127) < 0) { + return -1; + } + } + buffer_verify(buffer); + return 0; +} + +/** + * @internal + * + * @brief Add data at the tail of a buffer. + * + * @param[in] buffer The buffer to add the data. + * + * @param[in] data A pointer to the data to add. + * + * @param[in] len The length of the data to add. + * + * @return 0 on success, < 0 on error. + */ +int buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint32_t len) { + buffer_verify(buffer); + + if (buffer->used + len < len) { + return -1; + } + + if (buffer->allocated < (buffer->used + len)) { + if(buffer->pos > 0) + buffer_shift(buffer); + if (realloc_buffer(buffer, buffer->used + len) < 0) { + return -1; + } + } + + memcpy(buffer->data+buffer->used, data, len); + buffer->used+=len; + buffer_verify(buffer); + return 0; +} + +/** + * @internal + * + * @brief Add a SSH string to the tail of a buffer. + * + * @param[in] buffer The buffer to add the string. + * + * @param[in] string The SSH String to add. + * + * @return 0 on success, < 0 on error. + */ +int buffer_add_ssh_string(struct ssh_buffer_struct *buffer, + struct ssh_string_struct *string) { + uint32_t len = 0; + + len = ssh_string_len(string); + if (buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) { + return -1; + } + + return 0; +} + +/** + * @internal + * + * @brief Add a 32 bits unsigned integer to the tail of a buffer. + * + * @param[in] buffer The buffer to add the integer. + * + * @param[in] data The 32 bits integer to add. + * + * @return 0 on success, -1 on error. + */ +int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data){ + if (buffer_add_data(buffer, &data, sizeof(data)) < 0) { + return -1; + } + + return 0; +} + +/** + * @internal + * + * @brief Add a 16 bits unsigned integer to the tail of a buffer. + * + * @param[in] buffer The buffer to add the integer. + * + * @param[in] data The 16 bits integer to add. + * + * @return 0 on success, -1 on error. + */ +int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data){ + if (buffer_add_data(buffer, &data, sizeof(data)) < 0) { + return -1; + } + + return 0; +} + +/** + * @internal + * + * @brief Add a 64 bits unsigned integer to the tail of a buffer. + * + * @param[in] buffer The buffer to add the integer. + * + * @param[in] data The 64 bits integer to add. + * + * @return 0 on success, -1 on error. + */ +int buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data){ + if (buffer_add_data(buffer, &data, sizeof(data)) < 0) { + return -1; + } + + return 0; +} + +/** + * @internal + * + * @brief Add a 8 bits unsigned integer to the tail of a buffer. + * + * @param[in] buffer The buffer to add the integer. + * + * @param[in] data The 8 bits integer to add. + * + * @return 0 on success, -1 on error. + */ +int buffer_add_u8(struct ssh_buffer_struct *buffer,uint8_t data){ + if (buffer_add_data(buffer, &data, sizeof(uint8_t)) < 0) { + return -1; + } + + return 0; +} + +/** + * @internal + * + * @brief Add data at the head of a buffer. + * + * @param[in] buffer The buffer to add the data. + * + * @param[in] data The data to prepend. + * + * @param[in] len The length of data to prepend. + * + * @return 0 on success, -1 on error. + */ +int buffer_prepend_data(struct ssh_buffer_struct *buffer, const void *data, + uint32_t len) { + buffer_verify(buffer); + + if(len <= buffer->pos){ + /* It's possible to insert data between begin and pos */ + memcpy(buffer->data + (buffer->pos - len), data, len); + buffer->pos -= len; + buffer_verify(buffer); + return 0; + } + /* pos isn't high enough */ + if (buffer->used - buffer->pos + len < len) { + return -1; + } + + if (buffer->allocated < (buffer->used - buffer->pos + len)) { + if (realloc_buffer(buffer, buffer->used - buffer->pos + len) < 0) { + return -1; + } + } + memmove(buffer->data + len, buffer->data + buffer->pos, buffer->used - buffer->pos); + memcpy(buffer->data, data, len); + buffer->used += len - buffer->pos; + buffer->pos = 0; + buffer_verify(buffer); + return 0; +} + +/** + * @internal + * + * @brief Append data from a buffer to the tail of another buffer. + * + * @param[in] buffer The destination buffer. + * + * @param[in] source The source buffer to append. It doesn't take the + * position of the buffer into account. + * + * @return 0 on success, -1 on error. + */ +int buffer_add_buffer(struct ssh_buffer_struct *buffer, + struct ssh_buffer_struct *source) { + if (buffer_add_data(buffer, buffer_get_rest(source), buffer_get_rest_len(source)) < 0) { + return -1; + } + + return 0; +} + +/** + * @brief Get a pointer on the head of a buffer. + * + * @param[in] buffer The buffer to get the head pointer. + * + * @return A data pointer on the head. It doesn't take the position + * into account. + * + * @warning Don't expect data to be nul-terminated. + * + * @see buffer_get_rest() + * @see buffer_get_len() + */ +void *ssh_buffer_get_begin(struct ssh_buffer_struct *buffer){ + return buffer->data; +} + +/** + * @internal + * + * @brief Get a pointer to the head of a buffer at the current position. + * + * @param[in] buffer The buffer to get the head pointer. + * + * @return A pointer to the data from current position. + * + * @see buffer_get_rest_len() + * @see buffer_get() + */ +void *buffer_get_rest(struct ssh_buffer_struct *buffer){ + return buffer->data + buffer->pos; +} + +/** + * @brief Get the length of the buffer, not counting position. + * + * @param[in] buffer The buffer to get the length from. + * + * @return The length of the buffer. + * + * @see buffer_get() + */ +uint32_t ssh_buffer_get_len(struct ssh_buffer_struct *buffer){ + return buffer->used; +} + +/** + * @internal + * + * @brief Get the length of the buffer from the current position. + * + * @param[in] buffer The buffer to get the length from. + * + * @return The length of the buffer. + * + * @see buffer_get_rest() + */ +uint32_t buffer_get_rest_len(struct ssh_buffer_struct *buffer){ + buffer_verify(buffer); + return buffer->used - buffer->pos; +} + +/** + * @internal + * + * @brief Advance the position in the buffer. + * + * This has effect to "eat" bytes at head of the buffer. + * + * @param[in] buffer The buffer to advance the position. + * + * @param[in] len The number of bytes to eat. + * + * @return The new size of the buffer. + */ +uint32_t buffer_pass_bytes(struct ssh_buffer_struct *buffer, uint32_t len){ + buffer_verify(buffer); + + if (buffer->pos + len < len || buffer->used < buffer->pos + len) { + return 0; + } + + buffer->pos+=len; + /* if the buffer is empty after having passed the whole bytes into it, we can clean it */ + if(buffer->pos==buffer->used){ + buffer->pos=0; + buffer->used=0; + } + buffer_verify(buffer); + return len; +} + +/** + * @internal + * + * @brief Cut the end of the buffer. + * + * @param[in] buffer The buffer to cut. + * + * @param[in] len The number of bytes to remove from the tail. + * + * @return The new size of the buffer. + */ +uint32_t buffer_pass_bytes_end(struct ssh_buffer_struct *buffer, uint32_t len){ + buffer_verify(buffer); + + if (buffer->used < len) { + return 0; + } + + buffer->used-=len; + buffer_verify(buffer); + return len; +} + +/** + * @internal + * + * @brief Get the remaining data out of the buffer and adjust the read pointer. + * + * @param[in] buffer The buffer to read. + * + * @param[in] data The data buffer where to store the data. + * + * @param[in] len The length to read from the buffer. + * + * @returns 0 if there is not enough data in buffer, len otherwise. + */ +uint32_t buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint32_t len){ + /* + * Check for a integer overflow first, then check if not enough data is in + * the buffer. + */ + if (buffer->pos + len < len || buffer->pos + len > buffer->used) { + return 0; + } + memcpy(data,buffer->data+buffer->pos,len); + buffer->pos+=len; + return len; /* no yet support for partial reads (is it really needed ?? ) */ +} + +/** + * @internal + * + * @brief Get a 8 bits unsigned int out of the buffer and adjusts the read + * pointer. + * + * @param[in] buffer The buffer to read. + * + * @param[in] data A pointer to a uint8_t where to store the data. + * + * @returns 0 if there is not enough data in buffer, 1 otherwise. + */ +int buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){ + return buffer_get_data(buffer,data,sizeof(uint8_t)); +} + +/** \internal + * \brief gets a 32 bits unsigned int out of the buffer. Adjusts the read pointer. + * \param buffer Buffer to read + * \param data pointer to a uint32_t where to store the data + * \returns 0 if there is not enough data in buffer + * \returns 4 otherwise. + */ +int buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){ + return buffer_get_data(buffer,data,sizeof(uint32_t)); +} +/** + * @internal + * + * @brief Get a 64 bits unsigned int out of the buffer and adjusts the read + * pointer. + * + * @param[in] buffer The buffer to read. + * + * @param[in] data A pointer to a uint64_t where to store the data. + * + * @returns 0 if there is not enough data in buffer, 8 otherwise. + */ +int buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){ + return buffer_get_data(buffer,data,sizeof(uint64_t)); +} + +/** + * @internal + * + * @brief Get a SSH String out of the buffer and adjusts the read pointer. + * + * @param[in] buffer The buffer to read. + * + * @returns The SSH String, NULL on error. + */ +struct ssh_string_struct *buffer_get_ssh_string(struct ssh_buffer_struct *buffer) { + uint32_t stringlen; + uint32_t hostlen; + struct ssh_string_struct *str = NULL; + + if (buffer_get_u32(buffer, &stringlen) == 0) { + return NULL; + } + hostlen = ntohl(stringlen); + /* verify if there is enough space in buffer to get it */ + if (buffer->pos + hostlen < hostlen || buffer->pos + hostlen > buffer->used) { + return NULL; /* it is indeed */ + } + str = ssh_string_new(hostlen); + if (str == NULL) { + return NULL; + } + if (buffer_get_data(buffer, ssh_string_data(str), hostlen) != hostlen) { + /* should never happen */ + SAFE_FREE(str); + return NULL; + } + + return str; +} + +/** + * @internal + * + * @brief Get a mpint out of the buffer and adjusts the read pointer. + * + * @note This function is SSH-1 only. + * + * @param[in] buffer The buffer to read. + * + * @returns The SSH String containing the mpint, NULL on error. + */ +struct ssh_string_struct *buffer_get_mpint(struct ssh_buffer_struct *buffer) { + uint16_t bits; + uint32_t len; + struct ssh_string_struct *str = NULL; + + if (buffer_get_data(buffer, &bits, sizeof(uint16_t)) != sizeof(uint16_t)) { + return NULL; + } + bits = ntohs(bits); + len = (bits + 7) / 8; + if (buffer->pos + len < len || buffer->pos + len > buffer->used) { + return NULL; + } + str = ssh_string_new(len); + if (str == NULL) { + return NULL; + } + if (buffer_get_data(buffer, ssh_string_data(str), len) != len) { + SAFE_FREE(str); + return NULL; + } + return str; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/callbacks.c b/libssh/src/callbacks.c new file mode 100644 index 00000000..5a61180d --- /dev/null +++ b/libssh/src/callbacks.c @@ -0,0 +1,61 @@ +/* + * callbacks.c - callback functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include "libssh/callbacks.h" +#include "libssh/session.h" + +int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) { + if (session == NULL || cb == NULL) { + return SSH_ERROR; + } + enter_function(); + if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ + ssh_set_error(session,SSH_FATAL, + "Invalid callback passed in (badly initialized)"); + leave_function(); + return SSH_ERROR; + } + session->common.callbacks = cb; + leave_function(); + return 0; +} + +int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb) { + ssh_session session = NULL; + if (channel == NULL || cb == NULL) { + return SSH_ERROR; + } + session = channel->session; + enter_function(); + if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ + ssh_set_error(session,SSH_FATAL, + "Invalid channel callback passed in (badly initialized)"); + leave_function(); + return SSH_ERROR; + } + channel->callbacks = cb; + leave_function(); + return 0; +} diff --git a/libssh/src/channels.c b/libssh/src/channels.c new file mode 100644 index 00000000..6e9b2eb0 --- /dev/null +++ b/libssh/src/channels.c @@ -0,0 +1,3461 @@ +/* + * channels.c - SSH channel functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * Copyright (c) 2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/ssh2.h" +#include "libssh/buffer.h" +#include "libssh/packet.h" +#include "libssh/socket.h" +#include "libssh/channels.h" +#include "libssh/session.h" +#include "libssh/misc.h" +#include "libssh/messages.h" +#if WITH_SERVER +#include "libssh/server.h" +#endif + +#define WINDOWBASE 1280000 +#define WINDOWLIMIT (WINDOWBASE/2) + +/* + * All implementations MUST be able to process packets with an + * uncompressed payload length of 32768 bytes or less and a total packet + * size of 35000 bytes or less. + */ +#define CHANNEL_MAX_PACKET 32768 +#define CHANNEL_INITIAL_WINDOW 64000 + +/** + * @defgroup libssh_channel The SSH channel functions + * @ingroup libssh + * + * Functions that manage a SSH channel. + * + * @{ + */ + +static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet); + +/** + * @brief Allocate a new channel. + * + * @param[in] session The ssh session to use. + * + * @return A pointer to a newly allocated channel, NULL on error. + */ +ssh_channel ssh_channel_new(ssh_session session) { + ssh_channel channel = NULL; + + if(session == NULL) { + return NULL; + } + + channel = malloc(sizeof(struct ssh_channel_struct)); + if (channel == NULL) { + ssh_set_error_oom(session); + return NULL; + } + memset(channel,0,sizeof(struct ssh_channel_struct)); + + channel->stdout_buffer = ssh_buffer_new(); + if (channel->stdout_buffer == NULL) { + ssh_set_error_oom(session); + SAFE_FREE(channel); + return NULL; + } + + channel->stderr_buffer = ssh_buffer_new(); + if (channel->stderr_buffer == NULL) { + ssh_set_error_oom(session); + ssh_buffer_free(channel->stdout_buffer); + SAFE_FREE(channel); + return NULL; + } + + channel->session = session; + channel->version = session->version; + channel->exit_status = -1; + channel->flags = SSH_CHANNEL_FLAG_NOT_BOUND; + + if(session->channels == NULL) { + session->channels = ssh_list_new(); + } + ssh_list_prepend(session->channels, channel); + return channel; +} + +/** + * @internal + * + * @brief Create a new channel identifier. + * + * @param[in] session The SSH session to use. + * + * @return The new channel identifier. + */ +uint32_t ssh_channel_new_id(ssh_session session) { + return ++(session->maxchannel); +} + +/** + * @internal + * + * @brief Handle a SSH_PACKET_CHANNEL_OPEN_CONFIRMATION packet. + * + * Constructs the channel object. + */ +SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ + uint32_t channelid=0; + uint32_t tmp; + ssh_channel channel; + (void)type; + (void)user; + enter_function(); + ssh_log(session,SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"); + + buffer_get_u32(packet, &channelid); + channelid=ntohl(channelid); + channel=ssh_channel_from_local(session,channelid); + if(channel==NULL){ + ssh_set_error(session, SSH_FATAL, + "Unknown channel id %lu", + (long unsigned int) channelid); + /* TODO: Set error marking in channel object */ + leave_function(); + return SSH_PACKET_USED; + } + + buffer_get_u32(packet, &tmp); + channel->remote_channel = ntohl(tmp); + + buffer_get_u32(packet, &tmp); + channel->remote_window = ntohl(tmp); + + buffer_get_u32(packet,&tmp); + channel->remote_maxpacket=ntohl(tmp); + + ssh_log(session, SSH_LOG_PROTOCOL, + "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d", + channel->local_channel, + channel->remote_channel); + ssh_log(session, SSH_LOG_PROTOCOL, + "Remote window : %lu, maxpacket : %lu", + (long unsigned int) channel->remote_window, + (long unsigned int) channel->remote_maxpacket); + + channel->state = SSH_CHANNEL_STATE_OPEN; + channel->flags = channel->flags & ~SSH_CHANNEL_FLAG_NOT_BOUND; + leave_function(); + return SSH_PACKET_USED; +} + +/** + * @internal + * + * @brief Handle a SSH_CHANNEL_OPEN_FAILURE and set the state of the channel. + */ +SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){ + + ssh_channel channel; + ssh_string error_s; + char *error = NULL; + uint32_t code; + (void)user; + (void)type; + channel=channel_from_msg(session,packet); + if(channel==NULL){ + ssh_log(session,SSH_LOG_RARE,"Invalid channel in packet"); + return SSH_PACKET_USED; + } + buffer_get_u32(packet, &code); + + error_s = buffer_get_ssh_string(packet); + if(error_s != NULL) + error = ssh_string_to_char(error_s); + ssh_string_free(error_s); + if (error == NULL) { + ssh_set_error_oom(session); + return SSH_PACKET_USED; + } + + ssh_set_error(session, SSH_REQUEST_DENIED, + "Channel opening failure: channel %u error (%lu) %s", + channel->local_channel, + (long unsigned int) ntohl(code), + error); + SAFE_FREE(error); + channel->state=SSH_CHANNEL_STATE_OPEN_DENIED; + return SSH_PACKET_USED; +} + +static int ssh_channel_open_termination(void *c){ + ssh_channel channel = (ssh_channel) c; + if (channel->state != SSH_CHANNEL_STATE_OPENING || + channel->session->session_state == SSH_SESSION_STATE_ERROR) + return 1; + else + return 0; +} + +/** + * @internal + * + * @brief Open a channel by sending a SSH_OPEN_CHANNEL message and + * wait for the reply. + * + * @param[in] channel The current channel. + * + * @param[in] type_c A C string describing the kind of channel (e.g. "exec"). + * + * @param[in] window The receiving window of the channel. The window is the + * maximum size of data that can stay in buffers and + * network. + * + * @param[in] maxpacket The maximum packet size allowed (like MTU). + * + * @param[in] payload The buffer containing additional payload for the query. + */ +static int channel_open(ssh_channel channel, const char *type_c, int window, + int maxpacket, ssh_buffer payload) { + ssh_session session = channel->session; + ssh_string type = NULL; + int err=SSH_ERROR; + + enter_function(); + switch(channel->state){ + case SSH_CHANNEL_STATE_NOT_OPEN: + break; + case SSH_CHANNEL_STATE_OPENING: + goto pending; + case SSH_CHANNEL_STATE_OPEN: + case SSH_CHANNEL_STATE_CLOSED: + case SSH_CHANNEL_STATE_OPEN_DENIED: + goto end; + default: + ssh_set_error(session,SSH_FATAL,"Bad state in channel_open: %d",channel->state); + } + channel->local_channel = ssh_channel_new_id(session); + channel->local_maxpacket = maxpacket; + channel->local_window = window; + + ssh_log(session, SSH_LOG_PROTOCOL, + "Creating a channel %d with %d window and %d max packet", + channel->local_channel, window, maxpacket); + + type = ssh_string_from_char(type_c); + if (type == NULL) { + ssh_set_error_oom(session); + leave_function(); + return err; + } + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN) < 0 || + buffer_add_ssh_string(session->out_buffer,type) < 0 || + buffer_add_u32(session->out_buffer, htonl(channel->local_channel)) < 0 || + buffer_add_u32(session->out_buffer, htonl(channel->local_window)) < 0 || + buffer_add_u32(session->out_buffer, htonl(channel->local_maxpacket)) < 0) { + ssh_set_error_oom(session); + ssh_string_free(type); + leave_function(); + return err; + } + + ssh_string_free(type); + + if (payload != NULL) { + if (buffer_add_buffer(session->out_buffer, payload) < 0) { + ssh_set_error_oom(session); + leave_function(); + return err; + } + } + channel->state = SSH_CHANNEL_STATE_OPENING; + if (packet_send(session) == SSH_ERROR) { + leave_function(); + return err; + } + + ssh_log(session, SSH_LOG_PACKET, + "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d", + type_c, channel->local_channel); +pending: + /* wait until channel is opened by server */ + err = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, ssh_channel_open_termination, channel); + if (err != SSH_OK || session->session_state == SSH_SESSION_STATE_ERROR) + err = SSH_ERROR; +end: + if(channel->state == SSH_CHANNEL_STATE_OPEN) + err=SSH_OK; + leave_function(); + return err; +} + +/* return channel with corresponding local id, or NULL if not found */ +ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) { + struct ssh_iterator *it; + ssh_channel channel; + + for (it = ssh_list_get_iterator(session->channels); it != NULL ; it=it->next) { + channel = ssh_iterator_value(ssh_channel, it); + if (channel == NULL) { + continue; + } + if (channel->local_channel == id) { + return channel; + } + } + + return NULL; +} + +/** + * @internal + * @brief grows the local window and send a packet to the other party + * @param session SSH session + * @param channel SSH channel + * @param minimumsize The minimum acceptable size for the new window. + */ +static int grow_window(ssh_session session, ssh_channel channel, int minimumsize) { + uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE; + + enter_function(); +#ifdef WITH_SSH1 + if (session->version == 1){ + channel->remote_window = new_window; + leave_function(); + return SSH_OK; + } +#endif + if(new_window <= channel->local_window){ + ssh_log(session,SSH_LOG_PROTOCOL, + "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)", + channel->local_channel, channel->remote_channel, new_window, + channel->local_window); + leave_function(); + return SSH_OK; + } + /* WINDOW_ADJUST packet needs a relative increment rather than an absolute + * value, so we give here the missing bytes needed to reach new_window + */ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_WINDOW_ADJUST) < 0 || + buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 || + buffer_add_u32(session->out_buffer, htonl(new_window - channel->local_window)) < 0) { + ssh_set_error_oom(session); + goto error; + } + + if (packet_send(session) == SSH_ERROR) { + goto error; + } + + ssh_log(session, SSH_LOG_PROTOCOL, + "growing window (channel %d:%d) to %d bytes", + channel->local_channel, + channel->remote_channel, + new_window); + + channel->local_window = new_window; + + leave_function(); + return SSH_OK; +error: + buffer_reinit(session->out_buffer); + + leave_function(); + return SSH_ERROR; +} + +/** + * @internal + * + * @brief Parse a channel-related packet to resolve it to a ssh_channel. + * + * This works on SSH1 sessions too. + * + * @param[in] session The current SSH session. + * + * @param[in] packet The buffer to parse packet from. The read pointer will + * be moved after the call. + * + * @returns The related ssh_channel, or NULL if the channel is + * unknown or the packet is invalid. + */ +static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) { + ssh_channel channel; + uint32_t chan; +#ifdef WITH_SSH1 + /* With SSH1, the channel is always the first one */ + if(session->version==1) + return ssh_get_channel1(session); +#endif + if (buffer_get_u32(packet, &chan) != sizeof(uint32_t)) { + ssh_set_error(session, SSH_FATAL, + "Getting channel from message: short read"); + return NULL; + } + + channel = ssh_channel_from_local(session, ntohl(chan)); + if (channel == NULL) { + ssh_set_error(session, SSH_FATAL, + "Server specified invalid channel %lu", + (long unsigned int) ntohl(chan)); + } + + return channel; +} + +SSH_PACKET_CALLBACK(channel_rcv_change_window) { + ssh_channel channel; + uint32_t bytes; + int rc; + (void)user; + (void)type; + enter_function(); + + channel = channel_from_msg(session,packet); + if (channel == NULL) { + ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + } + + rc = buffer_get_u32(packet, &bytes); + if (channel == NULL || rc != sizeof(uint32_t)) { + ssh_log(session, SSH_LOG_PACKET, + "Error getting a window adjust message: invalid packet"); + leave_function(); + return SSH_PACKET_USED; + } + + bytes = ntohl(bytes); + ssh_log(session, SSH_LOG_PROTOCOL, + "Adding %d bytes to channel (%d:%d) (from %d bytes)", + bytes, + channel->local_channel, + channel->remote_channel, + channel->remote_window); + + channel->remote_window += bytes; + + leave_function(); + return SSH_PACKET_USED; +} + +/* is_stderr is set to 1 if the data are extended, ie stderr */ +SSH_PACKET_CALLBACK(channel_rcv_data){ + ssh_channel channel; + ssh_string str; + ssh_buffer buf; + size_t len; + int is_stderr; + int rest; + (void)user; + enter_function(); + if(type==SSH2_MSG_CHANNEL_DATA) + is_stderr=0; + else + is_stderr=1; + + channel = channel_from_msg(session,packet); + if (channel == NULL) { + ssh_log(session, SSH_LOG_FUNCTIONS, + "%s", ssh_get_error(session)); + leave_function(); + return SSH_PACKET_USED; + } + + if (is_stderr) { + uint32_t ignore; + /* uint32 data type code. we can ignore it */ + buffer_get_u32(packet, &ignore); + } + + str = buffer_get_ssh_string(packet); + if (str == NULL) { + ssh_log(session, SSH_LOG_PACKET, "Invalid data packet!"); + leave_function(); + return SSH_PACKET_USED; + } + len = ssh_string_len(str); + + ssh_log(session, SSH_LOG_PROTOCOL, + "Channel receiving %" PRIdS " bytes data in %d (local win=%d remote win=%d)", + len, + is_stderr, + channel->local_window, + channel->remote_window); + + /* What shall we do in this case? Let's accept it anyway */ + if (len > channel->local_window) { + ssh_log(session, SSH_LOG_RARE, + "Data packet too big for our window(%" PRIdS " vs %d)", + len, + channel->local_window); + } + + if (channel_default_bufferize(channel, ssh_string_data(str), len, + is_stderr) < 0) { + ssh_string_free(str); + leave_function(); + return SSH_PACKET_USED; + } + + if (len <= channel->local_window) { + channel->local_window -= len; + } else { + channel->local_window = 0; /* buggy remote */ + } + + ssh_log(session, SSH_LOG_PROTOCOL, + "Channel windows are now (local win=%d remote win=%d)", + channel->local_window, + channel->remote_window); + + ssh_string_free(str); + + if(ssh_callbacks_exists(channel->callbacks, channel_data_function)) { + if(is_stderr) { + buf = channel->stderr_buffer; + } else { + buf = channel->stdout_buffer; + } + rest = channel->callbacks->channel_data_function(channel->session, + channel, + buffer_get_rest(buf), + buffer_get_rest_len(buf), + is_stderr, + channel->callbacks->userdata); + if(rest > 0) { + buffer_pass_bytes(buf, rest); + } + if (channel->local_window + buffer_get_rest_len(buf) < WINDOWLIMIT) { + if (grow_window(session, channel, 0) < 0) { + leave_function(); + return -1; + } + } + } + + leave_function(); + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(channel_rcv_eof) { + ssh_channel channel; + (void)user; + (void)type; + enter_function(); + + channel = channel_from_msg(session,packet); + if (channel == NULL) { + ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + leave_function(); + return SSH_PACKET_USED; + } + + ssh_log(session, SSH_LOG_PACKET, + "Received eof on channel (%d:%d)", + channel->local_channel, + channel->remote_channel); + /* channel->remote_window = 0; */ + channel->remote_eof = 1; + + if(ssh_callbacks_exists(channel->callbacks, channel_eof_function)) { + channel->callbacks->channel_eof_function(channel->session, + channel, + channel->callbacks->userdata); + } + + leave_function(); + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(channel_rcv_close) { + ssh_channel channel; + (void)user; + (void)type; + enter_function(); + + channel = channel_from_msg(session,packet); + if (channel == NULL) { + ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + leave_function(); + return SSH_PACKET_USED; + } + + ssh_log(session, SSH_LOG_PACKET, + "Received close on channel (%d:%d)", + channel->local_channel, + channel->remote_channel); + + if ((channel->stdout_buffer && + buffer_get_rest_len(channel->stdout_buffer) > 0) || + (channel->stderr_buffer && + buffer_get_rest_len(channel->stderr_buffer) > 0)) { + channel->delayed_close = 1; + } else { + channel->state = SSH_CHANNEL_STATE_CLOSED; + } + if (channel->remote_eof == 0) { + ssh_log(session, SSH_LOG_PACKET, + "Remote host not polite enough to send an eof before close"); + } + channel->remote_eof = 1; + /* + * The remote eof doesn't break things if there was still data into read + * buffer because the eof is ignored until the buffer is empty. + */ + + if(ssh_callbacks_exists(channel->callbacks, channel_close_function)) { + channel->callbacks->channel_close_function(channel->session, + channel, + channel->callbacks->userdata); + } + channel->flags &= SSH_CHANNEL_FLAG_CLOSED_REMOTE; + if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL) + ssh_channel_do_free(channel); + leave_function(); + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(channel_rcv_request) { + ssh_channel channel; + ssh_string request_s; + char *request; + uint8_t status; + int rc; + (void)user; + (void)type; + + enter_function(); + + channel = channel_from_msg(session,packet); + if (channel == NULL) { + ssh_log(session, SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session)); + leave_function(); + return SSH_PACKET_USED; + } + + request_s = buffer_get_ssh_string(packet); + if (request_s == NULL) { + ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + leave_function(); + return SSH_PACKET_USED; + } + + request = ssh_string_to_char(request_s); + ssh_string_free(request_s); + if (request == NULL) { + leave_function(); + return SSH_PACKET_USED; + } + + buffer_get_u8(packet, (uint8_t *) &status); + + if (strcmp(request,"exit-status") == 0) { + uint32_t exit_status = 0; + + SAFE_FREE(request); + buffer_get_u32(packet, &exit_status); + channel->exit_status = ntohl(exit_status); + ssh_log(session, SSH_LOG_PACKET, "received exit-status %d", channel->exit_status); + + if(ssh_callbacks_exists(channel->callbacks, channel_exit_status_function)) { + channel->callbacks->channel_exit_status_function(channel->session, + channel, + channel->exit_status, + channel->callbacks->userdata); + } + + leave_function(); + return SSH_PACKET_USED; + } + + if (strcmp(request,"signal") == 0) { + ssh_string signal_str; + char *sig; + + SAFE_FREE(request); + ssh_log(session, SSH_LOG_PACKET, "received signal"); + + signal_str = buffer_get_ssh_string(packet); + if (signal_str == NULL) { + ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + leave_function(); + return SSH_PACKET_USED; + } + + sig = ssh_string_to_char(signal_str); + ssh_string_free(signal_str); + if (sig == NULL) { + leave_function(); + return SSH_PACKET_USED; + } + + + ssh_log(session, SSH_LOG_PACKET, + "Remote connection sent a signal SIG %s", sig); + if(ssh_callbacks_exists(channel->callbacks, channel_signal_function)) { + channel->callbacks->channel_signal_function(channel->session, + channel, + sig, + channel->callbacks->userdata); + } + SAFE_FREE(sig); + + leave_function(); + return SSH_PACKET_USED; + } + + if (strcmp(request, "exit-signal") == 0) { + const char *core = "(core dumped)"; + ssh_string tmp; + char *sig; + char *errmsg = NULL; + char *lang = NULL; + uint8_t i; + + SAFE_FREE(request); + + tmp = buffer_get_ssh_string(packet); + if (tmp == NULL) { + ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + leave_function(); + return SSH_PACKET_USED; + } + + sig = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (sig == NULL) { + leave_function(); + return SSH_PACKET_USED; + } + + buffer_get_u8(packet, &i); + if (i == 0) { + core = ""; + } + + tmp = buffer_get_ssh_string(packet); + if (tmp == NULL) { + ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + SAFE_FREE(sig); + leave_function(); + return SSH_PACKET_USED; + } + + errmsg = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (errmsg == NULL) { + SAFE_FREE(sig); + leave_function(); + return SSH_PACKET_USED; + } + + tmp = buffer_get_ssh_string(packet); + if (tmp == NULL) { + ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + SAFE_FREE(errmsg); + SAFE_FREE(sig); + leave_function(); + return SSH_PACKET_USED; + } + + lang = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (lang == NULL) { + SAFE_FREE(errmsg); + SAFE_FREE(sig); + leave_function(); + return SSH_PACKET_USED; + } + + ssh_log(session, SSH_LOG_PACKET, + "Remote connection closed by signal SIG %s %s", sig, core); + if(ssh_callbacks_exists(channel->callbacks, channel_exit_signal_function)) { + channel->callbacks->channel_exit_signal_function(channel->session, + channel, + sig, i, errmsg, lang, + channel->callbacks->userdata); + } + + SAFE_FREE(lang); + SAFE_FREE(errmsg); + SAFE_FREE(sig); + + leave_function(); + return SSH_PACKET_USED; + } + if(strcmp(request,"keepalive@openssh.com")==0){ + SAFE_FREE(request); + ssh_log(session, SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive"); + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_FAILURE); + if (rc < 0) { + return SSH_PACKET_USED; + } + rc = buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)); + if (rc < 0) { + return SSH_PACKET_USED; + } + packet_send(session); + leave_function(); + return SSH_PACKET_USED; + } + + /* If we are here, that means we have a request that is not in the understood + * client requests. That means we need to create a ssh message to be passed + * to the user code handling ssh messages + */ + ssh_message_handle_channel_request(session,channel,packet,request,status); + + SAFE_FREE(request); + + leave_function(); + return SSH_PACKET_USED; +} + +/* + * When data has been received from the ssh server, it can be applied to the + * known user function, with help of the callback, or inserted here + * + * FIXME is the window changed? + */ +int channel_default_bufferize(ssh_channel channel, void *data, int len, + int is_stderr) { + ssh_session session; + + if(channel == NULL) { + return -1; + } + + session = channel->session; + + if(data == NULL) { + ssh_set_error_invalid(session); + return -1; + } + + ssh_log(session, SSH_LOG_RARE, + "placing %d bytes into channel buffer (stderr=%d)", len, is_stderr); + if (is_stderr == 0) { + /* stdout */ + if (channel->stdout_buffer == NULL) { + channel->stdout_buffer = ssh_buffer_new(); + if (channel->stdout_buffer == NULL) { + ssh_set_error_oom(session); + return -1; + } + } + + if (buffer_add_data(channel->stdout_buffer, data, len) < 0) { + ssh_set_error_oom(session); + ssh_buffer_free(channel->stdout_buffer); + channel->stdout_buffer = NULL; + return -1; + } + } else { + /* stderr */ + if (channel->stderr_buffer == NULL) { + channel->stderr_buffer = ssh_buffer_new(); + if (channel->stderr_buffer == NULL) { + ssh_set_error_oom(session); + return -1; + } + } + + if (buffer_add_data(channel->stderr_buffer, data, len) < 0) { + ssh_set_error_oom(session); + ssh_buffer_free(channel->stderr_buffer); + channel->stderr_buffer = NULL; + return -1; + } + } + + return 0; +} + +/** + * @brief Open a session channel (suited for a shell, not TCP forwarding). + * + * @param[in] channel An allocated channel. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * + * @see channel_open_forward() + * @see channel_request_env() + * @see channel_request_shell() + * @see channel_request_exec() + */ +int ssh_channel_open_session(ssh_channel channel) { + if(channel == NULL) { + return SSH_ERROR; + } + +#ifdef WITH_SSH1 + if (channel->session->version == 1) { + return channel_open_session1(channel); + } +#endif + + return channel_open(channel, + "session", + CHANNEL_INITIAL_WINDOW, + CHANNEL_MAX_PACKET, + NULL); +} + +/** + * @brief Open a TCP/IP forwarding channel. + * + * @param[in] channel An allocated channel. + * + * @param[in] remotehost The remote host to connected (host name or IP). + * + * @param[in] remoteport The remote port. + * + * @param[in] sourcehost The numeric IP address of the machine from where the + * connection request originates. This is mostly for + * logging purposes. + * + * @param[in] localport The port on the host from where the connection + * originated. This is mostly for logging purposes. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * + * @warning This function does not bind the local port and does not automatically + * forward the content of a socket to the channel. You still have to + * use channel_read and channel_write for this. + */ +int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, + int remoteport, const char *sourcehost, int localport) { + ssh_session session; + ssh_buffer payload = NULL; + ssh_string str = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return rc; + } + + session = channel->session; + + if(remotehost == NULL || sourcehost == NULL) { + ssh_set_error_invalid(session); + return rc; + } + + enter_function(); + + payload = ssh_buffer_new(); + if (payload == NULL) { + ssh_set_error_oom(session); + goto error; + } + str = ssh_string_from_char(remotehost); + if (str == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_ssh_string(payload, str) < 0 || + buffer_add_u32(payload,htonl(remoteport)) < 0) { + ssh_set_error_oom(session); + goto error; + } + + ssh_string_free(str); + str = ssh_string_from_char(sourcehost); + if (str == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_ssh_string(payload, str) < 0 || + buffer_add_u32(payload,htonl(localport)) < 0) { + ssh_set_error_oom(session); + goto error; + } + + rc = channel_open(channel, + "direct-tcpip", + CHANNEL_INITIAL_WINDOW, + CHANNEL_MAX_PACKET, + payload); + +error: + ssh_buffer_free(payload); + ssh_string_free(str); + + leave_function(); + return rc; +} + + +/** + * @brief Close and free a channel. + * + * @param[in] channel The channel to free. + * + * @warning Any data unread on this channel will be lost. + */ +void ssh_channel_free(ssh_channel channel) { + ssh_session session; + + if (channel == NULL) { + return; + } + + session = channel->session; + enter_function(); + + if (session->alive && channel->state == SSH_CHANNEL_STATE_OPEN) { + ssh_channel_close(channel); + } + channel->flags &= SSH_CHANNEL_FLAG_FREED_LOCAL; + + /* The idea behind the flags is the following : it is well possible + * that a client closes a channel that stills exists on the server side. + * We definitively close the channel when we receive a close message *and* + * the user closed it. + */ + if((channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) + || (channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)){ + ssh_channel_do_free(channel); + } + leave_function(); +} + +/** + * @internal + * @brief Effectively free a channel, without caring about flags + */ + +void ssh_channel_do_free(ssh_channel channel){ + struct ssh_iterator *it; + ssh_session session = channel->session; + it = ssh_list_find(session->channels, channel); + if(it != NULL){ + ssh_list_remove(session->channels, it); + } + ssh_buffer_free(channel->stdout_buffer); + ssh_buffer_free(channel->stderr_buffer); + + /* debug trick to catch use after frees */ + memset(channel, 'X', sizeof(struct ssh_channel_struct)); + SAFE_FREE(channel); +} + +/** + * @brief Send an end of file on the channel. + * + * This doesn't close the channel. You may still read from it but not write. + * + * @param[in] channel The channel to send the eof to. + * + * @return SSH_OK on success, SSH_ERROR if an error occurred. + * + * @see channel_close() + * @see channel_free() + */ +int ssh_channel_send_eof(ssh_channel channel){ + ssh_session session; + int rc = SSH_ERROR; + + if(channel == NULL) { + return rc; + } + + session = channel->session; + enter_function(); + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_EOF) < 0) { + ssh_set_error_oom(session); + goto error; + } + if (buffer_add_u32(session->out_buffer,htonl(channel->remote_channel)) < 0) { + ssh_set_error_oom(session); + goto error; + } + rc = packet_send(session); + ssh_log(session, SSH_LOG_PACKET, + "Sent a EOF on client channel (%d:%d)", + channel->local_channel, + channel->remote_channel); + + channel->local_eof = 1; + + leave_function(); + return rc; +error: + buffer_reinit(session->out_buffer); + + leave_function(); + return rc; +} + +/** + * @brief Close a channel. + * + * This sends an end of file and then closes the channel. You won't be able + * to recover any data the server was going to send or was in buffers. + * + * @param[in] channel The channel to close. + * + * @return SSH_OK on success, SSH_ERROR if an error occurred. + * + * @see channel_free() + * @see channel_eof() + */ +int ssh_channel_close(ssh_channel channel){ + ssh_session session; + int rc = 0; + + if(channel == NULL) { + return SSH_ERROR; + } + + session = channel->session; + enter_function(); + + if (channel->local_eof == 0) { + rc = ssh_channel_send_eof(channel); + } + + if (rc != SSH_OK) { + leave_function(); + return rc; + } + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_CLOSE) < 0 || + buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0) { + ssh_set_error_oom(session); + goto error; + } + + rc = packet_send(session); + ssh_log(session, SSH_LOG_PACKET, + "Sent a close on client channel (%d:%d)", + channel->local_channel, + channel->remote_channel); + + if(rc == SSH_OK) { + channel->state=SSH_CHANNEL_STATE_CLOSED; + } + + leave_function(); + return rc; +error: + buffer_reinit(session->out_buffer); + + leave_function(); + return rc; +} + +/* this termination function waits for a window growing condition */ +static int ssh_channel_waitwindow_termination(void *c){ + ssh_channel channel = (ssh_channel) c; + if (channel->remote_window > 0 || + channel->session->session_state == SSH_SESSION_STATE_ERROR) + return 1; + else + return 0; +} + +/* This termination function waits until the session is not in blocked status + * anymore, e.g. because of a key re-exchange. + */ +static int ssh_waitsession_unblocked(void *s){ + ssh_session session = (ssh_session)s; + switch (session->session_state){ + case SSH_SESSION_STATE_DH: + case SSH_SESSION_STATE_INITIAL_KEX: + case SSH_SESSION_STATE_KEXINIT_RECEIVED: + return 0; + default: + return 1; + } +} +/** + * @internal + * @brief Flushes a channel (and its session) until the output buffer + * is empty, or timeout elapsed. + * @param channel SSH channel + * @returns SSH_OK On success, + * SSH_ERROR on error + * SSH_AGAIN Timeout elapsed (or in nonblocking mode) + */ +int ssh_channel_flush(ssh_channel channel){ + return ssh_blocking_flush(channel->session, SSH_TIMEOUT_USER); +} + +int channel_write_common(ssh_channel channel, const void *data, + uint32_t len, int is_stderr) { + ssh_session session; + uint32_t origlen = len; + size_t effectivelen; + size_t maxpacketlen; + int rc; + + if(channel == NULL) { + return -1; + } + session = channel->session; + if(data == NULL) { + ssh_set_error_invalid(session); + return -1; + } + + if (len > INT_MAX) { + ssh_log(session, SSH_LOG_PROTOCOL, + "Length (%u) is bigger than INT_MAX", len); + return SSH_ERROR; + } + + enter_function(); + + /* + * Handle the max packet len from remote side, be nice + * 10 bytes for the headers + */ + maxpacketlen = channel->remote_maxpacket - 10; + + if (channel->local_eof) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Can't write to channel %d:%d after EOF was sent", + channel->local_channel, + channel->remote_channel); + leave_function(); + return -1; + } + + if (channel->state != SSH_CHANNEL_STATE_OPEN || channel->delayed_close != 0) { + ssh_set_error(session, SSH_REQUEST_DENIED, "Remote channel is closed"); + leave_function(); + return -1; + } + if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ + leave_function(); + return SSH_ERROR; + } +#ifdef WITH_SSH1 + if (channel->version == 1) { + rc = channel_write1(channel, data, len); + leave_function(); + return rc; + } +#endif + if (ssh_waitsession_unblocked(session) == 0){ + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + ssh_waitsession_unblocked, session); + if (rc == SSH_ERROR || !ssh_waitsession_unblocked(session)) + goto out; + } + while (len > 0) { + if (channel->remote_window < len) { + ssh_log(session, SSH_LOG_PROTOCOL, + "Remote window is %d bytes. going to write %d bytes", + channel->remote_window, + len); + /* What happens when the channel window is zero? */ + if(channel->remote_window == 0) { + /* nothing can be written */ + ssh_log(session, SSH_LOG_PROTOCOL, + "Wait for a growing window message..."); + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + ssh_channel_waitwindow_termination,channel); + if (rc == SSH_ERROR || !ssh_channel_waitwindow_termination(channel)) + goto out; + continue; + } + effectivelen = len > channel->remote_window ? channel->remote_window : len; + } else { + effectivelen = len; + } + effectivelen = effectivelen > maxpacketlen ? maxpacketlen : effectivelen; + if (buffer_add_u8(session->out_buffer, is_stderr ? + SSH2_MSG_CHANNEL_EXTENDED_DATA : SSH2_MSG_CHANNEL_DATA) < 0 || + buffer_add_u32(session->out_buffer, + htonl(channel->remote_channel)) < 0) { + ssh_set_error_oom(session); + goto error; + } + /* stderr message has an extra field */ + if (is_stderr && + buffer_add_u32(session->out_buffer, htonl(SSH2_EXTENDED_DATA_STDERR)) < 0) { + ssh_set_error_oom(session); + goto error; + } + /* append payload data */ + if (buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 || + buffer_add_data(session->out_buffer, data, effectivelen) < 0) { + ssh_set_error_oom(session); + goto error; + } + + if (packet_send(session) == SSH_ERROR) { + leave_function(); + return SSH_ERROR; + } + + ssh_log(session, SSH_LOG_RARE, + "channel_write wrote %ld bytes", (long int) effectivelen); + + channel->remote_window -= effectivelen; + len -= effectivelen; + data = ((uint8_t*)data + effectivelen); + } + /* it's a good idea to flush the socket now */ + rc = ssh_channel_flush(channel); + if(rc == SSH_ERROR) + goto error; +out: + leave_function(); + return (int)(origlen - len); + +error: + buffer_reinit(session->out_buffer); + + leave_function(); + return SSH_ERROR; +} + +uint32_t ssh_channel_window_size(ssh_channel channel) { + return channel->remote_window; +} + +/** + * @brief Blocking write on a channel. + * + * @param[in] channel The channel to write to. + * + * @param[in] data A pointer to the data to write. + * + * @param[in] len The length of the buffer to write to. + * + * @return The number of bytes written, SSH_ERROR on error. + * + * @see channel_read() + */ +int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) { + return channel_write_common(channel, data, len, 0); +} + +/** + * @brief Check if the channel is open or not. + * + * @param[in] channel The channel to check. + * + * @return 0 if channel is closed, nonzero otherwise. + * + * @see channel_is_closed() + */ +int ssh_channel_is_open(ssh_channel channel) { + if(channel == NULL) { + return 0; + } + return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0); +} + +/** + * @brief Check if the channel is closed or not. + * + * @param[in] channel The channel to check. + * + * @return 0 if channel is opened, nonzero otherwise. + * + * @see channel_is_open() + */ +int ssh_channel_is_closed(ssh_channel channel) { + if(channel == NULL) { + return SSH_ERROR; + } + return (channel->state != SSH_CHANNEL_STATE_OPEN || channel->session->alive == 0); +} + +/** + * @brief Check if remote has sent an EOF. + * + * @param[in] channel The channel to check. + * + * @return 0 if there is no EOF, nonzero otherwise. + */ +int ssh_channel_is_eof(ssh_channel channel) { + if(channel == NULL) { + return SSH_ERROR; + } + if ((channel->stdout_buffer && + buffer_get_rest_len(channel->stdout_buffer) > 0) || + (channel->stderr_buffer && + buffer_get_rest_len(channel->stderr_buffer) > 0)) { + return 0; + } + + return (channel->remote_eof != 0); +} + +/** + * @brief Put the channel into blocking or nonblocking mode. + * + * @param[in] channel The channel to use. + * + * @param[in] blocking A boolean for blocking or nonblocking. + * + * @warning A side-effect of this is to put the whole session + * in non-blocking mode. + * @see ssh_set_blocking() + */ +void ssh_channel_set_blocking(ssh_channel channel, int blocking) { + if(channel == NULL) { + return; + } + ssh_set_blocking(channel->session,blocking); +} + +/** + * @internal + * + * @brief handle a SSH_CHANNEL_SUCCESS packet and set the channel state. + * + * This works on SSH1 sessions too. + */ +SSH_PACKET_CALLBACK(ssh_packet_channel_success){ + ssh_channel channel; + (void)type; + (void)user; + enter_function(); + channel=channel_from_msg(session,packet); + if (channel == NULL) { + ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + leave_function(); + return SSH_PACKET_USED; + } + + ssh_log(session, SSH_LOG_PACKET, + "Received SSH_CHANNEL_SUCCESS on channel (%d:%d)", + channel->local_channel, + channel->remote_channel); + if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){ + ssh_log(session, SSH_LOG_RARE, "SSH_CHANNEL_SUCCESS received in incorrect state %d", + channel->request_state); + } else { + channel->request_state=SSH_CHANNEL_REQ_STATE_ACCEPTED; + } + + leave_function(); + return SSH_PACKET_USED; +} + +/** + * @internal + * + * @brief Handle a SSH_CHANNEL_FAILURE packet and set the channel state. + * + * This works on SSH1 sessions too. + */ +SSH_PACKET_CALLBACK(ssh_packet_channel_failure){ + ssh_channel channel; + (void)type; + (void)user; + enter_function(); + channel=channel_from_msg(session,packet); + if (channel == NULL) { + ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + leave_function(); + return SSH_PACKET_USED; + } + + ssh_log(session, SSH_LOG_PACKET, + "Received SSH_CHANNEL_FAILURE on channel (%d:%d)", + channel->local_channel, + channel->remote_channel); + if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){ + ssh_log(session, SSH_LOG_RARE, "SSH_CHANNEL_FAILURE received in incorrect state %d", + channel->request_state); + } else { + channel->request_state=SSH_CHANNEL_REQ_STATE_DENIED; + } + leave_function(); + return SSH_PACKET_USED; +} + +static int ssh_channel_request_termination(void *c){ + ssh_channel channel = (ssh_channel)c; + if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING || + channel->session->session_state == SSH_SESSION_STATE_ERROR) + return 1; + else + return 0; +} + +static int channel_request(ssh_channel channel, const char *request, + ssh_buffer buffer, int reply) { + ssh_session session = channel->session; + ssh_string req = NULL; + int rc = SSH_ERROR; + + enter_function(); + switch(channel->request_state){ + case SSH_CHANNEL_REQ_STATE_NONE: + break; + default: + goto pending; + } + + req = ssh_string_from_char(request); + if (req == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_REQUEST) < 0 || + buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 || + buffer_add_ssh_string(session->out_buffer, req) < 0 || + buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { + ssh_set_error_oom(session); + ssh_string_free(req); + goto error; + } + ssh_string_free(req); + + if (buffer != NULL) { + if (buffer_add_data(session->out_buffer, buffer_get_rest(buffer), + buffer_get_rest_len(buffer)) < 0) { + ssh_set_error_oom(session); + goto error; + } + } + channel->request_state = SSH_CHANNEL_REQ_STATE_PENDING; + if (packet_send(session) == SSH_ERROR) { + leave_function(); + return rc; + } + + ssh_log(session, SSH_LOG_PACKET, + "Sent a SSH_MSG_CHANNEL_REQUEST %s", request); + if (reply == 0) { + channel->request_state = SSH_CHANNEL_REQ_STATE_NONE; + leave_function(); + return SSH_OK; + } +pending: + rc = ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, ssh_channel_request_termination, channel); + if(session->session_state == SSH_SESSION_STATE_ERROR || rc == SSH_ERROR) { + channel->request_state = SSH_CHANNEL_REQ_STATE_ERROR; + } + /* we received something */ + switch (channel->request_state){ + case SSH_CHANNEL_REQ_STATE_ERROR: + rc=SSH_ERROR; + break; + case SSH_CHANNEL_REQ_STATE_DENIED: + ssh_set_error(session, SSH_REQUEST_DENIED, + "Channel request %s failed", request); + rc=SSH_ERROR; + break; + case SSH_CHANNEL_REQ_STATE_ACCEPTED: + ssh_log(session, SSH_LOG_PROTOCOL, + "Channel request %s success",request); + rc=SSH_OK; + break; + case SSH_CHANNEL_REQ_STATE_PENDING: + rc = SSH_AGAIN; + leave_function(); + return rc; + case SSH_CHANNEL_REQ_STATE_NONE: + /* Never reached */ + ssh_set_error(session, SSH_FATAL, "Invalid state in channel_request()"); + rc=SSH_ERROR; + break; + } + channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; + leave_function(); + return rc; +error: + buffer_reinit(session->out_buffer); + + leave_function(); + return rc; +} + +/** + * @brief Request a pty with a specific type and size. + * + * @param[in] channel The channel to sent the request. + * + * @param[in] terminal The terminal type ("vt100, xterm,..."). + * + * @param[in] col The number of columns. + * + * @param[in] row The number of rows. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + */ +int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, + int col, int row) { + ssh_session session; + ssh_string term = NULL; + ssh_buffer buffer = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return SSH_ERROR; + } + session = channel->session; + + if(terminal == NULL) { + ssh_set_error_invalid(channel->session); + return rc; + } + + enter_function(); +#ifdef WITH_SSH1 + if (channel->version==1) { + rc = channel_request_pty_size1(channel,terminal, col, row); + leave_function(); + return rc; + } +#endif + switch(channel->request_state){ + case SSH_CHANNEL_REQ_STATE_NONE: + break; + default: + goto pending; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(session); + goto error; + } + + term = ssh_string_from_char(terminal); + if (term == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_ssh_string(buffer, term) < 0 || + buffer_add_u32(buffer, htonl(col)) < 0 || + buffer_add_u32(buffer, htonl(row)) < 0 || + buffer_add_u32(buffer, 0) < 0 || + buffer_add_u32(buffer, 0) < 0 || + buffer_add_u32(buffer, htonl(1)) < 0 || /* Add a 0byte string */ + buffer_add_u8(buffer, 0) < 0) { + ssh_set_error_oom(session); + goto error; + } +pending: + rc = channel_request(channel, "pty-req", buffer, 1); +error: + ssh_buffer_free(buffer); + ssh_string_free(term); + + leave_function(); + return rc; +} + +/** + * @brief Request a PTY. + * + * @param[in] channel The channel to send the request. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * + * @see channel_request_pty_size() + */ +int ssh_channel_request_pty(ssh_channel channel) { + return ssh_channel_request_pty_size(channel, "xterm", 80, 24); +} + +/** + * @brief Change the size of the terminal associated to a channel. + * + * @param[in] channel The channel to change the size. + * + * @param[in] cols The new number of columns. + * + * @param[in] rows The new number of rows. + * + * @return SSH_OK on success, SSH_ERROR if an error occurred. + * + * @warning Do not call it from a signal handler if you are not sure any other + * libssh function using the same channel/session is running at same + * time (not 100% threadsafe). + */ +int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) { + ssh_session session = channel->session; + ssh_buffer buffer = NULL; + int rc = SSH_ERROR; + + enter_function(); + +#ifdef WITH_SSH1 + if (channel->version == 1) { + rc = channel_change_pty_size1(channel,cols,rows); + leave_function(); + return rc; + } +#endif + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_u32(buffer, htonl(cols)) < 0 || + buffer_add_u32(buffer, htonl(rows)) < 0 || + buffer_add_u32(buffer, 0) < 0 || + buffer_add_u32(buffer, 0) < 0) { + ssh_set_error_oom(session); + goto error; + } + + rc = channel_request(channel, "window-change", buffer, 0); +error: + ssh_buffer_free(buffer); + + leave_function(); + return rc; +} + +/** + * @brief Request a shell. + * + * @param[in] channel The channel to send the request. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + */ +int ssh_channel_request_shell(ssh_channel channel) { + if(channel == NULL) { + return SSH_ERROR; + } +#ifdef WITH_SSH1 + if (channel->version == 1) { + return channel_request_shell1(channel); + } +#endif + return channel_request(channel, "shell", NULL, 1); +} + +/** + * @brief Request a subsystem (for example "sftp"). + * + * @param[in] channel The channel to send the request. + * + * @param[in] subsys The subsystem to request (for example "sftp"). + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * + * @warning You normally don't have to call it for sftp, see sftp_new(). + */ +int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) { + ssh_buffer buffer = NULL; + ssh_string subsystem = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return SSH_ERROR; + } + if(subsys == NULL) { + ssh_set_error_invalid(channel->session); + return rc; + } + switch(channel->request_state){ + case SSH_CHANNEL_REQ_STATE_NONE: + break; + default: + goto pending; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + subsystem = ssh_string_from_char(subsys); + if (subsystem == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + if (buffer_add_ssh_string(buffer, subsystem) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } +pending: + rc = channel_request(channel, "subsystem", buffer, 1); +error: + ssh_buffer_free(buffer); + ssh_string_free(subsystem); + + return rc; +} + +int ssh_channel_request_sftp( ssh_channel channel){ + if(channel == NULL) { + return SSH_ERROR; + } + return ssh_channel_request_subsystem(channel, "sftp"); +} + +static ssh_string generate_cookie(void) { + static const char *hex = "0123456789abcdef"; + char s[36]; + unsigned char rnd[16]; + int i; + + ssh_get_random(rnd,sizeof(rnd),0); + for (i = 0; i < 16; i++) { + s[i*2] = hex[rnd[i] & 0x0f]; + s[i*2+1] = hex[rnd[i] >> 4]; + } + s[32] = '\0'; + return ssh_string_from_char(s); +} + +/** + * @brief Sends the "x11-req" channel request over an existing session channel. + * + * This will enable redirecting the display of the remote X11 applications to + * local X server over an secure tunnel. + * + * @param[in] channel An existing session channel where the remote X11 + * applications are going to be executed. + * + * @param[in] single_connection A boolean to mark only one X11 app will be + * redirected. + * + * @param[in] protocol A x11 authentication protocol. Pass NULL to use the + * default value MIT-MAGIC-COOKIE-1. + * + * @param[in] cookie A x11 authentication cookie. Pass NULL to generate + * a random cookie. + * + * @param[in] screen_number The screen number. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + */ +int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, + const char *cookie, int screen_number) { + ssh_buffer buffer = NULL; + ssh_string p = NULL; + ssh_string c = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return SSH_ERROR; + } + switch(channel->request_state){ + case SSH_CHANNEL_REQ_STATE_NONE: + break; + default: + goto pending; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + p = ssh_string_from_char(protocol ? protocol : "MIT-MAGIC-COOKIE-1"); + if (p == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + if (cookie) { + c = ssh_string_from_char(cookie); + } else { + c = generate_cookie(); + } + if (c == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + if (buffer_add_u8(buffer, single_connection == 0 ? 0 : 1) < 0 || + buffer_add_ssh_string(buffer, p) < 0 || + buffer_add_ssh_string(buffer, c) < 0 || + buffer_add_u32(buffer, htonl(screen_number)) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } +pending: + rc = channel_request(channel, "x11-req", buffer, 1); + +error: + ssh_buffer_free(buffer); + ssh_string_free(p); + ssh_string_free(c); + return rc; +} + +static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, + int timeout_ms) { +#ifndef _WIN32 + static const struct timespec ts = { + .tv_sec = 0, + .tv_nsec = 50000000 /* 50ms */ + }; +#endif + ssh_message msg = NULL; + ssh_channel channel = NULL; + struct ssh_iterator *iterator; + int t; + + for (t = timeout_ms; t >= 0; t -= 50) + { + ssh_handle_packets(session, 50); + + if (session->ssh_message_list) { + iterator = ssh_list_get_iterator(session->ssh_message_list); + while (iterator) { + msg = (ssh_message)iterator->data; + if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL_OPEN && + ssh_message_subtype(msg) == channeltype) { + ssh_list_remove(session->ssh_message_list, iterator); + channel = ssh_message_channel_request_open_reply_accept(msg); + ssh_message_free(msg); + return channel; + } + iterator = iterator->next; + } + } + if(t>0){ +#ifdef _WIN32 + Sleep(50); /* 50ms */ +#else + nanosleep(&ts, NULL); +#endif + } + } + + ssh_set_error(session, SSH_NO_ERROR, "No channel request of this type from server"); + return NULL; +} + +/** + * @brief Accept an X11 forwarding channel. + * + * @param[in] channel An x11-enabled session channel. + * + * @param[in] timeout_ms Timeout in milliseconds. + * + * @return A newly created channel, or NULL if no X11 request from + * the server. + */ +ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) { + return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms); +} + +/** + * @internal + * + * @brief Handle a SSH_REQUEST_SUCCESS packet normally sent after a global + * request. + */ +SSH_PACKET_CALLBACK(ssh_request_success){ + (void)type; + (void)user; + (void)packet; + enter_function(); + + ssh_log(session, SSH_LOG_PACKET, + "Received SSH_REQUEST_SUCCESS"); + if(session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING){ + ssh_log(session, SSH_LOG_RARE, "SSH_REQUEST_SUCCESS received in incorrect state %d", + session->global_req_state); + } else { + session->global_req_state=SSH_CHANNEL_REQ_STATE_ACCEPTED; + } + + leave_function(); + return SSH_PACKET_USED; +} + +/** + * @internal + * + * @brief Handle a SSH_REQUEST_DENIED packet normally sent after a global + * request. + */ +SSH_PACKET_CALLBACK(ssh_request_denied){ + (void)type; + (void)user; + (void)packet; + enter_function(); + + ssh_log(session, SSH_LOG_PACKET, + "Received SSH_REQUEST_FAILURE"); + if(session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING){ + ssh_log(session, SSH_LOG_RARE, "SSH_REQUEST_DENIED received in incorrect state %d", + session->global_req_state); + } else { + session->global_req_state=SSH_CHANNEL_REQ_STATE_DENIED; + } + + leave_function(); + return SSH_PACKET_USED; + +} + +static int ssh_global_request_termination(void *s){ + ssh_session session = (ssh_session) s; + if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING || + session->session_state != SSH_SESSION_STATE_ERROR) + return 1; + else + return 0; +} + +/** + * @internal + * + * @brief Send a global request (needed for forward listening) and wait for the + * result. + * + * @param[in] session The SSH session handle. + * + * @param[in] request The type of request (defined in RFC). + * + * @param[in] buffer Additional data to put in packet. + * + * @param[in] reply Set if you expect a reply from server. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + */ +static int global_request(ssh_session session, const char *request, + ssh_buffer buffer, int reply) { + ssh_string req = NULL; + int rc = SSH_ERROR; + + enter_function(); + if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) + goto pending; + req = ssh_string_from_char(request); + if (req == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST) < 0 || + buffer_add_ssh_string(session->out_buffer, req) < 0 || + buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { + ssh_set_error_oom(session); + goto error; + } + ssh_string_free(req); + req=NULL; + + if (buffer != NULL) { + if (buffer_add_data(session->out_buffer, buffer_get_rest(buffer), + buffer_get_rest_len(buffer)) < 0) { + ssh_set_error_oom(session); + goto error; + } + } + session->global_req_state = SSH_CHANNEL_REQ_STATE_PENDING; + if (packet_send(session) == SSH_ERROR) { + leave_function(); + return rc; + } + + ssh_log(session, SSH_LOG_PACKET, + "Sent a SSH_MSG_GLOBAL_REQUEST %s", request); + if (reply == 0) { + session->global_req_state=SSH_CHANNEL_REQ_STATE_NONE; + leave_function(); + return SSH_OK; + } +pending: + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + ssh_global_request_termination, session); + if(rc==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){ + session->global_req_state = SSH_CHANNEL_REQ_STATE_ERROR; + } + switch(session->global_req_state){ + case SSH_CHANNEL_REQ_STATE_ACCEPTED: + ssh_log(session, SSH_LOG_PROTOCOL, "Global request %s success",request); + rc=SSH_OK; + break; + case SSH_CHANNEL_REQ_STATE_DENIED: + ssh_log(session, SSH_LOG_PACKET, + "Global request %s failed", request); + ssh_set_error(session, SSH_REQUEST_DENIED, + "Global request %s failed", request); + rc=SSH_ERROR; + break; + case SSH_CHANNEL_REQ_STATE_ERROR: + case SSH_CHANNEL_REQ_STATE_NONE: + rc=SSH_ERROR; + break; + case SSH_CHANNEL_REQ_STATE_PENDING: + rc=SSH_AGAIN; + break; + } + + leave_function(); + return rc; +error: + ssh_string_free(req); + leave_function(); + return rc; +} + +/** + * @brief Sends the "tcpip-forward" global request to ask the server to begin + * listening for inbound connections. + * + * @param[in] session The ssh session to send the request. + * + * @param[in] address The address to bind to on the server. Pass NULL to bind + * to all available addresses on all protocol families + * supported by the server. + * + * @param[in] port The port to bind to on the server. Pass 0 to ask the + * server to allocate the next available unprivileged port + * number + * + * @param[in] bound_port The pointer to get actual bound port. Pass NULL to + * ignore. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + **/ +int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port) { + ssh_buffer buffer = NULL; + ssh_string addr = NULL; + int rc = SSH_ERROR; + uint32_t tmp; + + if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) + goto pending; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(session); + goto error; + } + + addr = ssh_string_from_char(address ? address : ""); + if (addr == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_ssh_string(buffer, addr) < 0 || + buffer_add_u32(buffer, htonl(port)) < 0) { + ssh_set_error_oom(session); + goto error; + } +pending: + rc = global_request(session, "tcpip-forward", buffer, 1); + + /* TODO: FIXME no guarantee the last packet we received contains + * that info */ + if (rc == SSH_OK && port == 0 && bound_port) { + buffer_get_u32(session->in_buffer, &tmp); + *bound_port = ntohl(tmp); + } + +error: + ssh_buffer_free(buffer); + ssh_string_free(addr); + return rc; +} + +/** + * @brief Accept an incoming TCP/IP forwarding channel. + * + * @param[in] session The ssh session to use. + * + * @param[in] timeout_ms A timeout in milliseconds. + * + * @return Newly created channel, or NULL if no incoming channel request from + * the server + */ +ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) { + return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms); +} + +/** + * @brief Sends the "cancel-tcpip-forward" global request to ask the server to + * cancel the tcpip-forward request. + * + * @param[in] session The ssh session to send the request. + * + * @param[in] address The bound address on the server. + * + * @param[in] port The bound port on the server. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + */ +int ssh_forward_cancel(ssh_session session, const char *address, int port) { + ssh_buffer buffer = NULL; + ssh_string addr = NULL; + int rc = SSH_ERROR; + + if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) + goto pending; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(session); + goto error; + } + + addr = ssh_string_from_char(address ? address : ""); + if (addr == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_ssh_string(buffer, addr) < 0 || + buffer_add_u32(buffer, htonl(port)) < 0) { + ssh_set_error_oom(session); + goto error; + } +pending: + rc = global_request(session, "cancel-tcpip-forward", buffer, 1); + +error: + ssh_buffer_free(buffer); + ssh_string_free(addr); + return rc; +} + +/** + * @brief Set environment variables. + * + * @param[in] channel The channel to set the environment variables. + * + * @param[in] name The name of the variable. + * + * @param[in] value The value to set. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * @warning Some environment variables may be refused by security reasons. + */ +int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value) { + ssh_buffer buffer = NULL; + ssh_string str = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return SSH_ERROR; + } + if(name == NULL || value == NULL) { + ssh_set_error_invalid(channel->session); + return rc; + } + switch(channel->request_state){ + case SSH_CHANNEL_REQ_STATE_NONE: + break; + default: + goto pending; + } + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + str = ssh_string_from_char(name); + if (str == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + if (buffer_add_ssh_string(buffer, str) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } + + ssh_string_free(str); + str = ssh_string_from_char(value); + if (str == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + if (buffer_add_ssh_string(buffer, str) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } +pending: + rc = channel_request(channel, "env", buffer,1); +error: + ssh_buffer_free(buffer); + ssh_string_free(str); + + return rc; +} + +/** + * @brief Run a shell command without an interactive shell. + * + * This is similar to 'sh -c command'. + * + * @param[in] channel The channel to execute the command. + * + * @param[in] cmd The command to execute + * (e.g. "ls ~/ -al | grep -i reports"). + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * @code + * rc = channel_request_exec(channel, "ps aux"); + * if (rc > 0) { + * return -1; + * } + * + * while ((rc = channel_read(channel, buffer, sizeof(buffer), 0)) > 0) { + * if (fwrite(buffer, 1, rc, stdout) != (unsigned int) rc) { + * return -1; + * } + * } + * @endcode + * + * @see channel_request_shell() + */ +int ssh_channel_request_exec(ssh_channel channel, const char *cmd) { + ssh_buffer buffer = NULL; + ssh_string command = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return SSH_ERROR; + } + if(cmd == NULL) { + ssh_set_error_invalid(channel->session); + return rc; + } + +#ifdef WITH_SSH1 + if (channel->version == 1) { + return channel_request_exec1(channel, cmd); + } +#endif + switch(channel->request_state){ + case SSH_CHANNEL_REQ_STATE_NONE: + break; + default: + goto pending; + } + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + command = ssh_string_from_char(cmd); + if (command == NULL) { + goto error; + ssh_set_error_oom(channel->session); + } + + if (buffer_add_ssh_string(buffer, command) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } +pending: + rc = channel_request(channel, "exec", buffer, 1); +error: + ssh_buffer_free(buffer); + ssh_string_free(command); + return rc; +} + + +/** + * @brief Send a signal to remote process (as described in RFC 4254, section 6.9). + * + * Sends a signal 'sig' to the remote process. + * Note, that remote system may not support signals concept. + * In such a case this request will be silently ignored. + * Only SSH-v2 is supported (I'm not sure about SSH-v1). + * + * OpenSSH doesn't support signals yet, see: + * https://bugzilla.mindrot.org/show_bug.cgi?id=1424 + * + * @param[in] channel The channel to send signal. + * + * @param[in] sig The signal to send (without SIG prefix) + * \n\n + * SIGABRT -> ABRT \n + * SIGALRM -> ALRM \n + * SIGFPE -> FPE \n + * SIGHUP -> HUP \n + * SIGILL -> ILL \n + * SIGINT -> INT \n + * SIGKILL -> KILL \n + * SIGPIPE -> PIPE \n + * SIGQUIT -> QUIT \n + * SIGSEGV -> SEGV \n + * SIGTERM -> TERM \n + * SIGUSR1 -> USR1 \n + * SIGUSR2 -> USR2 \n + * + * @return SSH_OK on success, SSH_ERROR if an error occurred + * (including attempts to send signal via SSH-v1 session). + */ +int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) { + ssh_buffer buffer = NULL; + ssh_string encoded_signal = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return SSH_ERROR; + } + if(sig == NULL) { + ssh_set_error_invalid(channel->session); + return rc; + } + +#ifdef WITH_SSH1 + if (channel->version == 1) { + return SSH_ERROR; // TODO: Add support for SSH-v1 if possible. + } +#endif + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + encoded_signal = ssh_string_from_char(sig); + if (encoded_signal == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + if (buffer_add_ssh_string(buffer, encoded_signal) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } + + rc = channel_request(channel, "signal", buffer, 0); +error: + ssh_buffer_free(buffer); + ssh_string_free(encoded_signal); + return rc; +} + + +/** + * @brief Read data from a channel into a buffer. + * + * @param[in] channel The channel to read from. + * + * @param[in] buffer The buffer which will get the data. + * + * @param[in] count The count of bytes to be read. If it is bigger than 0, + * the exact size will be read, else (bytes=0) it will + * return once anything is available. + * + * @param is_stderr A boolean value to mark reading from the stderr stream. + * + * @return The number of bytes read, 0 on end of file or SSH_ERROR + * on error. + * @deprecated Please use ssh_channel_read instead + * @warning This function doesn't work in nonblocking/timeout mode + * @see ssh_channel_read + */ +int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, + int is_stderr) { + ssh_session session; + char buffer_tmp[8192]; + int r; + uint32_t total=0; + + if(channel == NULL) { + return SSH_ERROR; + } + session = channel->session; + + if(buffer == NULL) { + ssh_set_error_invalid(channel->session); + return SSH_ERROR; + } + + enter_function(); + buffer_reinit(buffer); + if(count==0){ + do { + r=ssh_channel_poll(channel, is_stderr); + if(r < 0){ + leave_function(); + return r; + } + if(r > 0){ + r=ssh_channel_read(channel, buffer_tmp, r, is_stderr); + if(r < 0){ + leave_function(); + return r; + } + if(buffer_add_data(buffer,buffer_tmp,r) < 0){ + ssh_set_error_oom(session); + r = SSH_ERROR; + } + leave_function(); + return r; + } + if(ssh_channel_is_eof(channel)){ + leave_function(); + return 0; + } + ssh_handle_packets(channel->session, SSH_TIMEOUT_INFINITE); + } while (r == 0); + } + while(total < count){ + r=ssh_channel_read(channel, buffer_tmp, sizeof(buffer_tmp), is_stderr); + if(r<0){ + leave_function(); + return r; + } + if(r==0){ + leave_function(); + return total; + } + if(buffer_add_data(buffer,buffer_tmp,r) < 0){ + ssh_set_error_oom(session); + leave_function(); + return SSH_ERROR; + } + total += r; + } + leave_function(); + return total; +} + +struct ssh_channel_read_termination_struct { + ssh_channel channel; + uint32_t count; + ssh_buffer buffer; +}; + +static int ssh_channel_read_termination(void *s){ + struct ssh_channel_read_termination_struct *ctx = s; + if (buffer_get_rest_len(ctx->buffer) >= ctx->count || + ctx->channel->remote_eof || + ctx->channel->session->session_state == SSH_SESSION_STATE_ERROR) + return 1; + else + return 0; +} + +/* TODO FIXME Fix the delayed close thing */ +/* TODO FIXME Fix the blocking behaviours */ + +/** + * @brief Reads data from a channel. + * + * @param[in] channel The channel to read from. + * + * @param[in] dest The destination buffer which will get the data. + * + * @param[in] count The count of bytes to be read. + * + * @param[in] is_stderr A boolean value to mark reading from the stderr flow. + * + * @return The number of bytes read, 0 on end of file or SSH_ERROR + * on error. Can return 0 if nothing is available in nonblocking + * mode. + * + * @warning This function may return less than count bytes of data, and won't + * block until count bytes have been read. + * @warning The read function using a buffer has been renamed to + * channel_read_buffer(). + */ +int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr) { + ssh_session session; + ssh_buffer stdbuf; + uint32_t len; + struct ssh_channel_read_termination_struct ctx; + int rc; + + if(channel == NULL) { + return SSH_ERROR; + } + if(dest == NULL) { + ssh_set_error_invalid(channel->session); + return SSH_ERROR; + } + + session = channel->session; + stdbuf = channel->stdout_buffer; + enter_function(); + + if (count == 0) { + leave_function(); + return 0; + } + + if (is_stderr) { + stdbuf=channel->stderr_buffer; + } + + /* + * We may have problem if the window is too small to accept as much data + * as asked + */ + ssh_log(session, SSH_LOG_PROTOCOL, + "Read (%d) buffered : %d bytes. Window: %d", + count, + buffer_get_rest_len(stdbuf), + channel->local_window); + + if (count > buffer_get_rest_len(stdbuf) + channel->local_window) { + if (grow_window(session, channel, count - buffer_get_rest_len(stdbuf)) < 0) { + leave_function(); + return -1; + } + } + + /* block reading until all bytes are read + * and ignore the trivial case count=0 + */ + ctx.channel = channel; + ctx.buffer = stdbuf; + ctx.count = count; + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + ssh_channel_read_termination, &ctx); + if (rc == SSH_ERROR){ + leave_function(); + return rc; + } + if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ + leave_function(); + return SSH_ERROR; + } + if (channel->remote_eof && buffer_get_rest_len(stdbuf) == 0) { + leave_function(); + return 0; + } + len = buffer_get_rest_len(stdbuf); + /* Read count bytes if len is greater, everything otherwise */ + len = (len > count ? count : len); + memcpy(dest, buffer_get_rest(stdbuf), len); + buffer_pass_bytes(stdbuf,len); + /* Authorize some buffering while userapp is busy */ + if (channel->local_window < WINDOWLIMIT) { + if (grow_window(session, channel, 0) < 0) { + leave_function(); + return -1; + } + } + + leave_function(); + return len; +} + +/** + * @brief Do a nonblocking read on the channel. + * + * A nonblocking read on the specified channel. it will return <= count bytes of + * data read atomically. + * + * @param[in] channel The channel to read from. + * + * @param[in] dest A pointer to a destination buffer. + * + * @param[in] count The count of bytes of data to be read. + * + * @param[in] is_stderr A boolean to select the stderr stream. + * + * @return The number of bytes read, 0 if nothing is available or + * SSH_ERROR on error. + * + * @warning Don't forget to check for EOF as it would return 0 here. + * + * @see channel_is_eof() + */ +int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, + int is_stderr) { + ssh_session session; + int to_read; + int rc; + int blocking; + + if(channel == NULL) { + return SSH_ERROR; + } + if(dest == NULL) { + ssh_set_error_invalid(channel->session); + return SSH_ERROR; + } + + session = channel->session; + enter_function(); + + to_read = ssh_channel_poll(channel, is_stderr); + + if (to_read <= 0) { + if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ + leave_function(); + return SSH_ERROR; + } + leave_function(); + return to_read; /* may be an error code */ + } + + if (to_read > (int)count) { + to_read = (int)count; + } + blocking = ssh_is_blocking(session); + ssh_set_blocking(session, 0); + rc = ssh_channel_read(channel, dest, to_read, is_stderr); + ssh_set_blocking(session,blocking); + leave_function(); + return rc; +} + +/** + * @brief Polls a channel for data to read. + * + * @param[in] channel The channel to poll. + * + * @param[in] is_stderr A boolean to select the stderr stream. + * + * @return The number of bytes available for reading, 0 if nothing + * is available or SSH_ERROR on error. + * + * @warning When the channel is in EOF state, the function returns SSH_EOF. + * + * @see channel_is_eof() + */ +int ssh_channel_poll(ssh_channel channel, int is_stderr){ + ssh_session session; + ssh_buffer stdbuf; + + if(channel == NULL) { + return SSH_ERROR; + } + + session = channel->session; + stdbuf = channel->stdout_buffer; + enter_function(); + + if (is_stderr) { + stdbuf = channel->stderr_buffer; + } + + if (buffer_get_rest_len(stdbuf) == 0 && channel->remote_eof == 0) { + if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ + leave_function(); + return SSH_ERROR; + } + if (ssh_handle_packets(channel->session, SSH_TIMEOUT_NONBLOCKING)==SSH_ERROR) { + leave_function(); + return SSH_ERROR; + } + } + + if (buffer_get_rest_len(stdbuf) > 0){ + leave_function(); + return buffer_get_rest_len(stdbuf); + } + + if (channel->remote_eof) { + leave_function(); + return SSH_EOF; + } + + leave_function(); + return buffer_get_rest_len(stdbuf); +} + +/** + * @brief Polls a channel for data to read, waiting for a certain timeout. + * + * @param[in] channel The channel to poll. + * @param[in] timeout Set an upper limit on the time for which this function + * will block, in milliseconds. Specifying a negative value + * means an infinite timeout. This parameter is passed to + * the poll() function. + * @param[in] is_stderr A boolean to select the stderr stream. + * + * @return The number of bytes available for reading, + * 0 if nothing is available (timeout elapsed), + * SSH_EOF on end of file, + * SSH_ERROR on error. + * + * @warning When the channel is in EOF state, the function returns SSH_EOF. + * + * @see channel_is_eof() + */ +int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){ + ssh_session session; + ssh_buffer stdbuf; + struct ssh_channel_read_termination_struct ctx; + int rc; + + if(channel == NULL) { + return SSH_ERROR; + } + + session = channel->session; + stdbuf = channel->stdout_buffer; + enter_function(); + + if (is_stderr) { + stdbuf = channel->stderr_buffer; + } + ctx.buffer = stdbuf; + ctx.channel = channel; + ctx.count = 1; + rc = ssh_handle_packets_termination(channel->session, timeout, + ssh_channel_read_termination, &ctx); + if(rc ==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){ + rc = SSH_ERROR; + goto end; + } + rc = buffer_get_rest_len(stdbuf); + if(rc > 0) + goto end; + if (channel->remote_eof) + rc = SSH_EOF; +end: + leave_function(); + return rc; +} + +/** + * @brief Recover the session in which belongs a channel. + * + * @param[in] channel The channel to recover the session from. + * + * @return The session pointer. + */ +ssh_session ssh_channel_get_session(ssh_channel channel) { + if(channel == NULL) { + return NULL; + } + + return channel->session; +} + +static int ssh_channel_exit_status_termination(void *c){ + ssh_channel channel = c; + if(channel->exit_status != -1 || + /* When a channel is closed, no exit status message can + * come anymore */ + (channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) || + channel->session->session_state == SSH_SESSION_STATE_ERROR) + return 1; + else + return 0; +} + +/** + * @brief Get the exit status of the channel (error code from the executed + * instruction). + * + * @param[in] channel The channel to get the status from. + * + * @returns The exit status, -1 if no exit status has been returned + * (yet). + * @warning This function may block until a timeout (or never) + * if the other side is not willing to close the channel. + */ +int ssh_channel_get_exit_status(ssh_channel channel) { + int rc; + if(channel == NULL) { + return SSH_ERROR; + } + rc = ssh_handle_packets_termination(channel->session, SSH_TIMEOUT_USER, + ssh_channel_exit_status_termination, channel); + if (rc == SSH_ERROR || channel->session->session_state == + SSH_SESSION_STATE_ERROR) + return SSH_ERROR; + return channel->exit_status; +} + +/* + * This function acts as a meta select. + * + * First, channels are analyzed to seek potential can-write or can-read ones, + * then if no channel has been elected, it goes in a loop with the posix + * select(2). + * This is made in two parts: protocol select and network select. The protocol + * select does not use the network functions at all + */ +static int channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans, + ssh_channel *echans, ssh_channel *rout, ssh_channel *wout, ssh_channel *eout) { + ssh_channel chan; + int i; + int j = 0; + + for (i = 0; rchans[i] != NULL; i++) { + chan = rchans[i]; + + while (ssh_channel_is_open(chan) && ssh_socket_data_available(chan->session->socket)) { + ssh_handle_packets(chan->session, SSH_TIMEOUT_NONBLOCKING); + } + + if ((chan->stdout_buffer && buffer_get_rest_len(chan->stdout_buffer) > 0) || + (chan->stderr_buffer && buffer_get_rest_len(chan->stderr_buffer) > 0) || + chan->remote_eof) { + rout[j] = chan; + j++; + } + } + rout[j] = NULL; + + j = 0; + for(i = 0; wchans[i] != NULL; i++) { + chan = wchans[i]; + /* It's not our business to seek if the file descriptor is writable */ + if (ssh_socket_data_writable(chan->session->socket) && + ssh_channel_is_open(chan) && (chan->remote_window > 0)) { + wout[j] = chan; + j++; + } + } + wout[j] = NULL; + + j = 0; + for (i = 0; echans[i] != NULL; i++) { + chan = echans[i]; + + if (!ssh_socket_is_open(chan->session->socket) || ssh_channel_is_closed(chan)) { + eout[j] = chan; + j++; + } + } + eout[j] = NULL; + + return 0; +} + +/* Just count number of pointers in the array */ +static int count_ptrs(ssh_channel *ptrs) { + int c; + for (c = 0; ptrs[c] != NULL; c++) + ; + + return c; +} + +/** + * @brief Act like the standard select(2) on channels. + * + * The list of pointers are then actualized and will only contain pointers to + * channels that are respectively readable, writable or have an exception to + * trap. + * + * @param[in] readchans A NULL pointer or an array of channel pointers, + * terminated by a NULL. + * + * @param[in] writechans A NULL pointer or an array of channel pointers, + * terminated by a NULL. + * + * @param[in] exceptchans A NULL pointer or an array of channel pointers, + * terminated by a NULL. + * + * @param[in] timeout Timeout as defined by select(2). + * + * @return SSH_OK on a successful operation, SSH_EINTR if the + * select(2) syscall was interrupted, then relaunch the + * function. + */ +int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, + ssh_channel *exceptchans, struct timeval * timeout) { + ssh_channel *rchans, *wchans, *echans; + ssh_channel dummy = NULL; + ssh_event event = NULL; + int rc; + int i; + int tm, tm_base; + int firstround=1; + struct ssh_timestamp ts; + + if (timeout != NULL) + tm_base = timeout->tv_sec * 1000 + timeout->tv_usec/1000; + else + tm_base = SSH_TIMEOUT_INFINITE; + ssh_timestamp_init(&ts); + tm = tm_base; + /* don't allow NULL pointers */ + if (readchans == NULL) { + readchans = &dummy; + } + + if (writechans == NULL) { + writechans = &dummy; + } + + if (exceptchans == NULL) { + exceptchans = &dummy; + } + + if (readchans[0] == NULL && writechans[0] == NULL && exceptchans[0] == NULL) { + /* No channel to poll?? Go away! */ + return 0; + } + + /* Prepare the outgoing temporary arrays */ + rchans = malloc(sizeof(ssh_channel ) * (count_ptrs(readchans) + 1)); + if (rchans == NULL) { + return SSH_ERROR; + } + + wchans = malloc(sizeof(ssh_channel ) * (count_ptrs(writechans) + 1)); + if (wchans == NULL) { + SAFE_FREE(rchans); + return SSH_ERROR; + } + + echans = malloc(sizeof(ssh_channel ) * (count_ptrs(exceptchans) + 1)); + if (echans == NULL) { + SAFE_FREE(rchans); + SAFE_FREE(wchans); + return SSH_ERROR; + } + + /* + * First, try without doing network stuff then, use the ssh_poll + * infrastructure to poll on all sessions. + */ + do { + channel_protocol_select(readchans, writechans, exceptchans, + rchans, wchans, echans); + if (rchans[0] != NULL || wchans[0] != NULL || echans[0] != NULL) { + /* At least one channel has an event */ + break; + } + /* Add all channels' sessions right into an event object */ + if (event == NULL) { + event = ssh_event_new(); + if (event == NULL) { + SAFE_FREE(rchans); + SAFE_FREE(wchans); + SAFE_FREE(echans); + + return SSH_ERROR; + } + for (i = 0; readchans[i] != NULL; i++) { + ssh_poll_get_default_ctx(readchans[i]->session); + ssh_event_add_session(event, readchans[i]->session); + } + for (i = 0; writechans[i] != NULL; i++) { + ssh_poll_get_default_ctx(writechans[i]->session); + ssh_event_add_session(event, writechans[i]->session); + } + for (i = 0; exceptchans[i] != NULL; i++) { + ssh_poll_get_default_ctx(exceptchans[i]->session); + ssh_event_add_session(event, exceptchans[i]->session); + } + } + /* Get out if the timeout has elapsed */ + if (!firstround && ssh_timeout_elapsed(&ts, tm_base)){ + break; + } + /* Here we go */ + rc = ssh_event_dopoll(event,tm); + if (rc != SSH_OK){ + SAFE_FREE(rchans); + SAFE_FREE(wchans); + SAFE_FREE(echans); + ssh_event_free(event); + return rc; + } + tm = ssh_timeout_update(&ts, tm_base); + firstround=0; + } while(1); + + memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel )); + memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel )); + memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel )); + SAFE_FREE(rchans); + SAFE_FREE(wchans); + SAFE_FREE(echans); + if(event) + ssh_event_free(event); + return 0; +} + +#if WITH_SERVER +/** + * @brief Blocking write on a channel stderr. + * + * @param[in] channel The channel to write to. + * + * @param[in] data A pointer to the data to write. + * + * @param[in] len The length of the buffer to write to. + * + * @return The number of bytes written, SSH_ERROR on error. + * + * @see channel_read() + */ +int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) { + return channel_write_common(channel, data, len, 1); +} + +/** + * @brief Open a TCP/IP reverse forwarding channel. + * + * @param[in] channel An allocated channel. + * + * @param[in] remotehost The remote host to connected (host name or IP). + * + * @param[in] remoteport The remote port. + * + * @param[in] sourcehost The source host (your local computer). It's optional + * and for logging purpose. + * + * @param[in] localport The source port (your local computer). It's optional + * and for logging purpose. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * + * @warning This function does not bind the local port and does not automatically + * forward the content of a socket to the channel. You still have to + * use channel_read and channel_write for this. + */ +int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost, + int remoteport, const char *sourcehost, int localport) { + ssh_session session; + ssh_buffer payload = NULL; + ssh_string str = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return rc; + } + if(remotehost == NULL || sourcehost == NULL) { + ssh_set_error_invalid(channel->session); + return rc; + } + + + session = channel->session; + + enter_function(); + if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN) + goto pending; + payload = ssh_buffer_new(); + if (payload == NULL) { + ssh_set_error_oom(session); + goto error; + } + str = ssh_string_from_char(remotehost); + if (str == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_ssh_string(payload, str) < 0 || + buffer_add_u32(payload,htonl(remoteport)) < 0) { + ssh_set_error_oom(session); + goto error; + } + + ssh_string_free(str); + str = ssh_string_from_char(sourcehost); + if (str == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_ssh_string(payload, str) < 0 || + buffer_add_u32(payload,htonl(localport)) < 0) { + ssh_set_error_oom(session); + goto error; + } +pending: + rc = channel_open(channel, + "forwarded-tcpip", + CHANNEL_INITIAL_WINDOW, + CHANNEL_MAX_PACKET, + payload); + +error: + ssh_buffer_free(payload); + ssh_string_free(str); + + leave_function(); + return rc; +} + +/** + * @brief Open a X11 channel. + * + * @param[in] channel An allocated channel. + * + * @param[in] orig_addr The source host (the local server). + * + * @param[in] orig_port The source port (the local server). + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * @warning This function does not bind the local port and does not automatically + * forward the content of a socket to the channel. You still have to + * use channel_read and channel_write for this. + */ +int ssh_channel_open_x11(ssh_channel channel, + const char *orig_addr, int orig_port) { + ssh_session session; + ssh_buffer payload = NULL; + ssh_string str = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return rc; + } + if(orig_addr == NULL) { + ssh_set_error_invalid(channel->session); + return rc; + } + session = channel->session; + + enter_function(); + + if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN) + goto pending; + + payload = ssh_buffer_new(); + if (payload == NULL) { + ssh_set_error_oom(session); + goto error; + } + + str = ssh_string_from_char(orig_addr); + if (str == NULL) { + ssh_set_error_oom(session); + goto error; + } + + if (buffer_add_ssh_string(payload, str) < 0 || + buffer_add_u32(payload,htonl(orig_port)) < 0) { + ssh_set_error_oom(session); + goto error; + } +pending: + rc = channel_open(channel, + "x11", + CHANNEL_INITIAL_WINDOW, + CHANNEL_MAX_PACKET, + payload); + +error: + ssh_buffer_free(payload); + ssh_string_free(str); + + leave_function(); + return rc; +} + +/** + * @brief Send the exit status to the remote process (as described in RFC 4254, section 6.10). + * + * Sends the exit status to the remote process. + * Only SSH-v2 is supported (I'm not sure about SSH-v1). + * + * @param[in] channel The channel to send exit status. + * + * @param[in] sig The exit status to send + * + * @return SSH_OK on success, SSH_ERROR if an error occurred + * (including attempts to send exit status via SSH-v1 session). + */ +int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) { + ssh_buffer buffer = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return SSH_ERROR; + } + +#ifdef WITH_SSH1 + if (channel->version == 1) { + return SSH_ERROR; // TODO: Add support for SSH-v1 if possible. + } +#endif + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + if (buffer_add_u32(buffer, ntohl(exit_status)) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } + + rc = channel_request(channel, "exit-status", buffer, 0); +error: + ssh_buffer_free(buffer); + return rc; +} + +/** + * @brief Send an exit signal to remote process (as described in RFC 4254, section 6.10). + * + * Sends a signal 'sig' to the remote process. + * Note, that remote system may not support signals concept. + * In such a case this request will be silently ignored. + * Only SSH-v2 is supported (I'm not sure about SSH-v1). + * + * @param[in] channel The channel to send signal. + * + * @param[in] sig The signal to send (without SIG prefix) + * (e.g. "TERM" or "KILL"). + * @param[in] core A boolean to tell if a core was dumped + * @param[in] errmsg A CRLF explanation text about the error condition + * @param[in] lang The language used in the message (format: RFC 3066) + * + * @return SSH_OK on success, SSH_ERROR if an error occurred + * (including attempts to send signal via SSH-v1 session). + */ +int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig, + int core, const char *errmsg, const char *lang) { + ssh_buffer buffer = NULL; + ssh_string tmp = NULL; + int rc = SSH_ERROR; + + if(channel == NULL) { + return rc; + } + if(sig == NULL || errmsg == NULL || lang == NULL) { + ssh_set_error_invalid(channel->session); + return rc; + } +#ifdef WITH_SSH1 + if (channel->version == 1) { + return SSH_ERROR; // TODO: Add support for SSH-v1 if possible. + } +#endif + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + + tmp = ssh_string_from_char(sig); + if (tmp == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + if (buffer_add_ssh_string(buffer, tmp) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } + + if (buffer_add_u8(buffer, core?1:0) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } + + ssh_string_free(tmp); + tmp = ssh_string_from_char(errmsg); + if (tmp == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + if (buffer_add_ssh_string(buffer, tmp) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } + + ssh_string_free(tmp); + tmp = ssh_string_from_char(lang); + if (tmp == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } + if (buffer_add_ssh_string(buffer, tmp) < 0) { + ssh_set_error_oom(channel->session); + goto error; + } + + rc = channel_request(channel, "signal", buffer, 0); +error: + ssh_buffer_free(buffer); + if(tmp) + ssh_string_free(tmp); + return rc; +} + +#endif + +/* @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/channels1.c b/libssh/src/channels1.c new file mode 100644 index 00000000..5d0158d2 --- /dev/null +++ b/libssh/src/channels1.c @@ -0,0 +1,385 @@ +/* + * channels1.c - Support for SSH-1 type channels + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * Copyright (c) 2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/ssh1.h" +#include "libssh/buffer.h" +#include "libssh/packet.h" +#include "libssh/channels.h" +#include "libssh/session.h" +#include "libssh/misc.h" + +#ifdef WITH_SSH1 + +/* + * This is a big hack. In fact, SSH1 doesn't make a clever use of channels. + * The whole packets concerning shells are sent outside of a channel. + * Thus, an inside limitation of this behavior is that you can't only + * request one shell. + * The question is still how they managed to embed two "channel" into one + * protocol. + */ + +int channel_open_session1(ssh_channel chan) { + ssh_session session; + + if (chan == NULL) { + return -1; + } + session = chan->session; + + /* + * We guess we are requesting an *exec* channel. It can only have one exec + * channel. So we abort with an error if we need more than one. + */ + if (session->exec_channel_opened) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "SSH1 supports only one execution channel. " + "One has already been opened"); + return -1; + } + session->exec_channel_opened = 1; + chan->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED; + chan->state = SSH_CHANNEL_STATE_OPEN; + chan->local_maxpacket = 32000; + chan->local_window = 64000; + ssh_log(session, SSH_LOG_PACKET, "Opened a SSH1 channel session"); + + return 0; +} + +/* 10 SSH_CMSG_REQUEST_PTY + * + * string TERM environment variable value (e.g. vt100) + * 32-bit int terminal height, rows (e.g., 24) + * 32-bit int terminal width, columns (e.g., 80) + * 32-bit int terminal width, pixels (0 if no graphics) (e.g., 480) + * 32-bit int terminal height, pixels (0 if no graphics) (e.g., 640) + * n bytes tty modes encoded in binary + * Some day, someone should have a look at that nasty tty encoded. It's + * much simplier under ssh2. I just hope the defaults values are ok ... + */ + +int channel_request_pty_size1(ssh_channel channel, const char *terminal, int col, + int row) { + ssh_session session; + ssh_string str = NULL; + + if (channel == NULL) { + return SSH_ERROR; + } + session = channel->session; + + if(channel->request_state != SSH_CHANNEL_REQ_STATE_NONE){ + ssh_set_error(session,SSH_REQUEST_DENIED,"Wrong request state"); + return SSH_ERROR; + } + str = ssh_string_from_char(terminal); + if (str == NULL) { + ssh_set_error_oom(session); + return -1; + } + + if (buffer_add_u8(session->out_buffer, SSH_CMSG_REQUEST_PTY) < 0 || + buffer_add_ssh_string(session->out_buffer, str) < 0) { + ssh_string_free(str); + return -1; + } + ssh_string_free(str); + + if (buffer_add_u32(session->out_buffer, ntohl(row)) < 0 || + buffer_add_u32(session->out_buffer, ntohl(col)) < 0 || + buffer_add_u32(session->out_buffer, 0) < 0 || /* x */ + buffer_add_u32(session->out_buffer, 0) < 0 || /* y */ + buffer_add_u8(session->out_buffer, 0) < 0) { /* tty things */ + return -1; + } + + ssh_log(session, SSH_LOG_FUNCTIONS, "Opening a ssh1 pty"); + channel->request_state = SSH_CHANNEL_REQ_STATE_PENDING; + if (packet_send(session) == SSH_ERROR) { + return -1; + } + + while (channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING) { + ssh_handle_packets(session, SSH_TIMEOUT_INFINITE); + } + + switch(channel->request_state){ + case SSH_CHANNEL_REQ_STATE_ERROR: + case SSH_CHANNEL_REQ_STATE_PENDING: + case SSH_CHANNEL_REQ_STATE_NONE: + channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; + return SSH_ERROR; + case SSH_CHANNEL_REQ_STATE_ACCEPTED: + channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; + ssh_log(session, SSH_LOG_RARE, "PTY: Success"); + return SSH_OK; + case SSH_CHANNEL_REQ_STATE_DENIED: + channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; + ssh_set_error(session, SSH_REQUEST_DENIED, + "Server denied PTY allocation"); + ssh_log(session, SSH_LOG_RARE, "PTY: denied\n"); + return SSH_ERROR; + } + // Not reached + return SSH_ERROR; +} + +int channel_change_pty_size1(ssh_channel channel, int cols, int rows) { + ssh_session session; + + if (channel == NULL) { + return SSH_ERROR; + } + session = channel->session; + + if(channel->request_state != SSH_CHANNEL_REQ_STATE_NONE){ + ssh_set_error(session,SSH_REQUEST_DENIED,"Wrong request state"); + return SSH_ERROR; + } + if (buffer_add_u8(session->out_buffer, SSH_CMSG_WINDOW_SIZE) < 0 || + buffer_add_u32(session->out_buffer, ntohl(rows)) < 0 || + buffer_add_u32(session->out_buffer, ntohl(cols)) < 0 || + buffer_add_u32(session->out_buffer, 0) < 0 || + buffer_add_u32(session->out_buffer, 0) < 0) { + return SSH_ERROR; + } + channel->request_state=SSH_CHANNEL_REQ_STATE_PENDING; + if (packet_send(session) == SSH_ERROR) { + return SSH_ERROR; + } + + ssh_log(session, SSH_LOG_PROTOCOL, "Change pty size send"); + while(channel->request_state==SSH_CHANNEL_REQ_STATE_PENDING){ + ssh_handle_packets(session, SSH_TIMEOUT_INFINITE); + } + switch(channel->request_state){ + case SSH_CHANNEL_REQ_STATE_ERROR: + case SSH_CHANNEL_REQ_STATE_PENDING: + case SSH_CHANNEL_REQ_STATE_NONE: + channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; + return SSH_ERROR; + case SSH_CHANNEL_REQ_STATE_ACCEPTED: + channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; + ssh_log(session, SSH_LOG_PROTOCOL, "pty size changed"); + return SSH_OK; + case SSH_CHANNEL_REQ_STATE_DENIED: + channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; + ssh_log(session, SSH_LOG_RARE, "pty size change denied"); + ssh_set_error(session, SSH_REQUEST_DENIED, "pty size change denied"); + return SSH_ERROR; + } + // Not reached + return SSH_ERROR; + +} + +int channel_request_shell1(ssh_channel channel) { + ssh_session session; + + if (channel == NULL) { + return -1; + } + session = channel->session; + + if (buffer_add_u8(session->out_buffer,SSH_CMSG_EXEC_SHELL) < 0) { + return -1; + } + + if (packet_send(session) == SSH_ERROR) { + return -1; + } + + ssh_log(session, SSH_LOG_RARE, "Launched a shell"); + + return 0; +} + +int channel_request_exec1(ssh_channel channel, const char *cmd) { + ssh_session session; + ssh_string command = NULL; + + if (channel == NULL) { + return -1; + } + session = channel->session; + + command = ssh_string_from_char(cmd); + if (command == NULL) { + return -1; + } + + if (buffer_add_u8(session->out_buffer, SSH_CMSG_EXEC_CMD) < 0 || + buffer_add_ssh_string(session->out_buffer, command) < 0) { + ssh_string_free(command); + return -1; + } + ssh_string_free(command); + + if(packet_send(session) == SSH_ERROR) { + return -1; + } + + ssh_log(session, SSH_LOG_RARE, "Executing %s ...", cmd); + + return 0; +} + +SSH_PACKET_CALLBACK(ssh_packet_data1){ + ssh_channel channel = ssh_get_channel1(session); + ssh_string str = NULL; + int is_stderr=(type==SSH_SMSG_STDOUT_DATA ? 0 : 1); + (void)user; + + if (channel == NULL) { + return SSH_PACKET_NOT_USED; + } + + str = buffer_get_ssh_string(packet); + if (str == NULL) { + ssh_log(session, SSH_LOG_FUNCTIONS, "Invalid data packet !\n"); + return SSH_PACKET_USED; + } + + ssh_log(session, SSH_LOG_PROTOCOL, + "Adding %" PRIdS " bytes data in %d", + ssh_string_len(str), is_stderr); + + if (channel_default_bufferize(channel, ssh_string_data(str), ssh_string_len(str), + is_stderr) < 0) { + ssh_string_free(str); + return SSH_PACKET_USED; + } + ssh_string_free(str); + + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(ssh_packet_close1){ + ssh_channel channel = ssh_get_channel1(session); + uint32_t status; + (void)type; + (void)user; + + if (channel == NULL) { + return SSH_PACKET_NOT_USED; + } + + buffer_get_u32(packet, &status); + /* + * It's much more than a channel closing. spec says it's the last + * message sent by server (strange) + */ + + /* actually status is lost somewhere */ + channel->state = SSH_CHANNEL_STATE_CLOSED; + channel->remote_eof = 1; + + buffer_add_u8(session->out_buffer, SSH_CMSG_EXIT_CONFIRMATION); + packet_send(session); + + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(ssh_packet_exist_status1){ + ssh_channel channel = ssh_get_channel1(session); + uint32_t status; + (void)type; + (void)user; + + if (channel == NULL) { + return SSH_PACKET_NOT_USED; + } + + buffer_get_u32(packet, &status); + channel->state = SSH_CHANNEL_STATE_CLOSED; + channel->remote_eof = 1; + channel->exit_status = ntohl(status); + + return SSH_PACKET_USED; +} + + +int channel_write1(ssh_channel channel, const void *data, int len) { + ssh_session session; + int origlen = len; + int effectivelen; + const unsigned char *ptr=data; + + if (channel == NULL) { + return -1; + } + session = channel->session; + + while (len > 0) { + if (buffer_add_u8(session->out_buffer, SSH_CMSG_STDIN_DATA) < 0) { + return -1; + } + + effectivelen = len > 32000 ? 32000 : len; + + if (buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 || + buffer_add_data(session->out_buffer, ptr, effectivelen) < 0) { + return -1; + } + + ptr += effectivelen; + len -= effectivelen; + + if (packet_send(session) == SSH_ERROR) { + return -1; + } + ssh_handle_packets(session, SSH_TIMEOUT_NONBLOCKING); + } + if (ssh_blocking_flush(session,SSH_TIMEOUT_USER) == SSH_ERROR) + return -1; + return origlen; +} + +ssh_channel ssh_get_channel1(ssh_session session){ + struct ssh_iterator *it; + + if (session == NULL) { + return NULL; + } + + /* With SSH1, the channel is always the first one */ + if(session->channels != NULL){ + it = ssh_list_get_iterator(session->channels); + if(it) + return ssh_iterator_value(ssh_channel, it); + } + return NULL; +} +#endif /* WITH_SSH1 */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/client.c b/libssh/src/client.c new file mode 100644 index 00000000..ac1b83db --- /dev/null +++ b/libssh/src/client.c @@ -0,0 +1,696 @@ +/* + * client.c - SSH client functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/ssh2.h" +#include "libssh/buffer.h" +#include "libssh/packet.h" +#include "libssh/options.h" +#include "libssh/socket.h" +#include "libssh/session.h" +#include "libssh/dh.h" +#include "libssh/ecdh.h" +#include "libssh/threads.h" +#include "libssh/misc.h" +#include "libssh/pki.h" +#include "libssh/kex.h" + +#define set_status(session, status) do {\ + if (session->common.callbacks && session->common.callbacks->connect_status_function) \ + session->common.callbacks->connect_status_function(session->common.callbacks->userdata, status); \ + } while (0) + +/** + * @internal + * @brief Callback to be called when the socket is connected or had a + * connection error. Changes the state of the session and updates the error + * message. + * @param code one of SSH_SOCKET_CONNECTED_OK or SSH_SOCKET_CONNECTED_ERROR + * @param user is a pointer to session + */ +static void socket_callback_connected(int code, int errno_code, void *user){ + ssh_session session=(ssh_session)user; + enter_function(); + if(session->session_state != SSH_SESSION_STATE_CONNECTING){ + ssh_set_error(session,SSH_FATAL, "Wrong state in socket_callback_connected : %d", + session->session_state); + leave_function(); + return; + } + ssh_log(session,SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code); + if(code == SSH_SOCKET_CONNECTED_OK) + session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED; + else { + session->session_state=SSH_SESSION_STATE_ERROR; + ssh_set_error(session,SSH_FATAL,"%s",strerror(errno_code)); + } + session->ssh_connection_callback(session); + leave_function(); +} + +/** + * @internal + * + * @brief Gets the banner from socket and saves it in session. + * Updates the session state + * + * @param data pointer to the beginning of header + * @param len size of the banner + * @param user is a pointer to session + * @returns Number of bytes processed, or zero if the banner is not complete. + */ +static int callback_receive_banner(const void *data, size_t len, void *user) { + char *buffer = (char *)data; + ssh_session session=(ssh_session) user; + char *str = NULL; + size_t i; + int ret=0; + enter_function(); + if(session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED){ + ssh_set_error(session,SSH_FATAL,"Wrong state in callback_receive_banner : %d",session->session_state); + leave_function(); + return SSH_ERROR; + } + for(i=0;ipcap_ctx && buffer[i] == '\n'){ + ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_IN,buffer,i+1,i+1); + } +#endif + if(buffer[i]=='\r') + buffer[i]='\0'; + if(buffer[i]=='\n'){ + buffer[i]='\0'; + str=strdup(buffer); + /* number of bytes read */ + ret=i+1; + session->serverbanner=str; + session->session_state=SSH_SESSION_STATE_BANNER_RECEIVED; + ssh_log(session,SSH_LOG_PACKET,"Received banner: %s",str); + session->ssh_connection_callback(session); + leave_function(); + return ret; + } + if(i>127){ + /* Too big banner */ + session->session_state=SSH_SESSION_STATE_ERROR; + ssh_set_error(session,SSH_FATAL,"Receiving banner: too large banner"); + leave_function(); + return 0; + } + } + leave_function(); + return ret; +} + +/** @internal + * @brief Sends a SSH banner to the server. + * + * @param session The SSH session to use. + * + * @param server Send client or server banner. + * + * @return 0 on success, < 0 on error. + */ +int ssh_send_banner(ssh_session session, int server) { + const char *banner = NULL; + char buffer[128] = {0}; + int err=SSH_ERROR; + + enter_function(); + + banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2; + + if (server) { + session->serverbanner = strdup(banner); + if (session->serverbanner == NULL) { + goto end; + } + } else { + session->clientbanner = strdup(banner); + if (session->clientbanner == NULL) { + goto end; + } + } + + snprintf(buffer, 128, "%s\n", banner); + + if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERROR) { + goto end; + } +#ifdef WITH_PCAP + if(session->pcap_ctx) + ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT,buffer,strlen(buffer),strlen(buffer)); +#endif + err=SSH_OK; +end: + leave_function(); + return err; +} + +/** @internal + * @brief launches the DH handshake state machine + * @param session session handle + * @returns SSH_OK or SSH_ERROR + * @warning this function returning is no proof that DH handshake is + * completed + */ +static int dh_handshake(ssh_session session) { + + int rc = SSH_AGAIN; + + enter_function(); + + switch (session->dh_handshake_state) { + case DH_STATE_INIT: + switch(session->next_crypto->kex_type){ + case SSH_KEX_DH_GROUP1_SHA1: + case SSH_KEX_DH_GROUP14_SHA1: + rc = ssh_client_dh_init(session); + break; +#ifdef HAVE_ECDH + case SSH_KEX_ECDH_SHA2_NISTP256: + rc = ssh_client_ecdh_init(session); + break; +#endif + default: + rc=SSH_ERROR; + goto error; + } + + if (rc == SSH_ERROR) { + goto error; + } + + session->dh_handshake_state = DH_STATE_INIT_SENT; + case DH_STATE_INIT_SENT: + /* wait until ssh_packet_dh_reply is called */ + break; + case DH_STATE_NEWKEYS_SENT: + /* wait until ssh_packet_newkeys is called */ + break; + case DH_STATE_FINISHED: + leave_function(); + return SSH_OK; + default: + ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d", + session->dh_handshake_state); + leave_function(); + return SSH_ERROR; + } +error: + leave_function(); + return rc; +} + +static int ssh_service_request_termination(void *s){ + ssh_session session = (ssh_session)s; + if(session->session_state == SSH_SESSION_STATE_ERROR || + session->auth_service_state != SSH_AUTH_SERVICE_SENT) + return 1; + else + return 0; +} + +/** + * @internal + * + * @brief Request a service from the SSH server. + * + * Service requests are for example: ssh-userauth, ssh-connection, etc. + * + * @param session The session to use to ask for a service request. + * @param service The service request. + * + * @return SSH_OK on success + * @return SSH_ERROR on error + * @return SSH_AGAIN No response received yet + * @bug actually only works with ssh-userauth + */ +int ssh_service_request(ssh_session session, const char *service) { + ssh_string service_s = NULL; + int rc=SSH_ERROR; + enter_function(); + if(session->auth_service_state != SSH_AUTH_SERVICE_NONE) + goto pending; + if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_REQUEST) < 0) { + goto error; + } + service_s = ssh_string_from_char(service); + if (service_s == NULL) { + goto error; + } + + if (buffer_add_ssh_string(session->out_buffer,service_s) < 0) { + ssh_string_free(service_s); + goto error; + } + ssh_string_free(service_s); + session->auth_service_state=SSH_AUTH_SERVICE_SENT; + if (packet_send(session) == SSH_ERROR) { + ssh_set_error(session, SSH_FATAL, + "Sending SSH2_MSG_SERVICE_REQUEST failed."); + goto error; + } + + ssh_log(session, SSH_LOG_PACKET, + "Sent SSH_MSG_SERVICE_REQUEST (service %s)", service); +pending: + rc=ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, + ssh_service_request_termination, session); + if(rc == SSH_ERROR) + goto error; + switch(session->auth_service_state){ + case SSH_AUTH_SERVICE_DENIED: + ssh_set_error(session,SSH_FATAL,"ssh_auth_service request denied"); + break; + case SSH_AUTH_SERVICE_ACCEPTED: + rc=SSH_OK; + break; + case SSH_AUTH_SERVICE_SENT: + rc=SSH_AGAIN; + break; + case SSH_AUTH_SERVICE_NONE: + case SSH_AUTH_SERVICE_USER_SENT: + /* Invalid state, SSH1 specific */ + rc=SSH_ERROR; + break; + } +error: + leave_function(); + return rc; +} + +/** + * @addtogroup libssh_session + * + * @{ + */ + +/** + * @internal + * + * @brief A function to be called each time a step has been done in the + * connection. + */ +static void ssh_client_connection_callback(ssh_session session){ + int ssh1,ssh2; + enter_function(); + switch(session->session_state){ + case SSH_SESSION_STATE_NONE: + case SSH_SESSION_STATE_CONNECTING: + case SSH_SESSION_STATE_SOCKET_CONNECTED: + break; + case SSH_SESSION_STATE_BANNER_RECEIVED: + if (session->serverbanner == NULL) { + goto error; + } + set_status(session, 0.4f); + ssh_log(session, SSH_LOG_RARE, + "SSH server banner: %s", session->serverbanner); + + /* Here we analyze the different protocols the server allows. */ + if (ssh_analyze_banner(session, 0, &ssh1, &ssh2) < 0) { + goto error; + } + /* Here we decide which version of the protocol to use. */ + if (ssh2 && session->opts.ssh2) { + session->version = 2; +#ifdef WITH_SSH1 + } else if(ssh1 && session->opts.ssh1) { + session->version = 1; +#endif + } else if(ssh1 && !session->opts.ssh1){ +#ifdef WITH_SSH1 + ssh_set_error(session, SSH_FATAL, + "SSH-1 protocol not available (configure session to allow SSH-1)"); + goto error; +#else + ssh_set_error(session, SSH_FATAL, + "SSH-1 protocol not available (libssh compiled without SSH-1 support)"); + goto error; +#endif + } else { + ssh_set_error(session, SSH_FATAL, + "No version of SSH protocol usable (banner: %s)", + session->serverbanner); + goto error; + } + /* from now, the packet layer is handling incoming packets */ + if(session->version==2) + session->socket_callbacks.data=ssh_packet_socket_callback; +#ifdef WITH_SSH1 + else + session->socket_callbacks.data=ssh_packet_socket_callback1; +#endif + ssh_packet_set_default_callbacks(session); + session->session_state=SSH_SESSION_STATE_INITIAL_KEX; + ssh_send_banner(session, 0); + set_status(session, 0.5f); + break; + case SSH_SESSION_STATE_INITIAL_KEX: + /* TODO: This state should disappear in favor of get_key handle */ +#ifdef WITH_SSH1 + if(session->version==1){ + if (ssh_get_kex1(session) < 0) + goto error; + set_status(session,0.6f); + session->connected = 1; + break; + } +#endif + break; + case SSH_SESSION_STATE_KEXINIT_RECEIVED: + set_status(session,0.6f); + ssh_list_kex(session, &session->next_crypto->server_kex); + if (set_client_kex(session) < 0) { + goto error; + } + if (ssh_kex_select_methods(session) == SSH_ERROR) + goto error; + if (ssh_send_kex(session, 0) < 0) { + goto error; + } + set_status(session,0.8f); + session->session_state=SSH_SESSION_STATE_DH; + if (dh_handshake(session) == SSH_ERROR) { + goto error; + } + case SSH_SESSION_STATE_DH: + if(session->dh_handshake_state==DH_STATE_FINISHED){ + set_status(session,1.0f); + session->connected = 1; + if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) + session->session_state = SSH_SESSION_STATE_AUTHENTICATED; + else + session->session_state=SSH_SESSION_STATE_AUTHENTICATING; + } + break; + case SSH_SESSION_STATE_AUTHENTICATING: + break; + case SSH_SESSION_STATE_ERROR: + goto error; + default: + ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); + } + leave_function(); + return; + error: + ssh_socket_close(session->socket); + session->alive = 0; + session->session_state=SSH_SESSION_STATE_ERROR; + leave_function(); +} + +/** @internal + * @brief describe under which conditions the ssh_connect function may stop + */ +static int ssh_connect_termination(void *user){ + ssh_session session = (ssh_session)user; + switch(session->session_state){ + case SSH_SESSION_STATE_ERROR: + case SSH_SESSION_STATE_AUTHENTICATING: + case SSH_SESSION_STATE_DISCONNECTED: + return 1; + default: + return 0; + } +} + +/** + * @brief Connect to the ssh server. + * + * @param[in] session The ssh session to connect. + * + * @returns SSH_OK on success, SSH_ERROR on error. + * @returns SSH_AGAIN, if the session is in nonblocking mode, + * and call must be done again. + * + * @see ssh_new() + * @see ssh_disconnect() + */ +int ssh_connect(ssh_session session) { + int ret; + + if (session == NULL) { + return SSH_ERROR; + } + + enter_function(); + switch(session->pending_call_state){ + case SSH_PENDING_CALL_NONE: + break; + case SSH_PENDING_CALL_CONNECT: + goto pending; + default: + ssh_set_error(session,SSH_FATAL,"Bad call during pending SSH call in ssh_connect"); + leave_function(); + return SSH_ERROR; + } + session->alive = 0; + session->client = 1; + + if (ssh_init() < 0) { + leave_function(); + return SSH_ERROR; + } + if (session->opts.fd == SSH_INVALID_SOCKET && + session->opts.host == NULL && + session->opts.ProxyCommand == NULL) { + ssh_set_error(session, SSH_FATAL, "Hostname required"); + leave_function(); + return SSH_ERROR; + } + + ret = ssh_options_apply(session); + if (ret < 0) { + ssh_set_error(session, SSH_FATAL, "Couldn't apply options"); + leave_function(); + return SSH_ERROR; + } + ssh_log(session,SSH_LOG_RARE,"libssh %s, using threading %s", ssh_copyright(), ssh_threads_get_type()); + session->ssh_connection_callback = ssh_client_connection_callback; + session->session_state=SSH_SESSION_STATE_CONNECTING; + ssh_socket_set_callbacks(session->socket,&session->socket_callbacks); + session->socket_callbacks.connected=socket_callback_connected; + session->socket_callbacks.data=callback_receive_banner; + session->socket_callbacks.exception=ssh_socket_exception_callback; + session->socket_callbacks.userdata=session; + if (session->opts.fd != SSH_INVALID_SOCKET) { + ssh_socket_set_fd(session->socket, session->opts.fd); + ret=SSH_OK; +#ifndef _WIN32 + } else if (session->opts.ProxyCommand != NULL){ + ret = ssh_socket_connect_proxycommand(session->socket, + session->opts.ProxyCommand); +#endif + } else { + ret=ssh_socket_connect(session->socket, + session->opts.host, + session->opts.port, + session->opts.bindaddr); + } + if (ret == SSH_ERROR) { + leave_function(); + return SSH_ERROR; + } + + set_status(session, 0.2f); + + session->alive = 1; + ssh_log(session,SSH_LOG_PROTOCOL,"Socket connecting, now waiting for the callbacks to work"); +pending: + session->pending_call_state=SSH_PENDING_CALL_CONNECT; + if(ssh_is_blocking(session)) { + int timeout = (session->opts.timeout * 1000) + + (session->opts.timeout_usec / 1000); + if (timeout == 0) { + timeout = 10 * 1000; + } + ssh_log(session,SSH_LOG_PACKET,"ssh_connect: Actual timeout : %d", timeout); + ret = ssh_handle_packets_termination(session, timeout, ssh_connect_termination, session); + if (ret == SSH_ERROR || !ssh_connect_termination(session)) { + ssh_set_error(session, SSH_FATAL, + "Timeout connecting to %s", session->opts.host); + session->session_state = SSH_SESSION_STATE_ERROR; + } + } + else { + ret = ssh_handle_packets_termination(session, + SSH_TIMEOUT_NONBLOCKING, + ssh_connect_termination, + session); + if (ret == SSH_ERROR) { + session->session_state = SSH_SESSION_STATE_ERROR; + } + } + ssh_log(session,SSH_LOG_PACKET,"ssh_connect: Actual state : %d",session->session_state); + if(!ssh_is_blocking(session) && !ssh_connect_termination(session)){ + leave_function(); + return SSH_AGAIN; + } + leave_function(); + session->pending_call_state=SSH_PENDING_CALL_NONE; + if(session->session_state == SSH_SESSION_STATE_ERROR || session->session_state == SSH_SESSION_STATE_DISCONNECTED) + return SSH_ERROR; + return SSH_OK; +} + +/** + * @brief Get the issue banner from the server. + * + * This is the banner showing a disclaimer to users who log in, + * typically their right or the fact that they will be monitored. + * + * @param[in] session The SSH session to use. + * + * @return A newly allocated string with the banner, NULL on error. + */ +char *ssh_get_issue_banner(ssh_session session) { + if (session == NULL || session->banner == NULL) { + return NULL; + } + + return ssh_string_to_char(session->banner); +} + +/** + * @brief Get the version of the OpenSSH server, if it is not an OpenSSH server + * then 0 will be returned. + * + * You can use the SSH_VERSION_INT macro to compare version numbers. + * + * @param[in] session The SSH session to use. + * + * @return The version number if available, 0 otherwise. + */ +int ssh_get_openssh_version(ssh_session session) { + if (session == NULL) { + return 0; + } + + return session->openssh; +} + +/** + * @brief Disconnect from a session (client or server). + * The session can then be reused to open a new session. + * + * @param[in] session The SSH session to use. + */ +void ssh_disconnect(ssh_session session) { + ssh_string str = NULL; + struct ssh_iterator *it; + + if (session == NULL) { + return; + } + + enter_function(); + + if (ssh_socket_is_open(session->socket)) { + if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { + goto error; + } + if (buffer_add_u32(session->out_buffer, + htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) { + goto error; + } + + str = ssh_string_from_char("Bye Bye"); + if (str == NULL) { + goto error; + } + + if (buffer_add_ssh_string(session->out_buffer,str) < 0) { + ssh_string_free(str); + goto error; + } + ssh_string_free(str); + + packet_send(session); + ssh_socket_close(session->socket); + } +error: + session->alive = 0; + if(session->socket){ + ssh_socket_reset(session->socket); + } + session->opts.fd = SSH_INVALID_SOCKET; + session->session_state=SSH_SESSION_STATE_DISCONNECTED; + + while ((it=ssh_list_get_iterator(session->channels)) != NULL) { + ssh_channel_free(ssh_iterator_value(ssh_channel,it)); + ssh_list_remove(session->channels, it); + } + if(session->current_crypto){ + crypto_free(session->current_crypto); + session->current_crypto=NULL; + } + if(session->in_buffer) + buffer_reinit(session->in_buffer); + if(session->out_buffer) + buffer_reinit(session->out_buffer); + if(session->in_hashbuf) + buffer_reinit(session->in_hashbuf); + if(session->out_hashbuf) + buffer_reinit(session->out_hashbuf); + session->auth_methods = 0; + SAFE_FREE(session->serverbanner); + SAFE_FREE(session->clientbanner); + + if(session->ssh_message_list){ + ssh_message msg; + while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list)) + != NULL){ + ssh_message_free(msg); + } + ssh_list_free(session->ssh_message_list); + session->ssh_message_list=NULL; + } + + if (session->packet_callbacks){ + ssh_list_free(session->packet_callbacks); + session->packet_callbacks=NULL; + } + + leave_function(); +} + +const char *ssh_copyright(void) { + return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2010 Aris Adamantiadis " + "(aris@0xbadc0de.be) Distributed under the LGPL, please refer to COPYING " + "file for information about your rights"; +} +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/config.c b/libssh/src/config.c new file mode 100644 index 00000000..632a50bb --- /dev/null +++ b/libssh/src/config.c @@ -0,0 +1,366 @@ +/* + * config.c - parse the ssh config file + * + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/misc.h" +#include "libssh/options.h" + +enum ssh_config_opcode_e { + SOC_UNSUPPORTED = -1, + SOC_HOST, + SOC_HOSTNAME, + SOC_PORT, + SOC_USERNAME, + SOC_IDENTITY, + SOC_CIPHERS, + SOC_COMPRESSION, + SOC_TIMEOUT, + SOC_PROTOCOL, + SOC_STRICTHOSTKEYCHECK, + SOC_KNOWNHOSTS, + SOC_PROXYCOMMAND +}; + +struct ssh_config_keyword_table_s { + const char *name; + enum ssh_config_opcode_e opcode; +}; + +static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = { + { "host", SOC_HOST }, + { "hostname", SOC_HOSTNAME }, + { "port", SOC_PORT }, + { "user", SOC_USERNAME }, + { "identityfile", SOC_IDENTITY }, + { "ciphers", SOC_CIPHERS }, + { "compression", SOC_COMPRESSION }, + { "connecttimeout", SOC_TIMEOUT }, + { "protocol", SOC_PROTOCOL }, + { "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK }, + { "userknownhostsfile", SOC_KNOWNHOSTS }, + { "proxycommand", SOC_PROXYCOMMAND }, + { NULL, SOC_UNSUPPORTED } +}; + +static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) { + int i; + + for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) { + if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) { + return ssh_config_keyword_table[i].opcode; + } + } + + return SOC_UNSUPPORTED; +} + +static char *ssh_config_get_cmd(char **str) { + register char *c; + char *r; + + /* Ignore leading spaces */ + for (c = *str; *c; c++) { + if (! isblank(*c)) { + break; + } + } + + if (*c == '\"') { + for (r = ++c; *c; c++) { + if (*c == '\"') { + *c = '\0'; + goto out; + } + } + } + + for (r = c; *c; c++) { + if (*c == '\n') { + *c = '\0'; + goto out; + } + } + +out: + *str = c + 1; + + return r; +} + +static char *ssh_config_get_token(char **str) { + register char *c; + char *r; + + c = ssh_config_get_cmd(str); + + for (r = c; *c; c++) { + if (isblank(*c)) { + *c = '\0'; + goto out; + } + } + +out: + *str = c + 1; + + return r; +} + +static int ssh_config_get_int(char **str, int notfound) { + char *p, *endp; + int i; + + p = ssh_config_get_token(str); + if (p && *p) { + i = strtol(p, &endp, 10); + if (p == endp) { + return notfound; + } + return i; + } + + return notfound; +} + +static const char *ssh_config_get_str_tok(char **str, const char *def) { + char *p; + + p = ssh_config_get_token(str); + if (p && *p) { + return p; + } + + return def; +} + +static int ssh_config_get_yesno(char **str, int notfound) { + const char *p; + + p = ssh_config_get_str_tok(str, NULL); + if (p == NULL) { + return notfound; + } + + if (strncasecmp(p, "yes", 3) == 0) { + return 1; + } else if (strncasecmp(p, "no", 2) == 0) { + return 0; + } + + return notfound; +} + +static int ssh_config_parse_line(ssh_session session, const char *line, + unsigned int count, int *parsing) { + enum ssh_config_opcode_e opcode; + const char *p; + char *s, *x; + char *keyword; + char *lowerhost; + size_t len; + int i; + + x = s = strdup(line); + if (s == NULL) { + ssh_set_error_oom(session); + return -1; + } + + /* Remove trailing spaces */ + for (len = strlen(s) - 1; len > 0; len--) { + if (! isspace(s[len])) { + break; + } + s[len] = '\0'; + } + + keyword = ssh_config_get_token(&s); + if (keyword == NULL || *keyword == '#' || + *keyword == '\0' || *keyword == '\n') { + SAFE_FREE(x); + return 0; + } + + opcode = ssh_config_get_opcode(keyword); + + switch (opcode) { + case SOC_HOST: + *parsing = 0; + lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL; + for (p = ssh_config_get_str_tok(&s, NULL); p && *p; + p = ssh_config_get_str_tok(&s, NULL)) { + if (match_hostname(lowerhost, p, strlen(p))) { + *parsing = 1; + } + } + SAFE_FREE(lowerhost); + break; + case SOC_HOSTNAME: + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_HOST, p); + } + break; + case SOC_PORT: + if (session->opts.port == 22) { + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_PORT_STR, p); + } + } + break; + case SOC_USERNAME: + if (session->opts.username == NULL) { + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_USER, p); + } + } + break; + case SOC_IDENTITY: + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p); + } + break; + case SOC_CIPHERS: + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p); + ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p); + } + break; + case SOC_COMPRESSION: + i = ssh_config_get_yesno(&s, -1); + if (i >= 0 && *parsing) { + if (i) { + ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); + } else { + ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no"); + } + } + break; + case SOC_PROTOCOL: + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + char *a, *b; + b = strdup(p); + if (b == NULL) { + SAFE_FREE(x); + ssh_set_error_oom(session); + return -1; + } + i = 0; + ssh_options_set(session, SSH_OPTIONS_SSH1, &i); + ssh_options_set(session, SSH_OPTIONS_SSH2, &i); + + for (a = strtok(b, ","); a; a = strtok(NULL, ",")) { + switch (atoi(a)) { + case 1: + i = 1; + ssh_options_set(session, SSH_OPTIONS_SSH1, &i); + break; + case 2: + i = 1; + ssh_options_set(session, SSH_OPTIONS_SSH2, &i); + break; + default: + break; + } + } + SAFE_FREE(b); + } + break; + case SOC_TIMEOUT: + i = ssh_config_get_int(&s, -1); + if (i >= 0 && *parsing) { + ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &i); + } + break; + case SOC_STRICTHOSTKEYCHECK: + i = ssh_config_get_yesno(&s, -1); + if (i >= 0 && *parsing) { + ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i); + } + break; + case SOC_KNOWNHOSTS: + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p); + } + break; + case SOC_PROXYCOMMAND: + p = ssh_config_get_cmd(&s); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p); + } + break; + case SOC_UNSUPPORTED: + ssh_log(session, SSH_LOG_RARE, "Unsupported option: %s, line: %d\n", + keyword, count); + break; + default: + ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d\n", + opcode); + SAFE_FREE(x); + return -1; + break; + } + + SAFE_FREE(x); + return 0; +} + +/* ssh_config_parse_file */ +int ssh_config_parse_file(ssh_session session, const char *filename) { + char line[1024] = {0}; + unsigned int count = 0; + FILE *f; + int parsing; + + if ((f = fopen(filename, "r")) == NULL) { + return 0; + } + + ssh_log(session, SSH_LOG_RARE, "Reading configuration data from %s", filename); + + parsing = 1; + while (fgets(line, sizeof(line), f)) { + count++; + if (ssh_config_parse_line(session, line, count, &parsing) < 0) { + fclose(f); + return -1; + } + } + + fclose(f); + return 0; +} diff --git a/libssh/src/connect.c b/libssh/src/connect.c new file mode 100644 index 00000000..ae55b140 --- /dev/null +++ b/libssh/src/connect.c @@ -0,0 +1,484 @@ +/* + * connect.c - handles connections to ssh servers + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "libssh/libssh.h" +#include "libssh/misc.h" + +#ifdef _WIN32 +/* + * Only use Windows API functions available on Windows 2000 SP4 or later. + * The available constants are in . + * http://msdn.microsoft.com/en-us/library/aa383745.aspx + * http://blogs.msdn.com/oldnewthing/archive/2007/04/11/2079137.aspx + */ +#undef _WIN32_WINNT +#ifdef HAVE_WSPIAPI_H +#define _WIN32_WINNT 0x0500 /* _WIN32_WINNT_WIN2K */ +#undef NTDDI_VERSION +#define NTDDI_VERSION 0x05000400 /* NTDDI_WIN2KSP4 */ +#else +#define _WIN32_WINNT 0x0501 /* _WIN32_WINNT_WINXP */ +#undef NTDDI_VERSION +#define NTDDI_VERSION 0x05010000 /* NTDDI_WINXP */ +#endif + +#if _MSC_VER >= 1400 +#include +#undef close +#define close _close +#endif /* _MSC_VER */ +#include +#include + +/* is necessary for getaddrinfo before Windows XP, but it isn't + * available on some platforms like MinGW. */ +#ifdef HAVE_WSPIAPI_H +#include +#endif + +#else /* _WIN32 */ + +#include +#include +#include +#include + +#endif /* _WIN32 */ + +#include "libssh/priv.h" +#include "libssh/socket.h" +#include "libssh/channels.h" +#include "libssh/session.h" +#include "libssh/poll.h" + +#ifndef HAVE_GETADDRINFO +#error "Your system must have getaddrinfo()" +#endif + +#ifdef _WIN32 +#ifndef gai_strerror +char WSAAPI *gai_strerrorA(int code) { + static char buf[256]; + + snprintf(buf, sizeof(buf), "Undetermined error code (%d)", code); + + return buf; +} +#endif /* gai_strerror */ +#endif /* _WIN32 */ + +static int ssh_connect_socket_close(socket_t s){ +#ifdef _WIN32 + return closesocket(s); +#else + return close(s); +#endif +} + + +static int getai(ssh_session session, const char *host, int port, struct addrinfo **ai) { + const char *service = NULL; + struct addrinfo hints; + char s_port[10]; + + ZERO_STRUCT(hints); + + hints.ai_protocol = IPPROTO_TCP; + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (port == 0) { + hints.ai_flags = AI_PASSIVE; + } else { + snprintf(s_port, sizeof(s_port), "%hu", (unsigned short)port); + service = s_port; +#ifdef AI_NUMERICSERV + hints.ai_flags=AI_NUMERICSERV; +#endif + } + + if (ssh_is_ipaddr(host)) { + /* this is an IP address */ + ssh_log(session,SSH_LOG_PACKET,"host %s matches an IP address",host); + hints.ai_flags |= AI_NUMERICHOST; + } + + return getaddrinfo(host, service, &hints, ai); +} + +static int ssh_connect_ai_timeout(ssh_session session, const char *host, + int port, struct addrinfo *ai, long timeout, long usec, socket_t s) { + int timeout_ms; + ssh_pollfd_t fds; + int rc = 0; + socklen_t len = sizeof(rc); + + enter_function(); + + /* I know we're losing some precision. But it's not like poll-like family + * type of mechanisms are precise up to the microsecond. + */ + timeout_ms=timeout * 1000 + usec / 1000; + + ssh_socket_set_nonblocking(s); + + ssh_log(session, SSH_LOG_RARE, "Trying to connect to host: %s:%d with " + "timeout %d ms", host, port, timeout_ms); + + /* The return value is checked later */ + connect(s, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + + fds.fd=s; + fds.revents=0; + fds.events=POLLOUT; +#ifdef _WIN32 + fds.events |= POLLWRNORM; +#endif + rc = ssh_poll(&fds,1,timeout_ms); + + if (rc == 0) { + /* timeout */ + ssh_set_error(session, SSH_FATAL, + "Timeout while connecting to %s:%d", host, port); + ssh_connect_socket_close(s); + leave_function(); + return -1; + } + + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, + "poll error: %s", strerror(errno)); + ssh_connect_socket_close(s); + leave_function(); + return -1; + } + rc = 0; + + /* Get connect(2) return code. Zero means no error */ + getsockopt(s, SOL_SOCKET, SO_ERROR,(char *) &rc, &len); + if (rc != 0) { + ssh_set_error(session, SSH_FATAL, + "Connect to %s:%d failed: %s", host, port, strerror(rc)); + ssh_connect_socket_close(s); + leave_function(); + return -1; + } + + /* s is connected ? */ + ssh_log(session, SSH_LOG_PACKET, "Socket connected with timeout\n"); + ssh_socket_set_blocking(s); + + leave_function(); + return s; +} + +/** + * @internal + * + * @brief Connect to an IPv4 or IPv6 host specified by its IP address or + * hostname. + * + * @returns A file descriptor, < 0 on error. + */ +socket_t ssh_connect_host(ssh_session session, const char *host, + const char *bind_addr, int port, long timeout, long usec) { + socket_t s = -1; + int rc; + struct addrinfo *ai; + struct addrinfo *itr; + + enter_function(); + + rc = getai(session,host, port, &ai); + if (rc != 0) { + ssh_set_error(session, SSH_FATAL, + "Failed to resolve hostname %s (%s)", host, gai_strerror(rc)); + leave_function(); + return -1; + } + + for (itr = ai; itr != NULL; itr = itr->ai_next){ + /* create socket */ + s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol); + if (s < 0) { + ssh_set_error(session, SSH_FATAL, + "Socket create failed: %s", strerror(errno)); + continue; + } + + if (bind_addr) { + struct addrinfo *bind_ai; + struct addrinfo *bind_itr; + + ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr); + + rc = getai(session,bind_addr, 0, &bind_ai); + if (rc != 0) { + ssh_set_error(session, SSH_FATAL, + "Failed to resolve bind address %s (%s)", + bind_addr, + gai_strerror(rc)); + freeaddrinfo(ai); + close(s); + leave_function(); + return -1; + } + + for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) { + if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) { + ssh_set_error(session, SSH_FATAL, + "Binding local address: %s", strerror(errno)); + continue; + } else { + break; + } + } + freeaddrinfo(bind_ai); + + /* Cannot bind to any local addresses */ + if (bind_itr == NULL) { + ssh_connect_socket_close(s); + s = -1; + continue; + } + } + + if (timeout || usec) { + socket_t ret = ssh_connect_ai_timeout(session, host, port, itr, + timeout, usec, s); + leave_function(); + return ret; + } + + if (connect(s, itr->ai_addr, itr->ai_addrlen) < 0) { + ssh_set_error(session, SSH_FATAL, "Connect failed: %s", strerror(errno)); + ssh_connect_socket_close(s); + s = -1; + leave_function(); + continue; + } else { + /* We are connected */ + break; + } + } + + freeaddrinfo(ai); + leave_function(); + + return s; +} + +/** + * @internal + * + * @brief Launches a nonblocking connect to an IPv4 or IPv6 host + * specified by its IP address or hostname. + * + * @returns A file descriptor, < 0 on error. + * @warning very ugly !!! + */ +socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, + const char *bind_addr, int port) { + socket_t s = -1; + int rc; + struct addrinfo *ai; + struct addrinfo *itr; + + enter_function(); + + rc = getai(session,host, port, &ai); + if (rc != 0) { + ssh_set_error(session, SSH_FATAL, + "Failed to resolve hostname %s (%s)", host, gai_strerror(rc)); + leave_function(); + return -1; + } + + for (itr = ai; itr != NULL; itr = itr->ai_next){ + /* create socket */ + s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol); + if (s < 0) { + ssh_set_error(session, SSH_FATAL, + "Socket create failed: %s", strerror(errno)); + continue; + } + + if (bind_addr) { + struct addrinfo *bind_ai; + struct addrinfo *bind_itr; + + ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr); + + rc = getai(session,bind_addr, 0, &bind_ai); + if (rc != 0) { + ssh_set_error(session, SSH_FATAL, + "Failed to resolve bind address %s (%s)", + bind_addr, + gai_strerror(rc)); + close(s); + s=-1; + break; + } + + for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) { + if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) { + ssh_set_error(session, SSH_FATAL, + "Binding local address: %s", strerror(errno)); + continue; + } else { + break; + } + } + freeaddrinfo(bind_ai); + + /* Cannot bind to any local addresses */ + if (bind_itr == NULL) { + ssh_connect_socket_close(s); + s = -1; + continue; + } + } + ssh_socket_set_nonblocking(s); + + connect(s, itr->ai_addr, itr->ai_addrlen); + break; + } + + freeaddrinfo(ai); + leave_function(); + + return s; +} + +/** + * @addtogroup libssh_session + * + * @{ + */ + +static int ssh_select_cb (socket_t fd, int revents, void *userdata){ + fd_set *set = (fd_set *)userdata; + if(revents & POLLIN) + FD_SET(fd, set); + return 0; +} + +/** + * @brief A wrapper for the select syscall + * + * This functions acts more or less like the select(2) syscall.\n + * There is no support for writing or exceptions.\n + * + * @param[in] channels Arrays of channels pointers terminated by a NULL. + * It is never rewritten. + * + * @param[out] outchannels Arrays of same size that "channels", there is no need + * to initialize it. + * + * @param[in] maxfd Maximum +1 file descriptor from readfds. + * + * @param[in] readfds A fd_set of file descriptors to be select'ed for + * reading. + * + * @param[in] timeout A timeout for the select. + * + * @return SSH_OK on success, + * SSH_ERROR on error, + * SSH_EINTR if it was interrupted. In that case, + * just restart it. + * + * @warning libssh is not reentrant here. That means that if a signal is caught + * during the processing of this function, you cannot call libssh + * functions on sessions that are busy with ssh_select(). + * + * @see select(2) + */ +int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, + fd_set *readfds, struct timeval *timeout) { + int i,j; + int rc; + int base_tm, tm; + struct ssh_timestamp ts; + ssh_event event = ssh_event_new(); + int firstround=1; + + base_tm = tm=timeout->tv_sec * 1000 + timeout->tv_usec/1000; + for (i=0 ; channels[i] != NULL; ++i){ + ssh_event_add_session(event, channels[i]->session); + } + for (i=0; i 0) { + ret = crc_table[(ret ^ *buf) & 0xff] ^ (ret >> 8); + --len; + ++buf; + } + + return ret; +} + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/dh.c b/libssh/src/dh.c new file mode 100644 index 00000000..e4f2062b --- /dev/null +++ b/libssh/src/dh.c @@ -0,0 +1,1136 @@ +/* + * dh.c - Diffie-Helman algorithm code against SSH 2 + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * Copyright (c) 2009 by Andreas Schneider + * Copyright (c) 2012 by Dmitriy Kuznetsov + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* + * Let us resume the dh protocol. + * Each side computes a private prime number, x at client side, y at server + * side. + * g and n are two numbers common to every ssh software. + * client's public key (e) is calculated by doing: + * e = g^x mod p + * client sends e to the server. + * the server computes his own public key, f + * f = g^y mod p + * it sends it to the client + * the common key K is calculated by the client by doing + * k = f^x mod p + * the server does the same with the client public key e + * k' = e^y mod p + * if everything went correctly, k and k' are equal + */ + +#include "config.h" +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/crypto.h" +#include "libssh/buffer.h" +#include "libssh/session.h" +#include "libssh/misc.h" +#include "libssh/dh.h" +#include "libssh/ssh2.h" +#include "libssh/pki.h" + +/* todo: remove it */ +#include "libssh/string.h" +#ifdef HAVE_LIBCRYPTO +#include +#include +#include +#endif + +static unsigned char p_group1_value[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +#define P_GROUP1_LEN 128 /* Size in bytes of the p number */ + + +static unsigned char p_group14_value[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, + 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, + 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF}; + +#define P_GROUP14_LEN 256 /* Size in bytes of the p number for group 14 */ + +static unsigned long g_int = 2 ; /* G is defined as 2 by the ssh2 standards */ +static bignum g; +static bignum p_group1; +static bignum p_group14; +static int ssh_crypto_initialized; + +static bignum select_p(enum ssh_key_exchange_e type) { + return type == SSH_KEX_DH_GROUP14_SHA1 ? p_group14 : p_group1; +} + +int ssh_get_random(void *where, int len, int strong){ + +#ifdef HAVE_LIBGCRYPT + /* variable not used in gcrypt */ + (void) strong; + /* not using GCRY_VERY_STRONG_RANDOM which is a bit overkill */ + gcry_randomize(where,len,GCRY_STRONG_RANDOM); + + return 1; +#elif defined HAVE_LIBCRYPTO + if (strong) { + return RAND_bytes(where,len); + } else { + return RAND_pseudo_bytes(where,len); + } +#endif + + /* never reached */ + return 1; +} + + +/* + * This inits the values g and p which are used for DH key agreement + * FIXME: Make the function thread safe by adding a semaphore or mutex. + */ +int ssh_crypto_init(void) { + if (ssh_crypto_initialized == 0) { +#ifdef HAVE_LIBGCRYPT + gcry_check_version(NULL); + if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P,0)) { + gcry_control(GCRYCTL_INIT_SECMEM, 4096); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED,0); + } +#endif + + g = bignum_new(); + if (g == NULL) { + return -1; + } + bignum_set_word(g,g_int); + +#ifdef HAVE_LIBGCRYPT + bignum_bin2bn(p_group1_value, P_GROUP1_LEN, &p_group1); + if (p_group1 == NULL) { + bignum_free(g); + g = NULL; + return -1; + } + bignum_bin2bn(p_group14_value, P_GROUP14_LEN, &p_group14); + if (p_group1 == NULL) { + bignum_free(g); + bignum_free(p_group1); + g = NULL; + p_group1 = NULL; + return -1; + } + +#elif defined HAVE_LIBCRYPTO + p_group1 = bignum_new(); + if (p_group1 == NULL) { + bignum_free(g); + g = NULL; + return -1; + } + bignum_bin2bn(p_group1_value, P_GROUP1_LEN, p_group1); + + p_group14 = bignum_new(); + if (p_group14 == NULL) { + bignum_free(g); + bignum_free(p_group1); + g = NULL; + p_group1 = NULL; + return -1; + } + bignum_bin2bn(p_group14_value, P_GROUP14_LEN, p_group14); + + OpenSSL_add_all_algorithms(); + +#endif + + ssh_crypto_initialized = 1; + } + + return 0; +} + +void ssh_crypto_finalize(void) { + if (ssh_crypto_initialized) { + bignum_free(g); + g = NULL; + bignum_free(p_group1); + p_group1 = NULL; + bignum_free(p_group14); + p_group14 = NULL; +#ifdef HAVE_LIBGCRYPT + gcry_control(GCRYCTL_TERM_SECMEM); +#elif defined HAVE_LIBCRYPTO + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +#endif + ssh_crypto_initialized=0; + } +} + +/* prints the bignum on stderr */ +void ssh_print_bignum(const char *which, bignum num) { +#ifdef HAVE_LIBGCRYPT + unsigned char *hex = NULL; + bignum_bn2hex(num, &hex); +#elif defined HAVE_LIBCRYPTO + char *hex = NULL; + hex = bignum_bn2hex(num); +#endif + fprintf(stderr, "%s value: ", which); + fprintf(stderr, "%s\n", (hex == NULL) ? "(null)" : (char *) hex); + SAFE_FREE(hex); +} + +/** + * @brief Convert a buffer into a colon separated hex string. + * The caller has to free the memory. + * + * @param what What should be converted to a hex string. + * + * @param len Length of the buffer to convert. + * + * @return The hex string or NULL on error. + * + * @see ssh_string_free_char() + */ +char *ssh_get_hexa(const unsigned char *what, size_t len) { + const char h[] = "0123456789abcdef"; + char *hexa; + size_t i; + size_t hlen = len * 3; + + if (len > (UINT_MAX - 1) / 3) { + return NULL; + } + + hexa = malloc(hlen + 1); + if (hexa == NULL) { + return NULL; + } + + for (i = 0; i < len; i++) { + hexa[i * 3] = h[(what[i] >> 4) & 0xF]; + hexa[i * 3 + 1] = h[what[i] & 0xF]; + hexa[i * 3 + 2] = ':'; + } + hexa[hlen - 1] = '\0'; + + return hexa; +} + +/** + * @brief Print a buffer as colon separated hex string. + * + * @param descr Description printed in front of the hex string. + * + * @param what What should be converted to a hex string. + * + * @param len Length of the buffer to convert. + */ +void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) { + char *hexa = ssh_get_hexa(what, len); + + if (hexa == NULL) { + return; + } + printf("%s: %s\n", descr, hexa); + + free(hexa); +} + +int dh_generate_x(ssh_session session) { + session->next_crypto->x = bignum_new(); + if (session->next_crypto->x == NULL) { + return -1; + } + +#ifdef HAVE_LIBGCRYPT + bignum_rand(session->next_crypto->x, 128); +#elif defined HAVE_LIBCRYPTO + bignum_rand(session->next_crypto->x, 128, 0, -1); +#endif + + /* not harder than this */ +#ifdef DEBUG_CRYPTO + ssh_print_bignum("x", session->next_crypto->x); +#endif + + return 0; +} + +/* used by server */ +int dh_generate_y(ssh_session session) { + session->next_crypto->y = bignum_new(); + if (session->next_crypto->y == NULL) { + return -1; + } + +#ifdef HAVE_LIBGCRYPT + bignum_rand(session->next_crypto->y, 128); +#elif defined HAVE_LIBCRYPTO + bignum_rand(session->next_crypto->y, 128, 0, -1); +#endif + + /* not harder than this */ +#ifdef DEBUG_CRYPTO + ssh_print_bignum("y", session->next_crypto->y); +#endif + + return 0; +} + +/* used by server */ +int dh_generate_e(ssh_session session) { +#ifdef HAVE_LIBCRYPTO + bignum_CTX ctx = bignum_ctx_new(); + if (ctx == NULL) { + return -1; + } +#endif + + session->next_crypto->e = bignum_new(); + if (session->next_crypto->e == NULL) { +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + return -1; + } + +#ifdef HAVE_LIBGCRYPT + bignum_mod_exp(session->next_crypto->e, g, session->next_crypto->x, + select_p(session->next_crypto->kex_type)); +#elif defined HAVE_LIBCRYPTO + bignum_mod_exp(session->next_crypto->e, g, session->next_crypto->x, + select_p(session->next_crypto->kex_type), ctx); +#endif + +#ifdef DEBUG_CRYPTO + ssh_print_bignum("e", session->next_crypto->e); +#endif + +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + + return 0; +} + +int dh_generate_f(ssh_session session) { +#ifdef HAVE_LIBCRYPTO + bignum_CTX ctx = bignum_ctx_new(); + if (ctx == NULL) { + return -1; + } +#endif + + session->next_crypto->f = bignum_new(); + if (session->next_crypto->f == NULL) { +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + return -1; + } + +#ifdef HAVE_LIBGCRYPT + bignum_mod_exp(session->next_crypto->f, g, session->next_crypto->y, + select_p(session->next_crypto->kex_type)); +#elif defined HAVE_LIBCRYPTO + bignum_mod_exp(session->next_crypto->f, g, session->next_crypto->y, + select_p(session->next_crypto->kex_type), ctx); +#endif + +#ifdef DEBUG_CRYPTO + ssh_print_bignum("f", session->next_crypto->f); +#endif + +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + + return 0; +} + +ssh_string make_bignum_string(bignum num) { + ssh_string ptr = NULL; + int pad = 0; + unsigned int len = bignum_num_bytes(num); + unsigned int bits = bignum_num_bits(num); + + if (len == 0) { + return NULL; + } + + /* If the first bit is set we have a negative number */ + if (!(bits % 8) && bignum_is_bit_set(num, bits - 1)) { + pad++; + } + +#ifdef DEBUG_CRYPTO + fprintf(stderr, "%d bits, %d bytes, %d padding\n", bits, len, pad); +#endif /* DEBUG_CRYPTO */ + + ptr = ssh_string_new(len + pad); + if (ptr == NULL) { + return NULL; + } + + /* We have a negative number so we need a leading zero */ + if (pad) { + ptr->data[0] = 0; + } + +#ifdef HAVE_LIBGCRYPT + bignum_bn2bin(num, len, ptr->data + pad); +#elif HAVE_LIBCRYPTO + bignum_bn2bin(num, ptr->data + pad); +#endif + + return ptr; +} + +bignum make_string_bn(ssh_string string){ + bignum bn = NULL; + unsigned int len = ssh_string_len(string); + +#ifdef DEBUG_CRYPTO + fprintf(stderr, "Importing a %d bits, %d bytes object ...\n", + len * 8, len); +#endif /* DEBUG_CRYPTO */ + +#ifdef HAVE_LIBGCRYPT + bignum_bin2bn(string->data, len, &bn); +#elif defined HAVE_LIBCRYPTO + bn = bignum_bin2bn(string->data, len, NULL); +#endif + + return bn; +} + +ssh_string dh_get_e(ssh_session session) { + return make_bignum_string(session->next_crypto->e); +} + +/* used by server */ +ssh_string dh_get_f(ssh_session session) { + return make_bignum_string(session->next_crypto->f); +} + +void dh_import_pubkey(ssh_session session, ssh_string pubkey_string) { + session->next_crypto->server_pubkey = pubkey_string; +} + +int dh_import_f(ssh_session session, ssh_string f_string) { + session->next_crypto->f = make_string_bn(f_string); + if (session->next_crypto->f == NULL) { + return -1; + } + +#ifdef DEBUG_CRYPTO + ssh_print_bignum("f",session->next_crypto->f); +#endif + + return 0; +} + +/* used by the server implementation */ +int dh_import_e(ssh_session session, ssh_string e_string) { + session->next_crypto->e = make_string_bn(e_string); + if (session->next_crypto->e == NULL) { + return -1; + } + +#ifdef DEBUG_CRYPTO + ssh_print_bignum("e",session->next_crypto->e); +#endif + + return 0; +} + +int dh_build_k(ssh_session session) { +#ifdef HAVE_LIBCRYPTO + bignum_CTX ctx = bignum_ctx_new(); + if (ctx == NULL) { + return -1; + } +#endif + + session->next_crypto->k = bignum_new(); + if (session->next_crypto->k == NULL) { +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + return -1; + } + + /* the server and clients don't use the same numbers */ +#ifdef HAVE_LIBGCRYPT + if(session->client) { + bignum_mod_exp(session->next_crypto->k, session->next_crypto->f, + session->next_crypto->x, select_p(session->next_crypto->kex_type)); + } else { + bignum_mod_exp(session->next_crypto->k, session->next_crypto->e, + session->next_crypto->y, select_p(session->next_crypto->kex_type)); + } +#elif defined HAVE_LIBCRYPTO + if (session->client) { + bignum_mod_exp(session->next_crypto->k, session->next_crypto->f, + session->next_crypto->x, select_p(session->next_crypto->kex_type), ctx); + } else { + bignum_mod_exp(session->next_crypto->k, session->next_crypto->e, + session->next_crypto->y, select_p(session->next_crypto->kex_type), ctx); + } +#endif + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Session server cookie", + session->next_crypto->server_kex.cookie, 16); + ssh_print_hexa("Session client cookie", + session->next_crypto->client_kex.cookie, 16); + ssh_print_bignum("Shared secret key", session->next_crypto->k); +#endif + +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + + return 0; +} + +/** @internal + * @brief Starts diffie-hellman-group1 key exchange + */ +int ssh_client_dh_init(ssh_session session){ + ssh_string e = NULL; + int rc; + enter_function(); + if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_INIT) < 0) { + goto error; + } + + if (dh_generate_x(session) < 0) { + goto error; + } + if (dh_generate_e(session) < 0) { + goto error; + } + + e = dh_get_e(session); + if (e == NULL) { + goto error; + } + + if (buffer_add_ssh_string(session->out_buffer, e) < 0) { + goto error; + } + ssh_string_burn(e); + ssh_string_free(e); + e=NULL; + + rc = packet_send(session); + return rc; + error: + if(e != NULL){ + ssh_string_burn(e); + ssh_string_free(e); + } + + leave_function(); + return SSH_ERROR; +} + +int ssh_client_dh_reply(ssh_session session, ssh_buffer packet){ + ssh_string f; + ssh_string pubkey = NULL; + ssh_string signature = NULL; + int rc; + pubkey = buffer_get_ssh_string(packet); + if (pubkey == NULL){ + ssh_set_error(session,SSH_FATAL, "No public key in packet"); + goto error; + } + dh_import_pubkey(session, pubkey); + + f = buffer_get_ssh_string(packet); + if (f == NULL) { + ssh_set_error(session,SSH_FATAL, "No F number in packet"); + goto error; + } + rc = dh_import_f(session, f); + ssh_string_burn(f); + ssh_string_free(f); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot import f number"); + goto error; + } + + signature = buffer_get_ssh_string(packet); + if (signature == NULL) { + ssh_set_error(session, SSH_FATAL, "No signature in packet"); + goto error; + } + session->next_crypto->dh_server_signature = signature; + signature=NULL; /* ownership changed */ + if (dh_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + goto error; + } + + /* Send the MSG_NEWKEYS */ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { + goto error; + } + + rc=packet_send(session); + ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + return rc; +error: + return SSH_ERROR; +} + + +/* +static void sha_add(ssh_string str,SHACTX ctx){ + sha1_update(ctx,str,string_len(str)+4); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("partial hashed sessionid",str,string_len(str)+4); +#endif +} +*/ + +int make_sessionid(ssh_session session) { + ssh_string num = NULL; + ssh_string str = NULL; + ssh_buffer server_hash = NULL; + ssh_buffer client_hash = NULL; + ssh_buffer buf = NULL; + uint32_t len; + int rc = SSH_ERROR; + + enter_function(); + + buf = ssh_buffer_new(); + if (buf == NULL) { + return rc; + } + + str = ssh_string_from_char(session->clientbanner); + if (str == NULL) { + goto error; + } + + if (buffer_add_ssh_string(buf, str) < 0) { + goto error; + } + ssh_string_free(str); + + str = ssh_string_from_char(session->serverbanner); + if (str == NULL) { + goto error; + } + + if (buffer_add_ssh_string(buf, str) < 0) { + goto error; + } + + if (session->client) { + server_hash = session->in_hashbuf; + client_hash = session->out_hashbuf; + } else { + server_hash = session->out_hashbuf; + client_hash = session->in_hashbuf; + } + + if (buffer_add_u32(server_hash, 0) < 0) { + goto error; + } + if (buffer_add_u8(server_hash, 0) < 0) { + goto error; + } + if (buffer_add_u32(client_hash, 0) < 0) { + goto error; + } + if (buffer_add_u8(client_hash, 0) < 0) { + goto error; + } + + len = ntohl(buffer_get_rest_len(client_hash)); + if (buffer_add_u32(buf,len) < 0) { + goto error; + } + if (buffer_add_data(buf, buffer_get_rest(client_hash), + buffer_get_rest_len(client_hash)) < 0) { + goto error; + } + + len = ntohl(buffer_get_rest_len(server_hash)); + if (buffer_add_u32(buf, len) < 0) { + goto error; + } + if (buffer_add_data(buf, buffer_get_rest(server_hash), + buffer_get_rest_len(server_hash)) < 0) { + goto error; + } + + len = ssh_string_len(session->next_crypto->server_pubkey) + 4; + if (buffer_add_data(buf, session->next_crypto->server_pubkey, len) < 0) { + goto error; + } + if(session->next_crypto->kex_type == SSH_KEX_DH_GROUP1_SHA1 || + session->next_crypto->kex_type == SSH_KEX_DH_GROUP14_SHA1) { + + num = make_bignum_string(session->next_crypto->e); + if (num == NULL) { + goto error; + } + + len = ssh_string_len(num) + 4; + if (buffer_add_data(buf, num, len) < 0) { + goto error; + } + + ssh_string_free(num); + num = make_bignum_string(session->next_crypto->f); + if (num == NULL) { + goto error; + } + + len = ssh_string_len(num) + 4; + if (buffer_add_data(buf, num, len) < 0) { + goto error; + } + + ssh_string_free(num); +#ifdef HAVE_ECDH + } else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256){ + if(session->next_crypto->ecdh_client_pubkey == NULL || + session->next_crypto->ecdh_server_pubkey == NULL){ + ssh_log(session,SSH_LOG_WARNING,"ECDH parameted missing"); + goto error; + } + buffer_add_ssh_string(buf,session->next_crypto->ecdh_client_pubkey); + buffer_add_ssh_string(buf,session->next_crypto->ecdh_server_pubkey); +#endif + } + num = make_bignum_string(session->next_crypto->k); + if (num == NULL) { + goto error; + } + + len = ssh_string_len(num) + 4; + if (buffer_add_data(buf, num, len) < 0) { + goto error; + } + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("hash buffer", ssh_buffer_get_begin(buf), ssh_buffer_get_len(buf)); +#endif + + switch(session->next_crypto->kex_type){ + case SSH_KEX_DH_GROUP1_SHA1: + case SSH_KEX_DH_GROUP14_SHA1: + session->next_crypto->digest_len = SHA_DIGEST_LENGTH; + session->next_crypto->mac_type = SSH_MAC_SHA1; + session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); + if(session->next_crypto->secret_hash == NULL){ + ssh_set_error_oom(session); + goto error; + } + sha1(buffer_get_rest(buf), buffer_get_rest_len(buf), + session->next_crypto->secret_hash); + break; + case SSH_KEX_ECDH_SHA2_NISTP256: + session->next_crypto->digest_len = SHA256_DIGEST_LENGTH; + session->next_crypto->mac_type = SSH_MAC_SHA256; + session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); + if(session->next_crypto->secret_hash == NULL){ + ssh_set_error_oom(session); + goto error; + } + sha256(buffer_get_rest(buf), buffer_get_rest_len(buf), + session->next_crypto->secret_hash); + break; + } + /* During the first kex, secret hash and session ID are equal. However, after + * a key re-exchange, a new secret hash is calculated. This hash will not replace + * but complement existing session id. + */ + if (!session->next_crypto->session_id){ + session->next_crypto->session_id = malloc(session->next_crypto->digest_len); + if (session->next_crypto->session_id == NULL){ + ssh_set_error_oom(session); + goto error; + } + memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash, + session->next_crypto->digest_len); + } +#ifdef DEBUG_CRYPTO + printf("Session hash: "); + ssh_print_hexa("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len); + ssh_print_hexa("session id", session->next_crypto->session_id, session->next_crypto->digest_len); +#endif + + rc = SSH_OK; +error: + ssh_buffer_free(buf); + ssh_buffer_free(client_hash); + ssh_buffer_free(server_hash); + + session->in_hashbuf = NULL; + session->out_hashbuf = NULL; + + ssh_string_free(str); + ssh_string_free(num); + + leave_function(); + + return rc; +} + +int hashbufout_add_cookie(ssh_session session) { + session->out_hashbuf = ssh_buffer_new(); + if (session->out_hashbuf == NULL) { + return -1; + } + + if (buffer_add_u8(session->out_hashbuf, 20) < 0) { + buffer_reinit(session->out_hashbuf); + return -1; + } + + if (session->server) { + if (buffer_add_data(session->out_hashbuf, + session->next_crypto->server_kex.cookie, 16) < 0) { + buffer_reinit(session->out_hashbuf); + return -1; + } + } else { + if (buffer_add_data(session->out_hashbuf, + session->next_crypto->client_kex.cookie, 16) < 0) { + buffer_reinit(session->out_hashbuf); + return -1; + } + } + + return 0; +} + +int hashbufin_add_cookie(ssh_session session, unsigned char *cookie) { + session->in_hashbuf = ssh_buffer_new(); + if (session->in_hashbuf == NULL) { + return -1; + } + + if (buffer_add_u8(session->in_hashbuf, 20) < 0) { + buffer_reinit(session->in_hashbuf); + return -1; + } + if (buffer_add_data(session->in_hashbuf,cookie, 16) < 0) { + buffer_reinit(session->in_hashbuf); + return -1; + } + + return 0; +} + +static int generate_one_key(ssh_string k, + struct ssh_crypto_struct *crypto, unsigned char *output, char letter) { + ssh_mac_ctx ctx; + ctx=ssh_mac_ctx_init(crypto->mac_type); + + if (ctx == NULL) { + return -1; + } + + ssh_mac_update(ctx, k, ssh_string_len(k) + 4); + ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); + ssh_mac_update(ctx, &letter, 1); + ssh_mac_update(ctx, crypto->session_id, crypto->digest_len); + ssh_mac_final(output, ctx); + + return 0; +} + +int generate_session_keys(ssh_session session) { + ssh_string k_string = NULL; + ssh_mac_ctx ctx = NULL; + struct ssh_crypto_struct *crypto = session->next_crypto; + int rc = -1; + + enter_function(); + + k_string = make_bignum_string(crypto->k); + if (k_string == NULL) { + ssh_set_error_oom(session); + goto error; + } + + crypto->encryptIV = malloc(crypto->digest_len); + crypto->decryptIV = malloc(crypto->digest_len); + crypto->encryptkey = malloc(crypto->digest_len); + crypto->decryptkey = malloc(crypto->digest_len); + crypto->encryptMAC = malloc(crypto->digest_len); + crypto->decryptMAC = malloc(crypto->digest_len); + if(crypto->encryptIV == NULL || crypto->decryptIV == NULL || + crypto->encryptkey == NULL || crypto->decryptkey == NULL || + crypto->encryptMAC == NULL || crypto->decryptMAC == NULL){ + ssh_set_error_oom(session); + goto error; + } + + /* IV */ + if (session->client) { + if (generate_one_key(k_string, crypto, crypto->encryptIV, 'A') < 0) { + goto error; + } + if (generate_one_key(k_string, crypto, crypto->decryptIV, 'B') < 0) { + goto error; + } + } else { + if (generate_one_key(k_string, crypto, crypto->decryptIV, 'A') < 0) { + goto error; + } + if (generate_one_key(k_string, crypto, crypto->encryptIV, 'B') < 0) { + goto error; + } + } + if (session->client) { + if (generate_one_key(k_string, crypto, crypto->encryptkey, 'C') < 0) { + goto error; + } + if (generate_one_key(k_string, crypto, crypto->decryptkey, 'D') < 0) { + goto error; + } + } else { + if (generate_one_key(k_string, crypto, crypto->decryptkey, 'C') < 0) { + goto error; + } + if (generate_one_key(k_string, crypto, crypto->encryptkey, 'D') < 0) { + goto error; + } + } + + /* some ciphers need more than DIGEST_LEN bytes of input key */ + if (crypto->out_cipher->keysize > crypto->digest_len * 8) { + crypto->encryptkey = realloc(crypto->encryptkey, crypto->digest_len * 2); + if(crypto->encryptkey == NULL) + goto error; + ctx = ssh_mac_ctx_init(crypto->mac_type); + if (ctx == NULL) { + goto error; + } + ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); + ssh_mac_update(ctx, crypto->session_id, + crypto->digest_len); + ssh_mac_update(ctx, crypto->encryptkey, crypto->digest_len); + ssh_mac_final(crypto->encryptkey + crypto->digest_len, ctx); + } + + if (crypto->in_cipher->keysize > crypto->digest_len * 8) { + crypto->decryptkey = realloc(crypto->decryptkey, crypto->digest_len *2); + if(crypto->decryptkey == NULL) + goto error; + ctx = ssh_mac_ctx_init(crypto->mac_type); + ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); + ssh_mac_update(ctx, crypto->session_id, + crypto->digest_len); + ssh_mac_update(ctx, crypto->decryptkey, crypto->digest_len); + ssh_mac_final(crypto->decryptkey + crypto->digest_len, ctx); + } + if(session->client) { + if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'E') < 0) { + goto error; + } + if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'F') < 0) { + goto error; + } + } else { + if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'E') < 0) { + goto error; + } + if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'F') < 0) { + goto error; + } + } + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Encrypt IV", crypto->encryptIV, SHA_DIGEST_LEN); + ssh_print_hexa("Decrypt IV", crypto->decryptIV, SHA_DIGEST_LEN); + ssh_print_hexa("Encryption key", crypto->encryptkey, + crypto->out_cipher->keysize); + ssh_print_hexa("Decryption key", crypto->decryptkey, + crypto->in_cipher->keysize); + ssh_print_hexa("Encryption MAC", crypto->encryptMAC, SHA_DIGEST_LEN); + ssh_print_hexa("Decryption MAC", crypto->decryptMAC, 20); +#endif + + rc = 0; +error: + ssh_string_free(k_string); + leave_function(); + + return rc; +} + +/** + * @addtogroup libssh_session + * + * @{ + */ + +/** + * @brief Allocates a buffer with the MD5 hash of the server public key. + * + * This function allows you to get a MD5 hash of the public key. You can then + * print this hash in a human-readable form to the user so that he is able to + * verify it. Use ssh_get_hexa() or ssh_print_hexa() to display it. + * + * @param[in] session The SSH session to use. + * + * @param[in] hash The buffer to allocate. + * + * @return The bytes allocated or < 0 on error. + * + * @warning It is very important that you verify at some moment that the hash + * matches a known server. If you don't do it, cryptography wont help + * you at making things secure + * + * @see ssh_is_server_known() + * @see ssh_get_hexa() + * @see ssh_print_hexa() + */ +int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash) { + ssh_string pubkey; + MD5CTX ctx; + unsigned char *h; + + if (session == NULL || hash == NULL) { + return SSH_ERROR; + } + *hash = NULL; + if (session->current_crypto == NULL || + session->current_crypto->server_pubkey == NULL){ + ssh_set_error(session,SSH_FATAL,"No current cryptographic context"); + return SSH_ERROR; + } + + h = malloc(sizeof(unsigned char) * MD5_DIGEST_LEN); + if (h == NULL) { + return SSH_ERROR; + } + + ctx = md5_init(); + if (ctx == NULL) { + SAFE_FREE(h); + return SSH_ERROR; + } + + pubkey = session->current_crypto->server_pubkey; + + md5_update(ctx, ssh_string_data(pubkey), ssh_string_len(pubkey)); + md5_final(h, ctx); + + *hash = h; + + return MD5_DIGEST_LEN; +} + +/** + * @brief Deallocate the hash obtained by ssh_get_pubkey_hash. + * + * This is required under Microsoft platform as this library might use a + * different C library than your software, hence a different heap. + * + * @param[in] hash The buffer to deallocate. + * + * @see ssh_get_pubkey_hash() + */ +void ssh_clean_pubkey_hash(unsigned char **hash) { + SAFE_FREE(*hash); + *hash = NULL; +} + +/** + * @brief Get the server public key from a session. + * + * @param[in] session The session to get the key from. + * + * @param[out] key A pointer to store the allocated key. You need to free + * the key. + * + * @return SSH_OK on success, SSH_ERROR on errror. + * + * @see ssh_key_free() + */ +int ssh_get_publickey(ssh_session session, ssh_key *key) +{ + if (session==NULL || + session->current_crypto ==NULL || + session->current_crypto->server_pubkey == NULL) { + return SSH_ERROR; + } + + return ssh_pki_import_pubkey_blob(session->current_crypto->server_pubkey, + key); +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/ecdh.c b/libssh/src/ecdh.c new file mode 100644 index 00000000..bc0b03ce --- /dev/null +++ b/libssh/src/ecdh.c @@ -0,0 +1,278 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2011 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/session.h" +#include "libssh/ecdh.h" +#include "libssh/dh.h" +#include "libssh/buffer.h" +#include "libssh/ssh2.h" +#include "libssh/pki.h" + +#ifdef HAVE_ECDH +#include + +#define NISTP256 NID_X9_62_prime256v1 +#define NISTP384 NID_secp384r1 +#define NISTP521 NID_secp521r1 + +/** @internal + * @brief Starts ecdh-sha2-nistp256 key exchange + */ +int ssh_client_ecdh_init(ssh_session session){ + EC_KEY *key=NULL; + const EC_GROUP *group; + const EC_POINT *pubkey; + ssh_string client_pubkey; + int len; + int rc; + bignum_CTX ctx=BN_CTX_new(); + enter_function(); + if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT) < 0) { + goto error; + } + key = EC_KEY_new_by_curve_name(NISTP256); + group = EC_KEY_get0_group(key); + EC_KEY_generate_key(key); + pubkey=EC_KEY_get0_public_key(key); + len = EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED, + NULL,0,ctx); + client_pubkey=ssh_string_new(len); + + EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED, + ssh_string_data(client_pubkey),len,ctx); + buffer_add_ssh_string(session->out_buffer,client_pubkey); + BN_CTX_free(ctx); + session->next_crypto->ecdh_privkey = key; + session->next_crypto->ecdh_client_pubkey = client_pubkey; + rc = packet_send(session); + leave_function(); + return rc; +error: + leave_function(); + return SSH_ERROR; +} + +static void ecdh_import_pubkey(ssh_session session, ssh_string pubkey_string) { + session->next_crypto->server_pubkey = pubkey_string; +} + +static int ecdh_build_k(ssh_session session) { + const EC_GROUP *group = EC_KEY_get0_group(session->next_crypto->ecdh_privkey); + EC_POINT *pubkey; + void *buffer; + int len = (EC_GROUP_get_degree(group) + 7) / 8; + bignum_CTX ctx = bignum_ctx_new(); + if (ctx == NULL) { + return -1; + } + + session->next_crypto->k = bignum_new(); + if (session->next_crypto->k == NULL) { + bignum_ctx_free(ctx); + return -1; + } + + pubkey = EC_POINT_new(group); + if (pubkey == NULL) { + bignum_ctx_free(ctx); + return -1; + } + + if (session->server) + EC_POINT_oct2point(group,pubkey,ssh_string_data(session->next_crypto->ecdh_client_pubkey), + ssh_string_len(session->next_crypto->ecdh_client_pubkey),ctx); + else + EC_POINT_oct2point(group,pubkey,ssh_string_data(session->next_crypto->ecdh_server_pubkey), + ssh_string_len(session->next_crypto->ecdh_server_pubkey),ctx); + buffer = malloc(len); + ECDH_compute_key(buffer,len,pubkey,session->next_crypto->ecdh_privkey,NULL); + EC_POINT_free(pubkey); + BN_bin2bn(buffer,len,session->next_crypto->k); + free(buffer); + EC_KEY_free(session->next_crypto->ecdh_privkey); + session->next_crypto->ecdh_privkey=NULL; +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Session server cookie", + session->next_crypto->server_kex.cookie, 16); + ssh_print_hexa("Session client cookie", + session->next_crypto->client_kex.cookie, 16); + ssh_print_bignum("Shared secret key", session->next_crypto->k); +#endif + +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + + return 0; +} + +/** @internal + * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back + * a SSH_MSG_NEWKEYS + */ +int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet){ + ssh_string q_s_string = NULL; + ssh_string pubkey = NULL; + ssh_string signature = NULL; + int rc; + pubkey = buffer_get_ssh_string(packet); + if (pubkey == NULL){ + ssh_set_error(session,SSH_FATAL, "No public key in packet"); + goto error; + } + ecdh_import_pubkey(session, pubkey); + + q_s_string = buffer_get_ssh_string(packet); + if (q_s_string == NULL) { + ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet"); + goto error; + } + session->next_crypto->ecdh_server_pubkey = q_s_string; + signature = buffer_get_ssh_string(packet); + if (signature == NULL) { + ssh_set_error(session, SSH_FATAL, "No signature in packet"); + goto error; + } + session->next_crypto->dh_server_signature = signature; + signature=NULL; /* ownership changed */ + /* TODO: verify signature now instead of waiting for NEWKEYS */ + if (ecdh_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + goto error; + } + + /* Send the MSG_NEWKEYS */ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { + goto error; + } + + rc=packet_send(session); + ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + return rc; +error: + return SSH_ERROR; +} + +#ifdef WITH_SERVER + +/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a + * SSH_MSG_KEXDH_REPLY + */ + +int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ + /* ECDH keys */ + ssh_string q_c_string = NULL; + ssh_string q_s_string = NULL; + EC_KEY *ecdh_key=NULL; + const EC_GROUP *group; + const EC_POINT *ecdh_pubkey; + bignum_CTX ctx; + /* SSH host keys (rsa,dsa,ecdsa) */ + ssh_key privkey; + ssh_string sig_blob = NULL; + int len; + int rc; + + enter_function(); + + /* Extract the client pubkey from the init packet */ + + q_c_string = buffer_get_ssh_string(packet); + if (q_c_string == NULL) { + ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); + goto error; + } + session->next_crypto->ecdh_client_pubkey = q_c_string; + + /* Build server's keypair */ + + ctx = BN_CTX_new(); + ecdh_key = EC_KEY_new_by_curve_name(NISTP256); + group = EC_KEY_get0_group(ecdh_key); + EC_KEY_generate_key(ecdh_key); + ecdh_pubkey=EC_KEY_get0_public_key(ecdh_key); + len = EC_POINT_point2oct(group,ecdh_pubkey,POINT_CONVERSION_UNCOMPRESSED, + NULL,0,ctx); + q_s_string=ssh_string_new(len); + + EC_POINT_point2oct(group,ecdh_pubkey,POINT_CONVERSION_UNCOMPRESSED, + ssh_string_data(q_s_string),len,ctx); + + BN_CTX_free(ctx); + session->next_crypto->ecdh_privkey = ecdh_key; + session->next_crypto->ecdh_server_pubkey = q_s_string; + + buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY); + /* build k and session_id */ + if (ecdh_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + goto error; + } + if (ssh_get_key_params(session, &privkey) == SSH_ERROR) + goto error; + if (make_sessionid(session) != SSH_OK) { + ssh_set_error(session, SSH_FATAL, "Could not create a session id"); + goto error; + } + + /* add host's public key */ + buffer_add_ssh_string(session->out_buffer, session->next_crypto->server_pubkey); + /* add ecdh public key */ + buffer_add_ssh_string(session->out_buffer,q_s_string); + /* add signature blob */ + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + if (sig_blob == NULL) { + ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); + goto error; + } + buffer_add_ssh_string(session->out_buffer, sig_blob); + ssh_string_free(sig_blob); + /* Free private keys as they should not be readable after this point */ + if (session->srv.rsa_key) { + ssh_key_free(session->srv.rsa_key); + session->srv.rsa_key = NULL; + } + if (session->srv.dsa_key) { + ssh_key_free(session->srv.dsa_key); + session->srv.dsa_key = NULL; + } + + ssh_log(session,SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent"); + rc = packet_send(session); + if (rc == SSH_ERROR) + goto error; + + /* Send the MSG_NEWKEYS */ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { + goto error; + } + session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; + rc=packet_send(session); + ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + return rc; + error: + return SSH_ERROR; +} + +#endif /* WITH_SERVER */ + +#endif /* HAVE_ECDH */ diff --git a/libssh/src/error.c b/libssh/src/error.c new file mode 100644 index 00000000..f4a2af00 --- /dev/null +++ b/libssh/src/error.c @@ -0,0 +1,139 @@ +/* + * error.c - functions for ssh error handling + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include "libssh/priv.h" +#include "libssh/session.h" + +/** + * @defgroup libssh_error The SSH error functions. + * @ingroup libssh + * + * Functions for error handling. + * + * @{ + */ + +/** + * @internal + * + * @brief Registers an error with a description. + * + * @param error The place to store the error. + * + * @param code The class of error. + * + * @param descr The description, which can be a format string. + * + * @param ... The arguments for the format string. + */ +void _ssh_set_error(void *error, + int code, + const char *function, + const char *descr, ...) +{ + struct ssh_common_struct *err = error; + va_list va; + + va_start(va, descr); + vsnprintf(err->error.error_buffer, ERROR_BUFFERLEN, descr, va); + va_end(va); + + err->error.error_code = code; + ssh_log_common(err, + SSH_LOG_WARN, + function, + "Error: %s", + err->error.error_buffer); +} + +/** + * @internal + * + * @brief Registers an out of memory error + * + * @param error The place to store the error. + * + */ +void _ssh_set_error_oom(void *error, const char *function) +{ + struct error_struct *err = error; + + snprintf(err->error_buffer, sizeof(err->error_buffer), + "%s: Out of memory", function); + err->error_code = SSH_FATAL; +} + +/** + * @internal + * + * @brief Registers an invalid argument error + * + * @param error The place to store the error. + * + * @param function The function the error happened in. + * + */ +void _ssh_set_error_invalid(void *error, const char *function) +{ + _ssh_set_error(error, SSH_FATAL, function, + "Invalid argument in %s", function); +} + +/** + * @brief Retrieve the error text message from the last error. + * + * @param error The SSH session pointer. + * + * @return A static string describing the error. + */ +const char *ssh_get_error(void *error) { + struct error_struct *err = error; + + return err->error_buffer; +} + +/** + * @brief Retrieve the error code from the last error. + * + * @param error The SSH session pointer. + * + * \return SSH_NO_ERROR No error occurred\n + * SSH_REQUEST_DENIED The last request was denied but situation is + * recoverable\n + * SSH_FATAL A fatal error occurred. This could be an unexpected + * disconnection\n + * + * Other error codes are internal but can be considered same than + * SSH_FATAL. + */ +int ssh_get_error_code(void *error) { + struct error_struct *err = error; + + return err->error_code; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/gcrypt_missing.c b/libssh/src/gcrypt_missing.c new file mode 100644 index 00000000..b21e5f30 --- /dev/null +++ b/libssh/src/gcrypt_missing.c @@ -0,0 +1,101 @@ +/* + * gcrypt_missing.c - routines that are in OpenSSL but not in libgcrypt. + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2006 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include + +#include "libssh/priv.h" +#include "libssh/libgcrypt.h" + +#ifdef HAVE_LIBGCRYPT +int my_gcry_dec2bn(bignum *bn, const char *data) { + int count; + + *bn = bignum_new(); + if (*bn == NULL) { + return 0; + } + gcry_mpi_set_ui(*bn, 0); + for (count = 0; data[count]; count++) { + gcry_mpi_mul_ui(*bn, *bn, 10); + gcry_mpi_add_ui(*bn, *bn, data[count] - '0'); + } + + return count; +} + +char *my_gcry_bn2dec(bignum bn) { + bignum bndup, num, ten; + char *ret; + int count, count2; + int size, rsize; + char decnum; + + size = gcry_mpi_get_nbits(bn) * 3; + rsize = size / 10 + size / 1000 + 2; + + ret = malloc(rsize + 1); + if (ret == NULL) { + return NULL; + } + + if (!gcry_mpi_cmp_ui(bn, 0)) { + strcpy(ret, "0"); + } else { + ten = bignum_new(); + if (ten == NULL) { + SAFE_FREE(ret); + return NULL; + } + + num = bignum_new(); + if (num == NULL) { + SAFE_FREE(ret); + bignum_free(ten); + return NULL; + } + + for (bndup = gcry_mpi_copy(bn), bignum_set_word(ten, 10), count = rsize; + count; count--) { + gcry_mpi_div(bndup, num, bndup, ten, 0); + for (decnum = 0, count2 = gcry_mpi_get_nbits(num); count2; + decnum *= 2, decnum += (gcry_mpi_test_bit(num, count2 - 1) ? 1 : 0), + count2--) + ; + ret[count - 1] = decnum + '0'; + } + for (count = 0; count < rsize && ret[count] == '0'; count++) + ; + for (count2 = 0; count2 < rsize - count; ++count2) { + ret[count2] = ret[count2 + count]; + } + ret[count2] = 0; + bignum_free(num); + bignum_free(bndup); + bignum_free(ten); + } + + return ret; +} + +#endif +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/getpass.c b/libssh/src/getpass.c new file mode 100644 index 00000000..cc6d33ca --- /dev/null +++ b/libssh/src/getpass.c @@ -0,0 +1,282 @@ +/* + * getpass.c - platform independent getpass function. + * + * This file is part of the SSH Library + * + * Copyright (c) 2011 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include + +#include + +/** + * @internal + * + * @brief Get the password from the console. + * + * @param[in] prompt The prompt to display. + * + * @param[in] buf The buffer to fill. + * + * @param[in] len The length of the buffer. + * + * @param[in] verify Should the password be verified? + * + * @return 1 on success, 0 on error. + */ +static int ssh_gets(const char *prompt, char *buf, size_t len, int verify) { + char *tmp; + char *ptr = NULL; + int ok = 0; + + tmp = malloc(len); + if (tmp == NULL) { + return 0; + } + memset(tmp,'\0',len); + + /* read the password */ + while (!ok) { + if (buf[0] != '\0') { + fprintf(stdout, "%s[%s] ", prompt, buf); + } else { + fprintf(stdout, "%s", prompt); + } + fflush(stdout); + if (fgets(tmp, len, stdin) == NULL) { + free(tmp); + return 0; + } + + if ((ptr = strchr(tmp, '\n'))) { + *ptr = '\0'; + } + fprintf(stdout, "\n"); + + if (*tmp) { + strncpy(buf, tmp, len); + } + + if (verify) { + char *key_string; + + key_string = malloc(len); + if (key_string == NULL) { + break; + } + memset(key_string, '\0', len); + + fprintf(stdout, "\nVerifying, please re-enter. %s", prompt); + fflush(stdout); + if (! fgets(key_string, len, stdin)) { + memset(key_string, '\0', len); + SAFE_FREE(key_string); + clearerr(stdin); + continue; + } + if ((ptr = strchr(key_string, '\n'))) { + *ptr = '\0'; + } + fprintf(stdout, "\n"); + if (strcmp(buf, key_string)) { + printf("\n\07\07Mismatch - try again\n"); + memset(key_string, '\0', len); + SAFE_FREE(key_string); + fflush(stdout); + continue; + } + memset(key_string, '\0', len); + SAFE_FREE(key_string); + } + ok = 1; + } + memset(tmp, '\0', len); + free(tmp); + + return ok; +} + +#ifdef _WIN32 +#include + +int ssh_getpass(const char *prompt, + char *buf, + size_t len, + int echo, + int verify) { + HANDLE h; + DWORD mode = 0; + int ok; + + /* fgets needs at least len - 1 */ + if (prompt == NULL || buf == NULL || len < 2) { + return -1; + } + + /* get stdin and mode */ + h = GetStdHandle(STD_INPUT_HANDLE); + if (!GetConsoleMode(h, &mode)) { + return -1; + } + + /* disable echo */ + if (!echo) { + if (!SetConsoleMode(h, mode & ~ENABLE_ECHO_INPUT)) { + return -1; + } + } + + ok = ssh_gets(prompt, buf, len, verify); + + /* reset echo */ + SetConsoleMode(h, mode); + + if (!ok) { + memset (buf, '\0', len); + return -1; + } + + /* force termination */ + buf[len - 1] = '\0'; + + return 0; +} + +#else + +#include +#include +#include + +/** + * @ingroup libssh_misc + * + * @brief Get a password from the console. + * + * You should make sure that the buffer is an empty string! + * + * You can also use this function to ask for a username. Then you can fill the + * buffer with the username and it is shows to the users. If the users just + * presses enter the buffer will be untouched. + * + * @code + * char username[128]; + * + * snprintf(username, sizeof(username), "john"); + * + * ssh_getpass("Username:", username, sizeof(username), 1, 0); + * @endcode + * + * The prompt will look like this: + * + * Username: [john] + * + * If you press enter then john is used as the username, or you can type it in + * to change it. + * + * @param[in] prompt The prompt to show to ask for the password. + * + * @param[out] buf The buffer the password should be stored. It NEEDS to be + * empty or filled out. + * + * @param[in] len The length of the buffer. + * + * @param[in] echo Should we echo what you type. + * + * @param[in] verify Should we ask for the password twice. + * + * @return 0 on success, -1 on error. + */ +int ssh_getpass(const char *prompt, + char *buf, + size_t len, + int echo, + int verify) { + struct termios attr; + struct termios old_attr; + int ok = 0; + int fd = -1; + + /* fgets needs at least len - 1 */ + if (prompt == NULL || buf == NULL || len < 2) { + return -1; + } + + if (isatty(STDIN_FILENO)) { + ZERO_STRUCT(attr); + ZERO_STRUCT(old_attr); + + /* get local terminal attributes */ + if (tcgetattr(STDIN_FILENO, &attr) < 0) { + perror("tcgetattr"); + return -1; + } + + /* save terminal attributes */ + memcpy(&old_attr, &attr, sizeof(attr)); + if((fd = fcntl(0, F_GETFL, 0)) < 0) { + perror("fcntl"); + return -1; + } + + /* disable echo */ + if (!echo) { + attr.c_lflag &= ~(ECHO); + } + + /* write attributes to terminal */ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) < 0) { + perror("tcsetattr"); + return -1; + } + } + + /* disable nonblocking I/O */ + if (fd & O_NDELAY) { + fcntl(0, F_SETFL, fd & ~O_NDELAY); + } + + ok = ssh_gets(prompt, buf, len, verify); + + if (isatty(STDIN_FILENO)) { + /* reset terminal */ + tcsetattr(STDIN_FILENO, TCSANOW, &old_attr); + } + + /* close fd */ + if (fd & O_NDELAY) { + fcntl(0, F_SETFL, fd); + } + + if (!ok) { + memset (buf, '\0', len); + return -1; + } + + /* force termination */ + buf[len - 1] = '\0'; + + return 0; +} + +#endif + +/* vim: set ts=4 sw=4 et cindent syntax=c.doxygen: */ diff --git a/libssh/src/gzip.c b/libssh/src/gzip.c new file mode 100644 index 00000000..339217d4 --- /dev/null +++ b/libssh/src/gzip.c @@ -0,0 +1,221 @@ +/* + * gzip.c - hooks for compression of packets + * + * This file is part of the SSH Library + * + * Copyright (c) 2003 by Aris Adamantiadis + * Copyright (c) 2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/buffer.h" +#include "libssh/crypto.h" +#include "libssh/session.h" + +#define BLOCKSIZE 4092 + +static z_stream *initcompress(ssh_session session, int level) { + z_stream *stream = NULL; + int status; + + stream = malloc(sizeof(z_stream)); + if (stream == NULL) { + return NULL; + } + memset(stream, 0, sizeof(z_stream)); + + status = deflateInit(stream, level); + if (status != Z_OK) { + SAFE_FREE(stream); + ssh_set_error(session, SSH_FATAL, + "status %d inititalising zlib deflate", status); + return NULL; + } + + return stream; +} + +static ssh_buffer gzip_compress(ssh_session session,ssh_buffer source,int level){ + z_stream *zout = session->current_crypto->compress_out_ctx; + void *in_ptr = buffer_get_rest(source); + unsigned long in_size = buffer_get_rest_len(source); + ssh_buffer dest = NULL; + unsigned char out_buf[BLOCKSIZE] = {0}; + unsigned long len; + int status; + + if(zout == NULL) { + zout = session->current_crypto->compress_out_ctx = initcompress(session, level); + if (zout == NULL) { + return NULL; + } + } + + dest = ssh_buffer_new(); + if (dest == NULL) { + return NULL; + } + + zout->next_out = out_buf; + zout->next_in = in_ptr; + zout->avail_in = in_size; + do { + zout->avail_out = BLOCKSIZE; + status = deflate(zout, Z_PARTIAL_FLUSH); + if (status != Z_OK) { + ssh_buffer_free(dest); + ssh_set_error(session, SSH_FATAL, + "status %d deflating zlib packet", status); + return NULL; + } + len = BLOCKSIZE - zout->avail_out; + if (buffer_add_data(dest, out_buf, len) < 0) { + ssh_buffer_free(dest); + return NULL; + } + zout->next_out = out_buf; + } while (zout->avail_out == 0); + + return dest; +} + +int compress_buffer(ssh_session session, ssh_buffer buf) { + ssh_buffer dest = NULL; + + dest = gzip_compress(session, buf, session->opts.compressionlevel); + if (dest == NULL) { + return -1; + } + + if (buffer_reinit(buf) < 0) { + ssh_buffer_free(dest); + return -1; + } + + if (buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) { + ssh_buffer_free(dest); + return -1; + } + + ssh_buffer_free(dest); + return 0; +} + +/* decompression */ + +static z_stream *initdecompress(ssh_session session) { + z_stream *stream = NULL; + int status; + + stream = malloc(sizeof(z_stream)); + if (stream == NULL) { + return NULL; + } + memset(stream,0,sizeof(z_stream)); + + status = inflateInit(stream); + if (status != Z_OK) { + SAFE_FREE(stream); + ssh_set_error(session, SSH_FATAL, + "Status = %d initiating inflate context!", status); + return NULL; + } + + return stream; +} + +static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen) { + z_stream *zin = session->current_crypto->compress_in_ctx; + void *in_ptr = buffer_get_rest(source); + unsigned long in_size = buffer_get_rest_len(source); + unsigned char out_buf[BLOCKSIZE] = {0}; + ssh_buffer dest = NULL; + unsigned long len; + int status; + + if (zin == NULL) { + zin = session->current_crypto->compress_in_ctx = initdecompress(session); + if (zin == NULL) { + return NULL; + } + } + + dest = ssh_buffer_new(); + if (dest == NULL) { + return NULL; + } + + zin->next_out = out_buf; + zin->next_in = in_ptr; + zin->avail_in = in_size; + + do { + zin->avail_out = BLOCKSIZE; + status = inflate(zin, Z_PARTIAL_FLUSH); + if (status != Z_OK && status != Z_BUF_ERROR) { + ssh_set_error(session, SSH_FATAL, + "status %d inflating zlib packet", status); + ssh_buffer_free(dest); + return NULL; + } + + len = BLOCKSIZE - zin->avail_out; + if (buffer_add_data(dest,out_buf,len) < 0) { + ssh_buffer_free(dest); + return NULL; + } + if (buffer_get_rest_len(dest) > maxlen){ + /* Size of packet exceeded, avoid a denial of service attack */ + ssh_buffer_free(dest); + return NULL; + } + zin->next_out = out_buf; + } while (zin->avail_out == 0); + + return dest; +} + +int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen){ + ssh_buffer dest = NULL; + + dest = gzip_decompress(session,buf, maxlen); + if (dest == NULL) { + return -1; + } + + if (buffer_reinit(buf) < 0) { + ssh_buffer_free(dest); + return -1; + } + + if (buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) { + ssh_buffer_free(dest); + return -1; + } + + ssh_buffer_free(dest); + return 0; +} + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/init.c b/libssh/src/init.c new file mode 100644 index 00000000..241b8618 --- /dev/null +++ b/libssh/src/init.c @@ -0,0 +1,85 @@ +/* + * init.c - initialization and finalization of the library + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/priv.h" +#include "libssh/socket.h" +#include "libssh/dh.h" +#include "libssh/poll.h" +#include "libssh/threads.h" + +#ifdef _WIN32 +#include +#endif + +/** + * @defgroup libssh The libssh API + * + * The libssh library is implementing the SSH protocols and some of its + * extensions. This group of functions is mostly used to implment a SSH client. + * Some function are needed to implement a SSH server too. + * + * @{ + */ + +/** + * @brief Initialize global cryptographic data structures. + * + * This function should only be called once, at the beginning of the program, in + * the main thread. It may be omitted if your program is not multithreaded. + * + * @returns 0 on success, -1 if an error occured. + */ +int ssh_init(void) { + if(ssh_threads_init()) + return -1; + if(ssh_crypto_init()) + return -1; + if(ssh_socket_init()) + return -1; + return 0; +} + + +/** + * @brief Finalize and cleanup all libssh and cryptographic data structures. + * + * This function should only be called once, at the end of the program! + * + * @returns 0 on succes, -1 if an error occured. + * + @returns 0 otherwise + */ +int ssh_finalize(void) { + ssh_crypto_finalize(); + ssh_socket_cleanup(); + /* It is important to finalize threading after CRYPTO because + * it still depends on it */ + ssh_threads_finalize(); + + return 0; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/kex.c b/libssh/src/kex.c new file mode 100644 index 00000000..e900a91a --- /dev/null +++ b/libssh/src/kex.c @@ -0,0 +1,501 @@ +/* + * kex.c - key exchange + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/buffer.h" +#include "libssh/dh.h" +#include "libssh/kex.h" +#include "libssh/session.h" +#include "libssh/ssh2.h" +#include "libssh/string.h" + +#ifdef HAVE_LIBGCRYPT +# define BLOWFISH "blowfish-cbc," +# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc," +# define DES "3des-cbc,des-cbc-ssh1" +#elif defined(HAVE_LIBCRYPTO) +# ifdef HAVE_OPENSSL_BLOWFISH_H +# define BLOWFISH "blowfish-cbc," +# else +# define BLOWFISH "" +# endif +# ifdef HAVE_OPENSSL_AES_H +# ifdef BROKEN_AES_CTR +# define AES "aes256-cbc,aes192-cbc,aes128-cbc," +# else +# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc," +# endif /* BROKEN_AES_CTR */ +# else +# define AES "" +# endif +# define DES "3des-cbc,des-cbc-ssh1" +#endif + +#ifdef WITH_ZLIB +#define ZLIB "none,zlib,zlib@openssh.com" +#else +#define ZLIB "none" +#endif + +#ifdef HAVE_ECDH +#define KEY_EXCHANGE "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" +#define HOSTKEYS "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss" +#else +#define KEY_EXCHANGE "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" +#define HOSTKEYS "ssh-rsa,ssh-dss" +#endif + +#define KEX_METHODS_SIZE 10 + +/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ +static const char *default_methods[] = { + KEY_EXCHANGE, + HOSTKEYS, + AES BLOWFISH DES, + AES BLOWFISH DES, + "hmac-sha1", + "hmac-sha1", + "none", + "none", + "", + "", + NULL +}; + +/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ +static const char *supported_methods[] = { + KEY_EXCHANGE, + HOSTKEYS, + AES BLOWFISH DES, + AES BLOWFISH DES, + "hmac-sha1", + "hmac-sha1", + ZLIB, + ZLIB, + "", + "", + NULL +}; + +/* descriptions of the key exchange packet */ +static const char *ssh_kex_descriptions[] = { + "kex algos", + "server host key algo", + "encryption client->server", + "encryption server->client", + "mac algo client->server", + "mac algo server->client", + "compression algo client->server", + "compression algo server->client", + "languages client->server", + "languages server->client", + NULL +}; + +/* tokenize will return a token of strings delimited by ",". the first element has to be freed */ +static char **tokenize(const char *chain){ + char **tokens; + int n=1; + int i=0; + char *tmp; + char *ptr; + + tmp = strdup(chain); + if (tmp == NULL) { + return NULL; + } + ptr = tmp; + while(*ptr){ + if(*ptr==','){ + n++; + *ptr=0; + } + ptr++; + } + /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */ + tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */ + if (tokens == NULL) { + SAFE_FREE(tmp); + return NULL; + } + ptr=tmp; + for(i=0;i= KEX_METHODS_SIZE) { + return NULL; + } + + return supported_methods[algo]; +} + +const char *ssh_kex_get_description(uint32_t algo) { + if (algo >= KEX_METHODS_SIZE) { + return NULL; + } + + return ssh_kex_descriptions[algo]; +} + +/* find_matching gets 2 parameters : a list of available objects (available_d), separated by colons,*/ +/* and a list of preferred objects (preferred_d) */ +/* it will return a strduped pointer on the first preferred object found in the available objects list */ + +char *ssh_find_matching(const char *available_d, const char *preferred_d){ + char ** tok_available, **tok_preferred; + int i_avail, i_pref; + char *ret; + + if ((available_d == NULL) || (preferred_d == NULL)) { + return NULL; /* don't deal with null args */ + } + + tok_available = tokenize(available_d); + if (tok_available == NULL) { + return NULL; + } + + tok_preferred = tokenize(preferred_d); + if (tok_preferred == NULL) { + SAFE_FREE(tok_available[0]); + SAFE_FREE(tok_available); + return NULL; + } + + for(i_pref=0; tok_preferred[i_pref] ; ++i_pref){ + for(i_avail=0; tok_available[i_avail]; ++i_avail){ + if(strcmp(tok_available[i_avail],tok_preferred[i_pref]) == 0){ + /* match */ + ret=strdup(tok_available[i_avail]); + /* free the tokens */ + SAFE_FREE(tok_available[0]); + SAFE_FREE(tok_preferred[0]); + SAFE_FREE(tok_available); + SAFE_FREE(tok_preferred); + return ret; + } + } + } + SAFE_FREE(tok_available[0]); + SAFE_FREE(tok_preferred[0]); + SAFE_FREE(tok_available); + SAFE_FREE(tok_preferred); + return NULL; +} + +SSH_PACKET_CALLBACK(ssh_packet_kexinit){ + int server_kex=session->server; + ssh_string str = NULL; + char *strings[KEX_METHODS_SIZE]; + int i; + + enter_function(); + (void)type; + (void)user; + memset(strings, 0, sizeof(strings)); + if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED){ + ssh_log(session,SSH_LOG_WARNING, "Other side initiating key re-exchange"); + } else if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){ + ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); + goto error; + } + if (server_kex) { + if (buffer_get_data(packet,session->next_crypto->client_kex.cookie,16) != 16) { + ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); + goto error; + } + + if (hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie) < 0) { + ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); + goto error; + } + } else { + if (buffer_get_data(packet,session->next_crypto->server_kex.cookie,16) != 16) { + ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); + goto error; + } + + if (hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie) < 0) { + ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); + goto error; + } + } + + for (i = 0; i < KEX_METHODS_SIZE; i++) { + str = buffer_get_ssh_string(packet); + if (str == NULL) { + break; + } + + if (buffer_add_ssh_string(session->in_hashbuf, str) < 0) { + ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer"); + goto error; + } + + strings[i] = ssh_string_to_char(str); + if (strings[i] == NULL) { + ssh_set_error_oom(session); + goto error; + } + ssh_string_free(str); + str = NULL; + } + + /* copy the server kex info into an array of strings */ + if (server_kex) { + for (i = 0; i < SSH_KEX_METHODS; i++) { + session->next_crypto->client_kex.methods[i] = strings[i]; + } + } else { /* client */ + for (i = 0; i < SSH_KEX_METHODS; i++) { + session->next_crypto->server_kex.methods[i] = strings[i]; + } + } + + leave_function(); + session->session_state=SSH_SESSION_STATE_KEXINIT_RECEIVED; + session->dh_handshake_state=DH_STATE_INIT; + session->ssh_connection_callback(session); + return SSH_PACKET_USED; +error: + ssh_string_free(str); + for (i = 0; i < SSH_KEX_METHODS; i++) { + SAFE_FREE(strings[i]); + } + + session->session_state = SSH_SESSION_STATE_ERROR; + leave_function(); + return SSH_PACKET_USED; +} + +void ssh_list_kex(ssh_session session, struct ssh_kex_struct *kex) { + int i = 0; + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("session cookie", kex->cookie, 16); +#endif + + for(i = 0; i < SSH_KEX_METHODS; i++) { + if (kex->methods[i] == NULL) { + continue; + } + ssh_log(session, SSH_LOG_FUNCTIONS, "%s: %s", + ssh_kex_descriptions[i], kex->methods[i]); + } +} + +/** + * @brief sets the key exchange parameters to be sent to the server, + * in function of the options and available methods. + */ +int set_client_kex(ssh_session session){ + struct ssh_kex_struct *client= &session->next_crypto->client_kex; + const char *wanted; + int i; + + ssh_get_random(client->cookie, 16, 0); + + memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **)); + for (i = 0; i < KEX_METHODS_SIZE; i++) { + wanted = session->opts.wanted_methods[i]; + if (wanted == NULL) + wanted = default_methods[i]; + client->methods[i] = strdup(wanted); + } + + return SSH_OK; +} + +/** @brief Select the different methods on basis of client's and + * server's kex messages, and watches out if a match is possible. + */ +int ssh_kex_select_methods (ssh_session session){ + struct ssh_kex_struct *server = &session->next_crypto->server_kex; + struct ssh_kex_struct *client = &session->next_crypto->client_kex; + int rc = SSH_ERROR; + int i; + + enter_function(); + + for (i = 0; i < KEX_METHODS_SIZE; i++) { + session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]); + if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){ + ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]", + ssh_kex_descriptions[i],server->methods[i],client->methods[i]); + goto error; + } else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) { + /* we can safely do that for languages */ + session->next_crypto->kex_methods[i] = strdup(""); + } + } + if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){ + session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1; + } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha1") == 0){ + session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1; + } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){ + session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256; + } + rc = SSH_OK; +error: + leave_function(); + return rc; +} + + +/* this function only sends the predefined set of kex methods */ +int ssh_send_kex(ssh_session session, int server_kex) { + struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex : + &session->next_crypto->client_kex); + ssh_string str = NULL; + int i; + + enter_function(); + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXINIT) < 0) { + goto error; + } + if (buffer_add_data(session->out_buffer, kex->cookie, 16) < 0) { + goto error; + } + + if (hashbufout_add_cookie(session) < 0) { + goto error; + } + + ssh_list_kex(session, kex); + + for (i = 0; i < KEX_METHODS_SIZE; i++) { + str = ssh_string_from_char(kex->methods[i]); + if (str == NULL) { + goto error; + } + + if (buffer_add_ssh_string(session->out_hashbuf, str) < 0) { + goto error; + } + if (buffer_add_ssh_string(session->out_buffer, str) < 0) { + goto error; + } + ssh_string_free(str); + } + + if (buffer_add_u8(session->out_buffer, 0) < 0) { + goto error; + } + if (buffer_add_u32(session->out_buffer, 0) < 0) { + goto error; + } + + if (packet_send(session) == SSH_ERROR) { + leave_function(); + return -1; + } + + leave_function(); + return 0; +error: + buffer_reinit(session->out_buffer); + buffer_reinit(session->out_hashbuf); + ssh_string_free(str); + + leave_function(); + return -1; +} + +/* returns 1 if at least one of the name algos is in the default algorithms table */ +int verify_existing_algo(int algo, const char *name){ + char *ptr; + if(algo>9 || algo <0) + return -1; + ptr=ssh_find_matching(supported_methods[algo],name); + if(ptr){ + free(ptr); + return 1; + } + return 0; +} + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/kex1.c b/libssh/src/kex1.c new file mode 100644 index 00000000..b11cf007 --- /dev/null +++ b/libssh/src/kex1.c @@ -0,0 +1,496 @@ +/* + * kex.c - key exchange + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#ifndef _WIN32 +#include +#endif + +#include "libssh/priv.h" +#include "libssh/buffer.h" +#include "libssh/crypto.h" +#include "libssh/kex.h" +#include "libssh/keys.h" +#include "libssh/session.h" +#include "libssh/ssh1.h" +#include "libssh/wrapper.h" + +/* SSHv1 functions */ + +/* makes a STRING contating 3 strings : ssh-rsa1,e and n */ +/* this is a public key in openssh's format */ +static ssh_string make_rsa1_string(ssh_string e, ssh_string n){ + ssh_buffer buffer = NULL; + ssh_string rsa = NULL; + ssh_string ret = NULL; + + buffer = ssh_buffer_new(); + rsa = ssh_string_from_char("ssh-rsa1"); + + if (buffer_add_ssh_string(buffer, rsa) < 0) { + goto error; + } + if (buffer_add_ssh_string(buffer, e) < 0) { + goto error; + } + if (buffer_add_ssh_string(buffer, n) < 0) { + goto error; + } + + ret = ssh_string_new(ssh_buffer_get_len(buffer)); + if (ret == NULL) { + goto error; + } + + ssh_string_fill(ret, ssh_buffer_get_begin(buffer), ssh_buffer_get_len(buffer)); +error: + ssh_buffer_free(buffer); + ssh_string_free(rsa); + + return ret; +} + +static int build_session_id1(ssh_session session, ssh_string servern, + ssh_string hostn) { + MD5CTX md5 = NULL; + + md5 = md5_init(); + if (md5 == NULL) { + return -1; + } + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("host modulus",ssh_string_data(hostn),ssh_string_len(hostn)); + ssh_print_hexa("server modulus",ssh_string_data(servern),ssh_string_len(servern)); +#endif + md5_update(md5,ssh_string_data(hostn),ssh_string_len(hostn)); + md5_update(md5,ssh_string_data(servern),ssh_string_len(servern)); + md5_update(md5,session->next_crypto->server_kex.cookie,8); + if(session->next_crypto->session_id != NULL) + SAFE_FREE(session->next_crypto->session_id); + session->next_crypto->session_id = malloc(MD5_DIGEST_LEN); + if(session->next_crypto->session_id == NULL){ + ssh_set_error_oom(session); + return SSH_ERROR; + } + md5_final(session->next_crypto->session_id,md5); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("session_id",session->next_crypto->session_id,MD5_DIGEST_LEN); +#endif + + return 0; +} + +/* returns 1 if the modulus of k1 is < than the one of k2 */ +static int modulus_smaller(ssh_public_key k1, ssh_public_key k2){ + bignum n1; + bignum n2; + int res; +#ifdef HAVE_LIBGCRYPT + gcry_sexp_t sexp; + sexp=gcry_sexp_find_token(k1->rsa_pub,"n",0); + n1=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + sexp=gcry_sexp_find_token(k2->rsa_pub,"n",0); + n2=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); +#elif defined HAVE_LIBCRYPTO + n1=k1->rsa_pub->n; + n2=k2->rsa_pub->n; +#endif + if(bignum_cmp(n1,n2)<0) + res=1; + else + res=0; +#ifdef HAVE_LIBGCRYPT + bignum_free(n1); + bignum_free(n2); +#endif + return res; + +} + +static ssh_string ssh_encrypt_rsa1(ssh_session session, + ssh_string data, + ssh_public_key key) { + ssh_string str = NULL; + size_t len = ssh_string_len(data); + size_t size = 0; +#ifdef HAVE_LIBGCRYPT + const char *tmp = NULL; + gcry_sexp_t ret_sexp; + gcry_sexp_t data_sexp; + + if (gcry_sexp_build(&data_sexp, NULL, "(data(flags pkcs1)(value %b))", + len, ssh_string_data(data))) { + ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error"); + return NULL; + } + if (gcry_pk_encrypt(&ret_sexp, data_sexp, key->rsa_pub)) { + gcry_sexp_release(data_sexp); + ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error"); + return NULL; + } + + gcry_sexp_release(data_sexp); + + data_sexp = gcry_sexp_find_token(ret_sexp, "a", 0); + if (data_sexp == NULL) { + ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error"); + gcry_sexp_release(ret_sexp); + return NULL; + } + tmp = gcry_sexp_nth_data(data_sexp, 1, &size); + if (*tmp == 0) { + size--; + tmp++; + } + + str = ssh_string_new(size); + if (str == NULL) { + ssh_set_error(session, SSH_FATAL, "Not enough space"); + gcry_sexp_release(data_sexp); + gcry_sexp_release(ret_sexp); + return NULL; + } + ssh_string_fill(str, tmp, size); + + gcry_sexp_release(data_sexp); + gcry_sexp_release(ret_sexp); +#elif defined HAVE_LIBCRYPTO + size = RSA_size(key->rsa_pub); + + str = ssh_string_new(size); + if (str == NULL) { + ssh_set_error(session, SSH_FATAL, "Not enough space"); + return NULL; + } + + if (RSA_public_encrypt(len, ssh_string_data(data), ssh_string_data(str), key->rsa_pub, + RSA_PKCS1_PADDING) < 0) { + ssh_string_free(str); + return NULL; + } +#endif + + return str; +} + +#define ABS(A) ( (A)<0 ? -(A):(A) ) +static ssh_string encrypt_session_key(ssh_session session, ssh_public_key srvkey, + ssh_public_key hostkey, int slen, int hlen) { + unsigned char buffer[32] = {0}; + int i; + ssh_string data1 = NULL; + ssh_string data2 = NULL; + if(session->next_crypto->encryptkey != NULL) + SAFE_FREE(session->next_crypto->encryptkey); + if(session->next_crypto->decryptkey != NULL) + SAFE_FREE(session->next_crypto->decryptkey); + if(session->next_crypto->encryptIV != NULL) + SAFE_FREE(session->next_crypto->encryptIV); + if(session->next_crypto->decryptIV != NULL) + SAFE_FREE(session->next_crypto->decryptIV); + session->next_crypto->encryptkey = malloc(32); + session->next_crypto->decryptkey = malloc(32); + session->next_crypto->encryptIV = malloc(32); + session->next_crypto->decryptIV = malloc(32); + if(session->next_crypto->encryptkey == NULL || + session->next_crypto->decryptkey == NULL || + session->next_crypto->encryptIV == NULL || + session->next_crypto->decryptIV == NULL){ + ssh_set_error_oom(session); + return NULL; + } + /* first, generate a session key */ + ssh_get_random(session->next_crypto->encryptkey, 32, 1); + memcpy(buffer, session->next_crypto->encryptkey, 32); + memcpy(session->next_crypto->decryptkey, session->next_crypto->encryptkey, 32); + memset(session->next_crypto->encryptIV, 0, 32); + memset(session->next_crypto->decryptIV, 0, 32); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("session key",buffer,32); +#endif + + /* xor session key with session_id */ + for (i = 0; i < 16; i++) { + buffer[i] ^= session->next_crypto->session_id[i]; + } + data1 = ssh_string_new(32); + if (data1 == NULL) { + return NULL; + } + ssh_string_fill(data1, buffer, 32); + if (ABS(hlen - slen) < 128){ + ssh_log(session, SSH_LOG_FUNCTIONS, + "Difference between server modulus and host modulus is only %d. " + "It's illegal and may not work", + ABS(hlen - slen)); + } + + if (modulus_smaller(srvkey, hostkey)) { + data2 = ssh_encrypt_rsa1(session, data1, srvkey); + ssh_string_free(data1); + data1 = NULL; + if (data2 == NULL) { + return NULL; + } + data1 = ssh_encrypt_rsa1(session, data2, hostkey); + ssh_string_free(data2); + if (data1 == NULL) { + return NULL; + } + } else { + data2 = ssh_encrypt_rsa1(session, data1, hostkey); + ssh_string_free(data1); + data1 = NULL; + if (data2 == NULL) { + return NULL; + } + data1 = ssh_encrypt_rsa1(session, data2, srvkey); + ssh_string_free(data2); + if (data1 == NULL) { + return NULL; + } + } + + return data1; +} + +/* 2 SSH_SMSG_PUBLIC_KEY + * + * 8 bytes anti_spoofing_cookie + * 32-bit int server_key_bits + * mp-int server_key_public_exponent + * mp-int server_key_public_modulus + * 32-bit int host_key_bits + * mp-int host_key_public_exponent + * mp-int host_key_public_modulus + * 32-bit int protocol_flags + * 32-bit int supported_ciphers_mask + * 32-bit int supported_authentications_mask + */ +/** + * @brief Wait for a SSH_SMSG_PUBLIC_KEY and does the key exchange + */ +SSH_PACKET_CALLBACK(ssh_packet_publickey1){ + ssh_string server_exp = NULL; + ssh_string server_mod = NULL; + ssh_string host_exp = NULL; + ssh_string host_mod = NULL; + ssh_string serverkey = NULL; + ssh_string hostkey = NULL; + ssh_public_key srv = NULL; + ssh_public_key host = NULL; + uint32_t server_bits; + uint32_t host_bits; + uint32_t protocol_flags; + uint32_t supported_ciphers_mask; + uint32_t supported_authentications_mask; + ssh_string enc_session = NULL; + uint16_t bits; + int ko; + uint32_t support_3DES = 0; + uint32_t support_DES = 0; + enter_function(); + (void)type; + (void)user; + ssh_log(session, SSH_LOG_PROTOCOL, "Got a SSH_SMSG_PUBLIC_KEY"); + if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){ + ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); + goto error; + } + if (buffer_get_data(packet, session->next_crypto->server_kex.cookie, 8) != 8) { + ssh_set_error(session, SSH_FATAL, "Can't get cookie in buffer"); + goto error; + } + + buffer_get_u32(packet, &server_bits); + server_exp = buffer_get_mpint(packet); + if (server_exp == NULL) { + goto error; + } + server_mod = buffer_get_mpint(packet); + if (server_mod == NULL) { + goto error; + } + buffer_get_u32(packet, &host_bits); + host_exp = buffer_get_mpint(packet); + if (host_exp == NULL) { + goto error; + } + host_mod = buffer_get_mpint(packet); + if (host_mod == NULL) { + goto error; + } + buffer_get_u32(packet, &protocol_flags); + buffer_get_u32(packet, &supported_ciphers_mask); + ko = buffer_get_u32(packet, &supported_authentications_mask); + + if ((ko != sizeof(uint32_t)) || !host_mod || !host_exp + || !server_mod || !server_exp) { + ssh_log(session, SSH_LOG_RARE, "Invalid SSH_SMSG_PUBLIC_KEY packet"); + ssh_set_error(session, SSH_FATAL, "Invalid SSH_SMSG_PUBLIC_KEY packet"); + goto error; + } + + server_bits = ntohl(server_bits); + host_bits = ntohl(host_bits); + protocol_flags = ntohl(protocol_flags); + supported_ciphers_mask = ntohl(supported_ciphers_mask); + supported_authentications_mask = ntohl(supported_authentications_mask); + ssh_log(session, SSH_LOG_PROTOCOL, + "Server bits: %d; Host bits: %d; Protocol flags: %.8lx; " + "Cipher mask: %.8lx; Auth mask: %.8lx", + server_bits, + host_bits, + (unsigned long int) protocol_flags, + (unsigned long int) supported_ciphers_mask, + (unsigned long int) supported_authentications_mask); + + serverkey = make_rsa1_string(server_exp, server_mod); + if (serverkey == NULL) { + goto error; + } + hostkey = make_rsa1_string(host_exp,host_mod); + if (serverkey == NULL) { + goto error; + } + if (build_session_id1(session, server_mod, host_mod) < 0) { + goto error; + } + + srv = publickey_from_string(session, serverkey); + if (srv == NULL) { + goto error; + } + host = publickey_from_string(session, hostkey); + if (host == NULL) { + goto error; + } + + session->next_crypto->server_pubkey = ssh_string_copy(hostkey); + if (session->next_crypto->server_pubkey == NULL) { + goto error; + } + session->next_crypto->server_pubkey_type = "ssh-rsa1"; + + /* now, we must choose an encryption algo */ + /* hardcode 3des */ + // + support_3DES = (supported_ciphers_mask & (1<out_buffer, SSH_CMSG_SESSION_KEY) < 0) { + goto error; + } + if (buffer_add_u8(session->out_buffer, support_3DES ? SSH_CIPHER_3DES : SSH_CIPHER_DES) < 0) { + goto error; + } + if (buffer_add_data(session->out_buffer, session->next_crypto->server_kex.cookie, 8) < 0) { + goto error; + } + + enc_session = encrypt_session_key(session, srv, host, server_bits, host_bits); + if (enc_session == NULL) { + goto error; + } + + bits = ssh_string_len(enc_session) * 8 - 7; + ssh_log(session, SSH_LOG_PROTOCOL, "%d bits, %" PRIdS " bytes encrypted session", + bits, ssh_string_len(enc_session)); + bits = htons(bits); + /* the encrypted mpint */ + if (buffer_add_data(session->out_buffer, &bits, sizeof(uint16_t)) < 0) { + goto error; + } + if (buffer_add_data(session->out_buffer, ssh_string_data(enc_session), + ssh_string_len(enc_session)) < 0) { + goto error; + } + /* the protocol flags */ + if (buffer_add_u32(session->out_buffer, 0) < 0) { + goto error; + } + session->session_state=SSH_SESSION_STATE_KEXINIT_RECEIVED; + if (packet_send(session) == SSH_ERROR) { + goto error; + } + + /* we can set encryption */ + if(crypt_set_algorithms(session, support_3DES ? SSH_3DES : SSH_DES)){ + goto error; + } + + session->current_crypto = session->next_crypto; + session->next_crypto = NULL; + goto end; +error: + session->session_state=SSH_SESSION_STATE_ERROR; +end: + + ssh_string_free(host_mod); + ssh_string_free(host_exp); + ssh_string_free(server_mod); + ssh_string_free(server_exp); + ssh_string_free(serverkey); + ssh_string_free(hostkey); + ssh_string_free(enc_session); + + publickey_free(srv); + publickey_free(host); + + leave_function(); + return SSH_PACKET_USED; +} + +int ssh_get_kex1(ssh_session session) { + int ret=SSH_ERROR; + enter_function(); + ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_PUBLIC_KEY"); + /* Here the callback is called */ + while(session->session_state==SSH_SESSION_STATE_INITIAL_KEX){ + ssh_handle_packets(session, SSH_TIMEOUT_USER); + } + if(session->session_state==SSH_SESSION_STATE_ERROR) + goto error; + ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_SUCCESS"); + /* Waiting for SSH_SMSG_SUCCESS */ + while(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){ + ssh_handle_packets(session, SSH_TIMEOUT_USER); + } + if(session->session_state==SSH_SESSION_STATE_ERROR) + goto error; + ssh_log(session, SSH_LOG_PROTOCOL, "received SSH_SMSG_SUCCESS\n"); + ret=SSH_OK; +error: + leave_function(); + return ret; +} diff --git a/libssh/src/known_hosts.c b/libssh/src/known_hosts.c new file mode 100644 index 00000000..2d281e72 --- /dev/null +++ b/libssh/src/known_hosts.c @@ -0,0 +1,666 @@ +/* + * keyfiles.c - private and public key handling for authentication. + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * Copyright (c) 2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/buffer.h" +#include "libssh/misc.h" +#include "libssh/pki.h" +#include "libssh/options.h" + +/*todo: remove this include */ +#include "libssh/string.h" + +#ifdef HAVE_LIBGCRYPT +#include +#elif defined HAVE_LIBCRYPTO +#include +#include +#include +#include +#endif /* HAVE_LIBCRYPTO */ + +#ifndef _WIN32 +# include +# include +#endif + +/** + * @addtogroup libssh_session + * + * @{ + */ + +static int alldigits(const char *s) { + while (*s) { + if (isdigit(*s)) { + s++; + } else { + return 0; + } + } + + return 1; +} + +/** + * @internal + * + * @brief Free a token array. + */ +static void tokens_free(char **tokens) { + if (tokens == NULL) { + return; + } + + SAFE_FREE(tokens[0]); + /* It's not needed to free other pointers because tokens generated by + * space_tokenize fit all in one malloc + */ + SAFE_FREE(tokens); +} + +/** + * @internal + * + * @brief Return one line of known host file. + * + * This will return a token array containing (host|ip), keytype and key. + * + * @param[out] file A pointer to the known host file. Could be pointing to + * NULL at start. + * + * @param[in] filename The file name of the known host file. + * + * @param[out] found_type A pointer to a string to be set with the found key + * type. + * + * @returns The found_type type of key (ie "dsa","ssh-rsa1"). Don't + * free that value. NULL if no match was found or the file + * was not found. + */ +static char **ssh_get_knownhost_line(ssh_session session, FILE **file, + const char *filename, const char **found_type) { + char buffer[4096] = {0}; + char *ptr; + char **tokens; + + enter_function(); + + if(*file == NULL){ + *file = fopen(filename,"r"); + if (*file == NULL) { + leave_function(); + return NULL; + } + } + + while (fgets(buffer, sizeof(buffer), *file)) { + ptr = strchr(buffer, '\n'); + if (ptr) { + *ptr = '\0'; + } + + ptr = strchr(buffer,'\r'); + if (ptr) { + *ptr = '\0'; + } + + if (!buffer[0] || buffer[0] == '#') { + continue; /* skip empty lines */ + } + + tokens = space_tokenize(buffer); + if (tokens == NULL) { + fclose(*file); + *file = NULL; + leave_function(); + return NULL; + } + + if(!tokens[0] || !tokens[1] || !tokens[2]) { + /* it should have at least 3 tokens */ + tokens_free(tokens); + continue; + } + + *found_type = tokens[1]; + if (tokens[3]) { + /* openssh rsa1 format has 4 tokens on the line. Recognize it + by the fact that everything is all digits */ + if (tokens[4]) { + /* that's never valid */ + tokens_free(tokens); + continue; + } + if (alldigits(tokens[1]) && alldigits(tokens[2]) && alldigits(tokens[3])) { + *found_type = "ssh-rsa1"; + } else { + /* 3 tokens only, not four */ + tokens_free(tokens); + continue; + } + } + leave_function(); + return tokens; + } + + fclose(*file); + *file = NULL; + + /* we did not find anything, end of file*/ + leave_function(); + return NULL; +} + +/** + * @brief Check the public key in the known host line matches the public key of + * the currently connected server. + * + * @param[in] session The SSH session to use. + * + * @param[in] tokens A list of tokens in the known_hosts line. + * + * @returns 1 if the key matches, 0 if the key doesn't match and -1 + * on error. + */ +static int check_public_key(ssh_session session, char **tokens) { + ssh_string pubkey = session->current_crypto->server_pubkey; + ssh_buffer pubkey_buffer; + char *pubkey_64; + + /* ok we found some public key in known hosts file. now un-base64it */ + if (alldigits(tokens[1])) { + /* openssh rsa1 format */ + bignum tmpbn; + ssh_string tmpstring; + unsigned int len; + int i; + + pubkey_buffer = ssh_buffer_new(); + if (pubkey_buffer == NULL) { + return -1; + } + + tmpstring = ssh_string_from_char("ssh-rsa1"); + if (tmpstring == NULL) { + ssh_buffer_free(pubkey_buffer); + return -1; + } + + if (buffer_add_ssh_string(pubkey_buffer, tmpstring) < 0) { + ssh_buffer_free(pubkey_buffer); + ssh_string_free(tmpstring); + return -1; + } + ssh_string_free(tmpstring); + + for (i = 2; i < 4; i++) { /* e, then n */ + tmpbn = NULL; + bignum_dec2bn(tokens[i], &tmpbn); + if (tmpbn == NULL) { + ssh_buffer_free(pubkey_buffer); + return -1; + } + /* for some reason, make_bignum_string does not work + because of the padding which it does --kv */ + /* tmpstring = make_bignum_string(tmpbn); */ + /* do it manually instead */ + len = bignum_num_bytes(tmpbn); + tmpstring = malloc(4 + len); + if (tmpstring == NULL) { + ssh_buffer_free(pubkey_buffer); + bignum_free(tmpbn); + return -1; + } + /* TODO: fix the hardcoding */ + tmpstring->size = htonl(len); +#ifdef HAVE_LIBGCRYPT + bignum_bn2bin(tmpbn, len, string_data(tmpstring)); +#elif defined HAVE_LIBCRYPTO + bignum_bn2bin(tmpbn, string_data(tmpstring)); +#endif + bignum_free(tmpbn); + if (buffer_add_ssh_string(pubkey_buffer, tmpstring) < 0) { + ssh_buffer_free(pubkey_buffer); + ssh_string_free(tmpstring); + bignum_free(tmpbn); + return -1; + } + ssh_string_free(tmpstring); + } + } else { + /* ssh-dss or ssh-rsa */ + pubkey_64 = tokens[2]; + pubkey_buffer = base64_to_bin(pubkey_64); + } + + if (pubkey_buffer == NULL) { + ssh_set_error(session, SSH_FATAL, + "Verifying that server is a known host: base64 error"); + return -1; + } + + if (buffer_get_rest_len(pubkey_buffer) != ssh_string_len(pubkey)) { + ssh_buffer_free(pubkey_buffer); + return 0; + } + + /* now test that they are identical */ + if (memcmp(buffer_get_rest(pubkey_buffer), ssh_string_data(pubkey), + buffer_get_rest_len(pubkey_buffer)) != 0) { + ssh_buffer_free(pubkey_buffer); + return 0; + } + + ssh_buffer_free(pubkey_buffer); + return 1; +} + +/** + * @brief Check if a hostname matches a openssh-style hashed known host. + * + * @param[in] host The host to check. + * + * @param[in] hashed The hashed value. + * + * @returns 1 if it matches, 0 otherwise. + */ +static int match_hashed_host(ssh_session session, const char *host, + const char *sourcehash) { + /* Openssh hash structure : + * |1|base64 encoded salt|base64 encoded hash + * hash is produced that way : + * hash := HMAC_SHA1(key=salt,data=host) + */ + unsigned char buffer[256] = {0}; + ssh_buffer salt; + ssh_buffer hash; + HMACCTX mac; + char *source; + char *b64hash; + int match; + unsigned int size; + + enter_function(); + + if (strncmp(sourcehash, "|1|", 3) != 0) { + leave_function(); + return 0; + } + + source = strdup(sourcehash + 3); + if (source == NULL) { + leave_function(); + return 0; + } + + b64hash = strchr(source, '|'); + if (b64hash == NULL) { + /* Invalid hash */ + SAFE_FREE(source); + leave_function(); + return 0; + } + + *b64hash = '\0'; + b64hash++; + + salt = base64_to_bin(source); + if (salt == NULL) { + SAFE_FREE(source); + leave_function(); + return 0; + } + + hash = base64_to_bin(b64hash); + SAFE_FREE(source); + if (hash == NULL) { + ssh_buffer_free(salt); + leave_function(); + return 0; + } + + mac = hmac_init(buffer_get_rest(salt), buffer_get_rest_len(salt), SSH_HMAC_SHA1); + if (mac == NULL) { + ssh_buffer_free(salt); + ssh_buffer_free(hash); + leave_function(); + return 0; + } + size = sizeof(buffer); + hmac_update(mac, host, strlen(host)); + hmac_final(mac, buffer, &size); + + if (size == buffer_get_rest_len(hash) && + memcmp(buffer, buffer_get_rest(hash), size) == 0) { + match = 1; + } else { + match = 0; + } + + ssh_buffer_free(salt); + ssh_buffer_free(hash); + + ssh_log(session, SSH_LOG_PACKET, + "Matching a hashed host: %s match=%d", host, match); + + leave_function(); + return match; +} + +/* How it's working : + * 1- we open the known host file and bitch if it doesn't exist + * 2- we need to examine each line of the file, until going on state SSH_SERVER_KNOWN_OK: + * - there's a match. if the key is good, state is SSH_SERVER_KNOWN_OK, + * else it's SSH_SERVER_KNOWN_CHANGED (or SSH_SERVER_FOUND_OTHER) + * - there's no match : no change + */ + +/** + * @brief Check if the server is known. + * + * Checks the user's known host file for a previous connection to the + * current server. + * + * @param[in] session The SSH session to use. + * + * @returns SSH_SERVER_KNOWN_OK: The server is known and has not changed.\n + * SSH_SERVER_KNOWN_CHANGED: The server key has changed. Either you + * are under attack or the administrator + * changed the key. You HAVE to warn the + * user about a possible attack.\n + * SSH_SERVER_FOUND_OTHER: The server gave use a key of a type while + * we had an other type recorded. It is a + * possible attack.\n + * SSH_SERVER_NOT_KNOWN: The server is unknown. User should + * confirm the MD5 is correct.\n + * SSH_SERVER_FILE_NOT_FOUND: The known host file does not exist. The + * host is thus unknown. File will be + * created if host key is accepted.\n + * SSH_SERVER_ERROR: Some error happened. + * + * @see ssh_get_pubkey_hash() + * + * @bug There is no current way to remove or modify an entry into the known + * host table. + */ +int ssh_is_server_known(ssh_session session) { + FILE *file = NULL; + char **tokens; + char *host; + char *hostport; + const char *type; + int match; + int ret = SSH_SERVER_NOT_KNOWN; + + enter_function(); + + if (session->opts.knownhosts == NULL) { + if (ssh_options_apply(session) < 0) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Can't find a known_hosts file"); + leave_function(); + return SSH_SERVER_FILE_NOT_FOUND; + } + } + + if (session->opts.host == NULL) { + ssh_set_error(session, SSH_FATAL, + "Can't verify host in known hosts if the hostname isn't known"); + leave_function(); + return SSH_SERVER_ERROR; + } + + if (session->current_crypto == NULL){ + ssh_set_error(session, SSH_FATAL, + "ssh_is_host_known called without cryptographic context"); + leave_function(); + return SSH_SERVER_ERROR; + } + host = ssh_lowercase(session->opts.host); + hostport = ssh_hostport(host, session->opts.port); + if (host == NULL || hostport == NULL) { + ssh_set_error_oom(session); + SAFE_FREE(host); + SAFE_FREE(hostport); + leave_function(); + return SSH_SERVER_ERROR; + } + + do { + tokens = ssh_get_knownhost_line(session, + &file, + session->opts.knownhosts, + &type); + + /* End of file, return the current state */ + if (tokens == NULL) { + break; + } + match = match_hashed_host(session, host, tokens[0]); + if (match == 0){ + match = match_hostname(hostport, tokens[0], strlen(tokens[0])); + } + if (match == 0) { + match = match_hostname(host, tokens[0], strlen(tokens[0])); + } + if (match == 0) { + match = match_hashed_host(session, hostport, tokens[0]); + } + if (match) { + /* We got a match. Now check the key type */ + if (strcmp(session->current_crypto->server_pubkey_type, type) != 0) { + ssh_log(session, + SSH_LOG_PACKET, + "ssh_is_server_known: server type [%s] doesn't match the " + "type [%s] in known_hosts file", + session->current_crypto->server_pubkey_type, + type); + /* Different type. We don't override the known_changed error which is + * more important */ + if (ret != SSH_SERVER_KNOWN_CHANGED) + ret = SSH_SERVER_FOUND_OTHER; + tokens_free(tokens); + continue; + } + /* so we know the key type is good. We may get a good key or a bad key. */ + match = check_public_key(session, tokens); + tokens_free(tokens); + + if (match < 0) { + ret = SSH_SERVER_ERROR; + break; + } else if (match == 1) { + ret = SSH_SERVER_KNOWN_OK; + break; + } else if(match == 0) { + /* We override the status with the wrong key state */ + ret = SSH_SERVER_KNOWN_CHANGED; + } + } else { + tokens_free(tokens); + } + } while (1); + + if ((ret == SSH_SERVER_NOT_KNOWN) && + (session->opts.StrictHostKeyChecking == 0)) { + ssh_write_knownhost(session); + ret = SSH_SERVER_KNOWN_OK; + } + + SAFE_FREE(host); + SAFE_FREE(hostport); + if (file != NULL) { + fclose(file); + } + + /* Return the current state at end of file */ + leave_function(); + return ret; +} + +/** + * @brief Write the current server as known in the known hosts file. + * + * This will create the known hosts file if it does not exist. You generaly use + * it when ssh_is_server_known() answered SSH_SERVER_NOT_KNOWN. + * + * @param[in] session The ssh session to use. + * + * @return SSH_OK on success, SSH_ERROR on error. + */ +int ssh_write_knownhost(ssh_session session) { + ssh_key key; + ssh_string pubkey_s; + char *b64_key; + char buffer[4096] = {0}; + FILE *file; + char *dir; + char *host; + char *hostport; + int rc; + + if (session->opts.host == NULL) { + ssh_set_error(session, SSH_FATAL, + "Can't write host in known hosts if the hostname isn't known"); + return SSH_ERROR; + } + + host = ssh_lowercase(session->opts.host); + /* If using a nonstandard port, save the host in the [host]:port format */ + if(session->opts.port != 22) { + hostport = ssh_hostport(host, session->opts.port); + SAFE_FREE(host); + if (hostport == NULL) { + return SSH_ERROR; + } + host = hostport; + hostport = NULL; + } + + if (session->opts.knownhosts == NULL) { + if (ssh_options_apply(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Can't find a known_hosts file"); + SAFE_FREE(host); + return SSH_ERROR; + } + } + + if (session->current_crypto==NULL) { + ssh_set_error(session, SSH_FATAL, "No current crypto context"); + SAFE_FREE(host); + return SSH_ERROR; + } + + pubkey_s = session->current_crypto->server_pubkey; + if (pubkey_s == NULL){ + ssh_set_error(session, SSH_FATAL, "No public key present"); + SAFE_FREE(host); + return SSH_ERROR; + } + + /* Check if ~/.ssh exists and create it if not */ + dir = ssh_dirname(session->opts.knownhosts); + if (dir == NULL) { + ssh_set_error(session, SSH_FATAL, "%s", strerror(errno)); + SAFE_FREE(host); + return SSH_ERROR; + } + + if (!ssh_file_readaccess_ok(dir)) { + if (ssh_mkdir(dir, 0700) < 0) { + ssh_set_error(session, SSH_FATAL, + "Cannot create %s directory.", dir); + SAFE_FREE(dir); + SAFE_FREE(host); + return SSH_ERROR; + } + } + SAFE_FREE(dir); + + file = fopen(session->opts.knownhosts, "a"); + if (file == NULL) { + ssh_set_error(session, SSH_FATAL, + "Couldn't open known_hosts file %s for appending: %s", + session->opts.knownhosts, strerror(errno)); + SAFE_FREE(host); + return SSH_ERROR; + } + + rc = ssh_pki_import_pubkey_blob(pubkey_s, &key); + if (rc < 0) { + fclose(file); + SAFE_FREE(host); + return -1; + } + + if (strcmp(session->current_crypto->server_pubkey_type, "ssh-rsa1") == 0) { + /* openssh uses a different format for ssh-rsa1 keys. + Be compatible --kv */ + rc = ssh_pki_export_pubkey_rsa1(key, host, buffer, sizeof(buffer)); + ssh_key_free(key); + SAFE_FREE(host); + if (rc < 0) { + fclose(file); + return -1; + } + } else { + rc = ssh_pki_export_pubkey_base64(key, &b64_key); + if (rc < 0) { + ssh_key_free(key); + fclose(file); + SAFE_FREE(host); + return -1; + } + + snprintf(buffer, sizeof(buffer), + "%s %s %s\n", + host, + key->type_c, + b64_key); + + ssh_key_free(key); + SAFE_FREE(host); + SAFE_FREE(b64_key); + } + + if (fwrite(buffer, strlen(buffer), 1, file) != 1 || ferror(file)) { + fclose(file); + return -1; + } + + fclose(file); + return 0; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/legacy.c b/libssh/src/legacy.c new file mode 100644 index 00000000..6ad4fdc2 --- /dev/null +++ b/libssh/src/legacy.c @@ -0,0 +1,731 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/** functions in that file are wrappers to the newly named functions. All + * of them are depreciated, but these wrapper will avoid breaking backward + * compatibility + */ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include +#include "libssh/pki_priv.h" +#include +#include +#include "libssh/options.h" + +/* AUTH FUNCTIONS */ +int ssh_auth_list(ssh_session session) { + return ssh_userauth_list(session, NULL); +} + +int ssh_userauth_offer_pubkey(ssh_session session, const char *username, + int type, ssh_string publickey) +{ + ssh_key key; + int rc; + + (void) type; /* unused */ + + rc = ssh_pki_import_pubkey_blob(publickey, &key); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "Failed to convert public key"); + return SSH_AUTH_ERROR; + } + + rc = ssh_userauth_try_publickey(session, username, key); + ssh_key_free(key); + + return rc; +} + +int ssh_userauth_pubkey(ssh_session session, + const char *username, + ssh_string publickey, + ssh_private_key privatekey) +{ + ssh_key key; + int rc; + + (void) publickey; /* unused */ + + key = ssh_key_new(); + if (key == NULL) { + return SSH_AUTH_ERROR; + } + + key->type = privatekey->type; + key->type_c = ssh_key_type_to_char(key->type); + key->flags = SSH_KEY_FLAG_PRIVATE|SSH_KEY_FLAG_PUBLIC; + key->dsa = privatekey->dsa_priv; + key->rsa = privatekey->rsa_priv; + + rc = ssh_userauth_publickey(session, username, key); + key->dsa = NULL; + key->rsa = NULL; + ssh_key_free(key); + + return rc; +} + +int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) { + return ssh_userauth_publickey_auto(session, NULL, passphrase); +} + +int ssh_userauth_privatekey_file(ssh_session session, + const char *username, + const char *filename, + const char *passphrase) { + char *pubkeyfile = NULL; + ssh_string pubkey = NULL; + ssh_private_key privkey = NULL; + int type = 0; + int rc = SSH_AUTH_ERROR; + size_t klen = strlen(filename) + 4 + 1; + + enter_function(); + + pubkeyfile = malloc(klen); + if (pubkeyfile == NULL) { + ssh_set_error_oom(session); + leave_function(); + return SSH_AUTH_ERROR; + } + snprintf(pubkeyfile, klen, "%s.pub", filename); + + pubkey = publickey_from_file(session, pubkeyfile, &type); + if (pubkey == NULL) { + ssh_log(session, SSH_LOG_RARE, "Public key file %s not found. Trying to generate it.", pubkeyfile); + /* auto-detect the key type with type=0 */ + privkey = privatekey_from_file(session, filename, 0, passphrase); + } else { + ssh_log(session, SSH_LOG_RARE, "Public key file %s loaded.", pubkeyfile); + privkey = privatekey_from_file(session, filename, type, passphrase); + } + if (privkey == NULL) { + goto error; + } + /* ssh_userauth_pubkey is responsible for taking care of null-pubkey */ + rc = ssh_userauth_pubkey(session, username, pubkey, privkey); + privatekey_free(privkey); + +error: + SAFE_FREE(pubkeyfile); + ssh_string_free(pubkey); + + leave_function(); + return rc; +} + +/* BUFFER FUNCTIONS */ + +void buffer_free(ssh_buffer buffer){ + ssh_buffer_free(buffer); +} +void *buffer_get(ssh_buffer buffer){ + return ssh_buffer_get_begin(buffer); +} +uint32_t buffer_get_len(ssh_buffer buffer){ + return ssh_buffer_get_len(buffer); +} +ssh_buffer buffer_new(void){ + return ssh_buffer_new(); +} + +ssh_channel channel_accept_x11(ssh_channel channel, int timeout_ms){ + return ssh_channel_accept_x11(channel, timeout_ms); +} + +int channel_change_pty_size(ssh_channel channel,int cols,int rows){ + return ssh_channel_change_pty_size(channel,cols,rows); +} + +ssh_channel channel_forward_accept(ssh_session session, int timeout_ms){ + return ssh_forward_accept(session,timeout_ms); +} + +int channel_close(ssh_channel channel){ + return ssh_channel_close(channel); +} + +int channel_forward_cancel(ssh_session session, const char *address, int port){ + return ssh_forward_cancel(session, address, port); +} + +int channel_forward_listen(ssh_session session, const char *address, + int port, int *bound_port){ + return ssh_forward_listen(session, address, port, bound_port); +} + +void channel_free(ssh_channel channel){ + ssh_channel_free(channel); +} + +int channel_get_exit_status(ssh_channel channel){ + return ssh_channel_get_exit_status(channel); +} + +ssh_session channel_get_session(ssh_channel channel){ + return ssh_channel_get_session(channel); +} + +int channel_is_closed(ssh_channel channel){ + return ssh_channel_is_closed(channel); +} + +int channel_is_eof(ssh_channel channel){ + return ssh_channel_is_eof(channel); +} + +int channel_is_open(ssh_channel channel){ + return ssh_channel_is_open(channel); +} + +ssh_channel channel_new(ssh_session session){ + return ssh_channel_new(session); +} + +int channel_open_forward(ssh_channel channel, const char *remotehost, + int remoteport, const char *sourcehost, int localport){ + return ssh_channel_open_forward(channel, remotehost, remoteport, + sourcehost,localport); +} + +int channel_open_session(ssh_channel channel){ + return ssh_channel_open_session(channel); +} + +int channel_poll(ssh_channel channel, int is_stderr){ + return ssh_channel_poll(channel, is_stderr); +} + +int channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr){ + return ssh_channel_read(channel, dest, count, is_stderr); +} + +/* + * This function will completely be depreciated. The old implementation was not + * renamed. + * int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, + * int is_stderr); + */ + +int channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, + int is_stderr){ + return ssh_channel_read_nonblocking(channel, dest, count, is_stderr); +} + +int channel_request_env(ssh_channel channel, const char *name, const char *value){ + return ssh_channel_request_env(channel, name, value); +} + +int channel_request_exec(ssh_channel channel, const char *cmd){ + return ssh_channel_request_exec(channel, cmd); +} + +int channel_request_pty(ssh_channel channel){ + return ssh_channel_request_pty(channel); +} + +int channel_request_pty_size(ssh_channel channel, const char *term, + int cols, int rows){ + return ssh_channel_request_pty_size(channel, term, cols, rows); +} + +int channel_request_shell(ssh_channel channel){ + return ssh_channel_request_shell(channel); +} + +int channel_request_send_signal(ssh_channel channel, const char *signum){ + return ssh_channel_request_send_signal(channel, signum); +} + +int channel_request_sftp(ssh_channel channel){ + return ssh_channel_request_sftp(channel); +} + +int channel_request_subsystem(ssh_channel channel, const char *subsystem){ + return ssh_channel_request_subsystem(channel, subsystem); +} + +int channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, + const char *cookie, int screen_number){ + return ssh_channel_request_x11(channel, single_connection, protocol, cookie, + screen_number); +} + +int channel_send_eof(ssh_channel channel){ + return ssh_channel_send_eof(channel); +} + +int channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct + timeval * timeout){ + return ssh_channel_select(readchans, writechans, exceptchans, timeout); +} + +void channel_set_blocking(ssh_channel channel, int blocking){ + ssh_channel_set_blocking(channel, blocking); +} + +int channel_write(ssh_channel channel, const void *data, uint32_t len){ + return ssh_channel_write(channel, data, len); +} + +/* + * These functions have to be wrapped around the pki.c functions. + +void privatekey_free(ssh_private_key prv); +ssh_private_key privatekey_from_file(ssh_session session, const char *filename, + int type, const char *passphrase); +int ssh_publickey_to_file(ssh_session session, const char *file, + ssh_string pubkey, int type); +ssh_string publickey_to_string(ssh_public_key key); + * + */ + +void string_burn(ssh_string str){ + ssh_string_burn(str); +} + +ssh_string string_copy(ssh_string str){ + return ssh_string_copy(str); +} + +void *string_data(ssh_string str){ + return ssh_string_data(str); +} + +int string_fill(ssh_string str, const void *data, size_t len){ + return ssh_string_fill(str,data,len); +} + +void string_free(ssh_string str){ + ssh_string_free(str); +} + +ssh_string string_from_char(const char *what){ + return ssh_string_from_char(what); +} + +size_t string_len(ssh_string str){ + return ssh_string_len(str); +} + +ssh_string string_new(size_t size){ + return ssh_string_new(size); +} + +char *string_to_char(ssh_string str){ + return ssh_string_to_char(str); +} + +/* OLD PKI FUNCTIONS */ + +void publickey_free(ssh_public_key key) { + if (key == NULL) { + return; + } + + switch(key->type) { + case SSH_KEYTYPE_DSS: +#ifdef HAVE_LIBGCRYPT + gcry_sexp_release(key->dsa_pub); +#elif HAVE_LIBCRYPTO + DSA_free(key->dsa_pub); +#endif + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: +#ifdef HAVE_LIBGCRYPT + gcry_sexp_release(key->rsa_pub); +#elif defined HAVE_LIBCRYPTO + RSA_free(key->rsa_pub); +#endif + break; + default: + break; + } + SAFE_FREE(key); +} + +ssh_public_key publickey_from_privatekey(ssh_private_key prv) { + struct ssh_public_key_struct *p; + ssh_key privkey; + ssh_key pubkey; + int rc; + + privkey = ssh_key_new(); + if (privkey == NULL) { + return NULL; + } + + privkey->type = prv->type; + privkey->type_c = ssh_key_type_to_char(privkey->type); + privkey->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; + privkey->dsa = prv->dsa_priv; + privkey->rsa = prv->rsa_priv; + + rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); + privkey->dsa = NULL; + privkey->rsa = NULL; + ssh_key_free(privkey); + if (rc < 0) { + return NULL; + } + + p = ssh_pki_convert_key_to_publickey(pubkey); + ssh_key_free(pubkey); + + return p; +} + +ssh_private_key privatekey_from_file(ssh_session session, + const char *filename, + int type, + const char *passphrase) { + ssh_auth_callback auth_fn = NULL; + void *auth_data = NULL; + ssh_private_key privkey; + ssh_key key; + int rc; + + (void) type; /* unused */ + + if (session->common.callbacks) { + auth_fn = session->common.callbacks->auth_function; + auth_data = session->common.callbacks->userdata; + } + + + rc = ssh_pki_import_privkey_file(filename, + passphrase, + auth_fn, + auth_data, + &key); + if (rc == SSH_ERROR) { + return NULL; + } + + privkey = malloc(sizeof(struct ssh_private_key_struct)); + if (privkey == NULL) { + ssh_key_free(key); + return NULL; + } + + privkey->type = key->type; + privkey->dsa_priv = key->dsa; + privkey->rsa_priv = key->rsa; + + key->dsa = NULL; + key->rsa = NULL; + + ssh_key_free(key); + + return privkey; +} + +enum ssh_keytypes_e ssh_privatekey_type(ssh_private_key privatekey){ + if (privatekey==NULL) + return SSH_KEYTYPE_UNKNOWN; + return privatekey->type; +} + +void privatekey_free(ssh_private_key prv) { + if (prv == NULL) { + return; + } + +#ifdef HAVE_LIBGCRYPT + gcry_sexp_release(prv->dsa_priv); + gcry_sexp_release(prv->rsa_priv); +#elif defined HAVE_LIBCRYPTO + DSA_free(prv->dsa_priv); + RSA_free(prv->rsa_priv); +#endif + memset(prv, 0, sizeof(struct ssh_private_key_struct)); + SAFE_FREE(prv); +} + +ssh_string publickey_from_file(ssh_session session, const char *filename, + int *type) { + ssh_key key; + ssh_string key_str = NULL; + int rc; + + (void) session; /* unused */ + + rc = ssh_pki_import_pubkey_file(filename, &key); + if (rc < 0) { + return NULL; + } + + rc = ssh_pki_export_pubkey_blob(key, &key_str); + if (rc < 0) { + ssh_key_free(key); + return NULL; + } + + if (type) { + *type = key->type; + } + ssh_key_free(key); + + return key_str; +} + +const char *ssh_type_to_char(int type) { + return ssh_key_type_to_char(type); +} + +int ssh_type_from_name(const char *name) { + return ssh_key_type_from_name(name); +} + +ssh_public_key publickey_from_string(ssh_session session, ssh_string pubkey_s) { + struct ssh_public_key_struct *pubkey; + ssh_key key; + int rc; + + (void) session; /* unused */ + + rc = ssh_pki_import_pubkey_blob(pubkey_s, &key); + if (rc < 0) { + return NULL; + } + + pubkey = malloc(sizeof(struct ssh_public_key_struct)); + if (pubkey == NULL) { + ssh_key_free(key); + return NULL; + } + + pubkey->type = key->type; + pubkey->type_c = key->type_c; + + pubkey->dsa_pub = key->dsa; + key->dsa = NULL; + pubkey->rsa_pub = key->rsa; + key->rsa = NULL; + + ssh_key_free(key); + + return pubkey; +} + +ssh_string publickey_to_string(ssh_public_key pubkey) { + ssh_key key; + ssh_string key_blob; + int rc; + + key = ssh_key_new(); + if (key == NULL) { + return NULL; + } + + key->type = pubkey->type; + key->type_c = pubkey->type_c; + + key->dsa = pubkey->dsa_pub; + key->rsa = pubkey->rsa_pub; + + rc = ssh_pki_export_pubkey_blob(key, &key_blob); + if (rc < 0) { + key_blob = NULL; + } + + key->dsa = NULL; + key->rsa = NULL; + ssh_key_free(key); + + return key_blob; +} + +int ssh_publickey_to_file(ssh_session session, + const char *file, + ssh_string pubkey, + int type) +{ + FILE *fp; + char *user; + char buffer[1024]; + char host[256]; + unsigned char *pubkey_64; + size_t len; + int rc; + if(session==NULL) + return SSH_ERROR; + if(file==NULL || pubkey==NULL){ + ssh_set_error(session, SSH_FATAL, "Invalid parameters"); + return SSH_ERROR; + } + pubkey_64 = bin_to_base64(string_data(pubkey), ssh_string_len(pubkey)); + if (pubkey_64 == NULL) { + return SSH_ERROR; + } + + user = ssh_get_local_username(); + if (user == NULL) { + SAFE_FREE(pubkey_64); + return SSH_ERROR; + } + + rc = gethostname(host, sizeof(host)); + if (rc < 0) { + SAFE_FREE(user); + SAFE_FREE(pubkey_64); + return SSH_ERROR; + } + + snprintf(buffer, sizeof(buffer), "%s %s %s@%s\n", + ssh_type_to_char(type), + pubkey_64, + user, + host); + + SAFE_FREE(pubkey_64); + SAFE_FREE(user); + + ssh_log(session, SSH_LOG_RARE, "Trying to write public key file: %s", file); + ssh_log(session, SSH_LOG_PACKET, "public key file content: %s", buffer); + + fp = fopen(file, "w+"); + if (fp == NULL) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Error opening %s: %s", file, strerror(errno)); + return SSH_ERROR; + } + + len = strlen(buffer); + if (fwrite(buffer, len, 1, fp) != 1 || ferror(fp)) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Unable to write to %s", file); + fclose(fp); + unlink(file); + return SSH_ERROR; + } + + fclose(fp); + return SSH_OK; +} + +int ssh_try_publickey_from_file(ssh_session session, + const char *keyfile, + ssh_string *publickey, + int *type) { + char *pubkey_file; + size_t len; + ssh_string pubkey_string; + int pubkey_type; + + if (session == NULL || keyfile == NULL || publickey == NULL || type == NULL) { + return -1; + } + + if (session->opts.sshdir == NULL) { + if (ssh_options_apply(session) < 0) { + return -1; + } + } + + ssh_log(session, SSH_LOG_PACKET, "Trying to open privatekey %s", keyfile); + if (!ssh_file_readaccess_ok(keyfile)) { + ssh_log(session, SSH_LOG_PACKET, "Failed to open privatekey %s", keyfile); + return -1; + } + + len = strlen(keyfile) + 5; + pubkey_file = malloc(len); + if (pubkey_file == NULL) { + return -1; + } + snprintf(pubkey_file, len, "%s.pub", keyfile); + + ssh_log(session, SSH_LOG_PACKET, "Trying to open publickey %s", + pubkey_file); + if (!ssh_file_readaccess_ok(pubkey_file)) { + ssh_log(session, SSH_LOG_PACKET, "Failed to open publickey %s", + pubkey_file); + SAFE_FREE(pubkey_file); + return 1; + } + + ssh_log(session, SSH_LOG_PACKET, "Success opening public and private key"); + + /* + * We are sure both the private and public key file is readable. We return + * the public as a string, and the private filename as an argument + */ + pubkey_string = publickey_from_file(session, pubkey_file, &pubkey_type); + if (pubkey_string == NULL) { + ssh_log(session, SSH_LOG_PACKET, + "Wasn't able to open public key file %s: %s", + pubkey_file, + ssh_get_error(session)); + SAFE_FREE(pubkey_file); + return -1; + } + + SAFE_FREE(pubkey_file); + + *publickey = pubkey_string; + *type = pubkey_type; + + return 0; +} + +ssh_string ssh_get_pubkey(ssh_session session){ + if(session==NULL || session->current_crypto ==NULL || + session->current_crypto->server_pubkey==NULL) + return NULL; + else + return ssh_string_copy(session->current_crypto->server_pubkey); +} + +/**************************************************************************** + * SERVER SUPPORT + ****************************************************************************/ + +#ifdef WITH_SERVER +int ssh_accept(ssh_session session) { + return ssh_handle_key_exchange(session); +} + +int channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) { + return ssh_channel_write(channel, data, len); +} + +/** @deprecated + * @brief Interface previously exported by error. + */ +ssh_message ssh_message_retrieve(ssh_session session, uint32_t packettype){ + (void) packettype; + ssh_set_error(session, SSH_FATAL, "ssh_message_retrieve: obsolete libssh call"); + return NULL; +} + +#endif /* WITH_SERVER */ diff --git a/libssh/src/libcrypto.c b/libssh/src/libcrypto.c new file mode 100644 index 00000000..44b0fb36 --- /dev/null +++ b/libssh/src/libcrypto.c @@ -0,0 +1,600 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + + +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/crypto.h" +#include "libssh/wrapper.h" +#include "libssh/libcrypto.h" + +#ifdef HAVE_LIBCRYPTO + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_OPENSSL_AES_H +#define HAS_AES +#include +#endif +#ifdef HAVE_OPENSSL_BLOWFISH_H +#define HAS_BLOWFISH +#include +#endif +#ifdef HAVE_OPENSSL_DES_H +#define HAS_DES +#include +#endif + +#if (OPENSSL_VERSION_NUMBER<0x00907000L) +#define OLD_CRYPTO +#endif + +#include "libssh/crypto.h" + +struct ssh_mac_ctx_struct { + enum ssh_mac_e mac_type; + union { + SHACTX sha1_ctx; + SHA256CTX sha256_ctx; + } ctx; +}; + +static int alloc_key(struct ssh_cipher_struct *cipher) { + cipher->key = malloc(cipher->keylen); + if (cipher->key == NULL) { + return -1; + } + + return 0; +} + +SHACTX sha1_init(void) { + SHACTX c = malloc(sizeof(*c)); + if (c == NULL) { + return NULL; + } + SHA1_Init(c); + + return c; +} + +void sha1_update(SHACTX c, const void *data, unsigned long len) { + SHA1_Update(c,data,len); +} + +void sha1_final(unsigned char *md, SHACTX c) { + SHA1_Final(md, c); + SAFE_FREE(c); +} + +void sha1(unsigned char *digest, int len, unsigned char *hash) { + SHA1(digest, len, hash); +} + +#ifdef HAVE_OPENSSL_ECC +static const EVP_MD *nid_to_evpmd(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return EVP_sha256(); + case NID_secp384r1: + return EVP_sha384(); + case NID_secp521r1: + return EVP_sha512(); + default: + return NULL; + } + + return NULL; +} + +void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen) +{ + const EVP_MD *evp_md = nid_to_evpmd(nid); + EVP_MD_CTX md; + + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, digest, len); + EVP_DigestFinal(&md, hash, hlen); +} +#endif + +SHA256CTX sha256_init(void){ + SHA256CTX c = malloc(sizeof(*c)); + if (c == NULL) { + return NULL; + } + SHA256_Init(c); + + return c; +} + +void sha256_update(SHA256CTX c, const void *data, unsigned long len){ + SHA256_Update(c,data,len); +} + +void sha256_final(unsigned char *md, SHA256CTX c) { + SHA256_Final(md, c); + SAFE_FREE(c); +} + +void sha256(unsigned char *digest, int len, unsigned char *hash) { + SHA256(digest, len, hash); +} + +MD5CTX md5_init(void) { + MD5CTX c = malloc(sizeof(*c)); + if (c == NULL) { + return NULL; + } + + MD5_Init(c); + + return c; +} + +void md5_update(MD5CTX c, const void *data, unsigned long len) { + MD5_Update(c, data, len); +} + +void md5_final(unsigned char *md, MD5CTX c) { + MD5_Final(md,c); + SAFE_FREE(c); +} + +ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ + ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct)); + ctx->mac_type=type; + switch(type){ + case SSH_MAC_SHA1: + ctx->ctx.sha1_ctx = sha1_init(); + return ctx; + case SSH_MAC_SHA256: + ctx->ctx.sha256_ctx = sha256_init(); + return ctx; + case SSH_MAC_SHA384: + case SSH_MAC_SHA512: + default: + SAFE_FREE(ctx); + return NULL; + } +} + +void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { + switch(ctx->mac_type){ + case SSH_MAC_SHA1: + sha1_update(ctx->ctx.sha1_ctx, data, len); + break; + case SSH_MAC_SHA256: + sha256_update(ctx->ctx.sha256_ctx, data, len); + break; + case SSH_MAC_SHA384: + case SSH_MAC_SHA512: + default: + break; + } +} + +void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { + switch(ctx->mac_type){ + case SSH_MAC_SHA1: + sha1_final(md,ctx->ctx.sha1_ctx); + break; + case SSH_MAC_SHA256: + sha256_final(md,ctx->ctx.sha256_ctx); + break; + case SSH_MAC_SHA384: + case SSH_MAC_SHA512: + default: + break; + } + SAFE_FREE(ctx); +} + +HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { + HMACCTX ctx = NULL; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + +#ifndef OLD_CRYPTO + HMAC_CTX_init(ctx); // openssl 0.9.7 requires it. +#endif + + switch(type) { + case SSH_HMAC_SHA1: + HMAC_Init(ctx, key, len, EVP_sha1()); + break; + case SSH_HMAC_MD5: + HMAC_Init(ctx, key, len, EVP_md5()); + break; + default: + SAFE_FREE(ctx); + ctx = NULL; + } + + return ctx; +} + +void hmac_update(HMACCTX ctx, const void *data, unsigned long len) { + HMAC_Update(ctx, data, len); +} + +void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len) { + HMAC_Final(ctx,hashmacbuf,len); + +#ifndef OLD_CRYPTO + HMAC_CTX_cleanup(ctx); +#else + HMAC_cleanup(ctx); +#endif + + SAFE_FREE(ctx); +} + +#ifdef HAS_BLOWFISH +/* the wrapper functions for blowfish */ +static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){ + if (cipher->key == NULL) { + if (alloc_key(cipher) < 0) { + return -1; + } + BF_set_key(cipher->key, 16, key); + } + cipher->IV = IV; + return 0; +} + +static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_ENCRYPT); +} + +static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_DECRYPT); +} +#endif /* HAS_BLOWFISH */ + +#ifdef HAS_AES +static int aes_set_encrypt_key(struct ssh_cipher_struct *cipher, void *key, + void *IV) { + if (cipher->key == NULL) { + if (alloc_key(cipher) < 0) { + return -1; + } + if (AES_set_encrypt_key(key,cipher->keysize,cipher->key) < 0) { + SAFE_FREE(cipher->key); + return -1; + } + } + cipher->IV=IV; + return 0; +} +static int aes_set_decrypt_key(struct ssh_cipher_struct *cipher, void *key, + void *IV) { + if (cipher->key == NULL) { + if (alloc_key(cipher) < 0) { + return -1; + } + if (AES_set_decrypt_key(key,cipher->keysize,cipher->key) < 0) { + SAFE_FREE(cipher->key); + return -1; + } + } + cipher->IV=IV; + return 0; +} + +static void aes_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len) { + AES_cbc_encrypt(in, out, len, cipher->key, cipher->IV, AES_ENCRYPT); +} + +static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len) { + AES_cbc_encrypt(in, out, len, cipher->key, cipher->IV, AES_DECRYPT); +} + +#ifndef BROKEN_AES_CTR +/* OpenSSL until 0.9.7c has a broken AES_ctr128_encrypt implementation which + * increments the counter from 2^64 instead of 1. It's better not to use it + */ + +/** @internal + * @brief encrypts/decrypts data with stream cipher AES_ctr128. 128 bits is actually + * the size of the CTR counter and incidentally the blocksize, but not the keysize. + * @param len[in] must be a multiple of AES128 block size. + */ +static void aes_ctr128_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len) { + unsigned char tmp_buffer[128/8]; + unsigned int num=0; + /* Some things are special with ctr128 : + * In this case, tmp_buffer is not being used, because it is used to store temporary data + * when an encryption is made on lengths that are not multiple of blocksize. + * Same for num, which is being used to store the current offset in blocksize in CTR + * function. + */ + AES_ctr128_encrypt(in, out, len, cipher->key, cipher->IV, tmp_buffer, &num); +} +#endif /* BROKEN_AES_CTR */ +#endif /* HAS_AES */ + +#ifdef HAS_DES +static int des3_set_key(struct ssh_cipher_struct *cipher, void *key,void *IV) { + if (cipher->key == NULL) { + if (alloc_key(cipher) < 0) { + return -1; + } + + DES_set_odd_parity(key); + DES_set_odd_parity((void*)((uint8_t*)key + 8)); + DES_set_odd_parity((void*)((uint8_t*)key + 16)); + DES_set_key_unchecked(key, cipher->key); + DES_set_key_unchecked((void*)((uint8_t*)key + 8), (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule))); + DES_set_key_unchecked((void*)((uint8_t*)key + 16), (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule))); + } + cipher->IV=IV; + return 0; +} + +static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + DES_ede3_cbc_encrypt(in, out, len, cipher->key, + (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), + (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), + cipher->IV, 1); +} + +static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + DES_ede3_cbc_encrypt(in, out, len, cipher->key, + (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), + (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), + cipher->IV, 0); +} + +static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Encrypt IV before", cipher->IV, 24); +#endif + DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1); + DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), + (void*)((uint8_t*)cipher->IV + 8), 0); + DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), + (void*)((uint8_t*)cipher->IV + 16), 1); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Encrypt IV after", cipher->IV, 24); +#endif +} + +static void des3_1_decrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Decrypt IV before", cipher->IV, 24); +#endif + + DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), + cipher->IV, 0); + DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), + (void*)((uint8_t*)cipher->IV + 8), 1); + DES_ncbc_encrypt(in, out, len, cipher->key, (void*)((uint8_t*)cipher->IV + 16), 0); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Decrypt IV after", cipher->IV, 24); +#endif +} + +static int des1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){ + if(!cipher->key){ + if (alloc_key(cipher) < 0) { + return -1; + } + DES_set_odd_parity(key); + DES_set_key_unchecked(key,cipher->key); + } + cipher->IV=IV; + return 0; +} + +static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len){ + + DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1); +} + +static void des1_1_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len){ + + DES_ncbc_encrypt(in,out,len, cipher->key, cipher->IV, 0); +} + +#endif /* HAS_DES */ + +/* + * The table of supported ciphers + * + * WARNING: If you modify ssh_cipher_struct, you must make sure the order is + * correct! + */ +static struct ssh_cipher_struct ssh_ciphertab[] = { +#ifdef HAS_BLOWFISH + { + "blowfish-cbc", + 8, + sizeof (BF_KEY), + NULL, + NULL, + 128, + blowfish_set_key, + blowfish_set_key, + blowfish_encrypt, + blowfish_decrypt + }, +#endif /* HAS_BLOWFISH */ +#ifdef HAS_AES +#ifndef BROKEN_AES_CTR + { + "aes128-ctr", + 16, + sizeof(AES_KEY), + NULL, + NULL, + 128, + aes_set_encrypt_key, + aes_set_encrypt_key, + aes_ctr128_encrypt, + aes_ctr128_encrypt + }, + { + "aes192-ctr", + 16, + sizeof(AES_KEY), + NULL, + NULL, + 192, + aes_set_encrypt_key, + aes_set_encrypt_key, + aes_ctr128_encrypt, + aes_ctr128_encrypt + }, + { + "aes256-ctr", + 16, + sizeof(AES_KEY), + NULL, + NULL, + 256, + aes_set_encrypt_key, + aes_set_encrypt_key, + aes_ctr128_encrypt, + aes_ctr128_encrypt + }, +#endif /* BROKEN_AES_CTR */ + { + "aes128-cbc", + 16, + sizeof(AES_KEY), + NULL, + NULL, + 128, + aes_set_encrypt_key, + aes_set_decrypt_key, + aes_encrypt, + aes_decrypt + }, + { + "aes192-cbc", + 16, + sizeof(AES_KEY), + NULL, + NULL, + 192, + aes_set_encrypt_key, + aes_set_decrypt_key, + aes_encrypt, + aes_decrypt + }, + { + "aes256-cbc", + 16, + sizeof(AES_KEY), + NULL, + NULL, + 256, + aes_set_encrypt_key, + aes_set_decrypt_key, + aes_encrypt, + aes_decrypt + }, +#endif /* HAS_AES */ +#ifdef HAS_DES + { + "3des-cbc", + 8, + sizeof(DES_key_schedule) * 3, + NULL, + NULL, + 192, + des3_set_key, + des3_set_key, + des3_encrypt, + des3_decrypt + }, + { + "3des-cbc-ssh1", + 8, + sizeof(DES_key_schedule) * 3, + NULL, + NULL, + 192, + des3_set_key, + des3_set_key, + des3_1_encrypt, + des3_1_decrypt + }, + { + "des-cbc-ssh1", + 8, + sizeof(DES_key_schedule), + NULL, + NULL, + 64, + des1_set_key, + des1_set_key, + des1_1_encrypt, + des1_1_decrypt + }, +#endif /* HAS_DES */ + { + NULL, + 0, + 0, + NULL, + NULL, + 0, + NULL, + NULL, + NULL, + NULL + } +}; + + +struct ssh_cipher_struct *ssh_get_ciphertab(void) +{ + return ssh_ciphertab; +} + +#endif /* LIBCRYPTO */ + diff --git a/libssh/src/libgcrypt.c b/libssh/src/libgcrypt.c new file mode 100644 index 00000000..899bccdf --- /dev/null +++ b/libssh/src/libgcrypt.c @@ -0,0 +1,526 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/crypto.h" +#include "libssh/wrapper.h" + +#ifdef HAVE_LIBGCRYPT +#include + +struct ssh_mac_ctx_struct { + enum ssh_mac_e mac_type; + gcry_md_hd_t ctx; +}; + +static int alloc_key(struct ssh_cipher_struct *cipher) { + cipher->key = malloc(cipher->keylen); + if (cipher->key == NULL) { + return -1; + } + + return 0; +} + +SHACTX sha1_init(void) { + SHACTX ctx = NULL; + gcry_md_open(&ctx, GCRY_MD_SHA1, 0); + + return ctx; +} + +void sha1_update(SHACTX c, const void *data, unsigned long len) { + gcry_md_write(c, data, len); +} + +void sha1_final(unsigned char *md, SHACTX c) { + gcry_md_final(c); + memcpy(md, gcry_md_read(c, 0), SHA_DIGEST_LEN); + gcry_md_close(c); +} + +void sha1(unsigned char *digest, int len, unsigned char *hash) { + gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len); +} + +void sha256(unsigned char *digest, int len, unsigned char *hash){ + gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len); +} + +MD5CTX md5_init(void) { + MD5CTX c = NULL; + gcry_md_open(&c, GCRY_MD_MD5, 0); + + return c; +} + +void md5_update(MD5CTX c, const void *data, unsigned long len) { + gcry_md_write(c,data,len); +} + +void md5_final(unsigned char *md, MD5CTX c) { + gcry_md_final(c); + memcpy(md, gcry_md_read(c, 0), MD5_DIGEST_LEN); + gcry_md_close(c); +} + +ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ + ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct)); + ctx->mac_type=type; + switch(type){ + case SSH_MAC_SHA1: + gcry_md_open(&ctx->ctx, GCRY_MD_SHA1, 0); + break; + case SSH_MAC_SHA256: + gcry_md_open(&ctx->ctx, GCRY_MD_SHA256, 0); + break; + case SSH_MAC_SHA384: + gcry_md_open(&ctx->ctx, GCRY_MD_SHA384, 0); + break; + case SSH_MAC_SHA512: + gcry_md_open(&ctx->ctx, GCRY_MD_SHA512, 0); + break; + default: + SAFE_FREE(ctx); + return NULL; + } + return ctx; +} + +void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { + gcry_md_write(ctx->ctx,data,len); +} + +void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { + size_t len; + switch(ctx->mac_type){ + case SSH_MAC_SHA1: + len=SHA_DIGEST_LEN; + break; + case SSH_MAC_SHA256: + len=SHA256_DIGEST_LENGTH; + break; + case SSH_MAC_SHA384: + len=SHA384_DIGEST_LENGTH; + break; + case SSH_MAC_SHA512: + len=SHA512_DIGEST_LENGTH; + break; + } + gcry_md_final(ctx->ctx); + memcpy(md, gcry_md_read(ctx->ctx, 0), len); + gcry_md_close(ctx->ctx); + SAFE_FREE(ctx); +} + +HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { + HMACCTX c = NULL; + + switch(type) { + case SSH_HMAC_SHA1: + gcry_md_open(&c, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); + break; + case SSH_HMAC_MD5: + gcry_md_open(&c, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC); + break; + default: + c = NULL; + } + + gcry_md_setkey(c, key, len); + + return c; +} + +void hmac_update(HMACCTX c, const void *data, unsigned long len) { + gcry_md_write(c, data, len); +} + +void hmac_final(HMACCTX c, unsigned char *hashmacbuf, unsigned int *len) { + *len = gcry_md_get_algo_dlen(gcry_md_get_algo(c)); + memcpy(hashmacbuf, gcry_md_read(c, 0), *len); + gcry_md_close(c); +} + +/* the wrapper functions for blowfish */ +static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){ + if (cipher->key == NULL) { + if (alloc_key(cipher) < 0) { + return -1; + } + + if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_BLOWFISH, + GCRY_CIPHER_MODE_CBC, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setkey(cipher->key[0], key, 16)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + } + + return 0; +} + +static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + gcry_cipher_encrypt(cipher->key[0], out, len, in, len); +} + +static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + gcry_cipher_decrypt(cipher->key[0], out, len, in, len); +} + +static int aes_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) { + int mode=GCRY_CIPHER_MODE_CBC; + if (cipher->key == NULL) { + if (alloc_key(cipher) < 0) { + return -1; + } + if(strstr(cipher->name,"-ctr")) + mode=GCRY_CIPHER_MODE_CTR; + switch (cipher->keysize) { + case 128: + if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES128, + mode, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + break; + case 192: + if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES192, + mode, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + break; + case 256: + if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES256, + mode, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + break; + } + if (gcry_cipher_setkey(cipher->key[0], key, cipher->keysize / 8)) { + SAFE_FREE(cipher->key); + return -1; + } + if(mode == GCRY_CIPHER_MODE_CBC){ + if (gcry_cipher_setiv(cipher->key[0], IV, 16)) { + + SAFE_FREE(cipher->key); + return -1; + } + } else { + if(gcry_cipher_setctr(cipher->key[0],IV,16)){ + SAFE_FREE(cipher->key); + return -1; + } + } + } + + return 0; +} + +static void aes_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len) { + gcry_cipher_encrypt(cipher->key[0], out, len, in, len); +} + +static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out, + unsigned long len) { + gcry_cipher_decrypt(cipher->key[0], out, len, in, len); +} + +static int des1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){ + if(!cipher->key){ + if (alloc_key(cipher) < 0) { + return -1; + } + if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_CBC, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setkey(cipher->key[0], key, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + } + return 0; +} + +static int des3_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) { + if (cipher->key == NULL) { + if (alloc_key(cipher) < 0) { + return -1; + } + if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_3DES, + GCRY_CIPHER_MODE_CBC, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setkey(cipher->key[0], key, 24)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + } + + return 0; +} + + +static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + gcry_cipher_encrypt(cipher->key[0], out, len, in, len); +} + +static void des1_1_decrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + gcry_cipher_decrypt(cipher->key[0], out, len, in, len); +} + +static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + gcry_cipher_encrypt(cipher->key[0], out, len, in, len); +} + +static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + gcry_cipher_decrypt(cipher->key[0], out, len, in, len); +} + +static int des3_1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) { + if (cipher->key == NULL) { + if (alloc_key(cipher) < 0) { + return -1; + } + if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_CBC, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setkey(cipher->key[0], key, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + + if (gcry_cipher_open(&cipher->key[1], GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_CBC, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setkey(cipher->key[1], (unsigned char *)key + 8, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setiv(cipher->key[1], (unsigned char *)IV + 8, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + + if (gcry_cipher_open(&cipher->key[2], GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_CBC, 0)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setkey(cipher->key[2], (unsigned char *)key + 16, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + if (gcry_cipher_setiv(cipher->key[2], (unsigned char *)IV + 16, 8)) { + SAFE_FREE(cipher->key); + return -1; + } + } + + return 0; +} + +static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + gcry_cipher_encrypt(cipher->key[0], out, len, in, len); + gcry_cipher_decrypt(cipher->key[1], in, len, out, len); + gcry_cipher_encrypt(cipher->key[2], out, len, in, len); +} + +static void des3_1_decrypt(struct ssh_cipher_struct *cipher, void *in, + void *out, unsigned long len) { + gcry_cipher_decrypt(cipher->key[2], out, len, in, len); + gcry_cipher_encrypt(cipher->key[1], in, len, out, len); + gcry_cipher_decrypt(cipher->key[0], out, len, in, len); +} + +/* the table of supported ciphers */ +static struct ssh_cipher_struct ssh_ciphertab[] = { + { + .name = "blowfish-cbc", + .blocksize = 8, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 128, + .set_encrypt_key = blowfish_set_key, + .set_decrypt_key = blowfish_set_key, + .cbc_encrypt = blowfish_encrypt, + .cbc_decrypt = blowfish_decrypt + }, + { + .name = "aes128-ctr", + .blocksize = 16, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 128, + .set_encrypt_key = aes_set_key, + .set_decrypt_key = aes_set_key, + .cbc_encrypt = aes_encrypt, + .cbc_decrypt = aes_encrypt + }, + { + .name = "aes192-ctr", + .blocksize = 16, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 192, + .set_encrypt_key = aes_set_key, + .set_decrypt_key = aes_set_key, + .cbc_encrypt = aes_encrypt, + .cbc_decrypt = aes_encrypt + }, + { + .name = "aes256-ctr", + .blocksize = 16, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 256, + .set_encrypt_key = aes_set_key, + .set_decrypt_key = aes_set_key, + .cbc_encrypt = aes_encrypt, + .cbc_decrypt = aes_encrypt + }, + { + .name = "aes128-cbc", + .blocksize = 16, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 128, + .set_encrypt_key = aes_set_key, + .set_decrypt_key = aes_set_key, + .cbc_encrypt = aes_encrypt, + .cbc_decrypt = aes_decrypt + }, + { + .name = "aes192-cbc", + .blocksize = 16, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 192, + .set_encrypt_key = aes_set_key, + .set_decrypt_key = aes_set_key, + .cbc_encrypt = aes_encrypt, + .cbc_decrypt = aes_decrypt + }, + { + .name = "aes256-cbc", + .blocksize = 16, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 256, + .set_encrypt_key = aes_set_key, + .set_decrypt_key = aes_set_key, + .cbc_encrypt = aes_encrypt, + .cbc_decrypt = aes_decrypt + }, + { + .name = "3des-cbc", + .blocksize = 8, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 192, + .set_encrypt_key = des3_set_key, + .set_decrypt_key = des3_set_key, + .cbc_encrypt = des3_encrypt, + .cbc_decrypt = des3_decrypt + }, + { + .name = "3des-cbc-ssh1", + .blocksize = 8, + .keylen = sizeof(gcry_cipher_hd_t) * 3, + .key = NULL, + .keysize = 192, + .set_encrypt_key = des3_1_set_key, + .set_decrypt_key = des3_1_set_key, + .cbc_encrypt = des3_1_encrypt, + .cbc_decrypt = des3_1_decrypt + }, + { + .name = "des-cbc-ssh1", + .blocksize = 8, + .keylen = sizeof(gcry_cipher_hd_t), + .key = NULL, + .keysize = 64, + .set_encrypt_key = des1_set_key, + .set_decrypt_key = des1_set_key, + .cbc_encrypt = des1_1_encrypt, + .cbc_decrypt = des1_1_decrypt + }, + { + .name = NULL, + .blocksize = 0, + .keylen = 0, + .key = NULL, + .keysize = 0, + .set_encrypt_key = NULL, + .set_decrypt_key = NULL, + .cbc_encrypt = NULL, + .cbc_decrypt = NULL + } +}; + +struct ssh_cipher_struct *ssh_get_ciphertab(void) +{ + return ssh_ciphertab; +} + +#endif diff --git a/libssh/src/log.c b/libssh/src/log.c new file mode 100644 index 00000000..687afabc --- /dev/null +++ b/libssh/src/log.c @@ -0,0 +1,152 @@ +/* + * log.c - logging and debugging functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include +#ifndef _WIN32 +#include +#else +#include +#endif +#include + +#include "libssh/priv.h" +#include "libssh/misc.h" +#include "libssh/session.h" + +/** + * @defgroup libssh_log The SSH logging functions. + * @ingroup libssh + * + * Logging functions for debugging and problem resolving. + * + * @{ + */ + +static int current_timestring(int hires, char *buf, size_t len) +{ + char tbuf[64]; + struct timeval tv; + struct tm *tm; + time_t t; + + gettimeofday(&tv, NULL); + t = (time_t) tv.tv_sec; + + tm = localtime(&t); + if (tm == NULL) { + return -1; + } + + if (hires) { + strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm); + snprintf(buf, len, "%s.%06ld", tbuf, tv.tv_usec); + } else { + strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm); + snprintf(buf, len, "%s", tbuf); + } + + return 0; +} + +void ssh_log_function(int verbosity, + const char *function, + const char *buffer) +{ + char date[64] = {0}; + int rc; + + rc = current_timestring(1, date, sizeof(date)); + if (rc == 0) { + fprintf(stderr, "[%s, %d] %s", date, verbosity, function); + } else { + fprintf(stderr, "[%d] %s", verbosity, function); + } + fprintf(stderr, " %s\n", buffer); +} + +/** @internal + * @brief do the actual work of logging an event + */ + +static void do_ssh_log(struct ssh_common_struct *common, + int verbosity, + const char *function, + const char *buffer) { + if (common->callbacks && common->callbacks->log_function) { + char buf[1024]; + + snprintf(buf, sizeof(buf), "%s: %s", function, buffer); + + common->callbacks->log_function((ssh_session)common, + verbosity, + buf, + common->callbacks->userdata); + return; + } + + ssh_log_function(verbosity, function, buffer); +} + +/* legacy function */ +void ssh_log(ssh_session session, + int verbosity, + const char *format, ...) +{ + char buffer[1024]; + va_list va; + + if (verbosity <= session->common.log_verbosity) { + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + do_ssh_log(&session->common, verbosity, "", buffer); + } +} + +/** @internal + * @brief log a SSH event with a common pointer + * @param common The SSH/bind session. + * @param verbosity The verbosity of the event. + * @param format The format string of the log entry. + */ +void ssh_log_common(struct ssh_common_struct *common, + int verbosity, + const char *function, + const char *format, ...) +{ + char buffer[1024]; + va_list va; + + if (verbosity <= common->log_verbosity) { + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + do_ssh_log(common, verbosity, function, buffer); + } +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/match.c b/libssh/src/match.c new file mode 100644 index 00000000..53620bdd --- /dev/null +++ b/libssh/src/match.c @@ -0,0 +1,189 @@ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Simple pattern matching, with '*' and '?' as wildcards. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +/* + * Copyright (c) 2000 Markus Friedl. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "libssh/priv.h" + +/* + * Returns true if the given string matches the pattern (which may contain ? + * and * as wildcards), and zero if it does not match. + */ +static int match_pattern(const char *s, const char *pattern) { + if (s == NULL || pattern == NULL) { + return 0; + } + + for (;;) { + /* If at end of pattern, accept if also at end of string. */ + if (*pattern == '\0') { + return (*s == '\0'); + } + + if (*pattern == '*') { + /* Skip the asterisk. */ + pattern++; + + /* If at end of pattern, accept immediately. */ + if (!*pattern) + return 1; + + /* If next character in pattern is known, optimize. */ + if (*pattern != '?' && *pattern != '*') { + /* + * Look instances of the next character in + * pattern, and try to match starting from + * those. + */ + for (; *s; s++) + if (*s == *pattern && match_pattern(s + 1, pattern + 1)) { + return 1; + } + /* Failed. */ + return 0; + } + /* + * Move ahead one character at a time and try to + * match at each position. + */ + for (; *s; s++) { + if (match_pattern(s, pattern)) { + return 1; + } + } + /* Failed. */ + return 0; + } + /* + * There must be at least one more character in the string. + * If we are at the end, fail. + */ + if (!*s) { + return 0; + } + + /* Check if the next character of the string is acceptable. */ + if (*pattern != '?' && *pattern != *s) { + return 0; + } + + /* Move to the next character, both in string and in pattern. */ + s++; + pattern++; + } + + /* NOTREACHED */ +} + +/* + * Tries to match the string against the comma-separated sequence of subpatterns + * (each possibly preceded by ! to indicate negation). + * Returns -1 if negation matches, 1 if there is a positive match, 0 if there is + * no match at all. + */ +static int match_pattern_list(const char *string, const char *pattern, + unsigned int len, int dolower) { + char sub[1024]; + int negated; + int got_positive; + unsigned int i, subi; + + got_positive = 0; + for (i = 0; i < len;) { + /* Check if the subpattern is negated. */ + if (pattern[i] == '!') { + negated = 1; + i++; + } else { + negated = 0; + } + + /* + * Extract the subpattern up to a comma or end. Convert the + * subpattern to lowercase. + */ + for (subi = 0; + i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; + subi++, i++) { + sub[subi] = dolower && isupper(pattern[i]) ? + (char)tolower(pattern[i]) : pattern[i]; + } + + /* If subpattern too long, return failure (no match). */ + if (subi >= sizeof(sub) - 1) { + return 0; + } + + /* If the subpattern was terminated by a comma, skip the comma. */ + if (i < len && pattern[i] == ',') { + i++; + } + + /* Null-terminate the subpattern. */ + sub[subi] = '\0'; + + /* Try to match the subpattern against the string. */ + if (match_pattern(string, sub)) { + if (negated) { + return -1; /* Negative */ + } else { + got_positive = 1; /* Positive */ + } + } + } + + /* + * Return success if got a positive match. If there was a negative + * match, we have already returned -1 and never get here. + */ + return got_positive; +} + +/* + * Tries to match the host name (which must be in all lowercase) against the + * comma-separated sequence of subpatterns (each possibly preceded by ! to + * indicate negation). + * Returns -1 if negation matches, 1 if there is a positive match, 0 if there + * is no match at all. + */ +int match_hostname(const char *host, const char *pattern, unsigned int len) { + return match_pattern_list(host, pattern, len, 1); +} + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/messages.c b/libssh/src/messages.c new file mode 100644 index 00000000..1ef8a745 --- /dev/null +++ b/libssh/src/messages.c @@ -0,0 +1,1319 @@ +/* + * messages.c - message parsing for client and server + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/libssh.h" +#include "libssh/priv.h" +#include "libssh/ssh2.h" +#include "libssh/buffer.h" +#include "libssh/packet.h" +#include "libssh/channels.h" +#include "libssh/session.h" +#include "libssh/misc.h" +#include "libssh/pki.h" +#include "libssh/dh.h" +#include "libssh/messages.h" +#ifdef WITH_SERVER +#include "libssh/server.h" +#endif + +/** + * @defgroup libssh_messages The SSH message functions + * @ingroup libssh + * + * This file contains the message parsing utilities for client and server + * programs using libssh. + * + * On the server the the main loop of the program will call + * ssh_message_get(session) to get messages as they come. They are not 1-1 with + * the protocol messages. Then, the user will know what kind of a message it is + * and use the appropriate functions to handle it (or use the default handlers + * if you don't know what to do). + * + * @{ + */ + +static ssh_message ssh_message_new(ssh_session session){ + ssh_message msg = malloc(sizeof(struct ssh_message_struct)); + if (msg == NULL) { + return NULL; + } + ZERO_STRUCTP(msg); + msg->session = session; + return msg; +} + +#ifndef WITH_SERVER + +/* Reduced version of the reply default that only reply with + * SSH_MSG_UNIMPLEMENTED + */ +static int ssh_message_reply_default(ssh_message msg) { + ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Reporting unknown packet"); + + if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_UNIMPLEMENTED) < 0) + goto error; + if (buffer_add_u32(msg->session->out_buffer, + htonl(msg->session->recv_seq-1)) < 0) + goto error; + return packet_send(msg->session); + error: + return SSH_ERROR; +} + +#endif + +static int ssh_execute_message_callback(ssh_session session, ssh_message msg) { + int ret; + if(session->ssh_message_callback != NULL) { + ret = session->ssh_message_callback(session, msg, + session->ssh_message_callback_data); + if(ret == 1) { + ret = ssh_message_reply_default(msg); + ssh_message_free(msg); + if(ret != SSH_OK) { + return ret; + } + } else { + ssh_message_free(msg); + } + } else { + ret = ssh_message_reply_default(msg); + ssh_message_free(msg); + if(ret != SSH_OK) { + return ret; + } + } + return SSH_OK; +} + + +/** + * @internal + * + * @brief Add a message to the current queue of messages to be parsed. + * + * @param[in] session The SSH session to add the message. + * + * @param[in] message The message to add to the queue. + */ +void ssh_message_queue(ssh_session session, ssh_message message){ + if(message) { + if(session->ssh_message_list == NULL) { + if(session->ssh_message_callback != NULL) { + ssh_execute_message_callback(session, message); + return; + } + session->ssh_message_list = ssh_list_new(); + } + ssh_list_append(session->ssh_message_list, message); + } +} + +/** + * @internal + * + * @brief Pop a message from the message list and dequeue it. + * + * @param[in] session The SSH session to pop the message. + * + * @returns The head message or NULL if it doesn't exist. + */ +ssh_message ssh_message_pop_head(ssh_session session){ + ssh_message msg=NULL; + struct ssh_iterator *i; + if(session->ssh_message_list == NULL) + return NULL; + i=ssh_list_get_iterator(session->ssh_message_list); + if(i != NULL){ + msg=ssh_iterator_value(ssh_message,i); + ssh_list_remove(session->ssh_message_list,i); + } + return msg; +} + +/* Returns 1 if there is a message available */ +static int ssh_message_termination(void *s){ + ssh_session session = s; + struct ssh_iterator *it; + if(session->session_state == SSH_SESSION_STATE_ERROR) + return 1; + it = ssh_list_get_iterator(session->ssh_message_list); + if(!it) + return 0; + else + return 1; +} +/** + * @brief Retrieve a SSH message from a SSH session. + * + * @param[in] session The SSH session to get the message. + * + * @returns The SSH message received, NULL in case of error, or timeout + * elapsed. + * + * @warning This function blocks until a message has been received. Betterset up + * a callback if this behavior is unwanted. + */ +ssh_message ssh_message_get(ssh_session session) { + ssh_message msg = NULL; + int rc; + enter_function(); + + msg=ssh_message_pop_head(session); + if(msg) { + leave_function(); + return msg; + } + if(session->ssh_message_list == NULL) { + session->ssh_message_list = ssh_list_new(); + } + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + ssh_message_termination, session); + if(rc || session->session_state == SSH_SESSION_STATE_ERROR) + return NULL; + msg=ssh_list_pop_head(ssh_message, session->ssh_message_list); + leave_function(); + return msg; +} + +/** + * @brief Get the type of the message. + * + * @param[in] msg The message to get the type from. + * + * @return The message type or -1 on error. + */ +int ssh_message_type(ssh_message msg) { + if (msg == NULL) { + return -1; + } + + return msg->type; +} + +/** + * @brief Get the subtype of the message. + * + * @param[in] msg The message to get the subtype from. + * + * @return The message type or -1 on error. + */ +int ssh_message_subtype(ssh_message msg) { + if (msg == NULL) { + return -1; + } + + switch(msg->type) { + case SSH_REQUEST_AUTH: + return msg->auth_request.method; + case SSH_REQUEST_CHANNEL_OPEN: + return msg->channel_request_open.type; + case SSH_REQUEST_CHANNEL: + return msg->channel_request.type; + case SSH_REQUEST_GLOBAL: + return msg->global_request.type; + } + + return -1; +} + +/** + * @brief Free a SSH message. + * + * @param[in] msg The message to release the memory. + */ +void ssh_message_free(ssh_message msg){ + if (msg == NULL) { + return; + } + + switch(msg->type) { + case SSH_REQUEST_AUTH: + SAFE_FREE(msg->auth_request.username); + if (msg->auth_request.password) { + memset(msg->auth_request.password, 0, + strlen(msg->auth_request.password)); + SAFE_FREE(msg->auth_request.password); + } + ssh_key_free(msg->auth_request.pubkey); + break; + case SSH_REQUEST_CHANNEL_OPEN: + SAFE_FREE(msg->channel_request_open.originator); + SAFE_FREE(msg->channel_request_open.destination); + break; + case SSH_REQUEST_CHANNEL: + SAFE_FREE(msg->channel_request.TERM); + SAFE_FREE(msg->channel_request.modes); + SAFE_FREE(msg->channel_request.var_name); + SAFE_FREE(msg->channel_request.var_value); + SAFE_FREE(msg->channel_request.command); + SAFE_FREE(msg->channel_request.subsystem); + break; + case SSH_REQUEST_SERVICE: + SAFE_FREE(msg->service_request.service); + break; + case SSH_REQUEST_GLOBAL: + SAFE_FREE(msg->global_request.bind_address); + break; + } + ZERO_STRUCTP(msg); + SAFE_FREE(msg); +} + +SSH_PACKET_CALLBACK(ssh_packet_service_request){ + ssh_string service = NULL; + char *service_c = NULL; + ssh_message msg=NULL; + + enter_function(); + (void)type; + (void)user; + service = buffer_get_ssh_string(packet); + if (service == NULL) { + ssh_set_error(session, SSH_FATAL, "Invalid SSH_MSG_SERVICE_REQUEST packet"); + goto error; + } + + service_c = ssh_string_to_char(service); + if (service_c == NULL) { + goto error; + } + ssh_log(session, SSH_LOG_PACKET, + "Received a SERVICE_REQUEST for service %s", service_c); + msg=ssh_message_new(session); + if(!msg){ + SAFE_FREE(service_c); + goto error; + } + msg->type=SSH_REQUEST_SERVICE; + msg->service_request.service=service_c; +error: + ssh_string_free(service); + if(msg != NULL) + ssh_message_queue(session,msg); + leave_function(); + return SSH_PACKET_USED; +} + + +/* + * This function concats in a buffer the values needed to do a signature + * verification. + */ +static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session, + ssh_message msg, + const char *service) +{ + struct ssh_crypto_struct *crypto = + session->current_crypto ? session->current_crypto : + session->next_crypto; + ssh_buffer buffer; + ssh_string str; + int rc; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + return NULL; + } + + /* Add session id */ + str = ssh_string_new(crypto->digest_len); + if (str == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + ssh_string_fill(str, crypto->session_id, crypto->digest_len); + + rc = buffer_add_ssh_string(buffer, str); + string_free(str); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + /* Add the type */ + rc = buffer_add_u8(buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + /* Add the username */ + str = ssh_string_from_char(msg->auth_request.username); + if (str == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + rc = buffer_add_ssh_string(buffer, str); + string_free(str); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + /* Add the service name */ + str = ssh_string_from_char(service); + if (str == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + rc = buffer_add_ssh_string(buffer, str); + string_free(str); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + /* Add the method (publickey) */ + str = ssh_string_from_char("publickey"); + if (str == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + rc = buffer_add_ssh_string(buffer, str); + string_free(str); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + /* Has been signed (TRUE) */ + rc = buffer_add_u8(buffer, 1); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + /* Add the public key algorithm */ + str = ssh_string_from_char(msg->auth_request.pubkey->type_c); + if (str == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + rc = buffer_add_ssh_string(buffer, str); + string_free(str); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + /* Add the publickey as blob */ + rc = ssh_pki_export_pubkey_blob(msg->auth_request.pubkey, &str); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + rc = buffer_add_ssh_string(buffer, str); + string_free(str); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + return buffer; +} + +/** + * @internal + * + * @brief Handle a SSH_MSG_MSG_USERAUTH_REQUEST packet and queue a + * SSH Message + */ +SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ + ssh_string str; + ssh_message msg = NULL; + char *service = NULL; + char *method = NULL; + uint32_t method_size = 0; + + enter_function(); + + (void)user; + (void)type; + + msg = ssh_message_new(session); + if (msg == NULL) { + ssh_set_error_oom(session); + goto error; + } + msg->type = SSH_REQUEST_AUTH; + + str = buffer_get_ssh_string(packet); + if (str == NULL) { + goto error; + } + msg->auth_request.username = ssh_string_to_char(str); + ssh_string_free(str); + if (msg->auth_request.username == NULL) { + goto error; + } + + str = buffer_get_ssh_string(packet); + if (str == NULL) { + goto error; + } + service = ssh_string_to_char(str); + ssh_string_free(str); + if (service == NULL) { + goto error; + } + + str = buffer_get_ssh_string(packet); + if (str == NULL) { + goto error; + } + method = ssh_string_to_char(str); + method_size = ssh_string_len(str); + ssh_string_free(str); + if (method == NULL) { + goto error; + } + + ssh_log(session, SSH_LOG_PACKET, + "Auth request for service %s, method %s for user '%s'", + service, method, + msg->auth_request.username); + + + if (strncmp(method, "none", method_size) == 0) { + msg->auth_request.method = SSH_AUTH_METHOD_NONE; + goto end; + } + + if (strncmp(method, "password", method_size) == 0) { + ssh_string pass = NULL; + uint8_t tmp; + + msg->auth_request.method = SSH_AUTH_METHOD_PASSWORD; + buffer_get_u8(packet, &tmp); + pass = buffer_get_ssh_string(packet); + if (pass == NULL) { + goto error; + } + msg->auth_request.password = ssh_string_to_char(pass); + ssh_string_burn(pass); + ssh_string_free(pass); + pass = NULL; + if (msg->auth_request.password == NULL) { + goto error; + } + goto end; + } + + if (strncmp(method, "keyboard-interactive", method_size) == 0) { + ssh_string lang = NULL; + ssh_string submethods = NULL; + + msg->auth_request.method = SSH_AUTH_METHOD_INTERACTIVE; + lang = buffer_get_ssh_string(packet); + if (lang == NULL) { + goto error; + } + /* from the RFC 4256 + * 3.1. Initial Exchange + * "The language tag is deprecated and SHOULD be the empty string." + */ + ssh_string_free(lang); + + submethods = buffer_get_ssh_string(packet); + if (submethods == NULL) { + goto error; + } + /* from the RFC 4256 + * 3.1. Initial Exchange + * "One possible implementation strategy of the submethods field on the + * server is that, unless the user may use multiple different + * submethods, the server ignores this field." + */ + ssh_string_free(submethods); + + goto end; + } + + if (strncmp(method, "publickey", method_size) == 0) { + ssh_string algo = NULL; + ssh_string pubkey_blob = NULL; + uint8_t has_sign; + int rc; + + msg->auth_request.method = SSH_AUTH_METHOD_PUBLICKEY; + SAFE_FREE(method); + buffer_get_u8(packet, &has_sign); + algo = buffer_get_ssh_string(packet); + if (algo == NULL) { + goto error; + } + pubkey_blob = buffer_get_ssh_string(packet); + if (pubkey_blob == NULL) { + ssh_string_free(algo); + algo = NULL; + goto error; + } + ssh_string_free(algo); + algo = NULL; + + rc = ssh_pki_import_pubkey_blob(pubkey_blob, &msg->auth_request.pubkey); + ssh_string_free(pubkey_blob); + pubkey_blob = NULL; + if (rc < 0) { + goto error; + } + msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_NONE; + // has a valid signature ? + if(has_sign) { + ssh_string sig_blob = NULL; + ssh_buffer digest = NULL; + + sig_blob = buffer_get_ssh_string(packet); + if(sig_blob == NULL) { + ssh_log(session, SSH_LOG_PACKET, "Invalid signature packet from peer"); + msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_ERROR; + goto error; + } + + digest = ssh_msg_userauth_build_digest(session, msg, service); + if (digest == NULL) { + ssh_string_free(sig_blob); + ssh_log(session, SSH_LOG_PACKET, "Failed to get digest"); + msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG; + goto error; + } + + rc = ssh_pki_signature_verify_blob(session, + sig_blob, + msg->auth_request.pubkey, + buffer_get_rest(digest), + buffer_get_rest_len(digest)); + ssh_string_free(sig_blob); + ssh_buffer_free(digest); + if (rc < 0) { + ssh_log(session, + SSH_LOG_PACKET, + "Received an invalid signature from peer"); + msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG; + goto error; + } + + ssh_log(session, SSH_LOG_PACKET, "Valid signature received"); + + msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_VALID; + } + goto end; + } + + msg->auth_request.method = SSH_AUTH_METHOD_UNKNOWN; + SAFE_FREE(method); + goto end; +error: + SAFE_FREE(service); + SAFE_FREE(method); + + ssh_message_free(msg); + + leave_function(); + return SSH_PACKET_USED; +end: + SAFE_FREE(service); + SAFE_FREE(method); + + ssh_message_queue(session,msg); + leave_function(); + return SSH_PACKET_USED; +} + +/** + * @internal + * + * @brief Handle a SSH_MSG_MSG_USERAUTH_INFO_RESPONSE packet and queue a + * SSH Message + */ +#ifndef WITH_SERVER +SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ + (void)session; + (void)type; + (void)packet; + (void)user; + return SSH_PACKET_USED; +} +#else +SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ + uint32_t nanswers; + uint32_t i; + ssh_string tmp; + + ssh_message msg = NULL; + + enter_function(); + + (void)user; + (void)type; + + msg = ssh_message_new(session); + if (msg == NULL) { + ssh_set_error_oom(session); + goto error; + } + + /* HACK: we forge a message to be able to handle it in the + * same switch() as other auth methods */ + msg->type = SSH_REQUEST_AUTH; + msg->auth_request.method = SSH_AUTH_METHOD_INTERACTIVE; + msg->auth_request.kbdint_response = 1; +#if 0 // should we wipe the username ? + msg->auth_request.username = NULL; +#endif + + buffer_get_u32(packet, &nanswers); + + if (session->kbdint == NULL) { + ssh_log(session, SSH_LOG_PROTOCOL, "Warning: Got a keyboard-interactive " + "response but it seems we didn't send the request."); + + session->kbdint = ssh_kbdint_new(); + if (session->kbdint == NULL) { + ssh_set_error_oom(session); + + goto error; + } + } + + nanswers = ntohl(nanswers); + ssh_log(session,SSH_LOG_PACKET,"kbdint: %d answers",nanswers); + if (nanswers > KBDINT_MAX_PROMPT) { + ssh_set_error(session, SSH_FATAL, + "Too much answers received from client: %u (0x%.4x)", + nanswers, nanswers); + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + + goto error; + } + + if(nanswers != session->kbdint->nprompts) { + /* warn but let the application handle this case */ + ssh_log(session, SSH_LOG_PROTOCOL, "Warning: Number of prompts and answers" + " mismatch: p=%u a=%u", session->kbdint->nprompts, nanswers); + } + session->kbdint->nanswers = nanswers; + session->kbdint->answers = malloc(nanswers * sizeof(char *)); + if (session->kbdint->answers == NULL) { + session->kbdint->nanswers = 0; + ssh_set_error_oom(session); + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + + goto error; + } + memset(session->kbdint->answers, 0, nanswers * sizeof(char *)); + + for (i = 0; i < nanswers; i++) { + tmp = buffer_get_ssh_string(packet); + if (tmp == NULL) { + ssh_set_error(session, SSH_FATAL, "Short INFO_RESPONSE packet"); + session->kbdint->nanswers = i; + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + + goto error; + } + session->kbdint->answers[i] = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (session->kbdint->answers[i] == NULL) { + ssh_set_error_oom(session); + session->kbdint->nanswers = i; + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + + goto error; + } + } + + ssh_message_queue(session,msg); + leave_function(); + return SSH_PACKET_USED; + +error: + ssh_message_free(msg); + + leave_function(); + return SSH_PACKET_USED; +} +#endif + +SSH_PACKET_CALLBACK(ssh_packet_channel_open){ + ssh_message msg = NULL; + ssh_string type_s = NULL, originator = NULL, destination = NULL; + char *type_c = NULL; + uint32_t sender, window, packet_size, originator_port, destination_port; + + enter_function(); + (void)type; + (void)user; + msg = ssh_message_new(session); + if (msg == NULL) { + ssh_set_error_oom(session); + goto error; + } + + msg->type = SSH_REQUEST_CHANNEL_OPEN; + + type_s = buffer_get_ssh_string(packet); + if (type_s == NULL) { + ssh_set_error_oom(session); + goto error; + } + type_c = ssh_string_to_char(type_s); + if (type_c == NULL) { + ssh_set_error_oom(session); + goto error; + } + + ssh_log(session, SSH_LOG_PACKET, + "Clients wants to open a %s channel", type_c); + ssh_string_free(type_s); + type_s=NULL; + + buffer_get_u32(packet, &sender); + buffer_get_u32(packet, &window); + buffer_get_u32(packet, &packet_size); + + msg->channel_request_open.sender = ntohl(sender); + msg->channel_request_open.window = ntohl(window); + msg->channel_request_open.packet_size = ntohl(packet_size); + + if (strcmp(type_c,"session") == 0) { + msg->channel_request_open.type = SSH_CHANNEL_SESSION; + SAFE_FREE(type_c); + goto end; + } + + if (strcmp(type_c,"direct-tcpip") == 0) { + destination = buffer_get_ssh_string(packet); + if (destination == NULL) { + ssh_set_error_oom(session); + goto error; + } + msg->channel_request_open.destination = ssh_string_to_char(destination); + if (msg->channel_request_open.destination == NULL) { + ssh_set_error_oom(session); + ssh_string_free(destination); + goto error; + } + ssh_string_free(destination); + + buffer_get_u32(packet, &destination_port); + msg->channel_request_open.destination_port = (uint16_t) ntohl(destination_port); + + originator = buffer_get_ssh_string(packet); + if (originator == NULL) { + ssh_set_error_oom(session); + goto error; + } + msg->channel_request_open.originator = ssh_string_to_char(originator); + if (msg->channel_request_open.originator == NULL) { + ssh_set_error_oom(session); + ssh_string_free(originator); + goto error; + } + ssh_string_free(originator); + + buffer_get_u32(packet, &originator_port); + msg->channel_request_open.originator_port = (uint16_t) ntohl(originator_port); + + msg->channel_request_open.type = SSH_CHANNEL_DIRECT_TCPIP; + goto end; + } + + if (strcmp(type_c,"forwarded-tcpip") == 0) { + destination = buffer_get_ssh_string(packet); + if (destination == NULL) { + ssh_set_error_oom(session); + goto error; + } + msg->channel_request_open.destination = ssh_string_to_char(destination); + if (msg->channel_request_open.destination == NULL) { + ssh_set_error_oom(session); + ssh_string_free(destination); + goto error; + } + ssh_string_free(destination); + + buffer_get_u32(packet, &destination_port); + msg->channel_request_open.destination_port = (uint16_t) ntohl(destination_port); + + originator = buffer_get_ssh_string(packet); + if (originator == NULL) { + ssh_set_error_oom(session); + goto error; + } + msg->channel_request_open.originator = ssh_string_to_char(originator); + if (msg->channel_request_open.originator == NULL) { + ssh_set_error_oom(session); + ssh_string_free(originator); + goto error; + } + ssh_string_free(originator); + + buffer_get_u32(packet, &originator_port); + msg->channel_request_open.originator_port = (uint16_t) ntohl(originator_port); + + msg->channel_request_open.type = SSH_CHANNEL_FORWARDED_TCPIP; + goto end; + } + + if (strcmp(type_c,"x11") == 0) { + originator = buffer_get_ssh_string(packet); + if (originator == NULL) { + ssh_set_error_oom(session); + goto error; + } + msg->channel_request_open.originator = ssh_string_to_char(originator); + if (msg->channel_request_open.originator == NULL) { + ssh_set_error_oom(session); + ssh_string_free(originator); + goto error; + } + ssh_string_free(originator); + + buffer_get_u32(packet, &originator_port); + msg->channel_request_open.originator_port = (uint16_t) ntohl(originator_port); + + msg->channel_request_open.type = SSH_CHANNEL_X11; + goto end; + } + + msg->channel_request_open.type = SSH_CHANNEL_UNKNOWN; + goto end; + +error: + ssh_message_free(msg); + msg=NULL; +end: + if(type_s != NULL) + ssh_string_free(type_s); + SAFE_FREE(type_c); + if(msg != NULL) + ssh_message_queue(session,msg); + leave_function(); + return SSH_PACKET_USED; +} + +/* TODO: make this function accept a ssh_channel */ +ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg) { + ssh_session session; + ssh_channel chan = NULL; + + enter_function(); + + if (msg == NULL) { + leave_function(); + return NULL; + } + + session = msg->session; + + chan = ssh_channel_new(session); + if (chan == NULL) { + leave_function(); + return NULL; + } + + chan->local_channel = ssh_channel_new_id(session); + chan->local_maxpacket = 35000; + chan->local_window = 32000; + chan->remote_channel = msg->channel_request_open.sender; + chan->remote_maxpacket = msg->channel_request_open.packet_size; + chan->remote_window = msg->channel_request_open.window; + chan->state = SSH_CHANNEL_STATE_OPEN; + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) < 0) { + goto error; + } + if (buffer_add_u32(session->out_buffer, htonl(chan->remote_channel)) < 0) { + goto error; + } + if (buffer_add_u32(session->out_buffer, htonl(chan->local_channel)) < 0) { + goto error; + } + if (buffer_add_u32(session->out_buffer, htonl(chan->local_window)) < 0) { + goto error; + } + if (buffer_add_u32(session->out_buffer, htonl(chan->local_maxpacket)) < 0) { + goto error; + } + + ssh_log(session, SSH_LOG_PACKET, + "Accepting a channel request_open for chan %d", chan->remote_channel); + + if (packet_send(session) == SSH_ERROR) { + goto error; + } + + leave_function(); + return chan; +error: + ssh_channel_free(chan); + + leave_function(); + return NULL; +} + +/** + * @internal + * + * @brief This function parses the last end of a channel request packet. + * + * This is normally converted to a SSH message and placed in the queue. + * + * @param[in] session The SSH session. + * + * @param[in] channel The channel the request is made on. + * + * @param[in] packet The rest of the packet to be parsed. + * + * @param[in] request The type of request. + * + * @param[in] want_reply The want_reply field from the request. + * + * @returns SSH_OK on success, SSH_ERROR if an error occured. + */ +int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet, + const char *request, uint8_t want_reply) { + ssh_message msg = NULL; + enter_function(); + msg = ssh_message_new(session); + if (msg == NULL) { + ssh_set_error_oom(session); + goto error; + } + + ssh_log(session, SSH_LOG_PACKET, + "Received a %s channel_request for channel (%d:%d) (want_reply=%hhd)", + request, channel->local_channel, channel->remote_channel, want_reply); + + msg->type = SSH_REQUEST_CHANNEL; + msg->channel_request.channel = channel; + msg->channel_request.want_reply = want_reply; + + if (strcmp(request, "pty-req") == 0) { + ssh_string term = NULL; + char *term_c = NULL; + term = buffer_get_ssh_string(packet); + if (term == NULL) { + ssh_set_error_oom(session); + goto error; + } + term_c = ssh_string_to_char(term); + if (term_c == NULL) { + ssh_set_error_oom(session); + ssh_string_free(term); + goto error; + } + ssh_string_free(term); + + msg->channel_request.type = SSH_CHANNEL_REQUEST_PTY; + msg->channel_request.TERM = term_c; + + buffer_get_u32(packet, &msg->channel_request.width); + buffer_get_u32(packet, &msg->channel_request.height); + buffer_get_u32(packet, &msg->channel_request.pxwidth); + buffer_get_u32(packet, &msg->channel_request.pxheight); + + msg->channel_request.width = ntohl(msg->channel_request.width); + msg->channel_request.height = ntohl(msg->channel_request.height); + msg->channel_request.pxwidth = ntohl(msg->channel_request.pxwidth); + msg->channel_request.pxheight = ntohl(msg->channel_request.pxheight); + msg->channel_request.modes = buffer_get_ssh_string(packet); + if (msg->channel_request.modes == NULL) { + SAFE_FREE(term_c); + goto error; + } + goto end; + } + + if (strcmp(request, "window-change") == 0) { + msg->channel_request.type = SSH_CHANNEL_REQUEST_WINDOW_CHANGE; + + buffer_get_u32(packet, &msg->channel_request.width); + buffer_get_u32(packet, &msg->channel_request.height); + buffer_get_u32(packet, &msg->channel_request.pxwidth); + buffer_get_u32(packet, &msg->channel_request.pxheight); + + msg->channel_request.width = ntohl(msg->channel_request.width); + msg->channel_request.height = ntohl(msg->channel_request.height); + msg->channel_request.pxwidth = ntohl(msg->channel_request.pxwidth); + msg->channel_request.pxheight = ntohl(msg->channel_request.pxheight); + + goto end; + } + + if (strcmp(request, "subsystem") == 0) { + ssh_string subsys = NULL; + char *subsys_c = NULL; + subsys = buffer_get_ssh_string(packet); + if (subsys == NULL) { + ssh_set_error_oom(session); + goto error; + } + subsys_c = ssh_string_to_char(subsys); + if (subsys_c == NULL) { + ssh_set_error_oom(session); + ssh_string_free(subsys); + goto error; + } + ssh_string_free(subsys); + + msg->channel_request.type = SSH_CHANNEL_REQUEST_SUBSYSTEM; + msg->channel_request.subsystem = subsys_c; + + goto end; + } + + if (strcmp(request, "shell") == 0) { + msg->channel_request.type = SSH_CHANNEL_REQUEST_SHELL; + goto end; + } + + if (strcmp(request, "exec") == 0) { + ssh_string cmd = NULL; + cmd = buffer_get_ssh_string(packet); + if (cmd == NULL) { + ssh_set_error_oom(session); + goto error; + } + msg->channel_request.type = SSH_CHANNEL_REQUEST_EXEC; + msg->channel_request.command = ssh_string_to_char(cmd); + ssh_string_free(cmd); + if (msg->channel_request.command == NULL) { + goto error; + } + goto end; + } + + if (strcmp(request, "env") == 0) { + ssh_string name = NULL; + ssh_string value = NULL; + name = buffer_get_ssh_string(packet); + if (name == NULL) { + ssh_set_error_oom(session); + goto error; + } + value = buffer_get_ssh_string(packet); + if (value == NULL) { + ssh_set_error_oom(session); + ssh_string_free(name); + goto error; + } + + msg->channel_request.type = SSH_CHANNEL_REQUEST_ENV; + msg->channel_request.var_name = ssh_string_to_char(name); + msg->channel_request.var_value = ssh_string_to_char(value); + if (msg->channel_request.var_name == NULL || + msg->channel_request.var_value == NULL) { + ssh_string_free(name); + ssh_string_free(value); + goto error; + } + ssh_string_free(name); + ssh_string_free(value); + + goto end; + } + + if (strcmp(request, "x11-req") == 0) { + ssh_string auth_protocol = NULL; + ssh_string auth_cookie = NULL; + + buffer_get_u8(packet, &msg->channel_request.x11_single_connection); + + auth_protocol = buffer_get_ssh_string(packet); + if (auth_protocol == NULL) { + ssh_set_error_oom(session); + goto error; + } + auth_cookie = buffer_get_ssh_string(packet); + if (auth_cookie == NULL) { + ssh_set_error_oom(session); + ssh_string_free(auth_protocol); + goto error; + } + + msg->channel_request.type = SSH_CHANNEL_REQUEST_X11; + msg->channel_request.x11_auth_protocol = ssh_string_to_char(auth_protocol); + msg->channel_request.x11_auth_cookie = ssh_string_to_char(auth_cookie); + if (msg->channel_request.x11_auth_protocol == NULL || + msg->channel_request.x11_auth_cookie == NULL) { + ssh_string_free(auth_protocol); + ssh_string_free(auth_cookie); + goto error; + } + ssh_string_free(auth_protocol); + ssh_string_free(auth_cookie); + + buffer_get_u32(packet, &msg->channel_request.x11_screen_number); + + goto end; + } + + msg->channel_request.type = SSH_CHANNEL_REQUEST_UNKNOWN; +end: + ssh_message_queue(session,msg); + leave_function(); + return SSH_OK; +error: + ssh_message_free(msg); + + leave_function(); + return SSH_ERROR; +} + +int ssh_message_channel_request_reply_success(ssh_message msg) { + uint32_t channel; + + if (msg == NULL) { + return SSH_ERROR; + } + + if (msg->channel_request.want_reply) { + channel = msg->channel_request.channel->remote_channel; + + ssh_log(msg->session, SSH_LOG_PACKET, + "Sending a channel_request success to channel %d", channel); + + if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_CHANNEL_SUCCESS) < 0) { + return SSH_ERROR; + } + if (buffer_add_u32(msg->session->out_buffer, htonl(channel)) < 0) { + return SSH_ERROR; + } + + return packet_send(msg->session); + } + + ssh_log(msg->session, SSH_LOG_PACKET, + "The client doesn't want to know the request succeeded"); + + return SSH_OK; +} + +#ifdef WITH_SERVER +SSH_PACKET_CALLBACK(ssh_packet_global_request){ + ssh_message msg = NULL; + ssh_string request_s=NULL; + char *request=NULL; + ssh_string bind_addr_s=NULL; + char *bind_addr=NULL; + uint32_t bind_port; + uint8_t want_reply; + int rc = SSH_PACKET_USED; + (void)user; + (void)type; + (void)packet; + + request_s = buffer_get_ssh_string(packet); + if (request_s != NULL) { + request = ssh_string_to_char(request_s); + ssh_string_free(request_s); + } + + buffer_get_u8(packet, &want_reply); + + ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_MSG_GLOBAL_REQUEST packet"); + + msg = ssh_message_new(session); + if (msg == NULL) { + return SSH_PACKET_NOT_USED; + } + msg->type = SSH_REQUEST_GLOBAL; + + if (request && strcmp(request, "tcpip-forward") == 0) { + bind_addr_s = buffer_get_ssh_string(packet); + if (bind_addr_s != NULL) { + bind_addr = ssh_string_to_char(bind_addr_s); + ssh_string_free(bind_addr_s); + } + + buffer_get_u32(packet, &bind_port); + bind_port = ntohl(bind_port); + + msg->global_request.type = SSH_GLOBAL_REQUEST_TCPIP_FORWARD; + msg->global_request.want_reply = want_reply; + msg->global_request.bind_address = bind_addr; + msg->global_request.bind_port = bind_port; + + ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + + if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) { + ssh_log(session, SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata); + } else { + ssh_message_reply_default(msg); + } + } else if (request && strcmp(request, "cancel-tcpip-forward") == 0) { + bind_addr_s = buffer_get_ssh_string(packet); + if (bind_addr_s != NULL) { + bind_addr = ssh_string_to_char(bind_addr_s); + ssh_string_free(bind_addr_s); + } + buffer_get_u32(packet, &bind_port); + bind_port = ntohl(bind_port); + + msg->global_request.type = SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD; + msg->global_request.want_reply = want_reply; + msg->global_request.bind_address = bind_addr; + msg->global_request.bind_port = bind_port; + + ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + + if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) { + session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata); + } else { + ssh_message_reply_default(msg); + } + } else { + ssh_log(session, SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s %d", request, want_reply); + rc = SSH_PACKET_NOT_USED; + } + + SAFE_FREE(msg); + SAFE_FREE(request); + SAFE_FREE(bind_addr); + + return rc; +} + +#endif /* WITH_SERVER */ + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/misc.c b/libssh/src/misc.c new file mode 100644 index 00000000..99f60b48 --- /dev/null +++ b/libssh/src/misc.c @@ -0,0 +1,1036 @@ +/* + * misc.c - useful client functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * Copyright (c) 2008-2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#ifndef _WIN32 +/* This is needed for a standard getpwuid_r on opensolaris */ +#define _POSIX_PTHREAD_SEMANTICS +#include +#include +#include +#include +#include + +#ifndef HAVE_CLOCK_GETTIME +#include +#endif /* HAVE_CLOCK_GETTIME */ +#endif /* _WIN32 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#ifndef _WIN32_IE +# define _WIN32_IE 0x0501 // SHGetSpecialFolderPath +#endif + +#include // Must be the first to include +#include +#include +#include + +#if _MSC_VER >= 1400 +# include +#endif /* _MSC_VER */ + +#endif /* _WIN32 */ + +#include "libssh/priv.h" +#include "libssh/misc.h" +#include "libssh/session.h" + +#ifdef HAVE_LIBGCRYPT +#define GCRYPT_STRING "/gnutls" +#else +#define GCRYPT_STRING "" +#endif + +#ifdef HAVE_LIBCRYPTO +#define CRYPTO_STRING "/openssl" +#else +#define CRYPTO_STRING "" +#endif + +#ifdef WITH_ZLIB +#define ZLIB_STRING "/zlib" +#else +#define ZLIB_STRING "" +#endif + +/** + * @defgroup libssh_misc The SSH helper functions. + * @ingroup libssh + * + * Different helper functions used in the SSH Library. + * + * @{ + */ + +#ifdef _WIN32 +char *ssh_get_user_home_dir(void) { + char tmp[MAX_PATH] = {0}; + char *szPath = NULL; + + if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) { + szPath = malloc(strlen(tmp) + 1); + if (szPath == NULL) { + return NULL; + } + + strcpy(szPath, tmp); + return szPath; + } + + return NULL; +} + +/* we have read access on file */ +int ssh_file_readaccess_ok(const char *file) { + if (_access(file, 4) < 0) { + return 0; + } + + return 1; +} + +#define SSH_USEC_IN_SEC 1000000LL +#define SSH_SECONDS_SINCE_1601 11644473600LL + +int gettimeofday(struct timeval *__p, void *__t) { + union { + unsigned long long ns100; /* time since 1 Jan 1601 in 100ns units */ + FILETIME ft; + } now; + + GetSystemTimeAsFileTime (&now.ft); + __p->tv_usec = (long) ((now.ns100 / 10LL) % SSH_USEC_IN_SEC); + __p->tv_sec = (long)(((now.ns100 / 10LL ) / SSH_USEC_IN_SEC) - SSH_SECONDS_SINCE_1601); + + return (0); +} + +char *ssh_get_local_username(void) { + DWORD size = 0; + char *user; + + /* get the size */ + GetUserName(NULL, &size); + + user = (char *) malloc(size); + if (user == NULL) { + return NULL; + } + + if (GetUserName(user, &size)) { + return user; + } + + return NULL; +} + +int ssh_is_ipaddr_v4(const char *str) { + struct sockaddr_storage ss; + int sslen = sizeof(ss); + int rc = SOCKET_ERROR; + + /* WSAStringToAddressA thinks that 0.0.0 is a valid IP */ + if (strlen(str) < 7) { + return 0; + } + + rc = WSAStringToAddressA((LPSTR) str, + AF_INET, + NULL, + (struct sockaddr*)&ss, + &sslen); + if (rc == 0) { + return 1; + } + + return 0; +} + +int ssh_is_ipaddr(const char *str) { + int rc = SOCKET_ERROR; + + if (strchr(str, ':')) { + struct sockaddr_storage ss; + int sslen = sizeof(ss); + + /* TODO link-local (IP:v6:addr%ifname). */ + rc = WSAStringToAddressA((LPSTR) str, + AF_INET6, + NULL, + (struct sockaddr*)&ss, + &sslen); + if (rc == 0) { + return 1; + } + } + + return ssh_is_ipaddr_v4(str); +} +#else /* _WIN32 */ + +#ifndef NSS_BUFLEN_PASSWD +#define NSS_BUFLEN_PASSWD 4096 +#endif /* NSS_BUFLEN_PASSWD */ + +char *ssh_get_user_home_dir(void) { + char *szPath = NULL; + struct passwd pwd; + struct passwd *pwdbuf; + char buf[NSS_BUFLEN_PASSWD]; + int rc; + + rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); + if (rc != 0) { + szPath = getenv("HOME"); + if (szPath == NULL) { + return NULL; + } + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf), "%s", getenv("HOME")); + + return strdup(buf); + } + + szPath = strdup(pwd.pw_dir); + + return szPath; +} + +/* we have read access on file */ +int ssh_file_readaccess_ok(const char *file) { + if (access(file, R_OK) < 0) { + return 0; + } + + return 1; +} + +char *ssh_get_local_username(void) { + struct passwd pwd; + struct passwd *pwdbuf; + char buf[NSS_BUFLEN_PASSWD]; + char *name; + int rc; + + rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); + if (rc != 0) { + return NULL; + } + + name = strdup(pwd.pw_name); + + if (name == NULL) { + return NULL; + } + + return name; +} + +int ssh_is_ipaddr_v4(const char *str) { + int rc = -1; + struct in_addr dest; + + rc = inet_pton(AF_INET, str, &dest); + if (rc > 0) { + return 1; + } + + return 0; +} + +int ssh_is_ipaddr(const char *str) { + int rc = -1; + + if (strchr(str, ':')) { + struct in6_addr dest6; + + /* TODO link-local (IP:v6:addr%ifname). */ + rc = inet_pton(AF_INET6, str, &dest6); + if (rc > 0) { + return 1; + } + } + + return ssh_is_ipaddr_v4(str); +} + +#endif /* _WIN32 */ + +#ifndef HAVE_NTOHLL +uint64_t ntohll(uint64_t a) { +#ifdef WORDS_BIGENDIAN + return a; +#else /* WORDS_BIGENDIAN */ + return (((uint64_t)(a) << 56) | \ + (((uint64_t)(a) << 40) & 0xff000000000000ULL) | \ + (((uint64_t)(a) << 24) & 0xff0000000000ULL) | \ + (((uint64_t)(a) << 8) & 0xff00000000ULL) | \ + (((uint64_t)(a) >> 8) & 0xff000000ULL) | \ + (((uint64_t)(a) >> 24) & 0xff0000ULL) | \ + (((uint64_t)(a) >> 40) & 0xff00ULL) | \ + ((uint64_t)(a) >> 56)); +#endif /* WORDS_BIGENDIAN */ +} +#endif /* HAVE_NTOHLL */ + +char *ssh_lowercase(const char* str) { + char *new, *p; + + if (str == NULL) { + return NULL; + } + + new = strdup(str); + if (new == NULL) { + return NULL; + } + + for (p = new; *p; p++) { + *p = tolower(*p); + } + + return new; +} + +char *ssh_hostport(const char *host, int port){ + char *dest; + size_t len; + if(host==NULL) + return NULL; + /* 3 for []:, 5 for 65536 and 1 for nul */ + len=strlen(host) + 3 + 5 + 1; + dest=malloc(len); + if(dest==NULL) + return NULL; + snprintf(dest,len,"[%s]:%d",host,port); + return dest; +} + +/** + * @brief Check if libssh is the required version or get the version + * string. + * + * @param[in] req_version The version required. + * + * @return If the version of libssh is newer than the version + * required it will return a version string. + * NULL if the version is older. + * + * Example: + * + * @code + * if (ssh_version(SSH_VERSION_INT(0,2,1)) == NULL) { + * fprintf(stderr, "libssh version is too old!\n"); + * exit(1); + * } + * + * if (debug) { + * printf("libssh %s\n", ssh_version(0)); + * } + * @endcode + */ +const char *ssh_version(int req_version) { + if (req_version <= LIBSSH_VERSION_INT) { + return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING + ZLIB_STRING; + } + + return NULL; +} + +struct ssh_list *ssh_list_new(void) { + struct ssh_list *ret=malloc(sizeof(struct ssh_list)); + if(!ret) + return NULL; + ret->root=ret->end=NULL; + return ret; +} + +void ssh_list_free(struct ssh_list *list){ + struct ssh_iterator *ptr,*next; + if(!list) + return; + ptr=list->root; + while(ptr){ + next=ptr->next; + SAFE_FREE(ptr); + ptr=next; + } + SAFE_FREE(list); +} + +struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list){ + if(!list) + return NULL; + return list->root; +} + +struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value){ + struct ssh_iterator *it; + for(it = ssh_list_get_iterator(list); it != NULL ;it=it->next) + if(it->data==value) + return it; + return NULL; +} + +static struct ssh_iterator *ssh_iterator_new(const void *data){ + struct ssh_iterator *iterator=malloc(sizeof(struct ssh_iterator)); + if(!iterator) + return NULL; + iterator->next=NULL; + iterator->data=data; + return iterator; +} + +int ssh_list_append(struct ssh_list *list,const void *data){ + struct ssh_iterator *iterator=ssh_iterator_new(data); + if(!iterator) + return SSH_ERROR; + if(!list->end){ + /* list is empty */ + list->root=list->end=iterator; + } else { + /* put it on end of list */ + list->end->next=iterator; + list->end=iterator; + } + return SSH_OK; +} + +int ssh_list_prepend(struct ssh_list *list, const void *data){ + struct ssh_iterator *it = ssh_iterator_new(data); + + if (it == NULL) { + return SSH_ERROR; + } + + if (list->end == NULL) { + /* list is empty */ + list->root = list->end = it; + } else { + /* set as new root */ + it->next = list->root; + list->root = it; + } + + return SSH_OK; +} + +void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator){ + struct ssh_iterator *ptr,*prev; + prev=NULL; + ptr=list->root; + while(ptr && ptr != iterator){ + prev=ptr; + ptr=ptr->next; + } + if(!ptr){ + /* we did not find the element */ + return; + } + /* unlink it */ + if(prev) + prev->next=ptr->next; + /* if iterator was the head */ + if(list->root == iterator) + list->root=iterator->next; + /* if iterator was the tail */ + if(list->end == iterator) + list->end = prev; + SAFE_FREE(iterator); +} + +/** + * @internal + * + * @brief Removes the top element of the list and returns the data value + * attached to it. + * + * @param[in[ list The ssh_list to remove the element. + * + * @returns A pointer to the element being stored in head, or NULL + * if the list is empty. + */ +const void *_ssh_list_pop_head(struct ssh_list *list){ + struct ssh_iterator *iterator=list->root; + const void *data; + if(!list->root) + return NULL; + data=iterator->data; + list->root=iterator->next; + if(list->end==iterator) + list->end=NULL; + SAFE_FREE(iterator); + return data; +} + +/** + * @brief Parse directory component. + * + * dirname breaks a null-terminated pathname string into a directory component. + * In the usual case, ssh_dirname() returns the string up to, but not including, + * the final '/'. Trailing '/' characters are not counted as part of the + * pathname. The caller must free the memory. + * + * @param[in] path The path to parse. + * + * @return The dirname of path or NULL if we can't allocate memory. + * If path does not contain a slash, c_dirname() returns + * the string ".". If path is the string "/", it returns + * the string "/". If path is NULL or an empty string, + * "." is returned. + */ +char *ssh_dirname (const char *path) { + char *new = NULL; + size_t len; + + if (path == NULL || *path == '\0') { + return strdup("."); + } + + len = strlen(path); + + /* Remove trailing slashes */ + while(len > 0 && path[len - 1] == '/') --len; + + /* We have only slashes */ + if (len == 0) { + return strdup("/"); + } + + /* goto next slash */ + while(len > 0 && path[len - 1] != '/') --len; + + if (len == 0) { + return strdup("."); + } else if (len == 1) { + return strdup("/"); + } + + /* Remove slashes again */ + while(len > 0 && path[len - 1] == '/') --len; + + new = malloc(len + 1); + if (new == NULL) { + return NULL; + } + + strncpy(new, path, len); + new[len] = '\0'; + + return new; +} + +/** + * @brief basename - parse filename component. + * + * basename breaks a null-terminated pathname string into a filename component. + * ssh_basename() returns the component following the final '/'. Trailing '/' + * characters are not counted as part of the pathname. + * + * @param[in] path The path to parse. + * + * @return The filename of path or NULL if we can't allocate + * memory. If path is a the string "/", basename returns + * the string "/". If path is NULL or an empty string, + * "." is returned. + */ +char *ssh_basename (const char *path) { + char *new = NULL; + const char *s; + size_t len; + + if (path == NULL || *path == '\0') { + return strdup("."); + } + + len = strlen(path); + /* Remove trailing slashes */ + while(len > 0 && path[len - 1] == '/') --len; + + /* We have only slashes */ + if (len == 0) { + return strdup("/"); + } + + while(len > 0 && path[len - 1] != '/') --len; + + if (len > 0) { + s = path + len; + len = strlen(s); + + while(len > 0 && s[len - 1] == '/') --len; + } else { + return strdup(path); + } + + new = malloc(len + 1); + if (new == NULL) { + return NULL; + } + + strncpy(new, s, len); + new[len] = '\0'; + + return new; +} + +/** + * @brief Attempts to create a directory with the given pathname. + * + * This is the portable version of mkdir, mode is ignored on Windows systems. + * + * @param[in] pathname The path name to create the directory. + * + * @param[in] mode The permissions to use. + * + * @return 0 on success, < 0 on error with errno set. + */ +int ssh_mkdir(const char *pathname, mode_t mode) { + int r; + +#ifdef _WIN32 + r = _mkdir(pathname); +#else + r = mkdir(pathname, mode); +#endif + + return r; +} + +/** + * @brief Expand a directory starting with a tilde '~' + * + * @param[in] d The directory to expand. + * + * @return The expanded directory, NULL on error. + */ +char *ssh_path_expand_tilde(const char *d) { + char *h = NULL, *r; + const char *p; + size_t ld; + size_t lh = 0; + + if (d[0] != '~') { + return strdup(d); + } + d++; + + /* handle ~user/path */ + p = strchr(d, '/'); + if (p != NULL && p > d) { +#ifdef _WIN32 + return strdup(d); +#else + struct passwd *pw; + size_t s = p - d; + char u[128]; + + if (s >= sizeof(u)) { + return NULL; + } + memcpy(u, d, s); + u[s] = '\0'; + pw = getpwnam(u); + if (pw == NULL) { + return NULL; + } + ld = strlen(p); + h = strdup(pw->pw_dir); +#endif + } else { + ld = strlen(d); + p = (char *) d; + h = ssh_get_user_home_dir(); + } + if (h == NULL) { + return NULL; + } + lh = strlen(h); + + r = malloc(ld + lh + 1); + if (r == NULL) { + SAFE_FREE(h); + return NULL; + } + + if (lh > 0) { + memcpy(r, h, lh); + } + SAFE_FREE(h); + memcpy(r + lh, p, ld + 1); + + return r; +} + +char *ssh_path_expand_escape(ssh_session session, const char *s) { +#define MAX_BUF_SIZE 4096 + char host[NI_MAXHOST]; + char buf[MAX_BUF_SIZE]; + char *r, *x = NULL; + const char *p; + size_t i, l; + + r = ssh_path_expand_tilde(s); + if (r == NULL) { + ssh_set_error_oom(session); + return NULL; + } + + if (strlen(r) > MAX_BUF_SIZE) { + ssh_set_error(session, SSH_FATAL, "string to expand too long"); + free(r); + return NULL; + } + + p = r; + buf[0] = '\0'; + + for (i = 0; *p != '\0'; p++) { + if (*p != '%') { + buf[i] = *p; + i++; + if (i >= MAX_BUF_SIZE) { + free(r); + return NULL; + } + buf[i] = '\0'; + continue; + } + + p++; + if (*p == '\0') { + break; + } + + switch (*p) { + case 'd': + x = strdup(session->opts.sshdir); + break; + case 'u': + x = ssh_get_local_username(); + break; + case 'l': + if (gethostname(host, sizeof(host) == 0)) { + x = strdup(host); + } + break; + case 'h': + x = strdup(session->opts.host); + break; + case 'r': + x = strdup(session->opts.username); + break; + case 'p': + if (session->opts.port < 65536) { + char tmp[6]; + + snprintf(tmp, sizeof(tmp), "%u", session->opts.port); + x = strdup(tmp); + } + break; + default: + ssh_set_error(session, SSH_FATAL, + "Wrong escape sequence detected"); + free(r); + return NULL; + } + + if (x == NULL) { + ssh_set_error_oom(session); + free(r); + return NULL; + } + + i += strlen(x); + if (i >= MAX_BUF_SIZE) { + ssh_set_error(session, SSH_FATAL, + "String too long"); + free(x); + free(r); + return NULL; + } + l = strlen(buf); + strncpy(buf + l, x, sizeof(buf) - l - 1); + buf[i] = '\0'; + SAFE_FREE(x); + } + + free(r); + return strdup(buf); +#undef MAX_BUF_SIZE +} + +/** + * @internal + * + * @brief Analyze the SSH banner to find out if we have a SSHv1 or SSHv2 + * server. + * + * @param session The session to analyze the banner from. + * @param server 0 means we are a client, 1 a server. + * @param ssh1 The variable which is set if it is a SSHv1 server. + * @param ssh2 The variable which is set if it is a SSHv2 server. + * + * @return 0 on success, < 0 on error. + * + * @see ssh_get_banner() + */ +int ssh_analyze_banner(ssh_session session, int server, int *ssh1, int *ssh2) { + const char *banner; + const char *openssh; + + if (server) { + banner = session->clientbanner; + } else { + banner = session->serverbanner; + } + + if (banner == NULL) { + ssh_set_error(session, SSH_FATAL, "Invalid banner"); + return -1; + } + + /* + * Typical banners e.g. are: + * + * SSH-1.5-openSSH_5.4 + * SSH-1.99-openSSH_3.0 + * + * SSH-2.0-something + * 012345678901234567890 + */ + if (strlen(banner) < 6 || + strncmp(banner, "SSH-", 4) != 0) { + ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner); + return -1; + } + + ssh_log(session, SSH_LOG_RARE, "Analyzing banner: %s", banner); + + switch(banner[4]) { + case '1': + *ssh1 = 1; + if (strlen(banner) > 6) { + if (banner[6] == '9') { + *ssh2 = 1; + } else { + *ssh2 = 0; + } + } + break; + case '2': + *ssh1 = 0; + *ssh2 = 1; + break; + default: + ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner); + return -1; + } + + openssh = strstr(banner, "OpenSSH"); + if (openssh != NULL) { + int major, minor; + + /* + * The banner is typical: + * OpenSSH_5.4 + * 012345678901234567890 + */ + if (strlen(openssh) > 9) { + major = strtol(openssh + 8, (char **) NULL, 10); + minor = strtol(openssh + 10, (char **) NULL, 10); + session->openssh = SSH_VERSION_INT(major, minor, 0); + ssh_log(session, SSH_LOG_RARE, + "We are talking to an OpenSSH client version: %d.%d (%x)", + major, minor, session->openssh); + } + } + + + return 0; +} + +/* try the Monotonic clock if possible for perfs reasons */ +#ifdef _POSIX_MONOTONIC_CLOCK +#define CLOCK CLOCK_MONOTONIC +#else +#define CLOCK CLOCK_REALTIME +#endif + +/** + * @internal + * @brief initializes a timestamp to the current time + * @param[out] ts pointer to an allocated ssh_timestamp structure + */ +void ssh_timestamp_init(struct ssh_timestamp *ts){ +#ifdef HAVE_CLOCK_GETTIME + struct timespec tp; + clock_gettime(CLOCK, &tp); + ts->useconds = tp.tv_nsec / 1000; +#else + struct timeval tp; + gettimeofday(&tp, NULL); + ts->useconds = tp.tv_usec; +#endif + ts->seconds = tp.tv_sec; +} + +#undef CLOCK + +/** + * @internal + * @brief gets the time difference between two timestamps in ms + * @param[in] old older value + * @param[in] new newer value + * @returns difference in milliseconds + */ + +static int ssh_timestamp_difference(struct ssh_timestamp *old, + struct ssh_timestamp *new){ + long seconds, usecs, msecs; + seconds = new->seconds - old->seconds; + usecs = new->useconds - old->useconds; + if (usecs < 0){ + seconds--; + usecs += 1000000; + } + msecs = seconds * 1000 + usecs/1000; + return msecs; +} + +/** + * @internal + * @brief turn seconds and microseconds pair (as provided by user-set options) + * into millisecond value + * @param[in] sec number of seconds + * @param[in] usec number of microseconds + * @returns milliseconds, or 10000 if user supplied values are equal to zero + */ +int ssh_make_milliseconds(long sec, long usec) { + int res = usec ? (usec / 1000) : 0; + res += (sec * 1000); + if (res == 0) { + res = 10 * 1000; /* use a reasonable default value in case + * SSH_OPTIONS_TIMEOUT is not set in options. */ + } + return res; +} + +/** + * @internal + * @brief Checks if a timeout is elapsed, in function of a previous + * timestamp and an assigned timeout + * @param[in] ts pointer to an existing timestamp + * @param[in] timeout timeout in milliseconds. Negative values mean infinite + * timeout + * @returns 1 if timeout is elapsed + * 0 otherwise + */ +int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) { + struct ssh_timestamp now; + + switch(timeout) { + case -2: /* + * -2 means user-defined timeout as available in + * session->timeout, session->timeout_usec. + */ + fprintf(stderr, "ssh_timeout_elapsed called with -2. this needs to " + "be fixed. please set a breakpoint on %s:%d and " + "fix the caller\n", __FILE__, __LINE__); + case -1: /* -1 means infinite timeout */ + return 0; + case 0: /* 0 means no timeout */ + return 1; + default: + break; + } + + ssh_timestamp_init(&now); + + return (ssh_timestamp_difference(ts,&now) >= timeout); +} + +/** + * @brief updates a timeout value so it reflects the remaining time + * @param[in] ts pointer to an existing timestamp + * @param[in] timeout timeout in milliseconds. Negative values mean infinite + * timeout + * @returns remaining time in milliseconds, 0 if elapsed, -1 if never. + */ +int ssh_timeout_update(struct ssh_timestamp *ts, int timeout){ + struct ssh_timestamp now; + int ms, ret; + if (timeout <= 0) { + return timeout; + } + ssh_timestamp_init(&now); + ms = ssh_timestamp_difference(ts,&now); + if(ms < 0) + ms = 0; + ret = timeout - ms; + return ret >= 0 ? ret: 0; +} + + +int ssh_match_group(const char *group, const char *object) +{ + const char *a; + const char *z; + + z = group; + do { + a = strchr(z, ','); + if (a == NULL) { + if (strcmp(z, object) == 0) { + return 1; + } + return 0; + } else { + if (strncmp(z, object, a - z) == 0) { + return 1; + } + } + z = a + 1; + } while(1); + + /* not reached */ + return 0; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/options.c b/libssh/src/options.c new file mode 100644 index 00000000..931cb31e --- /dev/null +++ b/libssh/src/options.c @@ -0,0 +1,1424 @@ +/* + * options.c - handle pre-connection options + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * Copyright (c) 2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include +#include +#include +#ifndef _WIN32 +#include +#else +#include +#endif +#include +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/misc.h" +#include "libssh/options.h" +#ifdef WITH_SERVER +#include "libssh/server.h" +#include "libssh/bind.h" +#endif + +/** + * @addtogroup libssh_session + * @{ + */ + +/** + * @brief Duplicate the options of a session structure. + * + * If you make several sessions with the same options this is useful. You + * cannot use twice the same option structure in ssh_session_connect. + * + * @param src The session to use to copy the options. + * + * @param dest A pointer to store the allocated session with duplicated + * options. You have to free the memory. + * + * @returns 0 on sucess, -1 on error with errno set. + * + * @see ssh_session_connect() + */ +int ssh_options_copy(ssh_session src, ssh_session *dest) { + ssh_session new; + int i; + + if (src == NULL || dest == NULL) { + return -1; + } + + new = ssh_new(); + if (new == NULL) { + return -1; + } + + if (src->opts.username) { + new->opts.username = strdup(src->opts.username); + if (new->opts.username == NULL) { + ssh_free(new); + return -1; + } + } + + if (src->opts.host) { + new->opts.host = strdup(src->opts.host); + if (new->opts.host == NULL) { + ssh_free(new); + return -1; + } + } + + if (src->opts.identity) { + struct ssh_iterator *it; + + new->opts.identity = ssh_list_new(); + if (new->opts.identity == NULL) { + ssh_free(new); + return -1; + } + + it = ssh_list_get_iterator(src->opts.identity); + while (it) { + char *id; + int rc; + + id = strdup((char *) it->data); + if (id == NULL) { + ssh_free(new); + return -1; + } + + rc = ssh_list_append(new->opts.identity, id); + if (rc < 0) { + free(id); + ssh_free(new); + return -1; + } + it = it->next; + } + } + + if (src->opts.sshdir) { + new->opts.sshdir = strdup(src->opts.sshdir); + if (new->opts.sshdir == NULL) { + ssh_free(new); + return -1; + } + } + + if (src->opts.knownhosts) { + new->opts.knownhosts = strdup(src->opts.knownhosts); + if (new->opts.knownhosts == NULL) { + ssh_free(new); + return -1; + } + } + + for (i = 0; i < 10; ++i) { + if (src->opts.wanted_methods[i]) { + new->opts.wanted_methods[i] = strdup(src->opts.wanted_methods[i]); + if (new->opts.wanted_methods[i] == NULL) { + ssh_free(new); + return -1; + } + } + } + + if (src->opts.ProxyCommand) { + new->opts.ProxyCommand = strdup(src->opts.ProxyCommand); + if (new->opts.ProxyCommand == NULL) { + ssh_free(new); + return -1; + } + } + new->opts.fd = src->opts.fd; + new->opts.port = src->opts.port; + new->opts.timeout = src->opts.timeout; + new->opts.timeout_usec = src->opts.timeout_usec; + new->opts.ssh2 = src->opts.ssh2; + new->opts.ssh1 = src->opts.ssh1; + new->opts.compressionlevel = src->opts.compressionlevel; + new->common.log_verbosity = src->common.log_verbosity; + new->common.callbacks = src->common.callbacks; + + *dest = new; + + return 0; +} + +int ssh_options_set_algo(ssh_session session, int algo, + const char *list) { + if (!verify_existing_algo(algo, list)) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Setting method: no algorithm for method \"%s\" (%s)\n", + ssh_kex_get_description(algo), list); + return -1; + } + + SAFE_FREE(session->opts.wanted_methods[algo]); + session->opts.wanted_methods[algo] = strdup(list); + if (session->opts.wanted_methods[algo] == NULL) { + ssh_set_error_oom(session); + return -1; + } + + return 0; +} + +/** + * @brief This function can set all possible ssh options. + * + * @param session An allocated SSH session structure. + * + * @param type The option type to set. This could be one of the + * following: + * + * - SSH_OPTIONS_HOST: + * The hostname or ip address to connect to (const char *). + * + * - SSH_OPTIONS_PORT: + * The port to connect to (unsigned int). + * + * - SSH_OPTIONS_PORT_STR: + * The port to connect to (const char *). + * + * - SSH_OPTIONS_FD: + * The file descriptor to use (socket_t).\n + * \n + * If you wish to open the socket yourself for a reason + * or another, set the file descriptor. Don't forget to + * set the hostname as the hostname is used as a key in + * the known_host mechanism. + * + * - SSH_OPTIONS_BINDADDR: + * The address to bind the client to (const char *). + * + * - SSH_OPTIONS_USER: + * The username for authentication (const char *).\n + * \n + * If the value is NULL, the username is set to the + * default username. + * + * - SSH_OPTIONS_SSH_DIR: + * Set the ssh directory (const char *,format string).\n + * \n + * If the value is NULL, the directory is set to the + * default ssh directory.\n + * \n + * The ssh directory is used for files like known_hosts + * and identity (private and public key). It may include + * "%s" which will be replaced by the user home + * directory. + * + * - SSH_OPTIONS_KNOWNHOSTS: + * Set the known hosts file name (const char *,format string).\n + * \n + * If the value is NULL, the directory is set to the + * default known hosts file, normally + * ~/.ssh/known_hosts.\n + * \n + * The known hosts file is used to certify remote hosts + * are genuine. It may include "%s" which will be + * replaced by the user home directory. + * + * - SSH_OPTIONS_IDENTITY: + * Set the identity file name (const char *,format string).\n + * \n + * By default identity, id_dsa and id_rsa are checked.\n + * \n + * The identity file used authenticate with public key. + * It may include "%s" which will be replaced by the + * user home directory. + * + * - SSH_OPTIONS_TIMEOUT: + * Set a timeout for the connection in seconds (long). + * + * - SSH_OPTIONS_TIMEOUT_USEC: + * Set a timeout for the connection in micro seconds + * (long). + * + * - SSH_OPTIONS_SSH1: + * Allow or deny the connection to SSH1 servers + * (int, 0 is false). + * + * - SSH_OPTIONS_SSH2: + * Allow or deny the connection to SSH2 servers + * (int, 0 is false). + * + * - SSH_OPTIONS_LOG_VERBOSITY: + * Set the session logging verbosity (int).\n + * \n + * The verbosity of the messages. Every log smaller or + * equal to verbosity will be shown. + * - SSH_LOG_NOLOG: No logging + * - SSH_LOG_RARE: Rare conditions or warnings + * - SSH_LOG_ENTRY: API-accessible entrypoints + * - SSH_LOG_PACKET: Packet id and size + * - SSH_LOG_FUNCTIONS: Function entering and leaving + * + * - SSH_OPTIONS_LOG_VERBOSITY_STR: + * Set the session logging verbosity (const char *).\n + * \n + * The verbosity of the messages. Every log smaller or + * equal to verbosity will be shown. + * - SSH_LOG_NOLOG: No logging + * - SSH_LOG_RARE: Rare conditions or warnings + * - SSH_LOG_ENTRY: API-accessible entrypoints + * - SSH_LOG_PACKET: Packet id and size + * - SSH_LOG_FUNCTIONS: Function entering and leaving + * \n + * See the corresponding numbers in libssh.h. + * + * - SSH_OPTIONS_AUTH_CALLBACK: + * Set a callback to use your own authentication function + * (function pointer). + * + * - SSH_OPTIONS_AUTH_USERDATA: + * Set the user data passed to the authentication + * function (generic pointer). + * + * - SSH_OPTIONS_LOG_CALLBACK: + * Set a callback to use your own logging function + * (function pointer). + * + * - SSH_OPTIONS_LOG_USERDATA: + * Set the user data passed to the logging function + * (generic pointer). + * + * - SSH_OPTIONS_STATUS_CALLBACK: + * Set a callback to show connection status in realtime + * (function pointer).\n + * \n + * @code + * fn(void *arg, float status) + * @endcode + * \n + * During ssh_connect(), libssh will call the callback + * with status from 0.0 to 1.0. + * + * - SSH_OPTIONS_STATUS_ARG: + * Set the status argument which should be passed to the + * status callback (generic pointer). + * + * - SSH_OPTIONS_CIPHERS_C_S: + * Set the symmetric cipher client to server (const char *, + * comma-separated list). + * + * - SSH_OPTIONS_CIPHERS_S_C: + * Set the symmetric cipher server to client (const char *, + * comma-separated list). + * + * - SSH_OPTIONS_KEY_EXCHANGE: + * Set the key exchange method to be used (const char *, + * comma-separated list). ex: + * "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" + * + * - SSH_OPTIONS_HOSTKEYS: + * Set the preferred server host key types (const char *, + * comma-separated list). ex: + * "ssh-rsa,ssh-dsa,ecdh-sha2-nistp256" + * + * - SSH_OPTIONS_COMPRESSION_C_S: + * Set the compression to use for client to server + * communication (const char *, "yes", "no" or a specific + * algorithm name if needed ("zlib","zlib@openssh.com","none"). + * + * - SSH_OPTIONS_COMPRESSION_S_C: + * Set the compression to use for server to client + * communication (const char *, "yes", "no" or a specific + * algorithm name if needed ("zlib","zlib@openssh.com","none"). + * + * - SSH_OPTIONS_COMPRESSION: + * Set the compression to use for both directions + * communication (const char *, "yes", "no" or a specific + * algorithm name if needed ("zlib","zlib@openssh.com","none"). + * + * - SSH_OPTIONS_COMPRESSION_LEVEL: + * Set the compression level to use for zlib functions. (int, + * value from 1 to 9, 9 being the most efficient but slower). + * + * - SSH_OPTIONS_STRICTHOSTKEYCHECK: + * Set the parameter StrictHostKeyChecking to avoid + * asking about a fingerprint (int, 0 = false). + * + * - SSH_OPTIONS_PROXYCOMMAND: + * Set the command to be executed in order to connect to + * server (const char *). + * + * @param value The value to set. This is a generic pointer and the + * datatype which is used should be set according to the + * type set. + * + * @return 0 on success, < 0 on error. + */ +int ssh_options_set(ssh_session session, enum ssh_options_e type, + const void *value) { + const char *v; + char *p, *q; + long int i; + int rc; + + if (session == NULL) { + return -1; + } + + switch (type) { + case SSH_OPTIONS_HOST: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + q = strdup(value); + if (q == NULL) { + ssh_set_error_oom(session); + return -1; + } + p = strchr(q, '@'); + + SAFE_FREE(session->opts.host); + + if (p) { + *p = '\0'; + session->opts.host = strdup(p + 1); + if (session->opts.host == NULL) { + SAFE_FREE(q); + ssh_set_error_oom(session); + return -1; + } + + SAFE_FREE(session->opts.username); + session->opts.username = strdup(q); + SAFE_FREE(q); + if (session->opts.username == NULL) { + ssh_set_error_oom(session); + return -1; + } + } else { + session->opts.host = q; + } + } + break; + case SSH_OPTIONS_PORT: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + int *x = (int *) value; + if (*x <= 0) { + ssh_set_error_invalid(session); + return -1; + } + + session->opts.port = *x & 0xffff; + } + break; + case SSH_OPTIONS_PORT_STR: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + q = strdup(v); + if (q == NULL) { + ssh_set_error_oom(session); + return -1; + } + i = strtol(q, &p, 10); + if (q == p) { + SAFE_FREE(q); + } + SAFE_FREE(q); + if (i <= 0) { + ssh_set_error_invalid(session); + return -1; + } + + session->opts.port = i & 0xffff; + } + break; + case SSH_OPTIONS_FD: + if (value == NULL) { + session->opts.fd = SSH_INVALID_SOCKET; + ssh_set_error_invalid(session); + return -1; + } else { + socket_t *x = (socket_t *) value; + if (*x < 0) { + session->opts.fd = SSH_INVALID_SOCKET; + ssh_set_error_invalid(session); + return -1; + } + + session->opts.fd = *x & 0xffff; + } + break; + case SSH_OPTIONS_BINDADDR: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } + + q = strdup(v); + if (q == NULL) { + return -1; + } + SAFE_FREE(session->opts.bindaddr); + session->opts.bindaddr = q; + break; + case SSH_OPTIONS_USER: + v = value; + SAFE_FREE(session->opts.username); + if (v == NULL) { + q = ssh_get_local_username(); + if (q == NULL) { + ssh_set_error_oom(session); + return -1; + } + session->opts.username = q; + } else if (v[0] == '\0') { + ssh_set_error_oom(session); + return -1; + } else { /* username provided */ + session->opts.username = strdup(value); + if (session->opts.username == NULL) { + ssh_set_error_oom(session); + return -1; + } + } + break; + case SSH_OPTIONS_SSH_DIR: + v = value; + SAFE_FREE(session->opts.sshdir); + if (v == NULL) { + session->opts.sshdir = ssh_path_expand_tilde("~/.ssh"); + if (session->opts.sshdir == NULL) { + return -1; + } + } else if (v[0] == '\0') { + ssh_set_error_oom(session); + return -1; + } else { + session->opts.sshdir = ssh_path_expand_tilde(v); + if (session->opts.sshdir == NULL) { + ssh_set_error_oom(session); + return -1; + } + } + break; + case SSH_OPTIONS_IDENTITY: + case SSH_OPTIONS_ADD_IDENTITY: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } + q = strdup(v); + if (q == NULL) { + return -1; + } + rc = ssh_list_prepend(session->opts.identity, q); + if (rc < 0) { + free(q); + return -1; + } + break; + case SSH_OPTIONS_KNOWNHOSTS: + v = value; + SAFE_FREE(session->opts.knownhosts); + if (v == NULL) { + session->opts.knownhosts = ssh_path_expand_escape(session, + "%d/known_hosts"); + if (session->opts.knownhosts == NULL) { + ssh_set_error_oom(session); + return -1; + } + } else if (v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + session->opts.knownhosts = strdup(v); + if (session->opts.knownhosts == NULL) { + ssh_set_error_oom(session); + return -1; + } + } + break; + case SSH_OPTIONS_TIMEOUT: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + long *x = (long *) value; + if (*x < 0) { + ssh_set_error_invalid(session); + return -1; + } + + session->opts.timeout = *x & 0xffffffff; + } + break; + case SSH_OPTIONS_TIMEOUT_USEC: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + long *x = (long *) value; + if (*x < 0) { + ssh_set_error_invalid(session); + return -1; + } + + session->opts.timeout_usec = *x & 0xffffffff; + } + break; + case SSH_OPTIONS_SSH1: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + int *x = (int *) value; + if (*x < 0) { + ssh_set_error_invalid(session); + return -1; + } + + session->opts.ssh1 = *x; + } + break; + case SSH_OPTIONS_SSH2: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + int *x = (int *) value; + if (*x < 0) { + ssh_set_error_invalid(session); + return -1; + } + + session->opts.ssh2 = *x & 0xffff; + } + break; + case SSH_OPTIONS_LOG_VERBOSITY: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + int *x = (int *) value; + if (*x < 0) { + ssh_set_error_invalid(session); + return -1; + } + + session->common.log_verbosity = *x & 0xffff; + } + break; + case SSH_OPTIONS_LOG_VERBOSITY_STR: + v = value; + if (v == NULL || v[0] == '\0') { + session->common.log_verbosity = 0; + ssh_set_error_invalid(session); + return -1; + } else { + q = strdup(v); + if (q == NULL) { + ssh_set_error_oom(session); + return -1; + } + i = strtol(q, &p, 10); + if (q == p) { + SAFE_FREE(q); + } + SAFE_FREE(q); + if (i < 0) { + ssh_set_error_invalid(session); + return -1; + } + + session->common.log_verbosity = i & 0xffff; + } + break; + case SSH_OPTIONS_CIPHERS_C_S: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + if (ssh_options_set_algo(session, SSH_CRYPT_C_S, v) < 0) + return -1; + } + break; + case SSH_OPTIONS_CIPHERS_S_C: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + if (ssh_options_set_algo(session, SSH_CRYPT_S_C, v) < 0) + return -1; + } + break; + case SSH_OPTIONS_KEY_EXCHANGE: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + if (ssh_options_set_algo(session, SSH_KEX, v) < 0) + return -1; + } + break; + case SSH_OPTIONS_HOSTKEYS: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + if (ssh_options_set_algo(session, SSH_HOSTKEYS, v) < 0) + return -1; + } + break; + case SSH_OPTIONS_COMPRESSION_C_S: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + if (strcasecmp(value,"yes")==0){ + if(ssh_options_set_algo(session,SSH_COMP_C_S,"zlib@openssh.com,zlib") < 0) + return -1; + } else if (strcasecmp(value,"no")==0){ + if(ssh_options_set_algo(session,SSH_COMP_C_S,"none") < 0) + return -1; + } else { + if (ssh_options_set_algo(session, SSH_COMP_C_S, v) < 0) + return -1; + } + } + break; + case SSH_OPTIONS_COMPRESSION_S_C: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + if (strcasecmp(value,"yes")==0){ + if(ssh_options_set_algo(session,SSH_COMP_S_C,"zlib@openssh.com,zlib") < 0) + return -1; + } else if (strcasecmp(value,"no")==0){ + if(ssh_options_set_algo(session,SSH_COMP_S_C,"none") < 0) + return -1; + } else { + if (ssh_options_set_algo(session, SSH_COMP_S_C, v) < 0) + return -1; + } + } + break; + case SSH_OPTIONS_COMPRESSION: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } + if(ssh_options_set(session,SSH_OPTIONS_COMPRESSION_C_S, v) < 0) + return -1; + if(ssh_options_set(session,SSH_OPTIONS_COMPRESSION_S_C, v) < 0) + return -1; + break; + case SSH_OPTIONS_COMPRESSION_LEVEL: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + int *x = (int *)value; + if (*x < 1 || *x > 9) { + ssh_set_error_invalid(session); + return -1; + } + session->opts.compressionlevel = *x & 0xff; + } + break; + case SSH_OPTIONS_STRICTHOSTKEYCHECK: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + int *x = (int *) value; + + session->opts.StrictHostKeyChecking = (*x & 0xff) > 0 ? 1 : 0; + } + session->opts.StrictHostKeyChecking = *(int*)value; + break; + case SSH_OPTIONS_PROXYCOMMAND: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + SAFE_FREE(session->opts.ProxyCommand); + q = strdup(v); + if (q == NULL) { + return -1; + } + session->opts.ProxyCommand = q; + } + break; + default: + ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); + return -1; + break; + } + + return 0; +} + +/** + * @brief This function can get ssh the ssh port. It must only be used on + * a valid ssh session. This function is useful when the session + * options have been automatically inferred from the environment + * or configuration files and one + * + * @param session An allocated SSH session structure. + * + * @param port_target An unsigned integer into which the + * port will be set from the ssh session. + * + * @return 0 on success, < 0 on error. + * + */ +int ssh_options_get_port(ssh_session session, unsigned int* port_target) { + if (session == NULL) { + return -1; + } + if (!session->opts.port) { + ssh_set_error_invalid(session); + return -1; + } + *port_target = session->opts.port; + return 0; +} + +/** + * @brief This function can get ssh options, it does not support all options provided for + * ssh options set, but mostly those which a user-space program may care about having + * trusted the ssh driver to infer these values from underlaying configuration files. + * It operates only on those SSH_OPTIONS_* which return char*. If you wish to receive + * the port then please use ssh_options_get_port() which returns an unsigned int. + * + * @param session An allocated SSH session structure. + * + * @param type The option type to get. This could be one of the + * following: + * + * - SSH_OPTIONS_HOST: + * The hostname or ip address to connect to (const char *). + * + * - SSH_OPTIONS_USER: + * The username for authentication (const char *).\n + * \n when not explicitly set this will be inferred from the + * ~/.ssh/config file. + * + * - SSH_OPTIONS_IDENTITY: + * Set the identity file name (const char *,format string).\n + * \n + * By default identity, id_dsa and id_rsa are checked.\n + * \n + * The identity file used authenticate with public key. + * It may include "%s" which will be replaced by the + * user home directory. + * + * @param value The value to get into. As a char**, space will be + * allocated by the function for the value, it is + * your responsibility to free the memory using + * ssh_string_free_char(). + * + * @return SSH_OK on success, SSH_ERROR on error. + */ +int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value) +{ + char* src = NULL; + + if (session == NULL) { + return SSH_ERROR; + } + + if (value == NULL) { + ssh_set_error_invalid(session); + return SSH_ERROR; + } + + switch(type) + { + case SSH_OPTIONS_HOST: { + src = session->opts.host; + break; + } + case SSH_OPTIONS_USER: { + src = session->opts.username; + break; + } + case SSH_OPTIONS_IDENTITY: { + struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity); + if (it == NULL) { + return SSH_ERROR; + } + src = ssh_iterator_value(char *, it); + break; + } + default: + ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); + return SSH_ERROR; + break; + } + if (src == NULL) { + return SSH_ERROR; + } + *value = strdup(src); + if (*value == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + return SSH_OK; +} + +/** + * @brief Parse command line arguments. + * + * This is a helper for your application to generate the appropriate + * options from the command line arguments.\n + * The argv array and argc value are changed so that the parsed + * arguments wont appear anymore in them.\n + * The single arguments (without switches) are not parsed. thus, + * myssh -l user localhost\n + * The command wont set the hostname value of options to localhost. + * + * @param session The session to configure. + * + * @param argcptr The pointer to the argument count. + * + * @param argv The arguments list pointer. + * + * @returns 0 on success, < 0 on error. + * + * @see ssh_session_new() + */ +int ssh_options_getopt(ssh_session session, int *argcptr, char **argv) { + char *user = NULL; + char *cipher = NULL; + char *identity = NULL; + char *port = NULL; + char **save = NULL, **tmp; + int i = 0; + int argc = *argcptr; + int debuglevel = 0; + int usersa = 0; + int usedss = 0; + int compress = 0; + int cont = 1; + int current = 0; +#ifdef WITH_SSH1 + int ssh1 = 1; +#else + int ssh1 = 0; +#endif + int ssh2 = 1; +#ifdef _MSC_VER + /* Not supported with a Microsoft compiler */ + return -1; +#else + int saveoptind = optind; /* need to save 'em */ + int saveopterr = opterr; + + opterr = 0; /* shut up getopt */ + while(cont && ((i = getopt(argc, argv, "c:i:Cl:p:vb:rd12")) != -1)) { + switch(i) { + case 'l': + user = optarg; + break; + case 'p': + port = optarg; + break; + case 'v': + debuglevel++; + break; + case 'r': + usersa++; + break; + case 'd': + usedss++; + break; + case 'c': + cipher = optarg; + break; + case 'i': + identity = optarg; + break; + case 'C': + compress++; + break; + case '2': + ssh2 = 1; + ssh1 = 0; + break; + case '1': + ssh2 = 0; + ssh1 = 1; + break; + default: + { + char opt[3]="- "; + opt[1] = optopt; + tmp = realloc(save, (current + 1) * sizeof(char*)); + if (tmp == NULL) { + SAFE_FREE(save); + ssh_set_error_oom(session); + return -1; + } + save = tmp; + save[current] = strdup(opt); + if (save[current] == NULL) { + SAFE_FREE(save); + ssh_set_error_oom(session); + return -1; + } + current++; + if (optarg) { + save[current++] = argv[optind + 1]; + } + } + } /* switch */ + } /* while */ + opterr = saveopterr; + while (optind < argc) { + tmp = realloc(save, (current + 1) * sizeof(char*)); + if (tmp == NULL) { + SAFE_FREE(save); + ssh_set_error_oom(session); + return -1; + } + save = tmp; + save[current] = argv[optind]; + current++; + optind++; + } + + if (usersa && usedss) { + ssh_set_error(session, SSH_FATAL, "Either RSA or DSS must be chosen"); + cont = 0; + } + + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &debuglevel); + + optind = saveoptind; + + if(!cont) { + SAFE_FREE(save); + return -1; + } + + /* first recopy the save vector into the original's */ + for (i = 0; i < current; i++) { + /* don't erase argv[0] */ + argv[ i + 1] = save[i]; + } + argv[current + 1] = NULL; + *argcptr = current + 1; + SAFE_FREE(save); + + /* set a new option struct */ + if (compress) { + if (ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes") < 0) { + cont = 0; + } + } + + if (cont && cipher) { + if (ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher) < 0) { + cont = 0; + } + if (cont && ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher) < 0) { + cont = 0; + } + } + + if (cont && user) { + if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) { + cont = 0; + } + } + + if (cont && identity) { + if (ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity) < 0) { + cont = 0; + } + } + + ssh_options_set(session, SSH_OPTIONS_PORT_STR, port); + + ssh_options_set(session, SSH_OPTIONS_SSH1, &ssh1); + ssh_options_set(session, SSH_OPTIONS_SSH2, &ssh2); + + if (!cont) { + return SSH_ERROR; + } + + return SSH_OK; +#endif +} + +/** + * @brief Parse the ssh config file. + * + * This should be the last call of all options, it may overwrite options which + * are already set. It requires that the host name is already set with + * ssh_options_set_host(). + * + * @param session SSH session handle + * + * @param filename The options file to use, if NULL the default + * ~/.ssh/config will be used. + * + * @return 0 on success, < 0 on error. + * + * @see ssh_options_set_host() + */ +int ssh_options_parse_config(ssh_session session, const char *filename) { + char *expanded_filename; + int r; + + if (session == NULL) { + return -1; + } + if (session->opts.host == NULL) { + ssh_set_error_invalid(session); + return -1; + } + + if (session->opts.sshdir == NULL) { + r = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL); + if (r < 0) { + ssh_set_error_oom(session); + return -1; + } + } + + /* set default filename */ + if (filename == NULL) { + expanded_filename = ssh_path_expand_escape(session, "%d/config"); + } else { + expanded_filename = ssh_path_expand_escape(session, filename); + } + if (expanded_filename == NULL) { + return -1; + } + + r = ssh_config_parse_file(session, expanded_filename); + if (r < 0) { + goto out; + } + if (filename == NULL) { + r = ssh_config_parse_file(session, "/etc/ssh/ssh_config"); + } + +out: + free(expanded_filename); + return r; +} + +int ssh_options_apply(ssh_session session) { + struct ssh_iterator *it; + char *tmp; + int rc; + + if (session->opts.sshdir == NULL) { + rc = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL); + if (rc < 0) { + return -1; + } + } + + if (session->opts.username == NULL) { + rc = ssh_options_set(session, SSH_OPTIONS_USER, NULL); + if (rc < 0) { + return -1; + } + } + + if (session->opts.knownhosts == NULL) { + tmp = ssh_path_expand_escape(session, "%d/known_hosts"); + } else { + tmp = ssh_path_expand_escape(session, session->opts.knownhosts); + } + if (tmp == NULL) { + return -1; + } + free(session->opts.knownhosts); + session->opts.knownhosts = tmp; + + if (session->opts.ProxyCommand != NULL) { + tmp = ssh_path_expand_escape(session, session->opts.ProxyCommand); + if (tmp == NULL) { + return -1; + } + free(session->opts.ProxyCommand); + session->opts.ProxyCommand = tmp; + } + + for (it = ssh_list_get_iterator(session->opts.identity); + it != NULL; + it = it->next) { + char *id = (char *) it->data; + tmp = ssh_path_expand_escape(session, id); + if (tmp == NULL) { + return -1; + } + free(id); + it->data = tmp; + } + + return 0; +} + +/** @} */ + +#ifdef WITH_SERVER +/** + * @addtogroup libssh_server + * @{ + */ +static int ssh_bind_options_set_algo(ssh_bind sshbind, int algo, + const char *list) { + if (!verify_existing_algo(algo, list)) { + ssh_set_error(sshbind, SSH_REQUEST_DENIED, + "Setting method: no algorithm for method \"%s\" (%s)\n", + ssh_kex_get_description(algo), list); + return -1; + } + + SAFE_FREE(sshbind->wanted_methods[algo]); + sshbind->wanted_methods[algo] = strdup(list); + if (sshbind->wanted_methods[algo] == NULL) { + ssh_set_error_oom(sshbind); + return -1; + } + + return 0; +} + +/** + * @brief This function can set all possible ssh bind options. + * + * @param session An allocated ssh option structure. + * + * @param type The option type to set. This could be one of the + * following: + * + * SSH_BIND_OPTIONS_LOG_VERBOSITY: + * Set the session logging verbosity (integer). + * + * The verbosity of the messages. Every log smaller or + * equal to verbosity will be shown. + * SSH_LOG_NOLOG: No logging + * SSH_LOG_RARE: Rare conditions or warnings + * SSH_LOG_ENTRY: API-accessible entrypoints + * SSH_LOG_PACKET: Packet id and size + * SSH_LOG_FUNCTIONS: Function entering and leaving + * + * SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: + * Set the session logging verbosity (integer). + * + * The verbosity of the messages. Every log smaller or + * equal to verbosity will be shown. + * SSH_LOG_NOLOG: No logging + * SSH_LOG_RARE: Rare conditions or warnings + * SSH_LOG_ENTRY: API-accessible entrypoints + * SSH_LOG_PACKET: Packet id and size + * SSH_LOG_FUNCTIONS: Function entering and leaving + * + * SSH_BIND_OPTIONS_BINDADDR: + * Set the bind address. + * + * SSH_BIND_OPTIONS_BINDPORT: + * Set the bind port, default is 22. + * + * SSH_BIND_OPTIONS_HOSTKEY: + * Set the server public key type: ssh-rsa or ssh-dss + * (string). + * + * SSH_BIND_OPTIONS_DSAKEY: + * Set the path to the dsa ssh host key (string). + * + * SSH_BIND_OPTIONS_RSAKEY: + * Set the path to the ssh host rsa key (string). + * + * SSH_BIND_OPTIONS_BANNER: + * Set the server banner sent to clients (string). + * + * @param value The value to set. This is a generic pointer and the + * datatype which is used should be set according to the + * type set. + * + * @return 0 on success, < 0 on error. + */ +int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, + const void *value) { + char *p, *q; + int i; + + if (sshbind == NULL) { + return -1; + } + + switch (type) { + case SSH_BIND_OPTIONS_HOSTKEY: + if (value == NULL) { + ssh_set_error_invalid(sshbind); + return -1; + } else { + if (ssh_bind_options_set_algo(sshbind, SSH_HOSTKEYS, value) < 0) + return -1; + } + break; + case SSH_BIND_OPTIONS_BINDADDR: + if (value == NULL) { + ssh_set_error_invalid(sshbind); + return -1; + } else { + SAFE_FREE(sshbind->bindaddr); + sshbind->bindaddr = strdup(value); + if (sshbind->bindaddr == NULL) { + ssh_set_error_oom(sshbind); + return -1; + } + } + break; + case SSH_BIND_OPTIONS_BINDPORT: + if (value == NULL) { + ssh_set_error_invalid(sshbind); + return -1; + } else { + int *x = (int *) value; + sshbind->bindport = *x & 0xffff; + } + break; + case SSH_BIND_OPTIONS_BINDPORT_STR: + if (value == NULL) { + sshbind->bindport = 22 & 0xffff; + } else { + q = strdup(value); + if (q == NULL) { + ssh_set_error_oom(sshbind); + return -1; + } + i = strtol(q, &p, 10); + if (q == p) { + SAFE_FREE(q); + } + SAFE_FREE(q); + + sshbind->bindport = i & 0xffff; + } + break; + case SSH_BIND_OPTIONS_LOG_VERBOSITY: + if (value == NULL) { + ssh_set_error_invalid(sshbind); + return -1; + } else { + int *x = (int *) value; + sshbind->common.log_verbosity = *x & 0xffff; + } + break; + case SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: + if (value == NULL) { + sshbind->common.log_verbosity = 0; + } else { + q = strdup(value); + if (q == NULL) { + ssh_set_error_oom(sshbind); + return -1; + } + i = strtol(q, &p, 10); + if (q == p) { + SAFE_FREE(q); + } + SAFE_FREE(q); + + sshbind->common.log_verbosity = i & 0xffff; + } + break; + case SSH_BIND_OPTIONS_DSAKEY: + if (value == NULL) { + ssh_set_error_invalid(sshbind); + return -1; + } else { + SAFE_FREE(sshbind->dsakey); + sshbind->dsakey = strdup(value); + if (sshbind->dsakey == NULL) { + ssh_set_error_oom(sshbind); + return -1; + } + } + break; + case SSH_BIND_OPTIONS_RSAKEY: + if (value == NULL) { + ssh_set_error_invalid(sshbind); + return -1; + } else { + SAFE_FREE(sshbind->rsakey); + sshbind->rsakey = strdup(value); + if (sshbind->rsakey == NULL) { + ssh_set_error_oom(sshbind); + return -1; + } + } + break; + case SSH_BIND_OPTIONS_BANNER: + if (value == NULL) { + ssh_set_error_invalid(sshbind); + return -1; + } else { + SAFE_FREE(sshbind->banner); + sshbind->banner = strdup(value); + if (sshbind->banner == NULL) { + ssh_set_error_oom(sshbind); + return -1; + } + } + break; + default: + ssh_set_error(sshbind, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); + return -1; + break; + } + + return 0; +} +#endif + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/packet.c b/libssh/src/packet.c new file mode 100644 index 00000000..2256f113 --- /dev/null +++ b/libssh/src/packet.c @@ -0,0 +1,531 @@ +/* + * packet.c - packet building functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/ssh2.h" +#include "libssh/crypto.h" +#include "libssh/buffer.h" +#include "libssh/packet.h" +#include "libssh/socket.h" +#include "libssh/channels.h" +#include "libssh/misc.h" +#include "libssh/session.h" +#include "libssh/messages.h" +#include "libssh/pcap.h" +#include "libssh/kex.h" +#include "libssh/auth.h" + +#define MACSIZE SHA_DIGEST_LEN + +static ssh_packet_callback default_packet_handlers[]= { + ssh_packet_disconnect_callback, // SSH2_MSG_DISCONNECT 1 + ssh_packet_ignore_callback, // SSH2_MSG_IGNORE 2 + ssh_packet_unimplemented, // SSH2_MSG_UNIMPLEMENTED 3 + ssh_packet_ignore_callback, // SSH2_MSG_DEBUG 4 + ssh_packet_service_request, // SSH2_MSG_SERVICE_REQUEST 5 + ssh_packet_service_accept, // SSH2_MSG_SERVICE_ACCEPT 6 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, // 7-19 + ssh_packet_kexinit, // SSH2_MSG_KEXINIT 20 + ssh_packet_newkeys, // SSH2_MSG_NEWKEYS 21 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, // 22-29 +#if WITH_SERVER + ssh_packet_kexdh_init, // SSH2_MSG_KEXDH_INIT 30 + // SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 +#else + NULL, +#endif + ssh_packet_dh_reply, // SSH2_MSG_KEXDH_REPLY 31 + // SSH2_MSG_KEX_DH_GEX_GROUP 31 + NULL, // SSH2_MSG_KEX_DH_GEX_INIT 32 + NULL, // SSH2_MSG_KEX_DH_GEX_REPLY 33 + NULL, // SSH2_MSG_KEX_DH_GEX_REQUEST 34 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, // 35-49 + ssh_packet_userauth_request, // SSH2_MSG_USERAUTH_REQUEST 50 + ssh_packet_userauth_failure, // SSH2_MSG_USERAUTH_FAILURE 51 + ssh_packet_userauth_success, // SSH2_MSG_USERAUTH_SUCCESS 52 + ssh_packet_userauth_banner, // SSH2_MSG_USERAUTH_BANNER 53 + NULL,NULL,NULL,NULL,NULL,NULL, // 54-59 + ssh_packet_userauth_pk_ok, // SSH2_MSG_USERAUTH_PK_OK 60 + // SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 + // SSH2_MSG_USERAUTH_INFO_REQUEST 60 + ssh_packet_userauth_info_response, // SSH2_MSG_USERAUTH_INFO_RESPONSE 61 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, // 62-79 +#ifdef WITH_SERVER + ssh_packet_global_request, // SSH2_MSG_GLOBAL_REQUEST 80 +#else /* WITH_SERVER */ + NULL, +#endif /* WITH_SERVER */ + ssh_request_success, // SSH2_MSG_REQUEST_SUCCESS 81 + ssh_request_denied, // SSH2_MSG_REQUEST_FAILURE 82 + NULL, NULL, NULL, NULL, NULL, NULL, NULL,// 83-89 + ssh_packet_channel_open, // SSH2_MSG_CHANNEL_OPEN 90 + ssh_packet_channel_open_conf, // SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 + ssh_packet_channel_open_fail, // SSH2_MSG_CHANNEL_OPEN_FAILURE 92 + channel_rcv_change_window, // SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 + channel_rcv_data, // SSH2_MSG_CHANNEL_DATA 94 + channel_rcv_data, // SSH2_MSG_CHANNEL_EXTENDED_DATA 95 + channel_rcv_eof, // SSH2_MSG_CHANNEL_EOF 96 + channel_rcv_close, // SSH2_MSG_CHANNEL_CLOSE 97 + channel_rcv_request, // SSH2_MSG_CHANNEL_REQUEST 98 + ssh_packet_channel_success, // SSH2_MSG_CHANNEL_SUCCESS 99 + ssh_packet_channel_failure, // SSH2_MSG_CHANNEL_FAILURE 100 +}; + +/* in nonblocking mode, socket_read will read as much as it can, and return */ +/* SSH_OK if it has read at least len bytes, otherwise, SSH_AGAIN. */ +/* in blocking mode, it will read at least len bytes and will block until it's ok. */ + +/** @internal + * @handles a data received event. It then calls the handlers for the different packet types + * or and exception handler callback. + * @param user pointer to current ssh_session + * @param data pointer to the data received + * @len length of data received. It might not be enough for a complete packet + * @returns number of bytes read and processed. + */ +int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user){ + ssh_session session=(ssh_session) user; + unsigned int blocksize = (session->current_crypto ? + session->current_crypto->in_cipher->blocksize : 8); + int current_macsize = session->current_crypto ? MACSIZE : 0; + unsigned char mac[30] = {0}; + char buffer[16] = {0}; + const void *packet = NULL; + int to_be_read; + int rc; + uint32_t len, compsize, payloadsize; + uint8_t padding; + size_t processed=0; /* number of byte processed from the callback */ + + if (data == NULL) { + goto error; + } + + enter_function(); + if (session->session_state == SSH_SESSION_STATE_ERROR) + goto error; + switch(session->packet_state) { + case PACKET_STATE_INIT: + if(receivedlen < blocksize){ + /* We didn't receive enough data to read at least one block size, give up */ + leave_function(); + return 0; + } + memset(&session->in_packet, 0, sizeof(PACKET)); + + if (session->in_buffer) { + if (buffer_reinit(session->in_buffer) < 0) { + goto error; + } + } else { + session->in_buffer = ssh_buffer_new(); + if (session->in_buffer == NULL) { + goto error; + } + } + + memcpy(buffer,data,blocksize); + processed += blocksize; + len = packet_decrypt_len(session, buffer); + + if (buffer_add_data(session->in_buffer, buffer, blocksize) < 0) { + goto error; + } + + if(len > MAX_PACKET_LEN) { + ssh_set_error(session, SSH_FATAL, + "read_packet(): Packet len too high(%u %.4x)", len, len); + goto error; + } + + to_be_read = len - blocksize + sizeof(uint32_t); + if (to_be_read < 0) { + /* remote sshd sends invalid sizes? */ + ssh_set_error(session, SSH_FATAL, + "given numbers of bytes left to be read < 0 (%d)!", to_be_read); + goto error; + } + + /* saves the status of the current operations */ + session->in_packet.len = len; + session->packet_state = PACKET_STATE_SIZEREAD; + case PACKET_STATE_SIZEREAD: + len = session->in_packet.len; + to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize; + /* if to_be_read is zero, the whole packet was blocksize bytes. */ + if (to_be_read != 0) { + if(receivedlen - processed < (unsigned int)to_be_read){ + /* give up, not enough data in buffer */ + ssh_log(session,SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len); + return processed; + } + + packet = ((unsigned char *)data) + processed; +// ssh_socket_read(session->socket,packet,to_be_read-current_macsize); + + if (buffer_add_data(session->in_buffer, packet, + to_be_read - current_macsize) < 0) { + goto error; + } + processed += to_be_read - current_macsize; + } + + if (session->current_crypto) { + /* + * decrypt the rest of the packet (blocksize bytes already + * have been decrypted) + */ + if (packet_decrypt(session, + ((uint8_t*)buffer_get_rest(session->in_buffer) + blocksize), + buffer_get_rest_len(session->in_buffer) - blocksize) < 0) { + ssh_set_error(session, SSH_FATAL, "Decrypt error"); + goto error; + } + /* copy the last part from the incoming buffer */ + memcpy(mac,(unsigned char *)packet + to_be_read - current_macsize, MACSIZE); + + if (packet_hmac_verify(session, session->in_buffer, mac) < 0) { + ssh_set_error(session, SSH_FATAL, "HMAC error"); + goto error; + } + processed += current_macsize; + } + + /* skip the size field which has been processed before */ + buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); + + if (buffer_get_u8(session->in_buffer, &padding) == 0) { + ssh_set_error(session, SSH_FATAL, "Packet too short to read padding"); + goto error; + } + + if (padding > buffer_get_rest_len(session->in_buffer)) { + ssh_set_error(session, SSH_FATAL, + "Invalid padding: %d (%d left)", + padding, + buffer_get_rest_len(session->in_buffer)); + goto error; + } + buffer_pass_bytes_end(session->in_buffer, padding); + compsize = buffer_get_rest_len(session->in_buffer); + +#ifdef WITH_ZLIB + if (session->current_crypto + && session->current_crypto->do_compress_in + && buffer_get_rest_len(session->in_buffer)) { + if (decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN) < 0) { + goto error; + } + } +#endif /* WITH_ZLIB */ + payloadsize=buffer_get_rest_len(session->in_buffer); + session->recv_seq++; + /* We don't want to rewrite a new packet while still executing the packet callbacks */ + session->packet_state = PACKET_STATE_PROCESSING; + ssh_packet_parse_type(session); + ssh_log(session,SSH_LOG_PACKET, + "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", + session->in_packet.type, len, padding, compsize, payloadsize); + /* execute callbacks */ + ssh_packet_process(session, session->in_packet.type); + session->packet_state = PACKET_STATE_INIT; + if(processed < receivedlen){ + /* Handle a potential packet left in socket buffer */ + ssh_log(session,SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", + receivedlen-processed); + rc = ssh_packet_socket_callback(((unsigned char *)data) + processed, + receivedlen - processed,user); + processed += rc; + } + leave_function(); + return processed; + case PACKET_STATE_PROCESSING: + ssh_log(session, SSH_LOG_RARE, "Nested packet processing. Delaying."); + return 0; + } + + ssh_set_error(session, SSH_FATAL, + "Invalid state into packet_read2(): %d", + session->packet_state); + +error: + session->session_state= SSH_SESSION_STATE_ERROR; + leave_function(); + return processed; +} + +void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){ + session->socket_callbacks.data=ssh_packet_socket_callback; + session->socket_callbacks.connected=NULL; + session->socket_callbacks.controlflow=NULL; + session->socket_callbacks.exception=NULL; + session->socket_callbacks.userdata=session; + ssh_socket_set_callbacks(s,&session->socket_callbacks); +} + +/** @internal + * @brief sets the callbacks for the packet layer + */ +void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks){ + if(session->packet_callbacks == NULL){ + session->packet_callbacks = ssh_list_new(); + } + ssh_list_append(session->packet_callbacks, callbacks); +} + +/** @internal + * @brief sets the default packet handlers + */ +void ssh_packet_set_default_callbacks(ssh_session session){ +#ifdef WITH_SSH1 + if(session->version==1){ + ssh_packet_set_default_callbacks1(session); + return; + } +#endif + session->default_packet_callbacks.start=1; + session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers)/sizeof(ssh_packet_callback); + session->default_packet_callbacks.user=session; + session->default_packet_callbacks.callbacks=default_packet_handlers; + ssh_packet_set_callbacks(session, &session->default_packet_callbacks); +} + +/** @internal + * @brief dispatch the call of packet handlers callbacks for a received packet + * @param type type of packet + */ +void ssh_packet_process(ssh_session session, uint8_t type){ + struct ssh_iterator *i; + int r=SSH_PACKET_NOT_USED; + ssh_packet_callbacks cb; + enter_function(); + ssh_log(session,SSH_LOG_PACKET, "Dispatching handler for packet type %d",type); + if(session->packet_callbacks == NULL){ + ssh_log(session,SSH_LOG_RARE,"Packet callback is not initialized !"); + goto error; + } + i=ssh_list_get_iterator(session->packet_callbacks); + while(i != NULL){ + cb=ssh_iterator_value(ssh_packet_callbacks,i); + i=i->next; + if(!cb) + continue; + if(cb->start > type) + continue; + if(cb->start + cb->n_callbacks <= type) + continue; + if(cb->callbacks[type - cb->start]==NULL) + continue; + r=cb->callbacks[type - cb->start](session,type,session->in_buffer,cb->user); + if(r==SSH_PACKET_USED) + break; + } + if(r==SSH_PACKET_NOT_USED){ + ssh_log(session,SSH_LOG_RARE,"Couldn't do anything with packet type %d",type); + ssh_packet_send_unimplemented(session, session->recv_seq-1); + } +error: + leave_function(); +} + +/** @internal + * @brief sends a SSH_MSG_UNIMPLEMENTED answer to an unhandled packet + * @param session the SSH session + * @param seqnum the sequence number of the unknown packet + * @return SSH_ERROR on error, else SSH_OK + */ +int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum){ + int r; + enter_function(); + r = buffer_add_u8(session->out_buffer, SSH2_MSG_UNIMPLEMENTED); + if (r < 0) { + return SSH_ERROR; + } + r = buffer_add_u32(session->out_buffer, htonl(seqnum)); + if (r < 0) { + return SSH_ERROR; + } + r = packet_send(session); + leave_function(); + return r; +} + +/** @internal + * @brief handles a SSH_MSG_UNIMPLEMENTED packet + */ +SSH_PACKET_CALLBACK(ssh_packet_unimplemented){ + uint32_t seq; + (void)type; + (void)user; + buffer_get_u32(packet,&seq); + seq=ntohl(seq); + ssh_log(session,SSH_LOG_RARE, + "Received SSH_MSG_UNIMPLEMENTED (sequence number %d)",seq); + return SSH_PACKET_USED; +} + +/** @internal + * @parse the "Type" header field of a packet and updates the session + */ +int ssh_packet_parse_type(ssh_session session) { + enter_function(); + + memset(&session->in_packet, 0, sizeof(PACKET)); + if(session->in_buffer == NULL) { + leave_function(); + return SSH_ERROR; + } + + if(buffer_get_u8(session->in_buffer, &session->in_packet.type) == 0) { + ssh_set_error(session, SSH_FATAL, "Packet too short to read type"); + leave_function(); + return SSH_ERROR; + } + + session->in_packet.valid = 1; + + leave_function(); + return SSH_OK; +} + +/* + * This function places the outgoing packet buffer into an outgoing + * socket buffer + */ +static int ssh_packet_write(ssh_session session) { + int rc = SSH_ERROR; + + enter_function(); + + rc=ssh_socket_write(session->socket, + buffer_get_rest(session->out_buffer), + buffer_get_rest_len(session->out_buffer)); + leave_function(); + return rc; +} + +static int packet_send2(ssh_session session) { + unsigned int blocksize = (session->current_crypto ? + session->current_crypto->out_cipher->blocksize : 8); + uint32_t currentlen = buffer_get_rest_len(session->out_buffer); + unsigned char *hmac = NULL; + char padstring[32] = {0}; + int rc = SSH_ERROR; + uint32_t finallen,payloadsize,compsize; + uint8_t padding; + + enter_function(); + + payloadsize = currentlen; +#ifdef WITH_ZLIB + if (session->current_crypto + && session->current_crypto->do_compress_out + && buffer_get_rest_len(session->out_buffer)) { + if (compress_buffer(session,session->out_buffer) < 0) { + goto error; + } + currentlen = buffer_get_rest_len(session->out_buffer); + } +#endif /* WITH_ZLIB */ + compsize = currentlen; + padding = (blocksize - ((currentlen +5) % blocksize)); + if(padding < 4) { + padding += blocksize; + } + + if (session->current_crypto) { + ssh_get_random(padstring, padding, 0); + } else { + memset(padstring,0,padding); + } + + finallen = htonl(currentlen + padding + 1); + + if (buffer_prepend_data(session->out_buffer, &padding, sizeof(uint8_t)) < 0) { + goto error; + } + if (buffer_prepend_data(session->out_buffer, &finallen, sizeof(uint32_t)) < 0) { + goto error; + } + if (buffer_add_data(session->out_buffer, padstring, padding) < 0) { + goto error; + } +#ifdef WITH_PCAP + if(session->pcap_ctx){ + ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT, + buffer_get_rest(session->out_buffer),buffer_get_rest_len(session->out_buffer) + ,buffer_get_rest_len(session->out_buffer)); + } +#endif + hmac = packet_encrypt(session, buffer_get_rest(session->out_buffer), + buffer_get_rest_len(session->out_buffer)); + if (hmac) { + if (buffer_add_data(session->out_buffer, hmac, 20) < 0) { + goto error; + } + } + + rc = ssh_packet_write(session); + session->send_seq++; + + ssh_log(session,SSH_LOG_PACKET, + "packet: wrote [len=%d,padding=%hhd,comp=%d,payload=%d]", + ntohl(finallen), padding, compsize, payloadsize); + if (buffer_reinit(session->out_buffer) < 0) { + rc = SSH_ERROR; + } +error: + leave_function(); + return rc; /* SSH_OK, AGAIN or ERROR */ +} + + +int packet_send(ssh_session session) { +#ifdef WITH_SSH1 + if (session->version == 1) { + return packet_send1(session); + } +#endif + return packet_send2(session); +} + + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/packet1.c b/libssh/src/packet1.c new file mode 100644 index 00000000..5c8b81e5 --- /dev/null +++ b/libssh/src/packet1.c @@ -0,0 +1,370 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#ifndef _WIN32 +#include +#endif /* _WIN32 */ + +#include "libssh/priv.h" +#include "libssh/ssh1.h" +#include "libssh/crc32.h" +#include "libssh/packet.h" +#include "libssh/session.h" +#include "libssh/buffer.h" +#include "libssh/socket.h" +#include "libssh/kex.h" +#include "libssh/crypto.h" + +#ifdef WITH_SSH1 + +static ssh_packet_callback default_packet_handlers1[]= { + NULL, //SSH_MSG_NONE 0 + ssh_packet_disconnect1, //SSH_MSG_DISCONNECT 1 + ssh_packet_publickey1, //SSH_SMSG_PUBLIC_KEY 2 + NULL, //SSH_CMSG_SESSION_KEY 3 + NULL, //SSH_CMSG_USER 4 + NULL, //SSH_CMSG_AUTH_RHOSTS 5 + NULL, //SSH_CMSG_AUTH_RSA 6 + NULL, //SSH_SMSG_AUTH_RSA_CHALLENGE 7 + NULL, //SSH_CMSG_AUTH_RSA_RESPONSE 8 + NULL, //SSH_CMSG_AUTH_PASSWORD 9 + NULL, //SSH_CMSG_REQUEST_PTY 10 + NULL, //SSH_CMSG_WINDOW_SIZE 11 + NULL, //SSH_CMSG_EXEC_SHELL 12 + NULL, //SSH_CMSG_EXEC_CMD 13 + ssh_packet_smsg_success1, //SSH_SMSG_SUCCESS 14 + ssh_packet_smsg_failure1, //SSH_SMSG_FAILURE 15 + NULL, //SSH_CMSG_STDIN_DATA 16 + ssh_packet_data1, //SSH_SMSG_STDOUT_DATA 17 + ssh_packet_data1, //SSH_SMSG_STDERR_DATA 18 + NULL, //SSH_CMSG_EOF 19 + ssh_packet_exist_status1, //SSH_SMSG_EXITSTATUS 20 + NULL, //SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21 + NULL, //SSH_MSG_CHANNEL_OPEN_FAILURE 22 + NULL, //SSH_MSG_CHANNEL_DATA 23 + ssh_packet_close1, //SSH_MSG_CHANNEL_CLOSE 24 + NULL, //SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25 + NULL, //SSH_CMSG_X11_REQUEST_FORWARDING 26 + NULL, //SSH_SMSG_X11_OPEN 27 + NULL, //SSH_CMSG_PORT_FORWARD_REQUEST 28 + NULL, //SSH_MSG_PORT_OPEN 29 + NULL, //SSH_CMSG_AGENT_REQUEST_FORWARDING 30 + NULL, //SSH_SMSG_AGENT_OPEN 31 + ssh_packet_ignore_callback, //SSH_MSG_IGNORE 32 + NULL, //SSH_CMSG_EXIT_CONFIRMATION 33 + NULL, //SSH_CMSG_X11_REQUEST_FORWARDING 34 + NULL, //SSH_CMSG_AUTH_RHOSTS_RSA 35 + ssh_packet_ignore_callback, //SSH_MSG_DEBUG 36 +}; + +/** @internal + * @brief sets the default packet handlers + */ +void ssh_packet_set_default_callbacks1(ssh_session session){ + session->default_packet_callbacks.start=0; + session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers1)/sizeof(ssh_packet_callback); + session->default_packet_callbacks.user=session; + session->default_packet_callbacks.callbacks=default_packet_handlers1; + ssh_packet_set_callbacks(session, &session->default_packet_callbacks); +} + + +/** @internal + * @handles a data received event. It then calls the handlers for the different packet types + * or and exception handler callback. Adapted for SSH-1 packets. + * @param user pointer to current ssh_session + * @param data pointer to the data received + * @len length of data received. It might not be enough for a complete packet + * @returns number of bytes read and processed. + */ + +int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user) { + void *packet = NULL; + int to_be_read; + size_t processed=0; + uint32_t padding; + uint32_t crc; + uint32_t len; + ssh_session session=(ssh_session)user; + enter_function(); + + switch (session->packet_state){ + case PACKET_STATE_INIT: + memset(&session->in_packet, 0, sizeof(PACKET)); + + if (session->in_buffer) { + if (buffer_reinit(session->in_buffer) < 0) { + goto error; + } + } else { + session->in_buffer = ssh_buffer_new(); + if (session->in_buffer == NULL) { + goto error; + } + } + /* must have at least enough bytes for size */ + if(receivedlen < sizeof(uint32_t)){ + leave_function(); + return 0; + } + memcpy(&len,data,sizeof(uint32_t)); + processed += sizeof(uint32_t); + + /* len is not encrypted */ + len = ntohl(len); + if (len > MAX_PACKET_LEN) { + ssh_set_error(session, SSH_FATAL, + "read_packet(): Packet len too high (%u %.8x)", len, len); + goto error; + } + + ssh_log(session, SSH_LOG_PACKET, "Reading a %d bytes packet", len); + + session->in_packet.len = len; + session->packet_state = PACKET_STATE_SIZEREAD; + case PACKET_STATE_SIZEREAD: + len = session->in_packet.len; + /* SSH-1 has a fixed padding lenght */ + padding = 8 - (len % 8); + to_be_read = len + padding; + if(to_be_read + processed > receivedlen){ + /* wait for rest of packet */ + leave_function(); + return processed; + } + /* it is _not_ possible that to_be_read be < 8. */ + packet = (char *)data + processed; + + if (buffer_add_data(session->in_buffer,packet,to_be_read) < 0) { + SAFE_FREE(packet); + goto error; + } + processed += to_be_read; +#ifdef DEBUG_CRYPTO + ssh_print_hexa("read packet:", ssh_buffer_get_begin(session->in_buffer), + ssh_buffer_get_len(session->in_buffer)); +#endif + if (session->current_crypto) { + /* + * We decrypt everything, missing the lenght part (which was + * previously read, unencrypted, and is not part of the buffer + */ + if (packet_decrypt(session, + ssh_buffer_get_begin(session->in_buffer), + ssh_buffer_get_len(session->in_buffer)) < 0) { + ssh_set_error(session, SSH_FATAL, "Packet decrypt error"); + goto error; + } + } +#ifdef DEBUG_CRYPTO + ssh_print_hexa("read packet decrypted:", ssh_buffer_get_begin(session->in_buffer), + ssh_buffer_get_len(session->in_buffer)); +#endif + ssh_log(session, SSH_LOG_PACKET, "%d bytes padding", padding); + if(((len + padding) != buffer_get_rest_len(session->in_buffer)) || + ((len + padding) < sizeof(uint32_t))) { + ssh_log(session, SSH_LOG_RARE, "no crc32 in packet"); + ssh_set_error(session, SSH_FATAL, "no crc32 in packet"); + goto error; + } + + memcpy(&crc, + (unsigned char *)buffer_get_rest(session->in_buffer) + (len+padding) - sizeof(uint32_t), + sizeof(uint32_t)); + buffer_pass_bytes_end(session->in_buffer, sizeof(uint32_t)); + crc = ntohl(crc); + if (ssh_crc32(buffer_get_rest(session->in_buffer), + (len + padding) - sizeof(uint32_t)) != crc) { +#ifdef DEBUG_CRYPTO + ssh_print_hexa("crc32 on",buffer_get_rest(session->in_buffer), + len + padding - sizeof(uint32_t)); +#endif + ssh_log(session, SSH_LOG_RARE, "Invalid crc32"); + ssh_set_error(session, SSH_FATAL, + "Invalid crc32: expected %.8x, got %.8x", + crc, + ssh_crc32(buffer_get_rest(session->in_buffer), + len + padding - sizeof(uint32_t))); + goto error; + } + /* pass the padding */ + buffer_pass_bytes(session->in_buffer, padding); + ssh_log(session, SSH_LOG_PACKET, "The packet is valid"); + +/* TODO FIXME +#ifdef WITH_ZLIB + if(session->current_crypto && session->current_crypto->do_compress_in){ + decompress_buffer(session,session->in_buffer); + } +#endif +*/ + session->recv_seq++; + /* We don't want to rewrite a new packet while still executing the packet callbacks */ + session->packet_state = PACKET_STATE_PROCESSING; + ssh_packet_parse_type(session); + /* execute callbacks */ + ssh_packet_process(session, session->in_packet.type); + session->packet_state = PACKET_STATE_INIT; + if(processed < receivedlen){ + int rc; + /* Handle a potential packet left in socket buffer */ + ssh_log(session,SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", + receivedlen-processed); + rc = ssh_packet_socket_callback1((char *)data + processed, + receivedlen - processed,user); + processed += rc; + } + leave_function(); + return processed; + case PACKET_STATE_PROCESSING: + ssh_log(session, SSH_LOG_RARE, "Nested packet processing. Delaying."); + return 0; + } + +error: + session->session_state=SSH_SESSION_STATE_ERROR; + leave_function(); + return processed; +} + + +int packet_send1(ssh_session session) { + unsigned int blocksize = (session->current_crypto ? + session->current_crypto->out_cipher->blocksize : 8); + uint32_t currentlen = ssh_buffer_get_len(session->out_buffer) + sizeof(uint32_t); + char padstring[32] = {0}; + int rc = SSH_ERROR; + uint32_t finallen; + uint32_t crc; + uint8_t padding; + + enter_function(); + ssh_log(session,SSH_LOG_PACKET,"Sending a %d bytes long packet",currentlen); + +/* TODO FIXME +#ifdef WITH_ZLIB + if (session->current_crypto && session->current_crypto->do_compress_out) { + if (compress_buffer(session, session->out_buffer) < 0) { + goto error; + } + currentlen = buffer_get_len(session->out_buffer); + } +#endif +*/ + padding = blocksize - (currentlen % blocksize); + if (session->current_crypto) { + ssh_get_random(padstring, padding, 0); + } else { + memset(padstring, 0, padding); + } + + finallen = htonl(currentlen); + ssh_log(session, SSH_LOG_PACKET, + "%d bytes after comp + %d padding bytes = %d bytes packet", + currentlen, padding, ntohl(finallen)); + + if (buffer_prepend_data(session->out_buffer, &padstring, padding) < 0) { + goto error; + } + if (buffer_prepend_data(session->out_buffer, &finallen, sizeof(uint32_t)) < 0) { + goto error; + } + + crc = ssh_crc32((char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t), + ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t)); + + if (buffer_add_u32(session->out_buffer, ntohl(crc)) < 0) { + goto error; + } + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Clear packet", ssh_buffer_get_begin(session->out_buffer), + ssh_buffer_get_len(session->out_buffer)); +#endif + + packet_encrypt(session, (unsigned char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t), + ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t)); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("encrypted packet",ssh_buffer_get_begin(session->out_buffer), + ssh_buffer_get_len(session->out_buffer)); +#endif + rc=ssh_socket_write(session->socket, ssh_buffer_get_begin(session->out_buffer), + ssh_buffer_get_len(session->out_buffer)); + if(rc== SSH_ERROR) { + goto error; + } + + session->send_seq++; + + if (buffer_reinit(session->out_buffer) < 0) { + rc = SSH_ERROR; + } +error: + leave_function(); + return rc; /* SSH_OK, AGAIN or ERROR */ +} + +SSH_PACKET_CALLBACK(ssh_packet_disconnect1){ + (void)packet; + (void)user; + (void)type; + ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT"); + ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_DISCONNECT"); + ssh_socket_close(session->socket); + session->alive = 0; + session->session_state=SSH_SESSION_STATE_DISCONNECTED; + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(ssh_packet_smsg_success1){ + if(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){ + session->session_state=SSH_SESSION_STATE_AUTHENTICATING; + return SSH_PACKET_USED; + } else if(session->session_state==SSH_SESSION_STATE_AUTHENTICATING){ + ssh_auth1_handler(session,type); + return SSH_PACKET_USED; + } else { + return ssh_packet_channel_success(session,type,packet,user); + } +} + +SSH_PACKET_CALLBACK(ssh_packet_smsg_failure1){ + if(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){ + session->session_state=SSH_SESSION_STATE_ERROR; + ssh_set_error(session,SSH_FATAL,"Key exchange failed: received SSH_SMSG_FAILURE"); + return SSH_PACKET_USED; + } else if(session->session_state==SSH_SESSION_STATE_AUTHENTICATING){ + ssh_auth1_handler(session,type); + return SSH_PACKET_USED; + } else { + return ssh_packet_channel_failure(session,type,packet,user); + } +} + + +#endif /* WITH_SSH1 */ + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/packet_cb.c b/libssh/src/packet_cb.c new file mode 100644 index 00000000..41d0985c --- /dev/null +++ b/libssh/src/packet_cb.c @@ -0,0 +1,244 @@ +/* + * packet.c - packet building functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2011 Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include "libssh/priv.h" +#include "libssh/buffer.h" +#include "libssh/crypto.h" +#include "libssh/dh.h" +#include "libssh/misc.h" +#include "libssh/packet.h" +#include "libssh/pki.h" +#include "libssh/session.h" +#include "libssh/socket.h" +#include "libssh/ssh2.h" + +/** + * @internal + * + * @brief Handle a SSH_DISCONNECT packet. + */ +SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){ + uint32_t code; + char *error=NULL; + ssh_string error_s; + (void)user; + (void)type; + buffer_get_u32(packet, &code); + error_s = buffer_get_ssh_string(packet); + if (error_s != NULL) { + error = ssh_string_to_char(error_s); + ssh_string_free(error_s); + } + ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s",code, + error != NULL ? error : "no error"); + ssh_set_error(session, SSH_FATAL, + "Received SSH_MSG_DISCONNECT: %d:%s",code, + error != NULL ? error : "no error"); + SAFE_FREE(error); + + ssh_socket_close(session->socket); + session->alive = 0; + session->session_state= SSH_SESSION_STATE_ERROR; + /* TODO: handle a graceful disconnect */ + return SSH_PACKET_USED; +} + +/** + * @internal + * + * @brief Handle a SSH_IGNORE and SSH_DEBUG packet. + */ +SSH_PACKET_CALLBACK(ssh_packet_ignore_callback){ + (void)user; + (void)type; + (void)packet; + ssh_log(session,SSH_LOG_PROTOCOL,"Received %s packet",type==SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG"); + /* TODO: handle a graceful disconnect */ + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(ssh_packet_dh_reply){ + int rc; + (void)type; + (void)user; + ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY"); + if(session->session_state!= SSH_SESSION_STATE_DH && + session->dh_handshake_state != DH_STATE_INIT_SENT){ + ssh_set_error(session,SSH_FATAL,"ssh_packet_dh_reply called in wrong state : %d:%d", + session->session_state,session->dh_handshake_state); + goto error; + } + switch(session->next_crypto->kex_type){ + case SSH_KEX_DH_GROUP1_SHA1: + case SSH_KEX_DH_GROUP14_SHA1: + rc=ssh_client_dh_reply(session, packet); + break; +#ifdef HAVE_ECDH + case SSH_KEX_ECDH_SHA2_NISTP256: + rc = ssh_client_ecdh_reply(session, packet); + break; +#endif + default: + ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_dh_reply"); + goto error; + } + if(rc==SSH_OK) { + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + return SSH_PACKET_USED; + } +error: + session->session_state=SSH_SESSION_STATE_ERROR; + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(ssh_packet_newkeys){ + ssh_string sig_blob = NULL; + int rc; + (void)packet; + (void)user; + (void)type; + ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_NEWKEYS"); + if(session->session_state!= SSH_SESSION_STATE_DH && + session->dh_handshake_state != DH_STATE_NEWKEYS_SENT){ + ssh_set_error(session,SSH_FATAL,"ssh_packet_newkeys called in wrong state : %d:%d", + session->session_state,session->dh_handshake_state); + goto error; + } + if(session->server){ + /* server things are done in server.c */ + session->dh_handshake_state=DH_STATE_FINISHED; + } else { + ssh_key key; + /* client */ + rc = make_sessionid(session); + if (rc != SSH_OK) { + goto error; + } + + /* + * Set the cryptographic functions for the next crypto + * (it is needed for generate_session_keys for key lengths) + */ + if (crypt_set_algorithms(session, SSH_3DES) /* knows nothing about DES*/ ) { + goto error; + } + + if (generate_session_keys(session) < 0) { + goto error; + } + + /* Verify the host's signature. FIXME do it sooner */ + sig_blob = session->next_crypto->dh_server_signature; + session->next_crypto->dh_server_signature = NULL; + + /* get the server public key */ + rc = ssh_pki_import_pubkey_blob(session->next_crypto->server_pubkey, &key); + if (rc < 0) { + return SSH_ERROR; + } + + /* check if public key from server matches user preferences */ + if (session->opts.wanted_methods[SSH_HOSTKEYS]) { + if(!ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS], + key->type_c)) { + ssh_set_error(session, + SSH_FATAL, + "Public key from server (%s) doesn't match user " + "preference (%s)", + key->type_c, + session->opts.wanted_methods[SSH_HOSTKEYS]); + ssh_key_free(key); + return -1; + } + } + + rc = ssh_pki_signature_verify_blob(session, + sig_blob, + key, + session->next_crypto->secret_hash, + session->next_crypto->digest_len); + /* Set the server public key type for known host checking */ + session->next_crypto->server_pubkey_type = key->type_c; + + ssh_key_free(key); + ssh_string_burn(sig_blob); + ssh_string_free(sig_blob); + sig_blob = NULL; + if (rc == SSH_ERROR) { + goto error; + } + ssh_log(session,SSH_LOG_PROTOCOL,"Signature verified and valid"); + + /* + * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and + * current_crypto + */ + if (session->current_crypto) { + crypto_free(session->current_crypto); + session->current_crypto=NULL; + } + + /* FIXME later, include a function to change keys */ + session->current_crypto = session->next_crypto; + + session->next_crypto = crypto_new(); + if (session->next_crypto == NULL) { + ssh_set_error_oom(session); + goto error; + } + session->next_crypto->session_id = malloc(session->current_crypto->digest_len); + if (session->next_crypto->session_id == NULL) { + ssh_set_error_oom(session); + goto error; + } + memcpy(session->next_crypto->session_id, session->current_crypto->session_id, + session->current_crypto->digest_len); + } + session->dh_handshake_state = DH_STATE_FINISHED; + session->ssh_connection_callback(session); + return SSH_PACKET_USED; +error: + session->session_state=SSH_SESSION_STATE_ERROR; + return SSH_PACKET_USED; +} + +/** + * @internal + * @brief handles a SSH_SERVICE_ACCEPT packet + * + */ +SSH_PACKET_CALLBACK(ssh_packet_service_accept){ + (void)packet; + (void)type; + (void)user; + enter_function(); + session->auth_service_state=SSH_AUTH_SERVICE_ACCEPTED; + ssh_log(session, SSH_LOG_PACKET, + "Received SSH_MSG_SERVICE_ACCEPT"); + leave_function(); + return SSH_PACKET_USED; +} diff --git a/libssh/src/packet_crypt.c b/libssh/src/packet_crypt.c new file mode 100644 index 00000000..50b81893 --- /dev/null +++ b/libssh/src/packet_crypt.c @@ -0,0 +1,186 @@ +/* + * crypt.c - blowfish-cbc code + * + * This file is part of the SSH Library + * + * Copyright (c) 2003 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#ifdef OPENSSL_CRYPTO +#include +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/wrapper.h" +#include "libssh/crypto.h" +#include "libssh/buffer.h" + +uint32_t packet_decrypt_len(ssh_session session, char *crypted){ + uint32_t decrypted; + + if (session->current_crypto) { + if (packet_decrypt(session, crypted, + session->current_crypto->in_cipher->blocksize) < 0) { + return 0; + } + } + memcpy(&decrypted,crypted,sizeof(decrypted)); + return ntohl(decrypted); +} + +int packet_decrypt(ssh_session session, void *data,uint32_t len) { + struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher; + char *out = NULL; + if(len % session->current_crypto->in_cipher->blocksize != 0){ + ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); + return SSH_ERROR; + } + out = malloc(len); + if (out == NULL) { + return -1; + } + + if (crypto->set_decrypt_key(crypto, session->current_crypto->decryptkey, + session->current_crypto->decryptIV) < 0) { + SAFE_FREE(out); + return -1; + } + crypto->cbc_decrypt(crypto,data,out,len); + + memcpy(data,out,len); + memset(out,0,len); + + SAFE_FREE(out); + return 0; +} + +unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { + struct ssh_cipher_struct *crypto = NULL; + HMACCTX ctx = NULL; + char *out = NULL; + unsigned int finallen; + uint32_t seq; + + if (!session->current_crypto) { + return NULL; /* nothing to do here */ + } + if(len % session->current_crypto->in_cipher->blocksize != 0){ + ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); + return NULL; + } + out = malloc(len); + if (out == NULL) { + return NULL; + } + + seq = ntohl(session->send_seq); + crypto = session->current_crypto->out_cipher; + + if (crypto->set_encrypt_key(crypto, session->current_crypto->encryptkey, + session->current_crypto->encryptIV) < 0) { + SAFE_FREE(out); + return NULL; + } + + if (session->version == 2) { + ctx = hmac_init(session->current_crypto->encryptMAC,20,SSH_HMAC_SHA1); + if (ctx == NULL) { + SAFE_FREE(out); + return NULL; + } + hmac_update(ctx,(unsigned char *)&seq,sizeof(uint32_t)); + hmac_update(ctx,data,len); + hmac_final(ctx,session->current_crypto->hmacbuf,&finallen); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("mac: ",data,len); + if (finallen != 20) { + printf("Final len is %d\n",finallen); + } + ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, 20); +#endif + } + + crypto->cbc_encrypt(crypto, data, out, len); + + memcpy(data, out, len); + memset(out, 0, len); + SAFE_FREE(out); + + if (session->version == 2) { + return session->current_crypto->hmacbuf; + } + + return NULL; +} + +/** + * @internal + * + * @brief Verify the hmac of a packet + * + * @param session The session to use. + * @param buffer The buffer to verify the hmac from. + * @param mac The mac to compare with the hmac. + * + * @return 0 if hmac and mac are equal, < 0 if not or an error + * occurred. + */ +int packet_hmac_verify(ssh_session session, ssh_buffer buffer, + unsigned char *mac) { + unsigned char hmacbuf[EVP_MAX_MD_SIZE] = {0}; + HMACCTX ctx; + unsigned int len; + uint32_t seq; + + ctx = hmac_init(session->current_crypto->decryptMAC, 20, SSH_HMAC_SHA1); + if (ctx == NULL) { + return -1; + } + + seq = htonl(session->recv_seq); + + hmac_update(ctx, (unsigned char *) &seq, sizeof(uint32_t)); + hmac_update(ctx, buffer_get_rest(buffer), buffer_get_rest_len(buffer)); + hmac_final(ctx, hmacbuf, &len); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("received mac",mac,len); + ssh_print_hexa("Computed mac",hmacbuf,len); + ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(uint32_t)); +#endif + if (memcmp(mac, hmacbuf, len) == 0) { + return 0; + } + + return -1; +} + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/pcap.c b/libssh/src/pcap.c new file mode 100644 index 00000000..6e688962 --- /dev/null +++ b/libssh/src/pcap.c @@ -0,0 +1,554 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* pcap.c */ +#include "config.h" +#ifdef WITH_PCAP + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif +#include +#include + +#include "libssh/libssh.h" +#include "libssh/pcap.h" +#include "libssh/session.h" +#include "libssh/buffer.h" +#include "libssh/socket.h" + +/** + * @internal + * + * @defgroup libssh_pcap The libssh pcap functions + * @ingroup libssh + * + * The pcap file generation + * + * + * @{ + */ + +/* The header of a pcap file is the following. We are not going to make it + * very complicated. + * Just for information. + */ +struct pcap_hdr_s { + uint32_t magic_number; /* magic number */ + uint16_t version_major; /* major version number */ + uint16_t version_minor; /* minor version number */ + int32_t thiszone; /* GMT to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length of captured packets, in octets */ + uint32_t network; /* data link type */ +}; + +#define PCAP_MAGIC 0xa1b2c3d4 +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 + +#define DLT_RAW 12 /* raw IP */ + +/* TCP flags */ +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 + +/* The header of a pcap packet. + * Just for information. + */ +struct pcaprec_hdr_s { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +}; + +/** @private + * @brief a pcap context expresses the state of a pcap dump + * in a SSH session only. Multiple pcap contexts may be used into + * a single pcap file. + */ + +struct ssh_pcap_context_struct { + ssh_session session; + ssh_pcap_file file; + int connected; + /* All of these information are useful to generate + * the dummy IP and TCP packets + */ + uint32_t ipsource; + uint32_t ipdest; + uint16_t portsource; + uint16_t portdest; + uint32_t outsequence; + uint32_t insequence; +}; + +/** @private + * @brief a pcap file expresses the state of a pcap file which may + * contain several streams. + */ +struct ssh_pcap_file_struct { + FILE *output; + uint16_t ipsequence; +}; + +/** + * @brief create a new ssh_pcap_file object + */ +ssh_pcap_file ssh_pcap_file_new(void) { + struct ssh_pcap_file_struct *pcap; + + pcap = (struct ssh_pcap_file_struct *) malloc(sizeof(struct ssh_pcap_file_struct)); + if (pcap == NULL) { + return NULL; + } + ZERO_STRUCTP(pcap); + + return pcap; +} + +/** @internal + * @brief writes a packet on file + */ +static int ssh_pcap_file_write(ssh_pcap_file pcap, ssh_buffer packet){ + int err; + uint32_t len; + if(pcap == NULL || pcap->output==NULL) + return SSH_ERROR; + len=buffer_get_rest_len(packet); + err=fwrite(buffer_get_rest(packet),len,1,pcap->output); + if(err<0) + return SSH_ERROR; + else + return SSH_OK; +} + +/** @internal + * @brief prepends a packet with the pcap header and writes packet + * on file + */ +int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len){ + ssh_buffer header=ssh_buffer_new(); + struct timeval now; + int err; + if(header == NULL) + return SSH_ERROR; + gettimeofday(&now,NULL); + err = buffer_add_u32(header,htonl(now.tv_sec)); + if (err < 0) { + goto error; + } + err = buffer_add_u32(header,htonl(now.tv_usec)); + if (err < 0) { + goto error; + } + err = buffer_add_u32(header,htonl(buffer_get_rest_len(packet))); + if (err < 0) { + goto error; + } + err = buffer_add_u32(header,htonl(original_len)); + if (err < 0) { + goto error; + } + err = buffer_add_buffer(header,packet); + if (err < 0) { + goto error; + } + err=ssh_pcap_file_write(pcap,header); +error: + ssh_buffer_free(header); + return err; +} + +/** + * @brief opens a new pcap file and create header + */ +int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){ + ssh_buffer header; + int err; + if(pcap == NULL) + return SSH_ERROR; + if(pcap->output){ + fclose(pcap->output); + pcap->output=NULL; + } + pcap->output=fopen(filename,"wb"); + if(pcap->output==NULL) + return SSH_ERROR; + header=ssh_buffer_new(); + if(header==NULL) + return SSH_ERROR; + err = buffer_add_u32(header,htonl(PCAP_MAGIC)); + if (err < 0) { + goto error; + } + err = buffer_add_u16(header,htons(PCAP_VERSION_MAJOR)); + if (err < 0) { + goto error; + } + err = buffer_add_u16(header,htons(PCAP_VERSION_MINOR)); + if (err < 0) { + goto error; + } + /* currently hardcode GMT to 0 */ + err = buffer_add_u32(header,htonl(0)); + if (err < 0) { + goto error; + } + /* accuracy */ + err = buffer_add_u32(header,htonl(0)); + if (err < 0) { + goto error; + } + /* size of the biggest packet */ + err = buffer_add_u32(header,htonl(MAX_PACKET_LEN)); + if (err < 0) { + goto error; + } + /* we will write sort-of IP */ + err = buffer_add_u32(header,htonl(DLT_RAW)); + if (err < 0) { + goto error; + } + err=ssh_pcap_file_write(pcap,header); +error: + ssh_buffer_free(header); + return err; +} + +int ssh_pcap_file_close(ssh_pcap_file pcap){ + int err; + if(pcap ==NULL || pcap->output==NULL) + return SSH_ERROR; + err=fclose(pcap->output); + pcap->output=NULL; + if(err != 0) + return SSH_ERROR; + else + return SSH_OK; +} + +void ssh_pcap_file_free(ssh_pcap_file pcap){ + ssh_pcap_file_close(pcap); + SAFE_FREE(pcap); +} + + +/** @internal + * @brief allocates a new ssh_pcap_context object + */ + +ssh_pcap_context ssh_pcap_context_new(ssh_session session){ + ssh_pcap_context ctx = (struct ssh_pcap_context_struct *) malloc(sizeof(struct ssh_pcap_context_struct)); + if(ctx==NULL){ + ssh_set_error_oom(session); + return NULL; + } + ZERO_STRUCTP(ctx); + ctx->session=session; + return ctx; +} + +void ssh_pcap_context_free(ssh_pcap_context ctx){ + SAFE_FREE(ctx); +} + +void ssh_pcap_context_set_file(ssh_pcap_context ctx, ssh_pcap_file pcap){ + ctx->file=pcap; +} + +/** @internal + * @brief sets the IP and port parameters in the connection + */ +static int ssh_pcap_context_connect(ssh_pcap_context ctx){ + ssh_session session=ctx->session; + struct sockaddr_in local, remote; + socket_t fd; + socklen_t len; + if(session==NULL) + return SSH_ERROR; + if(session->socket==NULL) + return SSH_ERROR; + fd=ssh_socket_get_fd_in(session->socket); + /* TODO: adapt for windows */ + if(fd<0) + return SSH_ERROR; + len=sizeof(local); + if(getsockname(fd,(struct sockaddr *)&local,&len)<0){ + ssh_set_error(session,SSH_REQUEST_DENIED,"Getting local IP address: %s",strerror(errno)); + return SSH_ERROR; + } + len=sizeof(remote); + if(getpeername(fd,(struct sockaddr *)&remote,&len)<0){ + ssh_set_error(session,SSH_REQUEST_DENIED,"Getting remote IP address: %s",strerror(errno)); + return SSH_ERROR; + } + if(local.sin_family != AF_INET){ + ssh_set_error(session,SSH_REQUEST_DENIED,"Only IPv4 supported for pcap logging"); + return SSH_ERROR; + } + memcpy(&ctx->ipsource,&local.sin_addr,sizeof(ctx->ipsource)); + memcpy(&ctx->ipdest,&remote.sin_addr,sizeof(ctx->ipdest)); + memcpy(&ctx->portsource,&local.sin_port,sizeof(ctx->portsource)); + memcpy(&ctx->portdest,&remote.sin_port,sizeof(ctx->portdest)); + + ctx->connected=1; + return SSH_OK; +} + +#define IPHDR_LEN 20 +#define TCPHDR_LEN 20 +#define TCPIPHDR_LEN (IPHDR_LEN + TCPHDR_LEN) +/** @internal + * @brief write a SSH packet as a TCP over IP in a pcap file + * @param ctx open pcap context + * @param direction SSH_PCAP_DIRECTION_IN if the packet has been received + * @param direction SSH_PCAP_DIRECTION_OUT if the packet has been emitted + * @param data pointer to the data to write + * @param len data to write in the pcap file. May be smaller than origlen. + * @param origlen number of bytes of complete data. + * @returns SSH_OK write is successful + * @returns SSH_ERROR an error happened. + */ +int ssh_pcap_context_write(ssh_pcap_context ctx,enum ssh_pcap_direction direction + , void *data, uint32_t len, uint32_t origlen){ + ssh_buffer ip; + int err; + if(ctx==NULL || ctx->file ==NULL) + return SSH_ERROR; + if(ctx->connected==0) + if(ssh_pcap_context_connect(ctx)==SSH_ERROR) + return SSH_ERROR; + ip=ssh_buffer_new(); + if(ip==NULL){ + ssh_set_error_oom(ctx->session); + return SSH_ERROR; + } + /* build an IP packet */ + /* V4, 20 bytes */ + err = buffer_add_u8(ip,4 << 4 | 5); + if (err < 0) { + goto error; + } + /* tos */ + err = buffer_add_u8(ip,0); + if (err < 0) { + goto error; + } + /* total len */ + err = buffer_add_u16(ip,htons(origlen + TCPIPHDR_LEN)); + if (err < 0) { + goto error; + } + /* IP id number */ + err = buffer_add_u16(ip,htons(ctx->file->ipsequence)); + if (err < 0) { + goto error; + } + ctx->file->ipsequence++; + /* fragment offset */ + err = buffer_add_u16(ip,htons(0)); + if (err < 0) { + goto error; + } + /* TTL */ + err = buffer_add_u8(ip,64); + if (err < 0) { + goto error; + } + /* protocol TCP=6 */ + err = buffer_add_u8(ip,6); + if (err < 0) { + goto error; + } + /* checksum */ + err = buffer_add_u16(ip,0); + if (err < 0) { + goto error; + } + if(direction==SSH_PCAP_DIR_OUT){ + err = buffer_add_u32(ip,ctx->ipsource); + if (err < 0) { + goto error; + } + err = buffer_add_u32(ip,ctx->ipdest); + if (err < 0) { + goto error; + } + } else { + err = buffer_add_u32(ip,ctx->ipdest); + if (err < 0) { + goto error; + } + err = buffer_add_u32(ip,ctx->ipsource); + if (err < 0) { + goto error; + } + } + /* TCP */ + if(direction==SSH_PCAP_DIR_OUT){ + err = buffer_add_u16(ip,ctx->portsource); + if (err < 0) { + goto error; + } + err = buffer_add_u16(ip,ctx->portdest); + if (err < 0) { + goto error; + } + } else { + err = buffer_add_u16(ip,ctx->portdest); + if (err < 0) { + goto error; + } + err = buffer_add_u16(ip,ctx->portsource); + if (err < 0) { + goto error; + } + } + /* sequence number */ + if(direction==SSH_PCAP_DIR_OUT){ + err = buffer_add_u32(ip,ntohl(ctx->outsequence)); + if (err < 0) { + goto error; + } + ctx->outsequence+=origlen; + } else { + err = buffer_add_u32(ip,ntohl(ctx->insequence)); + if (err < 0) { + goto error; + } + ctx->insequence+=origlen; + } + /* ack number */ + if(direction==SSH_PCAP_DIR_OUT){ + err = buffer_add_u32(ip,ntohl(ctx->insequence)); + if (err < 0) { + goto error; + } + } else { + err = buffer_add_u32(ip,ntohl(ctx->outsequence)); + if (err < 0) { + goto error; + } + } + /* header len = 20 = 5 * 32 bits, at offset 4*/ + err = buffer_add_u8(ip,5 << 4); + if (err < 0) { + goto error; + } + /* flags */ + err = buffer_add_u8(ip,TH_PUSH | TH_ACK); + if (err < 0) { + goto error; + } + /* window */ + err = buffer_add_u16(ip,htons(65535)); + if (err < 0) { + goto error; + } + /* checksum */ + err = buffer_add_u16(ip,htons(0)); + if (err < 0) { + goto error; + } + /* urgent data ptr */ + err = buffer_add_u16(ip,0); + if (err < 0) { + goto error; + } + /* actual data */ + err = buffer_add_data(ip,data,len); + if (err < 0) { + goto error; + } + err=ssh_pcap_file_write_packet(ctx->file,ip,origlen + TCPIPHDR_LEN); +error: + ssh_buffer_free(ip); + return err; +} + +/** @brief sets the pcap file used to trace the session + * @param current session + * @param pcap an handler to a pcap file. A pcap file may be used in several + * sessions. + * @returns SSH_ERROR in case of error, SSH_OK otherwise. + */ +int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap){ + ssh_pcap_context ctx=ssh_pcap_context_new(session); + if(ctx==NULL){ + ssh_set_error_oom(session); + return SSH_ERROR; + } + ctx->file=pcap; + if(session->pcap_ctx) + ssh_pcap_context_free(session->pcap_ctx); + session->pcap_ctx=ctx; + return SSH_OK; +} + + +#else /* WITH_PCAP */ + +/* Simple stub returning errors when no pcap compiled in */ + +#include "libssh/libssh.h" +#include "libssh/priv.h" + +int ssh_pcap_file_close(ssh_pcap_file pcap){ + (void) pcap; + return SSH_ERROR; +} + +void ssh_pcap_file_free(ssh_pcap_file pcap){ + (void) pcap; +} + +ssh_pcap_file ssh_pcap_file_new(void){ + return NULL; +} +int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){ + (void) pcap; + (void) filename; + return SSH_ERROR; +} + +int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile){ + (void) pcapfile; + ssh_set_error(session,SSH_REQUEST_DENIED,"Pcap support not compiled in"); + return SSH_ERROR; +} + +#endif + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/pki.c b/libssh/src/pki.c new file mode 100644 index 00000000..87d7e765 --- /dev/null +++ b/libssh/src/pki.c @@ -0,0 +1,1410 @@ +/* + * known_hosts.c + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * Copyright (c) 2011-2012 Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/** + * @defgroup libssh_pki The SSH Public Key Infrastructure + * @ingroup libssh + * + * Functions for the creation, importation and manipulation of public and + * private keys in the context of the SSH protocol + * + * @{ + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# if _MSC_VER >= 1400 +# include +# undef open +# define open _open +# undef close +# define close _close +# undef read +# define read _read +# undef unlink +# define unlink _unlink +# endif /* _MSC_VER */ +#endif + +#include "libssh/libssh.h" +#include "libssh/session.h" +#include "libssh/priv.h" +#include "libssh/pki.h" +#include "libssh/pki_priv.h" +#include "libssh/keys.h" +#include "libssh/buffer.h" +#include "libssh/misc.h" +#include "libssh/agent.h" + +void _ssh_pki_log(const char *function, const char *format, ...) +{ +#ifdef DEBUG_CRYPTO + char buffer[1024]; + va_list va; + + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + + ssh_log_function(SSH_LOG_DEBUG, function, buffer); +#else + (void) function; + (void) format; +#endif + return; +} + +enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) { + if (strncmp(privkey, DSA_HEADER_BEGIN, strlen(DSA_HEADER_BEGIN)) == 0) { + return SSH_KEYTYPE_DSS; + } + + if (strncmp(privkey, RSA_HEADER_BEGIN, strlen(RSA_HEADER_BEGIN)) == 0) { + return SSH_KEYTYPE_RSA; + } + + if (strncmp(privkey, ECDSA_HEADER_BEGIN, strlen(ECDSA_HEADER_BEGIN)) == 0) { + return SSH_KEYTYPE_ECDSA; + } + + return SSH_KEYTYPE_UNKNOWN; +} + +/** + * @brief creates a new empty SSH key + * @returns an empty ssh_key handle, or NULL on error. + */ +ssh_key ssh_key_new (void) { + ssh_key ptr = malloc (sizeof (struct ssh_key_struct)); + if (ptr == NULL) { + return NULL; + } + ZERO_STRUCTP(ptr); + return ptr; +} + +ssh_key ssh_key_dup(const ssh_key key) +{ + if (key == NULL) { + return NULL; + } + + return pki_key_dup(key, 0); +} + +/** + * @brief clean up the key and deallocate all existing keys + * @param[in] key ssh_key to clean + */ +void ssh_key_clean (ssh_key key){ + if(key == NULL) + return; +#ifdef HAVE_LIBGCRYPT + if(key->dsa) gcry_sexp_release(key->dsa); + if(key->rsa) gcry_sexp_release(key->rsa); + if(key->ecdsa) gcry_sexp_release(key->ecdsa); +#elif defined HAVE_LIBCRYPTO + if(key->dsa) DSA_free(key->dsa); + if(key->rsa) RSA_free(key->rsa); +#ifdef HAVE_OPENSSL_ECC + if(key->ecdsa) EC_KEY_free(key->ecdsa); +#endif /* HAVE_OPENSSL_ECC */ +#endif + key->flags=SSH_KEY_FLAG_EMPTY; + key->type=SSH_KEYTYPE_UNKNOWN; + key->ecdsa_nid = 0; + key->type_c=NULL; + key->dsa = NULL; + key->rsa = NULL; + key->ecdsa = NULL; +} + +/** + * @brief deallocate a SSH key + * @param[in] key ssh_key handle to free + */ +void ssh_key_free (ssh_key key){ + if(key){ + ssh_key_clean(key); + SAFE_FREE(key); + } +} + +/** + * @brief returns the type of a ssh key + * @param[in] key the ssh_key handle + * @returns one of SSH_KEYTYPE_RSA,SSH_KEYTYPE_DSS,SSH_KEYTYPE_RSA1 + * @returns SSH_KEYTYPE_UNKNOWN if the type is unknown + */ +enum ssh_keytypes_e ssh_key_type(const ssh_key key){ + if (key == NULL) { + return SSH_KEYTYPE_UNKNOWN; + } + return key->type; +} + +/** + * @brief Convert a key type to a string. + * + * @param[in] type The type to convert. + * + * @return A string for the keytype or NULL if unknown. + */ +const char *ssh_key_type_to_char(enum ssh_keytypes_e type) { + switch (type) { + case SSH_KEYTYPE_DSS: + return "ssh-dss"; + case SSH_KEYTYPE_RSA: + return "ssh-rsa"; + case SSH_KEYTYPE_RSA1: + return "ssh-rsa1"; + case SSH_KEYTYPE_ECDSA: + return "ssh-ecdsa"; + case SSH_KEYTYPE_UNKNOWN: + return NULL; + } + + /* We should never reach this */ + return NULL; +} + +/** + * @brief Convert a ssh key name to a ssh key type. + * + * @param[in] name The name to convert. + * + * @return The enum ssh key type. + */ +enum ssh_keytypes_e ssh_key_type_from_name(const char *name) { + if (name == NULL) { + return SSH_KEYTYPE_UNKNOWN; + } + + if (strcmp(name, "rsa1") == 0) { + return SSH_KEYTYPE_RSA1; + } else if (strcmp(name, "rsa") == 0) { + return SSH_KEYTYPE_RSA; + } else if (strcmp(name, "dsa") == 0) { + return SSH_KEYTYPE_DSS; + } else if (strcmp(name, "ssh-rsa1") == 0) { + return SSH_KEYTYPE_RSA1; + } else if (strcmp(name, "ssh-rsa") == 0) { + return SSH_KEYTYPE_RSA; + } else if (strcmp(name, "ssh-dss") == 0) { + return SSH_KEYTYPE_DSS; + } else if (strcmp(name, "ssh-ecdsa") == 0 + || strcmp(name, "ecdsa") == 0 + || strcmp(name, "ecdsa-sha2-nistp256") == 0 + || strcmp(name, "ecdsa-sha2-nistp384") == 0 + || strcmp(name, "ecdsa-sha2-nistp521") == 0) { + return SSH_KEYTYPE_ECDSA; + } + + return SSH_KEYTYPE_UNKNOWN; +} + +/** + * @brief Check if the key has/is a public key. + * + * @param[in] k The key to check. + * + * @return 1 if it is a public key, 0 if not. + */ +int ssh_key_is_public(const ssh_key k) { + if (k == NULL) { + return 0; + } + + return (k->flags & SSH_KEY_FLAG_PUBLIC); +} + +/** + * @brief Check if the key is a private key. + * + * @param[in] k The key to check. + * + * @return 1 if it is a private key, 0 if not. + */ +int ssh_key_is_private(const ssh_key k) { + if (k == NULL) { + return 0; + } + + return (k->flags & SSH_KEY_FLAG_PRIVATE); +} + +/** + * @brief Compare keys if they are equal. + * + * @param[in] k1 The first key to compare. + * + * @param[in] k2 The second key to compare. + * + * @param[in] what What part or type of the key do you want to compare. + * + * @return 0 if equal, 1 if not. + */ +int ssh_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + if (k1 == NULL || k2 == NULL) { + return 1; + } + + if (k1->type != k2->type) { + ssh_pki_log("key types don't macth!"); + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (!ssh_key_is_private(k1) || + !ssh_key_is_private(k2)) { + return 1; + } + } + + return pki_key_compare(k1, k2, what); +} + +ssh_signature ssh_signature_new(void) +{ + struct ssh_signature_struct *sig; + + sig = malloc(sizeof(struct ssh_signature_struct)); + if (sig == NULL) { + return NULL; + } + ZERO_STRUCTP(sig); + + return sig; +} + +void ssh_signature_free(ssh_signature sig) +{ + if (sig == NULL) { + return; + } + + switch(sig->type) { + case SSH_KEYTYPE_DSS: +#ifdef HAVE_LIBGCRYPT + gcry_sexp_release(sig->dsa_sig); +#elif defined HAVE_LIBCRYPTO + DSA_SIG_free(sig->dsa_sig); +#endif + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: +#ifdef HAVE_LIBGCRYPT + gcry_sexp_release(sig->rsa_sig); +#elif defined HAVE_LIBCRYPTO + SAFE_FREE(sig->rsa_sig); +#endif + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + break; + } + + SAFE_FREE(sig); +} + +/** + * @brief import a base64 formated key from a memory c-string + * + * @param[in] b64_key The c-string holding the base64 encoded key + * + * @param[in] passphrase The passphrase to decrypt the key, or NULL + * + * @param[in] auth_fn An auth function you may want to use or NULL. + * + * @param[in] auth_data Private data passed to the auth function. + * + * @param[out] pkey A pointer where the key can be stored. You need + * to free the memory. + * + * @return SSH_ERROR in case of error, SSH_OK otherwise. + * + * @see ssh_key_free() + */ +int ssh_pki_import_privkey_base64(const char *b64_key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + ssh_key *pkey) +{ + ssh_key key; + + if (b64_key == NULL || pkey == NULL) { + return SSH_ERROR; + } + + if (b64_key == NULL || !*b64_key) { + return SSH_ERROR; + } + + ssh_pki_log("Trying to decode privkey passphrase=%s", + passphrase ? "true" : "false"); + + key = pki_private_key_from_base64(b64_key, passphrase, auth_fn, auth_data); + if (key == NULL) { + return SSH_ERROR; + } + + *pkey = key; + + return SSH_OK; +} + +/** + * @brief Import a key from a file. + * + * @param[in] filename The filename of the the private key. + * + * @param[in] passphrase The passphrase to decrypt the private key. Set to NULL + * if none is needed or it is unknown. + * + * @param[in] auth_fn An auth function you may want to use or NULL. + * + * @param[in] auth_data Private data passed to the auth function. + * + * @param[out] pkey A pointer to store the ssh_key. You need to free the + * key. + * + * @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission + * denied, SSH_ERROR otherwise. + * + * @see ssh_key_free() + **/ +int ssh_pki_import_privkey_file(const char *filename, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + ssh_key *pkey) { + struct stat sb; + char *key_buf; + ssh_key key; + FILE *file; + off_t size; + int rc; + + if (pkey == NULL || filename == NULL || *filename == '\0') { + return SSH_ERROR; + } + + rc = stat(filename, &sb); + if (rc < 0) { + ssh_pki_log("Error getting stat of %s: %s", + filename, strerror(errno)); + switch (errno) { + case ENOENT: + case EACCES: + return SSH_EOF; + } + + return SSH_ERROR; + } + + file = fopen(filename, "rb"); + if (file == NULL) { + ssh_pki_log("Error opening %s: %s", + filename, strerror(errno)); + return SSH_EOF; + } + + key_buf = malloc(sb.st_size + 1); + if (key_buf == NULL) { + fclose(file); + ssh_pki_log("Out of memory!"); + return SSH_ERROR; + } + + size = fread(key_buf, 1, sb.st_size, file); + fclose(file); + + if (size != sb.st_size) { + SAFE_FREE(key_buf); + ssh_pki_log("Error reading %s: %s", + filename, strerror(errno)); + return SSH_ERROR; + } + key_buf[size] = 0; + + key = pki_private_key_from_base64(key_buf, passphrase, auth_fn, auth_data); + SAFE_FREE(key_buf); + if (key == NULL) { + return SSH_ERROR; + } + + *pkey = key; + return SSH_OK; +} + +/* temporary function to migrate seemlessly to ssh_key */ +ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) { + ssh_public_key pub; + ssh_key tmp; + + if(key == NULL) { + return NULL; + } + + tmp = ssh_key_dup(key); + if (tmp == NULL) { + return NULL; + } + + pub = malloc(sizeof(struct ssh_public_key_struct)); + if (pub == NULL) { + ssh_key_free(tmp); + return NULL; + } + ZERO_STRUCTP(pub); + + pub->type = tmp->type; + pub->type_c = tmp->type_c; + + pub->dsa_pub = tmp->dsa; + tmp->dsa = NULL; + pub->rsa_pub = tmp->rsa; + tmp->rsa = NULL; + + ssh_key_free(tmp); + + return pub; +} + +ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key) { + ssh_private_key privkey; + + privkey = malloc(sizeof(struct ssh_private_key_struct)); + if (privkey == NULL) { + ssh_key_free(key); + return NULL; + } + + privkey->type = key->type; + privkey->dsa_priv = key->dsa; + privkey->rsa_priv = key->rsa; + + return privkey; +} + +static int pki_import_pubkey_buffer(ssh_buffer buffer, + enum ssh_keytypes_e type, + ssh_key *pkey) { + ssh_key key; + int rc; + + key = ssh_key_new(); + if (key == NULL) { + return SSH_ERROR; + } + + key->type = type; + key->type_c = ssh_key_type_to_char(type); + key->flags = SSH_KEY_FLAG_PUBLIC; + + switch (type) { + case SSH_KEYTYPE_DSS: + { + ssh_string p; + ssh_string q; + ssh_string g; + ssh_string pubkey; + + p = buffer_get_ssh_string(buffer); + if (p == NULL) { + goto fail; + } + q = buffer_get_ssh_string(buffer); + if (q == NULL) { + ssh_string_burn(p); + ssh_string_free(p); + + goto fail; + } + g = buffer_get_ssh_string(buffer); + if (g == NULL) { + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(q); + ssh_string_free(q); + + goto fail; + } + pubkey = buffer_get_ssh_string(buffer); + if (pubkey == NULL) { + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(q); + ssh_string_free(q); + ssh_string_burn(g); + ssh_string_free(g); + + goto fail; + } + + rc = pki_pubkey_build_dss(key, p, q, g, pubkey); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("p", ssh_string_data(p), ssh_string_len(p)); + ssh_print_hexa("q", ssh_string_data(q), ssh_string_len(q)); + ssh_print_hexa("g", ssh_string_data(g), ssh_string_len(g)); +#endif + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(q); + ssh_string_free(q); + ssh_string_burn(g); + ssh_string_free(g); + ssh_string_burn(pubkey); + ssh_string_free(pubkey); + if (rc == SSH_ERROR) { + goto fail; + } + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + { + ssh_string e; + ssh_string n; + + e = buffer_get_ssh_string(buffer); + if (e == NULL) { + goto fail; + } + n = buffer_get_ssh_string(buffer); + if (n == NULL) { + ssh_string_burn(e); + ssh_string_free(e); + + goto fail; + } + + rc = pki_pubkey_build_rsa(key, e, n); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("e", ssh_string_data(e), ssh_string_len(e)); + ssh_print_hexa("n", ssh_string_data(n), ssh_string_len(n)); +#endif + ssh_string_burn(e); + ssh_string_free(e); + ssh_string_burn(n); + ssh_string_free(n); + if (rc == SSH_ERROR) { + goto fail; + } + } + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_ECC + { + ssh_string e; + ssh_string i; + int nid; + + i = buffer_get_ssh_string(buffer); + if (i == NULL) { + goto fail; + } + nid = pki_key_ecdsa_nid_from_name(ssh_string_get_char(i)); + ssh_string_free(i); + if (nid == -1) { + goto fail; + } + + + e = buffer_get_ssh_string(buffer); + if (e == NULL) { + goto fail; + } + + rc = pki_pubkey_build_ecdsa(key, nid, e); + ssh_string_burn(e); + ssh_string_free(e); + if (rc < 0) { + goto fail; + } + } + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + ssh_pki_log("Unknown public key protocol %d", type); + goto fail; + } + + *pkey = key; + return SSH_OK; +fail: + ssh_key_free(key); + + return SSH_ERROR; +} + +/** + * @brief Import a base64 formated public key from a memory c-string. + * + * @param[in] b64_key The base64 key to format. + * + * @param[in] type The type of the key to format. + * + * @param[out] pkey A pointer where the key can be stored. You need + * to free the memory. + * + * @return SSH_OK on success, SSH_ERROR on error. + * + * @see ssh_key_free() + */ +int ssh_pki_import_pubkey_base64(const char *b64_key, + enum ssh_keytypes_e type, + ssh_key *pkey) { + ssh_buffer buffer; + ssh_string type_s; + int rc; + + if (b64_key == NULL || pkey == NULL) { + return SSH_ERROR; + } + + buffer = base64_to_bin(b64_key); + if (buffer == NULL) { + return SSH_ERROR; + } + + type_s = buffer_get_ssh_string(buffer); + if (type_s == NULL) { + ssh_buffer_free(buffer); + return SSH_ERROR; + } + ssh_string_free(type_s); + + rc = pki_import_pubkey_buffer(buffer, type, pkey); + ssh_buffer_free(buffer); + + return rc; +} + +/** + * @internal + * + * @brief Import a public key from a ssh string. + * + * @param[in] key_blob The key blob to import as specified in RFC 4253 section + * 6.6 "Public Key Algorithms". + * + * @param[out] pkey A pointer where the key can be stored. You need + * to free the memory. + * + * @return SSH_OK on success, SSH_ERROR on error. + * + * @see ssh_key_free() + */ +int ssh_pki_import_pubkey_blob(const ssh_string key_blob, + ssh_key *pkey) { + ssh_buffer buffer; + ssh_string type_s = NULL; + enum ssh_keytypes_e type; + int rc; + + if (key_blob == NULL || pkey == NULL) { + return SSH_ERROR; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_pki_log("Out of memory!"); + return SSH_ERROR; + } + + rc = buffer_add_data(buffer, ssh_string_data(key_blob), + ssh_string_len(key_blob)); + if (rc < 0) { + ssh_pki_log("Out of memory!"); + goto fail; + } + + type_s = buffer_get_ssh_string(buffer); + if (type_s == NULL) { + ssh_pki_log("Out of memory!"); + goto fail; + } + + type = ssh_key_type_from_name(ssh_string_get_char(type_s)); + if (type == SSH_KEYTYPE_UNKNOWN) { + ssh_pki_log("Unknown key type found!"); + goto fail; + } + ssh_string_free(type_s); + + rc = pki_import_pubkey_buffer(buffer, type, pkey); + + ssh_buffer_free(buffer); + + return rc; +fail: + ssh_buffer_free(buffer); + ssh_string_free(type_s); + + return SSH_ERROR; +} + +/** + * @brief Import a public key from the given filename. + * + * @param[in] filename The path to the public key. + * + * @param[out] pkey A pointer to store the public key. You need to free the + * memory. + * + * @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission + * denied, SSH_ERROR otherwise. + * + * @see ssh_key_free() + */ +int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey) +{ + enum ssh_keytypes_e type; + struct stat sb; + char *key_buf, *p; + const char *q; + FILE *file; + off_t size; + int rc; + + if (pkey == NULL || filename == NULL || *filename == '\0') { + return SSH_ERROR; + } + + rc = stat(filename, &sb); + if (rc < 0) { + ssh_pki_log("Error gettint stat of %s: %s", + filename, strerror(errno)); + switch (errno) { + case ENOENT: + case EACCES: + return SSH_EOF; + } + return SSH_ERROR; + } + + if (sb.st_size > MAX_PUBKEY_SIZE) { + return SSH_ERROR; + } + + file = fopen(filename, "r"); + if (file == NULL) { + ssh_pki_log("Error opening %s: %s", + filename, strerror(errno)); + return SSH_EOF; + } + + key_buf = malloc(sb.st_size + 1); + if (key_buf == NULL) { + fclose(file); + ssh_pki_log("Out of memory!"); + return SSH_ERROR; + } + + size = fread(key_buf, 1, sb.st_size, file); + fclose(file); + + if (size != sb.st_size) { + SAFE_FREE(key_buf); + ssh_pki_log("Error reading %s: %s", + filename, strerror(errno)); + return SSH_ERROR; + } + key_buf[size] = '\0'; + + q = p = key_buf; + while (!isspace((int)*p)) p++; + *p = '\0'; + + type = ssh_key_type_from_name(q); + if (type == SSH_KEYTYPE_UNKNOWN) { + SAFE_FREE(key_buf); + return SSH_ERROR; + } + q = ++p; + while (!isspace((int)*p)) p++; + *p = '\0'; + + rc = ssh_pki_import_pubkey_base64(q, type, pkey); + SAFE_FREE(key_buf); + + return rc; +} + +/** + * @brief Generates a keypair. + * @param[in] type Type of key to create + * @param[in] parameter Parameter to the creation of key: + * rsa : length of the key in bits (e.g. 1024, 2048, 4096) + * dsa : length of the key in bits (e.g. 1024, 2048, 3072) + * ecdsa : bits of the key (e.g. 256, 384, 512) + * @param[out] pkey A pointer to store the private key. You need to free the + * memory. + * @return SSH_OK on success, SSH_ERROR on error. + * @warning Generating a key pair may take some time. + */ + +int ssh_pki_generate(enum ssh_keytypes_e type, int parameter, + ssh_key *pkey){ + int rc; + ssh_key key = ssh_key_new(); + + if (key == NULL) { + return SSH_ERROR; + } + + key->type = type; + key->type_c = ssh_key_type_to_char(type); + key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; + + switch(type){ + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + rc = pki_key_generate_rsa(key, parameter); + if(rc == SSH_ERROR) + goto error; + break; + case SSH_KEYTYPE_DSS: + rc = pki_key_generate_dss(key, parameter); + if(rc == SSH_ERROR) + goto error; + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_ECC + rc = pki_key_generate_ecdsa(key, parameter); + if(rc == SSH_ERROR) + goto error; + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + goto error; + } + + *pkey = key; + return SSH_OK; +error: + ssh_key_free(key); + return SSH_ERROR; +} + +/** + * @brief Create a public key from a private key. + * + * @param[in] privkey The private key to get the public key from. + * + * @param[out] pkey A pointer to store the newly allocated public key. You + * NEED to free the key. + * + * @return A public key, NULL on error. + * + * @see ssh_key_free() + */ +int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey, + ssh_key *pkey) +{ + ssh_key pubkey; + + if (privkey == NULL || !ssh_key_is_private(privkey)) { + return SSH_ERROR; + } + + pubkey = pki_key_dup(privkey, 1); + if (pubkey == NULL) { + return SSH_ERROR; + } + + *pkey = pubkey; + return SSH_OK; +} + +/** + * @internal + * + * @brief Create a key_blob from a public key. + * + * The "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key + * Algorithms" for any of the supported protocol 2 key types. + * + * @param[in] key A public or private key to create the public ssh_string + * from. + * + * @param[out] pblob A pointer to store the newly allocated key blob. You + * NEED to free it. + * + * @return SSH_OK on success, SSH_ERROR otherwise. + * + * @see ssh_string_free() + */ +int ssh_pki_export_pubkey_blob(const ssh_key key, + ssh_string *pblob) +{ + ssh_string blob; + + if (key == NULL) { + return SSH_OK; + } + + blob = pki_publickey_to_blob(key); + if (blob == NULL) { + return SSH_ERROR; + } + + *pblob = blob; + return SSH_OK; +} + +/** + * @brief Convert a public key to a base64 hased key. + * + * @param[in] key The key to hash + * + * @param[out] b64_key A pointer to store the base64 hased key. + * + * @return SSH_OK on success, SSH_ERROR on error. + * + * @see ssh_string_free_char() + */ +int ssh_pki_export_pubkey_base64(const ssh_key key, + char **b64_key) +{ + ssh_string key_blob; + unsigned char *b64; + + if (key == NULL || b64_key == NULL) { + return SSH_ERROR; + } + + key_blob = pki_publickey_to_blob(key); + if (key_blob == NULL) { + return SSH_ERROR; + } + + b64 = bin_to_base64(ssh_string_data(key_blob), ssh_string_len(key_blob)); + ssh_string_free(key_blob); + if (b64 == NULL) { + return SSH_ERROR; + } + + *b64_key = (char *)b64; + + return SSH_OK; +} + +int ssh_pki_export_pubkey_file(const ssh_key key, + const char *filename) +{ + char key_buf[4096]; + char host[256]; + char *b64_key; + char *user; + FILE *fp; + int rc; + + if (key == NULL || filename == NULL || *filename == '\0') { + return SSH_ERROR; + } + + user = ssh_get_local_username(); + if (user == NULL) { + return SSH_ERROR; + } + + rc = gethostname(host, sizeof(host)); + if (rc < 0) { + free(user); + return SSH_ERROR; + } + + rc = ssh_pki_export_pubkey_base64(key, &b64_key); + if (rc < 0) { + free(user); + return SSH_ERROR; + } + + rc = snprintf(key_buf, sizeof(key_buf), + "%s %s %s@%s\n", + key->type_c, + b64_key, + user, + host); + free(user); + free(b64_key); + if (rc < 0) { + return SSH_ERROR; + } + + fp = fopen(filename, "w+"); + if (fp == NULL) { + return SSH_ERROR; + } + rc = fwrite(key_buf, strlen(key_buf), 1, fp); + if (rc != 1 || ferror(fp)) { + fclose(fp); + unlink(filename); + return SSH_ERROR; + } + fclose(fp); + + return SSH_OK; +} + +int ssh_pki_export_pubkey_rsa1(const ssh_key key, + const char *host, + char *rsa1, + size_t rsa1_len) +{ + return pki_export_pubkey_rsa1(key, host, rsa1, rsa1_len); +} + +int ssh_pki_export_signature_blob(const ssh_signature sig, + ssh_string *sig_blob) +{ + ssh_buffer buf = NULL; + ssh_string str; + int rc; + + if (sig == NULL || sig_blob == NULL) { + return SSH_ERROR; + } + + buf = ssh_buffer_new(); + if (buf == NULL) { + return SSH_ERROR; + } + + str = ssh_string_from_char(ssh_key_type_to_char(sig->type)); + if (str == NULL) { + ssh_buffer_free(buf); + return SSH_ERROR; + } + + rc = buffer_add_ssh_string(buf, str); + ssh_string_free(str); + if (rc < 0) { + ssh_buffer_free(buf); + return SSH_ERROR; + } + + str = pki_signature_to_blob(sig); + if (str == NULL) { + ssh_buffer_free(buf); + return SSH_ERROR; + } + + rc = buffer_add_ssh_string(buf, str); + ssh_string_free(str); + if (rc < 0) { + ssh_buffer_free(buf); + return SSH_ERROR; + } + + str = ssh_string_new(buffer_get_rest_len(buf)); + if (str == NULL) { + ssh_buffer_free(buf); + return SSH_ERROR; + } + + ssh_string_fill(str, buffer_get_rest(buf), buffer_get_rest_len(buf)); + ssh_buffer_free(buf); + + *sig_blob = str; + + return SSH_OK; +} + +int ssh_pki_import_signature_blob(const ssh_string sig_blob, + const ssh_key pubkey, + ssh_signature *psig) +{ + ssh_signature sig; + enum ssh_keytypes_e type; + ssh_string str; + ssh_buffer buf; + int rc; + + if (sig_blob == NULL || psig == NULL) { + return SSH_ERROR; + } + + buf = ssh_buffer_new(); + if (buf == NULL) { + return SSH_ERROR; + } + + rc = buffer_add_data(buf, + ssh_string_data(sig_blob), + ssh_string_len(sig_blob)); + if (rc < 0) { + ssh_buffer_free(buf); + return SSH_ERROR; + } + + str = buffer_get_ssh_string(buf); + if (str == NULL) { + ssh_buffer_free(buf); + return SSH_ERROR; + } + + type = ssh_key_type_from_name(ssh_string_get_char(str)); + ssh_string_free(str); + + str = buffer_get_ssh_string(buf); + ssh_buffer_free(buf); + if (str == NULL) { + return SSH_ERROR; + } + + sig = pki_signature_from_blob(pubkey, str, type); + ssh_string_free(str); + if (sig == NULL) { + return SSH_ERROR; + } + + *psig = sig; + return SSH_OK; +} + +int ssh_pki_signature_verify_blob(ssh_session session, + ssh_string sig_blob, + const ssh_key key, + unsigned char *digest, + size_t dlen) +{ + ssh_signature sig; + int rc; + + rc = ssh_pki_import_signature_blob(sig_blob, key, &sig); + if (rc < 0) { + return SSH_ERROR; + } + + ssh_log(session, + SSH_LOG_FUNCTIONS, + "Going to verify a %s type signature", + key->type_c); + + + if (key->type == SSH_KEYTYPE_ECDSA) { +#if HAVE_ECC + unsigned char ehash[EVP_DIGEST_LEN] = {0}; + uint32_t elen; + + evp(key->ecdsa_nid, digest, dlen, ehash, &elen); + + rc = pki_signature_verify(session, + sig, + key, + ehash, + elen); +#endif + } else { + unsigned char hash[SHA_DIGEST_LEN] = {0}; + + sha1(digest, dlen, hash); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Hash to be verified with dsa", hash, SHA_DIGEST_LEN); +#endif + + rc = pki_signature_verify(session, + sig, + key, + hash, + SHA_DIGEST_LEN); + } + + ssh_signature_free(sig); + + return rc; +} + +/* + * This function signs the session id (known as H) as a string then + * the content of sigbuf */ +ssh_string ssh_pki_do_sign(ssh_session session, + ssh_buffer sigbuf, + const ssh_key privkey) { + struct ssh_crypto_struct *crypto = + session->current_crypto ? session->current_crypto : + session->next_crypto; + unsigned char hash[SHA_DIGEST_LEN] = {0}; + ssh_signature sig; + ssh_string sig_blob; + ssh_string session_id; + SHACTX ctx; + int rc; + + if (privkey == NULL || !ssh_key_is_private(privkey)) { + return NULL; + } + + session_id = ssh_string_new(crypto->digest_len); + if (session_id == NULL) { + return NULL; + } + ssh_string_fill(session_id, crypto->session_id, crypto->digest_len); + + ctx = sha1_init(); + if (ctx == NULL) { + ssh_string_free(session_id); + return NULL; + } + + sha1_update(ctx, session_id, ssh_string_len(session_id) + 4); + ssh_string_free(session_id); + + sha1_update(ctx, buffer_get_rest(sigbuf), buffer_get_rest_len(sigbuf)); + sha1_final(hash, ctx); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN); +#endif + + sig = pki_do_sign(privkey, hash, SHA_DIGEST_LEN); + if (sig == NULL) { + return NULL; + } + + rc = ssh_pki_export_signature_blob(sig, &sig_blob); + ssh_signature_free(sig); + if (rc < 0) { + return NULL; + } + + return sig_blob; +} + +#ifndef _WIN32 +ssh_string ssh_pki_do_sign_agent(ssh_session session, + struct ssh_buffer_struct *buf, + const ssh_key pubkey) { + struct ssh_crypto_struct *crypto; + ssh_string session_id; + ssh_string sig_blob; + ssh_buffer sig_buf; + int rc; + + if (session->current_crypto) { + crypto = session->current_crypto; + } else { + crypto = session->next_crypto; + } + + /* prepend session identifier */ + session_id = ssh_string_new(crypto->digest_len); + if (session_id == NULL) { + return NULL; + } + ssh_string_fill(session_id, crypto->session_id, crypto->digest_len); + + sig_buf = ssh_buffer_new(); + if (sig_buf == NULL) { + ssh_string_free(session_id); + return NULL; + } + + rc = buffer_add_ssh_string(sig_buf, session_id); + if (rc < 0) { + ssh_string_free(session_id); + ssh_buffer_free(sig_buf); + return NULL; + } + ssh_string_free(session_id); + + /* append out buffer */ + if (buffer_add_buffer(sig_buf, buf) < 0) { + ssh_buffer_free(sig_buf); + return NULL; + } + + /* create signature */ + sig_blob = ssh_agent_sign_data(session, pubkey, sig_buf); + + ssh_buffer_free(sig_buf); + + return sig_blob; +} +#endif /* _WIN32 */ + +#ifdef WITH_SERVER +ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, + const ssh_key privkey) +{ + struct ssh_crypto_struct *crypto; + unsigned char hash[SHA_DIGEST_LEN] = {0}; + ssh_signature sig; + ssh_string sig_blob; + SHACTX ctx; + int rc; + + if (session == NULL || privkey == NULL || !ssh_key_is_private(privkey)) { + return NULL; + } + crypto = session->current_crypto ? session->current_crypto : + session->next_crypto; + + ctx = sha1_init(); + if (ctx == NULL) { + return NULL; + } + if (crypto->session_id == NULL){ + ssh_set_error(session,SSH_FATAL,"Missing session_id"); + return NULL; + } + sha1_update(ctx, crypto->session_id, crypto->digest_len); + sha1_final(hash, ctx); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN); +#endif + + sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN); + if (sig == NULL) { + return NULL; + } + + rc = ssh_pki_export_signature_blob(sig, &sig_blob); + ssh_signature_free(sig); + if (rc < 0) { + return NULL; + } + + return sig_blob; +} +#endif /* WITH_SERVER */ + +/** + * @} + */ diff --git a/libssh/src/pki_crypto.c b/libssh/src/pki_crypto.c new file mode 100644 index 00000000..0ec05d33 --- /dev/null +++ b/libssh/src/pki_crypto.c @@ -0,0 +1,1403 @@ +/* + * pki_crypto.c - PKI infrastructure using OpenSSL + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 by Aris Adamantiadis + * Copyright (c) 2009-2012 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef _PKI_CRYPTO_H +#define _PKI_CRYPTO_H + +#include +#include +#include +#include + +#ifdef HAVE_OPENSSL_EC_H +#include +#endif +#ifdef HAVE_OPENSSL_ECDSA_H +#include +#endif + + +#include "libssh/priv.h" +#include "libssh/libssh.h" +#include "libssh/buffer.h" +#include "libssh/session.h" +#include "libssh/pki.h" +#include "libssh/pki_priv.h" +#include "libssh/dh.h" + +struct pem_get_password_struct { + ssh_auth_callback fn; + void *data; +}; + +static int pem_get_password(char *buf, int size, int rwflag, void *userdata) { + struct pem_get_password_struct *pgp = userdata; + + (void) rwflag; /* unused */ + + if (buf == NULL) { + return 0; + } + + memset(buf, '\0', size); + if (pgp) { + int rc; + + rc = pgp->fn("Passphrase for private key:", + buf, size, 0, 0, + pgp->data); + if (rc == 0) { + return strlen(buf); + } + } + + return 0; +} + +#ifdef HAVE_OPENSSL_ECC +static int pki_key_ecdsa_to_nid(EC_KEY *k) +{ + const EC_GROUP *g = EC_KEY_get0_group(k); + int nid; + + nid = EC_GROUP_get_curve_name(g); + if (nid) { + return nid; + } + + return -1; +} + +static const char *pki_key_ecdsa_nid_to_name(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return "ecdsa-sha2-nistp256"; + case NID_secp384r1: + return "ecdsa-sha2-nistp384"; + case NID_secp521r1: + return "ecdsa-sha2-nistp521"; + default: + break; + } + + return "unknown"; +} + +static const char *pki_key_ecdsa_nid_to_char(int nid) +{ + switch (nid) { + case NID_X9_62_prime256v1: + return "nistp256"; + case NID_secp384r1: + return "nistp384"; + case NID_secp521r1: + return "nistp521"; + default: + break; + } + + return "unknown"; +} + +int pki_key_ecdsa_nid_from_name(const char *name) +{ + if (strcmp(name, "nistp256") == 0) { + return NID_X9_62_prime256v1; + } else if (strcmp(name, "nistp384") == 0) { + return NID_secp384r1; + } else if (strcmp(name, "nistp521") == 0) { + return NID_secp521r1; + } + + return -1; +} + +static ssh_string make_ecpoint_string(const EC_GROUP *g, + const EC_POINT *p) +{ + ssh_string s; + size_t len; + + len = EC_POINT_point2oct(g, + p, + POINT_CONVERSION_UNCOMPRESSED, + NULL, + 0, + NULL); + if (len == 0) { + return NULL; + } + + s = ssh_string_new(len); + if (s == NULL) { + return NULL; + } + + len = EC_POINT_point2oct(g, + p, + POINT_CONVERSION_UNCOMPRESSED, + ssh_string_data(s), + ssh_string_len(s), + NULL); + if (len != ssh_string_len(s)) { + ssh_string_free(s); + return NULL; + } + + return s; +} + +int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e) +{ + EC_POINT *p; + const EC_GROUP *g; + int ok; + + key->ecdsa_nid = nid; + key->type_c = pki_key_ecdsa_nid_to_name(nid); + + key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid); + if (key->ecdsa == NULL) { + return -1; + } + + g = EC_KEY_get0_group(key->ecdsa); + + p = EC_POINT_new(g); + if (p == NULL) { + return -1; + } + + ok = EC_POINT_oct2point(g, + p, + ssh_string_data(e), + ssh_string_len(e), + NULL); + if (!ok) { + EC_POINT_free(p); + return -1; + } + + ok = EC_KEY_set_public_key(key->ecdsa, p); + if (!ok) { + EC_POINT_free(p); + } + + return 0; +} +#endif + +ssh_key pki_key_dup(const ssh_key key, int demote) +{ + ssh_key new; + + new = ssh_key_new(); + if (new == NULL) { + return NULL; + } + + new->type = key->type; + new->type_c = key->type_c; + if (demote) { + new->flags = SSH_KEY_FLAG_PUBLIC; + } else { + new->flags = key->flags; + } + + switch (key->type) { + case SSH_KEYTYPE_DSS: + new->dsa = DSA_new(); + if (new->dsa == NULL) { + goto fail; + } + + /* + * p = public prime number + * q = public 160-bit subprime, q | p-1 + * g = public generator of subgroup + * pub_key = public key y = g^x + * priv_key = private key x + */ + new->dsa->p = BN_dup(key->dsa->p); + if (new->dsa->p == NULL) { + goto fail; + } + + new->dsa->q = BN_dup(key->dsa->q); + if (new->dsa->q == NULL) { + goto fail; + } + + new->dsa->g = BN_dup(key->dsa->g); + if (new->dsa->g == NULL) { + goto fail; + } + + new->dsa->pub_key = BN_dup(key->dsa->pub_key); + if (new->dsa->pub_key == NULL) { + goto fail; + } + + if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { + new->dsa->priv_key = BN_dup(key->dsa->priv_key); + if (new->dsa->priv_key == NULL) { + goto fail; + } + } + + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + new->rsa = RSA_new(); + if (new->rsa == NULL) { + goto fail; + } + + /* + * n = public modulus + * e = public exponent + * d = private exponent + * p = secret prime factor + * q = secret prime factor + * dmp1 = d mod (p-1) + * dmq1 = d mod (q-1) + * iqmp = q^-1 mod p + */ + new->rsa->n = BN_dup(key->rsa->n); + if (new->rsa->n == NULL) { + goto fail; + } + + new->rsa->e = BN_dup(key->rsa->e); + if (new->rsa->e == NULL) { + goto fail; + } + + if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { + new->rsa->d = BN_dup(key->rsa->d); + if (new->rsa->d == NULL) { + goto fail; + } + + /* p, q, dmp1, dmq1 and iqmp may be NULL in private keys, but the + * RSA operations are much faster when these values are available. + */ + if (key->rsa->p != NULL) { + new->rsa->p = BN_dup(key->rsa->p); + if (new->rsa->p == NULL) { + goto fail; + } + } + + if (key->rsa->q != NULL) { + new->rsa->q = BN_dup(key->rsa->q); + if (new->rsa->q == NULL) { + goto fail; + } + } + + if (key->rsa->dmp1 != NULL) { + new->rsa->dmp1 = BN_dup(key->rsa->dmp1); + if (new->rsa->dmp1 == NULL) { + goto fail; + } + } + + if (key->rsa->dmq1 != NULL) { + new->rsa->dmq1 = BN_dup(key->rsa->dmq1); + if (new->rsa->dmq1 == NULL) { + goto fail; + } + } + + if (key->rsa->iqmp != NULL) { + new->rsa->iqmp = BN_dup(key->rsa->iqmp); + if (new->rsa->iqmp == NULL) { + goto fail; + } + } + } + + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + /* privkey -> pubkey */ + if (demote && ssh_key_is_private(key)) { + const EC_POINT *p; + int ok; + + new->ecdsa_nid = key->ecdsa_nid; + + new->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid); + if (new->ecdsa == NULL) { + goto fail; + } + + p = EC_KEY_get0_public_key(key->ecdsa); + if (p == NULL) { + goto fail; + } + + ok = EC_KEY_set_public_key(new->ecdsa, p); + if (!ok) { + goto fail; + } + } else { + new->ecdsa = EC_KEY_dup(key->ecdsa); + } + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + ssh_key_free(new); + return NULL; + } + + return new; +fail: + ssh_key_free(new); + return NULL; +} + +int pki_key_generate_rsa(ssh_key key, int parameter){ + key->rsa = RSA_generate_key(parameter, 65537, NULL, NULL); + if(key->rsa == NULL) + return SSH_ERROR; + return SSH_OK; +} + +int pki_key_generate_dss(ssh_key key, int parameter){ + int rc; + key->dsa = DSA_generate_parameters(parameter, NULL, 0, NULL, NULL, + NULL, NULL); + if(key->dsa == NULL){ + return SSH_ERROR; + } + rc = DSA_generate_key(key->dsa); + if (rc != 1){ + DSA_free(key->dsa); + key->dsa=NULL; + return SSH_ERROR; + } + return SSH_OK; +} + +#ifdef HAVE_OPENSSL_ECC +int pki_key_generate_ecdsa(ssh_key key, int parameter) { + int nid; + int ok; + + switch (parameter) { + case 384: + nid = NID_secp384r1; + case 512: + nid = NID_secp521r1; + case 256: + default: + nid = NID_X9_62_prime256v1; + } + + key->ecdsa_nid = nid; + key->type = SSH_KEYTYPE_ECDSA; + key->type_c = pki_key_ecdsa_nid_to_name(nid); + + key->ecdsa = EC_KEY_new_by_curve_name(nid); + if (key->ecdsa == NULL) { + return SSH_ERROR; + } + + ok = EC_KEY_generate_key(key->ecdsa); + if (!ok) { + EC_KEY_free(key->ecdsa); + return SSH_ERROR; + } + + EC_KEY_set_asn1_flag(key->ecdsa, OPENSSL_EC_NAMED_CURVE); + + return SSH_OK; +} +#endif + +int pki_key_compare(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + switch (k1->type) { + case SSH_KEYTYPE_DSS: + if (DSA_size(k1->dsa) != DSA_size(k2->dsa)) { + return 1; + } + if (bignum_cmp(k1->dsa->p, k2->dsa->p) != 0) { + return 1; + } + if (bignum_cmp(k1->dsa->q, k2->dsa->q) != 0) { + return 1; + } + if (bignum_cmp(k1->dsa->g, k2->dsa->g) != 0) { + return 1; + } + if (bignum_cmp(k1->dsa->pub_key, k2->dsa->pub_key) != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (bignum_cmp(k1->dsa->priv_key, k2->dsa->priv_key) != 0) { + return 1; + } + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (RSA_size(k1->rsa) != RSA_size(k2->rsa)) { + return 1; + } + if (bignum_cmp(k1->rsa->e, k2->rsa->e) != 0) { + return 1; + } + if (bignum_cmp(k1->rsa->n, k2->rsa->n) != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (bignum_cmp(k1->rsa->p, k2->rsa->p) != 0) { + return 1; + } + + if (bignum_cmp(k1->rsa->q, k2->rsa->q) != 0) { + return 1; + } + } + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + { + const EC_POINT *p1 = EC_KEY_get0_public_key(k1->ecdsa); + const EC_POINT *p2 = EC_KEY_get0_public_key(k2->ecdsa); + const EC_GROUP *g1 = EC_KEY_get0_group(k1->ecdsa); + const EC_GROUP *g2 = EC_KEY_get0_group(k2->ecdsa); + + if (p1 == NULL || p2 == NULL) { + return 1; + } + + if (EC_GROUP_cmp(g1, g2, NULL) != 0) { + return 1; + } + + if (EC_POINT_cmp(g1, p1, p2, NULL) != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (bignum_cmp(EC_KEY_get0_private_key(k1->ecdsa), + EC_KEY_get0_private_key(k2->ecdsa))) { + return 1; + } + } + + break; + } +#endif + case SSH_KEYTYPE_UNKNOWN: + return 1; + } + + return 0; +} + +ssh_key pki_private_key_from_base64(const char *b64_key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data) { + BIO *mem = NULL; + DSA *dsa = NULL; + RSA *rsa = NULL; + ssh_key key; + enum ssh_keytypes_e type; +#ifdef HAVE_OPENSSL_ECC + EC_KEY *ecdsa = NULL; +#else + void *ecdsa = NULL; +#endif + + /* needed for openssl initialization */ + if (ssh_init() < 0) { + return NULL; + } + + type = pki_privatekey_type_from_string(b64_key); + if (type == SSH_KEYTYPE_UNKNOWN) { + ssh_pki_log("Unknown or invalid private key."); + return NULL; + } + + mem = BIO_new_mem_buf((void*)b64_key, -1); + + switch (type) { + case SSH_KEYTYPE_DSS: + if (passphrase == NULL) { + if (auth_fn) { + struct pem_get_password_struct pgp = { auth_fn, auth_data }; + + dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, pem_get_password, &pgp); + } else { + /* openssl uses its own callback to get the passphrase here */ + dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, NULL, NULL); + } + } else { + dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, NULL, (void *) passphrase); + } + + BIO_free(mem); + + if (dsa == NULL) { + ssh_pki_log("Parsing private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (passphrase == NULL) { + if (auth_fn) { + struct pem_get_password_struct pgp = { auth_fn, auth_data }; + + rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, pem_get_password, &pgp); + } else { + /* openssl uses its own callback to get the passphrase here */ + rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL); + } + } else { + rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, (void *) passphrase); + } + + BIO_free(mem); + + if (rsa == NULL) { + ssh_pki_log("Parsing private key: %s", + ERR_error_string(ERR_get_error(),NULL)); + return NULL; + } + + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + if (passphrase == NULL) { + if (auth_fn) { + struct pem_get_password_struct pgp = { auth_fn, auth_data }; + + ecdsa = PEM_read_bio_ECPrivateKey(mem, NULL, pem_get_password, &pgp); + } else { + /* openssl uses its own callback to get the passphrase here */ + ecdsa = PEM_read_bio_ECPrivateKey(mem, NULL, NULL, NULL); + } + } else { + ecdsa = PEM_read_bio_ECPrivateKey(mem, NULL, NULL, (void *) passphrase); + } + + BIO_free(mem); + + if (ecdsa == NULL) { + ssh_pki_log("Parsing private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + BIO_free(mem); + ssh_pki_log("Unkown or invalid private key type %d", type); + return NULL; + } + + key = ssh_key_new(); + if (key == NULL) { + goto fail; + } + + key->type = type; + key->type_c = ssh_key_type_to_char(type); + key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; + key->dsa = dsa; + key->rsa = rsa; + key->ecdsa = ecdsa; +#ifdef HAVE_OPENSSL_ECC + if (key->type == SSH_KEYTYPE_ECDSA) { + key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa); + key->type_c = pki_key_ecdsa_nid_to_name(key->ecdsa_nid); + } +#endif + + return key; +fail: + ssh_key_free(key); + DSA_free(dsa); + RSA_free(rsa); +#ifdef HAVE_OPENSSL_ECC + EC_KEY_free(ecdsa); +#endif + + return NULL; +} + +int pki_pubkey_build_dss(ssh_key key, + ssh_string p, + ssh_string q, + ssh_string g, + ssh_string pubkey) { + key->dsa = DSA_new(); + if (key->dsa == NULL) { + return SSH_ERROR; + } + + key->dsa->p = make_string_bn(p); + key->dsa->q = make_string_bn(q); + key->dsa->g = make_string_bn(g); + key->dsa->pub_key = make_string_bn(pubkey); + if (key->dsa->p == NULL || + key->dsa->q == NULL || + key->dsa->g == NULL || + key->dsa->pub_key == NULL) { + DSA_free(key->dsa); + return SSH_ERROR; + } + + return SSH_OK; +} + +int pki_pubkey_build_rsa(ssh_key key, + ssh_string e, + ssh_string n) { + key->rsa = RSA_new(); + if (key->rsa == NULL) { + return SSH_ERROR; + } + + key->rsa->e = make_string_bn(e); + key->rsa->n = make_string_bn(n); + if (key->rsa->e == NULL || + key->rsa->n == NULL) { + RSA_free(key->rsa); + return SSH_ERROR; + } + + return SSH_OK; +} + +ssh_string pki_publickey_to_blob(const ssh_key key) +{ + ssh_buffer buffer; + ssh_string type_s; + ssh_string str = NULL; + ssh_string e = NULL; + ssh_string n = NULL; + ssh_string p = NULL; + ssh_string g = NULL; + ssh_string q = NULL; + int rc; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + return NULL; + } + + type_s = ssh_string_from_char(key->type_c); + if (type_s == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + + rc = buffer_add_ssh_string(buffer, type_s); + ssh_string_free(type_s); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + switch (key->type) { + case SSH_KEYTYPE_DSS: + p = make_bignum_string(key->dsa->p); + if (p == NULL) { + goto fail; + } + + q = make_bignum_string(key->dsa->q); + if (q == NULL) { + goto fail; + } + + g = make_bignum_string(key->dsa->g); + if (g == NULL) { + goto fail; + } + + n = make_bignum_string(key->dsa->pub_key); + if (n == NULL) { + goto fail; + } + + if (buffer_add_ssh_string(buffer, p) < 0) { + goto fail; + } + if (buffer_add_ssh_string(buffer, q) < 0) { + goto fail; + } + if (buffer_add_ssh_string(buffer, g) < 0) { + goto fail; + } + if (buffer_add_ssh_string(buffer, n) < 0) { + goto fail; + } + + ssh_string_burn(p); + ssh_string_free(p); + p = NULL; + ssh_string_burn(g); + ssh_string_free(g); + g = NULL; + ssh_string_burn(q); + ssh_string_free(q); + q = NULL; + ssh_string_burn(n); + ssh_string_free(n); + n = NULL; + + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + e = make_bignum_string(key->rsa->e); + if (e == NULL) { + goto fail; + } + + n = make_bignum_string(key->rsa->n); + if (n == NULL) { + goto fail; + } + + if (buffer_add_ssh_string(buffer, e) < 0) { + goto fail; + } + if (buffer_add_ssh_string(buffer, n) < 0) { + goto fail; + } + + ssh_string_burn(e); + ssh_string_free(e); + e = NULL; + ssh_string_burn(n); + ssh_string_free(n); + n = NULL; + + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + rc = buffer_reinit(buffer); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_name(key->ecdsa_nid)); + if (type_s == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + + rc = buffer_add_ssh_string(buffer, type_s); + ssh_string_free(type_s); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_char(key->ecdsa_nid)); + if (type_s == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + + rc = buffer_add_ssh_string(buffer, type_s); + ssh_string_free(type_s); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + e = make_ecpoint_string(EC_KEY_get0_group(key->ecdsa), + EC_KEY_get0_public_key(key->ecdsa)); + if (e == NULL) { + return NULL; + } + + rc = buffer_add_ssh_string(buffer, e); + if (rc < 0) { + goto fail; + } + + ssh_string_burn(e); + ssh_string_free(e); + e = NULL; + + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + goto fail; + } + + str = ssh_string_new(buffer_get_rest_len(buffer)); + if (str == NULL) { + goto fail; + } + + rc = ssh_string_fill(str, buffer_get_rest(buffer), buffer_get_rest_len(buffer)); + if (rc < 0) { + goto fail; + } + ssh_buffer_free(buffer); + + return str; +fail: + ssh_buffer_free(buffer); + ssh_string_burn(str); + ssh_string_free(str); + ssh_string_burn(e); + ssh_string_free(e); + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(g); + ssh_string_free(g); + ssh_string_burn(q); + ssh_string_free(q); + ssh_string_burn(n); + ssh_string_free(n); + + return NULL; +} + +int pki_export_pubkey_rsa1(const ssh_key key, + const char *host, + char *rsa1, + size_t rsa1_len) +{ + char *e; + char *n; + int rsa_size = RSA_size(key->rsa); + + e = bignum_bn2dec(key->rsa->e); + if (e == NULL) { + return SSH_ERROR; + } + + n = bignum_bn2dec(key->rsa->n); + if (n == NULL) { + OPENSSL_free(e); + return SSH_ERROR; + } + + snprintf(rsa1, rsa1_len, + "%s %d %s %s\n", + host, rsa_size << 3, e, n); + OPENSSL_free(e); + OPENSSL_free(n); + + return SSH_OK; +} + +/** + * @internal + * + * @brief Compute a digital signature. + * + * @param[in] digest The message digest. + * + * @param[in] dlen The length of the digest. + * + * @param[in] privkey The private rsa key to use for signing. + * + * @return A newly allocated rsa sig blob or NULL on error. + */ +static ssh_string _RSA_do_sign(const unsigned char *digest, + int dlen, + RSA *privkey) +{ + ssh_string sig_blob; + unsigned char *sig; + unsigned int slen; + int ok; + + sig = malloc(RSA_size(privkey)); + if (sig == NULL) { + return NULL; + } + + ok = RSA_sign(NID_sha1, digest, dlen, sig, &slen, privkey); + if (!ok) { + SAFE_FREE(sig); + return NULL; + } + + sig_blob = ssh_string_new(slen); + if (sig_blob == NULL) { + SAFE_FREE(sig); + return NULL; + } + + ssh_string_fill(sig_blob, sig, slen); + memset(sig, 'd', slen); + SAFE_FREE(sig); + + return sig_blob; +} + +ssh_string pki_signature_to_blob(const ssh_signature sig) +{ + char buffer[40] = {0}; + ssh_string sig_blob = NULL; + ssh_string r; + ssh_string s; + + switch(sig->type) { + case SSH_KEYTYPE_DSS: + r = make_bignum_string(sig->dsa_sig->r); + if (r == NULL) { + return NULL; + } + s = make_bignum_string(sig->dsa_sig->s); + if (s == NULL) { + ssh_string_free(r); + return NULL; + } + + memcpy(buffer, + ((char *)ssh_string_data(r)) + ssh_string_len(r) - 20, + 20); + memcpy(buffer + 20, + ((char *)ssh_string_data(s)) + ssh_string_len(s) - 20, + 20); + + ssh_string_free(r); + ssh_string_free(s); + + sig_blob = ssh_string_new(40); + if (sig_blob == NULL) { + return NULL; + } + + ssh_string_fill(sig_blob, buffer, 40); + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + sig_blob = ssh_string_copy(sig->rsa_sig); + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + r = make_bignum_string(sig->ecdsa_sig->r); + if (r == NULL) { + return NULL; + } + s = make_bignum_string(sig->ecdsa_sig->s); + if (s == NULL) { + ssh_string_free(r); + return NULL; + } + + memcpy(buffer, + ((char *)ssh_string_data(r)) + ssh_string_len(r) - 20, + 20); + memcpy(buffer + 20, + ((char *)ssh_string_data(s)) + ssh_string_len(s) - 20, + 20); + + ssh_string_free(r); + ssh_string_free(s); + + sig_blob = ssh_string_new(40); + if (sig_blob == NULL) { + return NULL; + } + + ssh_string_fill(sig_blob, buffer, 40); + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + ssh_pki_log("Unknown signature key type: %d", sig->type); + return NULL; + } + + return sig_blob; +} + +ssh_signature pki_signature_from_blob(const ssh_key pubkey, + const ssh_string sig_blob, + enum ssh_keytypes_e type) +{ + ssh_signature sig; + ssh_string r; + ssh_string s; + size_t len; + size_t rsalen; + + sig = ssh_signature_new(); + if (sig == NULL) { + return NULL; + } + + sig->type = type; + + len = ssh_string_len(sig_blob); + + switch(type) { + case SSH_KEYTYPE_DSS: + /* 40 is the dual signature blob len. */ + if (len != 40) { + ssh_pki_log("Signature has wrong size: %lu", + (unsigned long)len); + ssh_signature_free(sig); + return NULL; + } + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("r", ssh_string_data(sig_blob), 20); + ssh_print_hexa("s", (unsigned char *)ssh_string_data(sig_blob) + 20, 20); +#endif + + sig->dsa_sig = DSA_SIG_new(); + if (sig->dsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + + r = ssh_string_new(20); + if (r == NULL) { + ssh_signature_free(sig); + return NULL; + } + ssh_string_fill(r, ssh_string_data(sig_blob), 20); + + sig->dsa_sig->r = make_string_bn(r); + ssh_string_free(r); + if (sig->dsa_sig->r == NULL) { + ssh_signature_free(sig); + return NULL; + } + + s = ssh_string_new(20); + if (s == NULL) { + ssh_signature_free(sig); + return NULL; + } + ssh_string_fill(s, (char *)ssh_string_data(sig_blob) + 20, 20); + + sig->dsa_sig->s = make_string_bn(s); + ssh_string_free(s); + if (sig->dsa_sig->s == NULL) { + ssh_signature_free(sig); + return NULL; + } + + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + rsalen = RSA_size(pubkey->rsa); + + if (len > rsalen) { + ssh_pki_log("Signature is to big size: %lu", + (unsigned long)len); + ssh_signature_free(sig); + return NULL; + } + + if (len < rsalen) { + ssh_pki_log("RSA signature len %lu < %lu", + (unsigned long)len, (unsigned long)rsalen); + } + +#ifdef DEBUG_CRYPTO + ssh_pki_log("RSA signature len: %lu", (unsigned long)len); + ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); +#endif + sig->rsa_sig = ssh_string_copy(sig_blob); + if (sig->rsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + sig->ecdsa_sig = ECDSA_SIG_new(); + if (sig->ecdsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + + { /* build ecdsa siganature */ + ssh_buffer b; + uint32_t rlen; + int rc; + + b = ssh_buffer_new(); + if (b == NULL) { + ssh_signature_free(sig); + return NULL; + } + + rc = buffer_add_data(b, + ssh_string_data(sig_blob), + ssh_string_len(sig_blob)); + if (rc < 0) { + ssh_buffer_free(b); + ssh_signature_free(sig); + return NULL; + } + + r = buffer_get_ssh_string(b); + if (r == NULL) { + ssh_buffer_free(b); + ssh_signature_free(sig); + return NULL; + } + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("r", ssh_string_data(r), ssh_string_len(r)); +#endif + + sig->ecdsa_sig->r = make_string_bn(r); + ssh_string_burn(r); + ssh_string_free(r); + if (sig->ecdsa_sig->r == NULL) { + ssh_buffer_free(b); + ssh_signature_free(sig); + return NULL; + } + + s = buffer_get_ssh_string(b); + rlen = buffer_get_rest_len(b); + ssh_buffer_free(b); + if (s == NULL) { + ssh_signature_free(sig); + return NULL; + } + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("s", ssh_string_data(s), ssh_string_len(s)); +#endif + + sig->ecdsa_sig->s = make_string_bn(s); + ssh_string_burn(s); + ssh_string_free(s); + if (sig->ecdsa_sig->s == NULL) { + ssh_signature_free(sig); + return NULL; + } + + if (rlen != 0) { + ssh_pki_log("Signature has remaining bytes in inner " + "sigblob: %lu", + (unsigned long)rlen); + ssh_signature_free(sig); + return NULL; + } + } + + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + ssh_pki_log("Unknown signature type"); + ssh_signature_free(sig); + return NULL; + } + + return sig; +} + +int pki_signature_verify(ssh_session session, + const ssh_signature sig, + const ssh_key key, + const unsigned char *hash, + size_t hlen) +{ + int rc; + + switch(key->type) { + case SSH_KEYTYPE_DSS: + rc = DSA_do_verify(hash, + hlen, + sig->dsa_sig, + key->dsa); + if (rc <= 0) { + ssh_set_error(session, + SSH_FATAL, + "DSA error: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + rc = RSA_verify(NID_sha1, + hash, + hlen, + ssh_string_data(sig->rsa_sig), + ssh_string_len(sig->rsa_sig), + key->rsa); + if (rc <= 0) { + ssh_set_error(session, + SSH_FATAL, + "RSA error: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + rc = ECDSA_do_verify(hash, + hlen, + sig->ecdsa_sig, + key->ecdsa); + if (rc <= 0) { + ssh_set_error(session, + SSH_FATAL, + "ECDSA error: %s", + ERR_error_string(ERR_get_error(), NULL)); + return SSH_ERROR; + } + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + ssh_set_error(session, SSH_FATAL, "Unknown public key type"); + return SSH_ERROR; + } + + return SSH_OK; +} + +ssh_signature pki_do_sign(const ssh_key privkey, + const unsigned char *hash, + size_t hlen) { + ssh_signature sig; + + sig = ssh_signature_new(); + if (sig == NULL) { + return NULL; + } + + sig->type = privkey->type; + + switch(privkey->type) { + case SSH_KEYTYPE_DSS: + sig->dsa_sig = DSA_do_sign(hash, hlen, privkey->dsa); + if (sig->dsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + +#ifdef DEBUG_CRYPTO + ssh_print_bignum("r", sig->dsa_sig->r); + ssh_print_bignum("s", sig->dsa_sig->s); +#endif + + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + sig->rsa_sig = _RSA_do_sign(hash, hlen, privkey->rsa); + if (sig->rsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + sig->dsa_sig = NULL; + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + sig->ecdsa_sig = ECDSA_do_sign(hash, hlen, privkey->ecdsa); + if (sig->ecdsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + +# ifdef DEBUG_CRYPTO + ssh_print_bignum("r", sig->ecdsa_sig->r); + ssh_print_bignum("s", sig->ecdsa_sig->s); +# endif /* DEBUG_CRYPTO */ + + break; +#endif /* HAVE_OPENSSL_ECC */ + case SSH_KEYTYPE_UNKNOWN: + ssh_signature_free(sig); + return NULL; + } + + return sig; +} + +#ifdef WITH_SERVER +ssh_signature pki_do_sign_sessionid(const ssh_key key, + const unsigned char *hash, + size_t hlen) +{ + ssh_signature sig; + + sig = ssh_signature_new(); + if (sig == NULL) { + return NULL; + } + sig->type = key->type; + + switch(key->type) { + case SSH_KEYTYPE_DSS: + sig->dsa_sig = DSA_do_sign(hash, hlen, key->dsa); + if (sig->dsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + sig->rsa_sig = _RSA_do_sign(hash, hlen, key->rsa); + if (sig->rsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_OPENSSL_ECC + sig->ecdsa_sig = ECDSA_do_sign(hash, hlen, key->ecdsa); + if (sig->ecdsa_sig == NULL) { + ssh_signature_free(sig); + return NULL; + } + break; +#endif + case SSH_KEYTYPE_UNKNOWN: + ssh_signature_free(sig); + return NULL; + } + + return sig; +} +#endif /* WITH_SERVER */ + +#endif /* _PKI_CRYPTO_H */ diff --git a/libssh/src/pki_gcrypt.c b/libssh/src/pki_gcrypt.c new file mode 100644 index 00000000..a44ed73a --- /dev/null +++ b/libssh/src/pki_gcrypt.c @@ -0,0 +1,1699 @@ +/* + * pki_gcrypt.c private and public key handling using gcrypt. + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2009 Aris Adamantiadis + * Copyright (c) 2009-2011 Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_LIBGCRYPT + +#include +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/buffer.h" +#include "libssh/session.h" +#include "libssh/wrapper.h" +#include "libssh/misc.h" +#include "libssh/pki.h" +#include "libssh/pki_priv.h" + +#define MAXLINESIZE 80 +#define RSA_HEADER_BEGIN "-----BEGIN RSA PRIVATE KEY-----" +#define RSA_HEADER_END "-----END RSA PRIVATE KEY-----" +#define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----" +#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----" + +#define MAX_KEY_SIZE 32 +#define MAX_PASSPHRASE_SIZE 1024 +#define ASN1_INTEGER 2 +#define ASN1_SEQUENCE 48 +#define PKCS5_SALT_LEN 8 + +static int load_iv(const char *header, unsigned char *iv, int iv_len) { + int i; + int j; + int k; + + memset(iv, 0, iv_len); + for (i = 0; i < iv_len; i++) { + if ((header[2*i] >= '0') && (header[2*i] <= '9')) + j = header[2*i] - '0'; + else if ((header[2*i] >= 'A') && (header[2*i] <= 'F')) + j = header[2*i] - 'A' + 10; + else if ((header[2*i] >= 'a') && (header[2*i] <= 'f')) + j = header[2*i] - 'a' + 10; + else + return -1; + if ((header[2*i+1] >= '0') && (header[2*i+1] <= '9')) + k = header[2*i+1] - '0'; + else if ((header[2*i+1] >= 'A') && (header[2*i+1] <= 'F')) + k = header[2*i+1] - 'A' + 10; + else if ((header[2*i+1] >= 'a') && (header[2*i+1] <= 'f')) + k = header[2*i+1] - 'a' + 10; + else + return -1; + iv[i] = (j << 4) + k; + } + return 0; +} + +static uint32_t char_to_u32(unsigned char *data, uint32_t size) { + uint32_t ret; + uint32_t i; + + for (i = 0, ret = 0; i < size; ret = ret << 8, ret += data[i++]) + ; + return ret; +} + +static uint32_t asn1_get_len(ssh_buffer buffer) { + uint32_t len; + unsigned char tmp[4]; + + if (buffer_get_data(buffer,tmp,1) == 0) { + return 0; + } + + if (tmp[0] > 127) { + len = tmp[0] & 127; + if (len > 4) { + return 0; /* Length doesn't fit in u32. Can this really happen? */ + } + if (buffer_get_data(buffer,tmp,len) == 0) { + return 0; + } + len = char_to_u32(tmp, len); + } else { + len = char_to_u32(tmp, 1); + } + + return len; +} + +static ssh_string asn1_get_int(ssh_buffer buffer) { + ssh_string str; + unsigned char type; + uint32_t size; + + if (buffer_get_data(buffer, &type, 1) == 0 || type != ASN1_INTEGER) { + return NULL; + } + size = asn1_get_len(buffer); + if (size == 0) { + return NULL; + } + + str = ssh_string_new(size); + if (str == NULL) { + return NULL; + } + + if (buffer_get_data(buffer, ssh_string_data(str), size) == 0) { + ssh_string_free(str); + return NULL; + } + + return str; +} + +static int asn1_check_sequence(ssh_buffer buffer) { + unsigned char *j = NULL; + unsigned char tmp; + int i; + uint32_t size; + uint32_t padding; + + if (buffer_get_data(buffer, &tmp, 1) == 0 || tmp != ASN1_SEQUENCE) { + return 0; + } + + size = asn1_get_len(buffer); + if ((padding = ssh_buffer_get_len(buffer) - buffer->pos - size) > 0) { + for (i = ssh_buffer_get_len(buffer) - buffer->pos - size, + j = (unsigned char*)ssh_buffer_get_begin(buffer) + size + buffer->pos; + i; + i--, j++) + { + if (*j != padding) { /* padding is allowed */ + return 0; /* but nothing else */ + } + } + } + + return 1; +} + +static int passphrase_to_key(char *data, unsigned int datalen, + unsigned char *salt, unsigned char *key, unsigned int keylen) { + MD5CTX md; + unsigned char digest[MD5_DIGEST_LEN] = {0}; + unsigned int i; + unsigned int j; + unsigned int md_not_empty; + + for (j = 0, md_not_empty = 0; j < keylen; ) { + md = md5_init(); + if (md == NULL) { + return -1; + } + + if (md_not_empty) { + md5_update(md, digest, MD5_DIGEST_LEN); + } else { + md_not_empty = 1; + } + + md5_update(md, data, datalen); + if (salt) { + md5_update(md, salt, PKCS5_SALT_LEN); + } + md5_final(digest, md); + + for (i = 0; j < keylen && i < MD5_DIGEST_LEN; j++, i++) { + if (key) { + key[j] = digest[i]; + } + } + } + + return 0; +} + +static int privatekey_decrypt(int algo, int mode, unsigned int key_len, + unsigned char *iv, unsigned int iv_len, + ssh_buffer data, ssh_auth_callback cb, + void *userdata, + const char *desc) +{ + char passphrase[MAX_PASSPHRASE_SIZE] = {0}; + unsigned char key[MAX_KEY_SIZE] = {0}; + unsigned char *tmp = NULL; + gcry_cipher_hd_t cipher; + int rc = -1; + + if (!algo) { + return -1; + } + + if (cb) { + rc = (*cb)(desc, passphrase, MAX_PASSPHRASE_SIZE, 0, 0, userdata); + if (rc < 0) { + return -1; + } + } else if (cb == NULL && userdata != NULL) { + snprintf(passphrase, MAX_PASSPHRASE_SIZE, "%s", (char *) userdata); + } + + if (passphrase_to_key(passphrase, strlen(passphrase), iv, key, key_len) < 0) { + return -1; + } + + if (gcry_cipher_open(&cipher, algo, mode, 0) + || gcry_cipher_setkey(cipher, key, key_len) + || gcry_cipher_setiv(cipher, iv, iv_len) + || (tmp = malloc(ssh_buffer_get_len(data) * sizeof (char))) == NULL + || gcry_cipher_decrypt(cipher, tmp, ssh_buffer_get_len(data), + ssh_buffer_get_begin(data), ssh_buffer_get_len(data))) { + gcry_cipher_close(cipher); + return -1; + } + + memcpy(ssh_buffer_get_begin(data), tmp, ssh_buffer_get_len(data)); + + SAFE_FREE(tmp); + gcry_cipher_close(cipher); + + return 0; +} + +static int privatekey_dek_header(const char *header, unsigned int header_len, + int *algo, int *mode, unsigned int *key_len, unsigned char **iv, + unsigned int *iv_len) { + unsigned int iv_pos; + + if (header_len > 13 && !strncmp("DES-EDE3-CBC", header, 12)) + { + *algo = GCRY_CIPHER_3DES; + iv_pos = 13; + *mode = GCRY_CIPHER_MODE_CBC; + *key_len = 24; + *iv_len = 8; + } + else if (header_len > 8 && !strncmp("DES-CBC", header, 7)) + { + *algo = GCRY_CIPHER_DES; + iv_pos = 8; + *mode = GCRY_CIPHER_MODE_CBC; + *key_len = 8; + *iv_len = 8; + } + else if (header_len > 12 && !strncmp("AES-128-CBC", header, 11)) + { + *algo = GCRY_CIPHER_AES128; + iv_pos = 12; + *mode = GCRY_CIPHER_MODE_CBC; + *key_len = 16; + *iv_len = 16; + } + else if (header_len > 12 && !strncmp("AES-192-CBC", header, 11)) + { + *algo = GCRY_CIPHER_AES192; + iv_pos = 12; + *mode = GCRY_CIPHER_MODE_CBC; + *key_len = 24; + *iv_len = 16; + } + else if (header_len > 12 && !strncmp("AES-256-CBC", header, 11)) + { + *algo = GCRY_CIPHER_AES256; + iv_pos = 12; + *mode = GCRY_CIPHER_MODE_CBC; + *key_len = 32; + *iv_len = 16; + } else { + return -1; + } + + *iv = malloc(*iv_len); + if (*iv == NULL) { + return -1; + } + + return load_iv(header + iv_pos, *iv, *iv_len); +} + +#define get_next_line(p, len) { \ + while(p[len] == '\n' || p[len] == '\r') /* skip empty lines */ \ + len++; \ + if(p[len] == '\0') /* EOL */ \ + len = -1; \ + else /* calculate length */ \ + for(p += len, len = 0; p[len] && p[len] != '\n' \ + && p[len] != '\r'; len++); \ + } + +static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, + ssh_auth_callback cb, void *userdata, const char *desc) { + ssh_buffer buffer = NULL; + ssh_buffer out = NULL; + const char *p; + unsigned char *iv = NULL; + const char *header_begin; + const char *header_end; + unsigned int header_begin_size; + unsigned int header_end_size; + unsigned int key_len = 0; + unsigned int iv_len = 0; + int algo = 0; + int mode = 0; + int len; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + return NULL; + } + + switch(type) { + case SSH_KEYTYPE_DSS: + header_begin = DSA_HEADER_BEGIN; + header_end = DSA_HEADER_END; + break; + case SSH_KEYTYPE_RSA: + header_begin = RSA_HEADER_BEGIN; + header_end = RSA_HEADER_END; + break; + default: + ssh_buffer_free(buffer); + return NULL; + } + + header_begin_size = strlen(header_begin); + header_end_size = strlen(header_end); + + p = pkey; + len = 0; + get_next_line(p, len); + + while(len > 0 && strncmp(p, header_begin, header_begin_size)) { + /* skip line */ + get_next_line(p, len); + } + if(len < 0) { + /* no header found */ + return NULL; + } + /* skip header line */ + get_next_line(p, len); + + if (len > 11 && strncmp("Proc-Type: 4,ENCRYPTED", p, 11) == 0) { + /* skip line */ + get_next_line(p, len); + + if (len > 10 && strncmp("DEK-Info: ", p, 10) == 0) { + p += 10; + len = 0; + get_next_line(p, len); + if (privatekey_dek_header(p, len, &algo, &mode, &key_len, + &iv, &iv_len) < 0) { + ssh_buffer_free(buffer); + SAFE_FREE(iv); + return NULL; + } + } else { + ssh_buffer_free(buffer); + SAFE_FREE(iv); + return NULL; + } + } else { + if(len > 0) { + if (buffer_add_data(buffer, p, len) < 0) { + ssh_buffer_free(buffer); + SAFE_FREE(iv); + return NULL; + } + } + } + + get_next_line(p, len); + while(len > 0 && strncmp(p, header_end, header_end_size) != 0) { + if (buffer_add_data(buffer, p, len) < 0) { + ssh_buffer_free(buffer); + SAFE_FREE(iv); + return NULL; + } + get_next_line(p, len); + } + + if (len == -1 || strncmp(p, header_end, header_end_size) != 0) { + ssh_buffer_free(buffer); + SAFE_FREE(iv); + return NULL; + } + + if (buffer_add_data(buffer, "\0", 1) < 0) { + ssh_buffer_free(buffer); + SAFE_FREE(iv); + return NULL; + } + + out = base64_to_bin(ssh_buffer_get_begin(buffer)); + ssh_buffer_free(buffer); + if (out == NULL) { + SAFE_FREE(iv); + return NULL; + } + + if (algo) { + if (privatekey_decrypt(algo, mode, key_len, iv, iv_len, out, + cb, userdata, desc) < 0) { + ssh_buffer_free(out); + SAFE_FREE(iv); + return NULL; + } + } + SAFE_FREE(iv); + + return out; +} + +static int b64decode_rsa_privatekey(const char *pkey, gcry_sexp_t *r, + ssh_auth_callback cb, void *userdata, const char *desc) { + const unsigned char *data; + ssh_string n = NULL; + ssh_string e = NULL; + ssh_string d = NULL; + ssh_string p = NULL; + ssh_string q = NULL; + ssh_string unused1 = NULL; + ssh_string unused2 = NULL; + ssh_string u = NULL; + ssh_string v = NULL; + ssh_buffer buffer = NULL; + int rc = 1; + + buffer = privatekey_string_to_buffer(pkey, SSH_KEYTYPE_RSA, cb, userdata, desc); + if (buffer == NULL) { + return 0; + } + + if (!asn1_check_sequence(buffer)) { + ssh_buffer_free(buffer); + return 0; + } + + v = asn1_get_int(buffer); + if (v == NULL) { + ssh_buffer_free(buffer); + return 0; + } + + data = ssh_string_data(v); + if (ssh_string_len(v) != 1 || data[0] != 0) { + ssh_buffer_free(buffer); + return 0; + } + + n = asn1_get_int(buffer); + e = asn1_get_int(buffer); + d = asn1_get_int(buffer); + q = asn1_get_int(buffer); + p = asn1_get_int(buffer); + unused1 = asn1_get_int(buffer); + unused2 = asn1_get_int(buffer); + u = asn1_get_int(buffer); + + ssh_buffer_free(buffer); + + if (n == NULL || e == NULL || d == NULL || p == NULL || q == NULL || + unused1 == NULL || unused2 == NULL|| u == NULL) { + rc = 0; + goto error; + } + + if (gcry_sexp_build(r, NULL, + "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %b)))", + ssh_string_len(n), ssh_string_data(n), + ssh_string_len(e), ssh_string_data(e), + ssh_string_len(d), ssh_string_data(d), + ssh_string_len(p), ssh_string_data(p), + ssh_string_len(q), ssh_string_data(q), + ssh_string_len(u), ssh_string_data(u))) { + rc = 0; + } + +error: + ssh_string_free(n); + ssh_string_free(e); + ssh_string_free(d); + ssh_string_free(p); + ssh_string_free(q); + ssh_string_free(unused1); + ssh_string_free(unused2); + ssh_string_free(u); + ssh_string_free(v); + + return rc; +} + +static int b64decode_dsa_privatekey(const char *pkey, gcry_sexp_t *r, ssh_auth_callback cb, + void *userdata, const char *desc) { + const unsigned char *data; + ssh_buffer buffer = NULL; + ssh_string p = NULL; + ssh_string q = NULL; + ssh_string g = NULL; + ssh_string y = NULL; + ssh_string x = NULL; + ssh_string v = NULL; + int rc = 1; + + buffer = privatekey_string_to_buffer(pkey, SSH_KEYTYPE_DSS, cb, userdata, desc); + if (buffer == NULL) { + return 0; + } + + if (!asn1_check_sequence(buffer)) { + ssh_buffer_free(buffer); + return 0; + } + + v = asn1_get_int(buffer); + if (v == NULL) { + ssh_buffer_free(buffer); + return 0; + } + + data = ssh_string_data(v); + if (ssh_string_len(v) != 1 || data[0] != 0) { + ssh_buffer_free(buffer); + return 0; + } + + p = asn1_get_int(buffer); + q = asn1_get_int(buffer); + g = asn1_get_int(buffer); + y = asn1_get_int(buffer); + x = asn1_get_int(buffer); + ssh_buffer_free(buffer); + + if (p == NULL || q == NULL || g == NULL || y == NULL || x == NULL) { + rc = 0; + goto error; + } + + if (gcry_sexp_build(r, NULL, + "(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))", + ssh_string_len(p), ssh_string_data(p), + ssh_string_len(q), ssh_string_data(q), + ssh_string_len(g), ssh_string_data(g), + ssh_string_len(y), ssh_string_data(y), + ssh_string_len(x), ssh_string_data(x))) { + rc = 0; + } + +error: + ssh_string_free(p); + ssh_string_free(q); + ssh_string_free(g); + ssh_string_free(y); + ssh_string_free(x); + ssh_string_free(v); + + return rc; +} + +#ifdef HAVE_GCRYPT_ECC +int pki_key_ecdsa_nid_from_name(const char *name) +{ + return -1; +} +#endif + +ssh_key pki_private_key_from_base64(const char *b64_key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data) +{ + gcry_sexp_t dsa = NULL; + gcry_sexp_t rsa = NULL; + ssh_key key = NULL; + enum ssh_keytypes_e type; + int valid; + + /* needed for gcrypt initialization */ + if (ssh_init() < 0) { + return NULL; + } + + type = pki_privatekey_type_from_string(b64_key); + if (type == SSH_KEYTYPE_UNKNOWN) { + ssh_pki_log("Unknown or invalid private key."); + return NULL; + } + + switch (type) { + case SSH_KEYTYPE_DSS: + if (passphrase == NULL) { + if (auth_fn) { + valid = b64decode_dsa_privatekey(b64_key, &dsa, auth_fn, + auth_data, "Passphrase for private key:"); + } else { + valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, NULL, + NULL); + } + } else { + valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, (void *) + passphrase, NULL); + } + + if (!valid) { + ssh_pki_log("Parsing private key"); + goto fail; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (passphrase == NULL) { + if (auth_fn) { + valid = b64decode_rsa_privatekey(b64_key, &rsa, auth_fn, + auth_data, "Passphrase for private key:"); + } else { + valid = b64decode_rsa_privatekey(b64_key, &rsa, NULL, NULL, + NULL); + } + } else { + valid = b64decode_rsa_privatekey(b64_key, &rsa, NULL, + (void *)passphrase, NULL); + } + + if (!valid) { + ssh_pki_log("Parsing private key"); + goto fail; + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + ssh_pki_log("Unkown or invalid private key type %d", type); + return NULL; + } + + key = ssh_key_new(); + if (key == NULL) { + goto fail; + } + + key->type = type; + key->type_c = ssh_key_type_to_char(type); + key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; + key->dsa = dsa; + key->rsa = rsa; + + return key; +fail: + ssh_key_free(key); + gcry_sexp_release(dsa); + gcry_sexp_release(rsa); + + return NULL; +} + +int pki_pubkey_build_dss(ssh_key key, + ssh_string p, + ssh_string q, + ssh_string g, + ssh_string pubkey) { + gcry_sexp_build(&key->dsa, NULL, + "(public-key(dsa(p %b)(q %b)(g %b)(y %b)))", + ssh_string_len(p), ssh_string_data(p), + ssh_string_len(q), ssh_string_data(q), + ssh_string_len(g), ssh_string_data(g), + ssh_string_len(pubkey), ssh_string_data(pubkey)); + if (key->dsa == NULL) { + return SSH_ERROR; + } + + return SSH_OK; +} + +int pki_pubkey_build_rsa(ssh_key key, + ssh_string e, + ssh_string n) { + gcry_sexp_build(&key->rsa, NULL, + "(public-key(rsa(n %b)(e %b)))", + ssh_string_len(n), ssh_string_data(n), + ssh_string_len(e),ssh_string_data(e)); + if (key->rsa == NULL) { + return SSH_ERROR; + } + + return SSH_OK; +} + +#ifdef HAVE_GCRYPT_ECC +int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e) +{ + return -1; +} +#endif + +ssh_key pki_key_dup(const ssh_key key, int demote) +{ + ssh_key new; + gcry_sexp_t sexp; + gcry_error_t err; + const char *tmp = NULL; + size_t size; + + ssh_string p = NULL; + ssh_string q = NULL; + ssh_string g = NULL; + ssh_string y = NULL; + ssh_string x = NULL; + + ssh_string e = NULL; + ssh_string n = NULL; + ssh_string d = NULL; + ssh_string u = NULL; + + new = ssh_key_new(); + if (new == NULL) { + return NULL; + } + new->type = key->type; + new->type_c = key->type_c; + if (demote) { + new->flags = SSH_KEY_FLAG_PUBLIC; + } else { + new->flags = key->flags; + } + + switch(key->type) { + case SSH_KEYTYPE_DSS: + sexp = gcry_sexp_find_token(key->dsa, "p", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + p = ssh_string_new(size); + if (p == NULL) { + goto fail; + } + ssh_string_fill(p, (char *)tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->dsa, "q", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + q = ssh_string_new(size); + if (q == NULL) { + goto fail; + } + ssh_string_fill(q, (char *)tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->dsa, "g", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + g = ssh_string_new(size); + if (g == NULL) { + goto fail; + } + ssh_string_fill(g, (char *)tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->dsa, "y", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + y = ssh_string_new(size); + if (y == NULL) { + goto fail; + } + ssh_string_fill(y, (char *)tmp, size); + gcry_sexp_release(sexp); + + if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { + sexp = gcry_sexp_find_token(key->dsa, "x", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + x = ssh_string_new(size); + if (x == NULL) { + goto fail; + } + ssh_string_fill(x, (char *)tmp, size); + gcry_sexp_release(sexp); + + err = gcry_sexp_build(&new->dsa, NULL, + "(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))", + ssh_string_len(p), ssh_string_data(p), + ssh_string_len(q), ssh_string_data(q), + ssh_string_len(g), ssh_string_data(g), + ssh_string_len(y), ssh_string_data(y), + ssh_string_len(x), ssh_string_data(x)); + } else { + err = gcry_sexp_build(&new->dsa, NULL, + "(public-key(dsa(p %b)(q %b)(g %b)(y %b)))", + ssh_string_len(p), ssh_string_data(p), + ssh_string_len(q), ssh_string_data(q), + ssh_string_len(g), ssh_string_data(g), + ssh_string_len(y), ssh_string_data(y)); + } + if (err) { + goto fail; + } + + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(q); + ssh_string_free(q); + ssh_string_burn(g); + ssh_string_free(g); + ssh_string_burn(y); + ssh_string_free(y); + ssh_string_burn(x); + ssh_string_free(x); + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + sexp = gcry_sexp_find_token(key->rsa, "e", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + e = ssh_string_new(size); + if (e == NULL) { + goto fail; + } + ssh_string_fill(e, (char *)tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->rsa, "n", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + n = ssh_string_new(size); + if (n == NULL) { + goto fail; + } + ssh_string_fill(n, (char *)tmp, size); + gcry_sexp_release(sexp); + + if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { + sexp = gcry_sexp_find_token(key->rsa, "d", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + d = ssh_string_new(size); + if (e == NULL) { + goto fail; + } + ssh_string_fill(d, (char *)tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->rsa, "p", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + p = ssh_string_new(size); + if (p == NULL) { + goto fail; + } + ssh_string_fill(p, (char *)tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->rsa, "q", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + q = ssh_string_new(size); + if (q == NULL) { + goto fail; + } + ssh_string_fill(q, (char *)tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->rsa, "u", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + u = ssh_string_new(size); + if (u == NULL) { + goto fail; + } + ssh_string_fill(u, (char *)tmp, size); + gcry_sexp_release(sexp); + + err = gcry_sexp_build(&new->rsa, NULL, + "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %b)))", + ssh_string_len(n), ssh_string_data(n), + ssh_string_len(e), ssh_string_data(e), + ssh_string_len(d), ssh_string_data(d), + ssh_string_len(p), ssh_string_data(p), + ssh_string_len(q), ssh_string_data(q), + ssh_string_len(u), ssh_string_data(u)); + } else { + err = gcry_sexp_build(&new->rsa, NULL, + "(public-key(rsa(n %b)(e %b)))", + ssh_string_len(n), ssh_string_data(n), + ssh_string_len(e), ssh_string_data(e)); + } + + if (err) { + goto fail; + } + + ssh_string_burn(e); + ssh_string_free(e); + ssh_string_burn(n); + ssh_string_free(n); + ssh_string_burn(d); + ssh_string_free(d); + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(q); + ssh_string_free(q); + ssh_string_burn(u); + ssh_string_free(u); + + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + ssh_key_free(new); + return NULL; + } + + return new; +fail: + gcry_sexp_release(sexp); + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(q); + ssh_string_free(q); + ssh_string_burn(g); + ssh_string_free(g); + ssh_string_burn(y); + ssh_string_free(y); + ssh_string_burn(x); + ssh_string_free(x); + + ssh_string_burn(e); + ssh_string_free(e); + ssh_string_burn(n); + ssh_string_free(n); + ssh_string_burn(u); + ssh_string_free(u); + + ssh_key_free(new); + + return NULL; +} + +static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int type){ + gcry_sexp_t parms; + int rc; + rc = gcry_sexp_build(&parms, + NULL, + "(genkey(%s(nbits %d)(transient-key)))", + type_s, + parameter); + if (rc != 0) + return SSH_ERROR; + if(type == SSH_KEYTYPE_RSA) + rc = gcry_pk_genkey(&key->rsa, parms); + else + rc = gcry_pk_genkey(&key->dsa, parms); + gcry_sexp_release(parms); + if (rc != 0) + return SSH_ERROR; + return SSH_OK; +} + +int pki_key_generate_rsa(ssh_key key, int parameter){ + return pki_key_generate(key, parameter, "rsa", SSH_KEYTYPE_RSA); +} +int pki_key_generate_dss(ssh_key key, int parameter){ + return pki_key_generate(key, parameter, "dsa", SSH_KEYTYPE_DSS); +} + +#ifdef HAVE_GCRYPT_ECC +int pki_key_generate_ecdsa(ssh_key key, int parameter) { + return -1; +} +#endif + +static int _bignum_cmp(const gcry_sexp_t s1, + const gcry_sexp_t s2, + const char *what) +{ + gcry_sexp_t sexp; + bignum b1; + bignum b2; + + sexp = gcry_sexp_find_token(s1, what, 0); + if (sexp == NULL) { + return 1; + } + b1 = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + if (b1 == NULL) { + return 1; + } + + sexp = gcry_sexp_find_token(s2, what, 0); + if (sexp == NULL) { + return 1; + } + b2 = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + if (b2 == NULL) { + return 1; + } + + if (bignum_cmp(b1, b2) != 0) { + return 1; + } + + return 0; +} + +int pki_key_compare(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + switch (k1->type) { + case SSH_KEYTYPE_DSS: + if (_bignum_cmp(k1->dsa, k2->dsa, "p") != 0) { + return 1; + } + + if (_bignum_cmp(k1->dsa, k2->dsa, "q") != 0) { + return 1; + } + + if (_bignum_cmp(k1->dsa, k2->dsa, "g") != 0) { + return 1; + } + + if (_bignum_cmp(k1->dsa, k2->dsa, "y") != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (_bignum_cmp(k1->dsa, k2->dsa, "x") != 0) { + return 1; + } + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (_bignum_cmp(k1->rsa, k2->rsa, "e") != 0) { + return 1; + } + + if (_bignum_cmp(k1->rsa, k2->rsa, "n") != 0) { + return 1; + } + + if (what == SSH_KEY_CMP_PRIVATE) { + if (_bignum_cmp(k1->rsa, k2->rsa, "d") != 0) { + return 1; + } + + if (_bignum_cmp(k1->rsa, k2->rsa, "p") != 0) { + return 1; + } + + if (_bignum_cmp(k1->rsa, k2->rsa, "q") != 0) { + return 1; + } + + if (_bignum_cmp(k1->rsa, k2->rsa, "u") != 0) { + return 1; + } + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + return 1; + } + + return 0; +} + +ssh_string pki_publickey_to_blob(const ssh_key key) +{ + ssh_buffer buffer; + ssh_string type_s; + ssh_string str = NULL; + ssh_string e = NULL; + ssh_string n = NULL; + ssh_string p = NULL; + ssh_string g = NULL; + ssh_string q = NULL; + const char *tmp = NULL; + size_t size; + gcry_sexp_t sexp; + int rc; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + return NULL; + } + + type_s = ssh_string_from_char(key->type_c); + if (type_s == NULL) { + ssh_buffer_free(buffer); + return NULL; + } + + rc = buffer_add_ssh_string(buffer, type_s); + string_free(type_s); + if (rc < 0) { + ssh_buffer_free(buffer); + return NULL; + } + + switch (key->type) { + case SSH_KEYTYPE_DSS: + sexp = gcry_sexp_find_token(key->dsa, "p", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + p = ssh_string_new(size); + if (p == NULL) { + goto fail; + } + ssh_string_fill(p, (char *) tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->dsa, "q", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + q = ssh_string_new(size); + if (q == NULL) { + goto fail; + } + ssh_string_fill(q, (char *) tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->dsa, "g", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + g = ssh_string_new(size); + if (g == NULL) { + goto fail; + } + ssh_string_fill(g, (char *) tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->dsa, "y", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + n = ssh_string_new(size); + if (n == NULL) { + goto fail; + } + ssh_string_fill(n, (char *) tmp, size); + + if (buffer_add_ssh_string(buffer, p) < 0) { + goto fail; + } + if (buffer_add_ssh_string(buffer, q) < 0) { + goto fail; + } + if (buffer_add_ssh_string(buffer, g) < 0) { + goto fail; + } + if (buffer_add_ssh_string(buffer, n) < 0) { + goto fail; + } + + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(g); + ssh_string_free(g); + ssh_string_burn(q); + ssh_string_free(q); + ssh_string_burn(n); + ssh_string_free(n); + + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + sexp = gcry_sexp_find_token(key->rsa, "e", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + e = ssh_string_new(size); + if (e == NULL) { + goto fail; + } + ssh_string_fill(e, (char *) tmp, size); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(key->rsa, "n", 0); + if (sexp == NULL) { + goto fail; + } + tmp = gcry_sexp_nth_data(sexp, 1, &size); + n = ssh_string_new(size); + if (n == NULL) { + goto fail; + } + ssh_string_fill(n, (char *) tmp, size); + gcry_sexp_release(sexp); + + if (buffer_add_ssh_string(buffer, e) < 0) { + goto fail; + } + if (buffer_add_ssh_string(buffer, n) < 0) { + goto fail; + } + + ssh_string_burn(e); + ssh_string_free(e); + ssh_string_burn(n); + ssh_string_free(n); + + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + goto fail; + } + + str = ssh_string_new(buffer_get_rest_len(buffer)); + if (str == NULL) { + goto fail; + } + + rc = ssh_string_fill(str, buffer_get_rest(buffer), buffer_get_rest_len(buffer)); + if (rc < 0) { + goto fail; + } + ssh_buffer_free(buffer); + + return str; +fail: + ssh_buffer_free(buffer); + ssh_string_burn(str); + ssh_string_free(str); + ssh_string_burn(e); + ssh_string_free(e); + ssh_string_burn(p); + ssh_string_free(p); + ssh_string_burn(g); + ssh_string_free(g); + ssh_string_burn(q); + ssh_string_free(q); + ssh_string_burn(n); + ssh_string_free(n); + + return NULL; +} + +int pki_export_pubkey_rsa1(const ssh_key key, + const char *host, + char *rsa1, + size_t rsa1_len) +{ + gcry_sexp_t sexp; + int rsa_size; + bignum b; + char *e, *n; + + sexp = gcry_sexp_find_token(key->rsa, "e", 0); + if (sexp == NULL) { + return SSH_ERROR; + } + b = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + if (b == NULL) { + return SSH_ERROR; + } + e = bignum_bn2dec(b); + + sexp = gcry_sexp_find_token(key->rsa, "n", 0); + if (sexp == NULL) { + SAFE_FREE(e); + return SSH_ERROR; + } + b = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + if (b == NULL) { + SAFE_FREE(e); + return SSH_ERROR; + } + n = bignum_bn2dec(b); + + rsa_size = (gcry_pk_get_nbits(key->rsa) + 7) / 8; + + snprintf(rsa1, rsa1_len, + "%s %d %s %s\n", + host, rsa_size << 3, e, n); + SAFE_FREE(e); + SAFE_FREE(n); + + return SSH_OK; +} + +ssh_string pki_signature_to_blob(const ssh_signature sig) +{ + char buffer[40] = {0}; + const char *r = NULL; + const char *s = NULL; + gcry_sexp_t sexp; + size_t size = 0; + ssh_string sig_blob = NULL; + + switch(sig->type) { + case SSH_KEYTYPE_DSS: + sexp = gcry_sexp_find_token(sig->dsa_sig, "r", 0); + if (sexp == NULL) { + return NULL; + } + r = gcry_sexp_nth_data(sexp, 1, &size); + /* libgcrypt put 0 when first bit is set */ + if (*r == 0) { + size--; + r++; + } + memcpy(buffer, r + size - 20, 20); + gcry_sexp_release(sexp); + + sexp = gcry_sexp_find_token(sig->dsa_sig, "s", 0); + if (sexp == NULL) { + return NULL; + } + s = gcry_sexp_nth_data(sexp,1,&size); + if (*s == 0) { + size--; + s++; + } + memcpy(buffer+ 20, s + size - 20, 20); + gcry_sexp_release(sexp); + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + sexp = gcry_sexp_find_token(sig->rsa_sig, "s", 0); + if (sexp == NULL) { + return NULL; + } + s = gcry_sexp_nth_data(sexp, 1, &size); + if (*s == 0) { + size--; + s++; + } + + sig_blob = ssh_string_new(size); + if (sig_blob == NULL) { + return NULL; + } + ssh_string_fill(sig_blob, discard_const_p(char, s), size); + + gcry_sexp_release(sexp); + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + ssh_pki_log("Unknown signature key type: %d", sig->type); + return NULL; + break; + } + + return sig_blob; +} + +ssh_signature pki_signature_from_blob(const ssh_key pubkey, + const ssh_string sig_blob, + enum ssh_keytypes_e type) +{ + ssh_signature sig; + gcry_error_t err; + size_t len; + size_t rsalen; + + sig = ssh_signature_new(); + if (sig == NULL) { + return NULL; + } + + sig->type = type; + + len = ssh_string_len(sig_blob); + + switch(type) { + case SSH_KEYTYPE_DSS: + /* 40 is the dual signature blob len. */ + if (len != 40) { + ssh_pki_log("Signature has wrong size: %lu", + (unsigned long)len); + ssh_signature_free(sig); + return NULL; + } + +#ifdef DEBUG_CRYPTO + ssh_pki_log("DSA signature len: %lu", (unsigned long)len); + ssh_print_hexa("DSA signature", ssh_string_data(sig_blob), len); +#endif + + err = gcry_sexp_build(&sig->dsa_sig, + NULL, + "(sig-val(dsa(r %b)(s %b)))", + 20, + ssh_string_data(sig_blob), + 20, + (unsigned char *)ssh_string_data(sig_blob) + 20); + if (err) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + rsalen = (gcry_pk_get_nbits(pubkey->rsa) + 7) / 8; + + if (len > rsalen) { + ssh_pki_log("Signature is to big size: %lu", + (unsigned long)len); + ssh_signature_free(sig); + return NULL; + } + + if (len < rsalen) { + ssh_pki_log("RSA signature len %lu < %lu", + (unsigned long)len, (unsigned long)rsalen); + } + +#ifdef DEBUG_CRYPTO + ssh_pki_log("RSA signature len: %lu", (unsigned long)len); + ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); +#endif + + err = gcry_sexp_build(&sig->rsa_sig, + NULL, + "(sig-val(rsa(s %b)))", + ssh_string_len(sig_blob), + ssh_string_data(sig_blob)); + if (err) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + ssh_pki_log("Unknown signature type"); + return NULL; + } + + return sig; +} + +int pki_signature_verify(ssh_session session, + const ssh_signature sig, + const ssh_key key, + const unsigned char *hash, + size_t hlen) +{ + unsigned char ghash[hlen + 1]; + gcry_sexp_t sexp; + gcry_error_t err; + + switch(key->type) { + case SSH_KEYTYPE_DSS: + /* That is to mark the number as positive */ + if(hash[0] >= 0x80) { + memcpy(ghash + 1, hash, hlen); + ghash[0] = 0; + hash = ghash; + hlen += 1; + } + + err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash); + if (err) { + ssh_set_error(session, + SSH_FATAL, + "DSA hash error: %s", gcry_strerror(err)); + return SSH_ERROR; + } + err = gcry_pk_verify(sig->dsa_sig, sexp, key->dsa); + gcry_sexp_release(sexp); + if (err) { + ssh_set_error(session, SSH_FATAL, "Invalid DSA signature"); + if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) { + ssh_set_error(session, + SSH_FATAL, + "DSA verify error: %s", + gcry_strerror(err)); + } + return SSH_ERROR; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + err = gcry_sexp_build(&sexp, + NULL, + "(data(flags pkcs1)(hash sha1 %b))", + hlen, hash); + if (err) { + ssh_set_error(session, + SSH_FATAL, + "RSA hash error: %s", + gcry_strerror(err)); + return SSH_ERROR; + } + err = gcry_pk_verify(sig->rsa_sig, sexp, key->rsa); + gcry_sexp_release(sexp); + if (err) { + ssh_set_error(session, SSH_FATAL, "Invalid RSA signature"); + if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) { + ssh_set_error(session, + SSH_FATAL, + "RSA verify error: %s", + gcry_strerror(err)); + } + return SSH_ERROR; + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + ssh_set_error(session, SSH_FATAL, "Unknown public key type"); + return SSH_ERROR; + } + + return SSH_OK; +} + +ssh_signature pki_do_sign(const ssh_key privkey, + const unsigned char *hash, + size_t hlen) { + unsigned char ghash[hlen + 1]; + ssh_signature sig; + gcry_sexp_t sexp; + gcry_error_t err; + + sig = ssh_signature_new(); + if (sig == NULL) { + return NULL; + } + sig->type = privkey->type; + + switch (privkey->type) { + case SSH_KEYTYPE_DSS: + /* That is to mark the number as positive */ + if(hash[0] >= 0x80) { + memcpy(ghash + 1, hash, hlen); + ghash[0] = 0; + hash = ghash; + hlen += 1; + } + + err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash); + if (err) { + ssh_signature_free(sig); + return NULL; + } + + err = gcry_pk_sign(&sig->dsa_sig, sexp, privkey->dsa); + gcry_sexp_release(sexp); + if (err) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + err = gcry_sexp_build(&sexp, + NULL, + "(data(flags pkcs1)(hash sha1 %b))", + hlen, + hash); + if (err) { + ssh_signature_free(sig); + return NULL; + } + + err = gcry_pk_sign(&sig->rsa_sig, sexp, privkey->rsa); + gcry_sexp_release(sexp); + if (err) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + ssh_signature_free(sig); + return NULL; + } + + return sig; +} + +#ifdef WITH_SERVER +ssh_signature pki_do_sign_sessionid(const ssh_key key, + const unsigned char *hash, + size_t hlen) +{ + unsigned char ghash[hlen + 1]; + ssh_signature sig; + gcry_sexp_t sexp; + gcry_error_t err; + + sig = ssh_signature_new(); + if (sig == NULL) { + return NULL; + } + sig->type = key->type; + + switch(key->type) { + case SSH_KEYTYPE_DSS: + /* That is to mark the number as positive */ + if(hash[0] >= 0x80) { + memcpy(ghash + 1, hash, hlen); + ghash[0] = 0; + hash = ghash; + hlen += 1; + } + + err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash); + if (err) { + ssh_signature_free(sig); + return NULL; + } + err = gcry_pk_sign(&sig->dsa_sig, sexp, key->dsa); + gcry_sexp_release(sexp); + if (err) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + err = gcry_sexp_build(&sexp, + NULL, + "(data(flags pkcs1)(hash sha1 %b))", + hlen, + hash); + if (err) { + ssh_signature_free(sig); + return NULL; + } + err = gcry_pk_sign(&sig->rsa_sig, sexp, key->rsa); + gcry_sexp_release(sexp); + if (err) { + ssh_signature_free(sig); + return NULL; + } + break; + case SSH_KEYTYPE_ECDSA: + case SSH_KEYTYPE_UNKNOWN: + return NULL; + } + + return sig; +} +#endif /* WITH_SERVER */ + +#endif /* HAVE_LIBGCRYPT */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/poll.c b/libssh/src/poll.c new file mode 100644 index 00000000..932c4918 --- /dev/null +++ b/libssh/src/poll.c @@ -0,0 +1,926 @@ +/* + * poll.c - poll wrapper + * + * This file is part of the SSH Library + * + * Copyright (c) 2009-2010 by Andreas Schneider + * Copyright (c) 2003-2009 by Aris Adamantiadis + * Copyright (c) 2009 Aleksandar Kanchev + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * vim: ts=2 sw=2 et cindent + */ + +#include "config.h" + +#include +#include + +#include "libssh/priv.h" +#include "libssh/libssh.h" +#include "libssh/poll.h" +#include "libssh/socket.h" +#include "libssh/session.h" +#ifdef WITH_SERVER +#include "libssh/server.h" +#include "libssh/misc.h" +#endif + + +#ifndef SSH_POLL_CTX_CHUNK +#define SSH_POLL_CTX_CHUNK 5 +#endif + +/** + * @defgroup libssh_poll The SSH poll functions. + * @ingroup libssh + * + * Add a generic way to handle sockets asynchronously. + * + * It's based on poll objects, each of which store a socket, its events and a + * callback, which gets called whenever an event is set. The poll objects are + * attached to a poll context, which should be allocated on per thread basis. + * + * Polling the poll context will poll all the attached poll objects and call + * their callbacks (handlers) if any of the socket events are set. This should + * be done within the main loop of an application. + * + * @{ + */ + +struct ssh_poll_handle_struct { + ssh_poll_ctx ctx; + union { + socket_t fd; + size_t idx; + } x; + short events; + ssh_poll_callback cb; + void *cb_data; +}; + +struct ssh_poll_ctx_struct { + ssh_poll_handle *pollptrs; + ssh_pollfd_t *pollfds; + size_t polls_allocated; + size_t polls_used; + size_t chunk_size; +}; + +#ifdef HAVE_POLL +#include + +void ssh_poll_init(void) { + return; +} + +void ssh_poll_cleanup(void) { + return; +} + +int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { + return poll((struct pollfd *) fds, nfds, timeout); +} + +#else /* HAVE_POLL */ + +typedef int (*poll_fn)(ssh_pollfd_t *, nfds_t, int); +static poll_fn ssh_poll_emu; + +#include + +#ifdef _WIN32 +#ifndef STRICT +#define STRICT +#endif /* STRICT */ + +#include +#include +#include +#else /* _WIN32 */ +#include +#include +#include +#include +#endif /* _WIN32 */ + + +/* + * This is a poll(2)-emulation using select for systems not providing a native + * poll implementation. + * + * Keep in mind that select is terribly inefficient. The interface is simply not + * meant to be used with maximum descriptor value greater, say, 32 or so. With + * a value as high as 1024 on Linux you'll pay dearly in every single call. + * poll() will be orders of magnitude faster. + */ +static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { + fd_set readfds, writefds, exceptfds; + struct timeval tv, *ptv; + socket_t max_fd; + int rc; + nfds_t i; + + if (fds == NULL) { + errno = EFAULT; + return -1; + } + + FD_ZERO (&readfds); + FD_ZERO (&writefds); + FD_ZERO (&exceptfds); + + /* compute fd_sets and find largest descriptor */ + for (rc = -1, max_fd = 0, i = 0; i < nfds; i++) { + if (fds[i].fd == SSH_INVALID_SOCKET) { + continue; + } +#ifndef _WIN32 + if (fds[i].fd >= FD_SETSIZE) { + rc = -1; + break; + } +#endif + + if (fds[i].events & (POLLIN | POLLRDNORM)) { + FD_SET (fds[i].fd, &readfds); + } + if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { + FD_SET (fds[i].fd, &writefds); + } + if (fds[i].events & (POLLPRI | POLLRDBAND)) { + FD_SET (fds[i].fd, &exceptfds); + } + if (fds[i].fd > max_fd && + (fds[i].events & (POLLIN | POLLOUT | POLLPRI | + POLLRDNORM | POLLRDBAND | + POLLWRNORM | POLLWRBAND))) { + max_fd = fds[i].fd; + rc = 0; + } + } + + if (max_fd == SSH_INVALID_SOCKET || rc == -1) { + errno = EINVAL; + return -1; + } + + if (timeout < 0) { + ptv = NULL; + } else { + ptv = &tv; + if (timeout == 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + } + } + + rc = select (max_fd + 1, &readfds, &writefds, &exceptfds, ptv); + if (rc < 0) { + return -1; + } + + for (rc = 0, i = 0; i < nfds; i++) + if (fds[i].fd >= 0) { + fds[i].revents = 0; + + if (FD_ISSET(fds[i].fd, &readfds)) { + int save_errno = errno; + char data[64] = {0}; + int ret; + + /* support for POLLHUP */ + ret = recv(fds[i].fd, data, 64, MSG_PEEK); +#ifdef _WIN32 + if ((ret == -1) && + (errno == WSAESHUTDOWN || errno == WSAECONNRESET || + errno == WSAECONNABORTED || errno == WSAENETRESET)) { +#else + if ((ret == -1) && + (errno == ESHUTDOWN || errno == ECONNRESET || + errno == ECONNABORTED || errno == ENETRESET)) { +#endif + fds[i].revents |= POLLHUP; + } else { + fds[i].revents |= fds[i].events & (POLLIN | POLLRDNORM); + } + + errno = save_errno; + } + if (FD_ISSET(fds[i].fd, &writefds)) { + fds[i].revents |= fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND); + } + + if (FD_ISSET(fds[i].fd, &exceptfds)) { + fds[i].revents |= fds[i].events & (POLLPRI | POLLRDBAND); + } + + if (fds[i].revents & ~POLLHUP) { + rc++; + } + } else { + fds[i].revents = POLLNVAL; + } + + return rc; +} + +void ssh_poll_init(void) { + ssh_poll_emu = bsd_poll; +} + +void ssh_poll_cleanup(void) { + ssh_poll_emu = bsd_poll; +} + +int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { + return (ssh_poll_emu)(fds, nfds, timeout); +} + +#endif /* HAVE_POLL */ + +/** + * @brief Allocate a new poll object, which could be used within a poll context. + * + * @param fd Socket that will be polled. + * @param events Poll events that will be monitored for the socket. i.e. + * POLLIN, POLLPRI, POLLOUT + * @param cb Function to be called if any of the events are set. + * The prototype of cb is: + * int (*ssh_poll_callback)(ssh_poll_handle p, socket_t fd, + * int revents, void *userdata); + * @param userdata Userdata to be passed to the callback function. NULL if + * not needed. + * + * @return A new poll object, NULL on error + */ + +ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb, + void *userdata) { + ssh_poll_handle p; + + p = malloc(sizeof(struct ssh_poll_handle_struct)); + if (p == NULL) { + return NULL; + } + ZERO_STRUCTP(p); + + p->x.fd = fd; + p->events = events; + p->cb = cb; + p->cb_data = userdata; + + return p; +} + + +/** + * @brief Free a poll object. + * + * @param p Pointer to an already allocated poll object. + */ + +void ssh_poll_free(ssh_poll_handle p) { + if(p->ctx != NULL){ + ssh_poll_ctx_remove(p->ctx,p); + p->ctx=NULL; + } + SAFE_FREE(p); +} + +/** + * @brief Get the poll context of a poll object. + * + * @param p Pointer to an already allocated poll object. + * + * @return Poll context or NULL if the poll object isn't attached. + */ +ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p) { + return p->ctx; +} + +/** + * @brief Get the events of a poll object. + * + * @param p Pointer to an already allocated poll object. + * + * @return Poll events. + */ +short ssh_poll_get_events(ssh_poll_handle p) { + return p->events; +} + +/** + * @brief Set the events of a poll object. The events will also be propagated + * to an associated poll context. + * + * @param p Pointer to an already allocated poll object. + * @param events Poll events. + */ +void ssh_poll_set_events(ssh_poll_handle p, short events) { + p->events = events; + if (p->ctx != NULL) { + p->ctx->pollfds[p->x.idx].events = events; + } +} + +/** + * @brief Set the file descriptor of a poll object. The FD will also be propagated + * to an associated poll context. + * + * @param p Pointer to an already allocated poll object. + * @param fd New file descriptor. + */ +void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd) { + if (p->ctx != NULL) { + p->ctx->pollfds[p->x.idx].fd = fd; + } else { + p->x.fd = fd; + } +} + +/** + * @brief Add extra events to a poll object. Duplicates are ignored. + * The events will also be propagated to an associated poll context. + * + * @param p Pointer to an already allocated poll object. + * @param events Poll events. + */ +void ssh_poll_add_events(ssh_poll_handle p, short events) { + ssh_poll_set_events(p, ssh_poll_get_events(p) | events); +} + +/** + * @brief Remove events from a poll object. Non-existent are ignored. + * The events will also be propagated to an associated poll context. + * + * @param p Pointer to an already allocated poll object. + * @param events Poll events. + */ +void ssh_poll_remove_events(ssh_poll_handle p, short events) { + ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events); +} + +/** + * @brief Get the raw socket of a poll object. + * + * @param p Pointer to an already allocated poll object. + * + * @return Raw socket. + */ + +socket_t ssh_poll_get_fd(ssh_poll_handle p) { + if (p->ctx != NULL) { + return p->ctx->pollfds[p->x.idx].fd; + } + + return p->x.fd; +} +/** + * @brief Set the callback of a poll object. + * + * @param p Pointer to an already allocated poll object. + * @param cb Function to be called if any of the events are set. + * @param userdata Userdata to be passed to the callback function. NULL if + * not needed. + */ +void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata) { + if (cb != NULL) { + p->cb = cb; + p->cb_data = userdata; + } +} + +/** + * @brief Create a new poll context. It could be associated with many poll object + * which are going to be polled at the same time as the poll context. You + * would need a single poll context per thread. + * + * @param chunk_size The size of the memory chunk that will be allocated, when + * more memory is needed. This is for efficiency reasons, + * i.e. don't allocate memory for each new poll object, but + * for the next 5. Set it to 0 if you want to use the + * library's default value. + */ +ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size) { + ssh_poll_ctx ctx; + + ctx = malloc(sizeof(struct ssh_poll_ctx_struct)); + if (ctx == NULL) { + return NULL; + } + ZERO_STRUCTP(ctx); + + if (chunk_size == 0) { + chunk_size = SSH_POLL_CTX_CHUNK; + } + + ctx->chunk_size = chunk_size; + + return ctx; +} + +/** + * @brief Free a poll context. + * + * @param ctx Pointer to an already allocated poll context. + */ +void ssh_poll_ctx_free(ssh_poll_ctx ctx) { + if (ctx->polls_allocated > 0) { + while (ctx->polls_used > 0){ + ssh_poll_handle p = ctx->pollptrs[0]; + ssh_poll_ctx_remove(ctx, p); + } + + SAFE_FREE(ctx->pollptrs); + SAFE_FREE(ctx->pollfds); + } + + SAFE_FREE(ctx); +} + +static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) { + ssh_poll_handle *pollptrs; + ssh_pollfd_t *pollfds; + + pollptrs = realloc(ctx->pollptrs, sizeof(ssh_poll_handle) * new_size); + if (pollptrs == NULL) { + return -1; + } + + pollfds = realloc(ctx->pollfds, sizeof(ssh_pollfd_t) * new_size); + if (pollfds == NULL) { + ctx->pollptrs = realloc(pollptrs, sizeof(ssh_poll_handle) * ctx->polls_allocated); + return -1; + } + + ctx->pollptrs = pollptrs; + ctx->pollfds = pollfds; + ctx->polls_allocated = new_size; + + return 0; +} + +/** + * @brief Add a poll object to a poll context. + * + * @param ctx Pointer to an already allocated poll context. + * @param p Pointer to an already allocated poll object. + * + * @return 0 on success, < 0 on error + */ +int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) { + socket_t fd; + + if (p->ctx != NULL) { + /* already attached to a context */ + return -1; + } + + if (ctx->polls_used == ctx->polls_allocated && + ssh_poll_ctx_resize(ctx, ctx->polls_allocated + ctx->chunk_size) < 0) { + return -1; + } + + fd = p->x.fd; + p->x.idx = ctx->polls_used++; + ctx->pollptrs[p->x.idx] = p; + ctx->pollfds[p->x.idx].fd = fd; + ctx->pollfds[p->x.idx].events = p->events; + ctx->pollfds[p->x.idx].revents = 0; + p->ctx = ctx; + + return 0; +} + +/** + * @brief Add a socket object to a poll context. + * + * @param ctx Pointer to an already allocated poll context. + * @param s A SSH socket handle + * + * @return 0 on success, < 0 on error + */ +int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, ssh_socket s) { + ssh_poll_handle p_in, p_out; + int ret; + p_in=ssh_socket_get_poll_handle_in(s); + if(p_in==NULL) + return -1; + ret = ssh_poll_ctx_add(ctx,p_in); + if(ret != 0) + return ret; + p_out=ssh_socket_get_poll_handle_out(s); + if(p_in != p_out) + ret = ssh_poll_ctx_add(ctx,p_out); + return ret; +} + + +/** + * @brief Remove a poll object from a poll context. + * + * @param ctx Pointer to an already allocated poll context. + * @param p Pointer to an already allocated poll object. + */ +void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) { + size_t i; + + i = p->x.idx; + p->x.fd = ctx->pollfds[i].fd; + p->ctx = NULL; + + ctx->polls_used--; + + /* fill the empty poll slot with the last one */ + if (ctx->polls_used > 0 && ctx->polls_used != i) { + ctx->pollfds[i] = ctx->pollfds[ctx->polls_used]; + ctx->pollptrs[i] = ctx->pollptrs[ctx->polls_used]; + ctx->pollptrs[i]->x.idx = i; + } + + /* this will always leave at least chunk_size polls allocated */ + if (ctx->polls_allocated - ctx->polls_used > ctx->chunk_size) { + ssh_poll_ctx_resize(ctx, ctx->polls_allocated - ctx->chunk_size); + } +} + +/** + * @brief Poll all the sockets associated through a poll object with a + * poll context. If any of the events are set after the poll, the + * call back function of the socket will be called. + * This function should be called once within the programs main loop. + * + * @param ctx Pointer to an already allocated poll context. + * @param timeout An upper limit on the time for which ssh_poll_ctx() will + * block, in milliseconds. Specifying a negative value + * means an infinite timeout. This parameter is passed to + * the poll() function. + * @returns SSH_OK No error. + * SSH_ERROR Error happened during the poll. + * SSH_AGAIN Timeout occured + */ + +int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout) { + int rc; + int i, used; + ssh_poll_handle p; + socket_t fd; + int revents; + + if (!ctx->polls_used) + return SSH_ERROR; + + rc = ssh_poll(ctx->pollfds, ctx->polls_used, timeout); + if(rc < 0) + return SSH_ERROR; + if (rc == 0) + return SSH_AGAIN; + used = ctx->polls_used; + for (i = 0; i < used && rc > 0; ) { + if (!ctx->pollfds[i].revents) { + i++; + } else { + int ret; + + p = ctx->pollptrs[i]; + fd = ctx->pollfds[i].fd; + revents = ctx->pollfds[i].revents; + + if (p->cb && (ret = p->cb(p, fd, revents, p->cb_data)) < 0) { + if (ret == -2) { + return -1; + } + /* the poll was removed, reload the used counter and start again */ + used = ctx->polls_used; + i=0; + } else { + ctx->pollfds[i].revents = 0; + i++; + } + + rc--; + } + } + + return rc; +} + +/** + * @internal + * @brief gets the default poll structure for the current session, + * when used in blocking mode. + * @param session SSH session + * @returns the default ssh_poll_ctx + */ +ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session){ + if(session->default_poll_ctx != NULL) + return session->default_poll_ctx; + /* 2 is enough for the default one */ + session->default_poll_ctx = ssh_poll_ctx_new(2); + return session->default_poll_ctx; +} + +/* public event API */ + +struct ssh_event_fd_wrapper { + ssh_event_callback cb; + void * userdata; +}; + +struct ssh_event_struct { + ssh_poll_ctx ctx; +#ifdef WITH_SERVER + struct ssh_list *sessions; +#endif +}; + +/** + * @brief Create a new event context. It could be associated with many + * ssh_session objects and socket fd which are going to be polled at the + * same time as the event context. You would need a single event context + * per thread. + * + * @return The ssh_event object on success, NULL on failure. + */ +ssh_event ssh_event_new(void) { + ssh_event event; + + event = malloc(sizeof(struct ssh_event_struct)); + if (event == NULL) { + return NULL; + } + ZERO_STRUCTP(event); + + event->ctx = ssh_poll_ctx_new(2); + if(event->ctx == NULL) { + free(event); + return NULL; + } + +#ifdef WITH_SERVER + event->sessions = ssh_list_new(); + if(event->sessions == NULL) { + ssh_poll_ctx_free(event->ctx); + free(event); + return NULL; + } +#endif + + return event; +} + +static int ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int revents, + void *userdata) { + struct ssh_event_fd_wrapper *pw = (struct ssh_event_fd_wrapper *)userdata; + + (void)p; + if(pw->cb != NULL) { + return pw->cb(fd, revents, pw->userdata); + } + return 0; +} + +/** + * @brief Add a fd to the event and assign it a callback, + * when used in blocking mode. + * @param event The ssh_event + * @param fd Socket that will be polled. + * @param events Poll events that will be monitored for the socket. i.e. + * POLLIN, POLLPRI, POLLOUT + * @param cb Function to be called if any of the events are set. + * The prototype of cb is: + * int (*ssh_event_callback)(socket_t fd, int revents, + * void *userdata); + * @param userdata Userdata to be passed to the callback function. NULL if + * not needed. + * + * @returns SSH_OK on success + * SSH_ERROR on failure + */ +int ssh_event_add_fd(ssh_event event, socket_t fd, short events, + ssh_event_callback cb, void *userdata) { + ssh_poll_handle p; + struct ssh_event_fd_wrapper *pw; + + if(event == NULL || event->ctx == NULL || cb == NULL + || fd == SSH_INVALID_SOCKET) { + return SSH_ERROR; + } + pw = malloc(sizeof(struct ssh_event_fd_wrapper)); + if(pw == NULL) { + return SSH_ERROR; + } + + pw->cb = cb; + pw->userdata = userdata; + + /* pw is freed by ssh_event_remove_fd */ + p = ssh_poll_new(fd, events, ssh_event_fd_wrapper_callback, pw); + if(p == NULL) { + free(pw); + return SSH_ERROR; + } + + if(ssh_poll_ctx_add(event->ctx, p) < 0) { + free(pw); + ssh_poll_free(p); + return SSH_ERROR; + } + return SSH_OK; +} + +/** + * @brief remove the poll handle from session and assign them to a event, + * when used in blocking mode. + * + * @param event The ssh_event object + * @param session The session to add to the event. + * + * @returns SSH_OK on success + * SSH_ERROR on failure + */ +int ssh_event_add_session(ssh_event event, ssh_session session) { + unsigned int i; + ssh_poll_handle p; +#ifdef WITH_SERVER + struct ssh_iterator *iterator; +#endif + + if(event == NULL || event->ctx == NULL || session == NULL) { + return SSH_ERROR; + } + if(session->default_poll_ctx == NULL) { + return SSH_ERROR; + } + for(i = 0; i < session->default_poll_ctx->polls_used; i++) { + p = session->default_poll_ctx->pollptrs[i]; + ssh_poll_ctx_remove(session->default_poll_ctx, p); + ssh_poll_ctx_add(event->ctx, p); + } +#ifdef WITH_SERVER + iterator = ssh_list_get_iterator(event->sessions); + while(iterator != NULL) { + if((ssh_session)iterator->data == session) { + /* allow only one instance of this session */ + return SSH_OK; + } + iterator = iterator->next; + } + if(ssh_list_append(event->sessions, session) == SSH_ERROR) { + return SSH_ERROR; + } +#endif + return SSH_OK; +} + +/** + * @brief Poll all the sockets and sessions associated through an event object. + * If any of the events are set after the poll, the + * call back functions of the sessions or sockets will be called. + * This function should be called once within the programs main loop. + * + * @param event The ssh_event object to poll. + * @param timeout An upper limit on the time for which the poll will + * block, in milliseconds. Specifying a negative value + * means an infinite timeout. This parameter is passed to + * the poll() function. + * @returns SSH_OK No error. + * SSH_ERROR Error happened during the poll. + */ +int ssh_event_dopoll(ssh_event event, int timeout) { + int rc; + + if(event == NULL || event->ctx == NULL) { + return SSH_ERROR; + } + rc = ssh_poll_ctx_dopoll(event->ctx, timeout); + return rc; +} + +/** + * @brief Remove a socket fd from an event context. + * + * @param event The ssh_event object. + * @param fd The fd to remove. + * + * @returns SSH_OK on success + * SSH_ERROR on failure + */ +int ssh_event_remove_fd(ssh_event event, socket_t fd) { + register size_t i, used; + int rc = SSH_ERROR; + + if(event == NULL || event->ctx == NULL) { + return SSH_ERROR; + } + + used = event->ctx->polls_used; + for (i = 0; i < used; i++) { + if(fd == event->ctx->pollfds[i].fd) { + ssh_poll_handle p = event->ctx->pollptrs[i]; + struct ssh_event_fd_wrapper *pw = p->cb_data; + + ssh_poll_ctx_remove(event->ctx, p); + free(pw); + ssh_poll_free(p); + rc = SSH_OK; + /* restart the loop */ + used = event->ctx->polls_used; + i = 0; + } + } + + return rc; +} + +/** + * @brief Remove a session object from an event context. + * + * @param event The ssh_event object. + * @param session The session to remove. + * + * @returns SSH_OK on success + * SSH_ERROR on failure + */ +int ssh_event_remove_session(ssh_event event, ssh_session session) { + ssh_poll_handle p; + register size_t i, used; + int rc = SSH_ERROR; + socket_t session_fd; +#ifdef WITH_SERVER + struct ssh_iterator *iterator; +#endif + + if(event == NULL || event->ctx == NULL || session == NULL) { + return SSH_ERROR; + } + + session_fd = ssh_get_fd(session); + used = event->ctx->polls_used; + for(i = 0; i < used; i++) { + if(session_fd == event->ctx->pollfds[i].fd) { + p = event->ctx->pollptrs[i]; + ssh_poll_ctx_remove(event->ctx, p); + ssh_poll_ctx_add(session->default_poll_ctx, p); + rc = SSH_OK; + } + } +#ifdef WITH_SERVER + iterator = ssh_list_get_iterator(event->sessions); + while(iterator != NULL) { + if((ssh_session)iterator->data == session) { + ssh_list_remove(event->sessions, iterator); + /* there should be only one instance of this session */ + break; + } + iterator = iterator->next; + } +#endif + + return rc; +} + +/** + * @brief Free an event context. + * + * @param event The ssh_event object to free. + * Note: you have to manually remove sessions and socket + * fds before freeing the event object. + * + */ +void ssh_event_free(ssh_event event) { + if(event == NULL) { + return; + } + if(event->ctx != NULL) { + ssh_poll_ctx_free(event->ctx); + } +#ifdef WITH_SERVER + if(event->sessions != NULL) { + ssh_list_free(event->sessions); + } +#endif + free(event); +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/scp.c b/libssh/src/scp.c new file mode 100644 index 00000000..df4f5753 --- /dev/null +++ b/libssh/src/scp.c @@ -0,0 +1,809 @@ +/* + * scp - SSH scp wrapper functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/scp.h" + +/** + * @defgroup libssh_scp The SSH scp functions + * @ingroup libssh + * + * SCP protocol over SSH functions + * + * @{ + */ + +/** + * @brief Create a new scp session. + * + * @param[in] session The SSH session to use. + * + * @param[in] mode One of SSH_SCP_WRITE or SSH_SCP_READ, depending if you + * need to drop files remotely or read them. + * It is not possible to combine read and write. + * SSH_SCP_RECURSIVE Flag can be or'ed to this to indicate + * that you're going to use recursion. Browsing through + * directories is not possible without this. + * + * @param[in] location The directory in which write or read will be done. Any + * push or pull will be relative to this place. + * This can also be a pattern of files to download (read). + * + * @returns A ssh_scp handle, NULL if the creation was impossible. + */ +ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location){ + ssh_scp scp=malloc(sizeof(struct ssh_scp_struct)); + if(scp == NULL){ + ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp"); + return NULL; + } + ZERO_STRUCTP(scp); + if((mode&~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE && (mode &~SSH_SCP_RECURSIVE) != SSH_SCP_READ){ + ssh_set_error(session,SSH_FATAL,"Invalid mode %d for ssh_scp_new()",mode); + ssh_scp_free(scp); + return NULL; + } + scp->location=strdup(location); + if (scp->location == NULL) { + ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp"); + ssh_scp_free(scp); + return NULL; + } + scp->session=session; + scp->mode=mode & ~SSH_SCP_RECURSIVE; + scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0; + scp->channel=NULL; + scp->state=SSH_SCP_NEW; + return scp; +} + +int ssh_scp_init(ssh_scp scp){ + int r; + char execbuffer[1024]; + uint8_t code; + if(scp==NULL) + return SSH_ERROR; + if(scp->state != SSH_SCP_NEW){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_init called under invalid state"); + return SSH_ERROR; + } + ssh_log(scp->session,SSH_LOG_PROTOCOL,"Initializing scp session %s %son location '%s'", + scp->mode==SSH_SCP_WRITE?"write":"read", + scp->recursive?"recursive ":"", + scp->location); + scp->channel=ssh_channel_new(scp->session); + if(scp->channel == NULL){ + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + r= ssh_channel_open_session(scp->channel); + if(r==SSH_ERROR){ + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + if(scp->mode == SSH_SCP_WRITE) + snprintf(execbuffer,sizeof(execbuffer),"scp -t %s %s", + scp->recursive ? "-r":"", scp->location); + else + snprintf(execbuffer,sizeof(execbuffer),"scp -f %s %s", + scp->recursive ? "-r":"", scp->location); + if(ssh_channel_request_exec(scp->channel,execbuffer) == SSH_ERROR){ + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + if(scp->mode == SSH_SCP_WRITE){ + r=ssh_channel_read(scp->channel,&code,1,0); + if(r<=0){ + ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + if(code != 0){ + ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + } else { + ssh_channel_write(scp->channel,"",1); + } + if(scp->mode == SSH_SCP_WRITE) + scp->state=SSH_SCP_WRITE_INITED; + else + scp->state=SSH_SCP_READ_INITED; + return SSH_OK; +} + +int ssh_scp_close(ssh_scp scp){ + char buffer[128]; + int err; + if(scp==NULL) + return SSH_ERROR; + if(scp->channel != NULL){ + if(ssh_channel_send_eof(scp->channel) == SSH_ERROR){ + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + /* avoid situations where data are buffered and + * not yet stored on disk. This can happen if the close is sent + * before we got the EOF back + */ + while(!ssh_channel_is_eof(scp->channel)){ + err=ssh_channel_read(scp->channel,buffer,sizeof(buffer),0); + if(err==SSH_ERROR || err==0) + break; + } + if(ssh_channel_close(scp->channel) == SSH_ERROR){ + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + ssh_channel_free(scp->channel); + scp->channel=NULL; + } + scp->state=SSH_SCP_NEW; + return SSH_OK; +} + +void ssh_scp_free(ssh_scp scp){ + if(scp==NULL) + return; + if(scp->state != SSH_SCP_NEW) + ssh_scp_close(scp); + if(scp->channel) + ssh_channel_free(scp->channel); + SAFE_FREE(scp->location); + SAFE_FREE(scp->request_name); + SAFE_FREE(scp->warning); + SAFE_FREE(scp); +} + +/** + * @brief Create a directory in a scp in sink mode. + * + * @param[in] scp The scp handle. + * + * @param[in] dirname The name of the directory being created. + * + * @param[in] mode The UNIX permissions for the new directory, e.g. 0755. + * + * @returns SSH_OK if the directory has been created, SSH_ERROR if + * an error occured. + * + * @see ssh_scp_leave_directory() + */ +int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode){ + char buffer[1024]; + int r; + uint8_t code; + char *dir; + char *perms; + if(scp==NULL) + return SSH_ERROR; + if(scp->state != SSH_SCP_WRITE_INITED){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_directory called under invalid state"); + return SSH_ERROR; + } + dir=ssh_basename(dirname); + perms=ssh_scp_string_mode(mode); + snprintf(buffer, sizeof(buffer), "D%s 0 %s\n", perms, dir); + SAFE_FREE(dir); + SAFE_FREE(perms); + r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); + if(r==SSH_ERROR){ + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + r=ssh_channel_read(scp->channel,&code,1,0); + if(r<=0){ + ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + if(code != 0){ + ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + return SSH_OK; +} + +/** + * @brief Leave a directory. + * + * @returns SSH_OK if the directory has been left,SSH_ERROR if an + * error occured. + * + * @see ssh_scp_push_directory() + */ + int ssh_scp_leave_directory(ssh_scp scp){ + char buffer[]="E\n"; + int r; + uint8_t code; + if(scp==NULL) + return SSH_ERROR; + if(scp->state != SSH_SCP_WRITE_INITED){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_leave_directory called under invalid state"); + return SSH_ERROR; + } + r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); + if(r==SSH_ERROR){ + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + r=ssh_channel_read(scp->channel,&code,1,0); + if(r<=0){ + ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + if(code != 0){ + ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + return SSH_OK; +} + +/** + * @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit size. + * + * @param[in] scp The scp handle. + * + * @param[in] filename The name of the file being sent. It should not contain + * any path indicator + * + * @param[in] size Exact size in bytes of the file being sent. + * + * @param[in] mode The UNIX permissions for the new file, e.g. 0644. + * + * @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an + * error occured. + * + * @see ssh_scp_push_file() + */ +int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int mode){ + char buffer[1024]; + int r; + uint8_t code; + char *file; + char *perms; + if(scp==NULL) + return SSH_ERROR; + if(scp->state != SSH_SCP_WRITE_INITED){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_file called under invalid state"); + return SSH_ERROR; + } + file=ssh_basename(filename); + perms=ssh_scp_string_mode(mode); + ssh_log(scp->session,SSH_LOG_PROTOCOL,"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",file,size,perms); + snprintf(buffer, sizeof(buffer), "C%s %" PRIu64 " %s\n", perms, size, file); + SAFE_FREE(file); + SAFE_FREE(perms); + r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); + if(r==SSH_ERROR){ + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + r=ssh_channel_read(scp->channel,&code,1,0); + if(r<=0){ + ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + if(code != 0){ + ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + scp->filelen = size; + scp->processed = 0; + scp->state=SSH_SCP_WRITE_WRITING; + return SSH_OK; +} + +/** + * @brief Initialize the sending of a file to a scp in sink mode. + * + * @param[in] scp The scp handle. + * + * @param[in] filename The name of the file being sent. It should not contain + * any path indicator + * + * @param[in] size Exact size in bytes of the file being sent. + * + * @param[in] mode The UNIX permissions for the new file, e.g. 0644. + * + * @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an + * error occured. + */ +int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode){ + return ssh_scp_push_file64(scp, filename, (uint64_t) size, mode); +} + +/** + * @internal + * + * @brief Wait for a response of the scp server. + * + * @param[in] scp The scp handle. + * + * @param[out] response A pointer where the response message must be copied if + * any. This pointer must then be free'd. + * + * @returns The return code, SSH_ERROR a error occured. + */ +int ssh_scp_response(ssh_scp scp, char **response){ + unsigned char code; + int r; + char msg[128]; + if(scp==NULL) + return SSH_ERROR; + r=ssh_channel_read(scp->channel,&code,1,0); + if(r == SSH_ERROR) + return SSH_ERROR; + if(code == 0) + return 0; + if(code > 2){ + ssh_set_error(scp->session,SSH_FATAL, "SCP: invalid status code %ud received", code); + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + r=ssh_scp_read_string(scp,msg,sizeof(msg)); + if(r==SSH_ERROR) + return r; + /* Warning */ + if(code == 1){ + ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Warning: status code 1 received: %s", msg); + ssh_log(scp->session,SSH_LOG_RARE,"SCP: Warning: status code 1 received: %s", msg); + if(response) + *response=strdup(msg); + return 1; + } + if(code == 2){ + ssh_set_error(scp->session,SSH_FATAL, "SCP: Error: status code 2 received: %s", msg); + if(response) + *response=strdup(msg); + return 2; + } + /* Not reached */ + return SSH_ERROR; +} + +/** + * @brief Write into a remote scp file. + * + * @param[in] scp The scp handle. + * + * @param[in] buffer The buffer to write. + * + * @param[in] len The number of bytes to write. + * + * @returns SSH_OK if the write was successful, SSH_ERROR an error + * occured while writing. + */ +int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len){ + int w; + int r; + uint8_t code; + if(scp==NULL) + return SSH_ERROR; + if(scp->state != SSH_SCP_WRITE_WRITING){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_write called under invalid state"); + return SSH_ERROR; + } + if(scp->processed + len > scp->filelen) + len = (size_t) (scp->filelen - scp->processed); + /* hack to avoid waiting for window change */ + r = ssh_channel_poll(scp->channel, 0); + if (r == SSH_ERROR) { + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + w=ssh_channel_write(scp->channel,buffer,len); + if(w != SSH_ERROR) + scp->processed += w; + else { + scp->state=SSH_SCP_ERROR; + //return=channel_get_exit_status(scp->channel); + return SSH_ERROR; + } + /* Far end sometimes send a status message, which we need to read + * and handle */ + r = ssh_channel_poll(scp->channel,0); + if(r > 0){ + r = ssh_channel_read(scp->channel, &code, 1, 0); + if(r == SSH_ERROR){ + return SSH_ERROR; + } + if(code == 1 || code == 2){ + ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Error: status code %i received", code); + return SSH_ERROR; + } + } + /* Check if we arrived at end of file */ + if(scp->processed == scp->filelen) { + code = 0; + w = ssh_channel_write(scp->channel, &code, 1); + if(w == SSH_ERROR){ + scp->state = SSH_SCP_ERROR; + return SSH_ERROR; + } + scp->processed=scp->filelen=0; + scp->state=SSH_SCP_WRITE_INITED; + } + return SSH_OK; +} + +/** + * @brief Read a string on a channel, terminated by '\n' + * + * @param[in] scp The scp handle. + * + * @param[out] buffer A pointer to a buffer to place the string. + * + * @param[in] len The size of the buffer in bytes. If the string is bigger + * than len-1, only len-1 bytes are read and the string is + * null-terminated. + * + * @returns SSH_OK if the string was read, SSH_ERROR if an error + * occured while reading. + */ +int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len){ + size_t r=0; + int err=SSH_OK; + if(scp==NULL) + return SSH_ERROR; + while(rchannel,&buffer[r],1,0); + if(err==SSH_ERROR){ + break; + } + if(err==0){ + ssh_set_error(scp->session,SSH_FATAL,"End of file while reading string"); + err=SSH_ERROR; + break; + } + r++; + if(buffer[r-1] == '\n') + break; + } + buffer[r]=0; + return err; +} + +/** + * @brief Wait for a scp request (file, directory). + * + * @returns SSH_SCP_REQUEST_NEWFILE: The other side is sending + * a file + * SSH_SCP_REQUEST_NEWDIR: The other side is sending + * a directory + * SSH_SCP_REQUEST_ENDDIR: The other side has + * finished with the current + * directory + * SSH_SCP_REQUEST_WARNING: The other side sent us a warning + * SSH_SCP_REQUEST_EOF: The other side finished sending us + * files and data. + * SSH_ERROR: Some error happened + * + * @see ssh_scp_read() + * @see ssh_scp_deny_request() + * @see ssh_scp_accept_request() + * @see ssh_scp_request_get_warning() + */ +int ssh_scp_pull_request(ssh_scp scp){ + char buffer[4096] = {0}; + char *mode=NULL; + char *p,*tmp; + uint64_t size; + char *name=NULL; + int err; + if(scp==NULL) + return SSH_ERROR; + if(scp->state != SSH_SCP_READ_INITED){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_pull_request called under invalid state"); + return SSH_ERROR; + } + err=ssh_scp_read_string(scp,buffer,sizeof(buffer)); + if(err==SSH_ERROR){ + if(ssh_channel_is_eof(scp->channel)){ + scp->state=SSH_SCP_TERMINATED; + return SSH_SCP_REQUEST_EOF; + } + return err; + } + p=strchr(buffer,'\n'); + if(p!=NULL) + *p='\0'; + ssh_log(scp->session,SSH_LOG_PROTOCOL,"Received SCP request: '%s'",buffer); + switch(buffer[0]){ + case 'C': + /* File */ + case 'D': + /* Directory */ + p=strchr(buffer,' '); + if(p==NULL) + goto error; + *p='\0'; + p++; + //mode=strdup(&buffer[1]); + scp->request_mode=ssh_scp_integer_mode(&buffer[1]); + tmp=p; + p=strchr(p,' '); + if(p==NULL) + goto error; + *p=0; + size = strtoull(tmp,NULL,10); + p++; + name=strdup(p); + SAFE_FREE(scp->request_name); + scp->request_name=name; + if(buffer[0]=='C'){ + scp->filelen=size; + scp->request_type=SSH_SCP_REQUEST_NEWFILE; + } else { + scp->filelen='0'; + scp->request_type=SSH_SCP_REQUEST_NEWDIR; + } + scp->state=SSH_SCP_READ_REQUESTED; + scp->processed = 0; + return scp->request_type; + break; + case 'E': + scp->request_type=SSH_SCP_REQUEST_ENDDIR; + ssh_channel_write(scp->channel,"",1); + return scp->request_type; + case 0x1: + ssh_set_error(scp->session,SSH_REQUEST_DENIED,"SCP: Warning: %s",&buffer[1]); + scp->request_type=SSH_SCP_REQUEST_WARNING; + SAFE_FREE(scp->warning); + scp->warning=strdup(&buffer[1]); + return scp->request_type; + case 0x2: + ssh_set_error(scp->session,SSH_FATAL,"SCP: Error: %s",&buffer[1]); + return SSH_ERROR; + case 'T': + /* Timestamp */ + default: + ssh_set_error(scp->session,SSH_FATAL,"Unhandled message: (%d)%s",buffer[0],buffer); + return SSH_ERROR; + } + + /* a parsing error occured */ + error: + SAFE_FREE(name); + SAFE_FREE(mode); + ssh_set_error(scp->session,SSH_FATAL,"Parsing error while parsing message: %s",buffer); + return SSH_ERROR; +} + +/** + * @brief Deny the transfer of a file or creation of a directory coming from the + * remote party. + * + * @param[in] scp The scp handle. + * @param[in] reason A nul-terminated string with a human-readable + * explanation of the deny. + * + * @returns SSH_OK if the message was sent, SSH_ERROR if the sending + * the message failed, or sending it in a bad state. + */ +int ssh_scp_deny_request(ssh_scp scp, const char *reason){ + char buffer[4096]; + int err; + if(scp==NULL) + return SSH_ERROR; + if(scp->state != SSH_SCP_READ_REQUESTED){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state"); + return SSH_ERROR; + } + snprintf(buffer,sizeof(buffer),"%c%s\n",2,reason); + err=ssh_channel_write(scp->channel,buffer,strlen(buffer)); + if(err==SSH_ERROR) { + return SSH_ERROR; + } + else { + scp->state=SSH_SCP_READ_INITED; + return SSH_OK; + } +} + +/** + * @brief Accepts transfer of a file or creation of a directory coming from the + * remote party. + * + * @param[in] scp The scp handle. + * + * @returns SSH_OK if the message was sent, SSH_ERROR if sending the + * message failed, or sending it in a bad state. + */ +int ssh_scp_accept_request(ssh_scp scp){ + char buffer[]={0x00}; + int err; + if(scp==NULL) + return SSH_ERROR; + if(scp->state != SSH_SCP_READ_REQUESTED){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state"); + return SSH_ERROR; + } + err=ssh_channel_write(scp->channel,buffer,1); + if(err==SSH_ERROR) { + return SSH_ERROR; + } + if(scp->request_type==SSH_SCP_REQUEST_NEWFILE) + scp->state=SSH_SCP_READ_READING; + else + scp->state=SSH_SCP_READ_INITED; + return SSH_OK; +} + +/** @brief Read from a remote scp file + * @param[in] scp The scp handle. + * + * @param[in] buffer The destination buffer. + * + * @param[in] size The size of the buffer. + * + * @returns The nNumber of bytes read, SSH_ERROR if an error occured + * while reading. + */ +int ssh_scp_read(ssh_scp scp, void *buffer, size_t size){ + int r; + int code; + if(scp==NULL) + return SSH_ERROR; + if(scp->state == SSH_SCP_READ_REQUESTED && scp->request_type == SSH_SCP_REQUEST_NEWFILE){ + r=ssh_scp_accept_request(scp); + if(r==SSH_ERROR) + return r; + } + if(scp->state != SSH_SCP_READ_READING){ + ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_read called under invalid state"); + return SSH_ERROR; + } + if(scp->processed + size > scp->filelen) + size = (size_t) (scp->filelen - scp->processed); + if(size > 65536) + size=65536; /* avoid too large reads */ + r=ssh_channel_read(scp->channel,buffer,size,0); + if(r != SSH_ERROR) + scp->processed += r; + else { + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + /* Check if we arrived at end of file */ + if(scp->processed == scp->filelen) { + scp->processed=scp->filelen=0; + ssh_channel_write(scp->channel,"",1); + code=ssh_scp_response(scp,NULL); + if(code == 0){ + scp->state=SSH_SCP_READ_INITED; + return r; + } + if(code==1){ + scp->state=SSH_SCP_READ_INITED; + return SSH_ERROR; + } + scp->state=SSH_SCP_ERROR; + return SSH_ERROR; + } + return r; +} + +/** + * @brief Get the name of the directory or file being pushed from the other + * party. + * + * @returns The file name, NULL on error. The string should not be + * freed. + */ +const char *ssh_scp_request_get_filename(ssh_scp scp){ + if(scp==NULL) + return NULL; + return scp->request_name; +} + +/** + * @brief Get the permissions of the directory or file being pushed from the + * other party. + * + * @returns The UNIX permission, e.g 0644, -1 on error. + */ +int ssh_scp_request_get_permissions(ssh_scp scp){ + if(scp==NULL) + return -1; + return scp->request_mode; +} + +/** @brief Get the size of the file being pushed from the other party. + * + * @returns The numeric size of the file being read. + * @warning The real size may not fit in a 32 bits field and may + * be truncated. + * @see ssh_scp_request_get_size64() + */ +size_t ssh_scp_request_get_size(ssh_scp scp){ + if(scp==NULL) + return 0; + return scp->filelen; +} + +/** @brief Get the size of the file being pushed from the other party. + * + * @returns The numeric size of the file being read. + */ +uint64_t ssh_scp_request_get_size64(ssh_scp scp){ + if(scp==NULL) + return 0; + return scp->filelen; +} + +/** + * @brief Convert a scp text mode to an integer. + * + * @param[in] mode The mode to convert, e.g. "0644". + * + * @returns An integer value, e.g. 420 for "0644". + */ +int ssh_scp_integer_mode(const char *mode){ + int value=strtoul(mode,NULL,8) & 0xffff; + return value; +} + +/** + * @brief Convert a unix mode into a scp string. + * + * @param[in] mode The mode to convert, e.g. 420 or 0644. + * + * @returns A pointer to a malloc'ed string containing the scp mode, + * e.g. "0644". + */ +char *ssh_scp_string_mode(int mode){ + char buffer[16]; + snprintf(buffer,sizeof(buffer),"%.4o",mode); + return strdup(buffer); +} + +/** + * @brief Get the warning string from a scp handle. + * + * @param[in] scp The scp handle. + * + * @returns A warning string, or NULL on error. The string should + * not be freed. + */ +const char *ssh_scp_request_get_warning(ssh_scp scp){ + if(scp==NULL) + return NULL; + return scp->warning; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/server.c b/libssh/src/server.c new file mode 100644 index 00000000..db8f8152 --- /dev/null +++ b/libssh/src/server.c @@ -0,0 +1,1205 @@ +/* + * server.c - functions for creating a SSH server + * + * This file is part of the SSH Library + * + * Copyright (c) 2004-2005 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include + + /* + * is necessary for getaddrinfo before Windows XP, but it isn't + * available on some platforms like MinGW. + */ +# ifdef HAVE_WSPIAPI_H +# include +# endif +#else +# include +#endif + +#include "libssh/priv.h" +#include "libssh/libssh.h" +#include "libssh/server.h" +#include "libssh/ssh2.h" +#include "libssh/buffer.h" +#include "libssh/packet.h" +#include "libssh/socket.h" +#include "libssh/session.h" +#include "libssh/kex.h" +#include "libssh/misc.h" +#include "libssh/pki.h" +#include "libssh/dh.h" +#include "libssh/messages.h" +#include "libssh/options.h" + +#define set_status(session, status) do {\ + if (session->common.callbacks && session->common.callbacks->connect_status_function) \ + session->common.callbacks->connect_status_function(session->common.callbacks->userdata, status); \ + } while (0) + +static int dh_handshake_server(ssh_session session); + + +/** + * @addtogroup libssh_server + * + * @{ + */ + +/** @internal + * This functions sets the Key Exchange protocols to be accepted + * by the server. They depend on + * -What the user asked (via options) + * -What is available (keys) + * It should then accept the intersection of what the user asked + * and what is available, and return an error if nothing matches + */ + +static int server_set_kex(ssh_session session) { + struct ssh_kex_struct *server = &session->next_crypto->server_kex; + int i, j, rc; + const char *wanted; + char hostkeys[64] = {0}; + enum ssh_keytypes_e keytype; + size_t len; + + ZERO_STRUCTP(server); + ssh_get_random(server->cookie, 16, 0); + +#ifdef HAVE_ECC + if (session->srv.ecdsa_key != NULL) { + snprintf(hostkeys, sizeof(hostkeys), + "%s", session->srv.ecdsa_key->type_c); + } +#endif + if (session->srv.dsa_key != NULL) { + len = strlen(hostkeys); + keytype = ssh_key_type(session->srv.dsa_key); + + snprintf(hostkeys + len, sizeof(hostkeys) - len, + ",%s", ssh_key_type_to_char(keytype)); + } + if (session->srv.rsa_key != NULL) { + len = strlen(hostkeys); + keytype = ssh_key_type(session->srv.rsa_key); + + snprintf(hostkeys + len, sizeof(hostkeys) - len, + ",%s", ssh_key_type_to_char(keytype)); + } + + if (strlen(hostkeys) == 0) { + return -1; + } + + rc = ssh_options_set_algo(session, + SSH_HOSTKEYS, + hostkeys[0] == ',' ? hostkeys + 1 : hostkeys); + if (rc < 0) { + return -1; + } + + for (i = 0; i < 10; i++) { + if ((wanted = session->opts.wanted_methods[i]) == NULL) { + wanted = ssh_kex_get_supported_method(i); + } + server->methods[i] = strdup(wanted); + if (server->methods[i] == NULL) { + for (j = 0; j < i; j++) { + SAFE_FREE(server->methods[j]); + } + return -1; + } + } + + return 0; +} + +/** @internal + * @brief parse an incoming SSH_MSG_KEXDH_INIT packet and complete + * key exchange + **/ +static int ssh_server_kexdh_init(ssh_session session, ssh_buffer packet){ + ssh_string e; + e = buffer_get_ssh_string(packet); + if (e == NULL) { + ssh_set_error(session, SSH_FATAL, "No e number in client request"); + return -1; + } + if (dh_import_e(session, e) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot import e number"); + session->session_state=SSH_SESSION_STATE_ERROR; + } else { + session->dh_handshake_state=DH_STATE_INIT_SENT; + dh_handshake_server(session); + } + ssh_string_free(e); + return SSH_OK; +} + +SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){ + int rc; + (void)type; + (void)user; + enter_function(); + ssh_log(session,SSH_LOG_PACKET,"Received SSH_MSG_KEXDH_INIT"); + if(session->dh_handshake_state != DH_STATE_INIT){ + ssh_log(session,SSH_LOG_RARE,"Invalid state for SSH_MSG_KEXDH_INIT"); + goto error; + } + switch(session->next_crypto->kex_type){ + case SSH_KEX_DH_GROUP1_SHA1: + case SSH_KEX_DH_GROUP14_SHA1: + rc=ssh_server_kexdh_init(session, packet); + break; + #ifdef HAVE_ECDH + case SSH_KEX_ECDH_SHA2_NISTP256: + rc = ssh_server_ecdh_init(session, packet); + break; + #endif + default: + ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_kexdh_init"); + rc = SSH_ERROR; + } + if (rc == SSH_ERROR) + session->session_state = SSH_SESSION_STATE_ERROR; + error: + leave_function(); + return SSH_PACKET_USED; +} + +int ssh_get_key_params(ssh_session session, ssh_key *privkey){ + ssh_key pubkey; + ssh_string pubkey_blob; + int rc; + + switch(session->srv.hostkey) { + case SSH_KEYTYPE_DSS: + *privkey = session->srv.dsa_key; + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + *privkey = session->srv.rsa_key; + break; + case SSH_KEYTYPE_ECDSA: + *privkey = session->srv.ecdsa_key; + break; + case SSH_KEYTYPE_UNKNOWN: + *privkey = NULL; + } + + rc = ssh_pki_export_privkey_to_pubkey(*privkey, &pubkey); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, + "Could not get the public key from the private key"); + + return -1; + } + + rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob); + ssh_key_free(pubkey); + if (rc < 0) { + ssh_set_error_oom(session); + return -1; + } + + dh_import_pubkey(session, pubkey_blob); + return SSH_OK; +} + +static int dh_handshake_server(ssh_session session) { + ssh_key privkey; + //ssh_string pubkey_blob = NULL; + ssh_string sig_blob; + ssh_string f; + + if (dh_generate_y(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Could not create y number"); + return -1; + } + if (dh_generate_f(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Could not create f number"); + return -1; + } + + f = dh_get_f(session); + if (f == NULL) { + ssh_set_error(session, SSH_FATAL, "Could not get the f number"); + return -1; + } + + if (ssh_get_key_params(session,&privkey) != SSH_OK){ + ssh_string_free(f); + return -1; + } + + if (dh_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Could not import the public key"); + ssh_string_free(f); + return -1; + } + + if (make_sessionid(session) != SSH_OK) { + ssh_set_error(session, SSH_FATAL, "Could not create a session id"); + ssh_string_free(f); + return -1; + } + + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + if (sig_blob == NULL) { + ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); + ssh_string_free(f); + return -1; + } + + /* Free private keys as they should not be readable after this point */ + if (session->srv.rsa_key) { + ssh_key_free(session->srv.rsa_key); + session->srv.rsa_key = NULL; + } + if (session->srv.dsa_key) { + ssh_key_free(session->srv.dsa_key); + session->srv.dsa_key = NULL; + } +#ifdef HAVE_ECC + if (session->srv.ecdsa_key) { + ssh_key_free(session->srv.ecdsa_key); + session->srv.ecdsa_key = NULL; + } +#endif + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY) < 0 || + buffer_add_ssh_string(session->out_buffer, + session->next_crypto->server_pubkey) < 0 || + buffer_add_ssh_string(session->out_buffer, f) < 0 || + buffer_add_ssh_string(session->out_buffer, sig_blob) < 0) { + ssh_set_error(session, SSH_FATAL, "Not enough space"); + buffer_reinit(session->out_buffer); + ssh_string_free(f); + ssh_string_free(sig_blob); + return -1; + } + ssh_string_free(f); + ssh_string_free(sig_blob); + if (packet_send(session) == SSH_ERROR) { + return -1; + } + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { + buffer_reinit(session->out_buffer); + return -1; + } + + if (packet_send(session) == SSH_ERROR) { + return -1; + } + ssh_log(session, SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent"); + session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; + + return 0; +} + +/** + * @internal + * + * @brief A function to be called each time a step has been done in the + * connection. + */ +static void ssh_server_connection_callback(ssh_session session){ + int ssh1,ssh2; + enter_function(); + switch(session->session_state){ + case SSH_SESSION_STATE_NONE: + case SSH_SESSION_STATE_CONNECTING: + case SSH_SESSION_STATE_SOCKET_CONNECTED: + break; + case SSH_SESSION_STATE_BANNER_RECEIVED: + if (session->clientbanner == NULL) { + goto error; + } + set_status(session, 0.4f); + ssh_log(session, SSH_LOG_RARE, + "SSH client banner: %s", session->clientbanner); + + /* Here we analyze the different protocols the server allows. */ + if (ssh_analyze_banner(session, 1, &ssh1, &ssh2) < 0) { + goto error; + } + /* Here we decide which version of the protocol to use. */ + if (ssh2 && session->opts.ssh2) { + session->version = 2; + } else if (ssh1 && session->opts.ssh1) { + session->version = 1; + } else if (ssh1 && !session->opts.ssh1) { +#ifdef WITH_SSH1 + ssh_set_error(session, SSH_FATAL, + "SSH-1 protocol not available (configure session to allow SSH-1)"); + goto error; +#else + ssh_set_error(session, SSH_FATAL, + "SSH-1 protocol not available (libssh compiled without SSH-1 support)"); + goto error; +#endif + } else { + ssh_set_error(session, SSH_FATAL, + "No version of SSH protocol usable (banner: %s)", + session->clientbanner); + goto error; + } + /* from now, the packet layer is handling incoming packets */ + if(session->version==2) + session->socket_callbacks.data=ssh_packet_socket_callback; +#ifdef WITH_SSH1 + else + session->socket_callbacks.data=ssh_packet_socket_callback1; +#endif + ssh_packet_set_default_callbacks(session); + set_status(session, 0.5f); + session->session_state=SSH_SESSION_STATE_INITIAL_KEX; + if (ssh_send_kex(session, 1) < 0) { + goto error; + } + break; + case SSH_SESSION_STATE_INITIAL_KEX: + /* TODO: This state should disappear in favor of get_key handle */ +#ifdef WITH_SSH1 + if(session->version==1){ + if (ssh_get_kex1(session) < 0) + goto error; + set_status(session,0.6f); + session->connected = 1; + break; + } +#endif + break; + case SSH_SESSION_STATE_KEXINIT_RECEIVED: + set_status(session,0.6f); + ssh_list_kex(session, &session->next_crypto->client_kex); // log client kex + if (ssh_kex_select_methods(session) < 0) { + goto error; + } + if (crypt_set_algorithms_server(session) == SSH_ERROR) + goto error; + set_status(session,0.8f); + session->session_state=SSH_SESSION_STATE_DH; + break; + case SSH_SESSION_STATE_DH: + if(session->dh_handshake_state==DH_STATE_FINISHED){ + if (generate_session_keys(session) < 0) { + goto error; + } + + /* + * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and + * current_crypto + */ + if (session->current_crypto) { + crypto_free(session->current_crypto); + } + + /* FIXME TODO later, include a function to change keys */ + session->current_crypto = session->next_crypto; + session->next_crypto = crypto_new(); + if (session->next_crypto == NULL) { + goto error; + } + set_status(session,1.0f); + session->connected = 1; + session->session_state=SSH_SESSION_STATE_AUTHENTICATING; + } + break; + case SSH_SESSION_STATE_AUTHENTICATING: + break; + case SSH_SESSION_STATE_ERROR: + goto error; + default: + ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); + } + leave_function(); + return; + error: + ssh_socket_close(session->socket); + session->alive = 0; + session->session_state=SSH_SESSION_STATE_ERROR; + leave_function(); +} + +/** + * @internal + * + * @brief Gets the banner from socket and saves it in session. + * Updates the session state + * + * @param data pointer to the beginning of header + * @param len size of the banner + * @param user is a pointer to session + * @returns Number of bytes processed, or zero if the banner is not complete. + */ +static int callback_receive_banner(const void *data, size_t len, void *user) { + char *buffer = (char *) data; + ssh_session session = (ssh_session) user; + char *str = NULL; + size_t i; + int ret=0; + + enter_function(); + + for (i = 0; i < len; i++) { +#ifdef WITH_PCAP + if(session->pcap_ctx && buffer[i] == '\n') { + ssh_pcap_context_write(session->pcap_ctx, + SSH_PCAP_DIR_IN, + buffer, + i + 1, + i + 1); + } +#endif + if (buffer[i] == '\r') { + buffer[i]='\0'; + } + + if (buffer[i] == '\n') { + buffer[i]='\0'; + + str = strdup(buffer); + /* number of bytes read */ + ret = i + 1; + session->clientbanner = str; + session->session_state = SSH_SESSION_STATE_BANNER_RECEIVED; + ssh_log(session, SSH_LOG_PACKET, "Received banner: %s", str); + session->ssh_connection_callback(session); + + leave_function(); + return ret; + } + + if(i > 127) { + /* Too big banner */ + session->session_state = SSH_SESSION_STATE_ERROR; + ssh_set_error(session, SSH_FATAL, "Receiving banner: too large banner"); + + leave_function(); + return 0; + } + } + + leave_function(); + return ret; +} + +/* returns 0 until the key exchange is not finished */ +static int ssh_server_kex_termination(void *s){ + ssh_session session = s; + if (session->session_state != SSH_SESSION_STATE_ERROR && + session->session_state != SSH_SESSION_STATE_AUTHENTICATING && + session->session_state != SSH_SESSION_STATE_DISCONNECTED) + return 0; + else + return 1; +} + +/* Do the banner and key exchange */ +int ssh_handle_key_exchange(ssh_session session) { + int rc; + if (session->session_state != SSH_SESSION_STATE_NONE) + goto pending; + rc = ssh_send_banner(session, 1); + if (rc < 0) { + return SSH_ERROR; + } + + session->alive = 1; + + session->ssh_connection_callback = ssh_server_connection_callback; + session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED; + ssh_socket_set_callbacks(session->socket,&session->socket_callbacks); + session->socket_callbacks.data=callback_receive_banner; + session->socket_callbacks.exception=ssh_socket_exception_callback; + session->socket_callbacks.userdata=session; + + rc = server_set_kex(session); + if (rc < 0) { + return SSH_ERROR; + } + pending: + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + ssh_server_kex_termination,session); + ssh_log(session,SSH_LOG_PACKET, "ssh_handle_key_exchange: Actual state : %d", + session->session_state); + if (rc != SSH_OK) + return rc; + if (session->session_state == SSH_SESSION_STATE_ERROR || + session->session_state == SSH_SESSION_STATE_DISCONNECTED) { + return SSH_ERROR; + } + + return SSH_OK; +} + +/* messages */ + +static int ssh_message_auth_reply_default(ssh_message msg,int partial) { + ssh_session session = msg->session; + char methods_c[128] = {0}; + ssh_string methods = NULL; + int rc = SSH_ERROR; + + enter_function(); + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_FAILURE) < 0) { + return rc; + } + + if (session->auth_methods == 0) { + session->auth_methods = SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD; + } + if (session->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { + strncat(methods_c, "publickey,", + sizeof(methods_c) - strlen(methods_c) - 1); + } + if (session->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { + strncat(methods_c, "keyboard-interactive,", + sizeof(methods_c) - strlen(methods_c) - 1); + } + if (session->auth_methods & SSH_AUTH_METHOD_PASSWORD) { + strncat(methods_c, "password,", + sizeof(methods_c) - strlen(methods_c) - 1); + } + if (session->auth_methods & SSH_AUTH_METHOD_HOSTBASED) { + strncat(methods_c, "hostbased,", + sizeof(methods_c) - strlen(methods_c) - 1); + } + + if (methods_c[0] == '\0' || methods_c[strlen(methods_c)-1] != ',') { + return SSH_ERROR; + } + + /* Strip the comma. */ + methods_c[strlen(methods_c) - 1] = '\0'; // strip the comma. We are sure there is at + + ssh_log(session, SSH_LOG_PACKET, + "Sending a auth failure. methods that can continue: %s", methods_c); + + methods = ssh_string_from_char(methods_c); + if (methods == NULL) { + goto error; + } + + if (buffer_add_ssh_string(msg->session->out_buffer, methods) < 0) { + goto error; + } + + if (partial) { + if (buffer_add_u8(session->out_buffer, 1) < 0) { + goto error; + } + } else { + if (buffer_add_u8(session->out_buffer, 0) < 0) { + goto error; + } + } + + rc = packet_send(msg->session); +error: + ssh_string_free(methods); + + leave_function(); + return rc; +} + +static int ssh_message_channel_request_open_reply_default(ssh_message msg) { + ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Refusing a channel"); + + if (buffer_add_u8(msg->session->out_buffer + , SSH2_MSG_CHANNEL_OPEN_FAILURE) < 0) { + goto error; + } + if (buffer_add_u32(msg->session->out_buffer, + htonl(msg->channel_request_open.sender)) < 0) { + goto error; + } + if (buffer_add_u32(msg->session->out_buffer, + htonl(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) < 0) { + goto error; + } + /* reason is an empty string */ + if (buffer_add_u32(msg->session->out_buffer, 0) < 0) { + goto error; + } + /* language too */ + if (buffer_add_u32(msg->session->out_buffer, 0) < 0) { + goto error; + } + + return packet_send(msg->session); +error: + return SSH_ERROR; +} + +static int ssh_message_channel_request_reply_default(ssh_message msg) { + uint32_t channel; + + if (msg->channel_request.want_reply) { + channel = msg->channel_request.channel->remote_channel; + + ssh_log(msg->session, SSH_LOG_PACKET, + "Sending a default channel_request denied to channel %d", channel); + + if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_CHANNEL_FAILURE) < 0) { + return SSH_ERROR; + } + if (buffer_add_u32(msg->session->out_buffer, htonl(channel)) < 0) { + return SSH_ERROR; + } + + return packet_send(msg->session); + } + + ssh_log(msg->session, SSH_LOG_PACKET, + "The client doesn't want to know the request failed!"); + + return SSH_OK; +} + +static int ssh_message_service_request_reply_default(ssh_message msg) { + /* The only return code accepted by specifications are success or disconnect */ + return ssh_message_service_reply_success(msg); +} + +int ssh_message_service_reply_success(ssh_message msg) { + struct ssh_string_struct *service; + ssh_session session; + + if (msg == NULL) { + return SSH_ERROR; + } + session = msg->session; + + ssh_log(session, SSH_LOG_PACKET, + "Sending a SERVICE_ACCEPT for service %s", msg->service_request.service); + if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_ACCEPT) < 0) { + return -1; + } + service=ssh_string_from_char(msg->service_request.service); + if (service == NULL) { + return -1; + } + + if (buffer_add_ssh_string(session->out_buffer, service) < 0) { + ssh_string_free(service); + return -1; + } + ssh_string_free(service); + return packet_send(msg->session); +} + +int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) { + ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Accepting a global request"); + + if (msg->global_request.want_reply) { + if (buffer_add_u8(msg->session->out_buffer + , SSH2_MSG_REQUEST_SUCCESS) < 0) { + goto error; + } + + if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD + && msg->global_request.bind_port == 0) { + if (buffer_add_u32(msg->session->out_buffer, htonl(bound_port)) < 0) { + goto error; + } + } + + return packet_send(msg->session); + } + + if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD + && msg->global_request.bind_port == 0) { + ssh_log(msg->session, SSH_LOG_PACKET, + "The client doesn't want to know the remote port!"); + } + + return SSH_OK; +error: + return SSH_ERROR; +} + +static int ssh_message_global_request_reply_default(ssh_message msg) { + ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Refusing a global request"); + + if (msg->global_request.want_reply) { + if (buffer_add_u8(msg->session->out_buffer + , SSH2_MSG_REQUEST_FAILURE) < 0) { + goto error; + } + return packet_send(msg->session); + } + ssh_log(msg->session, SSH_LOG_PACKET, + "The client doesn't want to know the request failed!"); + + return SSH_OK; +error: + return SSH_ERROR; +} + +int ssh_message_reply_default(ssh_message msg) { + if (msg == NULL) { + return -1; + } + + switch(msg->type) { + case SSH_REQUEST_AUTH: + return ssh_message_auth_reply_default(msg, 0); + case SSH_REQUEST_CHANNEL_OPEN: + return ssh_message_channel_request_open_reply_default(msg); + case SSH_REQUEST_CHANNEL: + return ssh_message_channel_request_reply_default(msg); + case SSH_REQUEST_SERVICE: + return ssh_message_service_request_reply_default(msg); + case SSH_REQUEST_GLOBAL: + return ssh_message_global_request_reply_default(msg); + default: + ssh_log(msg->session, SSH_LOG_PACKET, + "Don't know what to default reply to %d type", + msg->type); + break; + } + + return -1; +} + +const char *ssh_message_service_service(ssh_message msg){ + if (msg == NULL) { + return NULL; + } + return msg->service_request.service; +} + +const char *ssh_message_auth_user(ssh_message msg) { + if (msg == NULL) { + return NULL; + } + + return msg->auth_request.username; +} + +const char *ssh_message_auth_password(ssh_message msg){ + if (msg == NULL) { + return NULL; + } + + return msg->auth_request.password; +} + +ssh_key ssh_message_auth_pubkey(ssh_message msg) { + if (msg == NULL) { + return NULL; + } + + return msg->auth_request.pubkey; +} + +/* Get the publickey of an auth request */ +ssh_public_key ssh_message_auth_publickey(ssh_message msg){ + if (msg == NULL) { + return NULL; + } + + return ssh_pki_convert_key_to_publickey(msg->auth_request.pubkey); +} + +enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg){ + if (msg == NULL) { + return -1; + } + return msg->auth_request.signature_state; +} + +int ssh_message_auth_kbdint_is_response(ssh_message msg) { + if (msg == NULL) { + return -1; + } + + return msg->auth_request.kbdint_response != 0; +} + +int ssh_message_auth_set_methods(ssh_message msg, int methods) { + if (msg == NULL || msg->session == NULL) { + return -1; + } + + msg->session->auth_methods = methods; + + return 0; +} + +int ssh_message_auth_interactive_request(ssh_message msg, const char *name, + const char *instruction, unsigned int num_prompts, + const char **prompts, char *echo) { + int r; + unsigned int i = 0; + ssh_string tmp = NULL; + + if(name == NULL || instruction == NULL) { + return SSH_ERROR; + } + if(num_prompts > 0 && (prompts == NULL || echo == NULL)) { + return SSH_ERROR; + } + + if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_USERAUTH_INFO_REQUEST) < 0) { + return SSH_ERROR; + } + + /* name */ + tmp = ssh_string_from_char(name); + if (tmp == NULL) { + return SSH_ERROR; + } + + r = buffer_add_ssh_string(msg->session->out_buffer, tmp); + ssh_string_free(tmp); + if (r < 0) { + return SSH_ERROR; + } + + /* instruction */ + tmp = ssh_string_from_char(instruction); + if (tmp == NULL) { + return SSH_ERROR; + } + + r = buffer_add_ssh_string(msg->session->out_buffer, tmp); + ssh_string_free(tmp); + if (r < 0) { + return SSH_ERROR; + } + + /* language tag */ + tmp = ssh_string_from_char(""); + if (tmp == NULL) { + return SSH_ERROR; + } + + r = buffer_add_ssh_string(msg->session->out_buffer, tmp); + ssh_string_free(tmp); + if (r < 0) { + return SSH_ERROR; + } + + /* num prompts */ + if (buffer_add_u32(msg->session->out_buffer, ntohl(num_prompts)) < 0) { + return SSH_ERROR; + } + + for(i = 0; i < num_prompts; i++) { + /* prompt[i] */ + tmp = ssh_string_from_char(prompts[i]); + if (tmp == NULL) { + return SSH_ERROR; + } + + r = buffer_add_ssh_string(msg->session->out_buffer, tmp); + ssh_string_free(tmp); + if (r < 0) { + goto error; + } + + /* echo[i] */ + if (buffer_add_u8(msg->session->out_buffer, echo[i]) < 0) { + return SSH_ERROR; + } + } + + r = packet_send(msg->session); + + /* fill in the kbdint structure */ + if (msg->session->kbdint == NULL) { + ssh_log(msg->session, SSH_LOG_PROTOCOL, "Warning: Got a " + "keyboard-interactive response but it " + "seems we didn't send the request."); + + msg->session->kbdint = ssh_kbdint_new(); + if (msg->session->kbdint == NULL) { + ssh_set_error_oom(msg->session); + + return SSH_ERROR; + } + } else { + ssh_kbdint_clean(msg->session->kbdint); + } + + msg->session->kbdint->name = strdup(name); + if(msg->session->kbdint->name == NULL) { + ssh_set_error_oom(msg->session); + ssh_kbdint_free(msg->session->kbdint); + msg->session->kbdint = NULL; + return SSH_PACKET_USED; + } + msg->session->kbdint->instruction = strdup(instruction); + if(msg->session->kbdint->instruction == NULL) { + ssh_set_error_oom(msg->session); + ssh_kbdint_free(msg->session->kbdint); + msg->session->kbdint = NULL; + return SSH_PACKET_USED; + } + + msg->session->kbdint->nprompts = num_prompts; + if(num_prompts > 0) { + msg->session->kbdint->prompts = malloc(num_prompts * sizeof(char *)); + if (msg->session->kbdint->prompts == NULL) { + msg->session->kbdint->nprompts = 0; + ssh_set_error_oom(msg->session); + ssh_kbdint_free(msg->session->kbdint); + msg->session->kbdint = NULL; + return SSH_ERROR; + } + msg->session->kbdint->echo = malloc(num_prompts * sizeof(char)); + if (msg->session->kbdint->echo == NULL) { + ssh_set_error_oom(msg->session); + ssh_kbdint_free(msg->session->kbdint); + msg->session->kbdint = NULL; + return SSH_ERROR; + } + for (i = 0; i < num_prompts; i++) { + msg->session->kbdint->echo[i] = echo[i]; + msg->session->kbdint->prompts[i] = strdup(prompts[i]); + if (msg->session->kbdint->prompts[i] == NULL) { + ssh_set_error_oom(msg->session); + msg->session->kbdint->nprompts = i; + ssh_kbdint_free(msg->session->kbdint); + msg->session->kbdint = NULL; + return SSH_PACKET_USED; + } + } + } else { + msg->session->kbdint->prompts = NULL; + msg->session->kbdint->echo = NULL; + } + + return r; +error: + if(tmp) ssh_string_free(tmp); + return SSH_ERROR; +} + +int ssh_message_auth_reply_success(ssh_message msg, int partial) { + int r; + + if (msg == NULL) { + return SSH_ERROR; + } + + if (partial) { + return ssh_message_auth_reply_default(msg, partial); + } + + if (buffer_add_u8(msg->session->out_buffer,SSH2_MSG_USERAUTH_SUCCESS) < 0) { + return SSH_ERROR; + } + + r = packet_send(msg->session); + if(msg->session->current_crypto && msg->session->current_crypto->delayed_compress_out){ + ssh_log(msg->session,SSH_LOG_PROTOCOL,"Enabling delayed compression OUT"); + msg->session->current_crypto->do_compress_out=1; + } + if(msg->session->current_crypto && msg->session->current_crypto->delayed_compress_in){ + ssh_log(msg->session,SSH_LOG_PROTOCOL,"Enabling delayed compression IN"); + msg->session->current_crypto->do_compress_in=1; + } + return r; +} + +/* Answer OK to a pubkey auth request */ +int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) { + if (msg == NULL) { + return SSH_ERROR; + } + + if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_USERAUTH_PK_OK) < 0 || + buffer_add_ssh_string(msg->session->out_buffer, algo) < 0 || + buffer_add_ssh_string(msg->session->out_buffer, pubkey) < 0) { + return SSH_ERROR; + } + + return packet_send(msg->session); +} + +int ssh_message_auth_reply_pk_ok_simple(ssh_message msg) { + ssh_string algo; + ssh_string pubkey_blob = NULL; + int ret; + + algo = ssh_string_from_char(msg->auth_request.pubkey->type_c); + if (algo == NULL) { + return SSH_ERROR; + } + + ret = ssh_pki_export_pubkey_blob(msg->auth_request.pubkey, &pubkey_blob); + if (ret < 0) { + ssh_string_free(algo); + return SSH_ERROR; + } + + ret = ssh_message_auth_reply_pk_ok(msg, algo, pubkey_blob); + + ssh_string_free(algo); + ssh_string_free(pubkey_blob); + + return ret; +} + + +const char *ssh_message_channel_request_open_originator(ssh_message msg){ + return msg->channel_request_open.originator; +} + +int ssh_message_channel_request_open_originator_port(ssh_message msg){ + return msg->channel_request_open.originator_port; +} + +const char *ssh_message_channel_request_open_destination(ssh_message msg){ + return msg->channel_request_open.destination; +} + +int ssh_message_channel_request_open_destination_port(ssh_message msg){ + return msg->channel_request_open.destination_port; +} + +ssh_channel ssh_message_channel_request_channel(ssh_message msg){ + return msg->channel_request.channel; +} + +const char *ssh_message_channel_request_pty_term(ssh_message msg){ + return msg->channel_request.TERM; +} + +int ssh_message_channel_request_pty_width(ssh_message msg){ + return msg->channel_request.width; +} + +int ssh_message_channel_request_pty_height(ssh_message msg){ + return msg->channel_request.height; +} + +int ssh_message_channel_request_pty_pxwidth(ssh_message msg){ + return msg->channel_request.pxwidth; +} + +int ssh_message_channel_request_pty_pxheight(ssh_message msg){ + return msg->channel_request.pxheight; +} + +const char *ssh_message_channel_request_env_name(ssh_message msg){ + return msg->channel_request.var_name; +} + +const char *ssh_message_channel_request_env_value(ssh_message msg){ + return msg->channel_request.var_value; +} + +const char *ssh_message_channel_request_command(ssh_message msg){ + return msg->channel_request.command; +} + +const char *ssh_message_channel_request_subsystem(ssh_message msg){ + return msg->channel_request.subsystem; +} + +int ssh_message_channel_request_x11_single_connection(ssh_message msg){ + return msg->channel_request.x11_single_connection ? 1 : 0; +} + +const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg){ + return msg->channel_request.x11_auth_protocol; +} + +const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg){ + return msg->channel_request.x11_auth_cookie; +} + +int ssh_message_channel_request_x11_screen_number(ssh_message msg){ + return msg->channel_request.x11_screen_number; +} + +const char *ssh_message_global_request_address(ssh_message msg){ + return msg->global_request.bind_address; +} + +int ssh_message_global_request_port(ssh_message msg){ + return msg->global_request.bind_port; +} + +/** @brief defines the ssh_message callback + * @param session the current ssh session + * @param[in] ssh_bind_message_callback a function pointer to a callback taking the + * current ssh session and received message as parameters. the function returns + * 0 if the message has been parsed and treated successfully, 1 otherwise (libssh + * must take care of the response). + * @param[in] data void pointer to be passed to callback functions + */ +void ssh_set_message_callback(ssh_session session, + int(*ssh_bind_message_callback)(ssh_session session, ssh_message msg, void *data), + void *data) { + session->ssh_message_callback = ssh_bind_message_callback; + session->ssh_message_callback_data = data; +} + +int ssh_execute_message_callbacks(ssh_session session){ + ssh_message msg=NULL; + int ret; + ssh_handle_packets(session, SSH_TIMEOUT_NONBLOCKING); + if(!session->ssh_message_list) + return SSH_OK; + if(session->ssh_message_callback){ + while((msg=ssh_message_pop_head(session)) != NULL) { + ret=session->ssh_message_callback(session,msg, + session->ssh_message_callback_data); + if(ret==1){ + ret = ssh_message_reply_default(msg); + ssh_message_free(msg); + if(ret != SSH_OK) + return ret; + } else { + ssh_message_free(msg); + } + } + } else { + while((msg=ssh_message_pop_head(session)) != NULL) { + ret = ssh_message_reply_default(msg); + ssh_message_free(msg); + if(ret != SSH_OK) + return ret; + } + } + return SSH_OK; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/session.c b/libssh/src/session.c new file mode 100644 index 00000000..4e713948 --- /dev/null +++ b/libssh/src/session.c @@ -0,0 +1,737 @@ +/* + * session.c - non-networking functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2005-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include "libssh/priv.h" +#include "libssh/libssh.h" +#include "libssh/crypto.h" +#include "libssh/server.h" +#include "libssh/socket.h" +#include "libssh/ssh2.h" +#include "libssh/agent.h" +#include "libssh/packet.h" +#include "libssh/session.h" +#include "libssh/misc.h" +#include "libssh/buffer.h" +#include "libssh/poll.h" + +#define FIRST_CHANNEL 42 // why not ? it helps to find bugs. + +/** + * @defgroup libssh_session The SSH session functions. + * @ingroup libssh + * + * Functions that manage a session. + * + * @{ + */ + +/** + * @brief Create a new ssh session. + * + * @returns A new ssh_session pointer, NULL on error. + */ +ssh_session ssh_new(void) { + ssh_session session; + char *id = NULL; + int rc; + + session = malloc(sizeof (struct ssh_session_struct)); + if (session == NULL) { + return NULL; + } + ZERO_STRUCTP(session); + + session->next_crypto = crypto_new(); + if (session->next_crypto == NULL) { + goto err; + } + + session->socket = ssh_socket_new(session); + if (session->socket == NULL) { + goto err; + } + + session->out_buffer = ssh_buffer_new(); + if (session->out_buffer == NULL) { + goto err; + } + + session->in_buffer=ssh_buffer_new(); + if (session->in_buffer == NULL) { + goto err; + } + + session->alive = 0; + session->auth_methods = 0; + ssh_set_blocking(session, 1); + session->common.log_indent = 0; + session->maxchannel = FIRST_CHANNEL; + +#ifndef _WIN32 + session->agent = agent_new(session); + if (session->agent == NULL) { + goto err; + } +#endif /* _WIN32 */ + + /* OPTIONS */ + session->opts.StrictHostKeyChecking = 1; + session->opts.port = 22; + session->opts.fd = -1; + session->opts.ssh2 = 1; + session->opts.compressionlevel=7; +#ifdef WITH_SSH1 + session->opts.ssh1 = 1; +#else + session->opts.ssh1 = 0; +#endif + + session->opts.identity = ssh_list_new(); + if (session->opts.identity == NULL) { + goto err; + } + + id = strdup("%d/id_rsa"); + if (id == NULL) { + goto err; + } + rc = ssh_list_append(session->opts.identity, id); + if (rc == SSH_ERROR) { + goto err; + } + + id = strdup("%d/id_dsa"); + if (id == NULL) { + goto err; + } + rc = ssh_list_append(session->opts.identity, id); + if (rc == SSH_ERROR) { + goto err; + } + + id = strdup("%d/identity"); + if (id == NULL) { + goto err; + } + rc = ssh_list_append(session->opts.identity, id); + if (rc == SSH_ERROR) { + goto err; + } + + return session; + +err: + free(id); + ssh_free(session); + return NULL; +} + +/** + * @brief Deallocate a SSH session handle. + * + * @param[in] session The SSH session to free. + * + * @see ssh_disconnect() + * @see ssh_new() + */ +void ssh_free(ssh_session session) { + int i; + struct ssh_iterator *it; + + if (session == NULL) { + return; + } + + /* + * Delete all channels + * + * This needs the first thing we clean up cause if there is still an open + * channel we call ssh_channel_close() first. So we need a working socket + * and poll context for it. + */ + for (it = ssh_list_get_iterator(session->channels); + it != NULL; + it = ssh_list_get_iterator(session->channels)) { + ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); + ssh_list_remove(session->channels, it); + } + ssh_list_free(session->channels); + session->channels = NULL; + +#ifdef WITH_PCAP + if (session->pcap_ctx) { + ssh_pcap_context_free(session->pcap_ctx); + session->pcap_ctx = NULL; + } +#endif + + ssh_socket_free(session->socket); + session->socket = NULL; + + if (session->default_poll_ctx) { + ssh_poll_ctx_free(session->default_poll_ctx); + } + + ssh_buffer_free(session->in_buffer); + ssh_buffer_free(session->out_buffer); + session->in_buffer = session->out_buffer = NULL; + + if (session->in_hashbuf != NULL) { + ssh_buffer_free(session->in_hashbuf); + } + if (session->out_hashbuf != NULL) { + ssh_buffer_free(session->out_hashbuf); + } + + crypto_free(session->current_crypto); + crypto_free(session->next_crypto); + +#ifndef _WIN32 + agent_free(session->agent); +#endif /* _WIN32 */ + + ssh_key_free(session->srv.dsa_key); + ssh_key_free(session->srv.rsa_key); + + if (session->ssh_message_list) { + ssh_message msg; + + for (msg = ssh_list_pop_head(ssh_message, session->ssh_message_list); + msg != NULL; + msg = ssh_list_pop_head(ssh_message, session->ssh_message_list)) { + ssh_message_free(msg); + } + ssh_list_free(session->ssh_message_list); + } + + if (session->packet_callbacks) { + ssh_list_free(session->packet_callbacks); + } + + /* options */ + if (session->opts.identity) { + char *id; + + for (id = ssh_list_pop_head(char *, session->opts.identity); + id != NULL; + id = ssh_list_pop_head(char *, session->opts.identity)) { + SAFE_FREE(id); + } + ssh_list_free(session->opts.identity); + } + + SAFE_FREE(session->serverbanner); + SAFE_FREE(session->clientbanner); + SAFE_FREE(session->banner); + + SAFE_FREE(session->opts.bindaddr); + SAFE_FREE(session->opts.username); + SAFE_FREE(session->opts.host); + SAFE_FREE(session->opts.sshdir); + SAFE_FREE(session->opts.knownhosts); + SAFE_FREE(session->opts.ProxyCommand); + + for (i = 0; i < 10; i++) { + if (session->opts.wanted_methods[i]) { + SAFE_FREE(session->opts.wanted_methods[i]); + } + } + + /* burn connection, it could hang sensitive datas */ + ZERO_STRUCTP(session); + SAFE_FREE(session); +} + +/** + * @brief get the server banner + * @param[in] session The SSH session + */ +const char* ssh_get_serverbanner(ssh_session session) { + if(!session) { + return NULL; + } + return session->serverbanner; +} + +/** + * @brief Disconnect impolitely from a remote host by closing the socket. + * + * Suitable if you forked and want to destroy this session. + * + * @param[in] session The SSH session to disconnect. + */ +void ssh_silent_disconnect(ssh_session session) { + enter_function(); + + if (session == NULL) { + return; + } + + ssh_socket_close(session->socket); + session->alive = 0; + ssh_disconnect(session); + leave_function(); +} + +/** + * @brief Set the session in blocking/nonblocking mode. + * + * @param[in] session The ssh session to change. + * + * @param[in] blocking Zero for nonblocking mode. + * + * \bug nonblocking code is in development and won't work as expected + */ +void ssh_set_blocking(ssh_session session, int blocking) { + if (session == NULL) { + return; + } + session->flags &= ~SSH_SESSION_FLAG_BLOCKING; + session->flags |= blocking ? SSH_SESSION_FLAG_BLOCKING : 0; +} + +/** + * @brief Return the blocking mode of libssh + * @param[in] session The SSH session + * @returns 0 if the session is nonblocking, + * @returns 1 if the functions may block. + */ +int ssh_is_blocking(ssh_session session){ + return (session->flags&SSH_SESSION_FLAG_BLOCKING) ? 1 : 0; +} + +/* Waits until the output socket is empty */ +static int ssh_flush_termination(void *c){ + ssh_session session = c; + if (ssh_socket_buffered_write_bytes(session->socket) == 0 || + session->session_state == SSH_SESSION_STATE_ERROR) + return 1; + else + return 0; +} + +/** + * @brief Blocking flush of the outgoing buffer + * @param[in] session The SSH session + * @param[in] timeout Set an upper limit on the time for which this function + * will block, in milliseconds. Specifying -1 + * means an infinite timeout. This parameter is passed to + * the poll() function. + * @returns SSH_OK on success, SSH_AGAIN if timeout occurred, + * SSH_ERROR otherwise. + */ + +int ssh_blocking_flush(ssh_session session, int timeout){ + int rc; + if(!session) + return SSH_ERROR; + enter_function(); + + rc = ssh_handle_packets_termination(session, timeout, + ssh_flush_termination, session); + if (rc == SSH_ERROR) + goto end; + if (!ssh_flush_termination(session)) + rc = SSH_AGAIN; +end: + leave_function(); + return rc; +} + +/** + * @brief Check if we are connected. + * + * @param[in] session The session to check if it is connected. + * + * @return 1 if we are connected, 0 if not. + */ +int ssh_is_connected(ssh_session session) { + if (session == NULL) { + return 0; + } + + return session->alive; +} + +/** + * @brief Get the fd of a connection. + * + * In case you'd need the file descriptor of the connection to the server/client. + * + * @param[in] session The ssh session to use. + * + * @return The file descriptor of the connection, or -1 if it is + * not connected + */ +socket_t ssh_get_fd(ssh_session session) { + if (session == NULL) { + return -1; + } + + return ssh_socket_get_fd_in(session->socket); +} + +/** + * @brief Tell the session it has data to read on the file descriptor without + * blocking. + * + * @param[in] session The ssh session to use. + */ +void ssh_set_fd_toread(ssh_session session) { + if (session == NULL) { + return; + } + + ssh_socket_set_read_wontblock(session->socket); +} + +/** + * @brief Tell the session it may write to the file descriptor without blocking. + * + * @param[in] session The ssh session to use. + */ +void ssh_set_fd_towrite(ssh_session session) { + if (session == NULL) { + return; + } + + ssh_socket_set_write_wontblock(session->socket); +} + +/** + * @brief Tell the session it has an exception to catch on the file descriptor. + * + * \param[in] session The ssh session to use. + */ +void ssh_set_fd_except(ssh_session session) { + if (session == NULL) { + return; + } + + ssh_socket_set_except(session->socket); +} + +/** + * @internal + * + * @brief Poll the current session for an event and call the appropriate + * callbacks. This function will not loop until the timeout is expired. + * + * This will block until one event happens. + * + * @param[in] session The session handle to use. + * + * @param[in] timeout Set an upper limit on the time for which this function + * will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE + * (-1) means an infinite timeout. + * Specifying SSH_TIMEOUT_USER means to use the timeout + * specified in options. 0 means poll will return immediately. + * This parameter is passed to the poll() function. + * + * @return SSH_OK on success, SSH_ERROR otherwise. + */ +int ssh_handle_packets(ssh_session session, int timeout) { + ssh_poll_handle spoll_in,spoll_out; + ssh_poll_ctx ctx; + int tm = timeout; + int rc; + + if (session == NULL || session->socket == NULL) { + return SSH_ERROR; + } + enter_function(); + + spoll_in = ssh_socket_get_poll_handle_in(session->socket); + spoll_out = ssh_socket_get_poll_handle_out(session->socket); + if (session->server) { + ssh_poll_add_events(spoll_in, POLLIN); + } + ctx = ssh_poll_get_ctx(spoll_in); + + if (!ctx) { + ctx = ssh_poll_get_default_ctx(session); + ssh_poll_ctx_add(ctx, spoll_in); + if (spoll_in != spoll_out) { + ssh_poll_ctx_add(ctx, spoll_out); + } + } + + if (timeout == SSH_TIMEOUT_USER) { + if (ssh_is_blocking(session)) + tm = ssh_make_milliseconds(session->opts.timeout, + session->opts.timeout_usec); + else + tm = 0; + } + rc = ssh_poll_ctx_dopoll(ctx, tm); + if (rc == SSH_ERROR) { + session->session_state = SSH_SESSION_STATE_ERROR; + } + + leave_function(); + return rc; +} + +/** + * @internal + * + * @brief Poll the current session for an event and call the appropriate + * callbacks. + * + * This will block until termination function returns true, or timeout expired. + * + * @param[in] session The session handle to use. + * + * @param[in] timeout Set an upper limit on the time for which this function + * will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE + * (-1) means an infinite timeout. + * Specifying SSH_TIMEOUT_USER means to use the timeout + * specified in options. 0 means poll will return immediately. + * This parameter is passed to the poll() function. + * + * @param[in] fct Termination function to be used to determine if it is + * possible to stop polling. + * @param[in] user User parameter to be passed to fct termination function. + * @return SSH_OK on success, SSH_ERROR otherwise. + */ +int ssh_handle_packets_termination(ssh_session session, int timeout, + ssh_termination_function fct, void *user){ + int ret = SSH_OK; + struct ssh_timestamp ts; + int tm; + if (timeout == SSH_TIMEOUT_USER) { + if (ssh_is_blocking(session)) + timeout = ssh_make_milliseconds(session->opts.timeout, + session->opts.timeout_usec); + else + timeout = SSH_TIMEOUT_NONBLOCKING; + } + ssh_timestamp_init(&ts); + tm = timeout; + while(!fct(user)){ + ret = ssh_handle_packets(session, tm); + if(ret == SSH_ERROR) + break; + if(ssh_timeout_elapsed(&ts,timeout)) + break; + tm = ssh_timeout_update(&ts, timeout); + } + return ret; +} + +/** + * @brief Get session status + * + * @param session The ssh session to use. + * + * @returns A bitmask including SSH_CLOSED, SSH_READ_PENDING, SSH_WRITE_PENDING + * or SSH_CLOSED_ERROR which respectively means the session is closed, + * has data to read on the connection socket and session was closed + * due to an error. + */ +int ssh_get_status(ssh_session session) { + int socketstate; + int r = 0; + + if (session == NULL) { + return 0; + } + + socketstate = ssh_socket_get_status(session->socket); + + if (session->closed) { + r |= SSH_CLOSED; + } + if (socketstate & SSH_READ_PENDING) { + r |= SSH_READ_PENDING; + } + if (socketstate & SSH_WRITE_PENDING) { + r |= SSH_WRITE_PENDING; + } + if (session->closed && (socketstate & SSH_CLOSED_ERROR)) { + r |= SSH_CLOSED_ERROR; + } + + return r; +} + +/** + * @brief Get the disconnect message from the server. + * + * @param[in] session The ssh session to use. + * + * @return The message sent by the server along with the + * disconnect, or NULL in which case the reason of the + * disconnect may be found with ssh_get_error. + * + * @see ssh_get_error() + */ +const char *ssh_get_disconnect_message(ssh_session session) { + if (session == NULL) { + return NULL; + } + + if (!session->closed) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Connection not closed yet"); + } else if(session->closed_by_except) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Connection closed by socket error"); + } else if(!session->discon_msg) { + ssh_set_error(session, SSH_FATAL, + "Connection correctly closed but no disconnect message"); + } else { + return session->discon_msg; + } + + return NULL; +} + +/** + * @brief Get the protocol version of the session. + * + * @param session The ssh session to use. + * + * @return 1 or 2, for ssh1 or ssh2, < 0 on error. + */ +int ssh_get_version(ssh_session session) { + if (session == NULL) { + return -1; + } + + return session->version; +} + +/** + * @internal + * @brief Callback to be called when the socket received an exception code. + * @param user is a pointer to session + */ +void ssh_socket_exception_callback(int code, int errno_code, void *user){ + ssh_session session=(ssh_session)user; + enter_function(); + ssh_log(session,SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code); + session->session_state=SSH_SESSION_STATE_ERROR; + ssh_set_error(session,SSH_FATAL,"Socket error: %s",strerror(errno_code)); + session->ssh_connection_callback(session); + leave_function(); +} + +/** + * @brief Send a message that should be ignored + * + * @param[in] session The SSH session + * @param[in] data Data to be sent + * + * @return SSH_OK on success, SSH_ERROR otherwise. + */ +int ssh_send_ignore (ssh_session session, const char *data) { + ssh_string str; + + if (ssh_socket_is_open(session->socket)) { + if (buffer_add_u8(session->out_buffer, SSH2_MSG_IGNORE) < 0) { + goto error; + } + + str = ssh_string_from_char(data); + if (str == NULL) { + goto error; + } + + if (buffer_add_ssh_string(session->out_buffer, str) < 0) { + ssh_string_free(str); + goto error; + } + + packet_send(session); + ssh_handle_packets(session, 0); + + ssh_string_free(str); + } + + return SSH_OK; + +error: + buffer_reinit(session->out_buffer); + return SSH_ERROR; +} + +/** + * @brief Send a debug message + * + * @param[in] session The SSH session + * @param[in] message Data to be sent + * @param[in] always_display Message SHOULD be displayed by the server. It + * SHOULD NOT be displayed unless debugging + * information has been explicitly requested. + * + * @return SSH_OK on success, SSH_ERROR otherwise. + */ +int ssh_send_debug (ssh_session session, const char *message, int always_display) { + ssh_string str; + int rc; + + if (ssh_socket_is_open(session->socket)) { + if (buffer_add_u8(session->out_buffer, SSH2_MSG_DEBUG) < 0) { + goto error; + } + + if (buffer_add_u8(session->out_buffer, always_display) < 0) { + goto error; + } + + str = ssh_string_from_char(message); + if (str == NULL) { + goto error; + } + + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto error; + } + + /* Empty language tag */ + if (buffer_add_u32(session->out_buffer, 0) < 0) { + goto error; + } + + packet_send(session); + ssh_handle_packets(session, 0); + } + + return SSH_OK; + +error: + buffer_reinit(session->out_buffer); + return SSH_ERROR; +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/sftp.c b/libssh/src/sftp.c new file mode 100644 index 00000000..ee86107b --- /dev/null +++ b/libssh/src/sftp.c @@ -0,0 +1,3199 @@ +/* + * sftp.c - Secure FTP functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2005-2008 by Aris Adamantiadis + * Copyright (c) 2008-2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* This file contains code written by Nick Zitzmann */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#else +#define S_IFSOCK 0140000 +#define S_IFLNK 0120000 + +#ifdef _MSC_VER +#define S_IFBLK 0060000 +#define S_IFIFO 0010000 +#endif +#endif + +#include "libssh/priv.h" +#include "libssh/ssh2.h" +#include "libssh/sftp.h" +#include "libssh/buffer.h" +#include "libssh/channels.h" +#include "libssh/session.h" +#include "libssh/misc.h" + +#ifdef WITH_SFTP + +struct sftp_ext_struct { + unsigned int count; + char **name; + char **data; +}; + +/* functions */ +static int sftp_enqueue(sftp_session session, sftp_message msg); +static void sftp_message_free(sftp_message msg); +static void sftp_set_error(sftp_session sftp, int errnum); +static void status_msg_free(sftp_status_message status); + +static sftp_ext sftp_ext_new(void) { + sftp_ext ext; + + ext = malloc(sizeof(struct sftp_ext_struct)); + if (ext == NULL) { + return NULL; + } + ZERO_STRUCTP(ext); + + return ext; +} + +static void sftp_ext_free(sftp_ext ext) { + unsigned int i; + + if (ext == NULL) { + return; + } + + if (ext->count) { + for (i = 0; i < ext->count; i++) { + SAFE_FREE(ext->name[i]); + SAFE_FREE(ext->data[i]); + } + SAFE_FREE(ext->name); + SAFE_FREE(ext->data); + } + + SAFE_FREE(ext); +} + +sftp_session sftp_new(ssh_session session){ + sftp_session sftp; + + if (session == NULL) { + return NULL; + } + enter_function(); + + sftp = malloc(sizeof(struct sftp_session_struct)); + if (sftp == NULL) { + ssh_set_error_oom(session); + leave_function(); + return NULL; + } + ZERO_STRUCTP(sftp); + + sftp->ext = sftp_ext_new(); + if (sftp->ext == NULL) { + ssh_set_error_oom(session); + SAFE_FREE(sftp); + leave_function(); + return NULL; + } + + sftp->session = session; + sftp->channel = ssh_channel_new(session); + if (sftp->channel == NULL) { + SAFE_FREE(sftp); + leave_function(); + return NULL; + } + + if (ssh_channel_open_session(sftp->channel)) { + ssh_channel_free(sftp->channel); + SAFE_FREE(sftp); + leave_function(); + return NULL; + } + + if (ssh_channel_request_sftp(sftp->channel)) { + sftp_free(sftp); + leave_function(); + return NULL; + } + + leave_function(); + return sftp; +} + +#ifdef WITH_SERVER +sftp_session sftp_server_new(ssh_session session, ssh_channel chan){ + sftp_session sftp = NULL; + + sftp = malloc(sizeof(struct sftp_session_struct)); + if (sftp == NULL) { + ssh_set_error_oom(session); + return NULL; + } + ZERO_STRUCTP(sftp); + + sftp->session = session; + sftp->channel = chan; + + return sftp; +} + +int sftp_server_init(sftp_session sftp){ + ssh_session session = sftp->session; + sftp_packet packet = NULL; + ssh_buffer reply = NULL; + uint32_t version; + + packet = sftp_packet_read(sftp); + if (packet == NULL) { + return -1; + } + + if (packet->type != SSH_FXP_INIT) { + ssh_set_error(session, SSH_FATAL, + "Packet read of type %d instead of SSH_FXP_INIT", + packet->type); + + sftp_packet_free(packet); + return -1; + } + + ssh_log(session, SSH_LOG_PACKET, "Received SSH_FXP_INIT"); + + buffer_get_u32(packet->payload, &version); + version = ntohl(version); + ssh_log(session, SSH_LOG_PACKET, "Client version: %d", version); + sftp->client_version = version; + + sftp_packet_free(packet); + + reply = ssh_buffer_new(); + if (reply == NULL) { + ssh_set_error_oom(session); + return -1; + } + + if (buffer_add_u32(reply, ntohl(LIBSFTP_VERSION)) < 0) { + ssh_set_error_oom(session); + ssh_buffer_free(reply); + return -1; + } + + if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) { + ssh_buffer_free(reply); + return -1; + } + ssh_buffer_free(reply); + + ssh_log(session, SSH_LOG_RARE, "Server version sent"); + + if (version > LIBSFTP_VERSION) { + sftp->version = LIBSFTP_VERSION; + } else { + sftp->version=version; + } + + return 0; +} +#endif /* WITH_SERVER */ + +void sftp_free(sftp_session sftp){ + sftp_request_queue ptr; + + if (sftp == NULL) { + return; + } + + ssh_channel_send_eof(sftp->channel); + ptr = sftp->queue; + while(ptr) { + sftp_request_queue old; + sftp_message_free(ptr->message); + old = ptr->next; + SAFE_FREE(ptr); + ptr = old; + } + + ssh_channel_free(sftp->channel); + + SAFE_FREE(sftp->handles); + + sftp_ext_free(sftp->ext); + ZERO_STRUCTP(sftp); + + SAFE_FREE(sftp); +} + +int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload){ + int size; + + if (buffer_prepend_data(payload, &type, sizeof(uint8_t)) < 0) { + ssh_set_error_oom(sftp->session); + return -1; + } + + size = htonl(buffer_get_rest_len(payload)); + if (buffer_prepend_data(payload, &size, sizeof(uint32_t)) < 0) { + ssh_set_error_oom(sftp->session); + return -1; + } + + size = ssh_channel_write(sftp->channel, buffer_get_rest(payload), + buffer_get_rest_len(payload)); + if (size < 0) { + return -1; + } else if((uint32_t) size != buffer_get_rest_len(payload)) { + ssh_log(sftp->session, SSH_LOG_PACKET, + "Had to write %d bytes, wrote only %d", + buffer_get_rest_len(payload), + size); + } + + return size; +} + +sftp_packet sftp_packet_read(sftp_session sftp) { + unsigned char buffer[4096]; + sftp_packet packet = NULL; + uint32_t size; + int r; + + packet = malloc(sizeof(struct sftp_packet_struct)); + if (packet == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + packet->sftp = sftp; + packet->payload = ssh_buffer_new(); + if (packet->payload == NULL) { + ssh_set_error_oom(sftp->session); + SAFE_FREE(packet); + return NULL; + } + + r=ssh_channel_read(sftp->channel, buffer, 4, 0); + if (r < 0) { + ssh_buffer_free(packet->payload); + SAFE_FREE(packet); + return NULL; + } + buffer_add_data(packet->payload,buffer, r); + if (buffer_get_u32(packet->payload, &size) != sizeof(uint32_t)) { + ssh_set_error(sftp->session, SSH_FATAL, "Short sftp packet!"); + ssh_buffer_free(packet->payload); + SAFE_FREE(packet); + return NULL; + } + + size = ntohl(size); + r=ssh_channel_read(sftp->channel, buffer, 1, 0); + if (r <= 0) { + /* TODO: check if there are cases where an error needs to be set here */ + ssh_buffer_free(packet->payload); + SAFE_FREE(packet); + return NULL; + } + buffer_add_data(packet->payload, buffer, r); + buffer_get_u8(packet->payload, &packet->type); + size=size-1; + while (size>0){ + r=ssh_channel_read(sftp->channel,buffer, + sizeof(buffer)>size ? size:sizeof(buffer),0); + + if(r <= 0) { + /* TODO: check if there are cases where an error needs to be set here */ + ssh_buffer_free(packet->payload); + SAFE_FREE(packet); + return NULL; + } + if(buffer_add_data(packet->payload,buffer,r)==SSH_ERROR){ + ssh_buffer_free(packet->payload); + SAFE_FREE(packet); + ssh_set_error_oom(sftp->session); + return NULL; + } + size -= r; + } + + return packet; +} + +static void sftp_set_error(sftp_session sftp, int errnum) { + if (sftp != NULL) { + sftp->errnum = errnum; + } +} + +/* Get the last sftp error */ +int sftp_get_error(sftp_session sftp) { + if (sftp == NULL) { + return -1; + } + + return sftp->errnum; +} + +static sftp_message sftp_message_new(sftp_session sftp){ + sftp_message msg = NULL; + + msg = malloc(sizeof(struct sftp_message_struct)); + if (msg == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + ZERO_STRUCTP(msg); + + msg->payload = ssh_buffer_new(); + if (msg->payload == NULL) { + ssh_set_error_oom(sftp->session); + SAFE_FREE(msg); + return NULL; + } + msg->sftp = sftp; + + return msg; +} + +static void sftp_message_free(sftp_message msg) { + if (msg == NULL) { + return; + } + + ssh_buffer_free(msg->payload); + SAFE_FREE(msg); +} + +static sftp_message sftp_get_message(sftp_packet packet) { + sftp_session sftp = packet->sftp; + sftp_message msg = NULL; + + msg = sftp_message_new(sftp); + if (msg == NULL) { + return NULL; + } + + msg->sftp = packet->sftp; + msg->packet_type = packet->type; + + if ((packet->type != SSH_FXP_STATUS) && (packet->type!=SSH_FXP_HANDLE) && + (packet->type != SSH_FXP_DATA) && (packet->type != SSH_FXP_ATTRS) && + (packet->type != SSH_FXP_NAME) && (packet->type != SSH_FXP_EXTENDED_REPLY)) { + ssh_set_error(packet->sftp->session, SSH_FATAL, + "Unknown packet type %d", packet->type); + sftp_message_free(msg); + return NULL; + } + + if (buffer_get_u32(packet->payload, &msg->id) != sizeof(uint32_t)) { + ssh_set_error(packet->sftp->session, SSH_FATAL, + "Invalid packet %d: no ID", packet->type); + sftp_message_free(msg); + return NULL; + } + + ssh_log(packet->sftp->session, SSH_LOG_PACKET, + "Packet with id %d type %d", + msg->id, + msg->packet_type); + + if (buffer_add_data(msg->payload, buffer_get_rest(packet->payload), + buffer_get_rest_len(packet->payload)) < 0) { + ssh_set_error_oom(sftp->session); + sftp_message_free(msg); + return NULL; + } + + return msg; +} + +static int sftp_read_and_dispatch(sftp_session sftp) { + sftp_packet packet = NULL; + sftp_message msg = NULL; + + packet = sftp_packet_read(sftp); + if (packet == NULL) { + return -1; /* something nasty happened reading the packet */ + } + + msg = sftp_get_message(packet); + sftp_packet_free(packet); + if (msg == NULL) { + return -1; + } + + if (sftp_enqueue(sftp, msg) < 0) { + sftp_message_free(msg); + return -1; + } + + return 0; +} + +void sftp_packet_free(sftp_packet packet) { + if (packet == NULL) { + return; + } + + ssh_buffer_free(packet->payload); + free(packet); +} + +/* Initialize the sftp session with the server. */ +int sftp_init(sftp_session sftp) { + sftp_packet packet = NULL; + ssh_buffer buffer = NULL; + ssh_string ext_name_s = NULL; + ssh_string ext_data_s = NULL; + char *ext_name = NULL; + char *ext_data = NULL; + uint32_t version = htonl(LIBSFTP_VERSION); + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + if (buffer_add_u32(buffer, version) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + if (sftp_packet_write(sftp, SSH_FXP_INIT, buffer) < 0) { + ssh_buffer_free(buffer); + return -1; + } + ssh_buffer_free(buffer); + + packet = sftp_packet_read(sftp); + if (packet == NULL) { + return -1; + } + + if (packet->type != SSH_FXP_VERSION) { + ssh_set_error(sftp->session, SSH_FATAL, + "Received a %d messages instead of SSH_FXP_VERSION", packet->type); + sftp_packet_free(packet); + return -1; + } + + /* TODO: are we sure there are 4 bytes ready? */ + buffer_get_u32(packet->payload, &version); + version = ntohl(version); + ssh_log(sftp->session, SSH_LOG_RARE, + "SFTP server version %d", + version); + + ext_name_s = buffer_get_ssh_string(packet->payload); + while (ext_name_s != NULL) { + int count = sftp->ext->count; + char **tmp; + + ext_data_s = buffer_get_ssh_string(packet->payload); + if (ext_data_s == NULL) { + ssh_string_free(ext_name_s); + break; + } + + ext_name = ssh_string_to_char(ext_name_s); + ext_data = ssh_string_to_char(ext_data_s); + if (ext_name == NULL || ext_data == NULL) { + ssh_set_error_oom(sftp->session); + SAFE_FREE(ext_name); + SAFE_FREE(ext_data); + ssh_string_free(ext_name_s); + ssh_string_free(ext_data_s); + return -1; + } + ssh_log(sftp->session, SSH_LOG_RARE, + "SFTP server extension: %s, version: %s", + ext_name, ext_data); + + count++; + tmp = realloc(sftp->ext->name, count * sizeof(char *)); + if (tmp == NULL) { + ssh_set_error_oom(sftp->session); + SAFE_FREE(ext_name); + SAFE_FREE(ext_data); + ssh_string_free(ext_name_s); + ssh_string_free(ext_data_s); + return -1; + } + tmp[count - 1] = ext_name; + sftp->ext->name = tmp; + + tmp = realloc(sftp->ext->data, count * sizeof(char *)); + if (tmp == NULL) { + ssh_set_error_oom(sftp->session); + SAFE_FREE(ext_name); + SAFE_FREE(ext_data); + ssh_string_free(ext_name_s); + ssh_string_free(ext_data_s); + return -1; + } + tmp[count - 1] = ext_data; + sftp->ext->data = tmp; + + sftp->ext->count = count; + + ssh_string_free(ext_name_s); + ssh_string_free(ext_data_s); + + ext_name_s = buffer_get_ssh_string(packet->payload); + } + + sftp_packet_free(packet); + + sftp->version = sftp->server_version = version; + + + return 0; +} + +unsigned int sftp_extensions_get_count(sftp_session sftp) { + if (sftp == NULL || sftp->ext == NULL) { + return 0; + } + + return sftp->ext->count; +} + +const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx) { + if (sftp == NULL) + return NULL; + if (sftp->ext == NULL || sftp->ext->name == NULL) { + ssh_set_error_invalid(sftp->session); + return NULL; + } + + if (idx > sftp->ext->count) { + ssh_set_error_invalid(sftp->session); + return NULL; + } + + return sftp->ext->name[idx]; +} + +const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx) { + if (sftp == NULL) + return NULL; + if (sftp->ext == NULL || sftp->ext->name == NULL) { + ssh_set_error_invalid(sftp->session); + return NULL; + } + + if (idx > sftp->ext->count) { + ssh_set_error_invalid(sftp->session); + return NULL; + } + + return sftp->ext->data[idx]; +} + +int sftp_extension_supported(sftp_session sftp, const char *name, + const char *data) { + int i, n; + + if (sftp == NULL || name == NULL || data == NULL) { + return 0; + } + + n = sftp_extensions_get_count(sftp); + for (i = 0; i < n; i++) { + const char *ext_name = sftp_extensions_get_name(sftp, i); + const char *ext_data = sftp_extensions_get_data(sftp, i); + + if (ext_name != NULL && ext_data != NULL && + strcmp(ext_name, name) == 0 && + strcmp(ext_data, data) == 0) { + return 1; + } + } + + return 0; +} + +static sftp_request_queue request_queue_new(sftp_message msg) { + sftp_request_queue queue = NULL; + + queue = malloc(sizeof(struct sftp_request_queue_struct)); + if (queue == NULL) { + ssh_set_error_oom(msg->sftp->session); + return NULL; + } + ZERO_STRUCTP(queue); + + queue->message = msg; + + return queue; +} + +static void request_queue_free(sftp_request_queue queue) { + if (queue == NULL) { + return; + } + + ZERO_STRUCTP(queue); + SAFE_FREE(queue); +} + +static int sftp_enqueue(sftp_session sftp, sftp_message msg) { + sftp_request_queue queue = NULL; + sftp_request_queue ptr; + + queue = request_queue_new(msg); + if (queue == NULL) { + return -1; + } + + ssh_log(sftp->session, SSH_LOG_PACKET, + "Queued msg type %d id %d", + msg->id, msg->packet_type); + + if(sftp->queue == NULL) { + sftp->queue = queue; + } else { + ptr = sftp->queue; + while(ptr->next) { + ptr=ptr->next; /* find end of linked list */ + } + ptr->next = queue; /* add it on bottom */ + } + + return 0; +} + +/* + * Pulls of a message from the queue based on the ID. + * Returns NULL if no message has been found. + */ +static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){ + sftp_request_queue prev = NULL; + sftp_request_queue queue; + sftp_message msg; + + if(sftp->queue == NULL) { + return NULL; + } + + queue = sftp->queue; + while (queue) { + if(queue->message->id == id) { + /* remove from queue */ + if (prev == NULL) { + sftp->queue = queue->next; + } else { + prev->next = queue->next; + } + msg = queue->message; + request_queue_free(queue); + ssh_log(sftp->session, SSH_LOG_PACKET, + "Dequeued msg id %d type %d", + msg->id, + msg->packet_type); + return msg; + } + prev = queue; + queue = queue->next; + } + + return NULL; +} + +/* + * Assigns a new SFTP ID for new requests and assures there is no collision + * between them. + * Returns a new ID ready to use in a request + */ +static inline uint32_t sftp_get_new_id(sftp_session session) { + return ++session->id_counter; +} + +static sftp_status_message parse_status_msg(sftp_message msg){ + sftp_status_message status; + + if (msg->packet_type != SSH_FXP_STATUS) { + ssh_set_error(msg->sftp->session, SSH_FATAL, + "Not a ssh_fxp_status message passed in!"); + return NULL; + } + + status = malloc(sizeof(struct sftp_status_message_struct)); + if (status == NULL) { + ssh_set_error_oom(msg->sftp->session); + return NULL; + } + ZERO_STRUCTP(status); + + status->id = msg->id; + if (buffer_get_u32(msg->payload,&status->status) != 4){ + SAFE_FREE(status); + ssh_set_error(msg->sftp->session, SSH_FATAL, + "Invalid SSH_FXP_STATUS message"); + return NULL; + } + status->error = buffer_get_ssh_string(msg->payload); + status->lang = buffer_get_ssh_string(msg->payload); + if(status->error == NULL || status->lang == NULL){ + if(msg->sftp->version >=3){ + /* These are mandatory from version 3 */ + ssh_string_free(status->error); + /* status->lang never get allocated if something failed */ + SAFE_FREE(status); + ssh_set_error(msg->sftp->session, SSH_FATAL, + "Invalid SSH_FXP_STATUS message"); + return NULL; + } + } + + status->status = ntohl(status->status); + if(status->error) + status->errormsg = ssh_string_to_char(status->error); + else + status->errormsg = strdup("No error message in packet"); + if(status->lang) + status->langmsg = ssh_string_to_char(status->lang); + else + status->langmsg = strdup(""); + if (status->errormsg == NULL || status->langmsg == NULL) { + ssh_set_error_oom(msg->sftp->session); + status_msg_free(status); + return NULL; + } + + return status; +} + +static void status_msg_free(sftp_status_message status){ + if (status == NULL) { + return; + } + + ssh_string_free(status->error); + ssh_string_free(status->lang); + SAFE_FREE(status->errormsg); + SAFE_FREE(status->langmsg); + SAFE_FREE(status); +} + +static sftp_file parse_handle_msg(sftp_message msg){ + sftp_file file; + + if(msg->packet_type != SSH_FXP_HANDLE) { + ssh_set_error(msg->sftp->session, SSH_FATAL, + "Not a ssh_fxp_handle message passed in!"); + return NULL; + } + + file = malloc(sizeof(struct sftp_file_struct)); + if (file == NULL) { + ssh_set_error_oom(msg->sftp->session); + return NULL; + } + ZERO_STRUCTP(file); + + file->handle = buffer_get_ssh_string(msg->payload); + if (file->handle == NULL) { + ssh_set_error(msg->sftp->session, SSH_FATAL, + "Invalid SSH_FXP_HANDLE message"); + SAFE_FREE(file); + return NULL; + } + + file->sftp = msg->sftp; + file->offset = 0; + file->eof = 0; + + return file; +} + +/* Open a directory */ +sftp_dir sftp_opendir(sftp_session sftp, const char *path){ + sftp_message msg = NULL; + sftp_file file = NULL; + sftp_dir dir = NULL; + sftp_status_message status; + ssh_string path_s; + ssh_buffer payload; + uint32_t id; + + payload = ssh_buffer_new(); + if (payload == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + + path_s = ssh_string_from_char(path); + if (path_s == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(payload); + return NULL; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(payload, id) < 0 || + buffer_add_ssh_string(payload, path_s) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(payload); + ssh_string_free(path_s); + return NULL; + } + ssh_string_free(path_s); + + if (sftp_packet_write(sftp, SSH_FXP_OPENDIR, payload) < 0) { + ssh_buffer_free(payload); + return NULL; + } + ssh_buffer_free(payload); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + /* something nasty has happened */ + return NULL; + } + msg = sftp_dequeue(sftp, id); + } + + switch (msg->packet_type) { + case SSH_FXP_STATUS: + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + sftp_set_error(sftp, status->status); + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return NULL; + case SSH_FXP_HANDLE: + file = parse_handle_msg(msg); + sftp_message_free(msg); + if (file != NULL) { + dir = malloc(sizeof(struct sftp_dir_struct)); + if (dir == NULL) { + ssh_set_error_oom(sftp->session); + free(file); + return NULL; + } + ZERO_STRUCTP(dir); + + dir->sftp = sftp; + dir->name = strdup(path); + if (dir->name == NULL) { + SAFE_FREE(dir); + SAFE_FREE(file); + return NULL; + } + dir->handle = file->handle; + SAFE_FREE(file); + } + return dir; + default: + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d during opendir!", msg->packet_type); + sftp_message_free(msg); + } + + return NULL; +} + +/* + * Parse the attributes from a payload from some messages. It is coded on + * baselines from the protocol version 4. + * This code is more or less dead but maybe we need it in future. + */ +static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf, + int expectnames) { + sftp_attributes attr; + ssh_string owner = NULL; + ssh_string group = NULL; + uint32_t flags = 0; + int ok = 0; + + /* unused member variable */ + (void) expectnames; + + attr = malloc(sizeof(struct sftp_attributes_struct)); + if (attr == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + ZERO_STRUCTP(attr); + + /* This isn't really a loop, but it is like a try..catch.. */ + do { + if (buffer_get_u32(buf, &flags) != 4) { + break; + } + + flags = ntohl(flags); + attr->flags = flags; + + if (flags & SSH_FILEXFER_ATTR_SIZE) { + if (buffer_get_u64(buf, &attr->size) != 8) { + break; + } + attr->size = ntohll(attr->size); + } + + if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) { + owner = buffer_get_ssh_string(buf); + if (owner == NULL) { + break; + } + attr->owner = ssh_string_to_char(owner); + string_free(owner); + if (attr->owner == NULL) { + break; + } + + group = buffer_get_ssh_string(buf); + if (group == NULL) { + break; + } + attr->group = ssh_string_to_char(group); + string_free(group); + if (attr->group == NULL) { + break; + } + } + + if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { + if (buffer_get_u32(buf, &attr->permissions) != 4) { + break; + } + attr->permissions = ntohl(attr->permissions); + + /* FIXME on windows! */ + switch (attr->permissions & S_IFMT) { + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + attr->type = SSH_FILEXFER_TYPE_SPECIAL; + break; + case S_IFLNK: + attr->type = SSH_FILEXFER_TYPE_SYMLINK; + break; + case S_IFREG: + attr->type = SSH_FILEXFER_TYPE_REGULAR; + break; + case S_IFDIR: + attr->type = SSH_FILEXFER_TYPE_DIRECTORY; + break; + default: + attr->type = SSH_FILEXFER_TYPE_UNKNOWN; + break; + } + } + + if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { + if (buffer_get_u64(buf, &attr->atime64) != 8) { + break; + } + attr->atime64 = ntohll(attr->atime64); + } + + if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { + if (buffer_get_u32(buf, &attr->atime_nseconds) != 4) { + break; + } + attr->atime_nseconds = ntohl(attr->atime_nseconds); + } + + if (flags & SSH_FILEXFER_ATTR_CREATETIME) { + if (buffer_get_u64(buf, &attr->createtime) != 8) { + break; + } + attr->createtime = ntohll(attr->createtime); + } + + if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { + if (buffer_get_u32(buf, &attr->createtime_nseconds) != 4) { + break; + } + attr->createtime_nseconds = ntohl(attr->createtime_nseconds); + } + + if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) { + if (buffer_get_u64(buf, &attr->mtime64) != 8) { + break; + } + attr->mtime64 = ntohll(attr->mtime64); + } + + if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { + if (buffer_get_u32(buf, &attr->mtime_nseconds) != 4) { + break; + } + attr->mtime_nseconds = ntohl(attr->mtime_nseconds); + } + + if (flags & SSH_FILEXFER_ATTR_ACL) { + if ((attr->acl = buffer_get_ssh_string(buf)) == NULL) { + break; + } + } + + if (flags & SSH_FILEXFER_ATTR_EXTENDED) { + if (buffer_get_u32(buf,&attr->extended_count) != 4) { + break; + } + attr->extended_count = ntohl(attr->extended_count); + + while(attr->extended_count && + (attr->extended_type = buffer_get_ssh_string(buf)) && + (attr->extended_data = buffer_get_ssh_string(buf))){ + attr->extended_count--; + } + + if (attr->extended_count) { + break; + } + } + ok = 1; + } while (0); + + if (ok == 0) { + /* break issued somewhere */ + ssh_string_free(attr->acl); + ssh_string_free(attr->extended_type); + ssh_string_free(attr->extended_data); + SAFE_FREE(attr->owner); + SAFE_FREE(attr->group); + SAFE_FREE(attr); + + ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure"); + + return NULL; + } + + return attr; +} + +enum sftp_longname_field_e { + SFTP_LONGNAME_PERM = 0, + SFTP_LONGNAME_FIXME, + SFTP_LONGNAME_OWNER, + SFTP_LONGNAME_GROUP, + SFTP_LONGNAME_SIZE, + SFTP_LONGNAME_DATE, + SFTP_LONGNAME_TIME, + SFTP_LONGNAME_NAME, +}; + +static char *sftp_parse_longname(const char *longname, + enum sftp_longname_field_e longname_field) { + const char *p, *q; + size_t len, field = 0; + char *x; + + p = longname; + /* Find the beginning of the field which is specified by sftp_longanme_field_e. */ + while(field != longname_field) { + if(isspace(*p)) { + field++; + p++; + while(*p && isspace(*p)) { + p++; + } + } else { + p++; + } + } + + q = p; + while (! isspace(*q)) { + q++; + } + + /* There is no strndup on windows */ + len = q - p + 1; + x = malloc(len); + if (x == NULL) { + return NULL; + } + + snprintf(x, len, "%s", p); + + return x; +} + +/* sftp version 0-3 code. It is different from the v4 */ +/* maybe a paste of the draft is better than the code */ +/* + uint32 flags + uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE + uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID + uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID + uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS + uint32 atime present only if flag SSH_FILEXFER_ACMODTIME + uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME + uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED + string extended_type + string extended_data + ... more extended data (extended_type - extended_data pairs), + so that number of pairs equals extended_count */ +static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, + int expectname) { + ssh_string longname; + ssh_string name; + sftp_attributes attr; + uint32_t flags = 0; + int ok = 0; + + attr = malloc(sizeof(struct sftp_attributes_struct)); + if (attr == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + ZERO_STRUCTP(attr); + + /* This isn't really a loop, but it is like a try..catch.. */ + do { + if (expectname) { + name = buffer_get_ssh_string(buf); + if (name == NULL) { + break; + } + attr->name = ssh_string_to_char(name); + ssh_string_free(name); + if (attr->name == NULL) { + break; + } + + ssh_log(sftp->session, SSH_LOG_RARE, "Name: %s", attr->name); + + longname = buffer_get_ssh_string(buf); + if (longname == NULL) { + break; + } + attr->longname = ssh_string_to_char(longname); + ssh_string_free(longname); + if (attr->longname == NULL) { + break; + } + + /* Set owner and group if we talk to openssh and have the longname */ + if (ssh_get_openssh_version(sftp->session)) { + attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER); + if (attr->owner == NULL) { + break; + } + + attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP); + if (attr->group == NULL) { + break; + } + } + } + + if (buffer_get_u32(buf, &flags) != sizeof(uint32_t)) { + break; + } + flags = ntohl(flags); + attr->flags = flags; + ssh_log(sftp->session, SSH_LOG_RARE, + "Flags: %.8lx\n", (long unsigned int) flags); + + if (flags & SSH_FILEXFER_ATTR_SIZE) { + if(buffer_get_u64(buf, &attr->size) != sizeof(uint64_t)) { + break; + } + attr->size = ntohll(attr->size); + ssh_log(sftp->session, SSH_LOG_RARE, + "Size: %llu\n", + (long long unsigned int) attr->size); + } + + if (flags & SSH_FILEXFER_ATTR_UIDGID) { + if (buffer_get_u32(buf, &attr->uid) != sizeof(uint32_t)) { + break; + } + if (buffer_get_u32(buf, &attr->gid) != sizeof(uint32_t)) { + break; + } + attr->uid = ntohl(attr->uid); + attr->gid = ntohl(attr->gid); + } + + if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { + if (buffer_get_u32(buf, &attr->permissions) != sizeof(uint32_t)) { + break; + } + attr->permissions = ntohl(attr->permissions); + + switch (attr->permissions & S_IFMT) { + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + attr->type = SSH_FILEXFER_TYPE_SPECIAL; + break; + case S_IFLNK: + attr->type = SSH_FILEXFER_TYPE_SYMLINK; + break; + case S_IFREG: + attr->type = SSH_FILEXFER_TYPE_REGULAR; + break; + case S_IFDIR: + attr->type = SSH_FILEXFER_TYPE_DIRECTORY; + break; + default: + attr->type = SSH_FILEXFER_TYPE_UNKNOWN; + break; + } + } + + if (flags & SSH_FILEXFER_ATTR_ACMODTIME) { + if (buffer_get_u32(buf, &attr->atime) != sizeof(uint32_t)) { + break; + } + attr->atime = ntohl(attr->atime); + if (buffer_get_u32(buf, &attr->mtime) != sizeof(uint32_t)) { + break; + } + attr->mtime = ntohl(attr->mtime); + } + + if (flags & SSH_FILEXFER_ATTR_EXTENDED) { + if (buffer_get_u32(buf, &attr->extended_count) != sizeof(uint32_t)) { + break; + } + + attr->extended_count = ntohl(attr->extended_count); + while (attr->extended_count && + (attr->extended_type = buffer_get_ssh_string(buf)) + && (attr->extended_data = buffer_get_ssh_string(buf))) { + attr->extended_count--; + } + + if (attr->extended_count) { + break; + } + } + ok = 1; + } while (0); + + if (!ok) { + /* break issued somewhere */ + ssh_string_free(attr->extended_type); + ssh_string_free(attr->extended_data); + SAFE_FREE(attr->name); + SAFE_FREE(attr->longname); + SAFE_FREE(attr->owner); + SAFE_FREE(attr->group); + SAFE_FREE(attr); + + ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure"); + + return NULL; + } + + /* everything went smoothly */ + return attr; +} + +/* FIXME is this really needed as a public function? */ +int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr) { + uint32_t flags = (attr ? attr->flags : 0); + + flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID | + SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); + + if (buffer_add_u32(buffer, htonl(flags)) < 0) { + return -1; + } + + if (attr) { + if (flags & SSH_FILEXFER_ATTR_SIZE) { + if (buffer_add_u64(buffer, htonll(attr->size)) < 0) { + return -1; + } + } + + if (flags & SSH_FILEXFER_ATTR_UIDGID) { + if (buffer_add_u32(buffer,htonl(attr->uid)) < 0 || + buffer_add_u32(buffer,htonl(attr->gid)) < 0) { + return -1; + } + } + + if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { + if (buffer_add_u32(buffer, htonl(attr->permissions)) < 0) { + return -1; + } + } + + if (flags & SSH_FILEXFER_ATTR_ACMODTIME) { + if (buffer_add_u32(buffer, htonl(attr->atime)) < 0 || + buffer_add_u32(buffer, htonl(attr->mtime)) < 0) { + return -1; + } + } + } + + return 0; +} + + +sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf, + int expectname) { + switch(session->version) { + case 4: + return sftp_parse_attr_4(session, buf, expectname); + case 3: + case 2: + case 1: + case 0: + return sftp_parse_attr_3(session, buf, expectname); + default: + ssh_set_error(session->session, SSH_FATAL, + "Version %d unsupported by client", session->server_version); + return NULL; + } + + return NULL; +} + +/* Get the version of the SFTP protocol supported by the server */ +int sftp_server_version(sftp_session sftp) { + return sftp->server_version; +} + +/* Get a single file attributes structure of a directory. */ +sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) { + sftp_message msg = NULL; + sftp_status_message status; + sftp_attributes attr; + ssh_buffer payload; + uint32_t id; + + if (dir->buffer == NULL) { + payload = ssh_buffer_new(); + if (payload == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(payload, id) < 0 || + buffer_add_ssh_string(payload, dir->handle) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(payload); + return NULL; + } + + if (sftp_packet_write(sftp, SSH_FXP_READDIR, payload) < 0) { + ssh_buffer_free(payload); + return NULL; + } + ssh_buffer_free(payload); + + ssh_log(sftp->session, SSH_LOG_PACKET, + "Sent a ssh_fxp_readdir with id %d", id); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + /* something nasty has happened */ + return NULL; + } + msg = sftp_dequeue(sftp, id); + } + + switch (msg->packet_type){ + case SSH_FXP_STATUS: + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_EOF: + dir->eof = 1; + status_msg_free(status); + return NULL; + default: + break; + } + + ssh_set_error(sftp->session, SSH_FATAL, + "Unknown error status: %d", status->status); + status_msg_free(status); + + return NULL; + case SSH_FXP_NAME: + buffer_get_u32(msg->payload, &dir->count); + dir->count = ntohl(dir->count); + dir->buffer = msg->payload; + msg->payload = NULL; + sftp_message_free(msg); + break; + default: + ssh_set_error(sftp->session, SSH_FATAL, + "Unsupported message back %d", msg->packet_type); + sftp_message_free(msg); + + return NULL; + } + } + + /* now dir->buffer contains a buffer and dir->count != 0 */ + if (dir->count == 0) { + ssh_set_error(sftp->session, SSH_FATAL, + "Count of files sent by the server is zero, which is invalid, or " + "libsftp bug"); + return NULL; + } + + ssh_log(sftp->session, SSH_LOG_RARE, "Count is %d", dir->count); + + attr = sftp_parse_attr(sftp, dir->buffer, 1); + if (attr == NULL) { + ssh_set_error(sftp->session, SSH_FATAL, + "Couldn't parse the SFTP attributes"); + return NULL; + } + + dir->count--; + if (dir->count == 0) { + ssh_buffer_free(dir->buffer); + dir->buffer = NULL; + } + + return attr; +} + +/* Tell if the directory has reached EOF (End Of File). */ +int sftp_dir_eof(sftp_dir dir) { + return dir->eof; +} + +/* Free a SFTP_ATTRIBUTE handle */ +void sftp_attributes_free(sftp_attributes file){ + if (file == NULL) { + return; + } + + ssh_string_free(file->acl); + ssh_string_free(file->extended_data); + ssh_string_free(file->extended_type); + + SAFE_FREE(file->name); + SAFE_FREE(file->longname); + SAFE_FREE(file->group); + SAFE_FREE(file->owner); + + SAFE_FREE(file); +} + +static int sftp_handle_close(sftp_session sftp, ssh_string handle) { + sftp_status_message status; + sftp_message msg = NULL; + ssh_buffer buffer = NULL; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, handle) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + if (sftp_packet_write(sftp, SSH_FXP_CLOSE ,buffer) < 0) { + ssh_buffer_free(buffer); + return -1; + } + ssh_buffer_free(buffer); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + /* something nasty has happened */ + return -1; + } + msg = sftp_dequeue(sftp,id); + } + + switch (msg->packet_type) { + case SSH_FXP_STATUS: + status = parse_status_msg(msg); + sftp_message_free(msg); + if(status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_OK: + status_msg_free(status); + return 0; + break; + default: + break; + } + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return -1; + default: + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d during sftp_handle_close!", msg->packet_type); + sftp_message_free(msg); + } + + return -1; +} + +/* Close an open file handle. */ +int sftp_close(sftp_file file){ + int err = SSH_NO_ERROR; + + SAFE_FREE(file->name); + if (file->handle){ + err = sftp_handle_close(file->sftp,file->handle); + ssh_string_free(file->handle); + } + /* FIXME: check server response and implement errno */ + SAFE_FREE(file); + + return err; +} + +/* Close an open directory. */ +int sftp_closedir(sftp_dir dir){ + int err = SSH_NO_ERROR; + + SAFE_FREE(dir->name); + if (dir->handle) { + err = sftp_handle_close(dir->sftp, dir->handle); + ssh_string_free(dir->handle); + } + /* FIXME: check server response and implement errno */ + ssh_buffer_free(dir->buffer); + SAFE_FREE(dir); + + return err; +} + +/* Open a file on the server. */ +sftp_file sftp_open(sftp_session sftp, const char *file, int flags, + mode_t mode) { + sftp_message msg = NULL; + sftp_status_message status; + struct sftp_attributes_struct attr; + sftp_file handle; + ssh_string filename; + ssh_buffer buffer; + uint32_t sftp_flags = 0; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + + filename = ssh_string_from_char(file); + if (filename == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return NULL; + } + + ZERO_STRUCT(attr); + attr.permissions = mode; + attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; + + if (flags == O_RDONLY) + sftp_flags |= SSH_FXF_READ; /* if any of the other flag is set, + READ should not be set initialy */ + if (flags & O_WRONLY) + sftp_flags |= SSH_FXF_WRITE; + if (flags & O_RDWR) + sftp_flags |= (SSH_FXF_WRITE | SSH_FXF_READ); + if (flags & O_CREAT) + sftp_flags |= SSH_FXF_CREAT; + if (flags & O_TRUNC) + sftp_flags |= SSH_FXF_TRUNC; + if (flags & O_EXCL) + sftp_flags |= SSH_FXF_EXCL; + ssh_log(sftp->session,SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags); + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, filename) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(filename); + return NULL; + } + ssh_string_free(filename); + + if (buffer_add_u32(buffer, htonl(sftp_flags)) < 0 || + buffer_add_attributes(buffer, &attr) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return NULL; + } + if (sftp_packet_write(sftp, SSH_FXP_OPEN, buffer) < 0) { + ssh_buffer_free(buffer); + return NULL; + } + ssh_buffer_free(buffer); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + /* something nasty has happened */ + return NULL; + } + msg = sftp_dequeue(sftp, id); + } + + switch (msg->packet_type) { + case SSH_FXP_STATUS: + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + sftp_set_error(sftp, status->status); + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + + return NULL; + case SSH_FXP_HANDLE: + handle = parse_handle_msg(msg); + sftp_message_free(msg); + return handle; + default: + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d during open!", msg->packet_type); + sftp_message_free(msg); + } + + return NULL; +} + +void sftp_file_set_nonblocking(sftp_file handle){ + handle->nonblocking=1; +} +void sftp_file_set_blocking(sftp_file handle){ + handle->nonblocking=0; +} + +/* Read from a file using an opened sftp file handle. */ +ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { + sftp_session sftp = handle->sftp; + sftp_message msg = NULL; + sftp_status_message status; + ssh_string datastring; + ssh_buffer buffer; + int id; + + if (handle->eof) { + return 0; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + id = sftp_get_new_id(handle->sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, handle->handle) < 0 || + buffer_add_u64(buffer, htonll(handle->offset)) < 0 || + buffer_add_u32(buffer,htonl(count)) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + if (sftp_packet_write(handle->sftp, SSH_FXP_READ, buffer) < 0) { + ssh_buffer_free(buffer); + return -1; + } + ssh_buffer_free(buffer); + + while (msg == NULL) { + if (handle->nonblocking) { + if (ssh_channel_poll(handle->sftp->channel, 0) == 0) { + /* we cannot block */ + return 0; + } + } + if (sftp_read_and_dispatch(handle->sftp) < 0) { + /* something nasty has happened */ + return -1; + } + msg = sftp_dequeue(handle->sftp, id); + } + + switch (msg->packet_type) { + case SSH_FXP_STATUS: + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_EOF: + handle->eof = 1; + status_msg_free(status); + return 0; + default: + break; + } + ssh_set_error(sftp->session,SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return -1; + case SSH_FXP_DATA: + datastring = buffer_get_ssh_string(msg->payload); + sftp_message_free(msg); + if (datastring == NULL) { + ssh_set_error(sftp->session, SSH_FATAL, + "Received invalid DATA packet from sftp server"); + return -1; + } + + if (ssh_string_len(datastring) > count) { + ssh_set_error(sftp->session, SSH_FATAL, + "Received a too big DATA packet from sftp server: " + "%" PRIdS " and asked for %" PRIdS, + ssh_string_len(datastring), count); + ssh_string_free(datastring); + return -1; + } + count = ssh_string_len(datastring); + handle->offset += count; + memcpy(buf, ssh_string_data(datastring), count); + ssh_string_free(datastring); + return count; + default: + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d during read!", msg->packet_type); + sftp_message_free(msg); + return -1; + } + + return -1; /* not reached */ +} + +/* Start an asynchronous read from a file using an opened sftp file handle. */ +int sftp_async_read_begin(sftp_file file, uint32_t len){ + sftp_session sftp = file->sftp; + ssh_buffer buffer; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, file->handle) < 0 || + buffer_add_u64(buffer, htonll(file->offset)) < 0 || + buffer_add_u32(buffer, htonl(len)) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + if (sftp_packet_write(sftp, SSH_FXP_READ, buffer) < 0) { + ssh_buffer_free(buffer); + return -1; + } + ssh_buffer_free(buffer); + + file->offset += len; /* assume we'll read len bytes */ + + return id; +} + +/* Wait for an asynchronous read to complete and save the data. */ +int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){ + sftp_session sftp; + sftp_message msg = NULL; + sftp_status_message status; + ssh_string datastring; + int err = SSH_OK; + uint32_t len; + + if (file == NULL) { + return SSH_ERROR; + } + sftp = file->sftp; + + if (file->eof) { + return 0; + } + + /* handle an existing request */ + while (msg == NULL) { + if (file->nonblocking){ + if (ssh_channel_poll(sftp->channel, 0) == 0) { + /* we cannot block */ + return SSH_AGAIN; + } + } + + if (sftp_read_and_dispatch(sftp) < 0) { + /* something nasty has happened */ + return SSH_ERROR; + } + + msg = sftp_dequeue(sftp,id); + } + + switch (msg->packet_type) { + case SSH_FXP_STATUS: + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + if (status->status != SSH_FX_EOF) { + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server : %s", status->errormsg); + err = SSH_ERROR; + } else { + file->eof = 1; + } + status_msg_free(status); + return err; + case SSH_FXP_DATA: + datastring = buffer_get_ssh_string(msg->payload); + sftp_message_free(msg); + if (datastring == NULL) { + ssh_set_error(sftp->session, SSH_FATAL, + "Received invalid DATA packet from sftp server"); + return SSH_ERROR; + } + if (ssh_string_len(datastring) > size) { + ssh_set_error(sftp->session, SSH_FATAL, + "Received a too big DATA packet from sftp server: " + "%" PRIdS " and asked for %u", + ssh_string_len(datastring), size); + ssh_string_free(datastring); + return SSH_ERROR; + } + len = ssh_string_len(datastring); + /* Update the offset with the correct value */ + file->offset = file->offset - (size - len); + memcpy(data, ssh_string_data(datastring), len); + ssh_string_free(datastring); + return len; + default: + ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during read!",msg->packet_type); + sftp_message_free(msg); + return SSH_ERROR; + } + + return SSH_ERROR; +} + +ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { + sftp_session sftp = file->sftp; + sftp_message msg = NULL; + sftp_status_message status; + ssh_string datastring; + ssh_buffer buffer; + uint32_t id; + int len; + int packetlen; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + datastring = ssh_string_new(count); + if (datastring == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + ssh_string_fill(datastring, buf, count); + + id = sftp_get_new_id(file->sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, file->handle) < 0 || + buffer_add_u64(buffer, htonll(file->offset)) < 0 || + buffer_add_ssh_string(buffer, datastring) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(datastring); + return -1; + } + ssh_string_free(datastring); + packetlen=buffer_get_rest_len(buffer); + len = sftp_packet_write(file->sftp, SSH_FXP_WRITE, buffer); + ssh_buffer_free(buffer); + if (len < 0) { + return -1; + } else if (len != packetlen) { + ssh_log(sftp->session, SSH_LOG_PACKET, + "Could not write as much data as expected"); + } + + while (msg == NULL) { + if (sftp_read_and_dispatch(file->sftp) < 0) { + /* something nasty has happened */ + return -1; + } + msg = sftp_dequeue(file->sftp, id); + } + + switch (msg->packet_type) { + case SSH_FXP_STATUS: + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_OK: + file->offset += count; + status_msg_free(status); + return count; + default: + break; + } + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + file->offset += count; + status_msg_free(status); + return -1; + default: + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d during write!", msg->packet_type); + sftp_message_free(msg); + return -1; + } + + return -1; /* not reached */ +} + +/* Seek to a specific location in a file. */ +int sftp_seek(sftp_file file, uint32_t new_offset) { + if (file == NULL) { + return -1; + } + + file->offset = new_offset; + file->eof = 0; + + return 0; +} + +int sftp_seek64(sftp_file file, uint64_t new_offset) { + if (file == NULL) { + return -1; + } + + file->offset = new_offset; + file->eof = 0; + + return 0; +} + +/* Report current byte position in file. */ +unsigned long sftp_tell(sftp_file file) { + return (unsigned long)file->offset; +} +/* Report current byte position in file. */ +uint64_t sftp_tell64(sftp_file file) { + return (uint64_t) file->offset; +} + +/* Rewinds the position of the file pointer to the beginning of the file.*/ +void sftp_rewind(sftp_file file) { + file->offset = 0; + file->eof = 0; +} + +/* code written by Nick */ +int sftp_unlink(sftp_session sftp, const char *file) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_string filename; + ssh_buffer buffer; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + filename = ssh_string_from_char(file); + if (filename == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, filename) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(filename); + return -1; + } + if (sftp_packet_write(sftp, SSH_FXP_REMOVE, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(filename); + return -1; + } + ssh_string_free(filename); + ssh_buffer_free(buffer); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp)) { + return -1; + } + msg = sftp_dequeue(sftp, id); + } + + if (msg->packet_type == SSH_FXP_STATUS) { + /* by specification, this command's only supposed to return SSH_FXP_STATUS */ + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_OK: + status_msg_free(status); + return 0; + default: + break; + } + + /* + * The status should be SSH_FX_OK if the command was successful, if it + * didn't, then there was an error + */ + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return -1; + } else { + ssh_set_error(sftp->session,SSH_FATAL, + "Received message %d when attempting to remove file", msg->packet_type); + sftp_message_free(msg); + } + + return -1; +} + +/* code written by Nick */ +int sftp_rmdir(sftp_session sftp, const char *directory) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_string filename; + ssh_buffer buffer; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + filename = ssh_string_from_char(directory); + if (filename == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, filename) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(filename); + return -1; + } + if (sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(filename); + return -1; + } + ssh_buffer_free(buffer); + ssh_string_free(filename); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return -1; + } + msg = sftp_dequeue(sftp, id); + } + + /* By specification, this command returns SSH_FXP_STATUS */ + if (msg->packet_type == SSH_FXP_STATUS) { + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_OK: + status_msg_free(status); + return 0; + break; + default: + break; + } + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return -1; + } else { + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to remove directory", + msg->packet_type); + sftp_message_free(msg); + } + + return -1; +} + +/* Code written by Nick */ +int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + sftp_attributes errno_attr = NULL; + struct sftp_attributes_struct attr; + ssh_buffer buffer; + ssh_string path; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + path = ssh_string_from_char(directory); + if (path == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + + ZERO_STRUCT(attr); + attr.permissions = mode; + attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, path) < 0 || + buffer_add_attributes(buffer, &attr) < 0 || + sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(path); + return -1; + } + ssh_buffer_free(buffer); + ssh_string_free(path); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return -1; + } + msg = sftp_dequeue(sftp, id); + } + + /* By specification, this command only returns SSH_FXP_STATUS */ + if (msg->packet_type == SSH_FXP_STATUS) { + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_FAILURE: + /* + * mkdir always returns a failure, even if the path already exists. + * To be POSIX conform and to be able to map it to EEXIST a stat + * call is needed here. + */ + errno_attr = sftp_lstat(sftp, directory); + if (errno_attr != NULL) { + SAFE_FREE(errno_attr); + sftp_set_error(sftp, SSH_FX_FILE_ALREADY_EXISTS); + } + break; + case SSH_FX_OK: + status_msg_free(status); + return 0; + break; + default: + break; + } + /* + * The status should be SSH_FX_OK if the command was successful, if it + * didn't, then there was an error + */ + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return -1; + } else { + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to make directory", + msg->packet_type); + sftp_message_free(msg); + } + + return -1; +} + +/* code written by nick */ +int sftp_rename(sftp_session sftp, const char *original, const char *newname) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_buffer buffer; + ssh_string oldpath; + ssh_string newpath; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + oldpath = ssh_string_from_char(original); + if (oldpath == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + + newpath = ssh_string_from_char(newname); + if (newpath == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(oldpath); + return -1; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, oldpath) < 0 || + buffer_add_ssh_string(buffer, newpath) < 0 || + /* POSIX rename atomically replaces newpath, we should do the same + * only available on >=v4 */ + sftp->version>=4 ? (buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE) < 0):0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(oldpath); + ssh_string_free(newpath); + return -1; + } + if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(oldpath); + ssh_string_free(newpath); + return -1; + } + ssh_buffer_free(buffer); + ssh_string_free(oldpath); + ssh_string_free(newpath); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return -1; + } + msg = sftp_dequeue(sftp, id); + } + + /* By specification, this command only returns SSH_FXP_STATUS */ + if (msg->packet_type == SSH_FXP_STATUS) { + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_OK: + status_msg_free(status); + return 0; + default: + break; + } + /* + * Status should be SSH_FX_OK if the command was successful, if it didn't, + * then there was an error + */ + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return -1; + } else { + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to rename", + msg->packet_type); + sftp_message_free(msg); + } + + return -1; +} + +/* Code written by Nick */ +/* Set file attributes on a file, directory or symbolic link. */ +int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) { + uint32_t id; + ssh_buffer buffer; + ssh_string path; + sftp_message msg = NULL; + sftp_status_message status = NULL; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + path = ssh_string_from_char(file); + if (path == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, path) < 0 || + buffer_add_attributes(buffer, attr) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(path); + return -1; + } + if (sftp_packet_write(sftp, SSH_FXP_SETSTAT, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(path); + return -1; + } + ssh_buffer_free(buffer); + ssh_string_free(path); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return -1; + } + msg = sftp_dequeue(sftp, id); + } + + /* By specification, this command only returns SSH_FXP_STATUS */ + if (msg->packet_type == SSH_FXP_STATUS) { + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_OK: + status_msg_free(status); + return 0; + default: + break; + } + /* + * The status should be SSH_FX_OK if the command was successful, if it + * didn't, then there was an error + */ + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return -1; + } else { + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to set stats", msg->packet_type); + sftp_message_free(msg); + } + + return -1; +} + +/* Change the file owner and group */ +int sftp_chown(sftp_session sftp, const char *file, uid_t owner, gid_t group) { + struct sftp_attributes_struct attr; + ZERO_STRUCT(attr); + + attr.uid = owner; + attr.gid = group; + + attr.flags = SSH_FILEXFER_ATTR_UIDGID; + + return sftp_setstat(sftp, file, &attr); +} + +/* Change permissions of a file */ +int sftp_chmod(sftp_session sftp, const char *file, mode_t mode) { + struct sftp_attributes_struct attr; + ZERO_STRUCT(attr); + attr.permissions = mode; + attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; + + return sftp_setstat(sftp, file, &attr); +} + +/* Change the last modification and access time of a file. */ +int sftp_utimes(sftp_session sftp, const char *file, + const struct timeval *times) { + struct sftp_attributes_struct attr; + ZERO_STRUCT(attr); + + attr.atime = times[0].tv_sec; + attr.atime_nseconds = times[0].tv_usec; + + attr.mtime = times[1].tv_sec; + attr.mtime_nseconds = times[1].tv_usec; + + attr.flags |= SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_MODIFYTIME | + SSH_FILEXFER_ATTR_SUBSECOND_TIMES; + + return sftp_setstat(sftp, file, &attr); +} + +int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_string target_s; + ssh_string dest_s; + ssh_buffer buffer; + uint32_t id; + + if (sftp == NULL) + return -1; + if (target == NULL || dest == NULL) { + ssh_set_error_invalid(sftp->session); + return -1; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } + + target_s = ssh_string_from_char(target); + if (target_s == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + + dest_s = ssh_string_from_char(dest); + if (dest_s == NULL) { + ssh_set_error_oom(sftp->session); + ssh_string_free(target_s); + ssh_buffer_free(buffer); + return -1; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(dest_s); + ssh_string_free(target_s); + return -1; + } + if (ssh_get_openssh_version(sftp->session)) { + /* TODO check for version number if they ever fix it. */ + if (buffer_add_ssh_string(buffer, target_s) < 0 || + buffer_add_ssh_string(buffer, dest_s) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(dest_s); + ssh_string_free(target_s); + return -1; + } + } else { + if (buffer_add_ssh_string(buffer, dest_s) < 0 || + buffer_add_ssh_string(buffer, target_s) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(dest_s); + ssh_string_free(target_s); + return -1; + } + } + + if (sftp_packet_write(sftp, SSH_FXP_SYMLINK, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(dest_s); + ssh_string_free(target_s); + return -1; + } + ssh_buffer_free(buffer); + ssh_string_free(dest_s); + ssh_string_free(target_s); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return -1; + } + msg = sftp_dequeue(sftp, id); + } + + /* By specification, this command only returns SSH_FXP_STATUS */ + if (msg->packet_type == SSH_FXP_STATUS) { + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_OK: + status_msg_free(status); + return 0; + default: + break; + } + /* + * The status should be SSH_FX_OK if the command was successful, if it + * didn't, then there was an error + */ + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return -1; + } else { + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to set stats", msg->packet_type); + sftp_message_free(msg); + } + + return -1; +} + +char *sftp_readlink(sftp_session sftp, const char *path) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_string path_s = NULL; + ssh_string link_s = NULL; + ssh_buffer buffer; + char *lnk; + uint32_t ignored; + uint32_t id; + + if (sftp == NULL) + return NULL; + if (path == NULL) { + ssh_set_error_invalid(sftp); + return NULL; + } + if (sftp->version < 3){ + ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_readlink",sftp->version); + return NULL; + } + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + + path_s = ssh_string_from_char(path); + if (path_s == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return NULL; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, path_s) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(path_s); + return NULL; + } + if (sftp_packet_write(sftp, SSH_FXP_READLINK, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(path_s); + return NULL; + } + ssh_buffer_free(buffer); + ssh_string_free(path_s); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return NULL; + } + msg = sftp_dequeue(sftp, id); + } + + if (msg->packet_type == SSH_FXP_NAME) { + /* we don't care about "count" */ + buffer_get_u32(msg->payload, &ignored); + /* we only care about the file name string */ + link_s = buffer_get_ssh_string(msg->payload); + sftp_message_free(msg); + if (link_s == NULL) { + /* TODO: what error to set here? */ + return NULL; + } + lnk = ssh_string_to_char(link_s); + ssh_string_free(link_s); + + return lnk; + } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + } else { /* this shouldn't happen */ + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to set stats", msg->packet_type); + sftp_message_free(msg); + } + + return NULL; +} + +static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) { + sftp_statvfs_t statvfs; + uint64_t tmp; + int ok = 0; + + statvfs = malloc(sizeof(struct sftp_statvfs_struct)); + if (statvfs == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + ZERO_STRUCTP(statvfs); + + /* try .. catch */ + do { + /* file system block size */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_bsize = ntohll(tmp); + + /* fundamental fs block size */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_frsize = ntohll(tmp); + + /* number of blocks (unit f_frsize) */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_blocks = ntohll(tmp); + + /* free blocks in file system */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_bfree = ntohll(tmp); + + /* free blocks for non-root */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_bavail = ntohll(tmp); + + /* total file inodes */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_files = ntohll(tmp); + + /* free file inodes */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_ffree = ntohll(tmp); + + /* free file inodes for to non-root */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_favail = ntohll(tmp); + + /* file system id */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_fsid = ntohll(tmp); + + /* bit mask of f_flag values */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_flag = ntohll(tmp); + + /* maximum filename length */ + if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { + break; + } + statvfs->f_namemax = ntohll(tmp); + + ok = 1; + } while(0); + + if (!ok) { + SAFE_FREE(statvfs); + ssh_set_error(sftp->session, SSH_FATAL, "Invalid statvfs structure"); + return NULL; + } + + return statvfs; +} + +sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_string pathstr; + ssh_string ext; + ssh_buffer buffer; + uint32_t id; + + if (sftp == NULL) + return NULL; + if (path == NULL) { + ssh_set_error_invalid(sftp->session); + return NULL; + } + if (sftp->version < 3){ + ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_statvfs",sftp->version); + return NULL; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + + ext = ssh_string_from_char("statvfs@openssh.com"); + if (ext == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return NULL; + } + + pathstr = ssh_string_from_char(path); + if (pathstr == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(ext); + return NULL; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, ext) < 0 || + buffer_add_ssh_string(buffer, pathstr) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(ext); + ssh_string_free(pathstr); + return NULL; + } + if (sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(ext); + ssh_string_free(pathstr); + return NULL; + } + ssh_buffer_free(buffer); + ssh_string_free(ext); + ssh_string_free(pathstr); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return NULL; + } + msg = sftp_dequeue(sftp, id); + } + + if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) { + sftp_statvfs_t buf = sftp_parse_statvfs(sftp, msg->payload); + sftp_message_free(msg); + if (buf == NULL) { + return NULL; + } + + return buf; + } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + } else { /* this shouldn't happen */ + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to get statvfs", msg->packet_type); + sftp_message_free(msg); + } + + return NULL; +} + +sftp_statvfs_t sftp_fstatvfs(sftp_file file) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + sftp_session sftp; + ssh_string ext; + ssh_buffer buffer; + uint32_t id; + + if (file == NULL) { + return NULL; + } + sftp = file->sftp; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + + ext = ssh_string_from_char("fstatvfs@openssh.com"); + if (ext == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return NULL; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, ext) < 0 || + buffer_add_ssh_string(buffer, file->handle) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(ext); + return NULL; + } + if (sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(ext); + return NULL; + } + ssh_buffer_free(buffer); + ssh_string_free(ext); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return NULL; + } + msg = sftp_dequeue(sftp, id); + } + + if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) { + sftp_statvfs_t buf = sftp_parse_statvfs(sftp, msg->payload); + sftp_message_free(msg); + if (buf == NULL) { + return NULL; + } + + return buf; + } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + } else { /* this shouldn't happen */ + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to set stats", msg->packet_type); + sftp_message_free(msg); + } + + return NULL; +} + +void sftp_statvfs_free(sftp_statvfs_t statvfs) { + if (statvfs == NULL) { + return; + } + + SAFE_FREE(statvfs); +} + +/* another code written by Nick */ +char *sftp_canonicalize_path(sftp_session sftp, const char *path) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_string name = NULL; + ssh_string pathstr; + ssh_buffer buffer; + char *cname; + uint32_t ignored; + uint32_t id; + + if (sftp == NULL) + return NULL; + if (path == NULL) { + ssh_set_error_invalid(sftp->session); + return NULL; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + + pathstr = ssh_string_from_char(path); + if (pathstr == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return NULL; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, pathstr) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(pathstr); + return NULL; + } + if (sftp_packet_write(sftp, SSH_FXP_REALPATH, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(pathstr); + return NULL; + } + ssh_buffer_free(buffer); + ssh_string_free(pathstr); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return NULL; + } + msg = sftp_dequeue(sftp, id); + } + + if (msg->packet_type == SSH_FXP_NAME) { + /* we don't care about "count" */ + buffer_get_u32(msg->payload, &ignored); + /* we only care about the file name string */ + name = buffer_get_ssh_string(msg->payload); + sftp_message_free(msg); + if (name == NULL) { + /* TODO: error message? */ + return NULL; + } + cname = ssh_string_to_char(name); + ssh_string_free(name); + if (cname == NULL) { + ssh_set_error_oom(sftp->session); + } + return cname; + } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + } else { /* this shouldn't happen */ + ssh_set_error(sftp->session, SSH_FATAL, + "Received message %d when attempting to set stats", msg->packet_type); + sftp_message_free(msg); + } + + return NULL; +} + +static sftp_attributes sftp_xstat(sftp_session sftp, const char *path, + int param) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_string pathstr; + ssh_buffer buffer; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + + pathstr = ssh_string_from_char(path); + if (pathstr == NULL) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return NULL; + } + + id = sftp_get_new_id(sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, pathstr) < 0) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + ssh_string_free(pathstr); + return NULL; + } + if (sftp_packet_write(sftp, param, buffer) < 0) { + ssh_buffer_free(buffer); + ssh_string_free(pathstr); + return NULL; + } + ssh_buffer_free(buffer); + ssh_string_free(pathstr); + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return NULL; + } + msg = sftp_dequeue(sftp, id); + } + + if (msg->packet_type == SSH_FXP_ATTRS) { + sftp_attributes attr = sftp_parse_attr(sftp, msg->payload, 0); + sftp_message_free(msg); + + return attr; + } else if (msg->packet_type == SSH_FXP_STATUS) { + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + sftp_set_error(sftp, status->status); + ssh_set_error(sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + return NULL; + } + ssh_set_error(sftp->session, SSH_FATAL, + "Received mesg %d during stat()", msg->packet_type); + sftp_message_free(msg); + + return NULL; +} + +sftp_attributes sftp_stat(sftp_session session, const char *path) { + return sftp_xstat(session, path, SSH_FXP_STAT); +} + +sftp_attributes sftp_lstat(sftp_session session, const char *path) { + return sftp_xstat(session, path, SSH_FXP_LSTAT); +} + +sftp_attributes sftp_fstat(sftp_file file) { + sftp_status_message status = NULL; + sftp_message msg = NULL; + ssh_buffer buffer; + uint32_t id; + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(file->sftp->session); + return NULL; + } + + id = sftp_get_new_id(file->sftp); + if (buffer_add_u32(buffer, id) < 0 || + buffer_add_ssh_string(buffer, file->handle) < 0) { + ssh_set_error_oom(file->sftp->session); + ssh_buffer_free(buffer); + return NULL; + } + if (sftp_packet_write(file->sftp, SSH_FXP_FSTAT, buffer) < 0) { + ssh_buffer_free(buffer); + return NULL; + } + ssh_buffer_free(buffer); + + while (msg == NULL) { + if (sftp_read_and_dispatch(file->sftp) < 0) { + return NULL; + } + msg = sftp_dequeue(file->sftp, id); + } + + if (msg->packet_type == SSH_FXP_ATTRS){ + return sftp_parse_attr(file->sftp, msg->payload, 0); + } else if (msg->packet_type == SSH_FXP_STATUS) { + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return NULL; + } + ssh_set_error(file->sftp->session, SSH_REQUEST_DENIED, + "SFTP server: %s", status->errormsg); + status_msg_free(status); + + return NULL; + } + ssh_set_error(file->sftp->session, SSH_FATAL, + "Received msg %d during fstat()", msg->packet_type); + sftp_message_free(msg); + + return NULL; +} + +#endif /* WITH_SFTP */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/sftpserver.c b/libssh/src/sftpserver.c new file mode 100644 index 00000000..3ae93a99 --- /dev/null +++ b/libssh/src/sftpserver.c @@ -0,0 +1,518 @@ +/* + * sftpserver.c - server based function for the sftp protocol + * + * This file is part of the SSH Library + * + * Copyright (c) 2005 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/libssh.h" +#include "libssh/sftp.h" +#include "libssh/ssh2.h" +#include "libssh/priv.h" +#include "libssh/buffer.h" +#include "libssh/misc.h" + +sftp_client_message sftp_get_client_message(sftp_session sftp) { + ssh_session session = sftp->session; + sftp_packet packet; + sftp_client_message msg; + ssh_buffer payload; + ssh_string tmp; + + msg = malloc(sizeof (struct sftp_client_message_struct)); + if (msg == NULL) { + ssh_set_error_oom(session); + return NULL; + } + ZERO_STRUCTP(msg); + + packet = sftp_packet_read(sftp); + if (packet == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + + payload = packet->payload; + msg->type = packet->type; + msg->sftp = sftp; + + buffer_get_u32(payload, &msg->id); + + switch(msg->type) { + case SSH_FXP_CLOSE: + case SSH_FXP_READDIR: + msg->handle = buffer_get_ssh_string(payload); + if (msg->handle == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_READ: + msg->handle = buffer_get_ssh_string(payload); + if (msg->handle == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + buffer_get_u64(payload, &msg->offset); + buffer_get_u32(payload, &msg->len); + break; + case SSH_FXP_WRITE: + msg->handle = buffer_get_ssh_string(payload); + if (msg->handle == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + buffer_get_u64(payload, &msg->offset); + msg->data = buffer_get_ssh_string(payload); + if (msg->data == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_REMOVE: + case SSH_FXP_RMDIR: + case SSH_FXP_OPENDIR: + case SSH_FXP_READLINK: + case SSH_FXP_REALPATH: + tmp = buffer_get_ssh_string(payload); + if (tmp == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->filename = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (msg->filename == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_RENAME: + case SSH_FXP_SYMLINK: + tmp = buffer_get_ssh_string(payload); + if (tmp == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->filename = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (msg->filename == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->data = buffer_get_ssh_string(payload); + if (msg->data == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_MKDIR: + case SSH_FXP_SETSTAT: + tmp = buffer_get_ssh_string(payload); + if (tmp == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->filename=ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (msg->filename == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->attr = sftp_parse_attr(sftp, payload, 0); + if (msg->attr == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_FSETSTAT: + msg->handle = buffer_get_ssh_string(payload); + if (msg->handle == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->attr = sftp_parse_attr(sftp, payload, 0); + if (msg->attr == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_LSTAT: + case SSH_FXP_STAT: + tmp = buffer_get_ssh_string(payload); + if (tmp == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->filename = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (msg->filename == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + if(sftp->version > 3) { + buffer_get_u32(payload,&msg->flags); + } + break; + case SSH_FXP_OPEN: + tmp=buffer_get_ssh_string(payload); + if (tmp == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + msg->filename = ssh_string_to_char(tmp); + ssh_string_free(tmp); + if (msg->filename == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + buffer_get_u32(payload,&msg->flags); + msg->attr = sftp_parse_attr(sftp, payload, 0); + if (msg->attr == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + break; + case SSH_FXP_FSTAT: + msg->handle = buffer_get_ssh_string(payload); + if (msg->handle == NULL) { + ssh_set_error_oom(session); + sftp_client_message_free(msg); + return NULL; + } + buffer_get_u32(payload, &msg->flags); + break; + default: + ssh_set_error(sftp->session, SSH_FATAL, + "Received unhandled sftp message %d\n", msg->type); + sftp_client_message_free(msg); + return NULL; + } + + msg->flags = ntohl(msg->flags); + msg->offset = ntohll(msg->offset); + msg->len = ntohl(msg->len); + sftp_packet_free(packet); + + return msg; +} + +void sftp_client_message_free(sftp_client_message msg) { + if (msg == NULL) { + return; + } + + SAFE_FREE(msg->filename); + ssh_string_free(msg->data); + ssh_string_free(msg->handle); + sftp_attributes_free(msg->attr); + + ZERO_STRUCTP(msg); + SAFE_FREE(msg); +} + +int sftp_reply_name(sftp_client_message msg, const char *name, + sftp_attributes attr) { + ssh_buffer out; + ssh_string file; + + out = ssh_buffer_new(); + if (out == NULL) { + return -1; + } + + file = ssh_string_from_char(name); + if (file == NULL) { + ssh_buffer_free(out); + return -1; + } + + if (buffer_add_u32(out, msg->id) < 0 || + buffer_add_u32(out, htonl(1)) < 0 || + buffer_add_ssh_string(out, file) < 0 || + buffer_add_ssh_string(out, file) < 0 || /* The protocol is broken here between 3 & 4 */ + buffer_add_attributes(out, attr) < 0 || + sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) { + ssh_buffer_free(out); + ssh_string_free(file); + return -1; + } + ssh_buffer_free(out); + ssh_string_free(file); + + return 0; +} + +int sftp_reply_handle(sftp_client_message msg, ssh_string handle){ + ssh_buffer out; + + out = ssh_buffer_new(); + if (out == NULL) { + return -1; + } + + if (buffer_add_u32(out, msg->id) < 0 || + buffer_add_ssh_string(out, handle) < 0 || + sftp_packet_write(msg->sftp, SSH_FXP_HANDLE, out) < 0) { + ssh_buffer_free(out); + return -1; + } + ssh_buffer_free(out); + + return 0; +} + +int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr) { + ssh_buffer out; + + out = ssh_buffer_new(); + if (out == NULL) { + return -1; + } + + if (buffer_add_u32(out, msg->id) < 0 || + buffer_add_attributes(out, attr) < 0 || + sftp_packet_write(msg->sftp, SSH_FXP_ATTRS, out) < 0) { + ssh_buffer_free(out); + return -1; + } + ssh_buffer_free(out); + + return 0; +} + +int sftp_reply_names_add(sftp_client_message msg, const char *file, + const char *longname, sftp_attributes attr) { + ssh_string name; + + name = ssh_string_from_char(file); + if (name == NULL) { + return -1; + } + + if (msg->attrbuf == NULL) { + msg->attrbuf = ssh_buffer_new(); + if (msg->attrbuf == NULL) { + ssh_string_free(name); + return -1; + } + } + + if (buffer_add_ssh_string(msg->attrbuf, name) < 0) { + ssh_string_free(name); + return -1; + } + + ssh_string_free(name); + name = ssh_string_from_char(longname); + if (name == NULL) { + return -1; + } + if (buffer_add_ssh_string(msg->attrbuf,name) < 0 || + buffer_add_attributes(msg->attrbuf,attr) < 0) { + ssh_string_free(name); + return -1; + } + ssh_string_free(name); + msg->attr_num++; + + return 0; +} + +int sftp_reply_names(sftp_client_message msg) { + ssh_buffer out; + + out = ssh_buffer_new(); + if (out == NULL) { + ssh_buffer_free(msg->attrbuf); + return -1; + } + + if (buffer_add_u32(out, msg->id) < 0 || + buffer_add_u32(out, htonl(msg->attr_num)) < 0 || + buffer_add_data(out, buffer_get_rest(msg->attrbuf), + buffer_get_rest_len(msg->attrbuf)) < 0 || + sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) { + ssh_buffer_free(out); + ssh_buffer_free(msg->attrbuf); + return -1; + } + + ssh_buffer_free(out); + ssh_buffer_free(msg->attrbuf); + + msg->attr_num = 0; + msg->attrbuf = NULL; + + return 0; +} + +int sftp_reply_status(sftp_client_message msg, uint32_t status, + const char *message) { + ssh_buffer out; + ssh_string s; + + out = ssh_buffer_new(); + if (out == NULL) { + return -1; + } + + s = ssh_string_from_char(message ? message : ""); + if (s == NULL) { + ssh_buffer_free(out); + return -1; + } + + if (buffer_add_u32(out, msg->id) < 0 || + buffer_add_u32(out, htonl(status)) < 0 || + buffer_add_ssh_string(out, s) < 0 || + buffer_add_u32(out, 0) < 0 || /* language string */ + sftp_packet_write(msg->sftp, SSH_FXP_STATUS, out) < 0) { + ssh_buffer_free(out); + ssh_string_free(s); + return -1; + } + + ssh_buffer_free(out); + ssh_string_free(s); + + return 0; +} + +int sftp_reply_data(sftp_client_message msg, const void *data, int len) { + ssh_buffer out; + + out = ssh_buffer_new(); + if (out == NULL) { + return -1; + } + + if (buffer_add_u32(out, msg->id) < 0 || + buffer_add_u32(out, ntohl(len)) < 0 || + buffer_add_data(out, data, len) < 0 || + sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) { + ssh_buffer_free(out); + return -1; + } + ssh_buffer_free(out); + + return 0; +} + +/* + * This function will return you a new handle to give the client. + * the function accepts an info that can be retrieved later with + * the handle. Care is given that a corrupted handle won't give a + * valid info (or worse). + */ +ssh_string sftp_handle_alloc(sftp_session sftp, void *info) { + ssh_string ret; + uint32_t val; + int i; + + if (sftp->handles == NULL) { + sftp->handles = malloc(sizeof(void *) * SFTP_HANDLES); + if (sftp->handles == NULL) { + return NULL; + } + memset(sftp->handles, 0, sizeof(void *) * SFTP_HANDLES); + } + + for (i = 0; i < SFTP_HANDLES; i++) { + if (sftp->handles[i] == NULL) { + break; + } + } + + if (i == SFTP_HANDLES) { + return NULL; /* no handle available */ + } + + val = i; + ret = ssh_string_new(4); + if (ret == NULL) { + return NULL; + } + + memcpy(ssh_string_data(ret), &val, sizeof(uint32_t)); + sftp->handles[i] = info; + + return ret; +} + +void *sftp_handle(sftp_session sftp, ssh_string handle){ + uint32_t val; + + if (sftp->handles == NULL) { + return NULL; + } + + if (ssh_string_len(handle) != sizeof(uint32_t)) { + return NULL; + } + + memcpy(&val, ssh_string_data(handle), sizeof(uint32_t)); + + if (val > SFTP_HANDLES) { + return NULL; + } + + return sftp->handles[val]; +} + +void sftp_handle_remove(sftp_session sftp, void *handle) { + int i; + + for (i = 0; i < SFTP_HANDLES; i++) { + if (sftp->handles[i] == handle) { + sftp->handles[i] = NULL; + break; + } + } +} + +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/socket.c b/libssh/src/socket.c new file mode 100644 index 00000000..85b87b77 --- /dev/null +++ b/libssh/src/socket.c @@ -0,0 +1,846 @@ +/* + * socket.c - socket functions for the library + * + * This file is part of the SSH Library + * + * Copyright (c) 2008-2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#if _MSC_VER >= 1400 +#include +#undef open +#define open _open +#undef close +#define close _close +#undef read +#define read _read +#undef write +#define write _write +#endif /* _MSC_VER */ +#else /* _WIN32 */ +#include +#include +#include +#include +#endif /* _WIN32 */ + +#include "libssh/priv.h" +#include "libssh/callbacks.h" +#include "libssh/socket.h" +#include "libssh/buffer.h" +#include "libssh/poll.h" +#include "libssh/session.h" + +/** + * @internal + * + * @defgroup libssh_socket The SSH socket functions. + * @ingroup libssh + * + * Functions for handling sockets. + * + * @{ + */ + +enum ssh_socket_states_e { + SSH_SOCKET_NONE, + SSH_SOCKET_CONNECTING, + SSH_SOCKET_CONNECTED, + SSH_SOCKET_EOF, + SSH_SOCKET_ERROR, + SSH_SOCKET_CLOSED +}; + +struct ssh_socket_struct { + socket_t fd_in; + socket_t fd_out; + int fd_is_socket; + int last_errno; + int read_wontblock; /* reading now on socket will + not block */ + int write_wontblock; + int data_except; + enum ssh_socket_states_e state; + ssh_buffer out_buffer; + ssh_buffer in_buffer; + ssh_session session; + ssh_socket_callbacks callbacks; + ssh_poll_handle poll_in; + ssh_poll_handle poll_out; +}; + +static int sockets_initialized = 0; + +static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len); +static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, + uint32_t len); + +/** + * \internal + * \brief inits the socket system (windows specific) + */ +int ssh_socket_init(void) { + if (sockets_initialized == 0) { +#ifdef _WIN32 + struct WSAData wsaData; + + /* Initiates use of the Winsock DLL by a process. */ + if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { + return -1; + } + +#endif + ssh_poll_init(); + + sockets_initialized = 1; + } + + return 0; +} + +/** + * @brief Cleanup the socket system. + */ +void ssh_socket_cleanup(void) { + if (sockets_initialized == 1) { + ssh_poll_cleanup(); +#ifdef _WIN32 + WSACleanup(); +#endif + sockets_initialized = 0; + } +} + + +/** + * \internal + * \brief creates a new Socket object + */ +ssh_socket ssh_socket_new(ssh_session session) { + ssh_socket s; + + s = malloc(sizeof(struct ssh_socket_struct)); + if (s == NULL) { + ssh_set_error_oom(session); + return NULL; + } + s->fd_in = SSH_INVALID_SOCKET; + s->fd_out= SSH_INVALID_SOCKET; + s->last_errno = -1; + s->fd_is_socket = 1; + s->session = session; + s->in_buffer = ssh_buffer_new(); + if (s->in_buffer == NULL) { + ssh_set_error_oom(session); + SAFE_FREE(s); + return NULL; + } + s->out_buffer=ssh_buffer_new(); + if (s->out_buffer == NULL) { + ssh_set_error_oom(session); + ssh_buffer_free(s->in_buffer); + SAFE_FREE(s); + return NULL; + } + s->read_wontblock = 0; + s->write_wontblock = 0; + s->data_except = 0; + s->poll_in=s->poll_out=NULL; + s->state=SSH_SOCKET_NONE; + return s; +} + +/** + * @internal + * @brief Reset the state of a socket so it looks brand-new + * @param[in] s socket to rest + */ +void ssh_socket_reset(ssh_socket s){ + s->fd_in = SSH_INVALID_SOCKET; + s->fd_out= SSH_INVALID_SOCKET; + s->last_errno = -1; + s->fd_is_socket = 1; + buffer_reinit(s->in_buffer); + buffer_reinit(s->out_buffer); + s->read_wontblock = 0; + s->write_wontblock = 0; + s->data_except = 0; + s->poll_in=s->poll_out=NULL; + s->state=SSH_SOCKET_NONE; +} + +/** + * @internal + * @brief the socket callbacks, i.e. callbacks to be called + * upon a socket event. + * @param s socket to set callbacks on. + * @param callbacks a ssh_socket_callback object reference. + */ + +void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){ + s->callbacks=callbacks; +} + +/** + * @brief SSH poll callback. This callback will be used when an event + * caught on the socket. + * + * @param p Poll object this callback belongs to. + * @param fd The raw socket. + * @param revents The current poll events on the socket. + * @param userdata Userdata to be passed to the callback function, + * in this case the socket object. + * + * @return 0 on success, < 0 when the poll object has been removed + * from its poll context. + */ +int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s){ + ssh_socket s=(ssh_socket )v_s; + char buffer[4096]; + int r; + int err=0; + socklen_t errlen=sizeof(err); + /* Do not do anything if this socket was already closed */ + if(!ssh_socket_is_open(s)){ + return -1; + } + if(revents & POLLERR || revents & POLLHUP){ + /* Check if we are in a connecting state */ + if(s->state==SSH_SOCKET_CONNECTING){ + s->state=SSH_SOCKET_ERROR; + getsockopt(fd,SOL_SOCKET,SO_ERROR,(char *)&err,&errlen); + s->last_errno=err; + ssh_socket_close(s); + if(s->callbacks && s->callbacks->connected) + s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,err, + s->callbacks->userdata); + return -1; + } + /* Then we are in a more standard kind of error */ + /* force a read to get an explanation */ + revents |= POLLIN; + } + if(revents & POLLIN){ + s->read_wontblock=1; + r=ssh_socket_unbuffered_read(s,buffer,sizeof(buffer)); + if(r<0){ + if(p != NULL) { + ssh_poll_remove_events(p, POLLIN); + } + if(s->callbacks && s->callbacks->exception){ + s->callbacks->exception( + SSH_SOCKET_EXCEPTION_ERROR, + s->last_errno,s->callbacks->userdata); + /* p may have been freed, so don't use it + * anymore in this function */ + p = NULL; + return -2; + } + } + if(r==0){ + if(p != NULL) { + ssh_poll_remove_events(p, POLLIN); + } + if(p != NULL) { + ssh_poll_remove_events(p, POLLIN); + } + if(s->callbacks && s->callbacks->exception){ + s->callbacks->exception( + SSH_SOCKET_EXCEPTION_EOF, + 0,s->callbacks->userdata); + /* p may have been freed, so don't use it + * anymore in this function */ + p = NULL; + return -2; + } + } + if(r>0){ + /* Bufferize the data and then call the callback */ + r = buffer_add_data(s->in_buffer,buffer,r); + if (r < 0) { + return -1; + } + if(s->callbacks && s->callbacks->data){ + do { + r= s->callbacks->data(buffer_get_rest(s->in_buffer), + buffer_get_rest_len(s->in_buffer), + s->callbacks->userdata); + buffer_pass_bytes(s->in_buffer,r); + } while (r > 0); + /* p may have been freed, so don't use it + * anymore in this function */ + p = NULL; + } + } + } +#ifdef _WIN32 + if(revents & POLLOUT || revents & POLLWRNORM){ +#else + if(revents & POLLOUT){ +#endif + /* First, POLLOUT is a sign we may be connected */ + if(s->state == SSH_SOCKET_CONNECTING){ + ssh_log(s->session,SSH_LOG_PACKET,"Received POLLOUT in connecting state"); + s->state = SSH_SOCKET_CONNECTED; + ssh_poll_set_events(p,POLLOUT | POLLIN); + ssh_socket_set_blocking(ssh_socket_get_fd_in(s)); + if(s->callbacks && s->callbacks->connected) + s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); + return 0; + } + /* So, we can write data */ + s->write_wontblock=1; + if(p != NULL) { + ssh_poll_remove_events(p, POLLOUT); + } + + /* If buffered data is pending, write it */ + if(buffer_get_rest_len(s->out_buffer) > 0){ + ssh_socket_nonblocking_flush(s); + } else if(s->callbacks && s->callbacks->controlflow){ + /* Otherwise advertise the upper level that write can be done */ + s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,s->callbacks->userdata); + } + /* TODO: Find a way to put back POLLOUT when buffering occurs */ + } + /* Return -1 if one of the poll handlers disappeared */ + return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0; +} + +/** @internal + * @brief returns the input poll handle corresponding to the socket, + * creates it if it does not exist. + * @returns allocated and initialized ssh_poll_handle object + */ +ssh_poll_handle ssh_socket_get_poll_handle_in(ssh_socket s){ + if(s->poll_in) + return s->poll_in; + s->poll_in=ssh_poll_new(s->fd_in,0,ssh_socket_pollcallback,s); + if(s->fd_in == s->fd_out && s->poll_out == NULL) + s->poll_out=s->poll_in; + return s->poll_in; +} + +/** @internal + * @brief returns the output poll handle corresponding to the socket, + * creates it if it does not exist. + * @returns allocated and initialized ssh_poll_handle object + */ +ssh_poll_handle ssh_socket_get_poll_handle_out(ssh_socket s){ + if(s->poll_out) + return s->poll_out; + s->poll_out=ssh_poll_new(s->fd_out,0,ssh_socket_pollcallback,s); + if(s->fd_in == s->fd_out && s->poll_in == NULL) + s->poll_in=s->poll_out; + return s->poll_out; +} + +/** \internal + * \brief Deletes a socket object + */ +void ssh_socket_free(ssh_socket s){ + if (s == NULL) { + return; + } + ssh_socket_close(s); + ssh_buffer_free(s->in_buffer); + ssh_buffer_free(s->out_buffer); + SAFE_FREE(s); +} + +#ifndef _WIN32 +int ssh_socket_unix(ssh_socket s, const char *path) { + struct sockaddr_un sunaddr; + socket_t fd; + sunaddr.sun_family = AF_UNIX; + snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == SSH_INVALID_SOCKET) { + ssh_set_error(s->session, SSH_FATAL, + "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s", + strerror(errno)); + return -1; + } + + if (fcntl(fd, F_SETFD, 1) == -1) { + ssh_set_error(s->session, SSH_FATAL, + "Error from fcntl(fd, F_SETFD, 1): %s", + strerror(errno)); + close(fd); + return -1; + } + + if (connect(fd, (struct sockaddr *) &sunaddr, + sizeof(sunaddr)) < 0) { + ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s", + strerror(errno)); + close(fd); + return -1; + } + ssh_socket_set_fd(s,fd); + return 0; +} +#endif + +/** \internal + * \brief closes a socket + */ +void ssh_socket_close(ssh_socket s){ + if (ssh_socket_is_open(s)) { +#ifdef _WIN32 + closesocket(s->fd_in); + /* fd_in = fd_out under win32 */ + s->last_errno = WSAGetLastError(); +#else + close(s->fd_in); + if(s->fd_out != s->fd_in && s->fd_out != -1) + close(s->fd_out); + s->last_errno = errno; +#endif + s->fd_in = s->fd_out = SSH_INVALID_SOCKET; + } + if(s->poll_in != NULL){ + if(s->poll_out == s->poll_in) + s->poll_out = NULL; + ssh_poll_free(s->poll_in); + s->poll_in=NULL; + } + if(s->poll_out != NULL){ + ssh_poll_free(s->poll_out); + s->poll_out=NULL; + } +} + +/** + * @internal + * @brief sets the file descriptor of the socket. + * @param[out] s ssh_socket to update + * @param[in] fd file descriptor to set + * @warning this function updates boths the input and output + * file descriptors + */ +void ssh_socket_set_fd(ssh_socket s, socket_t fd) { + s->fd_in = s->fd_out = fd; + if(s->poll_in) + ssh_poll_set_fd(s->poll_in,fd); +} + +/** + * @internal + * @brief sets the input file descriptor of the socket. + * @param[out] s ssh_socket to update + * @param[in] fd file descriptor to set + */ +void ssh_socket_set_fd_in(ssh_socket s, socket_t fd) { + s->fd_in = fd; + if(s->poll_in) + ssh_poll_set_fd(s->poll_in,fd); +} + +/** + * @internal + * @brief sets the output file descriptor of the socket. + * @param[out] s ssh_socket to update + * @param[in] fd file descriptor to set + */ +void ssh_socket_set_fd_out(ssh_socket s, socket_t fd) { + s->fd_out = fd; + if(s->poll_out) + ssh_poll_set_fd(s->poll_out,fd); +} + + + +/** \internal + * \brief returns the input file descriptor of the socket + */ +socket_t ssh_socket_get_fd_in(ssh_socket s) { + return s->fd_in; +} + +/** \internal + * \brief returns nonzero if the socket is open + */ +int ssh_socket_is_open(ssh_socket s) { + return s->fd_in != SSH_INVALID_SOCKET; +} + +/** \internal + * \brief read len bytes from socket into buffer + */ +static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len) { + int rc = -1; + + if (s->data_except) { + return -1; + } + if(s->fd_is_socket) + rc = recv(s->fd_in,buffer, len, 0); + else + rc = read(s->fd_in,buffer, len); +#ifdef _WIN32 + s->last_errno = WSAGetLastError(); +#else + s->last_errno = errno; +#endif + s->read_wontblock = 0; + + if (rc < 0) { + s->data_except = 1; + } + + return rc; +} + +/** \internal + * \brief writes len bytes from buffer to socket + */ +static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, + uint32_t len) { + int w = -1; + + if (s->data_except) { + return -1; + } + if (s->fd_is_socket) + w = send(s->fd_out,buffer, len, 0); + else + w = write(s->fd_out, buffer, len); +#ifdef _WIN32 + s->last_errno = WSAGetLastError(); +#else + s->last_errno = errno; +#endif + s->write_wontblock = 0; + /* Reactive the POLLOUT detector in the poll multiplexer system */ + if(s->poll_out){ + ssh_log(s->session, SSH_LOG_PACKET, "Enabling POLLOUT for socket"); + ssh_poll_set_events(s->poll_out,ssh_poll_get_events(s->poll_out) | POLLOUT); + } + if (w < 0) { + s->data_except = 1; + } + + return w; +} + +/** \internal + * \brief returns nonzero if the current socket is in the fd_set + */ +int ssh_socket_fd_isset(ssh_socket s, fd_set *set) { + if(s->fd_in == SSH_INVALID_SOCKET) { + return 0; + } + return FD_ISSET(s->fd_in,set) || FD_ISSET(s->fd_out,set); +} + +/** \internal + * \brief sets the current fd in a fd_set and updates the max_fd + */ + +void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) { + if (s->fd_in == SSH_INVALID_SOCKET) { + return; + } + + FD_SET(s->fd_in,set); + FD_SET(s->fd_out,set); + + if (s->fd_in >= 0 && + s->fd_in >= *max_fd && + s->fd_in != SSH_INVALID_SOCKET) { + *max_fd = s->fd_in + 1; + } + if (s->fd_out >= 0 && + s->fd_out >= *max_fd && + s->fd_out != SSH_INVALID_SOCKET) { + *max_fd = s->fd_out + 1; + } +} + +/** \internal + * \brief buffered write of data + * \returns SSH_OK, or SSH_ERROR + * \warning has no effect on socket before a flush + */ +int ssh_socket_write(ssh_socket s, const void *buffer, int len) { + ssh_session session = s->session; + enter_function(); + if(len > 0) { + if (buffer_add_data(s->out_buffer, buffer, len) < 0) { + ssh_set_error_oom(s->session); + return SSH_ERROR; + } + ssh_socket_nonblocking_flush(s); + } + leave_function(); + return SSH_OK; +} + + +/** \internal + * \brief starts a nonblocking flush of the output buffer + * + */ +int ssh_socket_nonblocking_flush(ssh_socket s) { + ssh_session session = s->session; + uint32_t len; + int w; + + enter_function(); + + if (!ssh_socket_is_open(s)) { + session->alive = 0; + /* FIXME use ssh_socket_get_errno */ + ssh_set_error(session, SSH_FATAL, + "Writing packet: error on socket (or connection closed): %s", + strerror(s->last_errno)); + + leave_function(); + return SSH_ERROR; + } + + len = buffer_get_rest_len(s->out_buffer); + if (!s->write_wontblock && s->poll_out && len > 0) { + /* force the poll system to catch pollout events */ + ssh_poll_add_events(s->poll_out, POLLOUT); + leave_function(); + return SSH_AGAIN; + } + if (s->write_wontblock && len > 0) { + w = ssh_socket_unbuffered_write(s, buffer_get_rest(s->out_buffer), len); + if (w < 0) { + session->alive = 0; + ssh_socket_close(s); + /* FIXME use ssh_socket_get_errno() */ + /* FIXME use callback for errors */ + ssh_set_error(session, SSH_FATAL, + "Writing packet: error on socket (or connection closed): %s", + strerror(s->last_errno)); + leave_function(); + return SSH_ERROR; + } + buffer_pass_bytes(s->out_buffer, w); + } + + /* Is there some data pending? */ + len = buffer_get_rest_len(s->out_buffer); + if (s->poll_out && len > 0) { + /* force the poll system to catch pollout events */ + ssh_poll_add_events(s->poll_out, POLLOUT); + leave_function(); + return SSH_AGAIN; + } + + /* all data written */ + leave_function(); + return SSH_OK; +} + +void ssh_socket_set_write_wontblock(ssh_socket s) { + s->write_wontblock = 1; +} + +void ssh_socket_set_read_wontblock(ssh_socket s) { + s->read_wontblock = 1; +} + +void ssh_socket_set_except(ssh_socket s) { + s->data_except = 1; +} + +int ssh_socket_data_available(ssh_socket s) { + return s->read_wontblock; +} + +int ssh_socket_data_writable(ssh_socket s) { + return s->write_wontblock; +} + +/** @internal + * @brief returns the number of outgoing bytes currently buffered + * @param s the socket + * @returns numbers of bytes buffered, or 0 if the socket isn't connected + */ +int ssh_socket_buffered_write_bytes(ssh_socket s){ + if(s==NULL || s->out_buffer == NULL) + return 0; + return buffer_get_rest_len(s->out_buffer); +} + + +int ssh_socket_get_status(ssh_socket s) { + int r = 0; + + if (s->read_wontblock) { + r |= SSH_READ_PENDING; + } + + if (s->write_wontblock) { + r |= SSH_WRITE_PENDING; + } + + if (s->data_except) { + r |= SSH_CLOSED_ERROR; + } + + return r; +} + +#ifdef _WIN32 +void ssh_socket_set_nonblocking(socket_t fd) { + u_long nonblocking = 1; + ioctlsocket(fd, FIONBIO, &nonblocking); +} + +void ssh_socket_set_blocking(socket_t fd) { + u_long nonblocking = 0; + ioctlsocket(fd, FIONBIO, &nonblocking); +} + +#else /* _WIN32 */ +void ssh_socket_set_nonblocking(socket_t fd) { + fcntl(fd, F_SETFL, O_NONBLOCK); +} + +void ssh_socket_set_blocking(socket_t fd) { + fcntl(fd, F_SETFL, 0); +} +#endif /* _WIN32 */ + +/** + * @internal + * @brief Launches a socket connection + * If a the socket connected callback has been defined and + * a poll object exists, this call will be non blocking. + * @param s socket to connect. + * @param host hostname or ip address to connect to. + * @param port port number to connect to. + * @param bind_addr address to bind to, or NULL for default. + * @returns SSH_OK socket is being connected. + * @returns SSH_ERROR error while connecting to remote host. + * @bug It only tries connecting to one of the available AI's + * which is problematic for hosts having DNS fail-over. + */ + +int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr){ + socket_t fd; + ssh_session session=s->session; + enter_function(); + if(s->state != SSH_SOCKET_NONE) { + ssh_set_error(s->session, SSH_FATAL, + "ssh_socket_connect called on socket not unconnected"); + return SSH_ERROR; + } + fd=ssh_connect_host_nonblocking(s->session,host,bind_addr,port); + ssh_log(session,SSH_LOG_PROTOCOL,"Nonblocking connection socket: %d",fd); + if(fd == SSH_INVALID_SOCKET) + return SSH_ERROR; + ssh_socket_set_fd(s,fd); + s->state=SSH_SOCKET_CONNECTING; + /* POLLOUT is the event to wait for in a nonblocking connect */ + ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLOUT); +#ifdef _WIN32 + ssh_poll_add_events(ssh_socket_get_poll_handle_in(s),POLLWRNORM); +#endif + leave_function(); + return SSH_OK; +} + +#ifndef _WIN32 +/** + * @internal + * @brief executes a command and redirect input and outputs + * @param command command to execute + * @param in input file descriptor + * @param out output file descriptor + */ +void ssh_execute_command(const char *command, socket_t in, socket_t out){ + const char *args[]={"/bin/sh","-c",command,NULL}; + /* redirect in and out to stdin, stdout and stderr */ + dup2(in, 0); + dup2(out,1); + dup2(out,2); + close(in); + close(out); + execv(args[0],(char * const *)args); + exit(1); +} + +/** + * @internal + * @brief Open a socket on a ProxyCommand + * This call will always be nonblocking. + * @param s socket to connect. + * @param command Command to execute. + * @returns SSH_OK socket is being connected. + * @returns SSH_ERROR error while executing the command. + */ + +int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){ + socket_t in_pipe[2]; + socket_t out_pipe[2]; + int pid; + int rc; + ssh_session session=s->session; + enter_function(); + if(s->state != SSH_SOCKET_NONE) + return SSH_ERROR; + + rc = pipe(in_pipe); + if (rc < 0) { + return SSH_ERROR; + } + rc = pipe(out_pipe); + if (rc < 0) { + return SSH_ERROR; + } + + ssh_log(session,SSH_LOG_PROTOCOL,"Executing proxycommand '%s'",command); + pid = fork(); + if(pid == 0){ + ssh_execute_command(command,out_pipe[0],in_pipe[1]); + } + close(in_pipe[1]); + close(out_pipe[0]); + ssh_log(session,SSH_LOG_PROTOCOL,"ProxyCommand connection pipe: [%d,%d]",in_pipe[0],out_pipe[1]); + ssh_socket_set_fd_in(s,in_pipe[0]); + ssh_socket_set_fd_out(s,out_pipe[1]); + s->state=SSH_SOCKET_CONNECTED; + s->fd_is_socket=0; + /* POLLOUT is the event to wait for in a nonblocking connect */ + ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLIN); + ssh_poll_set_events(ssh_socket_get_poll_handle_out(s),POLLOUT); + if(s->callbacks && s->callbacks->connected) + s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); + leave_function(); + return SSH_OK; +} + +#endif /* _WIN32 */ +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/string.c b/libssh/src/string.c new file mode 100644 index 00000000..5ef90b0e --- /dev/null +++ b/libssh/src/string.c @@ -0,0 +1,270 @@ +/* + * string.c - ssh string functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#include "libssh/priv.h" +#include "libssh/string.h" + +/** + * @defgroup libssh_string The SSH string functions + * @ingroup libssh + * + * @brief String manipulations used in libssh. + * + * @{ + */ + +/** + * @brief Create a new SSH String object. + * + * @param[in] size The size of the string. + * + * @return The newly allocated string, NULL on error. + */ +struct ssh_string_struct *ssh_string_new(size_t size) { + struct ssh_string_struct *str = NULL; + + if (size > UINT_MAX - sizeof(struct ssh_string_struct)) { + return NULL; + } + + str = malloc(sizeof(struct ssh_string_struct) + size); + if (str == NULL) { + return NULL; + } + + str->size = htonl(size); + str->data[0] = 0; + + return str; +} + +/** + * @brief Fill a string with given data. The string should be big enough. + * + * @param s An allocated string to fill with data. + * + * @param data The data to fill the string with. + * + * @param len Size of data. + * + * @return 0 on success, < 0 on error. + */ +int ssh_string_fill(struct ssh_string_struct *s, const void *data, size_t len) { + if ((s == NULL) || (data == NULL) || + (len == 0) || (len > ssh_string_len(s))) { + return -1; + } + + memcpy(s->data, data, len); + + return 0; +} + +/** + * @brief Create a ssh string using a C string + * + * @param[in] what The source 0-terminated C string. + * + * @return The newly allocated string, NULL on error with errno + * set. + * + * @note The nul byte is not copied nor counted in the ouput string. + */ +struct ssh_string_struct *ssh_string_from_char(const char *what) { + struct ssh_string_struct *ptr; + size_t len; + + if(what == NULL) { + errno = EINVAL; + return NULL; + } + + len = strlen(what); + + ptr = ssh_string_new(len); + if (ptr == NULL) { + return NULL; + } + + memcpy(ptr->data, what, len); + + return ptr; +} + +/** + * @brief Return the size of a SSH string. + * + * @param[in] s The the input SSH string. + * + * @return The size of the content of the string, 0 on error. + */ +size_t ssh_string_len(struct ssh_string_struct *s) { + if (s == NULL) { + return ntohl(0); + } + + return ntohl(s->size); +} + +/** + * @brief Get the the string as a C nul-terminated string. + * + * This is only available as long as the SSH string exists. + * + * @param[in] s The SSH string to get the C string from. + * + * @return The char pointer, NULL on error. + */ +const char *ssh_string_get_char(struct ssh_string_struct *s) +{ + if (s == NULL) { + return NULL; + } + s->data[ssh_string_len(s)] = '\0'; + + return (const char *) s->data; +} + +/** + * @brief Convert a SSH string to a C nul-terminated string. + * + * @param[in] s The SSH input string. + * + * @return An allocated string pointer, NULL on error with errno + * set. + * + * @note If the input SSH string contains zeroes, some parts of the output + * string may not be readable with regular libc functions. + */ +char *ssh_string_to_char(struct ssh_string_struct *s) { + size_t len; + char *new; + + if (s == NULL) { + return NULL; + } + + len = ssh_string_len(s); + if (len + 1 < len) { + return NULL; + } + + new = malloc(len + 1); + if (new == NULL) { + return NULL; + } + memcpy(new, s->data, len); + new[len] = '\0'; + + return new; +} + +/** + * @brief Deallocate a char string object. + * + * @param[in] s The string to delete. + */ +void ssh_string_free_char(char *s) { + SAFE_FREE(s); +} + +/** + * @brief Copy a string, return a newly allocated string. The caller has to + * free the string. + * + * @param[in] s String to copy. + * + * @return Newly allocated copy of the string, NULL on error. + */ +struct ssh_string_struct *ssh_string_copy(struct ssh_string_struct *s) { + struct ssh_string_struct *new; + size_t len; + + if (s == NULL) { + return NULL; + } + + len = ssh_string_len(s); + if (len == 0) { + return NULL; + } + + new = ssh_string_new(len); + if (new == NULL) { + return NULL; + } + + memcpy(new->data, s->data, len); + + return new; +} + +/** + * @brief Destroy the data in a string so it couldn't appear in a core dump. + * + * @param[in] s The string to burn. + */ +void ssh_string_burn(struct ssh_string_struct *s) { + if (s == NULL) { + return; + } + memset(s->data, 'X', ssh_string_len(s)); +} + +/** + * @brief Get the payload of the string. + * + * @param s The string to get the data from. + * + * @return Return the data of the string or NULL on error. + */ +void *ssh_string_data(struct ssh_string_struct *s) { + if (s == NULL) { + return NULL; + } + + return s->data; +} + +/** + * @brief Deallocate a SSH string object. + * + * \param[in] s The SSH string to delete. + */ +void ssh_string_free(struct ssh_string_struct *s) { + SAFE_FREE(s); +} + +/** @} */ + +/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/threads.c b/libssh/src/threads.c new file mode 100644 index 00000000..107c65d2 --- /dev/null +++ b/libssh/src/threads.c @@ -0,0 +1,176 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/** + * @defgroup libssh_threads The SSH threading functions. + * @ingroup libssh + * + * Threading with libssh + * @{ + */ + +#include "config.h" + +#include "libssh/priv.h" +#include "libssh/crypto.h" +#include "libssh/threads.h" + +static int threads_noop (void **lock){ + (void)lock; + return 0; +} + +static unsigned long threads_id_noop (void){ + return 1; +} + +static struct ssh_threads_callbacks_struct ssh_threads_noop = +{ + "threads_noop", + threads_noop, + threads_noop, + threads_noop, + threads_noop, + threads_id_noop +}; + +struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void) { + return &ssh_threads_noop; +} + +static struct ssh_threads_callbacks_struct *user_callbacks =&ssh_threads_noop; + +#ifdef HAVE_LIBGCRYPT + +/* Libgcrypt specific way of handling thread callbacks */ + +static struct gcry_thread_cbs gcrypt_threads_callbacks; + +static int libgcrypt_thread_init(void){ + if(user_callbacks == NULL) + return SSH_ERROR; + if(user_callbacks == &ssh_threads_noop){ + gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || GCRY_THREAD_OPTION_DEFAULT; + } else { + gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || GCRY_THREAD_OPTION_USER; + } + gcrypt_threads_callbacks.mutex_init=user_callbacks->mutex_init; + gcrypt_threads_callbacks.mutex_destroy=user_callbacks->mutex_destroy; + gcrypt_threads_callbacks.mutex_lock=user_callbacks->mutex_lock; + gcrypt_threads_callbacks.mutex_unlock=user_callbacks->mutex_unlock; + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcrypt_threads_callbacks); + return SSH_OK; +} +#else + +/* Libcrypto specific stuff */ + +static void **libcrypto_mutexes; + +static void libcrypto_lock_callback(int mode, int i, const char *file, int line){ + (void)file; + (void)line; + if(mode & CRYPTO_LOCK){ + user_callbacks->mutex_lock(&libcrypto_mutexes[i]); + } else { + user_callbacks->mutex_unlock(&libcrypto_mutexes[i]); + } +} + +static int libcrypto_thread_init(void){ + int n=CRYPTO_num_locks(); + int i; + if(user_callbacks == &ssh_threads_noop) + return SSH_OK; + libcrypto_mutexes=malloc(sizeof(void *) * n); + if (libcrypto_mutexes == NULL) + return SSH_ERROR; + for (i=0;imutex_init(&libcrypto_mutexes[i]); + } + CRYPTO_set_id_callback(user_callbacks->thread_id); + CRYPTO_set_locking_callback(libcrypto_lock_callback); + + return SSH_OK; +} + +static void libcrypto_thread_finalize(void){ + int n=CRYPTO_num_locks(); + int i; + if (libcrypto_mutexes==NULL) + return; + for (i=0;imutex_destroy(&libcrypto_mutexes[i]); + } + SAFE_FREE(libcrypto_mutexes); + +} + +#endif + +/** @internal + * @brief inits the threading with the backend cryptographic libraries + */ + +int ssh_threads_init(void){ + static int threads_initialized=0; + int ret; + if(threads_initialized) + return SSH_OK; + /* first initialize the user_callbacks with our default handlers if not + * already the case + */ + if(user_callbacks == NULL){ + user_callbacks=&ssh_threads_noop; + } + + /* Then initialize the crypto libraries threading callbacks */ +#ifdef HAVE_LIBGCRYPT + ret = libgcrypt_thread_init(); +#else /* Libcrypto */ + ret = libcrypto_thread_init(); +#endif + if(ret == SSH_OK) + threads_initialized=1; + return ret; +} + +void ssh_threads_finalize(void){ +#ifdef HAVE_LIBGCRYPT +#else + libcrypto_thread_finalize(); +#endif +} + +int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb){ + user_callbacks=cb; + return SSH_OK; +} + +const char *ssh_threads_get_type(void) { + if(user_callbacks != NULL) + return user_callbacks->type; + return NULL; +} + +/** + * @} + */ diff --git a/libssh/src/threads/CMakeLists.txt b/libssh/src/threads/CMakeLists.txt new file mode 100644 index 00000000..b95525e4 --- /dev/null +++ b/libssh/src/threads/CMakeLists.txt @@ -0,0 +1,125 @@ +project(libssh-threads C) + +set(LIBSSH_THREADS_PUBLIC_INCLUDE_DIRS + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR} + CACHE INTERNAL "libssh public include directories" +) + +set(LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS + ${CMAKE_BINARY_DIR} +) + +set(LIBSSH_THREADS_SHARED_LIBRARY + ssh_threads_shared + CACHE INTERNAL "libssh threads shared library" +) + +if (WITH_STATIC_LIB) + set(LIBSSH_THREADS_STATIC_LIBRARY + ssh_threads_static + CACHE INTERNAL "libssh threads static library" + ) +endif (WITH_STATIC_LIB) + +set(LIBSSH_THREADS_LINK_LIBRARIES + ${LIBSSH_SHARED_LIBRARY} +) + +set(libssh_threads_SRCS +) + +# build and link pthread +if (CMAKE_USE_PTHREADS_INIT) + set(libssh_threads_SRCS + ${libssh_threads_SRCS} + pthread.c + ) + + set(LIBSSH_THREADS_LINK_LIBRARIES + ${LIBSSH_THREADS_LINK_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ) +endif (CMAKE_USE_PTHREADS_INIT) + +set(LIBSSH_THREADS_LINK_LIBRARIES + ${LIBSSH_THREADS_LINK_LIBRARIES} + CACHE INTERNAL "libssh threads link libraries" +) + +include_directories( + ${LIBSSH_THREADS_PUBLIC_INCLUDE_DIRS} + ${LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS} +) + +add_library(${LIBSSH_THREADS_SHARED_LIBRARY} SHARED ${libssh_threads_SRCS}) + +target_link_libraries(${LIBSSH_THREADS_SHARED_LIBRARY} ${LIBSSH_THREADS_LINK_LIBRARIES}) + +set_target_properties( + ${LIBSSH_THREADS_SHARED_LIBRARY} + PROPERTIES + VERSION + ${LIBRARY_VERSION} + SOVERSION + ${LIBRARY_SOVERSION} + OUTPUT_NAME + ssh_threads + DEFINE_SYMBOL + LIBSSH_EXPORTS +) + +if (WITH_VISIBILITY_HIDDEN) + set_target_properties(${LIBSSH_THREADS_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") +endif (WITH_VISIBILITY_HIDDEN) + +install( + TARGETS + ${LIBSSH_THREADS_SHARED_LIBRARY} + RUNTIME DESTINATION ${BIN_INSTALL_DIR} + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR} + COMPONENT libraries +) + +if (WITH_STATIC_LIB) + add_library(${LIBSSH_THREADS_STATIC_LIBRARY} STATIC ${libssh_threads_SRCS}) + + if (MSVC) + set(OUTPUT_SUFFIX static) + else (MSVC) + set(OUTPUT_SUFFIX ) + endif (MSVC) + + set_target_properties( + ${LIBSSH_THREADS_STATIC_LIBRARY} + PROPERTIES + VERSION + ${LIBRARY_VERSION} + SOVERSION + ${LIBRARY_SOVERSION} + OUTPUT_NAME + ssh_threads + ARCHIVE_OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX} + ) + + if (WIN32) + set_target_properties( + ${LIBSSH_THREADS_STATIC_LIBRARY} + PROPERTIES + COMPILE_FLAGS + "-DLIBSSH_STATIC" + ) + endif (WIN32) + + install( + TARGETS + ${LIBSSH_THREADS_STATIC_LIBRARY} + DESTINATION + ${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX} + COMPONENT + libraries + ) +endif (WITH_STATIC_LIB) diff --git a/libssh/src/threads/pthread.c b/libssh/src/threads/pthread.c new file mode 100644 index 00000000..829fa5c6 --- /dev/null +++ b/libssh/src/threads/pthread.c @@ -0,0 +1,99 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include + +#ifdef HAVE_PTHREAD + +#include +#include +#include + +/** @brief Defines the needed callbacks for pthread. Use this if your + * OS supports libpthread and want to use it for threading. + * @code + * #include + * #include + * #include + * SSH_THREADS_PTHREAD(ssh_pthread_callbacks); + * int main(){ + * ssh_init_set_threads_callbacks(&ssh_pthread_callbacks); + * ssh_init(); + * ... + * } + * @endcode + * @param name name of the structure to be declared, containing the + * callbacks for threading + * + */ + +static int ssh_pthread_mutex_init (void **priv){ + int err = 0; + *priv = malloc (sizeof (pthread_mutex_t)); + if (*priv==NULL) + return ENOMEM; + err = pthread_mutex_init (*priv, NULL); + if (err != 0){ + free (*priv); + *priv=NULL; + } + return err; +} + +static int ssh_pthread_mutex_destroy (void **lock) { + int err = pthread_mutex_destroy (*lock); + free (*lock); + *lock=NULL; + return err; +} + +static int ssh_pthread_mutex_lock (void **lock) { + return pthread_mutex_lock (*lock); +} + +static int ssh_pthread_mutex_unlock (void **lock){ + return pthread_mutex_unlock (*lock); +} + +static unsigned long ssh_pthread_thread_id (void){ +#if _WIN32 + return (unsigned long) pthread_self().p; +#else + return (unsigned long) pthread_self(); +#endif +} + +static struct ssh_threads_callbacks_struct ssh_threads_pthread = +{ + .type="threads_pthread", + .mutex_init=ssh_pthread_mutex_init, + .mutex_destroy=ssh_pthread_mutex_destroy, + .mutex_lock=ssh_pthread_mutex_lock, + .mutex_unlock=ssh_pthread_mutex_unlock, + .thread_id=ssh_pthread_thread_id +}; + +struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void) { + return &ssh_threads_pthread; +} + +#endif /* HAVE_PTHREAD */ diff --git a/libssh/src/wrapper.c b/libssh/src/wrapper.c new file mode 100644 index 00000000..b8a489d4 --- /dev/null +++ b/libssh/src/wrapper.c @@ -0,0 +1,345 @@ +/* + * wrapper.c - wrapper for crytpo functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2003 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* + * Why a wrapper? + * + * Let's say you want to port libssh from libcrypto of openssl to libfoo + * you are going to spend hours to remove every references to SHA1_Update() + * to libfoo_sha1_update after the work is finished, you're going to have + * only this file to modify it's not needed to say that your modifications + * are welcome. + */ + +#include "config.h" + + +#include +#include +#include + +#ifdef WITH_ZLIB +#include +#endif + +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/crypto.h" +#include "libssh/wrapper.h" +#include "libssh/pki.h" + +/* it allocates a new cipher structure based on its offset into the global table */ +static struct ssh_cipher_struct *cipher_new(int offset) { + struct ssh_cipher_struct *cipher = NULL; + + cipher = malloc(sizeof(struct ssh_cipher_struct)); + if (cipher == NULL) { + return NULL; + } + + /* note the memcpy will copy the pointers : so, you shouldn't free them */ + memcpy(cipher, &ssh_get_ciphertab()[offset], sizeof(*cipher)); + + return cipher; +} + +static void cipher_free(struct ssh_cipher_struct *cipher) { +#ifdef HAVE_LIBGCRYPT + unsigned int i; +#endif + + if (cipher == NULL) { + return; + } + + if(cipher->key) { +#ifdef HAVE_LIBGCRYPT + for (i = 0; i < (cipher->keylen / sizeof(gcry_cipher_hd_t)); i++) { + gcry_cipher_close(cipher->key[i]); + } +#elif defined HAVE_LIBCRYPTO + /* destroy the key */ + memset(cipher->key, 0, cipher->keylen); +#endif + SAFE_FREE(cipher->key); + } + SAFE_FREE(cipher); +} + +struct ssh_crypto_struct *crypto_new(void) { + struct ssh_crypto_struct *crypto; + + crypto = malloc(sizeof(struct ssh_crypto_struct)); + if (crypto == NULL) { + return NULL; + } + ZERO_STRUCTP(crypto); + return crypto; +} + +void crypto_free(struct ssh_crypto_struct *crypto){ + int i; + if (crypto == NULL) { + return; + } + + SAFE_FREE(crypto->server_pubkey); + + cipher_free(crypto->in_cipher); + cipher_free(crypto->out_cipher); + + bignum_free(crypto->e); + bignum_free(crypto->f); + bignum_free(crypto->x); + bignum_free(crypto->y); + bignum_free(crypto->k); +#ifdef HAVE_ECDH + SAFE_FREE(crypto->ecdh_client_pubkey); + SAFE_FREE(crypto->ecdh_server_pubkey); +#endif + if(crypto->session_id != NULL){ + memset(crypto->session_id, '\0', crypto->digest_len); + SAFE_FREE(crypto->session_id); + } + if(crypto->secret_hash != NULL){ + memset(crypto->secret_hash, '\0', crypto->digest_len); + SAFE_FREE(crypto->secret_hash); + } +#ifdef WITH_ZLIB + if (crypto->compress_out_ctx && + (deflateEnd(crypto->compress_out_ctx) != 0)) { + inflateEnd(crypto->compress_out_ctx); + } + if (crypto->compress_in_ctx && + (deflateEnd(crypto->compress_in_ctx) != 0)) { + inflateEnd(crypto->compress_in_ctx); + } +#endif /* WITH_ZLIB */ + if(crypto->encryptIV) + SAFE_FREE(crypto->encryptIV); + if(crypto->decryptIV) + SAFE_FREE(crypto->decryptIV); + if(crypto->encryptMAC) + SAFE_FREE(crypto->encryptMAC); + if(crypto->decryptMAC) + SAFE_FREE(crypto->decryptMAC); + if(crypto->encryptkey){ + memset(crypto->encryptkey, 0, crypto->digest_len); + SAFE_FREE(crypto->encryptkey); + } + if(crypto->decryptkey){ + memset(crypto->decryptkey, 0, crypto->digest_len); + SAFE_FREE(crypto->decryptkey); + } + + for (i = 0; i < SSH_KEX_METHODS; i++) { + SAFE_FREE(crypto->client_kex.methods[i]); + SAFE_FREE(crypto->server_kex.methods[i]); + SAFE_FREE(crypto->kex_methods[i]); + } + + memset(crypto,0,sizeof(*crypto)); + + SAFE_FREE(crypto); +} + +static int crypt_set_algorithms2(ssh_session session){ + const char *wanted; + int i = 0; + int rc = SSH_ERROR; + struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); + + enter_function(); + /* we must scan the kex entries to find crypto algorithms and set their appropriate structure */ + /* out */ + wanted = session->next_crypto->kex_methods[SSH_CRYPT_C_S]; + while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) { + i++; + } + + if (ssh_ciphertab[i].name == NULL) { + ssh_set_error(session, SSH_FATAL, + "crypt_set_algorithms2: no crypto algorithm function found for %s", + wanted); + goto error; + } + ssh_log(session, SSH_LOG_PACKET, "Set output algorithm to %s", wanted); + + session->next_crypto->out_cipher = cipher_new(i); + if (session->next_crypto->out_cipher == NULL) { + ssh_set_error_oom(session); + goto error; + } + i = 0; + + /* in */ + wanted = session->next_crypto->kex_methods[SSH_CRYPT_S_C]; + while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) { + i++; + } + + if (ssh_ciphertab[i].name == NULL) { + ssh_set_error(session, SSH_FATAL, + "Crypt_set_algorithms: no crypto algorithm function found for %s", + wanted); + goto error; + } + ssh_log(session, SSH_LOG_PACKET, "Set input algorithm to %s", wanted); + + session->next_crypto->in_cipher = cipher_new(i); + if (session->next_crypto->in_cipher == NULL) { + ssh_set_error_oom(session); + goto error; + } + + /* compression */ + if (strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib") == 0) { + session->next_crypto->do_compress_out = 1; + } + if (strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib") == 0) { + session->next_crypto->do_compress_in = 1; + } + if (strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib@openssh.com") == 0) { + session->next_crypto->delayed_compress_out = 1; + } + if (strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib@openssh.com") == 0) { + session->next_crypto->delayed_compress_in = 1; + } + rc = SSH_OK; +error: + leave_function(); + return rc; +} + +static int crypt_set_algorithms1(ssh_session session, enum ssh_des_e des_type) { + int i = 0; + struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); + + /* right now, we force 3des-cbc to be taken */ + while (ssh_ciphertab[i].name && strcmp(ssh_ciphertab[i].name, + des_type == SSH_DES ? "des-cbc-ssh1" : "3des-cbc-ssh1")) { + i++; + } + + if (ssh_ciphertab[i].name == NULL) { + ssh_set_error(session, SSH_FATAL, "cipher 3des-cbc-ssh1 or des-cbc-ssh1 not found!"); + return SSH_ERROR; + } + + session->next_crypto->out_cipher = cipher_new(i); + if (session->next_crypto->out_cipher == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + session->next_crypto->in_cipher = cipher_new(i); + if (session->next_crypto->in_cipher == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + return SSH_OK; +} + +int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type) { + return (session->version == 1) ? crypt_set_algorithms1(session, des_type) : + crypt_set_algorithms2(session); +} + +#ifdef WITH_SERVER +int crypt_set_algorithms_server(ssh_session session){ + char *method = NULL; + int i = 0; + int rc = SSH_ERROR; + struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); + + if (session == NULL) { + return SSH_ERROR; + } + + /* we must scan the kex entries to find crypto algorithms and set their appropriate structure */ + enter_function(); + /* out */ + method = session->next_crypto->kex_methods[SSH_CRYPT_S_C]; + while(ssh_ciphertab[i].name && strcmp(method,ssh_ciphertab[i].name)) + i++; + if(!ssh_ciphertab[i].name){ + ssh_set_error(session,SSH_FATAL,"crypt_set_algorithms_server : " + "no crypto algorithm function found for %s",method); + goto error; + } + ssh_log(session,SSH_LOG_PACKET,"Set output algorithm %s",method); + + session->next_crypto->out_cipher = cipher_new(i); + if (session->next_crypto->out_cipher == NULL) { + ssh_set_error_oom(session); + goto error; + } + i=0; + /* in */ + method = session->next_crypto->kex_methods[SSH_CRYPT_C_S]; + while(ssh_ciphertab[i].name && strcmp(method,ssh_ciphertab[i].name)) + i++; + if(!ssh_ciphertab[i].name){ + ssh_set_error(session,SSH_FATAL,"Crypt_set_algorithms_server :" + "no crypto algorithm function found for %s",method); + goto error; + } + ssh_log(session,SSH_LOG_PACKET,"Set input algorithm %s",method); + + session->next_crypto->in_cipher = cipher_new(i); + if (session->next_crypto->in_cipher == NULL) { + ssh_set_error_oom(session); + goto error; + } + + /* compression */ + method = session->next_crypto->kex_methods[SSH_CRYPT_C_S]; + if(strcmp(method,"zlib") == 0){ + ssh_log(session,SSH_LOG_PACKET,"enabling C->S compression"); + session->next_crypto->do_compress_in=1; + } + if(strcmp(method,"zlib@openssh.com") == 0){ + ssh_set_error(session,SSH_FATAL,"zlib@openssh.com not supported"); + goto error; + } + method = session->next_crypto->kex_methods[SSH_CRYPT_S_C]; + if(strcmp(method,"zlib") == 0){ + ssh_log(session,SSH_LOG_PACKET,"enabling S->C compression\n"); + session->next_crypto->do_compress_out=1; + } + if(strcmp(method,"zlib@openssh.com") == 0){ + ssh_set_error(session,SSH_FATAL,"zlib@openssh.com not supported"); + goto error; + } + + method = session->next_crypto->kex_methods[SSH_HOSTKEYS]; + session->srv.hostkey = ssh_key_type_from_name(method); + rc = SSH_OK; + error: + leave_function(); + return rc; +} + +#endif /* WITH_SERVER */ +/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/tests/CMakeLists.txt b/libssh/tests/CMakeLists.txt new file mode 100644 index 00000000..bb39755a --- /dev/null +++ b/libssh/tests/CMakeLists.txt @@ -0,0 +1,48 @@ +project(tests C) + +if (BSD OR SOLARIS) + find_package(Argp) +endif (BSD OR SOLARIS) + +set(TORTURE_LIBRARY torture) + +include_directories( + ${LIBSSH_PUBLIC_INCLUDE_DIRS} + ${CMOCKA_INCLUDE_DIR} + ${OPENSSL_INCLUDE_DIRS} + ${GCRYPT_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR} +) + +# create test library +add_library(${TORTURE_LIBRARY} STATIC cmdline.c torture.c) +target_link_libraries(${TORTURE_LIBRARY} + ${CMOCKA_LIBRARY} + ${LIBSSH_STATIC_LIBRARY} + ${LIBSSH_LINK_LIBRARIES} + ${LIBSSH_THREADS_STATIC_LIBRARY} + ${LIBSSH_THREADS_LINK_LIBRARIES} + ${ARGP_LIBRARIES} +) + +set(TEST_TARGET_LIBRARIES + ${TORTURE_LIBRARY} + ${CMOCKA_LIBRARY} + ${LIBSSH_STATIC_LIBRARY} + ${LIBSSH_LINK_LIBRARIES} + ${LIBSSH_THREADS_STATIC_LIBRARY} + ${LIBSSH_THREADS_LINK_LIBRARIES} +) + +add_subdirectory(unittests) +if (WITH_CLIENT_TESTING) + add_subdirectory(client) +endif (WITH_CLIENT_TESTING) + +if (WITH_BENCHMARKS) + add_subdirectory(benchmarks) +endif (WITH_BENCHMARKS) + diff --git a/libssh/tests/authentication.c b/libssh/tests/authentication.c new file mode 100644 index 00000000..248b646f --- /dev/null +++ b/libssh/tests/authentication.c @@ -0,0 +1,74 @@ +/* +This file is distributed in public domain. You can do whatever you want +with its content. +*/ + + +#include +#include +#include +#include + +#include + +#include "tests.h" +static int auth_kbdint(SSH_SESSION *session){ + int err=ssh_userauth_kbdint(session,NULL,NULL); + char *name,*instruction,*prompt,*ptr; + char buffer[128]; + int i,n; + char echo; + while (err==SSH_AUTH_INFO){ + name=ssh_userauth_kbdint_getname(session); + instruction=ssh_userauth_kbdint_getinstruction(session); + n=ssh_userauth_kbdint_getnprompts(session); + if(strlen(name)>0) + printf("%s\n",name); + if(strlen(instruction)>0) + printf("%s\n",instruction); + for(i=0;i&1`" +echo "Ping latency to $DEST": +ping -q -c 1 -n $DEST +echo "Destination $DEST SSHD vesion : `echo | nc $DEST 22 | head -n1`" +echo "ssh login latency :`(time -f user:%U ssh $DEST 'id > /dev/null') 2>&1`" +./generate.py | dd bs=4096 count=100000 | time ssh -c $CIPHER $DEST "dd bs=4096 of=/dev/null" 2>&1 + diff --git a/libssh/tests/benchmarks/bench2.sh b/libssh/tests/benchmarks/bench2.sh new file mode 100755 index 00000000..01d67777 --- /dev/null +++ b/libssh/tests/benchmarks/bench2.sh @@ -0,0 +1,13 @@ +export CIPHER=aes128-cbc +export DEST=localhost + +echo "Upload raw SSH statistics" +echo "local machine: `uname -a`" +echo "Cipher : $CIPHER ; Destination : $DEST (`ssh $DEST uname -a`)" +echo "Local ssh version: `samplessh -V 2>&1`" +echo "Ping latency to $DEST": +ping -q -c 1 -n $DEST +echo "Destination $DEST SSHD vesion : `echo | nc $DEST 22 | head -n1`" +echo "ssh login latency :`(time -f user:%U samplessh $DEST 'id > /dev/null') 2>&1`" +./generate.py | dd bs=4096 count=100000 | strace samplessh -c $CIPHER $DEST "dd bs=4096 of=/dev/null" 2>&1 + diff --git a/libssh/tests/benchmarks/bench_raw.c b/libssh/tests/benchmarks/bench_raw.c new file mode 100644 index 00000000..db1a057c --- /dev/null +++ b/libssh/tests/benchmarks/bench_raw.c @@ -0,0 +1,285 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "benchmarks.h" +#include +#include +#include + +#define PYTHON_PATH "/usr/bin/python" + +const char python_eater[]= +"#!/usr/bin/python\n" +"import sys\n" +"print 'go'\n" +"sys.stdout.flush()\n" +"toread=XXXXXXXXXX\n" +"read=0\n" +"while(read < toread):\n" +" buffersize=toread-read\n" +" if(buffersize > 4096):\n" +" buffersize=4096\n" +" r=len(sys.stdin.read(buffersize))\n" +" read+=r\n" +" if(r<=0):\n" +" print 'error'\n" +" exit()\n" +"print 'done'\n"; + +static char *get_python_eater(unsigned long bytes){ + char *eater=malloc(sizeof(python_eater)); + char *ptr; + char buf[12]; + + memcpy(eater,python_eater,sizeof(python_eater)); + ptr=strstr(eater,"XXXXXXXXXX"); + if(!ptr){ + free(eater); + return NULL; + } + sprintf(buf,"0x%.8lx",bytes); + memcpy(ptr,buf,10); + return eater; +} + +/** @internal + * @brief uploads a script (python or other) at a specific path on the + * remote host + * @param[in] session an active SSH session + * @param[in] path to copy the file + * @param[in] content of the file to copy + * @return 0 on success, -1 on error + */ +static int upload_script(ssh_session session, const char *path, + const char *script){ + ssh_channel channel; + char cmd[128]; + int err; + + channel=ssh_channel_new(session); + if(!channel) + goto error; + if(ssh_channel_open_session(channel) == SSH_ERROR) + goto error; + snprintf(cmd,sizeof(cmd),"cat > %s",path); + if(ssh_channel_request_exec(channel,cmd) == SSH_ERROR) + goto error; + err=ssh_channel_write(channel,script,strlen(script)); + if(err == SSH_ERROR) + goto error; + if(ssh_channel_send_eof(channel) == SSH_ERROR) + goto error; + if(ssh_channel_close(channel) == SSH_ERROR) + goto error; + ssh_channel_free(channel); + return 0; +error: + fprintf(stderr,"Error while copying script : %s\n",ssh_get_error(session)); + return -1; +} + +/** @internal + * @brief benchmarks a raw upload (simple upload in a SSH channel) using an + * existing SSH session. + * @param[in] session Open SSH session + * @param[in] args Parsed command line arguments + * @param[out] bps The calculated bytes per second obtained via benchmark. + * @return 0 on success, -1 on error. + */ +int benchmarks_raw_up (ssh_session session, struct argument_s *args, + float *bps){ + unsigned long bytes; + char *script; + char cmd[128]; + int err; + ssh_channel channel; + struct timestamp_struct ts; + float ms=0.0; + unsigned long total=0; + + bytes = args->datasize * 1024 * 1024; + script =get_python_eater(bytes); + err=upload_script(session,"/tmp/eater.py",script); + free(script); + if(err<0) + return err; + channel=ssh_channel_new(session); + if(channel == NULL) + goto error; + if(ssh_channel_open_session(channel)==SSH_ERROR) + goto error; + snprintf(cmd,sizeof(cmd),"%s /tmp/eater.py", PYTHON_PATH); + if(ssh_channel_request_exec(channel,cmd)==SSH_ERROR) + goto error; + if((err=ssh_channel_read(channel,buffer,sizeof(buffer)-1,0))==SSH_ERROR) + goto error; + buffer[err]=0; + if(!strstr(buffer,"go")){ + fprintf(stderr,"parse error : %s\n",buffer); + ssh_channel_close(channel); + ssh_channel_free(channel); + return -1; + } + if(args->verbose>0) + fprintf(stdout,"Starting upload of %lu bytes now\n",bytes); + timestamp_init(&ts); + while(total < bytes){ + unsigned long towrite = bytes - total; + int w; + if(towrite > args->chunksize) + towrite = args->chunksize; + w=ssh_channel_write(channel,buffer,towrite); + if(w == SSH_ERROR) + goto error; + total += w; + } + + if(args->verbose>0) + fprintf(stdout,"Finished upload, now waiting the ack\n"); + + if((err=ssh_channel_read(channel,buffer,5,0))==SSH_ERROR) + goto error; + buffer[err]=0; + if(!strstr(buffer,"done")){ + fprintf(stderr,"parse error : %s\n",buffer); + ssh_channel_close(channel); + ssh_channel_free(channel); + return -1; + } + ms=elapsed_time(&ts); + *bps=8000 * (float)bytes / ms; + if(args->verbose > 0) + fprintf(stdout,"Upload took %f ms for %lu bytes, at %f bps\n",ms, + bytes,*bps); + ssh_channel_close(channel); + ssh_channel_free(channel); + return 0; +error: + fprintf(stderr,"Error during raw upload : %s\n",ssh_get_error(session)); + if(channel){ + ssh_channel_close(channel); + ssh_channel_free(channel); + } + return -1; +} + +const char python_giver[] = +"#!/usr/bin/python\n" +"import sys\n" +"r=sys.stdin.read(2)\n" +"towrite=XXXXXXXXXX\n" +"wrote=0\n" +"mtu = 32786\n" +"buf = 'A'*mtu\n" +"while(wrote < towrite):\n" +" buffersize=towrite-wrote\n" +" if(buffersize > mtu):\n" +" buffersize=mtu\n" +" if(buffersize == mtu):\n" +" sys.stdout.write(buf)\n" +" else:\n" +" sys.stdout.write('A'*buffersize)\n" +" wrote+=buffersize\n" +"sys.stdout.flush()\n"; + +static char *get_python_giver(unsigned long bytes){ + char *giver=malloc(sizeof(python_giver)); + char *ptr; + char buf[12]; + + memcpy(giver,python_giver,sizeof(python_giver)); + ptr=strstr(giver,"XXXXXXXXXX"); + if(!ptr){ + free(giver); + return NULL; + } + sprintf(buf,"0x%.8lx",bytes); + memcpy(ptr,buf,10); + return giver; +} + +/** @internal + * @brief benchmarks a raw download (simple upload in a SSH channel) using an + * existing SSH session. + * @param[in] session Open SSH session + * @param[in] args Parsed command line arguments + * @param[out] bps The calculated bytes per second obtained via benchmark. + * @return 0 on success, -1 on error. + */ +int benchmarks_raw_down (ssh_session session, struct argument_s *args, + float *bps){ + unsigned long bytes; + char *script; + char cmd[128]; + int err; + ssh_channel channel; + struct timestamp_struct ts; + float ms=0.0; + unsigned long total=0; + + bytes = args->datasize * 1024 * 1024; + script =get_python_giver(bytes); + err=upload_script(session,"/tmp/giver.py",script); + free(script); + if(err<0) + return err; + channel=ssh_channel_new(session); + if(channel == NULL) + goto error; + if(ssh_channel_open_session(channel)==SSH_ERROR) + goto error; + snprintf(cmd,sizeof(cmd),"%s /tmp/giver.py", PYTHON_PATH); + if(ssh_channel_request_exec(channel,cmd)==SSH_ERROR) + goto error; + if((err=ssh_channel_write(channel,"go",2))==SSH_ERROR) + goto error; + if(args->verbose>0) + fprintf(stdout,"Starting download of %lu bytes now\n",bytes); + timestamp_init(&ts); + while(total < bytes){ + unsigned long toread = bytes - total; + int r; + if(toread > args->chunksize) + toread = args->chunksize; + r=ssh_channel_read(channel,buffer,toread,0); + if(r == SSH_ERROR) + goto error; + total += r; + } + + if(args->verbose>0) + fprintf(stdout,"Finished download\n"); + ms=elapsed_time(&ts); + *bps=8000 * (float)bytes / ms; + if(args->verbose > 0) + fprintf(stdout,"Download took %f ms for %lu bytes, at %f bps\n",ms, + bytes,*bps); + ssh_channel_close(channel); + ssh_channel_free(channel); + return 0; +error: + fprintf(stderr,"Error during raw upload : %s\n",ssh_get_error(session)); + if(channel){ + ssh_channel_close(channel); + ssh_channel_free(channel); + } + return -1; +} diff --git a/libssh/tests/benchmarks/bench_scp.c b/libssh/tests/benchmarks/bench_scp.c new file mode 100644 index 00000000..340637ba --- /dev/null +++ b/libssh/tests/benchmarks/bench_scp.c @@ -0,0 +1,150 @@ +/* bench_scp.c + * + * This file is part of the SSH Library + * + * Copyright (c) 2011 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "benchmarks.h" +#include +#include + +#define SCPDIR "/tmp/" +#define SCPFILE "scpbenchmark" + +/** @internal + * @brief benchmarks a scp upload using an + * existing SSH session. + * @param[in] session Open SSH session + * @param[in] args Parsed command line arguments + * @param[out] bps The calculated bytes per second obtained via benchmark. + * @return 0 on success, -1 on error. + */ +int benchmarks_scp_up (ssh_session session, struct argument_s *args, + float *bps){ + unsigned long bytes; + struct timestamp_struct ts; + float ms=0.0; + unsigned long total=0; + ssh_scp scp; + + bytes = args->datasize * 1024 * 1024; + scp = ssh_scp_new(session,SSH_SCP_WRITE,SCPDIR); + if(scp == NULL) + goto error; + if(ssh_scp_init(scp)==SSH_ERROR) + goto error; + if(ssh_scp_push_file(scp,SCPFILE,bytes,0777) != SSH_OK) + goto error; + if(args->verbose>0) + fprintf(stdout,"Starting upload of %lu bytes now\n",bytes); + timestamp_init(&ts); + while(total < bytes){ + unsigned long towrite = bytes - total; + int w; + if(towrite > args->chunksize) + towrite = args->chunksize; + w=ssh_scp_write(scp,buffer,towrite); + if(w == SSH_ERROR) + goto error; + total += towrite; + } + ms=elapsed_time(&ts); + *bps=8000 * (float)bytes / ms; + if(args->verbose > 0) + fprintf(stdout,"Upload took %f ms for %lu bytes, at %f bps\n",ms, + bytes,*bps); + ssh_scp_close(scp); + ssh_scp_free(scp); + return 0; +error: + fprintf(stderr,"Error during scp upload : %s\n",ssh_get_error(session)); + if(scp){ + ssh_scp_close(scp); + ssh_scp_free(scp); + } + return -1; +} + +/** @internal + * @brief benchmarks a scp download using an + * existing SSH session. + * @param[in] session Open SSH session + * @param[in] args Parsed command line arguments + * @param[out] bps The calculated bytes per second obtained via benchmark. + * @return 0 on success, -1 on error. + */ +int benchmarks_scp_down (ssh_session session, struct argument_s *args, + float *bps){ + unsigned long bytes; + struct timestamp_struct ts; + float ms=0.0; + unsigned long total=0; + ssh_scp scp; + int r; + size_t size; + + bytes = args->datasize * 1024 * 1024; + scp = ssh_scp_new(session,SSH_SCP_READ,SCPDIR SCPFILE); + if(scp == NULL) + goto error; + if(ssh_scp_init(scp)==SSH_ERROR) + goto error; + r=ssh_scp_pull_request(scp); + if(r == SSH_SCP_REQUEST_NEWFILE){ + size=ssh_scp_request_get_size(scp); + if(bytes > size){ + printf("Only %d bytes available (on %lu requested).\n",size,bytes); + bytes = size; + } + if(size > bytes){ + printf("File is %d bytes (on %lu requested). Will cut the end\n",size,bytes); + } + if(args->verbose>0) + fprintf(stdout,"Starting download of %lu bytes now\n",bytes); + timestamp_init(&ts); + ssh_scp_accept_request(scp); + while(total < bytes){ + unsigned long toread = bytes - total; + if(toread > args->chunksize) + toread = args->chunksize; + r=ssh_scp_read(scp,buffer,toread); + if(r == SSH_ERROR || r == 0) + goto error; + total += r; + } + ms=elapsed_time(&ts); + *bps=8000 * (float)bytes / ms; + if(args->verbose > 0) + fprintf(stdout,"download took %f ms for %lu bytes, at %f bps\n",ms, + bytes,*bps); + } else { + fprintf(stderr,"Expected SSH_SCP_REQUEST_NEWFILE, got %d\n",r); + goto error; + } + ssh_scp_close(scp); + ssh_scp_free(scp); + return 0; +error: + fprintf(stderr,"Error during scp download : %s\n",ssh_get_error(session)); + if(scp){ + ssh_scp_close(scp); + ssh_scp_free(scp); + } + return -1; +} diff --git a/libssh/tests/benchmarks/bench_sftp.c b/libssh/tests/benchmarks/bench_sftp.c new file mode 100644 index 00000000..9e4ab34d --- /dev/null +++ b/libssh/tests/benchmarks/bench_sftp.c @@ -0,0 +1,239 @@ +/* bench_sftp.c + * + * This file is part of the SSH Library + * + * Copyright (c) 2011 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "benchmarks.h" +#include +#include +#include +#include +#include + +#define SFTPDIR "/tmp/" +#define SFTPFILE "scpbenchmark" + +/** @internal + * @brief benchmarks a synchronous sftp upload using an + * existing SSH session. + * @param[in] session Open SSH session + * @param[in] args Parsed command line arguments + * @param[out] bps The calculated bytes per second obtained via benchmark. + * @return 0 on success, -1 on error. + */ +int benchmarks_sync_sftp_up (ssh_session session, struct argument_s *args, + float *bps){ + unsigned long bytes; + struct timestamp_struct ts; + float ms=0.0; + unsigned long total=0; + sftp_session sftp; + sftp_file file; + + bytes = args->datasize * 1024 * 1024; + sftp = sftp_new(session); + if(sftp == NULL) + goto error; + if(sftp_init(sftp)==SSH_ERROR) + goto error; + file = sftp_open(sftp,SFTPDIR SFTPFILE,O_RDWR | O_CREAT | O_TRUNC, 0777); + if(!file) + goto error; + if(args->verbose>0) + fprintf(stdout,"Starting upload of %lu bytes now\n",bytes); + timestamp_init(&ts); + while(total < bytes){ + unsigned long towrite = bytes - total; + int w; + if(towrite > args->chunksize) + towrite = args->chunksize; + w=sftp_write(file,buffer,towrite); + if(w == SSH_ERROR) + goto error; + total += w; + } + sftp_close(file); + ms=elapsed_time(&ts); + *bps=8000 * (float)bytes / ms; + if(args->verbose > 0) + fprintf(stdout,"Upload took %f ms for %lu bytes, at %f bps\n",ms, + bytes,*bps); + sftp_free(sftp); + return 0; +error: + fprintf(stderr,"Error during scp upload : %s\n",ssh_get_error(session)); + if(file) + sftp_close(file); + if(sftp) + sftp_free(sftp); + return -1; +} + +/** @internal + * @brief benchmarks a synchronous sftp download using an + * existing SSH session. + * @param[in] session Open SSH session + * @param[in] args Parsed command line arguments + * @param[out] bps The calculated bytes per second obtained via benchmark. + * @return 0 on success, -1 on error. + */ +int benchmarks_sync_sftp_down (ssh_session session, struct argument_s *args, + float *bps){ + unsigned long bytes; + struct timestamp_struct ts; + float ms=0.0; + unsigned long total=0; + sftp_session sftp; + sftp_file file; + int r; + + bytes = args->datasize * 1024 * 1024; + sftp = sftp_new(session); + if(sftp == NULL) + goto error; + if(sftp_init(sftp)==SSH_ERROR) + goto error; + file = sftp_open(sftp,SFTPDIR SFTPFILE,O_RDONLY,0); + if(!file) + goto error; + if(args->verbose>0) + fprintf(stdout,"Starting download of %lu bytes now\n",bytes); + timestamp_init(&ts); + while(total < bytes){ + unsigned long toread = bytes - total; + if(toread > args->chunksize) + toread = args->chunksize; + r=sftp_read(file,buffer,toread); + if(r == SSH_ERROR) + goto error; + total += r; + /* we had a smaller file */ + if(r==0){ + fprintf(stdout,"File smaller than expected : %lu (expected %lu).\n",total,bytes); + bytes = total; + break; + } + } + sftp_close(file); + ms=elapsed_time(&ts); + *bps=8000 * (float)bytes / ms; + if(args->verbose > 0) + fprintf(stdout,"download took %f ms for %lu bytes, at %f bps\n",ms, + bytes,*bps); + sftp_free(sftp); + return 0; +error: + fprintf(stderr,"Error during sftp download : %s\n",ssh_get_error(session)); + if(file) + sftp_close(file); + if(sftp) + sftp_free(sftp); + return -1; +} + +/** @internal + * @brief benchmarks an asynchronous sftp download using an + * existing SSH session. + * @param[in] session Open SSH session + * @param[in] args Parsed command line arguments + * @param[out] bps The calculated bytes per second obtained via benchmark. + * @return 0 on success, -1 on error. + */ +int benchmarks_async_sftp_down (ssh_session session, struct argument_s *args, + float *bps){ + unsigned long bytes; + struct timestamp_struct ts; + float ms=0.0; + unsigned long total=0; + sftp_session sftp; + sftp_file file; + int r,i; + int warned = 0; + unsigned long toread; + int *ids=NULL; + int concurrent_downloads = args->concurrent_requests; + + bytes = args->datasize * 1024 * 1024; + sftp = sftp_new(session); + if(sftp == NULL) + goto error; + if(sftp_init(sftp)==SSH_ERROR) + goto error; + file = sftp_open(sftp,SFTPDIR SFTPFILE,O_RDONLY,0); + if(!file) + goto error; + ids = malloc(concurrent_downloads * sizeof(int)); + if(args->verbose>0) + fprintf(stdout,"Starting download of %lu bytes now, using %d concurrent downloads\n",bytes, + concurrent_downloads); + timestamp_init(&ts); + for (i=0;ichunksize); + if(ids[i]==SSH_ERROR) + goto error; + } + i=0; + while(total < bytes){ + r = sftp_async_read(file, buffer, args->chunksize, ids[i]); + if(r == SSH_ERROR) + goto error; + total += r; + if(r != (int)args->chunksize && total != bytes && !warned){ + fprintf(stderr,"async_sftp_download : receiving short reads (%d, requested %d) " + "the received file will be corrupted and shorted. Adapt chunksize to %d\n", + r, args->chunksize,r); + warned = 1; + } + /* we had a smaller file */ + if(r==0){ + fprintf(stdout,"File smaller than expected : %lu (expected %lu).\n",total,bytes); + bytes = total; + break; + } + toread = bytes - total; + if(toread < args->chunksize * concurrent_downloads){ + /* we've got enough launched downloads */ + ids[i]=-1; + } + if(toread > args->chunksize) + toread = args->chunksize; + ids[i]=sftp_async_read_begin(file,toread); + if(ids[i] == SSH_ERROR) + goto error; + i = (i+1) % concurrent_downloads; + } + sftp_close(file); + ms=elapsed_time(&ts); + *bps=8000 * (float)bytes / ms; + if(args->verbose > 0) + fprintf(stdout,"download took %f ms for %lu bytes, at %f bps\n",ms, + bytes,*bps); + sftp_free(sftp); + free(ids); + return 0; +error: + fprintf(stderr,"Error during sftp download : %s\n",ssh_get_error(session)); + if(file) + sftp_close(file); + if(sftp) + sftp_free(sftp); + free(ids); + return -1; +} diff --git a/libssh/tests/benchmarks/benchmarks.c b/libssh/tests/benchmarks/benchmarks.c new file mode 100644 index 00000000..5e33dd4b --- /dev/null +++ b/libssh/tests/benchmarks/benchmarks.c @@ -0,0 +1,400 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "benchmarks.h" +#include + +#include +#include +#include + +struct benchmark benchmarks[]= { + { + .name="benchmark_raw_upload", + .fct=benchmarks_raw_up, + .enabled=0 + }, + { + .name="benchmark_raw_download", + .fct=benchmarks_raw_down, + .enabled=0 + }, + { + .name="benchmark_scp_upload", + .fct=benchmarks_scp_up, + .enabled=0 + }, + { + .name="benchmark_scp_download", + .fct=benchmarks_scp_down, + .enabled=0 + }, + { + .name="benchmark_sync_sftp_upload", + .fct=benchmarks_sync_sftp_up, + .enabled=0 + }, + { + .name="benchmark_sync_sftp_download", + .fct=benchmarks_sync_sftp_down, + .enabled=0 + }, + { + .name="benchmark_async_sftp_download", + .fct=benchmarks_async_sftp_down, + .enabled=0 + } +}; + +#ifdef HAVE_ARGP_H +#include + +const char *argp_program_version = "libssh benchmarks 2011-08-28"; +const char *argp_program_bug_address = "Aris Adamantiadis "; + +static char **cmdline; + +/* Program documentation. */ +static char doc[] = "libssh benchmarks"; + + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Make libssh benchmark more verbose", + .group = 0 + }, + { + .name = "raw-upload", + .key = '1', + .arg = NULL, + .flags = 0, + .doc = "Upload raw data using channel", + .group = 0 + }, + { + .name = "raw-download", + .key = '2', + .arg = NULL, + .flags = 0, + .doc = "Download raw data using channel", + .group = 0 + }, + { + .name = "scp-upload", + .key = '3', + .arg = NULL, + .flags = 0, + .doc = "Upload data using SCP", + .group = 0 + }, + { + .name = "scp-download", + .key = '4', + .arg = NULL, + .flags = 0, + .doc = "Download data using SCP", + .group = 0 + }, + { + .name = "sync-sftp-upload", + .key = '5', + .arg = NULL, + .flags = 0, + .doc = "Upload data using synchronous SFTP", + .group = 0 + + }, + { + .name = "sync-sftp-download", + .key = '6', + .arg = NULL, + .flags = 0, + .doc = "Download data using synchronous SFTP (slow)", + .group = 0 + + }, + { + .name = "async-sftp-download", + .key = '7', + .arg = NULL, + .flags = 0, + .doc = "Download data using asynchronous SFTP (fast)", + .group = 0 + + }, + { + .name = "host", + .key = 'h', + .arg = "HOST", + .flags = 0, + .doc = "Add a host to connect for benchmark (format user@hostname)", + .group = 0 + }, + { + .name = "size", + .key = 's', + .arg = "MBYTES", + .flags = 0, + .doc = "MBytes of data to send/receive per test", + .group = 0 + }, + { + .name = "chunk", + .key = 'c', + .arg = "bytes", + .flags = 0, + .doc = "size of data chunks to send/receive", + .group = 0 + }, + { + .name = "prequests", + .key = 'p', + .arg = "number [20]", + .flags = 0, + .doc = "[async SFTP] number of concurrent requests", + .group = 0 + }, + { + .name = "cipher", + .key = 'C', + .arg = "cipher", + .flags = 0, + .doc = "Cryptographic cipher to be used", + .group = 0 + }, + + {NULL, 0, NULL, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + struct argument_s *arguments = state->input; + + /* arg is currently not used */ + (void) arg; + + switch (key) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + benchmarks[key - '1'].enabled = 1; + arguments->ntests ++; + break; + case 'v': + arguments->verbose++; + break; + case 's': + arguments->datasize = atoi(arg); + break; + case 'p': + arguments->concurrent_requests = atoi(arg); + break; + case 'c': + arguments->chunksize = atoi(arg); + break; + case 'C': + arguments->cipher = arg; + break; + case 'h': + if(arguments->nhosts >= MAX_HOSTS_CONNECT){ + fprintf(stderr, "Too much hosts\n"); + return ARGP_ERR_UNKNOWN; + } + arguments->hosts[arguments->nhosts]=arg; + arguments->nhosts++; + break; + case ARGP_KEY_ARG: + /* End processing here. */ + cmdline = &state->argv [state->next - 1]; + state->next = state->argc; + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Our argp parser. */ +static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL}; + +#endif /* HAVE_ARGP_H */ + +static void cmdline_parse(int argc, char **argv, struct argument_s *arguments) { + /* + * Parse our arguments; every option seen by parse_opt will + * be reflected in arguments. + */ +#ifdef HAVE_ARGP_H + argp_parse(&argp, argc, argv, 0, 0, arguments); +#else /* HAVE_ARGP_H */ + (void) argc; + (void) argv; + arguments->hosts[0]="localhost"; + arguments->nhosts=1; +#endif /* HAVE_ARGP_H */ +} + +static void arguments_init(struct argument_s *arguments){ + memset(arguments,0,sizeof(*arguments)); + arguments->chunksize=32758; + arguments->concurrent_requests=20; + arguments->datasize = 10; +} + +static ssh_session connect_host(const char *host, int verbose, char *cipher){ + ssh_session session=ssh_new(); + if(session==NULL) + goto error; + if(ssh_options_set(session,SSH_OPTIONS_HOST, host)<0) + goto error; + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbose); + if(cipher != NULL){ + if (ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher) || + ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher)){ + goto error; + } + } + ssh_options_parse_config(session, NULL); + if(ssh_connect(session)==SSH_ERROR) + goto error; + if(ssh_userauth_autopubkey(session,NULL) != SSH_AUTH_SUCCESS) + goto error; + return session; +error: + fprintf(stderr,"Error connecting to \"%s\": %s\n",host,ssh_get_error(session)); + ssh_free(session); + return NULL; +} + +static char *network_speed(float bps){ + static char buf[128]; + if(bps > 1000*1000*1000){ + /* Gbps */ + snprintf(buf,sizeof(buf),"%f Gbps",bps/(1000*1000*1000)); + } else if(bps > 1000*1000){ + /* Mbps */ + snprintf(buf,sizeof(buf),"%f Mbps",bps/(1000*1000)); + } else if(bps > 1000){ + snprintf(buf,sizeof(buf),"%f Kbps",bps/1000); + } else { + snprintf(buf,sizeof(buf),"%f bps",bps); + } + return buf; +} + +static void do_benchmarks(ssh_session session, struct argument_s *arguments, + const char *hostname){ + float ping_rtt=0.0; + float ssh_rtt=0.0; + float bps=0.0; + int i; + int err; + struct benchmark *b; + + if(arguments->verbose>0) + fprintf(stdout,"Testing ICMP RTT\n"); + err=benchmarks_ping_latency(hostname, &ping_rtt); + if(err == 0){ + fprintf(stdout,"ping RTT : %f ms\n",ping_rtt); + } + err=benchmarks_ssh_latency(session, &ssh_rtt); + if(err==0){ + fprintf(stdout, "SSH RTT : %f ms. Theoretical max BW (win=128K) : %s\n",ssh_rtt,network_speed(128000.0/(ssh_rtt / 1000.0))); + } + for (i=0 ; ienabled){ + err=b->fct(session,arguments,&bps); + if(err==0){ + fprintf(stdout, "%s : %s : %s\n",hostname, b->name, network_speed(bps)); + } + } + } +} + +char *buffer; + +int main(int argc, char **argv){ + struct argument_s arguments; + ssh_session session; + int i; + + arguments_init(&arguments); + cmdline_parse(argc, argv, &arguments); + if (arguments.nhosts==0){ + fprintf(stderr,"At least one host (-h) must be specified\n"); + return EXIT_FAILURE; + } + if (arguments.ntests==0){ + for(i=0; i < BENCHMARK_NUMBER ; ++i){ + benchmarks[i].enabled=1; + } + arguments.ntests=BENCHMARK_NUMBER; + } + buffer=malloc(arguments.chunksize > 1024 ? arguments.chunksize : 1024); + if(buffer == NULL){ + fprintf(stderr,"Allocation of chunk buffer failed\n"); + return EXIT_FAILURE; + } + if (arguments.verbose > 0){ + fprintf(stdout, "Will try hosts "); + for(i=0;i 0) + fprintf(stdout,"Connecting to \"%s\"...\n",arguments.hosts[i]); + session=connect_host(arguments.hosts[i], arguments.verbose, arguments.cipher); + if(session != NULL && arguments.verbose > 0) + fprintf(stdout,"Success\n"); + if(session == NULL){ + fprintf(stderr,"Errors occurred, stopping\n"); + return EXIT_FAILURE; + } + do_benchmarks(session, &arguments, arguments.hosts[i]); + ssh_disconnect(session); + ssh_free(session); + } + return EXIT_SUCCESS; +} + diff --git a/libssh/tests/benchmarks/benchmarks.h b/libssh/tests/benchmarks/benchmarks.h new file mode 100644 index 00000000..26da09bb --- /dev/null +++ b/libssh/tests/benchmarks/benchmarks.h @@ -0,0 +1,99 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef BENCHMARKS_H_ +#define BENCHMARKS_H_ + +#include + +/* benchmarks.c */ + +/* maximum number of parallel hosts that may be checked */ +#define MAX_HOSTS_CONNECT 20 + +enum libssh_benchmarks { + BENCHMARK_RAW_UPLOAD=0, + BENCHMARK_RAW_DOWNLOAD, + BENCHMARK_SCP_UPLOAD, + BENCHMARK_SCP_DOWNLOAD, + BENCHMARK_SYNC_SFTP_UPLOAD, + BENCHMARK_SYNC_SFTP_DOWNLOAD, + BENCHMARK_ASYNC_SFTP_DOWNLOAD, + BENCHMARK_NUMBER +}; + +struct argument_s { + const char *hosts[MAX_HOSTS_CONNECT]; + int verbose; + int nhosts; + int ntests; + unsigned int datasize; + unsigned int chunksize; + int concurrent_requests; + char *cipher; +}; + +extern char *buffer; + +typedef int (*bench_fct)(ssh_session session, struct argument_s *args, + float *bps); + +struct benchmark { + const char *name; + bench_fct fct; + int enabled; +}; + +/* latency.c */ + +struct timestamp_struct { + struct timeval timestamp; +}; + +int benchmarks_ping_latency (const char *host, float *average); +int benchmarks_ssh_latency (ssh_session session, float *average); + +void timestamp_init(struct timestamp_struct *ts); +float elapsed_time(struct timestamp_struct *ts); + +/* bench_raw.c */ + +int benchmarks_raw_up (ssh_session session, struct argument_s *args, + float *bps); +int benchmarks_raw_down (ssh_session session, struct argument_s *args, + float *bps); + +/* bench_scp.c */ + +int benchmarks_scp_up (ssh_session session, struct argument_s *args, + float *bps); +int benchmarks_scp_down (ssh_session session, struct argument_s *args, + float *bps); + +/* bench_sftp.c */ + +int benchmarks_sync_sftp_up (ssh_session session, struct argument_s *args, + float *bps); +int benchmarks_sync_sftp_down (ssh_session session, struct argument_s *args, + float *bps); +int benchmarks_async_sftp_down (ssh_session session, struct argument_s *args, + float *bps); +#endif /* BENCHMARKS_H_ */ diff --git a/libssh/tests/benchmarks/latency.c b/libssh/tests/benchmarks/latency.c new file mode 100644 index 00000000..09c50a04 --- /dev/null +++ b/libssh/tests/benchmarks/latency.c @@ -0,0 +1,148 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "benchmarks.h" +#include + +#include +#include +#include +#include +#include + +#define PING_PROGRAM "/bin/ping" + +/** @internal + * @brief Calculates the RTT of the host with ICMP ping, and returns the + * average of the calculated RTT. + * @param[in] host hostname to ping. + * @param[out] average average RTT in milliseconds. + * @returns 0 on success, -1 if there is an error. + * @warning relies on an external ping program which may not exist on + * certain OS. + */ +int benchmarks_ping_latency (const char *host, float *average){ + const char *ptr; + char cmd[256]; + char line[1024]; + FILE *fd; + int found=0; + + /* strip out the hostname */ + ptr=strchr(host,'@'); + if(ptr) + ptr++; + else + ptr=host; + + snprintf(cmd,sizeof(cmd),"%s -n -q -c3 %s",PING_PROGRAM, ptr); + fd=popen(cmd,"r"); + if(fd==NULL){ + fprintf(stderr,"Error executing command : %s\n", strerror(errno)); + return -1; + } + + while(!found && fgets(line,sizeof(line),fd)!=NULL){ + if(strstr(line,"rtt")){ + ptr=strchr(line,'='); + if(ptr==NULL) + goto parseerror; + ptr=strchr(ptr,'/'); + if(ptr==NULL) + goto parseerror; + *average=strtof(ptr+1,NULL); + found=1; + break; + } + } + if(!found) + goto parseerror; + pclose(fd); + return 0; + +parseerror: + fprintf(stderr,"Parse error : couldn't locate average in %s",line); + pclose(fd); + return -1; +} + +/** @internal + * @brief initialize a timestamp to the current time. + * @param[out] ts A timestamp_struct pointer. + */ +void timestamp_init(struct timestamp_struct *ts){ + gettimeofday(&ts->timestamp,NULL); +} + +/** @internal + * @brief return the elapsed time since now and the moment ts was initialized. + * @param[in] ts An initialized timestamp_struct pointer. + * @return Elapsed time in milliseconds. + */ +float elapsed_time(struct timestamp_struct *ts){ + struct timeval now; + time_t secdiff; + long usecdiff; /* may be negative */ + + gettimeofday(&now,NULL); + secdiff=now.tv_sec - ts->timestamp.tv_sec; + usecdiff=now.tv_usec - ts->timestamp.tv_usec; + //printf("%d sec diff, %d usec diff\n",secdiff, usecdiff); + return (float) (secdiff*1000) + ((float)usecdiff)/1000; +} + +/** @internal + * @brief Calculates the RTT of the host with SSH channel operations, and + * returns the average of the calculated RTT. + * @param[in] session active SSH session to test. + * @param[out] average average RTT in milliseconds. + * @returns 0 on success, -1 if there is an error. + */ +int benchmarks_ssh_latency(ssh_session session, float *average){ + float times[3]; + struct timestamp_struct ts; + int i; + ssh_channel channel; + channel=ssh_channel_new(session); + if(channel==NULL) + goto error; + if(ssh_channel_open_session(channel)==SSH_ERROR) + goto error; + + for(i=0;i<3;++i){ + timestamp_init(&ts); + if(ssh_channel_request_env(channel,"TEST","test")==SSH_ERROR && + ssh_get_error_code(session)==SSH_FATAL) + goto error; + times[i]=elapsed_time(&ts); + } + ssh_channel_close(channel); + ssh_channel_free(channel); + channel=NULL; + printf("SSH request times : %f ms ; %f ms ; %f ms\n", times[0], times[1], times[2]); + *average=(times[0]+times[1]+times[2])/3; + return 0; +error: + fprintf(stderr,"Error calculating SSH latency : %s\n",ssh_get_error(session)); + if(channel) + ssh_channel_free(channel); + return -1; +} diff --git a/libssh/tests/chmodtest.c b/libssh/tests/chmodtest.c new file mode 100644 index 00000000..1e6f5112 --- /dev/null +++ b/libssh/tests/chmodtest.c @@ -0,0 +1,33 @@ +#include + +#include +#include "examples_common.h" +#include + +int main(void) { + ssh_session session; + sftp_session sftp; + char buffer[1024*1024]; + int rc; + + session = connect_ssh("localhost", NULL, 0); + if (session == NULL) { + return 1; + } + + sftp=sftp_new(session); + sftp_init(sftp); + rc=sftp_rename(sftp,"/tmp/test","/tmp/test"); + rc=sftp_rename(sftp,"/tmp/test","/tmp/test"); + rc=sftp_chmod(sftp,"/tmp/test",0644); + if (rc < 0) { + printf("error : %s\n",ssh_get_error(sftp)); + + ssh_disconnect(session); + return 1; + } + + ssh_disconnect(session); + + return 0; +} diff --git a/libssh/tests/client/CMakeLists.txt b/libssh/tests/client/CMakeLists.txt new file mode 100644 index 00000000..e3bb45d3 --- /dev/null +++ b/libssh/tests/client/CMakeLists.txt @@ -0,0 +1,12 @@ +project(clienttests C) + +add_cmocka_test(torture_algorithms torture_algorithms.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_auth torture_auth.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_connect torture_connect.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_knownhosts torture_knownhosts.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_proxycommand torture_proxycommand.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_session torture_session.c ${TORTURE_LIBRARY}) +if (WITH_SFTP) + add_cmocka_test(torture_sftp_static torture_sftp_static.c ${TORTURE_LIBRARY}) + add_cmocka_test(torture_sftp_dir torture_sftp_dir.c ${TORTURE_LIBRARY}) +endif (WITH_SFTP) diff --git a/libssh/tests/client/torture_algorithms.c b/libssh/tests/client/torture_algorithms.c new file mode 100644 index 00000000..180efb71 --- /dev/null +++ b/libssh/tests/client/torture_algorithms.c @@ -0,0 +1,245 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#define LIBSSH_STATIC + +#include "torture.h" +#include "libssh/libssh.h" +#include "libssh/priv.h" + + +static void setup(void **state) { + int verbosity=torture_libssh_verbosity(); + ssh_session session = ssh_new(); + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + *state = session; +} + +static void teardown(void **state) { + ssh_free(*state); +} + +static void test_algorithm(ssh_session session, const char *algo) { + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, algo); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, algo); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session, NULL); + if (rc != SSH_OK) { + rc = ssh_get_error_code(session); + assert_true(rc == SSH_REQUEST_DENIED); + } + + ssh_disconnect(session); +} + +static void torture_algorithms_aes128_cbc(void **state) { + test_algorithm(*state, "aes128-cbc"); +} + +static void torture_algorithms_aes192_cbc(void **state) { + test_algorithm(*state, "aes192-cbc"); +} + +static void torture_algorithms_aes256_cbc(void **state) { + test_algorithm(*state, "aes256-cbc"); +} + +static void torture_algorithms_aes128_ctr(void **state) { + test_algorithm(*state, "aes128-ctr"); +} + +static void torture_algorithms_aes192_ctr(void **state) { + test_algorithm(*state, "aes192-ctr"); +} + +static void torture_algorithms_aes256_ctr(void **state) { + test_algorithm(*state, "aes256-ctr"); +} + +static void torture_algorithms_3des_cbc(void **state) { + test_algorithm(*state, "3des-cbc"); +} + +static void torture_algorithms_blowfish_cbc(void **state) { + test_algorithm(*state, "blowfish-cbc"); +} + +static void torture_algorithms_zlib(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session,SSH_OPTIONS_HOST,"localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_C_S, "zlib"); +#ifdef WITH_ZLIB + assert_true(rc == SSH_OK); +#else + assert_true(rc == SSH_ERROR); +#endif + + rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_S_C, "zlib"); +#ifdef WITH_ZLIB + assert_true(rc == SSH_OK); +#else + assert_true(rc == SSH_ERROR); +#endif + + rc = ssh_connect(session); +#ifdef WITH_ZLIB + if (ssh_get_openssh_version(session)) { + assert_false(rc == SSH_OK); + ssh_disconnect(session); + return; + } +#endif + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session, NULL); + if (rc != SSH_OK) { + rc = ssh_get_error_code(session); + assert_true(rc == SSH_REQUEST_DENIED); + } + + ssh_disconnect(session); +} + +static void torture_algorithms_zlib_openssh(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session,SSH_OPTIONS_HOST,"localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_C_S, "zlib@openssh.com"); +#ifdef WITH_ZLIB + assert_true(rc == SSH_OK); +#else + assert_true(rc == SSH_ERROR); +#endif + + rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_S_C, "zlib@openssh.com"); +#ifdef WITH_ZLIB + assert_true(rc == SSH_OK); +#else + assert_true(rc == SSH_ERROR); +#endif + + rc = ssh_connect(session); +#ifdef WITH_ZLIB + if (ssh_get_openssh_version(session)) { + assert_true(rc==SSH_OK); + rc = ssh_userauth_none(session, NULL); + if (rc != SSH_OK) { + rc = ssh_get_error_code(session); + assert_true(rc == SSH_REQUEST_DENIED); + } + ssh_disconnect(session); + return; + } + assert_false(rc == SSH_OK); +#else + assert_true(rc == SSH_OK); +#endif + + ssh_disconnect(session); +} + +#if defined(HAVE_LIBCRYPTO) && defined(HAVE_ECC) +static void torture_algorithms_ecdh_sha2_nistp256(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session,SSH_OPTIONS_HOST,"localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "ecdh-sha2-nistp256"); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + rc = ssh_userauth_none(session, NULL); + if (rc != SSH_OK) { + rc = ssh_get_error_code(session); + assert_true(rc == SSH_REQUEST_DENIED); + } + + ssh_disconnect(session); +} +#endif + +static void torture_algorithms_dh_group1(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session,SSH_OPTIONS_HOST,"localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "diffie-hellman-group1-sha1"); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + rc = ssh_userauth_none(session, NULL); + if (rc != SSH_OK) { + rc = ssh_get_error_code(session); + assert_true(rc == SSH_REQUEST_DENIED); + } + + ssh_disconnect(session); +} +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_algorithms_aes128_cbc, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes192_cbc, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes256_cbc, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes128_ctr, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes192_ctr, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes256_ctr, setup, teardown), + unit_test_setup_teardown(torture_algorithms_3des_cbc, setup, teardown), + unit_test_setup_teardown(torture_algorithms_blowfish_cbc, setup, teardown), + unit_test_setup_teardown(torture_algorithms_zlib, setup, teardown), + unit_test_setup_teardown(torture_algorithms_zlib_openssh, setup, teardown), + unit_test_setup_teardown(torture_algorithms_dh_group1,setup,teardown), +#if defined(HAVE_LIBCRYPTO) && defined(HAVE_ECC) + unit_test_setup_teardown(torture_algorithms_ecdh_sha2_nistp256,setup,teardown) +#endif + }; + + ssh_init(); + + rc = run_tests(tests); + ssh_finalize(); + + return rc; +} diff --git a/libssh/tests/client/torture_auth.c b/libssh/tests/client/torture_auth.c new file mode 100644 index 00000000..83bbd406 --- /dev/null +++ b/libssh/tests/client/torture_auth.c @@ -0,0 +1,365 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#define LIBSSH_STATIC + +#include "torture.h" +#include "libssh/libssh.h" +#include "libssh/priv.h" +#include "libssh/session.h" +#include "agent.c" + +static void setup(void **state) { + int verbosity = torture_libssh_verbosity(); + ssh_session session = ssh_new(); + + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + + *state = session; +} + +static void teardown(void **state) { + ssh_disconnect(*state); + ssh_free(*state); +} + +static void torture_auth_autopubkey(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session,NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); + + rc = ssh_userauth_autopubkey(session, NULL); + assert_true(rc == SSH_AUTH_SUCCESS); +} + +static void torture_auth_autopubkey_nonblocking(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session,NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); + + ssh_set_blocking(session, 0); + do { + rc = ssh_userauth_autopubkey(session, NULL); + } while (rc == SSH_AUTH_AGAIN); + assert_true(rc == SSH_AUTH_SUCCESS); +} + +static void torture_auth_kbdint(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + char *password = getenv("TORTURE_PASSWORD"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + + if (password == NULL) { + print_message("*** Please set the environment variable " + "TORTURE_PASSWORD to enable this test!!\n"); + return; + } + + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session,NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_INTERACTIVE); + + rc = ssh_userauth_kbdint(session, NULL, NULL); + assert_true(rc == SSH_AUTH_INFO); + assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 1); + + rc = ssh_userauth_kbdint_setanswer(session, 0, password); + assert_false(rc < 0); + + rc = ssh_userauth_kbdint(session, NULL, NULL); + /* Sometimes, SSH server send an empty query at the end of exchange */ + if(rc == SSH_AUTH_INFO) { + assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 0); + rc = ssh_userauth_kbdint(session, NULL, NULL); + } + assert_true(rc == SSH_AUTH_SUCCESS); +} + +static void torture_auth_kbdint_nonblocking(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + char *password = getenv("TORTURE_PASSWORD"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + + if (password == NULL) { + print_message("*** Please set the environment variable " + "TORTURE_PASSWORD to enable this test!!\n"); + return; + } + + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session,NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_INTERACTIVE); + ssh_set_blocking(session,0); + do { + rc = ssh_userauth_kbdint(session, NULL, NULL); + } while (rc == SSH_AUTH_AGAIN); + assert_true(rc == SSH_AUTH_INFO); + assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 1); + do { + rc = ssh_userauth_kbdint_setanswer(session, 0, password); + } while (rc == SSH_AUTH_AGAIN); + assert_false(rc < 0); + + do { + rc = ssh_userauth_kbdint(session, NULL, NULL); + } while (rc == SSH_AUTH_AGAIN); + /* Sometimes, SSH server send an empty query at the end of exchange */ + if(rc == SSH_AUTH_INFO) { + assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 0); + do { + rc = ssh_userauth_kbdint(session, NULL, NULL); + } while (rc == SSH_AUTH_AGAIN); + } + assert_true(rc == SSH_AUTH_SUCCESS); +} + +static void torture_auth_password(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + char *password = getenv("TORTURE_PASSWORD"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + + if (password == NULL) { + print_message("*** Please set the environment variable " + "TORTURE_PASSWORD to enable this test!!\n"); + return; + } + + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session, NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_AUTH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PASSWORD); + + rc = ssh_userauth_password(session, NULL, password); + assert_true(rc == SSH_AUTH_SUCCESS); +} + +static void torture_auth_password_nonblocking(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + char *password = getenv("TORTURE_PASSWORD"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + + if (password == NULL) { + print_message("*** Please set the environment variable " + "TORTURE_PASSWORD to enable this test!!\n"); + return; + } + + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + ssh_set_blocking(session,0); + + do { + rc = ssh_userauth_none(session, NULL); + } while (rc==SSH_AUTH_AGAIN); + + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_AUTH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PASSWORD); + + do { + rc = ssh_userauth_password(session, NULL, password); + } while(rc==SSH_AUTH_AGAIN); + + assert_true(rc == SSH_AUTH_SUCCESS); +} + +static void torture_auth_agent(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + if (!agent_is_running(session)){ + print_message("*** Agent not running. Test ignored"); + return; + } + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session,NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); + + rc = ssh_userauth_agent(session, NULL); + assert_true(rc == SSH_AUTH_SUCCESS); +} + +static void torture_auth_agent_nonblocking(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + if (!agent_is_running(session)){ + print_message("*** Agent not running. Test ignored"); + return; + } + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session,NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); + ssh_set_blocking(session, 0); + do { + rc = ssh_userauth_agent(session, NULL); + } while (rc == SSH_AUTH_AGAIN); + assert_true(rc == SSH_AUTH_SUCCESS); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_auth_kbdint, setup, teardown), + unit_test_setup_teardown(torture_auth_kbdint_nonblocking, setup, teardown), + unit_test_setup_teardown(torture_auth_password, setup, teardown), + unit_test_setup_teardown(torture_auth_password_nonblocking, setup, teardown), + unit_test_setup_teardown(torture_auth_autopubkey, setup, teardown), + unit_test_setup_teardown(torture_auth_autopubkey_nonblocking, setup, teardown), + unit_test_setup_teardown(torture_auth_agent, setup, teardown), + unit_test_setup_teardown(torture_auth_agent_nonblocking, setup, teardown), + }; + + ssh_init(); + + rc = run_tests(tests); + ssh_finalize(); + + return rc; +} diff --git a/libssh/tests/client/torture_connect.c b/libssh/tests/client/torture_connect.c new file mode 100644 index 00000000..0e23fcd7 --- /dev/null +++ b/libssh/tests/client/torture_connect.c @@ -0,0 +1,132 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#define LIBSSH_STATIC + +#include "torture.h" +#include +#include + +#define HOST "localhost" +/* Should work until Apnic decides to assign it :) */ +#define BLACKHOLE "1.1.1.1" + +static void setup(void **state) { + int verbosity=torture_libssh_verbosity(); + ssh_session session = ssh_new(); + + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + + *state = session; +} + +static void teardown(void **state) { + ssh_session session = *state; + ssh_disconnect(session); + ssh_free(session); +} + +static void torture_connect_nonblocking(void **state) { + ssh_session session = *state; + + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, HOST); + assert_true(rc == SSH_OK); + ssh_set_blocking(session,0); + + do { + rc = ssh_connect(session); + assert_true(rc != SSH_ERROR); + } while(rc == SSH_AGAIN); + + assert_true(rc==SSH_OK); + +} + +static void torture_connect_timeout(void **state) { + ssh_session session = *state; + struct timeval before, after; + int rc; + long timeout = 2; + time_t sec; + suseconds_t usec; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, BLACKHOLE); + assert_true(rc == SSH_OK); + rc = ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &timeout); + assert_true(rc == SSH_OK); + + rc = gettimeofday(&before, NULL); + assert_true(rc == 0); + rc = ssh_connect(session); + assert_true(rc == SSH_ERROR); + rc = gettimeofday(&after, NULL); + assert_true(rc == 0); + sec = after.tv_sec - before.tv_sec; + usec = after.tv_usec - before.tv_usec; + /* Borrow a second for the missing usecs, but don't bother calculating */ + if(usec < 0) + sec--; + assert_in_range(sec,1,3); +} + +static void torture_connect_double(void **state) { + ssh_session session = *state; + + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, HOST); + assert_true(rc == SSH_OK); + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + ssh_disconnect(session); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + +} + +static void torture_connect_failure(void **state){ + /* + * The intent of this test is to check that a fresh + * ssh_new/ssh_disconnect/ssh_free sequence doesn't crash/leak + * and the behavior of a double ssh_disconnect + */ + ssh_session session = *state; + ssh_disconnect(session); +} +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_connect_nonblocking, setup, teardown), + unit_test_setup_teardown(torture_connect_double, setup, teardown), + unit_test_setup_teardown(torture_connect_failure, setup, teardown), + unit_test_setup_teardown(torture_connect_timeout, setup, teardown), + }; + + ssh_init(); + + rc = run_tests(tests); + + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/client/torture_knownhosts.c b/libssh/tests/client/torture_knownhosts.c new file mode 100644 index 00000000..fe827cbc --- /dev/null +++ b/libssh/tests/client/torture_knownhosts.c @@ -0,0 +1,108 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2010 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#define LIBSSH_STATIC + +#include "torture.h" +#include "session.c" + +#define KNOWNHOSTFILES "libssh_torture_knownhosts" + +static void setup(void **state) { + int verbosity=torture_libssh_verbosity(); + ssh_session session = ssh_new(); + + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + + *state = session; +} + +static void teardown(void **state) { + ssh_session session = *state; + + ssh_disconnect(session); + ssh_free(session); + + unlink(KNOWNHOSTFILES); +} + +static void torture_knownhosts_port(void **state) { + ssh_session session = *state; + char buffer[200]; + char *p; + FILE *file; + int rc; + + /* Connect to localhost:22, force the port to 1234 and then write + * the known hosts file. Then check that the entry written is + * [localhost]:1234 + */ + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc==SSH_OK); + + session->opts.port = 1234; + rc = ssh_write_knownhost(session); + assert_true(rc == SSH_OK); + + file = fopen(KNOWNHOSTFILES, "r"); + assert_true(file != NULL); + p = fgets(buffer, sizeof(buffer), file); + assert_false(p == NULL); + fclose(file); + buffer[sizeof(buffer) - 1] = '\0'; + assert_true(strstr(buffer,"[localhost]:1234 ") != NULL); + + ssh_disconnect(session); + ssh_free(session); + + /* Now, connect back to the ssh server and verify the known host line */ + *state = session = ssh_new(); + + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + session->opts.port = 1234; + rc = ssh_is_server_known(session); + assert_true(rc == SSH_SERVER_KNOWN_OK); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_knownhosts_port, setup, teardown), + }; + + ssh_init(); + + rc = run_tests(tests); + + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/client/torture_proxycommand.c b/libssh/tests/client/torture_proxycommand.c new file mode 100644 index 00000000..8cf68685 --- /dev/null +++ b/libssh/tests/client/torture_proxycommand.c @@ -0,0 +1,57 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#include +#include "libssh/priv.h" + +static void setup(void **state) { + ssh_session session = ssh_new(); + + *state = session; +} + +static void teardown(void **state) { + ssh_free(*state); +} + +static void torture_options_set_proxycommand(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == 0); + + rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, "nc localhost 22"); + assert_true(rc == 0); + rc = ssh_connect(session); + assert_true(rc == SSH_OK); +} + +static void torture_options_set_proxycommand_notexist(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == 0); + + rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, "this_command_does_not_exist"); + assert_true(rc == SSH_OK); + rc = ssh_connect(session); + assert_true(rc == SSH_ERROR); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_options_set_proxycommand, setup, teardown), + unit_test_setup_teardown(torture_options_set_proxycommand_notexist, setup, teardown), + }; + + + ssh_init(); + + rc = run_tests(tests); + ssh_finalize(); + + return rc; +} diff --git a/libssh/tests/client/torture_session.c b/libssh/tests/client/torture_session.c new file mode 100644 index 00000000..d66901da --- /dev/null +++ b/libssh/tests/client/torture_session.c @@ -0,0 +1,108 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2012 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#define LIBSSH_STATIC + +#include "torture.h" +#include "libssh/libssh.h" +#include "libssh/priv.h" +#include "libssh/session.h" + +#define BUFLEN 4096 +static char buffer[BUFLEN]; + +static void setup(void **state) { + int verbosity = torture_libssh_verbosity(); + ssh_session session = ssh_new(); + + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + + *state = session; +} + +static void teardown(void **state) { + ssh_disconnect(*state); + ssh_free(*state); +} + +static void torture_channel_read_error(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + ssh_channel channel; + int rc; + int i; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session,NULL); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); + + rc = ssh_userauth_autopubkey(session, NULL); + assert_true(rc == SSH_AUTH_SUCCESS); + + channel = ssh_channel_new(session); + assert_true(channel != NULL); + + rc = ssh_channel_open_session(channel); + assert_true(rc == SSH_OK); + + rc = ssh_channel_request_exec(channel, "hexdump -C /dev/urandom"); + assert_true(rc == SSH_OK); + + /* send crap and for server to send us a disconnect */ + write(ssh_get_fd(session),"AAAA", 4); + + for (i=0;i<20;++i){ + rc = ssh_channel_read(channel,buffer,sizeof(buffer),0); + if (rc == SSH_ERROR) + break; + } + assert_true(rc == SSH_ERROR); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_channel_read_error, setup, teardown), + }; + + ssh_init(); + + rc = run_tests(tests); + ssh_finalize(); + + return rc; +} diff --git a/libssh/tests/client/torture_sftp_dir.c b/libssh/tests/client/torture_sftp_dir.c new file mode 100644 index 00000000..8d2bcda8 --- /dev/null +++ b/libssh/tests/client/torture_sftp_dir.c @@ -0,0 +1,74 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#include "sftp.c" + +static void setup(void **state) { + ssh_session session; + struct torture_sftp *t; + const char *host; + const char *user; + const char *password; + + host = getenv("TORTURE_HOST"); + if (host == NULL) { + host = "localhost"; + } + + user = getenv("TORTURE_USER"); + password = getenv("TORTURE_PASSWORD"); + + session = torture_ssh_session(host, user, password); + assert_false(session == NULL); + t = torture_sftp_session(session); + assert_false(t == NULL); + + *state = t; +} + +static void teardown(void **state) { + struct torture_sftp *t = *state; + + assert_false(t == NULL); + + torture_rmdirs(t->testdir); + torture_sftp_close(t); +} + +static void torture_sftp_mkdir(void **state) { + struct torture_sftp *t = *state; + char tmpdir[128] = {0}; + int rc; + + assert_false(t == NULL); + + snprintf(tmpdir, sizeof(tmpdir) - 1, "%s/mkdir_test", t->testdir); + + rc = sftp_mkdir(t->sftp, tmpdir, 0755); + if(rc != SSH_OK) + fprintf(stderr,"error:%s\n",ssh_get_error(t->sftp->session)); + assert_true(rc == 0); + + /* check if it really has been created */ + assert_true(torture_isdir(tmpdir)); + + rc = sftp_rmdir(t->sftp, tmpdir); + assert_true(rc == 0); + + /* check if it has been deleted */ + assert_false(torture_isdir(tmpdir)); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_sftp_mkdir, setup, teardown) + }; + + ssh_init(); + + rc = run_tests(tests); + ssh_finalize(); + + return rc; +} diff --git a/libssh/tests/client/torture_sftp_static.c b/libssh/tests/client/torture_sftp_static.c new file mode 100644 index 00000000..7631def0 --- /dev/null +++ b/libssh/tests/client/torture_sftp_static.c @@ -0,0 +1,32 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#include "sftp.c" + +static void torture_sftp_ext_new(void **state) { + sftp_ext x; + + (void) state; + + x = sftp_ext_new(); + assert_false(x == NULL); + assert_int_equal(x->count, 0); + assert_true(x->name == NULL); + assert_true(x->data == NULL); + + sftp_ext_free(x); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test(torture_sftp_ext_new), + }; + + ssh_init(); + + rc = run_tests(tests); + ssh_finalize(); + + return rc; +} diff --git a/libssh/tests/cmdline.c b/libssh/tests/cmdline.c new file mode 100644 index 00000000..4e2a7d02 --- /dev/null +++ b/libssh/tests/cmdline.c @@ -0,0 +1,71 @@ +#include "config.h" +#include "torture.h" + +#ifdef HAVE_ARGP_H +#include + +const char *argp_program_version = "libssh test 0.2"; +const char *argp_program_bug_address = ""; + +static char **cmdline; + +/* Program documentation. */ +static char doc[] = "libssh test test"; + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Make libssh test more verbose", + .group = 0 + }, + {NULL, 0, NULL, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + struct argument_s *arguments = state->input; + + /* arg is currently not used */ + (void) arg; + + switch (key) { + case 'v': + arguments->verbose++; + break; + case ARGP_KEY_ARG: + /* End processing here. */ + cmdline = &state->argv [state->next - 1]; + state->next = state->argc; + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Our argp parser. */ +/* static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; */ +static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL}; +#endif /* HAVE_ARGP_H */ + +void torture_cmdline_parse(int argc, char **argv, struct argument_s *arguments) { + /* + * Parse our arguments; every option seen by parse_opt will + * be reflected in arguments. + */ +#ifdef HAVE_ARGP_H + argp_parse(&argp, argc, argv, 0, 0, arguments); +#else + (void) argc; + (void) argv; + (void) arguments; +#endif /* HAVE_ARGP_H */ +} diff --git a/libssh/tests/connection.c b/libssh/tests/connection.c new file mode 100644 index 00000000..889c5117 --- /dev/null +++ b/libssh/tests/connection.c @@ -0,0 +1,31 @@ +/* +This file is distributed in public domain. You can do whatever you want +with its content. +*/ + +#include +#include +#include "tests.h" +SSH_OPTIONS *set_opts(int argc, char **argv){ + SSH_OPTIONS *options=ssh_options_new(); + char *host=NULL; + if(ssh_options_getopt(options,&argc, argv)){ + fprintf(stderr,"error parsing command line :%s\n",ssh_get_error(options)); + return NULL; + } + int i; + while((i=getopt(argc,argv,""))!=-1){ + switch(i){ + default: + fprintf(stderr,"unknown option %c\n",optopt); + } + } + if(optind < argc) + host=argv[optind++]; + if(host==NULL){ + fprintf(stderr,"must provide an host name\n"); + return NULL; + } + ssh_options_set_host(options,host); + return options; +} diff --git a/libssh/tests/ctest-default.cmake b/libssh/tests/ctest-default.cmake new file mode 100644 index 00000000..a8a3e211 --- /dev/null +++ b/libssh/tests/ctest-default.cmake @@ -0,0 +1,71 @@ +## The directory to run ctest in. +set(CTEST_DIRECTORY "$ENV{HOME}/workspace/tmp/dashboards/libssh") + +## The hostname of the machine +set(CTEST_SITE "host.libssh.org") +## The buildname +set(CTEST_BUILD_NAME "Linux_2.6-GCC_4.5-x86_64-default") + +## The Makefile generator to use +set(CTEST_CMAKE_GENERATOR "Unix Makefiles") + +## The Build configuration to use. +set(CTEST_BUILD_CONFIGURATION "Debug") + +## The build options for the project +set(CTEST_BUILD_OPTIONS "-DWITH_TESTING=ON -DWITH_SSH1=ON -WITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DDEBUG_CRYPTO=ON -DWITH_GCRYPT=OFF") + +#set(CTEST_CUSTOM_MEMCHECK_IGNORE torture_rand) + +## The Model to set: Nightly, Continous, Experimental +set(CTEST_MODEL "Experimental") + +## The branch +#set(CTEST_GIT_BRANCH "--branch v0-5") + +## Wether to enable memory checking. +set(WITH_MEMCHECK FALSE) + +## Wether to enable code coverage. +set(WITH_COVERAGE FALSE) + +####################################################################### + +if (WITH_COVERAGE AND NOT WIN32) + set(CTEST_BUILD_CONFIGURATION "Profiling") +endif (WITH_COVERAGE AND NOT WIN32) + +set(CTEST_SOURCE_DIRECTORY "${CTEST_DIRECTORY}/${CTEST_BUILD_NAME}/source") +set(CTEST_BINARY_DIRECTORY "${CTEST_DIRECTORY}/${CTEST_BUILD_NAME}/build") + +set(CTEST_MEMORYCHECK_SUPPRESSIONS_FILE ${CMAKE_SOURCE_DIR}/tests/valgrind.supp) + +find_program(CTEST_GIT_COMMAND NAMES git) +find_program(CTEST_COVERAGE_COMMAND NAMES gcov) +find_program(CTEST_MEMORYCHECK_COMMAND NAMES valgrind) + +if(NOT EXISTS "${CTEST_SOURCE_DIRECTORY}") + set(CTEST_CHECKOUT_COMMAND "${CTEST_GIT_COMMAND} clone ${CTEST_GIT_BRANCH} git://git.libssh.org/projects/libssh.git ${CTEST_SOURCE_DIRECTORY}") +endif() + +set(CTEST_UPDATE_COMMAND "${CTEST_GIT_COMMAND}") + +set(CTEST_CONFIGURE_COMMAND "${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE:STRING=${CTEST_BUILD_CONFIGURATION}") +set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} -DWITH_TESTING:BOOL=ON ${CTEST_BUILD_OPTIONS}") +set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} \"-G${CTEST_CMAKE_GENERATOR}\"") +set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} \"${CTEST_SOURCE_DIRECTORY}\"") + +ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY}) + +ctest_start(${CTEST_MODEL} TRACK ${CTEST_MODEL}) +ctest_update(SOURCE ${CTEST_SOURCE_DIRECTORY}) +ctest_configure(BUILD ${CTEST_BINARY_DIRECTORY}) +ctest_build(BUILD ${CTEST_BINARY_DIRECTORY}) +ctest_test(BUILD ${CTEST_BINARY_DIRECTORY}) +if (WITH_MEMCHECK AND CTEST_COVERAGE_COMMAND) + ctest_coverage(BUILD ${CTEST_BINARY_DIRECTORY}) +endif (WITH_MEMCHECK AND CTEST_COVERAGE_COMMAND) +if (WITH_MEMCHECK AND CTEST_MEMORYCHECK_COMMAND) + ctest_memcheck(BUILD ${CTEST_BINARY_DIRECTORY}) +endif (WITH_MEMCHECK AND CTEST_MEMORYCHECK_COMMAND) +ctest_submit() diff --git a/libssh/tests/generate.py b/libssh/tests/generate.py new file mode 100755 index 00000000..08c2d5b1 --- /dev/null +++ b/libssh/tests/generate.py @@ -0,0 +1,10 @@ +#!/usr/bin/python +import os +a="" +for i in xrange(4096): + a+=chr(i % 256); +while True: + try: + os.write(1,a) + except: + exit(0) diff --git a/libssh/tests/sftp_stress/main.c b/libssh/tests/sftp_stress/main.c new file mode 100644 index 00000000..c9b0ad9f --- /dev/null +++ b/libssh/tests/sftp_stress/main.c @@ -0,0 +1,174 @@ +/* + * main.c + * + * Created on: 22 juin 2009 + * Author: aris + */ +#include +#include +#include +#include +#include +#include +#include +#include +#define TEST_READ 1 +#define TEST_WRITE 2 +#define NTHREADS 3 +#define FILESIZE 100000 +unsigned char samplefile[FILESIZE]; +volatile int stop=0; + +const char* hosts[]={"localhost","barebone"}; +void signal_stop(){ + stop=1; + printf("Stopping...\n"); +} + +SSH_SESSION *connect_host(const char *hostname); +int sftp_test(SSH_SESSION *session, int test); + +int docycle(const char *host, int test){ + SSH_SESSION *session=connect_host(host); + int ret=SSH_ERROR; + if(!session){ + printf("connect failed\n"); + } else { + printf("Connected\n"); + ret=sftp_test(session,test); + if(ret != SSH_OK){ + printf("Error in sftp\n"); + } + ssh_disconnect(session); + } + return ret; +} + +int thread(){ + while(docycle(hosts[rand()%2],TEST_WRITE) == SSH_OK) + if(stop) + break; + return 0; +} + +int main(int argc, char **argv){ + int i; + pthread_t threads[NTHREADS]; + ssh_init(); + srand(time(NULL)); + for(i=0;i +#include +#include +#include "tests.h" + +void do_connect(SSH_SESSION *session) { + char buf[4096] = {0}; + CHANNEL *channel; + + int error = ssh_connect(session); + if (error != SSH_OK) { + fprintf(stderr,"Error at connection: %s\n", ssh_get_error(session)); + return; + } + printf("Connected\n"); + + ssh_is_server_known(session); + + error = authenticate(session); + if(error != SSH_AUTH_SUCCESS) { + fprintf(stderr,"Error at authentication: %s\n", ssh_get_error(session)); + return; + } + printf("Authenticated\n"); + channel = ssh_channel_new(session); + ssh_channel_open_session(channel); + printf("Execute 'ls' on the channel\n"); + error = ssh_channel_request_exec(channel, "ls"); + if(error != SSH_OK){ + fprintf(stderr, "Error executing command: %s\n", ssh_get_error(session)); + return; + } + printf("--------------------output----------------------\n"); + while (ssh_channel_read(channel, buf, sizeof(buf), 0)) { + printf("%s", buf); + } + printf("\n"); + printf("---------------------end------------------------\n"); + ssh_channel_send_eof(channel); + fprintf(stderr, "Exit status: %d\n", ssh_channel_get_exit_status(channel)); + + printf("\nChannel test finished\n"); + ssh_channel_close(channel); + ssh_channel_free(channel); +} + +int main(int argc, char **argv){ + SSH_OPTIONS *options=set_opts(argc, argv); + SSH_SESSION *session=ssh_new(); + if(options==NULL){ + return 1; + } + ssh_set_options(session,options); + do_connect(session); + ssh_disconnect(session); + ssh_finalize(); + return 0; +} diff --git a/libssh/tests/test_pcap.c b/libssh/tests/test_pcap.c new file mode 100644 index 00000000..01aa714a --- /dev/null +++ b/libssh/tests/test_pcap.c @@ -0,0 +1,50 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* Simple test for the pcap functions */ + +#include +#include +#include + +#include +#include +#include + +int main(int argc, char **argv){ + ssh_pcap_file pcap; + ssh_pcap_context ctx; + ssh_buffer buffer=ssh_buffer_new(); + char *str="Hello, this is a test string to test the capabilities of the" + "pcap file writer."; + printf("Simple pcap tester\n"); + pcap=ssh_pcap_file_new(); + if(ssh_pcap_file_open(pcap,"test.cap") != SSH_OK){ + printf("error happened\n"); + return EXIT_FAILURE; + } + buffer_add_data(buffer,str,strlen(str)); + ctx=ssh_pcap_context_new(NULL); + ssh_pcap_context_set_file(ctx,pcap); + ssh_pcap_context_write(ctx,SSH_PCAP_DIR_OUT,str,strlen(str),strlen(str)); + + return EXIT_SUCCESS; +} diff --git a/libssh/tests/test_socket.c b/libssh/tests/test_socket.c new file mode 100644 index 00000000..84f7b35e --- /dev/null +++ b/libssh/tests/test_socket.c @@ -0,0 +1,93 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* Simple test for the socket callbacks */ + +#include +#include +#include +#include + +#include +#include +#include + +int stop=0; +ssh_socket s; + +static int data_rcv(const void *data, size_t len, void *user){ + printf("Received data: '"); + fwrite(data,1,len,stdout); + printf("'\n"); + ssh_socket_write(s,"Hello you !\n",12); + ssh_socket_nonblocking_flush(s); + return len; +} + +static void controlflow(int code,void *user){ + printf("Control flow: %x\n",code); +} + +static void exception(int code, int errno_code,void *user){ + printf("Exception: %d (%d)\n",code,errno_code); + stop=1; +} + +static void connected(int code, int errno_code,void *user){ + if(code == SSH_SOCKET_CONNECTED_OK) + printf("Connected: %d (%d)\n",code, errno_code); + else { + printf("Error while connecting:(%d, %d:%s)\n",code,errno_code,strerror(errno_code)); + stop=1; + } +} + +struct ssh_socket_callbacks_struct callbacks={ + data_rcv, + controlflow, + exception, + connected, + NULL +}; +int main(int argc, char **argv){ + ssh_session session; + ssh_poll_ctx ctx; + int verbosity=SSH_LOG_FUNCTIONS; + if(argc < 3){ + printf("Usage : %s host port\n", argv[0]); + return EXIT_FAILURE; + } + session=ssh_new(); + ssh_options_set(session,SSH_OPTIONS_LOG_VERBOSITY,&verbosity); + ssh_init(); + s=ssh_socket_new(session); + ctx=ssh_poll_ctx_new(2); + ssh_socket_set_callbacks(s, &callbacks); + ssh_poll_ctx_add_socket(ctx,s); + if(ssh_socket_connect(s,argv[1],atoi(argv[2]),NULL) != SSH_OK){ + printf("ssh_socket_connect: %s\n",ssh_get_error(session)); + return EXIT_FAILURE; + } + while(!stop) + ssh_poll_ctx_dopoll(ctx,-1); + printf("finished\n"); + return EXIT_SUCCESS; +} diff --git a/libssh/tests/test_tunnel.c b/libssh/tests/test_tunnel.c new file mode 100644 index 00000000..27f667b7 --- /dev/null +++ b/libssh/tests/test_tunnel.c @@ -0,0 +1,76 @@ +/* +This file is distributed in public domain. You can do whatever you want +with its content. +*/ +#include +#include +#include +#include "tests.h" +#define ECHO_PORT 7 +void do_connect(SSH_SESSION *session){ + int error=ssh_connect(session); + if(error != SSH_OK){ + fprintf(stderr,"Error at connection :%s\n",ssh_get_error(session)); + return; + } + printf("Connected\n"); + ssh_is_server_known(session); + // we don't care what happens here + error=authenticate(session); + if(error != SSH_AUTH_SUCCESS){ + fprintf(stderr,"Error at authentication :%s\n",ssh_get_error(session)); + return; + } + printf("Authenticated\n"); + CHANNEL *channel=ssh_channel_new(session); + error=ssh_channel_open_forward(channel,"localhost",ECHO_PORT,"localhost",42); + if(error!=SSH_OK){ + fprintf(stderr,"Error when opening forward:%s\n",ssh_get_error(session)); + return; + } + printf("Forward opened\n"); + int i=0; + char string[20]; + char buffer[20]; + for(i=0;i<2000;++i){ + sprintf(string,"%d\n",i); + ssh_channel_write(channel,string,strlen(string)); + do { + error=ssh_channel_poll(channel,0); + //if(error < strlen(string)) + //usleep(10); + } while(error < strlen(string) && error >= 0); + if(error>0){ + error=ssh_channel_read_nonblocking(channel,buffer,strlen(string),0); + if(error>=0){ + if(memcmp(buffer,string,strlen(string))!=0){ + fprintf(stderr,"Problem with answer: wanted %s got %s\n",string,buffer); + } else { + printf("."); + fflush(stdout); + } + } + + } + if(error==-1){ + fprintf(stderr,"Channel reading error : %s\n",ssh_get_error(session)); + break; + } + } + printf("\nChannel test finished\n"); + ssh_channel_close(channel); + ssh_channel_free(channel); +} + +int main(int argc, char **argv){ + SSH_OPTIONS *options=set_opts(argc, argv); + SSH_SESSION *session=ssh_new(); + if(options==NULL){ + return 1; + } + ssh_set_options(session,options); + do_connect(session); + ssh_disconnect(session); + ssh_finalize(); + return 0; +} diff --git a/libssh/tests/tests.h b/libssh/tests/tests.h new file mode 100644 index 00000000..dd001f1f --- /dev/null +++ b/libssh/tests/tests.h @@ -0,0 +1,8 @@ +/* +This file is distributed in public domain. You can do whatever you want +with its content. +*/ +#include +int authenticate (SSH_SESSION *session); +SSH_OPTIONS *set_opts(int argc, char **argv); + diff --git a/libssh/tests/torture.c b/libssh/tests/torture.c new file mode 100644 index 00000000..a75b0a94 --- /dev/null +++ b/libssh/tests/torture.c @@ -0,0 +1,316 @@ +/* + * torture.c - torture library for testing libssh + * + * This file is part of the SSH Library + * + * Copyright (c) 2008-2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#ifndef _WIN32 +# include +# include +# include +# include +# include +#endif + +#include "torture.h" + +static int verbosity = 0; + +#ifndef _WIN32 +static int _torture_auth_kbdint(ssh_session session, + const char *password) { + const char *prompt; + char echo; + int err; + + if (session == NULL || password == NULL) { + return SSH_AUTH_ERROR; + } + + err = ssh_userauth_kbdint(session, NULL, NULL); + if (err == SSH_AUTH_ERROR) { + return err; + } + + if (ssh_userauth_kbdint_getnprompts(session) != 1) { + return SSH_AUTH_ERROR; + } + + prompt = ssh_userauth_kbdint_getprompt(session, 0, &echo); + if (prompt == NULL) { + return SSH_AUTH_ERROR; + } + + if (ssh_userauth_kbdint_setanswer(session, 0, password) < 0) { + return SSH_AUTH_ERROR; + } + err = ssh_userauth_kbdint(session, NULL, NULL); + if (err == SSH_AUTH_INFO) { + if (ssh_userauth_kbdint_getnprompts(session) != 0) { + return SSH_AUTH_ERROR; + } + err = ssh_userauth_kbdint(session, NULL, NULL); + } + + return err; +} + +int torture_rmdirs(const char *path) { + DIR *d; + struct dirent *dp; + struct stat sb; + char *fname; + + if ((d = opendir(path)) != NULL) { + while(stat(path, &sb) == 0) { + /* if we can remove the directory we're done */ + if (rmdir(path) == 0) { + break; + } + switch (errno) { + case ENOTEMPTY: + case EEXIST: + case EBADF: + break; /* continue */ + default: + closedir(d); + return 0; + } + + while ((dp = readdir(d)) != NULL) { + size_t len; + /* skip '.' and '..' */ + if (dp->d_name[0] == '.' && + (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) { + continue; + } + + len = strlen(path) + strlen(dp->d_name) + 2; + fname = malloc(len); + if (fname == NULL) { + return -1; + } + snprintf(fname, len, "%s/%s", path, dp->d_name); + + /* stat the file */ + if (lstat(fname, &sb) != -1) { + if (S_ISDIR(sb.st_mode) && !S_ISLNK(sb.st_mode)) { + if (rmdir(fname) < 0) { /* can't be deleted */ + if (errno == EACCES) { + closedir(d); + SAFE_FREE(fname); + return -1; + } + torture_rmdirs(fname); + } + } else { + unlink(fname); + } + } /* lstat */ + SAFE_FREE(fname); + } /* readdir */ + + rewinddir(d); + } + } else { + return -1; + } + + closedir(d); + return 0; +} + +int torture_isdir(const char *path) { + struct stat sb; + + if (lstat (path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + return 1; + } + + return 0; +} + +ssh_session torture_ssh_session(const char *host, + const char *user, + const char *password) { + ssh_session session; + int method; + int rc; + + if (host == NULL) { + return NULL; + } + + session = ssh_new(); + if (session == NULL) { + return NULL; + } + + if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) { + goto failed; + } + + if (user != NULL) { + if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) { + goto failed; + } + } + + if (ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity) < 0) { + goto failed; + } + + if (ssh_connect(session)) { + goto failed; + } + + /* We are in testing mode, so consinder the hostkey as verified ;) */ + + /* This request should return a SSH_REQUEST_DENIED error */ + rc = ssh_userauth_none(session, NULL); + if (rc == SSH_ERROR) { + goto failed; + } + method = ssh_userauth_list(session, NULL); + if (method == 0) { + goto failed; + } + + if (password != NULL) { + if (method & SSH_AUTH_METHOD_INTERACTIVE) { + rc = _torture_auth_kbdint(session, password); + } else if (method & SSH_AUTH_METHOD_PASSWORD) { + rc = ssh_userauth_password(session, NULL, password); + } + } else { + rc = ssh_userauth_publickey_auto(session, NULL, NULL); + if (rc == SSH_AUTH_ERROR) { + goto failed; + } + } + if (rc != SSH_AUTH_SUCCESS) { + goto failed; + } + + return session; +failed: + if (ssh_is_connected(session)) { + ssh_disconnect(session); + } + ssh_free(session); + + return NULL; +} + +#ifdef WITH_SFTP + +struct torture_sftp *torture_sftp_session(ssh_session session) { + struct torture_sftp *t; + char template[] = "/tmp/ssh_torture_XXXXXX"; + char *p; + int rc; + + if (session == NULL) { + return NULL; + } + + t = malloc(sizeof(struct torture_sftp)); + if (t == NULL) { + return NULL; + } + + t->ssh = session; + t->sftp = sftp_new(session); + if (t->sftp == NULL) { + goto failed; + } + + rc = sftp_init(t->sftp); + if (rc < 0) { + goto failed; + } + + p = mkdtemp(template); + if (p == NULL) { + goto failed; + } + /* useful if TESTUSER is not the local user */ + chmod(template,0777); + t->testdir = strdup(p); + if (t->testdir == NULL) { + goto failed; + } + + return t; +failed: + if (t->sftp != NULL) { + sftp_free(t->sftp); + } + ssh_disconnect(t->ssh); + ssh_free(t->ssh); + free(t); + + return NULL; +} + +void torture_sftp_close(struct torture_sftp *t) { + if (t == NULL) { + return; + } + + if (t->sftp != NULL) { + sftp_free(t->sftp); + } + + if (t->ssh != NULL) { + if (ssh_is_connected(t->ssh)) { + ssh_disconnect(t->ssh); + } + ssh_free(t->ssh); + } + + free(t->testdir); + free(t); +} +#endif /* WITH_SFTP */ + +#endif /* _WIN32 */ + + +int torture_libssh_verbosity(void){ + return verbosity; +} + +int main(int argc, char **argv) { + struct argument_s arguments; + + arguments.verbose=0; + torture_cmdline_parse(argc, argv, &arguments); + verbosity=arguments.verbose; + + return torture_run_tests(); +} + +/* vim: set ts=4 sw=4 et cindent syntax=c.doxygen: */ diff --git a/libssh/tests/torture.h b/libssh/tests/torture.h new file mode 100644 index 00000000..dbc1906a --- /dev/null +++ b/libssh/tests/torture.h @@ -0,0 +1,76 @@ +/* + * torture.c - torture library for testing libssh + * + * This file is part of the SSH Library + * + * Copyright (c) 2008-2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef _TORTURE_H +#define _TORTURE_H + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + +#include "libssh/priv.h" +#include "libssh/sftp.h" + +#include + +/* Used by main to communicate with parse_opt. */ +struct argument_s { + char *args[2]; + int verbose; +}; + +struct torture_sftp { + ssh_session ssh; + sftp_session sftp; + char *testdir; +}; + +void torture_cmdline_parse(int argc, char **argv, struct argument_s *arguments); + +int torture_rmdirs(const char *path); +int torture_isdir(const char *path); + +/* + * Returns the verbosity level asked by user + */ +int torture_libssh_verbosity(void); + +ssh_session torture_ssh_session(const char *host, + const char *user, + const char *password); + +struct torture_sftp *torture_sftp_session(ssh_session session); +void torture_sftp_close(struct torture_sftp *t); + +/* + * This function must be defined in every unit test file. + */ +int torture_run_tests(void); + +#endif /* _TORTURE_H */ diff --git a/libssh/tests/unittests/CMakeLists.txt b/libssh/tests/unittests/CMakeLists.txt new file mode 100644 index 00000000..d8a6125f --- /dev/null +++ b/libssh/tests/unittests/CMakeLists.txt @@ -0,0 +1,16 @@ +project(unittests C) + +add_cmocka_test(torture_buffer torture_buffer.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_callbacks torture_callbacks.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_init torture_init.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_list torture_list.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_misc torture_misc.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_options torture_options.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_isipaddr torture_isipaddr.c ${TORTURE_LIBRARY}) +if (UNIX AND NOT WIN32) + # requires ssh-keygen + add_cmocka_test(torture_keyfiles torture_keyfiles.c ${TORTURE_LIBRARY}) + add_cmocka_test(torture_pki torture_pki.c ${TORTURE_LIBRARY}) + # requires pthread + add_cmocka_test(torture_rand torture_rand.c ${TORTURE_LIBRARY}) +endif (UNIX AND NOT WIN32) diff --git a/libssh/tests/unittests/torture_buffer.c b/libssh/tests/unittests/torture_buffer.c new file mode 100644 index 00000000..511cdf45 --- /dev/null +++ b/libssh/tests/unittests/torture_buffer.c @@ -0,0 +1,132 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#define DEBUG_BUFFER +#include "buffer.c" + +#define LIMIT (8*1024*1024) + +static void setup(void **state) { + ssh_buffer buffer; + buffer = ssh_buffer_new(); + *state = (void *) buffer; +} + +static void teardown(void **state) { + ssh_buffer_free(*state); +} + +/* + * Test if the continuously growing buffer size never exceeds 2 time its + * real capacity + */ +static void torture_growing_buffer(void **state) { + ssh_buffer buffer = *state; + int i; + + for(i=0;iused >= 128){ + if(buffer_get_rest_len(buffer) * 2 < buffer->allocated){ + assert_true(buffer_get_rest_len(buffer) * 2 >= buffer->allocated); + } + } + } +} + +/* + * Test if the continuously growing buffer size never exceeds 2 time its + * real capacity, when we remove 1 byte after each call (sliding window) + */ +static void torture_growing_buffer_shifting(void **state) { + ssh_buffer buffer = *state; + int i; + unsigned char c; + for(i=0; i<1024;++i){ + buffer_add_data(buffer,"S",1); + } + for(i=0;iused >= 128){ + if(buffer_get_rest_len(buffer) * 4 < buffer->allocated){ + assert_true(buffer_get_rest_len(buffer) * 4 >= buffer->allocated); + return; + } + } + } +} + +/* + * Test the behavior of buffer_prepend_data + */ +static void torture_buffer_prepend(void **state) { + ssh_buffer buffer = *state; + uint32_t v; + buffer_add_data(buffer,"abcdef",6); + buffer_prepend_data(buffer,"xyz",3); + assert_int_equal(buffer_get_rest_len(buffer),9); + assert_int_equal(memcmp(buffer_get_rest(buffer), "xyzabcdef", 9), 0); +// Now remove 4 bytes and see if we can replace them + buffer_get_u32(buffer,&v); + assert_int_equal(buffer_get_rest_len(buffer),5); + assert_int_equal(memcmp(buffer_get_rest(buffer), "bcdef", 5), 0); + buffer_prepend_data(buffer,"aris",4); + assert_int_equal(buffer_get_rest_len(buffer),9); + assert_int_equal(memcmp(buffer_get_rest(buffer), "arisbcdef", 9), 0); + /* same thing but we add 5 bytes now */ + buffer_get_u32(buffer,&v); + assert_int_equal(buffer_get_rest_len(buffer),5); + assert_int_equal(memcmp(buffer_get_rest(buffer), "bcdef", 5), 0); + buffer_prepend_data(buffer,"12345",5); + assert_int_equal(buffer_get_rest_len(buffer),10); + assert_int_equal(memcmp(buffer_get_rest(buffer), "12345bcdef", 10), 0); + +} + +/* + * Test the behavior of buffer_get_ssh_string with invalid data + */ +static void torture_buffer_get_ssh_string(void **state) { + ssh_buffer buffer; + int i,j,k,l; + /* some values that can go wrong */ + uint32_t values[] = {0xffffffff, 0xfffffffe, 0xfffffffc, 0xffffff00, + 0x80000000, 0x80000004, 0x7fffffff}; + char data[128]; + (void)state; + memset(data,'X',sizeof(data)); + for(i=0; i < (int)(sizeof(values)/sizeof(values[0]));++i){ + for(j=0; j< (int)sizeof(data);++j){ + for(k=1;k<5;++k){ + buffer=buffer_new(); + for(l=0;l +#include + +static int myauthcallback (const char *prompt, char *buf, size_t len, + int echo, int verify, void *userdata) { + (void) prompt; + (void) buf; + (void) len; + (void) echo; + (void) verify; + (void) userdata; + return 0; +} + +static void setup(void **state) { + struct ssh_callbacks_struct *cb; + + cb = malloc(sizeof(struct ssh_callbacks_struct)); + assert_false(cb == NULL); + ZERO_STRUCTP(cb); + + cb->userdata = (void *) 0x0badc0de; + cb->auth_function = myauthcallback; + + ssh_callbacks_init(cb); + *state = cb; +} + +static void teardown(void **state) { + free(*state); +} + +static void torture_callbacks_size(void **state) { + struct ssh_callbacks_struct *cb = *state;; + + assert_int_not_equal(cb->size, 0); +} + +static void torture_callbacks_exists(void **state) { + struct ssh_callbacks_struct *cb = *state; + + assert_int_not_equal(ssh_callbacks_exists(cb, auth_function), 0); + assert_int_equal(ssh_callbacks_exists(cb, log_function), 0); + + /* + * We redefine size so auth_function is outside the range of + * callbacks->size. + */ + cb->size = (unsigned char *) &cb->auth_function - (unsigned char *) cb; + assert_int_equal(ssh_callbacks_exists(cb, auth_function), 0); + + /* Now make it one pointer bigger so we spill over the auth_function slot */ + cb->size += sizeof(void *); + assert_int_not_equal(ssh_callbacks_exists(cb, auth_function), 0); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_callbacks_size, setup, teardown), + unit_test_setup_teardown(torture_callbacks_exists, setup, teardown), + }; + + ssh_init(); + rc=run_tests(tests); + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/unittests/torture_init.c b/libssh/tests/unittests/torture_init.c new file mode 100644 index 00000000..85bd95e1 --- /dev/null +++ b/libssh/tests/unittests/torture_init.c @@ -0,0 +1,23 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#include "libssh/libssh.h" + +static void torture_ssh_init(void **state) { + int rc; + + (void) state; + + rc = ssh_init(); + assert_int_equal(rc, SSH_OK); + rc = ssh_finalize(); + assert_int_equal(rc, SSH_OK); +} + +int torture_run_tests(void) { + const UnitTest tests[] = { + unit_test(torture_ssh_init), + }; + + return run_tests(tests); +} diff --git a/libssh/tests/unittests/torture_isipaddr.c b/libssh/tests/unittests/torture_isipaddr.c new file mode 100644 index 00000000..c2a1e079 --- /dev/null +++ b/libssh/tests/unittests/torture_isipaddr.c @@ -0,0 +1,56 @@ +#define LIBSSH_STATIC + +#include "torture.h" + +#include "misc.c" +#include "error.c" + +/* + * Test the behavior of ssh_is_ipaddr() + */ +static void torture_ssh_is_ipaddr(void **state) { + (void)state; + + assert_int_equal(ssh_is_ipaddr("127.0.0.1"),1); + assert_int_equal(ssh_is_ipaddr("0.0.0.0"),1); + assert_int_equal(ssh_is_ipaddr("1.1.1.1"),1); + assert_int_equal(ssh_is_ipaddr("255.255.255.255"),1); + assert_int_equal(ssh_is_ipaddr("128.128.128.128"),1); + assert_int_equal(ssh_is_ipaddr("1.10.100.1"),1); + assert_int_equal(ssh_is_ipaddr("0.1.10.100"),1); + + assert_int_equal(ssh_is_ipaddr("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),1); + assert_int_equal(ssh_is_ipaddr("fe80:0000:0000:0000:0202:b3ff:fe1e:8329"),1); + assert_int_equal(ssh_is_ipaddr("fe80:0:0:0:202:b3ff:fe1e:8329"),1); + assert_int_equal(ssh_is_ipaddr("fe80::202:b3ff:fe1e:8329"),1); + assert_int_equal(ssh_is_ipaddr("::1"),1); + + assert_int_equal(ssh_is_ipaddr("::ffff:192.0.2.128"),1); + + assert_int_equal(ssh_is_ipaddr("0.0.0.0.0"),0); + assert_int_equal(ssh_is_ipaddr("0.0.0.0.a"),0); + assert_int_equal(ssh_is_ipaddr("a.0.0.0"),0); + assert_int_equal(ssh_is_ipaddr("0a.0.0.0.0"),0); + assert_int_equal(ssh_is_ipaddr(""),0); + assert_int_equal(ssh_is_ipaddr("0.0.0."),0); + assert_int_equal(ssh_is_ipaddr("0.0"),0); + assert_int_equal(ssh_is_ipaddr("0"),0); + assert_int_equal(ssh_is_ipaddr("255.255.255"),0); + + assert_int_equal(ssh_is_ipaddr("2001:0db8:85a3:0000:0000:8a2e:0370:7334:1002"), 0); + assert_int_equal(ssh_is_ipaddr("fe80:x:202:b3ff:fe1e:8329"), 0); + assert_int_equal(ssh_is_ipaddr("fe80:x:202:b3ff:fe1e:8329"), 0); + assert_int_equal(ssh_is_ipaddr(":1"), 0); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test(torture_ssh_is_ipaddr) + }; + + ssh_init(); + rc=run_tests(tests); + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/unittests/torture_keyfiles.c b/libssh/tests/unittests/torture_keyfiles.c new file mode 100644 index 00000000..9446bc6d --- /dev/null +++ b/libssh/tests/unittests/torture_keyfiles.c @@ -0,0 +1,261 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#include "legacy.c" + +#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa" +#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa" +#define LIBSSH_PASSPHRASE "libssh-rocks" + +static void setup_rsa_key(void **state) { + ssh_session session; + int rc; + + unlink(LIBSSH_RSA_TESTKEY); + unlink(LIBSSH_RSA_TESTKEY ".pub"); + + rc = system("ssh-keygen -t rsa -q -N \"\" -f " LIBSSH_RSA_TESTKEY); + assert_true(rc == 0); + + session = ssh_new(); + *state = session; +} + +static void setup_dsa_key(void **state) { + ssh_session session; + int rc; + + unlink(LIBSSH_DSA_TESTKEY); + unlink(LIBSSH_DSA_TESTKEY ".pub"); + + rc = system("ssh-keygen -t dsa -q -N \"\" -f " LIBSSH_DSA_TESTKEY); + assert_true(rc == 0); + + session = ssh_new(); + *state = session; +} + +static void setup_both_keys(void **state) { + setup_rsa_key(state); + ssh_free(*state); + setup_dsa_key(state); +} + +static void setup_both_keys_passphrase(void **state) { + ssh_session session; + int rc; + + rc = system("ssh-keygen -t rsa -N " LIBSSH_PASSPHRASE " -f " LIBSSH_RSA_TESTKEY); + assert_true(rc == 0); + + rc = system("ssh-keygen -t dsa -N " LIBSSH_PASSPHRASE " -f " LIBSSH_DSA_TESTKEY); + assert_true(rc == 0); + + session = ssh_new(); + *state = session; +} +static void teardown(void **state) { + unlink(LIBSSH_DSA_TESTKEY); + unlink(LIBSSH_DSA_TESTKEY ".pub"); + + unlink(LIBSSH_RSA_TESTKEY); + unlink(LIBSSH_RSA_TESTKEY ".pub"); + + ssh_free(*state); +} + +static void torture_pubkey_from_file(void **state) { + ssh_session session = *state; + ssh_string pubkey; + int type, rc; + + rc = ssh_try_publickey_from_file(session, LIBSSH_RSA_TESTKEY, &pubkey, &type); + + assert_true(rc == 0); + + ssh_string_free(pubkey); + + /* test if it returns 1 if pubkey doesn't exist */ + unlink(LIBSSH_RSA_TESTKEY ".pub"); + + rc = ssh_try_publickey_from_file(session, LIBSSH_RSA_TESTKEY, &pubkey, &type); + assert_true(rc == 1); + + /* test if it returns -1 if privkey doesn't exist */ + unlink(LIBSSH_RSA_TESTKEY); + + rc = ssh_try_publickey_from_file(session, LIBSSH_RSA_TESTKEY, &pubkey, &type); + assert_true(rc == -1); +} + +static int torture_read_one_line(const char *filename, char *buffer, size_t len) { + FILE *fp; + size_t rc; + + fp = fopen(filename, "r"); + if (fp == NULL) { + return -1; + } + + rc = fread(buffer, len, 1, fp); + if (rc != 0 || ferror(fp)) { + fclose(fp); + return -1; + } + + fclose(fp); + + return 0; +} + +static void torture_pubkey_generate_from_privkey(void **state) { + ssh_session session = *state; + ssh_private_key privkey = NULL; + ssh_public_key pubkey = NULL; + ssh_string pubkey_orig = NULL; + ssh_string pubkey_new = NULL; + char pubkey_line_orig[512] = {0}; + char pubkey_line_new[512] = {0}; + int type_orig = 0; + int type_new = 0; + int rc; + + /* read the publickey */ + rc = ssh_try_publickey_from_file(session, LIBSSH_RSA_TESTKEY, &pubkey_orig, + &type_orig); + assert_true(rc == 0); + assert_true(pubkey_orig != NULL); + + rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub", pubkey_line_orig, + sizeof(pubkey_line_orig)); + assert_true(rc == 0); + + /* remove the public key, generate it from the private key and write it. */ + unlink(LIBSSH_RSA_TESTKEY ".pub"); + + privkey = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, 0, NULL); + assert_true(privkey != NULL); + + pubkey = publickey_from_privatekey(privkey); + assert_true(pubkey != NULL); + type_new = privkey->type; + privatekey_free(privkey); + + pubkey_new = publickey_to_string(pubkey); + publickey_free(pubkey); + + assert_true(pubkey_new != NULL); + + assert_true(ssh_string_len(pubkey_orig) == ssh_string_len(pubkey_new)); + assert_memory_equal(ssh_string_data(pubkey_orig), + ssh_string_data(pubkey_new), + ssh_string_len(pubkey_orig)); + + rc = ssh_publickey_to_file(session, LIBSSH_RSA_TESTKEY ".pub", pubkey_new, type_new); + assert_true(rc == 0); + + rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub", pubkey_line_new, + sizeof(pubkey_line_new)); + assert_true(rc == 0); + + assert_string_equal(pubkey_line_orig, pubkey_line_new); + + ssh_string_free(pubkey_orig); + ssh_string_free(pubkey_new); +} + +/** + * @brief tests the privatekey_from_file function without passphrase + */ +static void torture_privatekey_from_file(void **state) { + ssh_session session = *state; + ssh_private_key key = NULL; + + key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, SSH_KEYTYPE_RSA, NULL); + assert_true(key != NULL); + if (key != NULL) { + privatekey_free(key); + key = NULL; + } + + key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, SSH_KEYTYPE_DSS, NULL); + assert_true(key != NULL); + if (key != NULL) { + privatekey_free(key); + key = NULL; + } + + /* Test the automatic type discovery */ + key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, 0, NULL); + assert_true(key != NULL); + if (key != NULL) { + privatekey_free(key); + key = NULL; + } + + key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, 0, NULL); + assert_true(key != NULL); + if (key != NULL) { + privatekey_free(key); + key = NULL; + } +} + +/** + * @brief tests the privatekey_from_file function with passphrase + */ +static void torture_privatekey_from_file_passphrase(void **state) { + ssh_session session = *state; + ssh_private_key key = NULL; + + key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, SSH_KEYTYPE_RSA, LIBSSH_PASSPHRASE); + assert_true(key != NULL); + if (key != NULL) { + privatekey_free(key); + key = NULL; + } + + key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, SSH_KEYTYPE_DSS, LIBSSH_PASSPHRASE); + assert_true(key != NULL); + if (key != NULL) { + privatekey_free(key); + key = NULL; + } + + /* Test the automatic type discovery */ + key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, 0, LIBSSH_PASSPHRASE); + assert_true(key != NULL); + if (key != NULL) { + privatekey_free(key); + key = NULL; + } + + key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, 0, LIBSSH_PASSPHRASE); + assert_true(key != NULL); + if (key != NULL) { + privatekey_free(key); + key = NULL; + } +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_pubkey_from_file, + setup_rsa_key, + teardown), + unit_test_setup_teardown(torture_pubkey_generate_from_privkey, + setup_rsa_key, teardown), + unit_test_setup_teardown(torture_privatekey_from_file, + setup_both_keys, + teardown), + unit_test_setup_teardown(torture_privatekey_from_file_passphrase, + setup_both_keys_passphrase, teardown), + }; + + + ssh_init(); + rc=run_tests(tests); + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/unittests/torture_list.c b/libssh/tests/unittests/torture_list.c new file mode 100644 index 00000000..75f41825 --- /dev/null +++ b/libssh/tests/unittests/torture_list.c @@ -0,0 +1,91 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#include "error.c" +#include "misc.c" + +static void torture_ssh_list_new(void **state) { + struct ssh_list *xlist; + + (void) state; + + xlist = ssh_list_new(); + + assert_true(xlist != NULL); + assert_true(xlist->root == NULL); + assert_true(xlist->end == NULL); + + ssh_list_free(xlist); +} + +static void torture_ssh_list_append(void **state) { + struct ssh_list *xlist; + int rc; + + (void) state; + + xlist = ssh_list_new(); + assert_true(xlist != NULL); + + rc = ssh_list_append(xlist, "item1"); + assert_true(rc == 0); + assert_string_equal((const char *) xlist->root->data, "item1"); + assert_string_equal((const char *) xlist->end->data, "item1"); + + rc = ssh_list_append(xlist, "item2"); + assert_true(rc == 0); + assert_string_equal((const char *) xlist->root->data, "item1"); + assert_string_equal((const char *) xlist->end->data, "item2"); + + rc = ssh_list_append(xlist, "item3"); + assert_true(rc == 0); + assert_string_equal((const char *) xlist->root->data, "item1"); + assert_string_equal((const char *) xlist->root->next->data, "item2"); + assert_string_equal((const char *) xlist->root->next->next->data, "item3"); + assert_string_equal((const char *) xlist->end->data, "item3"); + + ssh_list_free(xlist); +} + +static void torture_ssh_list_prepend(void **state) { + struct ssh_list *xlist; + int rc; + + (void) state; + + xlist = ssh_list_new(); + assert_true(xlist != NULL); + + rc = ssh_list_prepend(xlist, "item1"); + assert_true(rc == 0); + assert_string_equal((const char *) xlist->root->data, "item1"); + assert_string_equal((const char *) xlist->end->data, "item1"); + + rc = ssh_list_append(xlist, "item2"); + assert_true(rc == 0); + assert_string_equal((const char *) xlist->root->data, "item1"); + assert_string_equal((const char *) xlist->end->data, "item2"); + + rc = ssh_list_prepend(xlist, "item3"); + assert_true(rc == 0); + assert_string_equal((const char *) xlist->root->data, "item3"); + assert_string_equal((const char *) xlist->root->next->data, "item1"); + assert_string_equal((const char *) xlist->root->next->next->data, "item2"); + assert_string_equal((const char *) xlist->end->data, "item2"); + + ssh_list_free(xlist); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test(torture_ssh_list_new), + unit_test(torture_ssh_list_append), + unit_test(torture_ssh_list_prepend), + }; + + ssh_init(); + rc=run_tests(tests); + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/unittests/torture_misc.c b/libssh/tests/unittests/torture_misc.c new file mode 100644 index 00000000..26770324 --- /dev/null +++ b/libssh/tests/unittests/torture_misc.c @@ -0,0 +1,210 @@ + +#include +#ifndef _WIN32 + +#define _POSIX_PTHREAD_SEMANTICS +#include +#endif + +#define LIBSSH_STATIC +#include + +#include "torture.h" +#include "misc.c" +#include "error.c" + +#define TORTURE_TEST_DIR "/usr/local/bin/truc/much/.." + + +static void setup(void **state) { + ssh_session session = ssh_new(); + *state = session; +} + +static void teardown(void **state) { + ssh_free(*state); +} + +static void torture_get_user_home_dir(void **state) { +#ifndef _WIN32 + struct passwd *pwd = getpwuid(getuid()); +#endif /* _WIN32 */ + char *user; + + (void) state; + + user = ssh_get_user_home_dir(); + assert_false(user == NULL); +#ifndef _WIN32 + assert_string_equal(user, pwd->pw_dir); +#endif /* _WIN32 */ + + SAFE_FREE(user); +} + +static void torture_basename(void **state) { + char *path; + + (void) state; + + path=ssh_basename(TORTURE_TEST_DIR "/test"); + assert_true(path != NULL); + assert_string_equal(path, "test"); + SAFE_FREE(path); + path=ssh_basename(TORTURE_TEST_DIR "/test/"); + assert_true(path != NULL); + assert_string_equal(path, "test"); + SAFE_FREE(path); +} + +static void torture_dirname(void **state) { + char *path; + + (void) state; + + path=ssh_dirname(TORTURE_TEST_DIR "/test"); + assert_true(path != NULL); + assert_string_equal(path, TORTURE_TEST_DIR ); + SAFE_FREE(path); + path=ssh_dirname(TORTURE_TEST_DIR "/test/"); + assert_true(path != NULL); + assert_string_equal(path, TORTURE_TEST_DIR); + SAFE_FREE(path); +} + +static void torture_ntohll(void **state) { + uint64_t value = 0x0123456789abcdef; + uint32_t sample = 1; + unsigned char *ptr = (unsigned char *) &sample; + uint64_t check; + + (void) state; + + if (ptr[0] == 1){ + /* we're in little endian */ + check = 0xefcdab8967452301; + } else { + /* big endian */ + check = value; + } + value = ntohll(value); + assert_true(value == check); +} + +#ifdef _WIN32 + +static void torture_path_expand_tilde_win(void **state) { + char *d; + + (void) state; + + d = ssh_path_expand_tilde("~\\.ssh"); + assert_false(d == NULL); + print_message("Expanded path: %s\n", d); + free(d); + + d = ssh_path_expand_tilde("/guru/meditation"); + assert_string_equal(d, "/guru/meditation"); + free(d); +} + +#else /* _WIN32 */ + +static void torture_path_expand_tilde_unix(void **state) { + char h[256]; + char *d; + + (void) state; + + snprintf(h, 256 - 1, "%s/.ssh", getenv("HOME")); + + d = ssh_path_expand_tilde("~/.ssh"); + assert_string_equal(d, h); + free(d); + + d = ssh_path_expand_tilde("/guru/meditation"); + assert_string_equal(d, "/guru/meditation"); + free(d); + + snprintf(h, 256 - 1, "~%s/.ssh", getenv("USER")); + d = ssh_path_expand_tilde(h); + + snprintf(h, 256 - 1, "%s/.ssh", getenv("HOME")); + assert_string_equal(d, h); + free(d); +} + +#endif /* _WIN32 */ + +static void torture_path_expand_escape(void **state) { + ssh_session session = *state; + const char *s = "%d/%h/by/%r"; + char *e; + + session->opts.sshdir = strdup("guru"); + session->opts.host = strdup("meditation"); + session->opts.username = strdup("root"); + + e = ssh_path_expand_escape(session, s); + assert_string_equal(e, "guru/meditation/by/root"); + free(e); +} + +static void torture_path_expand_known_hosts(void **state) { + ssh_session session = *state; + char *tmp; + + session->opts.sshdir = strdup("/home/guru/.ssh"); + + tmp = ssh_path_expand_escape(session, "%d/known_hosts"); + assert_string_equal(tmp, "/home/guru/.ssh/known_hosts"); + free(tmp); +} + +static void torture_timeout_elapsed(void **state){ + struct ssh_timestamp ts; + (void) state; + ssh_timestamp_init(&ts); + usleep(50000); + assert_true(ssh_timeout_elapsed(&ts,25)); + assert_false(ssh_timeout_elapsed(&ts,30000)); + assert_false(ssh_timeout_elapsed(&ts,75)); + assert_true(ssh_timeout_elapsed(&ts,0)); + assert_false(ssh_timeout_elapsed(&ts,-1)); +} + +static void torture_timeout_update(void **state){ + struct ssh_timestamp ts; + (void) state; + ssh_timestamp_init(&ts); + usleep(50000); + assert_int_equal(ssh_timeout_update(&ts,25), 0); + assert_in_range(ssh_timeout_update(&ts,30000),29000,29960); + assert_in_range(ssh_timeout_update(&ts,75),1,40); + assert_int_equal(ssh_timeout_update(&ts,0),0); + assert_int_equal(ssh_timeout_update(&ts,-1),-1); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test(torture_get_user_home_dir), + unit_test(torture_basename), + unit_test(torture_dirname), + unit_test(torture_ntohll), +#ifdef _WIN32 + unit_test(torture_path_expand_tilde_win), +#else + unit_test(torture_path_expand_tilde_unix), +#endif + unit_test_setup_teardown(torture_path_expand_escape, setup, teardown), + unit_test_setup_teardown(torture_path_expand_known_hosts, setup, teardown), + unit_test(torture_timeout_elapsed), + unit_test(torture_timeout_update), + }; + + ssh_init(); + rc=run_tests(tests); + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/unittests/torture_options.c b/libssh/tests/unittests/torture_options.c new file mode 100644 index 00000000..76da3edb --- /dev/null +++ b/libssh/tests/unittests/torture_options.c @@ -0,0 +1,197 @@ +#define LIBSSH_STATIC + +#ifndef _WIN32 +#define _POSIX_PTHREAD_SEMANTICS +# include +#endif + +#include "torture.h" +#include +#include + +static void setup(void **state) { + ssh_session session = ssh_new(); + *state = session; +} + +static void teardown(void **state) { + ssh_free(*state); +} + +static void torture_options_set_host(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == 0); + assert_string_equal(session->opts.host, "localhost"); + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "guru@meditation"); + assert_true(rc == 0); + assert_string_equal(session->opts.host, "meditation"); + assert_string_equal(session->opts.username, "guru"); +} + +static void torture_options_get_host(void **state) { + ssh_session session = *state; + int rc; + char* host = NULL; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == 0); + assert_string_equal(session->opts.host, "localhost"); + + assert_false(ssh_options_get(session, SSH_OPTIONS_HOST, &host)); + + assert_string_equal(host, "localhost"); + free(host); +} + +static void torture_options_set_port(void **state) { + ssh_session session = *state; + int rc; + unsigned int port = 42; + + rc = ssh_options_set(session, SSH_OPTIONS_PORT, &port); + assert_true(rc == 0); + assert_true(session->opts.port == port); + + rc = ssh_options_set(session, SSH_OPTIONS_PORT_STR, "23"); + assert_true(rc == 0); + assert_true(session->opts.port == 23); + + rc = ssh_options_set(session, SSH_OPTIONS_PORT_STR, "five"); + assert_true(rc == -1); + + rc = ssh_options_set(session, SSH_OPTIONS_PORT, NULL); + assert_true(rc == -1); +} + +static void torture_options_get_port(void **state) { + ssh_session session = *state; + unsigned int given_port = 1234; + unsigned int port_container; + int rc; + rc = ssh_options_set(session, SSH_OPTIONS_PORT, &given_port); + assert_true(rc == 0); + rc = ssh_options_get_port(session, &port_container); + assert_true(rc == 0); + assert_int_equal(port_container, 1234); +} + +static void torture_options_get_user(void **state) { + ssh_session session = *state; + char* user = NULL; + int rc; + rc = ssh_options_set(session, SSH_OPTIONS_USER, "magicaltrevor"); + assert_true(rc == SSH_OK); + rc = ssh_options_get(session, SSH_OPTIONS_USER, &user); + assert_string_equal(user, "magicaltrevor"); + free(user); +} + +static void torture_options_set_fd(void **state) { + ssh_session session = *state; + socket_t fd = 42; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_FD, &fd); + assert_true(rc == 0); + assert_true(session->opts.fd == fd); + + rc = ssh_options_set(session, SSH_OPTIONS_FD, NULL); + assert_true(rc == SSH_ERROR); + assert_true(session->opts.fd == SSH_INVALID_SOCKET); +} + +static void torture_options_set_user(void **state) { + ssh_session session = *state; + int rc; +#ifndef _WIN32 +# ifndef NSS_BUFLEN_PASSWD +# define NSS_BUFLEN_PASSWD 4096 +# endif /* NSS_BUFLEN_PASSWD */ + struct passwd pwd; + struct passwd *pwdbuf; + char buf[NSS_BUFLEN_PASSWD]; + + /* get local username */ + rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); + assert_true(rc == 0); +#endif /* _WIN32 */ + + rc = ssh_options_set(session, SSH_OPTIONS_USER, "guru"); + assert_true(rc == 0); + assert_string_equal(session->opts.username, "guru"); + + + rc = ssh_options_set(session, SSH_OPTIONS_USER, NULL); + assert_true(rc == 0); + +#ifndef _WIN32 + assert_string_equal(session->opts.username, pwd.pw_name); +#endif +} + +/* TODO */ +#if 0 +static voidtorture_options_set_sshdir) +{ +} +END_TEST +#endif + +static void torture_options_set_identity(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, "identity1"); + assert_true(rc == 0); + assert_string_equal(session->opts.identity->root->data, "identity1"); + + rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "identity2"); + assert_true(rc == 0); + assert_string_equal(session->opts.identity->root->data, "identity2"); + assert_string_equal(session->opts.identity->root->next->data, "identity1"); +} + +static void torture_options_get_identity(void **state) { + ssh_session session = *state; + char *identity = NULL; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, "identity1"); + assert_true(rc == 0); + rc = ssh_options_get(session, SSH_OPTIONS_IDENTITY, &identity); + assert_true(rc == SSH_OK); + assert_string_equal(identity, "identity1"); + SAFE_FREE(identity); + + rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "identity2"); + assert_true(rc == 0); + assert_string_equal(session->opts.identity->root->data, "identity2"); + rc = ssh_options_get(session, SSH_OPTIONS_IDENTITY, &identity); + assert_true(rc == SSH_OK); + assert_string_equal(identity, "identity2"); + free(identity); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_options_set_host, setup, teardown), + unit_test_setup_teardown(torture_options_get_host, setup, teardown), + unit_test_setup_teardown(torture_options_set_port, setup, teardown), + unit_test_setup_teardown(torture_options_get_port, setup, teardown), + unit_test_setup_teardown(torture_options_set_fd, setup, teardown), + unit_test_setup_teardown(torture_options_set_user, setup, teardown), + unit_test_setup_teardown(torture_options_get_user, setup, teardown), + unit_test_setup_teardown(torture_options_set_identity, setup, teardown), + unit_test_setup_teardown(torture_options_get_identity, setup, teardown), + }; + + ssh_init(); + rc=run_tests(tests); + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/unittests/torture_pki.c b/libssh/tests/unittests/torture_pki.c new file mode 100644 index 00000000..31618a41 --- /dev/null +++ b/libssh/tests/unittests/torture_pki.c @@ -0,0 +1,1037 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#include "pki.c" +#include +#include + +#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa" +#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa" +#define LIBSSH_ECDSA_TESTKEY "libssh_testkey.id_ecdsa" +#define LIBSSH_PASSPHRASE "libssh-rocks" +const unsigned char HASH[] = "12345678901234567890"; + +static void setup_rsa_key(void **state) { + int rc; + + (void) state; /* unused */ + + unlink(LIBSSH_RSA_TESTKEY); + unlink(LIBSSH_RSA_TESTKEY ".pub"); + + rc = system("ssh-keygen -t rsa -q -N \"\" -f " LIBSSH_RSA_TESTKEY); + assert_true(rc == 0); +} + +static void setup_dsa_key(void **state) { + int rc; + + (void) state; /* unused */ + + unlink(LIBSSH_DSA_TESTKEY); + unlink(LIBSSH_DSA_TESTKEY ".pub"); + + rc = system("ssh-keygen -t dsa -q -N \"\" -f " LIBSSH_DSA_TESTKEY); + assert_true(rc == 0); +} + +#ifdef HAVE_OPENSSL_ECC +static void setup_ecdsa_key(void **state) { + int rc; + + (void) state; /* unused */ + + unlink(LIBSSH_ECDSA_TESTKEY); + unlink(LIBSSH_ECDSA_TESTKEY ".pub"); + + rc = system("ssh-keygen -t ecdsa -q -N \"\" -f " LIBSSH_ECDSA_TESTKEY); + assert_true(rc == 0); +} +#endif + +static void setup_both_keys(void **state) { + (void) state; /* unused */ + + setup_rsa_key(state); + setup_dsa_key(state); +} + +static void setup_both_keys_passphrase(void **state) { + int rc; + + (void) state; /* unused */ + + rc = system("ssh-keygen -t rsa -q -N " LIBSSH_PASSPHRASE " -f " LIBSSH_RSA_TESTKEY); + assert_true(rc == 0); + + rc = system("ssh-keygen -t dsa -q -N " LIBSSH_PASSPHRASE " -f " LIBSSH_DSA_TESTKEY); + assert_true(rc == 0); +} + +static void teardown(void **state) { + (void) state; /* unused */ + + unlink(LIBSSH_DSA_TESTKEY); + unlink(LIBSSH_DSA_TESTKEY ".pub"); + + unlink(LIBSSH_RSA_TESTKEY); + unlink(LIBSSH_RSA_TESTKEY ".pub"); + + unlink(LIBSSH_ECDSA_TESTKEY); + unlink(LIBSSH_ECDSA_TESTKEY ".pub"); +} + +static char *read_file(const char *filename) { + char *key; + int fd; + int size; + struct stat buf; + + assert_true(filename != NULL); + assert_true(*filename != '\0'); + + stat(filename, &buf); + + key = malloc(buf.st_size + 1); + assert_true(key != NULL); + + fd = open(filename, O_RDONLY); + assert_true(fd >= 0); + + size = read(fd, key, buf.st_size); + assert_true(size == buf.st_size); + + close(fd); + + key[size] = '\0'; + return key; +} + +static int torture_read_one_line(const char *filename, char *buffer, size_t len) { + FILE *fp; + size_t rc; + + fp = fopen(filename, "r"); + if (fp == NULL) { + return -1; + } + + rc = fread(buffer, len, 1, fp); + if (rc != 0 || ferror(fp)) { + fclose(fp); + return -1; + } + + fclose(fp); + + return 0; +} + +static void torture_pki_keytype(void **state) { + enum ssh_keytypes_e type; + const char *type_c; + + (void) state; /* unused */ + + type = ssh_key_type(NULL); + assert_true(type == SSH_KEYTYPE_UNKNOWN); + + type = ssh_key_type_from_name(NULL); + assert_true(type == SSH_KEYTYPE_UNKNOWN); + + type = ssh_key_type_from_name("42"); + assert_true(type == SSH_KEYTYPE_UNKNOWN); + + type_c = ssh_key_type_to_char(SSH_KEYTYPE_UNKNOWN); + assert_true(type_c == NULL); + + type_c = ssh_key_type_to_char(42); + assert_true(type_c == NULL); +} + +static void torture_pki_signature(void **state) +{ + ssh_signature sig; + + (void) state; /* unused */ + + sig = ssh_signature_new(); + assert_true(sig != NULL); + + ssh_signature_free(sig); +} + +static void torture_pki_import_privkey_base64_RSA(void **state) { + int rc; + char *key_str; + ssh_key key; + const char *passphrase = LIBSSH_PASSPHRASE; + enum ssh_keytypes_e type; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_RSA_TESTKEY); + assert_true(key_str != NULL); + + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + + type = ssh_key_type(key); + assert_true(type == SSH_KEYTYPE_RSA); + + rc = ssh_key_is_public(key); + assert_true(rc == 1); + + free(key_str); + ssh_key_free(key); +} + +static void torture_pki_import_privkey_base64_NULL_key(void **state) { + int rc; + char *key_str; + ssh_key key; + const char *passphrase = LIBSSH_PASSPHRASE; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_RSA_TESTKEY); + assert_true(key_str != NULL); + + key = ssh_key_new(); + assert_true(key != NULL); + + /* test if it returns -1 if key is NULL */ + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, NULL); + assert_true(rc == -1); + + free(key_str); + ssh_key_free(key); +} + +static void torture_pki_import_privkey_base64_NULL_str(void **state) { + int rc; + char *key_str; + ssh_key key = NULL; + const char *passphrase = LIBSSH_PASSPHRASE; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_RSA_TESTKEY); + assert_true(key_str != NULL); + + /* test if it returns -1 if key_str is NULL */ + rc = ssh_pki_import_privkey_base64(NULL, passphrase, NULL, NULL, &key); + assert_true(rc == -1); + + free(key_str); + ssh_key_free(key); +} + +static void torture_pki_import_privkey_base64_DSA(void **state) { + int rc; + char *key_str; + ssh_key key; + const char *passphrase = LIBSSH_PASSPHRASE; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_DSA_TESTKEY); + assert_true(key_str != NULL); + + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + + free(key_str); + ssh_key_free(key); +} + +#ifdef HAVE_ECC +static void torture_pki_import_privkey_base64_ECDSA(void **state) { + int rc; + char *key_str; + ssh_key key; + const char *passphrase = LIBSSH_PASSPHRASE; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_ECDSA_TESTKEY); + assert_true(key_str != NULL); + + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + + free(key_str); + ssh_key_free(key); +} +#endif + +static void torture_pki_import_privkey_base64_passphrase(void **state) { + int rc; + char *key_str; + ssh_key key = NULL; + const char *passphrase = LIBSSH_PASSPHRASE; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_RSA_TESTKEY); + assert_true(key_str != NULL); + + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + ssh_key_free(key); + + /* test if it returns -1 if passphrase is wrong */ + rc = ssh_pki_import_privkey_base64(key_str, "wrong passphrase !!", NULL, + NULL, &key); + assert_true(rc == -1); + +#ifndef HAVE_LIBCRYPTO + /* test if it returns -1 if passphrase is NULL */ + /* libcrypto asks for a passphrase, so skip this test */ + rc = ssh_pki_import_privkey_base64(key_str, NULL, NULL, NULL, &key); + assert_true(rc == -1); +#endif + + free(key_str); + + /* same for DSA */ + key_str = read_file(LIBSSH_DSA_TESTKEY); + assert_true(key_str != NULL); + + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + ssh_key_free(key); + + /* test if it returns -1 if passphrase is wrong */ + rc = ssh_pki_import_privkey_base64(key_str, "wrong passphrase !!", NULL, NULL, &key); + assert_true(rc == -1); + +#ifndef HAVE_LIBCRYPTO + /* test if it returns -1 if passphrase is NULL */ + /* libcrypto asks for a passphrase, so skip this test */ + rc = ssh_pki_import_privkey_base64(key_str, NULL, NULL, NULL, &key); + assert_true(rc == -1); +#endif + + free(key_str); +} + +static void torture_pki_pki_publickey_from_privatekey_RSA(void **state) { + int rc; + char *key_str; + ssh_key key; + ssh_key pubkey; + const char *passphrase = NULL; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_RSA_TESTKEY); + assert_true(key_str != NULL); + + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + + rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); + assert_true(rc == SSH_OK); + + free(key_str); + ssh_key_free(key); + ssh_key_free(pubkey); +} + +static void torture_pki_pki_publickey_from_privatekey_DSA(void **state) { + int rc; + char *key_str; + ssh_key key; + ssh_key pubkey; + const char *passphrase = NULL; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_DSA_TESTKEY); + assert_true(key_str != NULL); + + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + + rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); + assert_true(rc == SSH_OK); + + free(key_str); + ssh_key_free(key); + ssh_key_free(pubkey); +} + +#ifdef HAVE_ECC +static void torture_pki_publickey_from_privatekey_ECDSA(void **state) { + int rc; + char *key_str; + ssh_key key; + ssh_key pubkey; + const char *passphrase = NULL; + + (void) state; /* unused */ + + key_str = read_file(LIBSSH_ECDSA_TESTKEY); + assert_true(key_str != NULL); + + rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + assert_true(rc == 0); + + rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); + assert_true(rc == SSH_OK); + + free(key_str); + ssh_key_free(key); + ssh_key_free(pubkey); +} +#endif + +static void torture_pki_publickey_dsa_base64(void **state) +{ + enum ssh_keytypes_e type; + char *b64_key, *key_buf, *p; + const char *q; + ssh_key key; + int rc; + + (void) state; /* unused */ + + key_buf = read_file(LIBSSH_DSA_TESTKEY ".pub"); + assert_true(key_buf != NULL); + + q = p = key_buf; + while (*p != ' ') p++; + *p = '\0'; + + type = ssh_key_type_from_name(q); + assert_true(type == SSH_KEYTYPE_DSS); + + q = ++p; + while (*p != ' ') p++; + *p = '\0'; + + rc = ssh_pki_import_pubkey_base64(q, type, &key); + assert_true(rc == 0); + + rc = ssh_pki_export_pubkey_base64(key, &b64_key); + assert_true(rc == 0); + + assert_string_equal(q, b64_key); + + free(b64_key); + free(key_buf); + ssh_key_free(key); +} + +#ifdef HAVE_ECC +static void torture_pki_publickey_ecdsa_base64(void **state) +{ + enum ssh_keytypes_e type; + char *b64_key, *key_buf, *p; + const char *q; + ssh_key key; + int rc; + + (void) state; /* unused */ + + key_buf = read_file(LIBSSH_ECDSA_TESTKEY ".pub"); + assert_true(key_buf != NULL); + + q = p = key_buf; + while (*p != ' ') p++; + *p = '\0'; + + type = ssh_key_type_from_name(q); + assert_true(type == SSH_KEYTYPE_ECDSA); + + q = ++p; + while (*p != ' ') p++; + *p = '\0'; + + rc = ssh_pki_import_pubkey_base64(q, type, &key); + assert_true(rc == 0); + + rc = ssh_pki_export_pubkey_base64(key, &b64_key); + assert_true(rc == 0); + + assert_string_equal(q, b64_key); + + free(b64_key); + free(key_buf); + ssh_key_free(key); +} +#endif + +static void torture_pki_publickey_rsa_base64(void **state) +{ + enum ssh_keytypes_e type; + char *b64_key, *key_buf, *p; + const char *q; + ssh_key key; + int rc; + + (void) state; /* unused */ + + key_buf = read_file(LIBSSH_RSA_TESTKEY ".pub"); + assert_true(key_buf != NULL); + + q = p = key_buf; + while (*p != ' ') p++; + *p = '\0'; + + type = ssh_key_type_from_name(q); + assert_true(((type == SSH_KEYTYPE_RSA) || + (type == SSH_KEYTYPE_RSA1))); + + q = ++p; + while (*p != ' ') p++; + *p = '\0'; + + rc = ssh_pki_import_pubkey_base64(q, type, &key); + assert_true(rc == 0); + + rc = ssh_pki_export_pubkey_base64(key, &b64_key); + assert_true(rc == 0); + + assert_string_equal(q, b64_key); + + free(b64_key); + free(key_buf); + ssh_key_free(key); +} + +static void torture_generate_pubkey_from_privkey_rsa(void **state) { + char pubkey_original[4096] = {0}; + char pubkey_generated[4096] = {0}; + ssh_key privkey; + ssh_key pubkey; + int rc; + + (void) state; /* unused */ + + rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub", + pubkey_original, + sizeof(pubkey_original)); + assert_true(rc == 0); + + /* remove the public key, generate it from the private key and write it. */ + unlink(LIBSSH_RSA_TESTKEY ".pub"); + + rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); + assert_true(rc == SSH_OK); + + rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_RSA_TESTKEY ".pub"); + assert_true(rc == 0); + + rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub", + pubkey_generated, + sizeof(pubkey_generated)); + assert_true(rc == 0); + + assert_string_equal(pubkey_original, pubkey_generated); + + ssh_key_free(privkey); + ssh_key_free(pubkey); +} + +static void torture_generate_pubkey_from_privkey_dsa(void **state) { + char pubkey_original[4096] = {0}; + char pubkey_generated[4096] = {0}; + ssh_key privkey; + ssh_key pubkey; + int rc; + + (void) state; /* unused */ + + rc = torture_read_one_line(LIBSSH_DSA_TESTKEY ".pub", + pubkey_original, + sizeof(pubkey_original)); + assert_true(rc == 0); + + /* remove the public key, generate it from the private key and write it. */ + unlink(LIBSSH_DSA_TESTKEY ".pub"); + + rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); + assert_true(rc == SSH_OK); + + rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_DSA_TESTKEY ".pub"); + assert_true(rc == 0); + + rc = torture_read_one_line(LIBSSH_DSA_TESTKEY ".pub", + pubkey_generated, + sizeof(pubkey_generated)); + assert_true(rc == 0); + + assert_string_equal(pubkey_original, pubkey_generated); + + ssh_key_free(privkey); + ssh_key_free(pubkey); +} + +#ifdef HAVE_ECC +static void torture_generate_pubkey_from_privkey_ecdsa(void **state) { + char pubkey_original[4096] = {0}; + char pubkey_generated[4096] = {0}; + ssh_key privkey; + ssh_key pubkey; + int rc; + + (void) state; /* unused */ + + rc = torture_read_one_line(LIBSSH_ECDSA_TESTKEY ".pub", + pubkey_original, + sizeof(pubkey_original)); + assert_true(rc == 0); + + /* remove the public key, generate it from the private key and write it. */ + unlink(LIBSSH_ECDSA_TESTKEY ".pub"); + + rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); + assert_true(rc == SSH_OK); + + rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_ECDSA_TESTKEY ".pub"); + assert_true(rc == 0); + + rc = torture_read_one_line(LIBSSH_ECDSA_TESTKEY ".pub", + pubkey_generated, + sizeof(pubkey_generated)); + assert_true(rc == 0); + + assert_string_equal(pubkey_original, pubkey_generated); + + ssh_key_free(privkey); + ssh_key_free(pubkey); +} +#endif + +static void torture_pki_duplicate_key_rsa(void **state) +{ + int rc; + char *b64_key; + char *b64_key_gen; + ssh_key pubkey; + ssh_key privkey; + ssh_key privkey_dup; + + (void) state; + + rc = ssh_pki_import_pubkey_file(LIBSSH_RSA_TESTKEY ".pub", &pubkey); + assert_true(rc == 0); + + rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key); + assert_true(rc == 0); + ssh_key_free(pubkey); + + rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + privkey_dup = ssh_key_dup(privkey); + assert_true(privkey_dup != NULL); + + rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); + assert_true(rc == SSH_OK); + + rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key_gen); + assert_true(rc == 0); + + assert_string_equal(b64_key, b64_key_gen); + + rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE); + assert_true(rc == 0); + + ssh_key_free(pubkey); + ssh_key_free(privkey); + ssh_key_free(privkey_dup); + ssh_string_free_char(b64_key); + ssh_string_free_char(b64_key_gen); +} + +static void torture_pki_duplicate_key_dsa(void **state) +{ + int rc; + char *b64_key; + char *b64_key_gen; + ssh_key pubkey; + ssh_key privkey; + ssh_key privkey_dup; + + (void) state; + + rc = ssh_pki_import_pubkey_file(LIBSSH_DSA_TESTKEY ".pub", &pubkey); + assert_true(rc == 0); + + rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key); + assert_true(rc == 0); + ssh_key_free(pubkey); + + rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + privkey_dup = ssh_key_dup(privkey); + assert_true(privkey_dup != NULL); + + rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); + assert_true(rc == SSH_OK); + + rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key_gen); + assert_true(rc == 0); + + assert_string_equal(b64_key, b64_key_gen); + + rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE); + assert_true(rc == 0); + + ssh_key_free(pubkey); + ssh_key_free(privkey); + ssh_key_free(privkey_dup); + ssh_string_free_char(b64_key); + ssh_string_free_char(b64_key_gen); +} + +#ifdef HAVE_ECC +static void torture_pki_duplicate_key_ecdsa(void **state) +{ + int rc; + char *b64_key; + char *b64_key_gen; + ssh_key pubkey; + ssh_key privkey; + ssh_key privkey_dup; + + (void) state; + + rc = ssh_pki_import_pubkey_file(LIBSSH_ECDSA_TESTKEY ".pub", &pubkey); + assert_true(rc == 0); + + rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key); + assert_true(rc == 0); + ssh_key_free(pubkey); + + rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + privkey_dup = ssh_key_dup(privkey); + assert_true(privkey_dup != NULL); + + rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); + assert_true(rc == SSH_OK); + + rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key_gen); + assert_true(rc == 0); + + assert_string_equal(b64_key, b64_key_gen); + + rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE); + assert_true(rc == 0); + + ssh_key_free(pubkey); + ssh_key_free(privkey); + ssh_key_free(privkey_dup); + ssh_string_free_char(b64_key); + ssh_string_free_char(b64_key_gen); +} +#endif + +static void torture_pki_generate_key_rsa(void **state) +{ + int rc; + ssh_key key; + ssh_signature sign; + ssh_session session=ssh_new(); + (void) state; + + rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 4096, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + ssh_free(session); +} + +static void torture_pki_generate_key_rsa1(void **state) +{ + int rc; + ssh_key key; + ssh_signature sign; + ssh_session session=ssh_new(); + (void) state; + + rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 1024, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 2048, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 4096, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + ssh_free(session); +} + +static void torture_pki_generate_key_dsa(void **state) +{ + int rc; + ssh_key key; + ssh_signature sign; + ssh_session session=ssh_new(); + (void) state; + + rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 1024, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 2048, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 3072, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + ssh_free(session); +} + +#ifdef HAVE_ECC +static void torture_pki_generate_key_ecdsa(void **state) +{ + int rc; + ssh_key key; + ssh_signature sign; + ssh_session session=ssh_new(); + (void) state; + + rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 256, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 384, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 512, &key); + assert_true(rc == SSH_OK); + assert_true(key != NULL); + sign = pki_do_sign(key, HASH, 20); + assert_true(sign != NULL); + rc = pki_signature_verify(session,sign,key,HASH,20); + assert_true(rc == SSH_OK); + ssh_signature_free(sign); + ssh_key_free(key); + key=NULL; + + ssh_free(session); +} +#endif + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test(torture_pki_keytype), + + unit_test(torture_pki_signature), + + /* ssh_pki_import_privkey_base64 */ + unit_test_setup_teardown(torture_pki_import_privkey_base64_NULL_key, + setup_rsa_key, + teardown), + unit_test_setup_teardown(torture_pki_import_privkey_base64_NULL_str, + setup_rsa_key, + teardown), + unit_test_setup_teardown(torture_pki_import_privkey_base64_RSA, + setup_rsa_key, + teardown), + unit_test_setup_teardown(torture_pki_import_privkey_base64_DSA, + setup_dsa_key, + teardown), +#ifdef HAVE_ECC + unit_test_setup_teardown(torture_pki_import_privkey_base64_ECDSA, + setup_ecdsa_key, + teardown), +#endif + unit_test_setup_teardown(torture_pki_import_privkey_base64_passphrase, + setup_both_keys_passphrase, + teardown), + /* ssh_pki_export_privkey_to_pubkey */ + unit_test_setup_teardown(torture_pki_pki_publickey_from_privatekey_RSA, + setup_rsa_key, + teardown), + unit_test_setup_teardown(torture_pki_pki_publickey_from_privatekey_DSA, + setup_dsa_key, + teardown), +#ifdef HAVE_ECC + unit_test_setup_teardown(torture_pki_publickey_from_privatekey_ECDSA, + setup_ecdsa_key, + teardown), +#endif + /* public key */ + unit_test_setup_teardown(torture_pki_publickey_dsa_base64, + setup_dsa_key, + teardown), + unit_test_setup_teardown(torture_pki_publickey_rsa_base64, + setup_rsa_key, + teardown), +#ifdef HAVE_ECC + unit_test_setup_teardown(torture_pki_publickey_ecdsa_base64, + setup_ecdsa_key, + teardown), +#endif + + unit_test_setup_teardown(torture_generate_pubkey_from_privkey_dsa, + setup_dsa_key, + teardown), + unit_test_setup_teardown(torture_generate_pubkey_from_privkey_rsa, + setup_rsa_key, + teardown), +#ifdef HAVE_ECC + unit_test_setup_teardown(torture_generate_pubkey_from_privkey_ecdsa, + setup_ecdsa_key, + teardown), +#endif + + unit_test_setup_teardown(torture_pki_duplicate_key_rsa, + setup_rsa_key, + teardown), + unit_test_setup_teardown(torture_pki_duplicate_key_dsa, + setup_dsa_key, + teardown), +#ifdef HAVE_ECC + unit_test_setup_teardown(torture_pki_duplicate_key_ecdsa, + setup_ecdsa_key, + teardown), +#endif + unit_test(torture_pki_generate_key_rsa), + unit_test(torture_pki_generate_key_rsa1), + unit_test(torture_pki_generate_key_dsa), +#ifdef HAVE_ECC + unit_test(torture_pki_generate_key_ecdsa), +#endif + }; + + (void)setup_both_keys; + + ssh_init(); + rc=run_tests(tests); + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/unittests/torture_rand.c b/libssh/tests/unittests/torture_rand.c new file mode 100644 index 00000000..829989b0 --- /dev/null +++ b/libssh/tests/unittests/torture_rand.c @@ -0,0 +1,68 @@ +#define LIBSSH_STATIC +#include +#include +#include +#include +#include "torture.h" + +#ifdef HAVE_LIBGCRYPT +#define NUM_LOOPS 1000 +#else +/* openssl is much faster */ +#define NUM_LOOPS 20000 +#endif +#define NUM_THREADS 100 + +static void setup(void **state) { + (void) state; + + ssh_threads_set_callbacks(ssh_threads_get_pthread()); + ssh_init(); +} + +static void teardown(void **state) { + (void) state; + + ssh_finalize(); +} + +static void *torture_rand_thread(void *threadid) { + char buffer[12]; + int i; + int r; + + (void) threadid; + + buffer[0] = buffer[1] = buffer[10] = buffer[11] = 'X'; + for(i = 0; i < NUM_LOOPS; ++i) { + r = ssh_get_random(&buffer[2], i % 8 + 1, 0); + assert_true(r == 1); + } + + pthread_exit(NULL); +} + +static void torture_rand_threading(void **state) { + pthread_t threads[NUM_THREADS]; + int i; + int err; + + (void) state; + + for(i = 0; i < NUM_THREADS; ++i) { + err = pthread_create(&threads[i], NULL, torture_rand_thread, NULL); + assert_int_equal(err, 0); + } + for(i = 0; i < NUM_THREADS; ++i) { + err=pthread_join(threads[i], NULL); + assert_int_equal(err, 0); + } +} + +int torture_run_tests(void) { + const UnitTest tests[] = { + unit_test_setup_teardown(torture_rand_threading, setup, teardown), + }; + + return run_tests(tests); +} diff --git a/libssh/tests/valgrind.supp b/libssh/tests/valgrind.supp new file mode 100644 index 00000000..4b553ead --- /dev/null +++ b/libssh/tests/valgrind.supp @@ -0,0 +1,106 @@ +### GLIBC +{ + glibc_regcomp + Memcheck:Leak + fun:*alloc + ... + fun:regcomp +} +{ + glibc_getaddrinfo_leak + Memcheck:Leak + fun:malloc + fun:make_request + fun:__check_pf + fun:getaddrinfo + fun:getai + fun:ssh_connect_host_nonblocking +} + +### OPENSSL +{ + openssl_crypto_value8 + Memcheck:Value8 + fun:* + obj:/lib*/libcrypto.so* +} + +{ + openssl_crypto_value4 + Memcheck:Value4 + fun:* + obj:/lib*/libcrypto.so* +} + +{ + openssl_crypto_cond + Memcheck:Cond + fun:* + obj:/lib*/libcrypto.so* +} + +{ + openssl_BN_cond + Memcheck:Cond + fun:BN_* +} + +{ + openssl_bn_value8 + Memcheck:Value8 + fun:bn_* +} + +{ + openssl_bn_value4 + Memcheck:Value4 + fun:bn_* +} + +{ + openssl_AES_cond + Memcheck:Cond + fun:AES_* +} + +{ + openssl_DES_cond + Memcheck:Cond + fun:DES_* +} + +{ + openssl_DES_value8 + Memcheck:Value8 + fun:DES_* +} + +{ + openssl_DES_value4 + Memcheck:Value4 + fun:DES_* +} + +{ + openssl_BF_cond + Memcheck:Cond + fun:BF_* +} + +{ + openssl_SHA1_cond + Memcheck:Cond + fun:SHA1_* +} + +{ + openssl_CRYPTO_leak + Memcheck:Leak + fun:*alloc + fun:CRYPTO_* +} +{ + openssl_CRYPTO_leak + Memcheck:Cond + fun:OPENSSL_cleanse +} From 05de59d1066d32f0f967bf76228f49f1a92d03bc Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 10 Jun 2013 01:22:22 -0400 Subject: [PATCH 003/703] Adding msgpack --- Makefile.am | 15 +- msgpack/.gitignore | 58 + msgpack/AUTHORS | 1 + msgpack/COPYING | 14 + msgpack/ChangeLog | 51 + msgpack/Doxyfile | 1552 +++++++++++++++++ msgpack/LICENSE | 202 +++ msgpack/Makefile.am | 23 + msgpack/NOTICE | 4 + msgpack/README.md | 73 + msgpack/README_crosslang.md | 38 + msgpack/bootstrap | 127 ++ msgpack/cases.json | 1 + msgpack/cases.mpac | Bin 0 -> 213 bytes msgpack/cases_compact.mpac | Bin 0 -> 116 bytes msgpack/cases_gen.rb | 99 ++ msgpack/configure.in | 100 ++ msgpack/crosslang.cc | 133 ++ msgpack/crosslang.rb | 88 + msgpack/example/custom.cc | 58 + msgpack/example/protocol.cc | 86 + msgpack/example/simple.c | 37 + msgpack/example/simple.cc | 37 + msgpack/example/stream.cc | 136 ++ msgpack/msgpack.pc.in | 10 + msgpack/msgpack_vc.postbuild.bat | 45 + msgpack/msgpack_vc8.sln | 20 + msgpack/msgpack_vc8.vcproj | 299 ++++ msgpack/pack_define.h | 26 + msgpack/pack_template.h | 771 ++++++++ msgpack/preprocess | 34 + msgpack/src/Makefile.am | 107 ++ msgpack/src/gcc_atomic.cpp | 17 + msgpack/src/gcc_atomic.h | 33 + msgpack/src/msgpack.h | 30 + msgpack/src/msgpack.hpp | 24 + msgpack/src/msgpack/object.h | 98 ++ msgpack/src/msgpack/object.hpp | 412 +++++ msgpack/src/msgpack/pack.h | 143 ++ msgpack/src/msgpack/pack.hpp | 318 ++++ msgpack/src/msgpack/sbuffer.h | 111 ++ msgpack/src/msgpack/sbuffer.hpp | 112 ++ msgpack/src/msgpack/type.hpp | 16 + msgpack/src/msgpack/type/bool.hpp | 55 + msgpack/src/msgpack/type/define.hpp.erb | 136 ++ msgpack/src/msgpack/type/deque.hpp | 77 + msgpack/src/msgpack/type/fixint.hpp | 172 ++ msgpack/src/msgpack/type/float.hpp | 82 + msgpack/src/msgpack/type/int.hpp | 211 +++ msgpack/src/msgpack/type/list.hpp | 77 + msgpack/src/msgpack/type/map.hpp | 205 +++ msgpack/src/msgpack/type/nil.hpp | 65 + msgpack/src/msgpack/type/pair.hpp | 61 + msgpack/src/msgpack/type/raw.hpp | 94 + msgpack/src/msgpack/type/set.hpp | 122 ++ msgpack/src/msgpack/type/string.hpp | 62 + .../src/msgpack/type/tr1/unordered_map.hpp | 129 ++ .../src/msgpack/type/tr1/unordered_set.hpp | 122 ++ msgpack/src/msgpack/type/tuple.hpp.erb | 206 +++ msgpack/src/msgpack/type/vector.hpp | 81 + msgpack/src/msgpack/unpack.h | 260 +++ msgpack/src/msgpack/unpack.hpp | 374 ++++ msgpack/src/msgpack/version.h.in | 40 + msgpack/src/msgpack/vrefbuffer.h | 142 ++ msgpack/src/msgpack/vrefbuffer.hpp | 97 ++ msgpack/src/msgpack/zbuffer.h | 207 +++ msgpack/src/msgpack/zbuffer.hpp | 100 ++ msgpack/src/msgpack/zone.h | 147 ++ msgpack/src/msgpack/zone.hpp.erb | 155 ++ msgpack/src/object.cpp | 87 + msgpack/src/objectc.c | 237 +++ msgpack/src/unpack.c | 468 +++++ msgpack/src/version.c | 17 + msgpack/src/vrefbuffer.c | 220 +++ msgpack/src/zone.c | 221 +++ msgpack/sysdep.h | 195 +++ msgpack/test/Makefile.am | 54 + msgpack/test/buffer.cc | 72 + msgpack/test/cases.cc | 38 + msgpack/test/convert.cc | 76 + msgpack/test/fixint.cc | 55 + msgpack/test/fixint_c.cc | 32 + msgpack/test/msgpack_test.cpp | 982 +++++++++++ msgpack/test/msgpackc_test.cpp | 424 +++++ msgpack/test/object.cc | 134 ++ msgpack/test/pack_unpack.cc | 123 ++ msgpack/test/pack_unpack_c.cc | 70 + msgpack/test/streaming.cc | 220 +++ msgpack/test/streaming_c.cc | 98 ++ msgpack/test/version.cc | 13 + msgpack/test/zone.cc | 78 + msgpack/unpack_define.h | 93 + msgpack/unpack_template.h | 413 +++++ 93 files changed, 13454 insertions(+), 4 deletions(-) create mode 100644 msgpack/.gitignore create mode 100644 msgpack/AUTHORS create mode 100644 msgpack/COPYING create mode 100644 msgpack/ChangeLog create mode 100644 msgpack/Doxyfile create mode 100644 msgpack/LICENSE create mode 100644 msgpack/Makefile.am create mode 100644 msgpack/NOTICE create mode 100644 msgpack/README.md create mode 100644 msgpack/README_crosslang.md create mode 100755 msgpack/bootstrap create mode 100644 msgpack/cases.json create mode 100644 msgpack/cases.mpac create mode 100644 msgpack/cases_compact.mpac create mode 100644 msgpack/cases_gen.rb create mode 100644 msgpack/configure.in create mode 100644 msgpack/crosslang.cc create mode 100644 msgpack/crosslang.rb create mode 100644 msgpack/example/custom.cc create mode 100644 msgpack/example/protocol.cc create mode 100644 msgpack/example/simple.c create mode 100644 msgpack/example/simple.cc create mode 100644 msgpack/example/stream.cc create mode 100644 msgpack/msgpack.pc.in create mode 100644 msgpack/msgpack_vc.postbuild.bat create mode 100644 msgpack/msgpack_vc8.sln create mode 100644 msgpack/msgpack_vc8.vcproj create mode 100644 msgpack/pack_define.h create mode 100644 msgpack/pack_template.h create mode 100755 msgpack/preprocess create mode 100644 msgpack/src/Makefile.am create mode 100644 msgpack/src/gcc_atomic.cpp create mode 100644 msgpack/src/gcc_atomic.h create mode 100644 msgpack/src/msgpack.h create mode 100644 msgpack/src/msgpack.hpp create mode 100644 msgpack/src/msgpack/object.h create mode 100644 msgpack/src/msgpack/object.hpp create mode 100644 msgpack/src/msgpack/pack.h create mode 100644 msgpack/src/msgpack/pack.hpp create mode 100644 msgpack/src/msgpack/sbuffer.h create mode 100644 msgpack/src/msgpack/sbuffer.hpp create mode 100644 msgpack/src/msgpack/type.hpp create mode 100644 msgpack/src/msgpack/type/bool.hpp create mode 100644 msgpack/src/msgpack/type/define.hpp.erb create mode 100644 msgpack/src/msgpack/type/deque.hpp create mode 100644 msgpack/src/msgpack/type/fixint.hpp create mode 100644 msgpack/src/msgpack/type/float.hpp create mode 100644 msgpack/src/msgpack/type/int.hpp create mode 100644 msgpack/src/msgpack/type/list.hpp create mode 100644 msgpack/src/msgpack/type/map.hpp create mode 100644 msgpack/src/msgpack/type/nil.hpp create mode 100644 msgpack/src/msgpack/type/pair.hpp create mode 100644 msgpack/src/msgpack/type/raw.hpp create mode 100644 msgpack/src/msgpack/type/set.hpp create mode 100644 msgpack/src/msgpack/type/string.hpp create mode 100644 msgpack/src/msgpack/type/tr1/unordered_map.hpp create mode 100644 msgpack/src/msgpack/type/tr1/unordered_set.hpp create mode 100644 msgpack/src/msgpack/type/tuple.hpp.erb create mode 100644 msgpack/src/msgpack/type/vector.hpp create mode 100644 msgpack/src/msgpack/unpack.h create mode 100644 msgpack/src/msgpack/unpack.hpp create mode 100644 msgpack/src/msgpack/version.h.in create mode 100644 msgpack/src/msgpack/vrefbuffer.h create mode 100644 msgpack/src/msgpack/vrefbuffer.hpp create mode 100644 msgpack/src/msgpack/zbuffer.h create mode 100644 msgpack/src/msgpack/zbuffer.hpp create mode 100644 msgpack/src/msgpack/zone.h create mode 100644 msgpack/src/msgpack/zone.hpp.erb create mode 100644 msgpack/src/object.cpp create mode 100644 msgpack/src/objectc.c create mode 100644 msgpack/src/unpack.c create mode 100644 msgpack/src/version.c create mode 100644 msgpack/src/vrefbuffer.c create mode 100644 msgpack/src/zone.c create mode 100644 msgpack/sysdep.h create mode 100644 msgpack/test/Makefile.am create mode 100644 msgpack/test/buffer.cc create mode 100644 msgpack/test/cases.cc create mode 100644 msgpack/test/convert.cc create mode 100644 msgpack/test/fixint.cc create mode 100644 msgpack/test/fixint_c.cc create mode 100644 msgpack/test/msgpack_test.cpp create mode 100644 msgpack/test/msgpackc_test.cpp create mode 100644 msgpack/test/object.cc create mode 100644 msgpack/test/pack_unpack.cc create mode 100644 msgpack/test/pack_unpack_c.cc create mode 100644 msgpack/test/streaming.cc create mode 100644 msgpack/test/streaming_c.cc create mode 100644 msgpack/test/version.cc create mode 100644 msgpack/test/zone.cc create mode 100644 msgpack/unpack_define.h create mode 100644 msgpack/unpack_template.h diff --git a/Makefile.am b/Makefile.am index 7f9192df..51b15ed8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,7 @@ CFLAGS += -D_GNU_SOURCE endif CFLAGS += -Wno-unused-parameter -Wno-unused-variable -CFLAGS += -Ilibssh/include/ +CFLAGS += -Ilibssh/include/ -Imsgpack/src # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. @@ -33,8 +33,7 @@ CFLAGS += -O0 -g CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare -CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align -CFLAGS += -Wdeclaration-after-statement +CFLAGS += -Wbad-function-cast -Winline -Wcast-align CPPFLAGS += -DDEBUG else CFLAGS += -O2 @@ -239,8 +238,16 @@ if NO_B64_NTOP nodist_tmate_SOURCES += compat/b64_ntop.c endif -tmate_LDADD = libssh/build/src/libssh.a +tmate_LDADD = \ + libssh/build/src/libssh.a \ + msgpack/src/.libs/libmsgpackc.a + +*.c: $(tmate_LDADD) libssh/build/src/libssh.a: cd libssh/build && cmake .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON +make -C libssh/build ssh_static + +msgpack/src/.libs/libmsgpackc.a: + cd msgpack && ./bootstrap && ./configure + +make -C msgpack/src libmsgpackc.la diff --git a/msgpack/.gitignore b/msgpack/.gitignore new file mode 100644 index 00000000..ca7355a4 --- /dev/null +++ b/msgpack/.gitignore @@ -0,0 +1,58 @@ +# Files generated by the bootstrap script. +/INSTALL +/NEWS +/README +/ac/ +/aclocal.m4 +/autom4te.cache/ +/config.h.in +/configure +/msgpack_vc2008.sln +/msgpack_vc2008.vcproj +/src/msgpack/pack_define.h +/src/msgpack/pack_template.h +/src/msgpack/sysdep.h +/src/msgpack/type/define.hpp +/src/msgpack/type/tuple.hpp +/src/msgpack/unpack_define.h +/src/msgpack/unpack_template.h +/src/msgpack/zone.hpp +/test/cases.mpac +/test/cases_compact.mpac +Makefile.in + +# Files generated by the configure script. + +/config.h +/config.log +/config.status +/libtool +/msgpack.pc +/src/msgpack/version.h +/stamp-h1 +Makefile +.deps +.libs + +# Files generated by make. +*.o +*.so +*.lo +*.la + +# Files generated by make check. +# TODO: Replace these with something like /test/*_test +/test/buffer +/test/cases +/test/convert +/test/fixint +/test/fixint_c +/test/msgpack_test +/test/msgpackc_test +/test/object +/test/pack_unpack +/test/pack_unpack_c +/test/streaming +/test/streaming_c +/test/version +/test/zone diff --git a/msgpack/AUTHORS b/msgpack/AUTHORS new file mode 100644 index 00000000..ababacb0 --- /dev/null +++ b/msgpack/AUTHORS @@ -0,0 +1 @@ +FURUHASHI Sadayuki diff --git a/msgpack/COPYING b/msgpack/COPYING new file mode 100644 index 00000000..4388e8fa --- /dev/null +++ b/msgpack/COPYING @@ -0,0 +1,14 @@ +Copyright (C) 2008-2010 FURUHASHI Sadayuki + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/msgpack/ChangeLog b/msgpack/ChangeLog new file mode 100644 index 00000000..a983051a --- /dev/null +++ b/msgpack/ChangeLog @@ -0,0 +1,51 @@ + +2011-08-08 version 0.5.7: + + * fixes compile error problem with llvm-gcc and Mac OS X Lion + +2011-04-24 version 0.5.6: + + * #42 fixes double-free problem on msgpack_unpacker_release_zone + +2011-02-24 version 0.5.5: + + * eliminates dependency of winsock2.h header + * fixes msgpack_vc.postbuild.bat file + * fixes some implicit cast warnings + +2010-08-29 version 0.5.4: + + * includes msgpack_vc2008.vcproj file in source package + * fixes type::fix_int types + +2010-08-27 version 0.5.3: + + * adds type::fix_{u,}int{8,16,32,64} types + * adds msgpack_pack_fix_{u,}int{8,16,32,64} functions + * adds packer::pack_fix_{u,}int{8,16,32,64} functions + * fixes include paths + +2010-07-14 version 0.5.2: + + * type::raw::str(), operator==, operator!=, operator< and operator> are now const + * generates version.h using AC_OUTPUT macro in ./configure + +2010-07-06 version 0.5.1: + + * Add msgpack_vrefbuffer_new and msgpack_vrefbuffer_free + * Add msgpack_sbuffer_new and msgpack_sbuffer_free + * Add msgpack_unpacker_next and msgpack_unpack_next + * msgpack::unpack returns void + * Add MSGPACK_VERSION{,_MAJOR,_MINOR} macros to check header version + * Add msgpack_version{,_major,_minor} functions to check library version + * ./configure supports --disable-cxx option not to build C++ API + +2010-04-29 version 0.5.0: + + * msgpack_object_type is changed. MSGPACK_OBJECT_NIL is now 0x00. + * New safe streaming deserializer API. + * Add object::object(const T&) and object::operator=(const T&) + * Add operator==(object, const T&) + * MSGPACK_DEFINE macro defines msgpack_object(object* obj, zone* z) + * C++ programs doesn't need to link "msgpackc" library. + diff --git a/msgpack/Doxyfile b/msgpack/Doxyfile new file mode 100644 index 00000000..ca772301 --- /dev/null +++ b/msgpack/Doxyfile @@ -0,0 +1,1552 @@ +# Doxyfile 1.6.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "MessagePack" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.hpp *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +#RECURSIVE = NO +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = NO + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/msgpack/LICENSE b/msgpack/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/msgpack/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/msgpack/Makefile.am b/msgpack/Makefile.am new file mode 100644 index 00000000..bcc24a42 --- /dev/null +++ b/msgpack/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = src test + +DOC_FILES = \ + README.md \ + LICENSE \ + NOTICE \ + msgpack_vc8.vcproj \ + msgpack_vc8.sln \ + msgpack_vc2008.vcproj \ + msgpack_vc2008.sln \ + msgpack_vc.postbuild.bat + +EXTRA_DIST = \ + $(DOC_FILES) + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = msgpack.pc + +doxygen: + ./preprocess clean + cd src && $(MAKE) doxygen + ./preprocess + diff --git a/msgpack/NOTICE b/msgpack/NOTICE new file mode 100644 index 00000000..e706f2ab --- /dev/null +++ b/msgpack/NOTICE @@ -0,0 +1,4 @@ +MessagePack is developed by FURUHASHI Sadayuki, licensed under Apache License, +Version 2.0. The original software and related information is available at +http://msgpack.sourceforge.jp/. + diff --git a/msgpack/README.md b/msgpack/README.md new file mode 100644 index 00000000..eac77935 --- /dev/null +++ b/msgpack/README.md @@ -0,0 +1,73 @@ +MessagePack for C/C++ +===================== +Binary-based efficient object serialization library. + + +## Installation + +Download latest package from [releases of MessagePack](http://sourceforge.net/projects/msgpack/files/) and extract it. + +On UNIX-like platform, run ./configure && make && sudo make install: + + $ ./configure + $ make + $ sudo make install + +On Windows, open msgpack_vc8.vcproj or msgpack_vc2008 file and build it using batch build. DLLs are built on lib folder, +and the headers are built on include folder. + +To use the library in your program, include msgpack.hpp header and link "msgpack" library. + + +## Example + + #include + #include + + int main(void) { + // This is target object. + std::vector target; + target.push_back("Hello,"); + target.push_back("World!"); + + // Serialize it. + msgpack::sbuffer buffer; // simple buffer + msgpack::pack(&buffer, target); + + // Deserialize the serialized data. + msgpack::unpacked msg; // includes memory pool and deserialized object + msgpack::unpack(&msg, sbuf.data(), sbuf.size()); + msgpack::object obj = msg.get(); + + // Print the deserialized object to stdout. + std::cout << obj << std::endl; // ["Hello," "World!"] + + // Convert the deserialized object to staticaly typed object. + std::vector result; + obj.convert(&result); + + // If the type is mismatched, it throws msgpack::type_error. + obj.as(); // type is mismatched, msgpack::type_error is thrown + } + +API documents and other example codes are available at the [wiki.](http://redmine.msgpack.org/projects/msgpack/wiki) + + +## License + + Copyright (C) 2008-2010 FURUHASHI Sadayuki + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +See also NOTICE file. + diff --git a/msgpack/README_crosslang.md b/msgpack/README_crosslang.md new file mode 100644 index 00000000..80270022 --- /dev/null +++ b/msgpack/README_crosslang.md @@ -0,0 +1,38 @@ +MessagePack cross-language test cases +===================================== + +## cases + +Valid serialized data are stored in "cases.mpac" and "cases_compact.mpac". +These files describe same objects. And "cases.json" describes an array of the described objects. + +Thus you can verify your implementations as comparing the objects. + + +## crosslang + +The *crosslang* tool reads serialized data from stdin and writes re-serialize data to stdout. + +There are C++ and Ruby implementation of crosslang tool. You can verify your implementation +as comparing that implementations. + +### C++ version + + $ cd ../cpp && ./configure && make && make install + or + $ port install msgpack # MacPorts + + $ g++ -Wall -lmsgpack crosslang.cc -o crosslang + + $ ./crosslang + Usage: ./crosslang [in-file] [out-file] + +### Ruby version + + $ gem install msgpack + or + $ port install rb_msgpack # MacPorts + + $ ruby crosslang.rb + Usage: crosslang.rb [in-file] [out-file] + diff --git a/msgpack/bootstrap b/msgpack/bootstrap new file mode 100755 index 00000000..1ff6b76e --- /dev/null +++ b/msgpack/bootstrap @@ -0,0 +1,127 @@ +#!/bin/sh +# vim:ts=4:sw=4 +# Calls autotools to build configure script and Makefile.in. +# Generated automatically using bootstrapper 0.2.1 +# http://bootstrapper.sourceforge.net/ +# +# Copyright (C) 2002 Anthony Ventimiglia +# +# This bootstrap script is free software; you can redistribute +# it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# +# Calls proper programs to create configure script and Makefile.in files. +# if run with the --clean option, bootstrap removes files it generates. To +# clean all autogenerated files (eg: for cvs imports) first run +# make distclean, then bootstrap --clean +# see bootstrapper(1) for more infor + + +if test x"$1" = x"--help"; then + echo "$0: automatic bootstrapping utility for GNU Autotools" + echo " cleans up old autogenerated files and runs autoconf," + echo " automake and aclocal on local directory" + echo + echo " --clean clean up auto-generated files without" + echo " creating new scripts" + echo + exit 0 +fi + + +mkdir -p ac +test -f AUTHORS || touch AUTHORS +test -f COPYING || touch COPYING +test -f ChangeLog || touch ChangeLog +test -f NEWS || touch NEWS +test -f README || cp -f README.md README + +./preprocess +if [ $? -ne 0 ]; then + exit 1 +fi + + + +ACLOCAL="aclocal" +ACLOCAL_FILES="aclocal.m4" +ALWAYS_CLEAN="config.status config.log config.cache libtool" +AUTOCONF="autoconf" +AUTOCONF_FILES="configure" +AUTOHEADER="autoheader" +AUTOHEADER_FILES="" +AUTOMAKE="automake --add-missing --copy" +AUTOMAKE_FILES="config.sub stamp-h.in ltmain.sh missing mkinstalldirs install-sh config.guess" +CONFIG_AUX_DIR="." +CONFIG_FILES="stamp-h ltconfig" +CONFIG_HEADER="" +if [ x`uname` = x"Darwin" ]; then + LIBTOOLIZE="glibtoolize --force --copy" +else + LIBTOOLIZE="libtoolize --force --copy" +fi +LIBTOOLIZE_FILES="config.sub ltmain.sh config.guess" +RM="rm" +SUBDIRS="[]" + + +# These are files created by configure, so we'll always clean them +for i in $ALWAYS_CLEAN; do + test -f $i && \ + $RM $i +done + +if test x"$1" = x"--clean"; then + # + #Clean Files left by previous bootstrap run + # + if test -n "$CONFIG_AUX_DIR"; + then CONFIG_AUX_DIR="$CONFIG_AUX_DIR/" + fi + # Clean Libtoolize generated files + for cf in $LIBTOOLIZE_FILES; do + cf="$CONFIG_AUX_DIR$cf" + test -f $cf && \ + $RM $cf + done + #aclocal.m4 created by aclocal + test -f $ACLOCAL_FILES && $RM $ACLOCAL_FILES + #Clean Autoheader Generated files + for cf in $AUTOHEADER_FILES; do + cf=$CONFIG_AUX_DIR$cf + test -f $cf && \ + $RM $cf + done + # remove config header (Usaually config.h) + test -n "$CONFIG_HEADER" && test -f $CONFIG_HEADER && $RM $CONFIG_HEADER + #Clean Automake generated files + for cf in $AUTOMAKE_FILES; do + cf=$CONFIG_AUX_DIR$cf + test -f $cf && \ + $RM $cf + done + for i in $SUBDIRS; do + test -f $i/Makefile.in && \ + $RM $i/Makefile.in + done + #Autoconf generated files + for cf in $AUTOCONF_FILES; do + test -f $cf && \ + $RM $cf + done + for cf in $CONFIG_FILES; do + cf="$CONFIG_AUX_DIR$cf" + test -f $cf && \ + $RM $cf + done +else + $LIBTOOLIZE + $ACLOCAL + $AUTOHEADER + $AUTOMAKE + $AUTOCONF +fi + + diff --git a/msgpack/cases.json b/msgpack/cases.json new file mode 100644 index 00000000..fd390d48 --- /dev/null +++ b/msgpack/cases.json @@ -0,0 +1 @@ +[false,true,null,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,127,127,255,65535,4294967295,-32,-32,-128,-32768,-2147483648,0.0,-0.0,1.0,-1.0,"a","a","a","","","",[0],[0],[0],[],[],[],{},{},{},{"a":97},{"a":97},{"a":97},[[]],[["a"]]] \ No newline at end of file diff --git a/msgpack/cases.mpac b/msgpack/cases.mpac new file mode 100644 index 0000000000000000000000000000000000000000..5ec08c6a9af42d9568eb429a209a639616e94711 GIT binary patch literal 213 zcmXYp!4<+V3`3R4n8lOSY|v~#CV>aXw2-v7Qc6c)17ihrkbe}**V_dHM&J(DgGLop zU?R;l%8FI9$y_sy>V|HFdDW~{neAn-roN|Wd+OcH160;F91fo!97} FixMap +de 00 01 a1 61 61 # {"a"=>97} map 16 +df 00 00 00 01 a1 61 61 # {"a"=>97} map 32 +91 90 # [[]] +91 91 a1 61 # [["a"]] +EOF + +source.gsub!(/\#.+$/,'') +bytes = source.strip.split(/\s+/).map {|x| x.to_i(16) }.pack('C*') + +objs = [] +compact_bytes = "" + +pac = MessagePack::Unpacker.new +pac.feed(bytes) +pac.each {|obj| + p obj + objs << obj + compact_bytes << obj.to_msgpack +} + +json = objs.to_json + +# self check +cpac = MessagePack::Unpacker.new +cpac.feed(compact_bytes) +cpac.each {|cobj| + obj = objs.shift + if obj != cobj + puts "** SELF CHECK FAILED **" + puts "expected: #{obj.inspect}" + puts "actual: #{cobj.inspect}" + exit 1 + end +} + +File.open("cases.mpac","w") {|f| f.write(bytes) } +File.open("cases_compact.mpac","w") {|f| f.write(compact_bytes) } +File.open("cases.json","w") {|f| f.write(json) } + diff --git a/msgpack/configure.in b/msgpack/configure.in new file mode 100644 index 00000000..e7540187 --- /dev/null +++ b/msgpack/configure.in @@ -0,0 +1,100 @@ +AC_INIT(src/object.cpp) +AC_CONFIG_AUX_DIR(ac) +AM_INIT_AUTOMAKE(msgpack, 0.5.7) +AC_CONFIG_HEADER(config.h) + +AC_SUBST(CFLAGS) +CFLAGS="-O3 -Wall $CFLAGS" + +AC_SUBST(CXXFLAGS) +CXXFLAGS="-O3 -Wall $CXXFLAGS" + + +AC_PROG_CC + + +AC_MSG_CHECKING([if C++ API is enabled]) +AC_ARG_ENABLE(cxx, + AS_HELP_STRING([--disable-cxx], + [don't build C++ API]) ) #' +AC_MSG_RESULT([$enable_cxx]) +if test "$enable_cxx" != "no"; then + AC_PROG_CXX + AM_PROG_CC_C_O +fi +AM_CONDITIONAL(ENABLE_CXX, test "$enable_cxx" != "no") + + +AC_PROG_LIBTOOL +AM_PROG_AS + + +AC_MSG_CHECKING([if debug option is enabled]) +AC_ARG_ENABLE(debug, + AS_HELP_STRING([--disable-debug], + [disable assert macros and omit -g option]) ) +AC_MSG_RESULT([$enable_debug]) +if test "$enable_debug" != "no"; then + CXXFLAGS="$CXXFLAGS -g" + CFLAGS="$CFLAGS -g" +else + CXXFLAGS="$CXXFLAGS -DNDEBUG" + CFLAGS="$CFLAGS -DNDEBUG" +fi + + +AC_CACHE_CHECK([for __sync_* atomic operations], msgpack_cv_atomic_ops, [ + AC_TRY_LINK([ + int atomic_sub(int i) { return __sync_sub_and_fetch(&i, 1); } + int atomic_add(int i) { return __sync_add_and_fetch(&i, 1); } + ], [atomic_sub(1); atomic_add(1);], msgpack_cv_atomic_ops="yes") + ]) +if test "$msgpack_cv_atomic_ops" != "yes"; then + if test "$enable_cxx" = "no"; then + AC_MSG_ERROR([__sync_* atomic operations are not found. Try to enable C++ support. +If you are using gcc >= 4.1 and the default target CPU architecture is "i386", try to +add CFLAGS="-march=i686" and CXXFLAGS="-march=i686" options to ./configure as follows: + + $ ./configure CFLAGS="-march=i686" CXXFLAGS="-march=i686" + ]) + fi + + AC_LANG_PUSH([C++]) + AC_CACHE_CHECK([for __gnu_cxx::__exchange_and_add], msgpack_cv_gcc_cxx_atomic_ops, [ + AC_TRY_LINK([ + #include + int atomic_sub(int i) { return __gnu_cxx::__exchange_and_add(&i, -1) - 1; } + int atomic_add(int i) { return __gnu_cxx::__exchange_and_add(&i, 1) + 1; } + ], [atomic_sub(1); atomic_add(1);], msgpack_cv_gcc_cxx_atomic_ops="yes") + ]) + AC_LANG_POP([C++]) + + if test "$msgpack_cv_gcc_cxx_atomic_ops" != "yes"; then + AC_MSG_ERROR([__sync_* atomic operations nor __gnu_cxx::__exchange_and_add are not found. + +If you are using gcc >= 4.1 and the default target CPU architecture is "i386", try to +add CFLAGS="-march=i686" and CXXFLAGS="-march=i686" options to ./configure as follows: + + $ ./configure CFLAGS="-march=i686" CXXFLAGS="-march=i686" +]) + + else + enable_gcc_cxx_atomic=yes + fi +fi + +AM_CONDITIONAL(ENABLE_GCC_CXX_ATOMIC, test "$enable_gcc_cxx_atomic" = "yes") + + +major=`echo $VERSION | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` +minor=`echo $VERSION | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` +AC_SUBST(VERSION_MAJOR, $major) +AC_SUBST(VERSION_MINOR, $minor) + + +AC_OUTPUT([Makefile + msgpack.pc + src/Makefile + src/msgpack/version.h + test/Makefile]) + diff --git a/msgpack/crosslang.cc b/msgpack/crosslang.cc new file mode 100644 index 00000000..be521b36 --- /dev/null +++ b/msgpack/crosslang.cc @@ -0,0 +1,133 @@ +// +// MessagePack cross-language test tool +// +// $ cd ../cpp && ./configure && make && make install +// or +// $ port install msgpack # MacPorts +// +// $ g++ -Wall -lmsgpack crosslang.cc +// +#include +#include +#include +#include +#include +#include +#include + +static int run(int infd, int outfd) +try { + msgpack::unpacker pac; + + while(true) { + pac.reserve_buffer(32*1024); + + ssize_t count = + read(infd, pac.buffer(), pac.buffer_capacity()); + + if(count <= 0) { + if(count == 0) { + return 0; + } + if(errno == EAGAIN || errno == EINTR) { + continue; + } + return 1; + } + + pac.buffer_consumed(count); + + msgpack::unpacked result; + while(pac.next(&result)) { + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, result.get()); + + const char* p = sbuf.data(); + const char* const pend = p + sbuf.size(); + while(p < pend) { + ssize_t bytes = write(outfd, p, pend-p); + + if(bytes <= 0) { + if(count == 0) { + return 0; + } + if(errno == EAGAIN || errno == EINTR) { + continue; + } + return 1; + } + + p += bytes; + } + + } + } + + return 0; + +} catch (std::exception& e) { + std::cerr << e.what() << std::endl; + return 1; +} + +static void usage(const char* prog) +{ + printf( + "Usage: %s [in-file] [out-file]\n" + "\n" + "This tool is for testing of MessagePack implementation.\n" + "This does following behavior:\n" + "\n" + " 1. Reads objects serialized by MessagePack from (default: stdin)\n" + " 2. Re-serializes the objects using C++ implementation of MessagePack (Note that C++ implementation is considered valid)\n" + " 3. Writes the re-serialized objects into (default: stdout)\n" + "\n" + , prog); + exit(1); +} + +int main(int argc, char* argv[]) +{ + int infd = 0; + int outfd = 1; + + if(argc < 1 || argc > 3) { + usage(argv[0]); + } + + for(int i=1; i < argc; ++i) { + if(strlen(argv[i]) > 1 && argv[i][0] == '-') { + usage(argv[0]); + } + } + + if(argc >= 2) { + const char* fname = argv[1]; + if(strcmp(fname, "-") != 0) { + infd = open(fname, O_RDONLY); + if(infd < 0) { + perror("can't open input file"); + exit(1); + } + } + } + + if(argc >= 3) { + const char* fname = argv[2]; + if(strcmp(fname, "-") != 0) { + outfd = open(fname, O_WRONLY | O_CREAT| O_TRUNC, 0666); + if(outfd < 0) { + perror("can't open output file"); + exit(1); + } + } + } + + int code = run(infd, outfd); + + close(infd); + close(outfd); + + return code; +} + diff --git a/msgpack/crosslang.rb b/msgpack/crosslang.rb new file mode 100644 index 00000000..7aa44820 --- /dev/null +++ b/msgpack/crosslang.rb @@ -0,0 +1,88 @@ +# +# MessagePack cross-language test tool +# +# $ gem install msgpack +# or +# $ port install rb_msgpack # MacPorts +# +begin +require 'rubygems' +rescue LoadError +end +require 'msgpack' + +def run(inio, outio) + pac = MessagePack::Unpacker.new(inio) + + begin + pac.each {|obj| + outio.write MessagePack.pack(obj) + outio.flush + } + rescue EOFError + return 0 + rescue + $stderr.puts $! + return 1 + end + + return 0 +end + +def usage + puts < (default: stdin) + 2. Re-serializes the objects using Ruby implementation of MessagePack (Note that Ruby implementation is considered valid) + 3. Writes the re-serialized objects into (default: stdout) + +EOF + exit 1 +end + +inio = $stdin +outio = $stdout + +if ARGV.length > 2 + usage +end + +ARGV.each {|str| + if str.size > 1 && str[0] == ?- + usage + end +} + +if fname = ARGV[0] + unless fname == "-" + begin + inio = File.open(fname) + rescue + puts "can't open output file: #{$!}" + exit 1 + end + end +end + +if fname = ARGV[1] + unless fname == "-" + begin + outio = File.open(fname, "w") + rescue + puts "can't open output file: #{$!}" + exit 1 + end + end +end + +code = run(inio, outio) + +inio.close +outio.close + +exit code + diff --git a/msgpack/example/custom.cc b/msgpack/example/custom.cc new file mode 100644 index 00000000..835ebed9 --- /dev/null +++ b/msgpack/example/custom.cc @@ -0,0 +1,58 @@ +#include +#include +#include + +class old_class { +public: + old_class() : value("default") { } + + std::string value; + + MSGPACK_DEFINE(value); +}; + +class new_class { +public: + new_class() : value("default"), flag(-1) { } + + std::string value; + int flag; + + MSGPACK_DEFINE(value, flag); +}; + +int main(void) +{ + { + old_class oc; + new_class nc; + + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, oc); + + msgpack::zone zone; + msgpack::object obj; + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &zone, &obj); + + obj.convert(&nc); + + std::cout << obj << " value=" << nc.value << " flag=" << nc.flag << std::endl; + } + + { + new_class nc; + old_class oc; + + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, nc); + + msgpack::zone zone; + msgpack::object obj; + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &zone, &obj); + + obj.convert(&oc); + + std::cout << obj << " value=" << oc.value << std::endl; + } +} + diff --git a/msgpack/example/protocol.cc b/msgpack/example/protocol.cc new file mode 100644 index 00000000..e7f2a387 --- /dev/null +++ b/msgpack/example/protocol.cc @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +namespace myprotocol { + using namespace msgpack::type; + using msgpack::define; + + struct Get : define< tuple > { + Get() { } + Get(uint32_t f, const std::string& k) : + define_type(msgpack_type(f, k)) { } + uint32_t& flags() { return get<0>(); } + std::string& key() { return get<1>(); } + }; + + struct Put : define< tuple > { + Put() { } + Put(uint32_t f, const std::string& k, const char* valref, size_t vallen) : + define_type(msgpack_type( f, k, raw_ref(valref,vallen) )) { } + uint32_t& flags() { return get<0>(); } + std::string& key() { return get<1>(); } + raw_ref& value() { return get<2>(); } + }; + + struct MultiGet : define< std::vector > { + }; +} + + +int main(void) +{ + // send Get request + std::stringstream stream; + { + myprotocol::Get req; + req.flags() = 0; + req.key() = "key0"; + msgpack::pack(stream, req); + } + + stream.seekg(0); + + // receive Get request + { + std::string buffer(stream.str()); + + msgpack::zone mempool; + msgpack::object o = + msgpack::unpack(buffer.data(), buffer.size(), mempool); + + myprotocol::Get req; + msgpack::convert(req, o); + std::cout << "received: " << o << std::endl; + } + + + stream.str(""); + + + // send MultiGet request + { + myprotocol::MultiGet req; + req.push_back( myprotocol::Get(1, "key1") ); + req.push_back( myprotocol::Get(2, "key2") ); + req.push_back( myprotocol::Get(3, "key3") ); + msgpack::pack(stream, req); + } + + stream.seekg(0); + + // receive MultiGet request + { + std::string buffer(stream.str()); + + msgpack::zone mempool; + msgpack::object o = + msgpack::unpack(buffer.data(), buffer.size(), mempool); + + myprotocol::MultiGet req; + msgpack::convert(req, o); + std::cout << "received: " << o << std::endl; + } +} + diff --git a/msgpack/example/simple.c b/msgpack/example/simple.c new file mode 100644 index 00000000..41d8bb70 --- /dev/null +++ b/msgpack/example/simple.c @@ -0,0 +1,37 @@ +#include +#include + +int main(void) +{ + /* msgpack::sbuffer is a simple buffer implementation. */ + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + + /* serialize values into the buffer using msgpack_sbuffer_write callback function. */ + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + + msgpack_pack_array(&pk, 3); + msgpack_pack_int(&pk, 1); + msgpack_pack_true(&pk); + msgpack_pack_raw(&pk, 7); + msgpack_pack_raw_body(&pk, "example", 7); + + /* deserialize the buffer into msgpack_object instance. */ + /* deserialized object is valid during the msgpack_zone instance alive. */ + msgpack_zone mempool; + msgpack_zone_init(&mempool, 2048); + + msgpack_object deserialized; + msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); + + /* print the deserialized object. */ + msgpack_object_print(stdout, deserialized); + puts(""); + + msgpack_zone_destroy(&mempool); + msgpack_sbuffer_destroy(&sbuf); + + return 0; +} + diff --git a/msgpack/example/simple.cc b/msgpack/example/simple.cc new file mode 100644 index 00000000..55ecdf92 --- /dev/null +++ b/msgpack/example/simple.cc @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +int main(void) +{ + msgpack::type::tuple src(1, true, "example"); + + // serialize the object into the buffer. + // any classes that implements write(const char*,size_t) can be a buffer. + std::stringstream buffer; + msgpack::pack(buffer, src); + + // send the buffer ... + buffer.seekg(0); + + // deserialize the buffer into msgpack::object instance. + std::string str(buffer.str()); + + // deserialized object is valid during the msgpack::zone instance alive. + msgpack::zone mempool; + + msgpack::object deserialized; + msgpack::unpack(str.data(), str.size(), NULL, &mempool, &deserialized); + + // msgpack::object supports ostream. + std::cout << deserialized << std::endl; + + // convert msgpack::object instance into the original type. + // if the type is mismatched, it throws msgpack::type_error exception. + msgpack::type::tuple dst; + deserialized.convert(&dst); + + return 0; +} + diff --git a/msgpack/example/stream.cc b/msgpack/example/stream.cc new file mode 100644 index 00000000..2241935c --- /dev/null +++ b/msgpack/example/stream.cc @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +class Server { +public: + Server(int sock) : m_sock(sock) { } + + ~Server() { } + + typedef std::auto_ptr auto_zone; + + void socket_readable() + { + m_pac.reserve_buffer(1024); + + ssize_t count = + read(m_sock, m_pac.buffer(), m_pac.buffer_capacity()); + + if(count <= 0) { + if(count == 0) { + throw std::runtime_error("connection closed"); + } + if(errno == EAGAIN || errno == EINTR) { + return; + } + throw std::runtime_error(strerror(errno)); + } + + m_pac.buffer_consumed(count); + + while(m_pac.execute()) { + msgpack::object msg = m_pac.data(); + + auto_zone life( m_pac.release_zone() ); + + m_pac.reset(); + + process_message(msg, life); + } + + if(m_pac.message_size() > 10*1024*1024) { + throw std::runtime_error("message is too large"); + } + } + +private: + void process_message(msgpack::object msg, auto_zone& life) + { + std::cout << "message reached: " << msg << std::endl; + } + +private: + int m_sock; + msgpack::unpacker m_pac; +}; + + +static void* run_server(void* arg) +try { + Server* srv = reinterpret_cast(arg); + + while(true) { + srv->socket_readable(); + } + return NULL; + +} catch (std::exception& e) { + std::cerr << "error while processing client packet: " + << e.what() << std::endl; + return NULL; + +} catch (...) { + std::cerr << "error while processing client packet: " + << "unknown error" << std::endl; + return NULL; +} + + +struct fwriter { + fwriter(int fd) : m_fp( fdopen(fd, "w") ) { } + + void write(const char* buf, size_t buflen) + { + size_t count = fwrite(buf, buflen, 1, m_fp); + if(count < 1) { + std::cout << buflen << std::endl; + std::cout << count << std::endl; + throw std::runtime_error(strerror(errno)); + } + } + + void flush() { fflush(m_fp); } + + void close() { fclose(m_fp); } + +private: + FILE* m_fp; +}; + + +int main(void) +{ + int pair[2]; + pipe(pair); + + // run server thread + Server srv(pair[0]); + pthread_t thread; + pthread_create(&thread, NULL, + run_server, reinterpret_cast(&srv)); + + // client thread: + fwriter writer(pair[1]); + msgpack::packer pk(writer); + + typedef msgpack::type::tuple put_t; + typedef msgpack::type::tuple get_t; + + put_t req1("put", "apple", "red"); + put_t req2("put", "lemon", "yellow"); + get_t req3("get", "apple"); + pk.pack(req1); + pk.pack(req2); + pk.pack(req3); + writer.flush(); + writer.close(); + + pthread_join(thread, NULL); +} + diff --git a/msgpack/msgpack.pc.in b/msgpack/msgpack.pc.in new file mode 100644 index 00000000..8c34bef9 --- /dev/null +++ b/msgpack/msgpack.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: MessagePack +Description: Binary-based efficient object serialization library +Version: @VERSION@ +Libs: -L${libdir} -lmsgpack +Cflags: -I${includedir} diff --git a/msgpack/msgpack_vc.postbuild.bat b/msgpack/msgpack_vc.postbuild.bat new file mode 100644 index 00000000..c7791e6a --- /dev/null +++ b/msgpack/msgpack_vc.postbuild.bat @@ -0,0 +1,45 @@ +IF NOT EXIST include MKDIR include +IF NOT EXIST include\msgpack MKDIR include\msgpack +IF NOT EXIST include\msgpack\type MKDIR include\msgpack\type +IF NOT EXIST include\msgpack\type\tr1 MKDIR include\msgpack\type\tr1 +copy src\msgpack\pack_define.h include\msgpack\ +copy src\msgpack\pack_template.h include\msgpack\ +copy src\msgpack\unpack_define.h include\msgpack\ +copy src\msgpack\unpack_template.h include\msgpack\ +copy src\msgpack\sysdep.h include\msgpack\ +copy src\msgpack.h include\ +copy src\msgpack\sbuffer.h include\msgpack\ +copy src\msgpack\version.h include\msgpack\ +copy src\msgpack\vrefbuffer.h include\msgpack\ +copy src\msgpack\zbuffer.h include\msgpack\ +copy src\msgpack\pack.h include\msgpack\ +copy src\msgpack\unpack.h include\msgpack\ +copy src\msgpack\object.h include\msgpack\ +copy src\msgpack\zone.h include\msgpack\ +copy src\msgpack.hpp include\ +copy src\msgpack\sbuffer.hpp include\msgpack\ +copy src\msgpack\vrefbuffer.hpp include\msgpack\ +copy src\msgpack\zbuffer.hpp include\msgpack\ +copy src\msgpack\pack.hpp include\msgpack\ +copy src\msgpack\unpack.hpp include\msgpack\ +copy src\msgpack\object.hpp include\msgpack\ +copy src\msgpack\zone.hpp include\msgpack\ +copy src\msgpack\type.hpp include\msgpack\ +copy src\msgpack\type\bool.hpp include\msgpack\type\ +copy src\msgpack\type\deque.hpp include\msgpack\type\ +copy src\msgpack\type\fixint.hpp include\msgpack\type\ +copy src\msgpack\type\float.hpp include\msgpack\type\ +copy src\msgpack\type\int.hpp include\msgpack\type\ +copy src\msgpack\type\list.hpp include\msgpack\type\ +copy src\msgpack\type\map.hpp include\msgpack\type\ +copy src\msgpack\type\nil.hpp include\msgpack\type\ +copy src\msgpack\type\pair.hpp include\msgpack\type\ +copy src\msgpack\type\raw.hpp include\msgpack\type\ +copy src\msgpack\type\set.hpp include\msgpack\type\ +copy src\msgpack\type\string.hpp include\msgpack\type\ +copy src\msgpack\type\vector.hpp include\msgpack\type\ +copy src\msgpack\type\tuple.hpp include\msgpack\type\ +copy src\msgpack\type\define.hpp include\msgpack\type\ +copy src\msgpack\type\tr1\unordered_map.hpp include\msgpack\type\tr1\ +copy src\msgpack\type\tr1\unordered_set.hpp include\msgpack\type\tr1\ + diff --git a/msgpack/msgpack_vc8.sln b/msgpack/msgpack_vc8.sln new file mode 100644 index 00000000..84718afa --- /dev/null +++ b/msgpack/msgpack_vc8.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MessagePack", "msgpack_vc8.vcproj", "{122A2EA4-B283-4241-9655-786DE78283B2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {122A2EA4-B283-4241-9655-786DE78283B2}.Debug|Win32.ActiveCfg = Debug|Win32 + {122A2EA4-B283-4241-9655-786DE78283B2}.Debug|Win32.Build.0 = Debug|Win32 + {122A2EA4-B283-4241-9655-786DE78283B2}.Release|Win32.ActiveCfg = Release|Win32 + {122A2EA4-B283-4241-9655-786DE78283B2}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/msgpack/msgpack_vc8.vcproj b/msgpack/msgpack_vc8.vcproj new file mode 100644 index 00000000..72d47b6b --- /dev/null +++ b/msgpack/msgpack_vc8.vcproj @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msgpack/pack_define.h b/msgpack/pack_define.h new file mode 100644 index 00000000..4845d52e --- /dev/null +++ b/msgpack/pack_define.h @@ -0,0 +1,26 @@ +/* + * MessagePack unpacking routine template + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_PACK_DEFINE_H__ +#define MSGPACK_PACK_DEFINE_H__ + +#include "msgpack/sysdep.h" +#include +#include + +#endif /* msgpack/pack_define.h */ + diff --git a/msgpack/pack_template.h b/msgpack/pack_template.h new file mode 100644 index 00000000..65c959dd --- /dev/null +++ b/msgpack/pack_template.h @@ -0,0 +1,771 @@ +/* + * MessagePack packing routine template + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(__LITTLE_ENDIAN__) +#define TAKE8_8(d) ((uint8_t*)&d)[0] +#define TAKE8_16(d) ((uint8_t*)&d)[0] +#define TAKE8_32(d) ((uint8_t*)&d)[0] +#define TAKE8_64(d) ((uint8_t*)&d)[0] +#elif defined(__BIG_ENDIAN__) +#define TAKE8_8(d) ((uint8_t*)&d)[0] +#define TAKE8_16(d) ((uint8_t*)&d)[1] +#define TAKE8_32(d) ((uint8_t*)&d)[3] +#define TAKE8_64(d) ((uint8_t*)&d)[7] +#endif + +#ifndef msgpack_pack_inline_func +#error msgpack_pack_inline_func template is not defined +#endif + +#ifndef msgpack_pack_user +#error msgpack_pack_user type is not defined +#endif + +#ifndef msgpack_pack_append_buffer +#error msgpack_pack_append_buffer callback is not defined +#endif + + +/* + * Integer + */ + +#define msgpack_pack_real_uint8(x, d) \ +do { \ + if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \ + } else { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_8(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ +} while(0) + +#define msgpack_pack_real_uint16(x, d) \ +do { \ + if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \ + } else if(d < (1<<8)) { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } \ +} while(0) + +#define msgpack_pack_real_uint32(x, d) \ +do { \ + if(d < (1<<8)) { \ + if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \ + } else { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } else { \ + if(d < (1<<16)) { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* unsigned 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } \ + } \ +} while(0) + +#define msgpack_pack_real_uint64(x, d) \ +do { \ + if(d < (1ULL<<8)) { \ + if(d < (1ULL<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \ + } else { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } else { \ + if(d < (1ULL<<16)) { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else if(d < (1ULL<<32)) { \ + /* unsigned 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } else { \ + /* unsigned 64 */ \ + unsigned char buf[9]; \ + buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \ + msgpack_pack_append_buffer(x, buf, 9); \ + } \ + } \ +} while(0) + +#define msgpack_pack_real_int8(x, d) \ +do { \ + if(d < -(1<<5)) { \ + /* signed 8 */ \ + unsigned char buf[2] = {0xd0, TAKE8_8(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \ + } \ +} while(0) + +#define msgpack_pack_real_int16(x, d) \ +do { \ + if(d < -(1<<5)) { \ + if(d < -(1<<7)) { \ + /* signed 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* signed 8 */ \ + unsigned char buf[2] = {0xd0, TAKE8_16(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } else if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \ + } else { \ + if(d < (1<<8)) { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } \ + } \ +} while(0) + +#define msgpack_pack_real_int32(x, d) \ +do { \ + if(d < -(1<<5)) { \ + if(d < -(1<<15)) { \ + /* signed 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } else if(d < -(1<<7)) { \ + /* signed 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* signed 8 */ \ + unsigned char buf[2] = {0xd0, TAKE8_32(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } else if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \ + } else { \ + if(d < (1<<8)) { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else if(d < (1<<16)) { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* unsigned 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } \ + } \ +} while(0) + +#define msgpack_pack_real_int64(x, d) \ +do { \ + if(d < -(1LL<<5)) { \ + if(d < -(1LL<<15)) { \ + if(d < -(1LL<<31)) { \ + /* signed 64 */ \ + unsigned char buf[9]; \ + buf[0] = 0xd3; _msgpack_store64(&buf[1], d); \ + msgpack_pack_append_buffer(x, buf, 9); \ + } else { \ + /* signed 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } \ + } else { \ + if(d < -(1<<7)) { \ + /* signed 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } else { \ + /* signed 8 */ \ + unsigned char buf[2] = {0xd0, TAKE8_64(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } \ + } \ + } else if(d < (1<<7)) { \ + /* fixnum */ \ + msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \ + } else { \ + if(d < (1LL<<16)) { \ + if(d < (1<<8)) { \ + /* unsigned 8 */ \ + unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \ + msgpack_pack_append_buffer(x, buf, 2); \ + } else { \ + /* unsigned 16 */ \ + unsigned char buf[3]; \ + buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ + msgpack_pack_append_buffer(x, buf, 3); \ + } \ + } else { \ + if(d < (1LL<<32)) { \ + /* unsigned 32 */ \ + unsigned char buf[5]; \ + buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ + msgpack_pack_append_buffer(x, buf, 5); \ + } else { \ + /* unsigned 64 */ \ + unsigned char buf[9]; \ + buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \ + msgpack_pack_append_buffer(x, buf, 9); \ + } \ + } \ + } \ +} while(0) + + +#ifdef msgpack_pack_inline_func_fixint + +msgpack_pack_inline_func_fixint(_uint8)(msgpack_pack_user x, uint8_t d) +{ + unsigned char buf[2] = {0xcc, TAKE8_8(d)}; + msgpack_pack_append_buffer(x, buf, 2); +} + +msgpack_pack_inline_func_fixint(_uint16)(msgpack_pack_user x, uint16_t d) +{ + unsigned char buf[3]; + buf[0] = 0xcd; _msgpack_store16(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 3); +} + +msgpack_pack_inline_func_fixint(_uint32)(msgpack_pack_user x, uint32_t d) +{ + unsigned char buf[5]; + buf[0] = 0xce; _msgpack_store32(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 5); +} + +msgpack_pack_inline_func_fixint(_uint64)(msgpack_pack_user x, uint64_t d) +{ + unsigned char buf[9]; + buf[0] = 0xcf; _msgpack_store64(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 9); +} + +msgpack_pack_inline_func_fixint(_int8)(msgpack_pack_user x, int8_t d) +{ + unsigned char buf[2] = {0xd0, TAKE8_8(d)}; + msgpack_pack_append_buffer(x, buf, 2); +} + +msgpack_pack_inline_func_fixint(_int16)(msgpack_pack_user x, int16_t d) +{ + unsigned char buf[3]; + buf[0] = 0xd1; _msgpack_store16(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 3); +} + +msgpack_pack_inline_func_fixint(_int32)(msgpack_pack_user x, int32_t d) +{ + unsigned char buf[5]; + buf[0] = 0xd2; _msgpack_store32(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 5); +} + +msgpack_pack_inline_func_fixint(_int64)(msgpack_pack_user x, int64_t d) +{ + unsigned char buf[9]; + buf[0] = 0xd3; _msgpack_store64(&buf[1], d); + msgpack_pack_append_buffer(x, buf, 9); +} + +#undef msgpack_pack_inline_func_fixint +#endif + + +msgpack_pack_inline_func(_uint8)(msgpack_pack_user x, uint8_t d) +{ + msgpack_pack_real_uint8(x, d); +} + +msgpack_pack_inline_func(_uint16)(msgpack_pack_user x, uint16_t d) +{ + msgpack_pack_real_uint16(x, d); +} + +msgpack_pack_inline_func(_uint32)(msgpack_pack_user x, uint32_t d) +{ + msgpack_pack_real_uint32(x, d); +} + +msgpack_pack_inline_func(_uint64)(msgpack_pack_user x, uint64_t d) +{ + msgpack_pack_real_uint64(x, d); +} + +msgpack_pack_inline_func(_int8)(msgpack_pack_user x, int8_t d) +{ + msgpack_pack_real_int8(x, d); +} + +msgpack_pack_inline_func(_int16)(msgpack_pack_user x, int16_t d) +{ + msgpack_pack_real_int16(x, d); +} + +msgpack_pack_inline_func(_int32)(msgpack_pack_user x, int32_t d) +{ + msgpack_pack_real_int32(x, d); +} + +msgpack_pack_inline_func(_int64)(msgpack_pack_user x, int64_t d) +{ + msgpack_pack_real_int64(x, d); +} + + +#ifdef msgpack_pack_inline_func_cint + +msgpack_pack_inline_func_cint(_short)(msgpack_pack_user x, short d) +{ +#if defined(SIZEOF_SHORT) +#if SIZEOF_SHORT == 2 + msgpack_pack_real_int16(x, d); +#elif SIZEOF_SHORT == 4 + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#elif defined(SHRT_MAX) +#if SHRT_MAX == 0x7fff + msgpack_pack_real_int16(x, d); +#elif SHRT_MAX == 0x7fffffff + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#else +if(sizeof(short) == 2) { + msgpack_pack_real_int16(x, d); +} else if(sizeof(short) == 4) { + msgpack_pack_real_int32(x, d); +} else { + msgpack_pack_real_int64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_int)(msgpack_pack_user x, int d) +{ +#if defined(SIZEOF_INT) +#if SIZEOF_INT == 2 + msgpack_pack_real_int16(x, d); +#elif SIZEOF_INT == 4 + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#elif defined(INT_MAX) +#if INT_MAX == 0x7fff + msgpack_pack_real_int16(x, d); +#elif INT_MAX == 0x7fffffff + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#else +if(sizeof(int) == 2) { + msgpack_pack_real_int16(x, d); +} else if(sizeof(int) == 4) { + msgpack_pack_real_int32(x, d); +} else { + msgpack_pack_real_int64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_long)(msgpack_pack_user x, long d) +{ +#if defined(SIZEOF_LONG) +#if SIZEOF_LONG == 2 + msgpack_pack_real_int16(x, d); +#elif SIZEOF_LONG == 4 + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#elif defined(LONG_MAX) +#if LONG_MAX == 0x7fffL + msgpack_pack_real_int16(x, d); +#elif LONG_MAX == 0x7fffffffL + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#else +if(sizeof(long) == 2) { + msgpack_pack_real_int16(x, d); +} else if(sizeof(long) == 4) { + msgpack_pack_real_int32(x, d); +} else { + msgpack_pack_real_int64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_long_long)(msgpack_pack_user x, long long d) +{ +#if defined(SIZEOF_LONG_LONG) +#if SIZEOF_LONG_LONG == 2 + msgpack_pack_real_int16(x, d); +#elif SIZEOF_LONG_LONG == 4 + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#elif defined(LLONG_MAX) +#if LLONG_MAX == 0x7fffL + msgpack_pack_real_int16(x, d); +#elif LLONG_MAX == 0x7fffffffL + msgpack_pack_real_int32(x, d); +#else + msgpack_pack_real_int64(x, d); +#endif + +#else +if(sizeof(long long) == 2) { + msgpack_pack_real_int16(x, d); +} else if(sizeof(long long) == 4) { + msgpack_pack_real_int32(x, d); +} else { + msgpack_pack_real_int64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_unsigned_short)(msgpack_pack_user x, unsigned short d) +{ +#if defined(SIZEOF_SHORT) +#if SIZEOF_SHORT == 2 + msgpack_pack_real_uint16(x, d); +#elif SIZEOF_SHORT == 4 + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#elif defined(USHRT_MAX) +#if USHRT_MAX == 0xffffU + msgpack_pack_real_uint16(x, d); +#elif USHRT_MAX == 0xffffffffU + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#else +if(sizeof(unsigned short) == 2) { + msgpack_pack_real_uint16(x, d); +} else if(sizeof(unsigned short) == 4) { + msgpack_pack_real_uint32(x, d); +} else { + msgpack_pack_real_uint64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_unsigned_int)(msgpack_pack_user x, unsigned int d) +{ +#if defined(SIZEOF_INT) +#if SIZEOF_INT == 2 + msgpack_pack_real_uint16(x, d); +#elif SIZEOF_INT == 4 + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#elif defined(UINT_MAX) +#if UINT_MAX == 0xffffU + msgpack_pack_real_uint16(x, d); +#elif UINT_MAX == 0xffffffffU + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#else +if(sizeof(unsigned int) == 2) { + msgpack_pack_real_uint16(x, d); +} else if(sizeof(unsigned int) == 4) { + msgpack_pack_real_uint32(x, d); +} else { + msgpack_pack_real_uint64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_unsigned_long)(msgpack_pack_user x, unsigned long d) +{ +#if defined(SIZEOF_LONG) +#if SIZEOF_LONG == 2 + msgpack_pack_real_uint16(x, d); +#elif SIZEOF_LONG == 4 + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#elif defined(ULONG_MAX) +#if ULONG_MAX == 0xffffUL + msgpack_pack_real_uint16(x, d); +#elif ULONG_MAX == 0xffffffffUL + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#else +if(sizeof(unsigned long) == 2) { + msgpack_pack_real_uint16(x, d); +} else if(sizeof(unsigned long) == 4) { + msgpack_pack_real_uint32(x, d); +} else { + msgpack_pack_real_uint64(x, d); +} +#endif +} + +msgpack_pack_inline_func_cint(_unsigned_long_long)(msgpack_pack_user x, unsigned long long d) +{ +#if defined(SIZEOF_LONG_LONG) +#if SIZEOF_LONG_LONG == 2 + msgpack_pack_real_uint16(x, d); +#elif SIZEOF_LONG_LONG == 4 + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#elif defined(ULLONG_MAX) +#if ULLONG_MAX == 0xffffUL + msgpack_pack_real_uint16(x, d); +#elif ULLONG_MAX == 0xffffffffUL + msgpack_pack_real_uint32(x, d); +#else + msgpack_pack_real_uint64(x, d); +#endif + +#else +if(sizeof(unsigned long long) == 2) { + msgpack_pack_real_uint16(x, d); +} else if(sizeof(unsigned long long) == 4) { + msgpack_pack_real_uint32(x, d); +} else { + msgpack_pack_real_uint64(x, d); +} +#endif +} + +#undef msgpack_pack_inline_func_cint +#endif + + + +/* + * Float + */ + +msgpack_pack_inline_func(_float)(msgpack_pack_user x, float d) +{ + union { float f; uint32_t i; } mem; + mem.f = d; + unsigned char buf[5]; + buf[0] = 0xca; _msgpack_store32(&buf[1], mem.i); + msgpack_pack_append_buffer(x, buf, 5); +} + +msgpack_pack_inline_func(_double)(msgpack_pack_user x, double d) +{ + union { double f; uint64_t i; } mem; + mem.f = d; + unsigned char buf[9]; + buf[0] = 0xcb; +#if defined(__arm__) && !(__ARM_EABI__) // arm-oabi + // https://github.com/msgpack/msgpack-perl/pull/1 + mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL); +#endif + _msgpack_store64(&buf[1], mem.i); + msgpack_pack_append_buffer(x, buf, 9); +} + + +/* + * Nil + */ + +msgpack_pack_inline_func(_nil)(msgpack_pack_user x) +{ + static const unsigned char d = 0xc0; + msgpack_pack_append_buffer(x, &d, 1); +} + + +/* + * Boolean + */ + +msgpack_pack_inline_func(_true)(msgpack_pack_user x) +{ + static const unsigned char d = 0xc3; + msgpack_pack_append_buffer(x, &d, 1); +} + +msgpack_pack_inline_func(_false)(msgpack_pack_user x) +{ + static const unsigned char d = 0xc2; + msgpack_pack_append_buffer(x, &d, 1); +} + + +/* + * Array + */ + +msgpack_pack_inline_func(_array)(msgpack_pack_user x, unsigned int n) +{ + if(n < 16) { + unsigned char d = 0x90 | n; + msgpack_pack_append_buffer(x, &d, 1); + } else if(n < 65536) { + unsigned char buf[3]; + buf[0] = 0xdc; _msgpack_store16(&buf[1], (uint16_t)n); + msgpack_pack_append_buffer(x, buf, 3); + } else { + unsigned char buf[5]; + buf[0] = 0xdd; _msgpack_store32(&buf[1], (uint32_t)n); + msgpack_pack_append_buffer(x, buf, 5); + } +} + + +/* + * Map + */ + +msgpack_pack_inline_func(_map)(msgpack_pack_user x, unsigned int n) +{ + if(n < 16) { + unsigned char d = 0x80 | n; + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); + } else if(n < 65536) { + unsigned char buf[3]; + buf[0] = 0xde; _msgpack_store16(&buf[1], (uint16_t)n); + msgpack_pack_append_buffer(x, buf, 3); + } else { + unsigned char buf[5]; + buf[0] = 0xdf; _msgpack_store32(&buf[1], (uint32_t)n); + msgpack_pack_append_buffer(x, buf, 5); + } +} + + +/* + * Raw + */ + +msgpack_pack_inline_func(_raw)(msgpack_pack_user x, size_t l) +{ + if(l < 32) { + unsigned char d = 0xa0 | (uint8_t)l; + msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); + } else if(l < 65536) { + unsigned char buf[3]; + buf[0] = 0xda; _msgpack_store16(&buf[1], (uint16_t)l); + msgpack_pack_append_buffer(x, buf, 3); + } else { + unsigned char buf[5]; + buf[0] = 0xdb; _msgpack_store32(&buf[1], (uint32_t)l); + msgpack_pack_append_buffer(x, buf, 5); + } +} + +msgpack_pack_inline_func(_raw_body)(msgpack_pack_user x, const void* b, size_t l) +{ + msgpack_pack_append_buffer(x, (const unsigned char*)b, l); +} + +#undef msgpack_pack_inline_func +#undef msgpack_pack_user +#undef msgpack_pack_append_buffer + +#undef TAKE8_8 +#undef TAKE8_16 +#undef TAKE8_32 +#undef TAKE8_64 + +#undef msgpack_pack_real_uint8 +#undef msgpack_pack_real_uint16 +#undef msgpack_pack_real_uint32 +#undef msgpack_pack_real_uint64 +#undef msgpack_pack_real_int8 +#undef msgpack_pack_real_int16 +#undef msgpack_pack_real_int32 +#undef msgpack_pack_real_int64 + diff --git a/msgpack/preprocess b/msgpack/preprocess new file mode 100755 index 00000000..839eb137 --- /dev/null +++ b/msgpack/preprocess @@ -0,0 +1,34 @@ +#!/bin/sh + +preprocess() { + ruby -r erb -e 'puts ERB.new(ARGF.read).result' $1.erb > $1.tmp + if [ "$?" != 0 ]; then + echo "" + echo "** preprocess failed **" + echo "" + exit 1 + else + mv $1.tmp $1 + fi +} + +if [ "$1" = "clean" ];then + rm -f src/msgpack/type/tuple.hpp + rm -f src/msgpack/type/define.hpp + rm -f src/msgpack/zone.hpp +else + preprocess src/msgpack/type/tuple.hpp + preprocess src/msgpack/type/define.hpp + preprocess src/msgpack/zone.hpp +fi +cp -f sysdep.h src/msgpack/ +cp -f pack_define.h src/msgpack/ +cp -f pack_template.h src/msgpack/ +cp -f unpack_define.h src/msgpack/ +cp -f unpack_template.h src/msgpack/ +cp -f cases.mpac test/ +cp -f cases_compact.mpac test/ + +sed -e 's/8\.00/9.00/' < msgpack_vc8.vcproj > msgpack_vc2008.vcproj +sed -e 's/9\.00/10.00/' -e 's/msgpack_vc8/msgpack_vc2008/' < msgpack_vc8.sln > msgpack_vc2008.sln + diff --git a/msgpack/src/Makefile.am b/msgpack/src/Makefile.am new file mode 100644 index 00000000..59c65010 --- /dev/null +++ b/msgpack/src/Makefile.am @@ -0,0 +1,107 @@ + +lib_LTLIBRARIES = libmsgpack.la + +libmsgpack_la_SOURCES = \ + unpack.c \ + objectc.c \ + version.c \ + vrefbuffer.c \ + zone.c + +if ENABLE_CXX +libmsgpack_la_SOURCES += \ + object.cpp +endif + +if ENABLE_GCC_CXX_ATOMIC +libmsgpack_la_SOURCES += \ + gcc_atomic.cpp +endif + + +# -version-info CURRENT:REVISION:AGE +libmsgpack_la_LDFLAGS = -version-info 3:0:0 -no-undefined + + +# backward compatibility +lib_LTLIBRARIES += libmsgpackc.la + +libmsgpackc_la_SOURCES = \ + unpack.c \ + objectc.c \ + version.c \ + vrefbuffer.c \ + zone.c + +libmsgpackc_la_LDFLAGS = -version-info 2:0:0 -no-undefined + + +nobase_include_HEADERS = \ + msgpack/pack_define.h \ + msgpack/pack_template.h \ + msgpack/unpack_define.h \ + msgpack/unpack_template.h \ + msgpack/sysdep.h \ + msgpack.h \ + msgpack/sbuffer.h \ + msgpack/version.h \ + msgpack/vrefbuffer.h \ + msgpack/zbuffer.h \ + msgpack/pack.h \ + msgpack/unpack.h \ + msgpack/object.h \ + msgpack/zone.h + +if ENABLE_CXX +nobase_include_HEADERS += \ + msgpack.hpp \ + msgpack/sbuffer.hpp \ + msgpack/vrefbuffer.hpp \ + msgpack/zbuffer.hpp \ + msgpack/pack.hpp \ + msgpack/unpack.hpp \ + msgpack/object.hpp \ + msgpack/zone.hpp \ + msgpack/type.hpp \ + msgpack/type/bool.hpp \ + msgpack/type/deque.hpp \ + msgpack/type/float.hpp \ + msgpack/type/fixint.hpp \ + msgpack/type/int.hpp \ + msgpack/type/list.hpp \ + msgpack/type/map.hpp \ + msgpack/type/nil.hpp \ + msgpack/type/pair.hpp \ + msgpack/type/raw.hpp \ + msgpack/type/set.hpp \ + msgpack/type/string.hpp \ + msgpack/type/vector.hpp \ + msgpack/type/tuple.hpp \ + msgpack/type/define.hpp \ + msgpack/type/tr1/unordered_map.hpp \ + msgpack/type/tr1/unordered_set.hpp +endif + +EXTRA_DIST = \ + msgpack/version.h.in \ + msgpack/zone.hpp.erb \ + msgpack/type/define.hpp.erb \ + msgpack/type/tuple.hpp.erb + + +doxygen_c: + cat ../Doxyfile > Doxyfile_c + echo "FILE_PATTERNS = *.h" >> Doxyfile_c + echo "OUTPUT_DIRECTORY = doc_c" >> Doxyfile_c + echo "PROJECT_NAME = \"MessagePack for C\"" >> Doxyfile_c + doxygen Doxyfile_c + +doxygen_cpp: + cat ../Doxyfile > Doxyfile_cpp + echo "FILE_PATTERNS = *.hpp" >> Doxyfile_cpp + echo "OUTPUT_DIRECTORY = doc_cpp" >> Doxyfile_cpp + echo "PROJECT_NAME = \"MessagePack for C++\"" >> Doxyfile_cpp + doxygen Doxyfile_cpp + +doxygen: doxygen_c doxygen_cpp + diff --git a/msgpack/src/gcc_atomic.cpp b/msgpack/src/gcc_atomic.cpp new file mode 100644 index 00000000..25681b67 --- /dev/null +++ b/msgpack/src/gcc_atomic.cpp @@ -0,0 +1,17 @@ +#if defined(__GNUC__) && ((__GNUC__*10 + __GNUC_MINOR__) < 41) + +#include "gcc_atomic.h" +#include + +int _msgpack_sync_decr_and_fetch(volatile _msgpack_atomic_counter_t* ptr) +{ + return __gnu_cxx::__exchange_and_add(ptr, -1) - 1; +} + +int _msgpack_sync_incr_and_fetch(volatile _msgpack_atomic_counter_t* ptr) +{ + return __gnu_cxx::__exchange_and_add(ptr, 1) + 1; +} + + +#endif // old gcc workaround diff --git a/msgpack/src/gcc_atomic.h b/msgpack/src/gcc_atomic.h new file mode 100644 index 00000000..842a48fb --- /dev/null +++ b/msgpack/src/gcc_atomic.h @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MSGPACK_GCC_ATOMIC_H__ +#define MSGPACK_GCC_ATOMIC_H__ + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef int _msgpack_atomic_counter_t; + +int _msgpack_sync_decr_and_fetch(volatile _msgpack_atomic_counter_t* ptr); +int _msgpack_sync_incr_and_fetch(volatile _msgpack_atomic_counter_t* ptr); + + +#if defined(__cplusplus) +} +#endif + + +#endif // MSGPACK_GCC_ATOMIC_H__ diff --git a/msgpack/src/msgpack.h b/msgpack/src/msgpack.h new file mode 100644 index 00000000..08f27688 --- /dev/null +++ b/msgpack/src/msgpack.h @@ -0,0 +1,30 @@ +/* + * MessagePack for C + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @defgroup msgpack MessagePack C + * @{ + * @} + */ +#include "msgpack/object.h" +#include "msgpack/zone.h" +#include "msgpack/pack.h" +#include "msgpack/unpack.h" +#include "msgpack/sbuffer.h" +#include "msgpack/vrefbuffer.h" +#include "msgpack/version.h" + diff --git a/msgpack/src/msgpack.hpp b/msgpack/src/msgpack.hpp new file mode 100644 index 00000000..e14680dd --- /dev/null +++ b/msgpack/src/msgpack.hpp @@ -0,0 +1,24 @@ +// +// MessagePack for C++ +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "msgpack/object.hpp" +#include "msgpack/zone.hpp" +#include "msgpack/pack.hpp" +#include "msgpack/unpack.hpp" +#include "msgpack/sbuffer.hpp" +#include "msgpack/vrefbuffer.hpp" +#include "msgpack.h" diff --git a/msgpack/src/msgpack/object.h b/msgpack/src/msgpack/object.h new file mode 100644 index 00000000..baeeb9b2 --- /dev/null +++ b/msgpack/src/msgpack/object.h @@ -0,0 +1,98 @@ +/* + * MessagePack for C dynamic typing routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_OBJECT_H__ +#define MSGPACK_OBJECT_H__ + +#include "zone.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_object Dynamically typed object + * @ingroup msgpack + * @{ + */ + +typedef enum { + MSGPACK_OBJECT_NIL = 0x00, + MSGPACK_OBJECT_BOOLEAN = 0x01, + MSGPACK_OBJECT_POSITIVE_INTEGER = 0x02, + MSGPACK_OBJECT_NEGATIVE_INTEGER = 0x03, + MSGPACK_OBJECT_DOUBLE = 0x04, + MSGPACK_OBJECT_RAW = 0x05, + MSGPACK_OBJECT_ARRAY = 0x06, + MSGPACK_OBJECT_MAP = 0x07, +} msgpack_object_type; + + +struct msgpack_object; +struct msgpack_object_kv; + +typedef struct { + uint32_t size; + struct msgpack_object* ptr; +} msgpack_object_array; + +typedef struct { + uint32_t size; + struct msgpack_object_kv* ptr; +} msgpack_object_map; + +typedef struct { + uint32_t size; + const char* ptr; +} msgpack_object_raw; + +typedef union { + bool boolean; + uint64_t u64; + int64_t i64; + double dec; + msgpack_object_array array; + msgpack_object_map map; + msgpack_object_raw raw; +} msgpack_object_union; + +typedef struct msgpack_object { + msgpack_object_type type; + msgpack_object_union via; +} msgpack_object; + +typedef struct msgpack_object_kv { + msgpack_object key; + msgpack_object val; +} msgpack_object_kv; + + +void msgpack_object_print(FILE* out, msgpack_object o); + +bool msgpack_object_equal(const msgpack_object x, const msgpack_object y); + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/object.h */ + diff --git a/msgpack/src/msgpack/object.hpp b/msgpack/src/msgpack/object.hpp new file mode 100644 index 00000000..97c8d4aa --- /dev/null +++ b/msgpack/src/msgpack/object.hpp @@ -0,0 +1,412 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_OBJECT_HPP__ +#define MSGPACK_OBJECT_HPP__ + +#include "object.h" +#include "pack.hpp" +#include "zone.hpp" +#include +#include +#include +#include +#include + +namespace msgpack { + + +class type_error : public std::bad_cast { }; + + +namespace type { + enum object_type { + NIL = MSGPACK_OBJECT_NIL, + BOOLEAN = MSGPACK_OBJECT_BOOLEAN, + POSITIVE_INTEGER = MSGPACK_OBJECT_POSITIVE_INTEGER, + NEGATIVE_INTEGER = MSGPACK_OBJECT_NEGATIVE_INTEGER, + DOUBLE = MSGPACK_OBJECT_DOUBLE, + RAW = MSGPACK_OBJECT_RAW, + ARRAY = MSGPACK_OBJECT_ARRAY, + MAP = MSGPACK_OBJECT_MAP, + }; +} + + +struct object; +struct object_kv; + +struct object_array { + uint32_t size; + object* ptr; +}; + +struct object_map { + uint32_t size; + object_kv* ptr; +}; + +struct object_raw { + uint32_t size; + const char* ptr; +}; + +struct object { + union union_type { + bool boolean; + uint64_t u64; + int64_t i64; + double dec; + object_array array; + object_map map; + object_raw raw; + object_raw ref; // obsolete + }; + + type::object_type type; + union_type via; + + bool is_nil() const { return type == type::NIL; } + + template + T as() const; + + template + void convert(T* v) const; + + object(); + + object(msgpack_object o); + + template + explicit object(const T& v); + + template + object(const T& v, zone* z); + + template + object& operator=(const T& v); + + operator msgpack_object() const; + + struct with_zone; + +private: + struct implicit_type; + +public: + implicit_type convert() const; +}; + +struct object_kv { + object key; + object val; +}; + +struct object::with_zone : object { + with_zone(msgpack::zone* zone) : zone(zone) { } + msgpack::zone* zone; +private: + with_zone(); +}; + + +bool operator==(const object x, const object y); +bool operator!=(const object x, const object y); + +template +bool operator==(const object x, const T& y); + +template +bool operator==(const T& y, const object x); + +template +bool operator!=(const object x, const T& y); + +template +bool operator!=(const T& y, const object x); + +std::ostream& operator<< (std::ostream& s, const object o); + + +// serialize operator +template +packer& operator<< (packer& o, const T& v); + +// convert operator +template +T& operator>> (object o, T& v); + +// deconvert operator +template +void operator<< (object::with_zone& o, const T& v); + + +struct object::implicit_type { + implicit_type(object o) : obj(o) { } + ~implicit_type() { } + + template + operator T() { return obj.as(); } + +private: + object obj; +}; + + +// obsolete +template +class define : public Type { +public: + typedef Type msgpack_type; + typedef define define_type; + + define() {} + define(const msgpack_type& v) : msgpack_type(v) {} + + template + void msgpack_pack(Packer& o) const + { + o << static_cast(*this); + } + + void msgpack_unpack(object o) + { + o >> static_cast(*this); + } +}; + + +template +template +inline packer& packer::pack(const T& v) +{ + *this << v; + return *this; +} + +inline object& operator>> (object o, object& v) +{ + v = o; + return v; +} + +template +inline T& operator>> (object o, T& v) +{ + v.msgpack_unpack(o.convert()); + return v; +} + +template +inline packer& operator<< (packer& o, const T& v) +{ + v.msgpack_pack(o); + return o; +} + +template +void operator<< (object::with_zone& o, const T& v) +{ + v.msgpack_object(static_cast(&o), o.zone); +} + + +inline bool operator==(const object x, const object y) +{ + return msgpack_object_equal(x, y); +} + +template +inline bool operator==(const object x, const T& y) +try { + return x == object(y); +} catch (msgpack::type_error&) { + return false; +} + +inline bool operator!=(const object x, const object y) +{ return !(x == y); } + +template +inline bool operator==(const T& y, const object x) +{ return x == y; } + +template +inline bool operator!=(const object x, const T& y) +{ return !(x == y); } + +template +inline bool operator!=(const T& y, const object x) +{ return x != y; } + + +inline object::implicit_type object::convert() const +{ + return implicit_type(*this); +} + +template +inline void object::convert(T* v) const +{ + *this >> *v; +} + +template +inline T object::as() const +{ + T v; + convert(&v); + return v; +} + + +inline object::object() +{ + type = type::NIL; +} + +template +inline object::object(const T& v) +{ + *this << v; +} + +template +inline object& object::operator=(const T& v) +{ + *this = object(v); + return *this; +} + +template +object::object(const T& v, zone* z) +{ + with_zone oz(z); + oz << v; + type = oz.type; + via = oz.via; +} + + +inline object::object(msgpack_object o) +{ + // FIXME beter way? + ::memcpy(this, &o, sizeof(o)); +} + +inline void operator<< (object& o, msgpack_object v) +{ + // FIXME beter way? + ::memcpy(&o, &v, sizeof(v)); +} + +inline object::operator msgpack_object() const +{ + // FIXME beter way? + msgpack_object obj; + ::memcpy(&obj, this, sizeof(obj)); + return obj; +} + + +// obsolete +template +inline void convert(T& v, object o) +{ + o.convert(&v); +} + +// obsolete +template +inline void pack(packer& o, const T& v) +{ + o.pack(v); +} + +// obsolete +template +inline void pack_copy(packer& o, T v) +{ + pack(o, v); +} + + +template +packer& operator<< (packer& o, const object& v) +{ + switch(v.type) { + case type::NIL: + o.pack_nil(); + return o; + + case type::BOOLEAN: + if(v.via.boolean) { + o.pack_true(); + } else { + o.pack_false(); + } + return o; + + case type::POSITIVE_INTEGER: + o.pack_uint64(v.via.u64); + return o; + + case type::NEGATIVE_INTEGER: + o.pack_int64(v.via.i64); + return o; + + case type::DOUBLE: + o.pack_double(v.via.dec); + return o; + + case type::RAW: + o.pack_raw(v.via.raw.size); + o.pack_raw_body(v.via.raw.ptr, v.via.raw.size); + return o; + + case type::ARRAY: + o.pack_array(v.via.array.size); + for(object* p(v.via.array.ptr), + * const pend(v.via.array.ptr + v.via.array.size); + p < pend; ++p) { + o << *p; + } + return o; + + case type::MAP: + o.pack_map(v.via.map.size); + for(object_kv* p(v.via.map.ptr), + * const pend(v.via.map.ptr + v.via.map.size); + p < pend; ++p) { + o << p->key; + o << p->val; + } + return o; + + default: + throw type_error(); + } +} + + +} // namespace msgpack + +#include "msgpack/type.hpp" + +#endif /* msgpack/object.hpp */ + diff --git a/msgpack/src/msgpack/pack.h b/msgpack/src/msgpack/pack.h new file mode 100644 index 00000000..f86e9299 --- /dev/null +++ b/msgpack/src/msgpack/pack.h @@ -0,0 +1,143 @@ +/* + * MessagePack for C packing routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_PACK_H__ +#define MSGPACK_PACK_H__ + +#include "pack_define.h" +#include "object.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_buffer Buffers + * @ingroup msgpack + * @{ + * @} + */ + +/** + * @defgroup msgpack_pack Serializer + * @ingroup msgpack + * @{ + */ + +typedef int (*msgpack_packer_write)(void* data, const char* buf, unsigned int len); + +typedef struct msgpack_packer { + void* data; + msgpack_packer_write callback; +} msgpack_packer; + +static void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback); + +static msgpack_packer* msgpack_packer_new(void* data, msgpack_packer_write callback); +static void msgpack_packer_free(msgpack_packer* pk); + +static int msgpack_pack_short(msgpack_packer* pk, short d); +static int msgpack_pack_int(msgpack_packer* pk, int d); +static int msgpack_pack_long(msgpack_packer* pk, long d); +static int msgpack_pack_long_long(msgpack_packer* pk, long long d); +static int msgpack_pack_unsigned_short(msgpack_packer* pk, unsigned short d); +static int msgpack_pack_unsigned_int(msgpack_packer* pk, unsigned int d); +static int msgpack_pack_unsigned_long(msgpack_packer* pk, unsigned long d); +static int msgpack_pack_unsigned_long_long(msgpack_packer* pk, unsigned long long d); + +static int msgpack_pack_uint8(msgpack_packer* pk, uint8_t d); +static int msgpack_pack_uint16(msgpack_packer* pk, uint16_t d); +static int msgpack_pack_uint32(msgpack_packer* pk, uint32_t d); +static int msgpack_pack_uint64(msgpack_packer* pk, uint64_t d); +static int msgpack_pack_int8(msgpack_packer* pk, int8_t d); +static int msgpack_pack_int16(msgpack_packer* pk, int16_t d); +static int msgpack_pack_int32(msgpack_packer* pk, int32_t d); +static int msgpack_pack_int64(msgpack_packer* pk, int64_t d); + +static int msgpack_pack_fix_uint8(msgpack_packer* pk, uint8_t d); +static int msgpack_pack_fix_uint16(msgpack_packer* pk, uint16_t d); +static int msgpack_pack_fix_uint32(msgpack_packer* pk, uint32_t d); +static int msgpack_pack_fix_uint64(msgpack_packer* pk, uint64_t d); +static int msgpack_pack_fix_int8(msgpack_packer* pk, int8_t d); +static int msgpack_pack_fix_int16(msgpack_packer* pk, int16_t d); +static int msgpack_pack_fix_int32(msgpack_packer* pk, int32_t d); +static int msgpack_pack_fix_int64(msgpack_packer* pk, int64_t d); + +static int msgpack_pack_float(msgpack_packer* pk, float d); +static int msgpack_pack_double(msgpack_packer* pk, double d); + +static int msgpack_pack_nil(msgpack_packer* pk); +static int msgpack_pack_true(msgpack_packer* pk); +static int msgpack_pack_false(msgpack_packer* pk); + +static int msgpack_pack_array(msgpack_packer* pk, unsigned int n); + +static int msgpack_pack_map(msgpack_packer* pk, unsigned int n); + +static int msgpack_pack_raw(msgpack_packer* pk, size_t l); +static int msgpack_pack_raw_body(msgpack_packer* pk, const void* b, size_t l); + +int msgpack_pack_object(msgpack_packer* pk, msgpack_object d); + + +/** @} */ + + +#define msgpack_pack_inline_func(name) \ + inline int msgpack_pack ## name + +#define msgpack_pack_inline_func_cint(name) \ + inline int msgpack_pack ## name + +#define msgpack_pack_inline_func_fixint(name) \ + inline int msgpack_pack_fix ## name + +#define msgpack_pack_user msgpack_packer* + +#define msgpack_pack_append_buffer(user, buf, len) \ + return (*(user)->callback)((user)->data, (const char*)buf, len) + +#include "pack_template.h" + +inline void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback) +{ + pk->data = data; + pk->callback = callback; +} + +inline msgpack_packer* msgpack_packer_new(void* data, msgpack_packer_write callback) +{ + msgpack_packer* pk = (msgpack_packer*)calloc(1, sizeof(msgpack_packer)); + if(!pk) { return NULL; } + msgpack_packer_init(pk, data, callback); + return pk; +} + +inline void msgpack_packer_free(msgpack_packer* pk) +{ + free(pk); +} + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/pack.h */ + diff --git a/msgpack/src/msgpack/pack.hpp b/msgpack/src/msgpack/pack.hpp new file mode 100644 index 00000000..0090b961 --- /dev/null +++ b/msgpack/src/msgpack/pack.hpp @@ -0,0 +1,318 @@ +// +// MessagePack for C++ serializing routine +// +// Copyright (C) 2008-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_PACK_HPP__ +#define MSGPACK_PACK_HPP__ + +#include "pack_define.h" +#include +#include + +namespace msgpack { + + +template +class packer { +public: + packer(Stream* s); + packer(Stream& s); + ~packer(); + +public: + template + packer& pack(const T& v); + + packer& pack_uint8(uint8_t d); + packer& pack_uint16(uint16_t d); + packer& pack_uint32(uint32_t d); + packer& pack_uint64(uint64_t d); + packer& pack_int8(int8_t d); + packer& pack_int16(int16_t d); + packer& pack_int32(int32_t d); + packer& pack_int64(int64_t d); + + packer& pack_fix_uint8(uint8_t d); + packer& pack_fix_uint16(uint16_t d); + packer& pack_fix_uint32(uint32_t d); + packer& pack_fix_uint64(uint64_t d); + packer& pack_fix_int8(int8_t d); + packer& pack_fix_int16(int16_t d); + packer& pack_fix_int32(int32_t d); + packer& pack_fix_int64(int64_t d); + + packer& pack_short(short d); + packer& pack_int(int d); + packer& pack_long(long d); + packer& pack_long_long(long long d); + packer& pack_unsigned_short(unsigned short d); + packer& pack_unsigned_int(unsigned int d); + packer& pack_unsigned_long(unsigned long d); + packer& pack_unsigned_long_long(unsigned long long d); + + packer& pack_float(float d); + packer& pack_double(double d); + + packer& pack_nil(); + packer& pack_true(); + packer& pack_false(); + + packer& pack_array(unsigned int n); + + packer& pack_map(unsigned int n); + + packer& pack_raw(size_t l); + packer& pack_raw_body(const char* b, size_t l); + +private: + static void _pack_uint8(Stream& x, uint8_t d); + static void _pack_uint16(Stream& x, uint16_t d); + static void _pack_uint32(Stream& x, uint32_t d); + static void _pack_uint64(Stream& x, uint64_t d); + static void _pack_int8(Stream& x, int8_t d); + static void _pack_int16(Stream& x, int16_t d); + static void _pack_int32(Stream& x, int32_t d); + static void _pack_int64(Stream& x, int64_t d); + + static void _pack_fix_uint8(Stream& x, uint8_t d); + static void _pack_fix_uint16(Stream& x, uint16_t d); + static void _pack_fix_uint32(Stream& x, uint32_t d); + static void _pack_fix_uint64(Stream& x, uint64_t d); + static void _pack_fix_int8(Stream& x, int8_t d); + static void _pack_fix_int16(Stream& x, int16_t d); + static void _pack_fix_int32(Stream& x, int32_t d); + static void _pack_fix_int64(Stream& x, int64_t d); + + static void _pack_short(Stream& x, short d); + static void _pack_int(Stream& x, int d); + static void _pack_long(Stream& x, long d); + static void _pack_long_long(Stream& x, long long d); + static void _pack_unsigned_short(Stream& x, unsigned short d); + static void _pack_unsigned_int(Stream& x, unsigned int d); + static void _pack_unsigned_long(Stream& x, unsigned long d); + static void _pack_unsigned_long_long(Stream& x, unsigned long long d); + + static void _pack_float(Stream& x, float d); + static void _pack_double(Stream& x, double d); + + static void _pack_nil(Stream& x); + static void _pack_true(Stream& x); + static void _pack_false(Stream& x); + + static void _pack_array(Stream& x, unsigned int n); + + static void _pack_map(Stream& x, unsigned int n); + + static void _pack_raw(Stream& x, size_t l); + static void _pack_raw_body(Stream& x, const void* b, size_t l); + + static void append_buffer(Stream& x, const unsigned char* buf, unsigned int len) + { x.write((const char*)buf, len); } + +private: + Stream& m_stream; + +private: + packer(); +}; + + +template +inline void pack(Stream* s, const T& v) +{ + packer(s).pack(v); +} + +template +inline void pack(Stream& s, const T& v) +{ + packer(s).pack(v); +} + + +#define msgpack_pack_inline_func(name) \ + template \ + inline void packer::_pack ## name + +#define msgpack_pack_inline_func_cint(name) \ + template \ + inline void packer::_pack ## name + +#define msgpack_pack_inline_func_fixint(name) \ + template \ + inline void packer::_pack_fix ## name + +#define msgpack_pack_user Stream& + +#define msgpack_pack_append_buffer append_buffer + +#include "pack_template.h" + + +template +packer::packer(Stream* s) : m_stream(*s) { } + +template +packer::packer(Stream& s) : m_stream(s) { } + +template +packer::~packer() { } + + +template +inline packer& packer::pack_uint8(uint8_t d) +{ _pack_uint8(m_stream, d); return *this; } + +template +inline packer& packer::pack_uint16(uint16_t d) +{ _pack_uint16(m_stream, d); return *this; } + +template +inline packer& packer::pack_uint32(uint32_t d) +{ _pack_uint32(m_stream, d); return *this; } + +template +inline packer& packer::pack_uint64(uint64_t d) +{ _pack_uint64(m_stream, d); return *this; } + +template +inline packer& packer::pack_int8(int8_t d) +{ _pack_int8(m_stream, d); return *this; } + +template +inline packer& packer::pack_int16(int16_t d) +{ _pack_int16(m_stream, d); return *this; } + +template +inline packer& packer::pack_int32(int32_t d) +{ _pack_int32(m_stream, d); return *this; } + +template +inline packer& packer::pack_int64(int64_t d) +{ _pack_int64(m_stream, d); return *this;} + + +template +inline packer& packer::pack_fix_uint8(uint8_t d) +{ _pack_fix_uint8(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_uint16(uint16_t d) +{ _pack_fix_uint16(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_uint32(uint32_t d) +{ _pack_fix_uint32(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_uint64(uint64_t d) +{ _pack_fix_uint64(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_int8(int8_t d) +{ _pack_fix_int8(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_int16(int16_t d) +{ _pack_fix_int16(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_int32(int32_t d) +{ _pack_fix_int32(m_stream, d); return *this; } + +template +inline packer& packer::pack_fix_int64(int64_t d) +{ _pack_fix_int64(m_stream, d); return *this;} + + +template +inline packer& packer::pack_short(short d) +{ _pack_short(m_stream, d); return *this; } + +template +inline packer& packer::pack_int(int d) +{ _pack_int(m_stream, d); return *this; } + +template +inline packer& packer::pack_long(long d) +{ _pack_long(m_stream, d); return *this; } + +template +inline packer& packer::pack_long_long(long long d) +{ _pack_long_long(m_stream, d); return *this; } + +template +inline packer& packer::pack_unsigned_short(unsigned short d) +{ _pack_unsigned_short(m_stream, d); return *this; } + +template +inline packer& packer::pack_unsigned_int(unsigned int d) +{ _pack_unsigned_int(m_stream, d); return *this; } + +template +inline packer& packer::pack_unsigned_long(unsigned long d) +{ _pack_unsigned_long(m_stream, d); return *this; } + +template +inline packer& packer::pack_unsigned_long_long(unsigned long long d) +{ _pack_unsigned_long_long(m_stream, d); return *this; } + + +template +inline packer& packer::pack_float(float d) +{ _pack_float(m_stream, d); return *this; } + +template +inline packer& packer::pack_double(double d) +{ _pack_double(m_stream, d); return *this; } + + +template +inline packer& packer::pack_nil() +{ _pack_nil(m_stream); return *this; } + +template +inline packer& packer::pack_true() +{ _pack_true(m_stream); return *this; } + +template +inline packer& packer::pack_false() +{ _pack_false(m_stream); return *this; } + + +template +inline packer& packer::pack_array(unsigned int n) +{ _pack_array(m_stream, n); return *this; } + + +template +inline packer& packer::pack_map(unsigned int n) +{ _pack_map(m_stream, n); return *this; } + + +template +inline packer& packer::pack_raw(size_t l) +{ _pack_raw(m_stream, l); return *this; } + +template +inline packer& packer::pack_raw_body(const char* b, size_t l) +{ _pack_raw_body(m_stream, b, l); return *this; } + + +} // namespace msgpack + +#endif /* msgpack/pack.hpp */ + diff --git a/msgpack/src/msgpack/sbuffer.h b/msgpack/src/msgpack/sbuffer.h new file mode 100644 index 00000000..778dea71 --- /dev/null +++ b/msgpack/src/msgpack/sbuffer.h @@ -0,0 +1,111 @@ +/* + * MessagePack for C simple buffer implementation + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_SBUFFER_H__ +#define MSGPACK_SBUFFER_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_sbuffer Simple buffer + * @ingroup msgpack_buffer + * @{ + */ + +typedef struct msgpack_sbuffer { + size_t size; + char* data; + size_t alloc; +} msgpack_sbuffer; + +static inline void msgpack_sbuffer_init(msgpack_sbuffer* sbuf) +{ + memset(sbuf, 0, sizeof(msgpack_sbuffer)); +} + +static inline void msgpack_sbuffer_destroy(msgpack_sbuffer* sbuf) +{ + free(sbuf->data); +} + +static inline msgpack_sbuffer* msgpack_sbuffer_new(void) +{ + return (msgpack_sbuffer*)calloc(1, sizeof(msgpack_sbuffer)); +} + +static inline void msgpack_sbuffer_free(msgpack_sbuffer* sbuf) +{ + if(sbuf == NULL) { return; } + msgpack_sbuffer_destroy(sbuf); + free(sbuf); +} + +#ifndef MSGPACK_SBUFFER_INIT_SIZE +#define MSGPACK_SBUFFER_INIT_SIZE 8192 +#endif + +static inline int msgpack_sbuffer_write(void* data, const char* buf, unsigned int len) +{ + msgpack_sbuffer* sbuf = (msgpack_sbuffer*)data; + + if(sbuf->alloc - sbuf->size < len) { + size_t nsize = (sbuf->alloc) ? + sbuf->alloc * 2 : MSGPACK_SBUFFER_INIT_SIZE; + + while(nsize < sbuf->size + len) { nsize *= 2; } + + void* tmp = realloc(sbuf->data, nsize); + if(!tmp) { return -1; } + + sbuf->data = (char*)tmp; + sbuf->alloc = nsize; + } + + memcpy(sbuf->data + sbuf->size, buf, len); + sbuf->size += len; + return 0; +} + +static inline char* msgpack_sbuffer_release(msgpack_sbuffer* sbuf) +{ + char* tmp = sbuf->data; + sbuf->size = 0; + sbuf->data = NULL; + sbuf->alloc = 0; + return tmp; +} + +static inline void msgpack_sbuffer_clear(msgpack_sbuffer* sbuf) +{ + sbuf->size = 0; +} + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/sbuffer.h */ + diff --git a/msgpack/src/msgpack/sbuffer.hpp b/msgpack/src/msgpack/sbuffer.hpp new file mode 100644 index 00000000..14c5d2a2 --- /dev/null +++ b/msgpack/src/msgpack/sbuffer.hpp @@ -0,0 +1,112 @@ +// +// MessagePack for C++ simple buffer implementation +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_SBUFFER_HPP__ +#define MSGPACK_SBUFFER_HPP__ + +#include "sbuffer.h" +#include + +namespace msgpack { + + +class sbuffer : public msgpack_sbuffer { +public: + sbuffer(size_t initsz = MSGPACK_SBUFFER_INIT_SIZE) + { + if(initsz == 0) { + base::data = NULL; + } else { + base::data = (char*)::malloc(initsz); + if(!base::data) { + throw std::bad_alloc(); + } + } + + base::size = 0; + base::alloc = initsz; + } + + ~sbuffer() + { + ::free(base::data); + } + +public: + void write(const char* buf, unsigned int len) + { + if(base::alloc - base::size < len) { + expand_buffer(len); + } + memcpy(base::data + base::size, buf, len); + base::size += len; + } + + char* data() + { + return base::data; + } + + const char* data() const + { + return base::data; + } + + size_t size() const + { + return base::size; + } + + char* release() + { + return msgpack_sbuffer_release(this); + } + + void clear() + { + msgpack_sbuffer_clear(this); + } + +private: + void expand_buffer(size_t len) + { + size_t nsize = (base::alloc > 0) ? + base::alloc * 2 : MSGPACK_SBUFFER_INIT_SIZE; + + while(nsize < base::size + len) { nsize *= 2; } + + void* tmp = realloc(base::data, nsize); + if(!tmp) { + throw std::bad_alloc(); + } + + base::data = (char*)tmp; + base::alloc = nsize; + } + +private: + typedef msgpack_sbuffer base; + +private: + sbuffer(const sbuffer&); +}; + + +} // namespace msgpack + +#endif /* msgpack/sbuffer.hpp */ + diff --git a/msgpack/src/msgpack/type.hpp b/msgpack/src/msgpack/type.hpp new file mode 100644 index 00000000..bca69bfd --- /dev/null +++ b/msgpack/src/msgpack/type.hpp @@ -0,0 +1,16 @@ +#include "type/bool.hpp" +#include "type/deque.hpp" +#include "type/fixint.hpp" +#include "type/float.hpp" +#include "type/int.hpp" +#include "type/list.hpp" +#include "type/map.hpp" +#include "type/nil.hpp" +#include "type/pair.hpp" +#include "type/raw.hpp" +#include "type/set.hpp" +#include "type/string.hpp" +#include "type/vector.hpp" +#include "type/tuple.hpp" +#include "type/define.hpp" + diff --git a/msgpack/src/msgpack/type/bool.hpp b/msgpack/src/msgpack/type/bool.hpp new file mode 100644 index 00000000..9433a982 --- /dev/null +++ b/msgpack/src/msgpack/type/bool.hpp @@ -0,0 +1,55 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_BOOL_HPP__ +#define MSGPACK_TYPE_BOOL_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +inline bool& operator>> (object o, bool& v) +{ + if(o.type != type::BOOLEAN) { throw type_error(); } + v = o.via.boolean; + return v; +} + +template +inline packer& operator<< (packer& o, const bool& v) +{ + if(v) { o.pack_true(); } + else { o.pack_false(); } + return o; +} + +inline void operator<< (object& o, bool v) +{ + o.type = type::BOOLEAN; + o.via.boolean = v; +} + +inline void operator<< (object::with_zone& o, bool v) + { static_cast(o) << v; } + + +} // namespace msgpack + +#endif /* msgpack/type/bool.hpp */ + diff --git a/msgpack/src/msgpack/type/define.hpp.erb b/msgpack/src/msgpack/type/define.hpp.erb new file mode 100644 index 00000000..c8a86512 --- /dev/null +++ b/msgpack/src/msgpack/type/define.hpp.erb @@ -0,0 +1,136 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_DEFINE_HPP__ +#define MSGPACK_TYPE_DEFINE_HPP__ + +#define MSGPACK_DEFINE(...) \ + template \ + void msgpack_pack(Packer& pk) const \ + { \ + msgpack::type::make_define(__VA_ARGS__).msgpack_pack(pk); \ + } \ + void msgpack_unpack(msgpack::object o) \ + { \ + msgpack::type::make_define(__VA_ARGS__).msgpack_unpack(o); \ + }\ + template \ + void msgpack_object(MSGPACK_OBJECT* o, msgpack::zone* z) const \ + { \ + msgpack::type::make_define(__VA_ARGS__).msgpack_object(o, z); \ + } + +// MSGPACK_ADD_ENUM must be used in the global namespace. +#define MSGPACK_ADD_ENUM(enum) \ + namespace msgpack { \ + template <> \ + inline enum& operator>> (object o, enum& v) \ + { \ + int tmp; \ + o >> tmp; \ + v = static_cast(tmp); \ + return v; \ + } \ + template <> \ + void operator<< (object::with_zone& o, const enum& v) \ + { \ + int tmp = static_cast(v); \ + o << tmp; \ + } \ + } + +namespace msgpack { +namespace type { + + +<% GENERATION_LIMIT = 31 %> +template , typename A<%=i%> = void<%}%>> +struct define; + + +template <> +struct define<> { + typedef define<> value_type; + typedef tuple<> tuple_type; + template + void msgpack_pack(Packer& pk) const + { + pk.pack_array(0); + } + void msgpack_unpack(msgpack::object o) + { + if(o.type != type::ARRAY) { throw type_error(); } + } + void msgpack_object(msgpack::object* o, msgpack::zone* z) const + { + o->type = type::ARRAY; + o->via.array.ptr = NULL; + o->via.array.size = 0; + } +}; +<%0.upto(GENERATION_LIMIT) {|i|%> +template , typename A<%=j%><%}%>> +struct define, A<%=j%><%}%>> { + typedef define, A<%=j%><%}%>> value_type; + typedef tuple, A<%=j%><%}%>> tuple_type; + define(A0& _a0<%1.upto(i) {|j|%>, A<%=j%>& _a<%=j%><%}%>) : + a0(_a0)<%1.upto(i) {|j|%>, a<%=j%>(_a<%=j%>)<%}%> {} + template + void msgpack_pack(Packer& pk) const + { + pk.pack_array(<%=i+1%>); + <%0.upto(i) {|j|%> + pk.pack(a<%=j%>);<%}%> + } + void msgpack_unpack(msgpack::object o) + { + if(o.type != type::ARRAY) { throw type_error(); } + const size_t size = o.via.array.size; + <%0.upto(i) {|j|%> + if(size <= <%=j%>) { return; } o.via.array.ptr[<%=j%>].convert(&a<%=j%>);<%}%> + } + void msgpack_object(msgpack::object* o, msgpack::zone* z) const + { + o->type = type::ARRAY; + o->via.array.ptr = (object*)z->malloc(sizeof(object)*<%=i+1%>); + o->via.array.size = <%=i+1%>; + <%0.upto(i) {|j|%> + o->via.array.ptr[<%=j%>] = object(a<%=j%>, z);<%}%> + } + <%0.upto(i) {|j|%> + A<%=j%>& a<%=j%>;<%}%> +}; +<%}%> + +inline define<> make_define() +{ + return define<>(); +} +<%0.upto(GENERATION_LIMIT) {|i|%> +template , typename A<%=j%><%}%>> +define, A<%=j%><%}%>> make_define(A0& a0<%1.upto(i) {|j|%>, A<%=j%>& a<%=j%><%}%>) +{ + return define, A<%=j%><%}%>>(a0<%1.upto(i) {|j|%>, a<%=j%><%}%>); +} +<%}%> + +} // namespace type +} // namespace msgpack + + +#endif /* msgpack/type/define.hpp */ + diff --git a/msgpack/src/msgpack/type/deque.hpp b/msgpack/src/msgpack/type/deque.hpp new file mode 100644 index 00000000..d21ceeae --- /dev/null +++ b/msgpack/src/msgpack/type/deque.hpp @@ -0,0 +1,77 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_DEQUE_HPP__ +#define MSGPACK_TYPE_DEQUE_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +template +inline std::deque& operator>> (object o, std::deque& v) +{ + if(o.type != type::ARRAY) { throw type_error(); } + v.resize(o.via.array.size); + object* p = o.via.array.ptr; + object* const pend = o.via.array.ptr + o.via.array.size; + typename std::deque::iterator it = v.begin(); + for(; p < pend; ++p, ++it) { + p->convert(&*it); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::deque& v) +{ + o.pack_array(v.size()); + for(typename std::deque::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(*it); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::deque& v) +{ + o.type = type::ARRAY; + if(v.empty()) { + o.via.array.ptr = NULL; + o.via.array.size = 0; + } else { + object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); + object* const pend = p + v.size(); + o.via.array.ptr = p; + o.via.array.size = v.size(); + typename std::deque::const_iterator it(v.begin()); + do { + *p = object(*it, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +} // namespace msgpack + +#endif /* msgpack/type/deque.hpp */ + diff --git a/msgpack/src/msgpack/type/fixint.hpp b/msgpack/src/msgpack/type/fixint.hpp new file mode 100644 index 00000000..ebe2696f --- /dev/null +++ b/msgpack/src/msgpack/type/fixint.hpp @@ -0,0 +1,172 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2020 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_FIXINT_HPP__ +#define MSGPACK_TYPE_FIXINT_HPP__ + +#include "msgpack/object.hpp" +#include "msgpack/type/int.hpp" + +namespace msgpack { + +namespace type { + + +template +struct fix_int { + fix_int() : value(0) { } + fix_int(T value) : value(value) { } + + operator T() const { return value; } + + T get() const { return value; } + +private: + T value; +}; + + +typedef fix_int fix_uint8; +typedef fix_int fix_uint16; +typedef fix_int fix_uint32; +typedef fix_int fix_uint64; + +typedef fix_int fix_int8; +typedef fix_int fix_int16; +typedef fix_int fix_int32; +typedef fix_int fix_int64; + + +} // namespace type + + +inline type::fix_int8& operator>> (object o, type::fix_int8& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_int16& operator>> (object o, type::fix_int16& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_int32& operator>> (object o, type::fix_int32& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_int64& operator>> (object o, type::fix_int64& v) + { v = type::detail::convert_integer(o); return v; } + + +inline type::fix_uint8& operator>> (object o, type::fix_uint8& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_uint16& operator>> (object o, type::fix_uint16& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_uint32& operator>> (object o, type::fix_uint32& v) + { v = type::detail::convert_integer(o); return v; } + +inline type::fix_uint64& operator>> (object o, type::fix_uint64& v) + { v = type::detail::convert_integer(o); return v; } + + +template +inline packer& operator<< (packer& o, const type::fix_int8& v) + { o.pack_fix_int8(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_int16& v) + { o.pack_fix_int16(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_int32& v) + { o.pack_fix_int32(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_int64& v) + { o.pack_fix_int64(v); return o; } + + +template +inline packer& operator<< (packer& o, const type::fix_uint8& v) + { o.pack_fix_uint8(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_uint16& v) + { o.pack_fix_uint16(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_uint32& v) + { o.pack_fix_uint32(v); return o; } + +template +inline packer& operator<< (packer& o, const type::fix_uint64& v) + { o.pack_fix_uint64(v); return o; } + + +inline void operator<< (object& o, type::fix_int8 v) + { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_int16 v) + { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_int32 v) + { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_int64 v) + { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + + +inline void operator<< (object& o, type::fix_uint8 v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_uint16 v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_uint32 v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + +inline void operator<< (object& o, type::fix_uint64 v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } + + +inline void operator<< (object::with_zone& o, type::fix_int8 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_int16 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_int32 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_int64 v) + { static_cast(o) << v; } + + +inline void operator<< (object::with_zone& o, type::fix_uint8 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_uint16 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_uint32 v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, type::fix_uint64 v) + { static_cast(o) << v; } + + +} // namespace msgpack + +#endif /* msgpack/type/fixint.hpp */ + diff --git a/msgpack/src/msgpack/type/float.hpp b/msgpack/src/msgpack/type/float.hpp new file mode 100644 index 00000000..11df6b2c --- /dev/null +++ b/msgpack/src/msgpack/type/float.hpp @@ -0,0 +1,82 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_FLOAT_HPP__ +#define MSGPACK_TYPE_FLOAT_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +// FIXME check overflow, underflow + + +inline float& operator>> (object o, float& v) +{ + if(o.type != type::DOUBLE) { throw type_error(); } + v = (float)o.via.dec; + return v; +} + +template +inline packer& operator<< (packer& o, const float& v) +{ + o.pack_float(v); + return o; +} + + +inline double& operator>> (object o, double& v) +{ + if(o.type != type::DOUBLE) { throw type_error(); } + v = o.via.dec; + return v; +} + +template +inline packer& operator<< (packer& o, const double& v) +{ + o.pack_double(v); + return o; +} + + +inline void operator<< (object& o, float v) +{ + o.type = type::DOUBLE; + o.via.dec = (double)v; +} + +inline void operator<< (object& o, double v) +{ + o.type = type::DOUBLE; + o.via.dec = v; +} + +inline void operator<< (object::with_zone& o, float v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, double v) + { static_cast(o) << v; } + + +} // namespace msgpack + +#endif /* msgpack/type/float.hpp */ + diff --git a/msgpack/src/msgpack/type/int.hpp b/msgpack/src/msgpack/type/int.hpp new file mode 100644 index 00000000..e45121df --- /dev/null +++ b/msgpack/src/msgpack/type/int.hpp @@ -0,0 +1,211 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_INT_HPP__ +#define MSGPACK_TYPE_INT_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +namespace type { +namespace detail { + template + struct convert_integer_sign; + + template + struct convert_integer_sign { + static inline T convert(object o) { + if(o.type == type::POSITIVE_INTEGER) { + if(o.via.u64 > (uint64_t)std::numeric_limits::max()) + { throw type_error(); } + return (T)o.via.u64; + } else if(o.type == type::NEGATIVE_INTEGER) { + if(o.via.i64 < (int64_t)std::numeric_limits::min()) + { throw type_error(); } + return (T)o.via.i64; + } + throw type_error(); + } + }; + + template + struct convert_integer_sign { + static inline T convert(object o) { + if(o.type == type::POSITIVE_INTEGER) { + if(o.via.u64 > (uint64_t)std::numeric_limits::max()) + { throw type_error(); } + return (T)o.via.u64; + } + throw type_error(); + } + }; + + template + static inline T convert_integer(object o) + { + return detail::convert_integer_sign::is_signed>::convert(o); + } + +} // namespace detail +} // namespace type + + +inline signed char& operator>> (object o, signed char& v) + { v = type::detail::convert_integer(o); return v; } + +inline signed short& operator>> (object o, signed short& v) + { v = type::detail::convert_integer(o); return v; } + +inline signed int& operator>> (object o, signed int& v) + { v = type::detail::convert_integer(o); return v; } + +inline signed long& operator>> (object o, signed long& v) + { v = type::detail::convert_integer(o); return v; } + +inline signed long long& operator>> (object o, signed long long& v) + { v = type::detail::convert_integer(o); return v; } + + +inline unsigned char& operator>> (object o, unsigned char& v) + { v = type::detail::convert_integer(o); return v; } + +inline unsigned short& operator>> (object o, unsigned short& v) + { v = type::detail::convert_integer(o); return v; } + +inline unsigned int& operator>> (object o, unsigned int& v) + { v = type::detail::convert_integer(o); return v; } + +inline unsigned long& operator>> (object o, unsigned long& v) + { v = type::detail::convert_integer(o); return v; } + +inline unsigned long long& operator>> (object o, unsigned long long& v) + { v = type::detail::convert_integer(o); return v; } + + +template +inline packer& operator<< (packer& o, const signed char& v) + { o.pack_int8(v); return o; } + +template +inline packer& operator<< (packer& o, const signed short& v) + { o.pack_short(v); return o; } + +template +inline packer& operator<< (packer& o, const signed int& v) + { o.pack_int(v); return o; } + +template +inline packer& operator<< (packer& o, const signed long& v) + { o.pack_long(v); return o; } + +template +inline packer& operator<< (packer& o, const signed long long& v) + { o.pack_long_long(v); return o; } + + +template +inline packer& operator<< (packer& o, const unsigned char& v) + { o.pack_uint8(v); return o; } + +template +inline packer& operator<< (packer& o, const unsigned short& v) + { o.pack_unsigned_short(v); return o; } + +template +inline packer& operator<< (packer& o, const unsigned int& v) + { o.pack_unsigned_int(v); return o; } + +template +inline packer& operator<< (packer& o, const unsigned long& v) + { o.pack_unsigned_long(v); return o; } + +template +inline packer& operator<< (packer& o, const unsigned long long& v) + { o.pack_unsigned_long_long(v); return o; } + + +inline void operator<< (object& o, signed char v) + { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + +inline void operator<< (object& o, signed short v) + { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + +inline void operator<< (object& o, signed int v) + { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + +inline void operator<< (object& o, signed long v) + { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + +inline void operator<< (object& o, signed long long v) + { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + + +inline void operator<< (object& o, unsigned char v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + +inline void operator<< (object& o, unsigned short v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + +inline void operator<< (object& o, unsigned int v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + +inline void operator<< (object& o, unsigned long v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + +inline void operator<< (object& o, unsigned long long v) + { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } + + +inline void operator<< (object::with_zone& o, signed char v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, signed short v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, signed int v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, signed long v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, signed long long v) + { static_cast(o) << v; } + + +inline void operator<< (object::with_zone& o, unsigned char v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, unsigned short v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, unsigned int v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, unsigned long v) + { static_cast(o) << v; } + +inline void operator<< (object::with_zone& o, unsigned long long v) + { static_cast(o) << v; } + + +} // namespace msgpack + +#endif /* msgpack/type/int.hpp */ + diff --git a/msgpack/src/msgpack/type/list.hpp b/msgpack/src/msgpack/type/list.hpp new file mode 100644 index 00000000..c0f8ce63 --- /dev/null +++ b/msgpack/src/msgpack/type/list.hpp @@ -0,0 +1,77 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_LIST_HPP__ +#define MSGPACK_TYPE_LIST_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +template +inline std::list& operator>> (object o, std::list& v) +{ + if(o.type != type::ARRAY) { throw type_error(); } + v.resize(o.via.array.size); + object* p = o.via.array.ptr; + object* const pend = o.via.array.ptr + o.via.array.size; + typename std::list::iterator it = v.begin(); + for(; p < pend; ++p, ++it) { + p->convert(&*it); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::list& v) +{ + o.pack_array(v.size()); + for(typename std::list::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(*it); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::list& v) +{ + o.type = type::ARRAY; + if(v.empty()) { + o.via.array.ptr = NULL; + o.via.array.size = 0; + } else { + object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); + object* const pend = p + v.size(); + o.via.array.ptr = p; + o.via.array.size = v.size(); + typename std::list::const_iterator it(v.begin()); + do { + *p = object(*it, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +} // namespace msgpack + +#endif /* msgpack/type/list.hpp */ + diff --git a/msgpack/src/msgpack/type/map.hpp b/msgpack/src/msgpack/type/map.hpp new file mode 100644 index 00000000..958447d5 --- /dev/null +++ b/msgpack/src/msgpack/type/map.hpp @@ -0,0 +1,205 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_MAP_HPP__ +#define MSGPACK_TYPE_MAP_HPP__ + +#include "msgpack/object.hpp" +#include +#include +#include + +namespace msgpack { + + +namespace type { + +template +class assoc_vector : public std::vector< std::pair > {}; + +namespace detail { + template + struct pair_first_less { + bool operator() (const std::pair& x, const std::pair& y) const + { return x.first < y.first; } + }; +} + +} //namespace type + + +template +inline type::assoc_vector& operator>> (object o, type::assoc_vector& v) +{ + if(o.type != type::MAP) { throw type_error(); } + v.resize(o.via.map.size); + object_kv* p = o.via.map.ptr; + object_kv* const pend = o.via.map.ptr + o.via.map.size; + std::pair* it(&v.front()); + for(; p < pend; ++p, ++it) { + p->key.convert(&it->first); + p->val.convert(&it->second); + } + std::sort(v.begin(), v.end(), type::detail::pair_first_less()); + return v; +} + +template +inline packer& operator<< (packer& o, const type::assoc_vector& v) +{ + o.pack_map(v.size()); + for(typename type::assoc_vector::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(it->first); + o.pack(it->second); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const type::assoc_vector& v) +{ + o.type = type::MAP; + if(v.empty()) { + o.via.map.ptr = NULL; + o.via.map.size = 0; + } else { + object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); + object_kv* const pend = p + v.size(); + o.via.map.ptr = p; + o.via.map.size = v.size(); + typename type::assoc_vector::const_iterator it(v.begin()); + do { + p->key = object(it->first, o.zone); + p->val = object(it->second, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +template +inline std::map operator>> (object o, std::map& v) +{ + if(o.type != type::MAP) { throw type_error(); } + object_kv* p(o.via.map.ptr); + object_kv* const pend(o.via.map.ptr + o.via.map.size); + for(; p != pend; ++p) { + K key; + p->key.convert(&key); + typename std::map::iterator it(v.lower_bound(key)); + if(it != v.end() && !(key < it->first)) { + p->val.convert(&it->second); + } else { + V val; + p->val.convert(&val); + v.insert(it, std::pair(key, val)); + } + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::map& v) +{ + o.pack_map(v.size()); + for(typename std::map::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(it->first); + o.pack(it->second); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::map& v) +{ + o.type = type::MAP; + if(v.empty()) { + o.via.map.ptr = NULL; + o.via.map.size = 0; + } else { + object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); + object_kv* const pend = p + v.size(); + o.via.map.ptr = p; + o.via.map.size = v.size(); + typename std::map::const_iterator it(v.begin()); + do { + p->key = object(it->first, o.zone); + p->val = object(it->second, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +template +inline std::multimap operator>> (object o, std::multimap& v) +{ + if(o.type != type::MAP) { throw type_error(); } + object_kv* p(o.via.map.ptr); + object_kv* const pend(o.via.map.ptr + o.via.map.size); + for(; p != pend; ++p) { + std::pair value; + p->key.convert(&value.first); + p->val.convert(&value.second); + v.insert(value); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::multimap& v) +{ + o.pack_map(v.size()); + for(typename std::multimap::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(it->first); + o.pack(it->second); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::multimap& v) +{ + o.type = type::MAP; + if(v.empty()) { + o.via.map.ptr = NULL; + o.via.map.size = 0; + } else { + object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); + object_kv* const pend = p + v.size(); + o.via.map.ptr = p; + o.via.map.size = v.size(); + typename std::multimap::const_iterator it(v.begin()); + do { + p->key = object(it->first, o.zone); + p->val = object(it->second, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +} // namespace msgpack + +#endif /* msgpack/type/map.hpp */ + diff --git a/msgpack/src/msgpack/type/nil.hpp b/msgpack/src/msgpack/type/nil.hpp new file mode 100644 index 00000000..f44e45e4 --- /dev/null +++ b/msgpack/src/msgpack/type/nil.hpp @@ -0,0 +1,65 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_NIL_HPP__ +#define MSGPACK_TYPE_NIL_HPP__ + +#include "msgpack/object.hpp" + +namespace msgpack { + +namespace type { + +struct nil { }; + +} // namespace type + + +inline type::nil& operator>> (object o, type::nil& v) +{ + if(o.type != type::NIL) { throw type_error(); } + return v; +} + +template +inline packer& operator<< (packer& o, const type::nil& v) +{ + o.pack_nil(); + return o; +} + +inline void operator<< (object& o, type::nil v) +{ + o.type = type::NIL; +} + +inline void operator<< (object::with_zone& o, type::nil v) + { static_cast(o) << v; } + + +template <> +inline void object::as() const +{ + msgpack::type::nil v; + convert(&v); +} + + +} // namespace msgpack + +#endif /* msgpack/type/nil.hpp */ + diff --git a/msgpack/src/msgpack/type/pair.hpp b/msgpack/src/msgpack/type/pair.hpp new file mode 100644 index 00000000..296a8b64 --- /dev/null +++ b/msgpack/src/msgpack/type/pair.hpp @@ -0,0 +1,61 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_PAIR_HPP__ +#define MSGPACK_TYPE_PAIR_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +template +inline std::pair& operator>> (object o, std::pair& v) +{ + if(o.type != type::ARRAY) { throw type_error(); } + if(o.via.array.size != 2) { throw type_error(); } + o.via.array.ptr[0].convert(&v.first); + o.via.array.ptr[1].convert(&v.second); + return v; +} + +template +inline packer& operator<< (packer& o, const std::pair& v) +{ + o.pack_array(2); + o.pack(v.first); + o.pack(v.second); + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::pair& v) +{ + o.type = type::ARRAY; + object* p = (object*)o.zone->malloc(sizeof(object)*2); + o.via.array.ptr = p; + o.via.array.size = 2; + p[0] = object(v.first, o.zone); + p[1] = object(v.second, o.zone); +} + + +} // namespace msgpack + +#endif /* msgpack/type/pair.hpp */ + diff --git a/msgpack/src/msgpack/type/raw.hpp b/msgpack/src/msgpack/type/raw.hpp new file mode 100644 index 00000000..87d188f6 --- /dev/null +++ b/msgpack/src/msgpack/type/raw.hpp @@ -0,0 +1,94 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_RAW_HPP__ +#define MSGPACK_TYPE_RAW_HPP__ + +#include "msgpack/object.hpp" +#include +#include + +namespace msgpack { + +namespace type { + +struct raw_ref { + raw_ref() : size(0), ptr(NULL) {} + raw_ref(const char* p, uint32_t s) : size(s), ptr(p) {} + + uint32_t size; + const char* ptr; + + std::string str() const { return std::string(ptr, size); } + + bool operator== (const raw_ref& x) const + { + return size == x.size && memcmp(ptr, x.ptr, size) == 0; + } + + bool operator!= (const raw_ref& x) const + { + return !(*this != x); + } + + bool operator< (const raw_ref& x) const + { + if(size == x.size) { return memcmp(ptr, x.ptr, size) < 0; } + else { return size < x.size; } + } + + bool operator> (const raw_ref& x) const + { + if(size == x.size) { return memcmp(ptr, x.ptr, size) > 0; } + else { return size > x.size; } + } +}; + +} // namespace type + + +inline type::raw_ref& operator>> (object o, type::raw_ref& v) +{ + if(o.type != type::RAW) { throw type_error(); } + v.ptr = o.via.raw.ptr; + v.size = o.via.raw.size; + return v; +} + +template +inline packer& operator<< (packer& o, const type::raw_ref& v) +{ + o.pack_raw(v.size); + o.pack_raw_body(v.ptr, v.size); + return o; +} + +inline void operator<< (object& o, const type::raw_ref& v) +{ + o.type = type::RAW; + o.via.raw.ptr = v.ptr; + o.via.raw.size = v.size; +} + +inline void operator<< (object::with_zone& o, const type::raw_ref& v) + { static_cast(o) << v; } + + +} // namespace msgpack + +#endif /* msgpack/type/raw.hpp */ + diff --git a/msgpack/src/msgpack/type/set.hpp b/msgpack/src/msgpack/type/set.hpp new file mode 100644 index 00000000..bcf1030a --- /dev/null +++ b/msgpack/src/msgpack/type/set.hpp @@ -0,0 +1,122 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_SET_HPP__ +#define MSGPACK_TYPE_SET_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +template +inline std::set& operator>> (object o, std::set& v) +{ + if(o.type != type::ARRAY) { throw type_error(); } + object* p = o.via.array.ptr + o.via.array.size; + object* const pbegin = o.via.array.ptr; + while(p > pbegin) { + --p; + v.insert(p->as()); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::set& v) +{ + o.pack_array(v.size()); + for(typename std::set::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(*it); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::set& v) +{ + o.type = type::ARRAY; + if(v.empty()) { + o.via.array.ptr = NULL; + o.via.array.size = 0; + } else { + object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); + object* const pend = p + v.size(); + o.via.array.ptr = p; + o.via.array.size = v.size(); + typename std::set::const_iterator it(v.begin()); + do { + *p = object(*it, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +template +inline std::multiset& operator>> (object o, std::multiset& v) +{ + if(o.type != type::ARRAY) { throw type_error(); } + object* p = o.via.array.ptr + o.via.array.size; + object* const pbegin = o.via.array.ptr; + while(p > pbegin) { + --p; + v.insert(p->as()); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::multiset& v) +{ + o.pack_array(v.size()); + for(typename std::multiset::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(*it); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::multiset& v) +{ + o.type = type::ARRAY; + if(v.empty()) { + o.via.array.ptr = NULL; + o.via.array.size = 0; + } else { + object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); + object* const pend = p + v.size(); + o.via.array.ptr = p; + o.via.array.size = v.size(); + typename std::multiset::const_iterator it(v.begin()); + do { + *p = object(*it, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +} // namespace msgpack + +#endif /* msgpack/type/set.hpp */ + diff --git a/msgpack/src/msgpack/type/string.hpp b/msgpack/src/msgpack/type/string.hpp new file mode 100644 index 00000000..37d4a172 --- /dev/null +++ b/msgpack/src/msgpack/type/string.hpp @@ -0,0 +1,62 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_STRING_HPP__ +#define MSGPACK_TYPE_STRING_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +inline std::string& operator>> (object o, std::string& v) +{ + if(o.type != type::RAW) { throw type_error(); } + v.assign(o.via.raw.ptr, o.via.raw.size); + return v; +} + +template +inline packer& operator<< (packer& o, const std::string& v) +{ + o.pack_raw(v.size()); + o.pack_raw_body(v.data(), v.size()); + return o; +} + +inline void operator<< (object::with_zone& o, const std::string& v) +{ + o.type = type::RAW; + char* ptr = (char*)o.zone->malloc(v.size()); + o.via.raw.ptr = ptr; + o.via.raw.size = (uint32_t)v.size(); + memcpy(ptr, v.data(), v.size()); +} + +inline void operator<< (object& o, const std::string& v) +{ + o.type = type::RAW; + o.via.raw.ptr = v.data(); + o.via.raw.size = (uint32_t)v.size(); +} + + +} // namespace msgpack + +#endif /* msgpack/type/string.hpp */ + diff --git a/msgpack/src/msgpack/type/tr1/unordered_map.hpp b/msgpack/src/msgpack/type/tr1/unordered_map.hpp new file mode 100644 index 00000000..4b29f0ca --- /dev/null +++ b/msgpack/src/msgpack/type/tr1/unordered_map.hpp @@ -0,0 +1,129 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_TR1_UNORDERED_MAP_HPP__ +#define MSGPACK_TYPE_TR1_UNORDERED_MAP_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +template +inline std::tr1::unordered_map operator>> (object o, std::tr1::unordered_map& v) +{ + if(o.type != type::MAP) { throw type_error(); } + object_kv* p(o.via.map.ptr); + object_kv* const pend(o.via.map.ptr + o.via.map.size); + for(; p != pend; ++p) { + K key; + p->key.convert(&key); + p->val.convert(&v[key]); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::tr1::unordered_map& v) +{ + o.pack_map(v.size()); + for(typename std::tr1::unordered_map::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(it->first); + o.pack(it->second); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::tr1::unordered_map& v) +{ + o.type = type::MAP; + if(v.empty()) { + o.via.map.ptr = NULL; + o.via.map.size = 0; + } else { + object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); + object_kv* const pend = p + v.size(); + o.via.map.ptr = p; + o.via.map.size = v.size(); + typename std::tr1::unordered_map::const_iterator it(v.begin()); + do { + p->key = object(it->first, o.zone); + p->val = object(it->second, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +template +inline std::tr1::unordered_multimap operator>> (object o, std::tr1::unordered_multimap& v) +{ + if(o.type != type::MAP) { throw type_error(); } + object_kv* p(o.via.map.ptr); + object_kv* const pend(o.via.map.ptr + o.via.map.size); + for(; p != pend; ++p) { + std::pair value; + p->key.convert(&value.first); + p->val.convert(&value.second); + v.insert(value); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::tr1::unordered_multimap& v) +{ + o.pack_map(v.size()); + for(typename std::tr1::unordered_multimap::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(it->first); + o.pack(it->second); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::tr1::unordered_multimap& v) +{ + o.type = type::MAP; + if(v.empty()) { + o.via.map.ptr = NULL; + o.via.map.size = 0; + } else { + object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); + object_kv* const pend = p + v.size(); + o.via.map.ptr = p; + o.via.map.size = v.size(); + typename std::tr1::unordered_multimap::const_iterator it(v.begin()); + do { + p->key = object(it->first, o.zone); + p->val = object(it->second, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +} // namespace msgpack + +#endif /* msgpack/type/map.hpp */ + diff --git a/msgpack/src/msgpack/type/tr1/unordered_set.hpp b/msgpack/src/msgpack/type/tr1/unordered_set.hpp new file mode 100644 index 00000000..4af6801c --- /dev/null +++ b/msgpack/src/msgpack/type/tr1/unordered_set.hpp @@ -0,0 +1,122 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_TR1_UNORDERED_SET_HPP__ +#define MSGPACK_TYPE_TR1_UNORDERED_SET_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +template +inline std::tr1::unordered_set& operator>> (object o, std::tr1::unordered_set& v) +{ + if(o.type != type::ARRAY) { throw type_error(); } + object* p = o.via.array.ptr + o.via.array.size; + object* const pbegin = o.via.array.ptr; + while(p > pbegin) { + --p; + v.insert(p->as()); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::tr1::unordered_set& v) +{ + o.pack_array(v.size()); + for(typename std::tr1::unordered_set::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(*it); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::tr1::unordered_set& v) +{ + o.type = type::ARRAY; + if(v.empty()) { + o.via.array.ptr = NULL; + o.via.array.size = 0; + } else { + object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); + object* const pend = p + v.size(); + o.via.array.ptr = p; + o.via.array.size = v.size(); + typename std::tr1::unordered_set::const_iterator it(v.begin()); + do { + *p = object(*it, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +template +inline std::tr1::unordered_multiset& operator>> (object o, std::tr1::unordered_multiset& v) +{ + if(o.type != type::ARRAY) { throw type_error(); } + object* p = o.via.array.ptr + o.via.array.size; + object* const pbegin = o.via.array.ptr; + while(p > pbegin) { + --p; + v.insert(p->as()); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::tr1::unordered_multiset& v) +{ + o.pack_array(v.size()); + for(typename std::tr1::unordered_multiset::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(*it); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::tr1::unordered_multiset& v) +{ + o.type = type::ARRAY; + if(v.empty()) { + o.via.array.ptr = NULL; + o.via.array.size = 0; + } else { + object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); + object* const pend = p + v.size(); + o.via.array.ptr = p; + o.via.array.size = v.size(); + typename std::tr1::unordered_multiset::const_iterator it(v.begin()); + do { + *p = object(*it, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +} // namespace msgpack + +#endif /* msgpack/type/set.hpp */ + diff --git a/msgpack/src/msgpack/type/tuple.hpp.erb b/msgpack/src/msgpack/type/tuple.hpp.erb new file mode 100644 index 00000000..ebef8163 --- /dev/null +++ b/msgpack/src/msgpack/type/tuple.hpp.erb @@ -0,0 +1,206 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_TUPLE_HPP__ +#define MSGPACK_TYPE_TUPLE_HPP__ + +#include "msgpack/object.hpp" + +namespace msgpack { + +namespace type { + +// FIXME operator== +// FIXME operator!= +<% GENERATION_LIMIT = 31 %> + +template , typename A<%=i%> = void<%}%>> +struct tuple; + +template +struct tuple_element; + +template +struct const_tuple_element; + +template +struct tuple_type { + typedef T type; + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; + typedef const T& transparent_reference; +}; + +template +struct tuple_type { + typedef T type; + typedef T& value_type; + typedef T& reference; + typedef const T& const_reference; + typedef T& transparent_reference; +}; + +template +struct tuple_type { + typedef T type; + typedef T& value_type; + typedef T& reference; + typedef const T& const_reference; + typedef const T& transparent_reference; +}; + +<%0.upto(GENERATION_LIMIT) {|i|%> +<%0.upto(i) {|j|%> +template , typename A<%=k%><%}%>> +struct tuple_element, A<%=k%><%}%>>, <%=j%>> : tuple_type> { + tuple_element(tuple, A<%=k%> <%}%>>& x) : _x(x.a<%=j%>) {} + typename tuple_type>::reference get() { return _x; } + typename tuple_type>::const_reference get() const { return _x; } +private: + typename tuple_type>::reference _x; +}; +<%}%> +<%}%> + +<%0.upto(GENERATION_LIMIT) {|i|%> +<%0.upto(i) {|j|%> +template , typename A<%=k%><%}%>> +struct const_tuple_element, A<%=k%><%}%>>, <%=j%>> : tuple_type> { + const_tuple_element(const tuple, A<%=k%><%}%>>& x) : _x(x.a<%=j%>) {} + typename tuple_type>::const_reference get() const { return _x; } +private: + typename tuple_type>::const_reference _x; +}; +<%}%> +<%}%> + +template <> +struct tuple<> { + tuple() {} + tuple(object o) { o.convert(this); } + typedef tuple<> value_type; +}; +<%0.upto(GENERATION_LIMIT) {|i|%> +template , typename A<%=j%><%}%>> +struct tuple, A<%=j%><%}%>> { + typedef tuple, A<%=j%><%}%>> value_type; + tuple() {} + tuple(typename tuple_type::transparent_reference _a0<%1.upto(i) {|j|%>, typename tuple_type>::transparent_reference _a<%=j%><%}%>) : + a0(_a0)<%1.upto(i) {|j|%>, a<%=j%>(_a<%=j%>)<%}%> {} + tuple(object o) { o.convert(this); } + template typename tuple_element::reference get() + { return tuple_element(*this).get(); } + template typename const_tuple_element::const_reference get() const + { return const_tuple_element(*this).get(); } + <%0.upto(i) {|j|%> + A<%=j%> a<%=j%>;<%}%> +}; +<%}%> + +inline tuple<> make_tuple() +{ + return tuple<>(); +} +<%0.upto(GENERATION_LIMIT) {|i|%> +template , typename A<%=j%><%}%>> +tuple, A<%=j%><%}%>> make_tuple(typename tuple_type::transparent_reference a0<%1.upto(i) {|j|%>, typename tuple_type>::transparent_reference a<%=j%><%}%>) +{ + return tuple, A<%=j%><%}%>>(a0<%1.upto(i) {|j|%>, a<%=j%><%}%>); +} +<%}%> + +} // namespace type + + +inline type::tuple<>& operator>> ( + object o, + type::tuple<>& v) { + if(o.type != type::ARRAY) { throw type_error(); } + return v; +} +<%0.upto(GENERATION_LIMIT) {|i|%> +template , typename A<%=j%><%}%>> +type::tuple, A<%=j%><%}%>>& operator>> ( + object o, + type::tuple, A<%=j%><%}%>>& v) { + if(o.type != type::ARRAY) { throw type_error(); } + if(o.via.array.size < <%=i+1%>) { throw type_error(); } + <%0.upto(i) {|j|%> + o.via.array.ptr[<%=j%>].convert>::type>(&v.template get<<%=j%>>());<%}%> + return v; +} +<%}%> + +template +const packer& operator<< ( + packer& o, + const type::tuple<>& v) { + o.pack_array(0); + return o; +} +<%0.upto(GENERATION_LIMIT) {|i|%> +template , typename A<%=j%><%}%>> +const packer& operator<< ( + packer& o, + const type::tuple, A<%=j%><%}%>>& v) { + o.pack_array(<%=i+1%>); + <%0.upto(i) {|j|%> + o.pack(v.template get<<%=j%>>());<%}%> + return o; +} +<%}%> + +inline void operator<< ( + object::with_zone& o, + const type::tuple<>& v) { + o.type = type::ARRAY; + o.via.array.ptr = NULL; + o.via.array.size = 0; +} +<%0.upto(GENERATION_LIMIT) {|i|%> +template , typename A<%=j%><%}%>> +inline void operator<< ( + object::with_zone& o, + const type::tuple, A<%=j%><%}%>>& v) { + o.type = type::ARRAY; + o.via.array.ptr = (object*)o.zone->malloc(sizeof(object)*<%=i+1%>); + o.via.array.size = <%=i+1%>; + <%0.upto(i) {|j|%> + o.via.array.ptr[<%=j%>] = object(v.template get<<%=j%>>(), o.zone);<%}%> +} +<%}%> + +} // namespace msgpack + + +//inline std::ostream& operator<< (std::ostream& o, const msgpack::type::tuple<>& v) { +// return o << "[]"; +//} +//<%0.upto(GENERATION_LIMIT) {|i|%> +//template , typename A<%=j%><%}%>> +//inline std::ostream& operator<< (std::ostream& o, +// const msgpack::type::tuple, A<%=j%><%}%>>& v) { +// return o << "[" +// <%0.upto(i) {|j|%> +// <<<%if j != 0 then%> ", " <<<%end%> v.template get<<%=j%>>()<%}%> +// << "]"; +//} +//<%}%> + +#endif /* msgpack/type/tuple.hpp */ + diff --git a/msgpack/src/msgpack/type/vector.hpp b/msgpack/src/msgpack/type/vector.hpp new file mode 100644 index 00000000..bd073ef8 --- /dev/null +++ b/msgpack/src/msgpack/type/vector.hpp @@ -0,0 +1,81 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_TYPE_VECTOR_HPP__ +#define MSGPACK_TYPE_VECTOR_HPP__ + +#include "msgpack/object.hpp" +#include + +namespace msgpack { + + +template +inline std::vector& operator>> (object o, std::vector& v) +{ + if(o.type != type::ARRAY) { throw type_error(); } + v.resize(o.via.array.size); + if(o.via.array.size > 0) { + object* p = o.via.array.ptr; + object* const pend = o.via.array.ptr + o.via.array.size; + T* it = &v[0]; + do { + p->convert(it); + ++p; + ++it; + } while(p < pend); + } + return v; +} + +template +inline packer& operator<< (packer& o, const std::vector& v) +{ + o.pack_array(v.size()); + for(typename std::vector::const_iterator it(v.begin()), it_end(v.end()); + it != it_end; ++it) { + o.pack(*it); + } + return o; +} + +template +inline void operator<< (object::with_zone& o, const std::vector& v) +{ + o.type = type::ARRAY; + if(v.empty()) { + o.via.array.ptr = NULL; + o.via.array.size = 0; + } else { + object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); + object* const pend = p + v.size(); + o.via.array.ptr = p; + o.via.array.size = v.size(); + typename std::vector::const_iterator it(v.begin()); + do { + *p = object(*it, o.zone); + ++p; + ++it; + } while(p < pend); + } +} + + +} // namespace msgpack + +#endif /* msgpack/type/vector.hpp */ + diff --git a/msgpack/src/msgpack/unpack.h b/msgpack/src/msgpack/unpack.h new file mode 100644 index 00000000..bea7d922 --- /dev/null +++ b/msgpack/src/msgpack/unpack.h @@ -0,0 +1,260 @@ +/* + * MessagePack for C unpacking routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_UNPACKER_H__ +#define MSGPACK_UNPACKER_H__ + +#include "zone.h" +#include "object.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_unpack Deserializer + * @ingroup msgpack + * @{ + */ + +typedef struct msgpack_unpacked { + msgpack_zone* zone; + msgpack_object data; +} msgpack_unpacked; + +bool msgpack_unpack_next(msgpack_unpacked* result, + const char* data, size_t len, size_t* off); + +/** @} */ + + +/** + * @defgroup msgpack_unpacker Streaming deserializer + * @ingroup msgpack + * @{ + */ + +typedef struct msgpack_unpacker { + char* buffer; + size_t used; + size_t free; + size_t off; + size_t parsed; + msgpack_zone* z; + size_t initial_buffer_size; + void* ctx; +} msgpack_unpacker; + + +#ifndef MSGPACK_UNPACKER_INIT_BUFFER_SIZE +#define MSGPACK_UNPACKER_INIT_BUFFER_SIZE (64*1024) +#endif + +/** + * Initializes a streaming deserializer. + * The initialized deserializer must be destroyed by msgpack_unpacker_destroy(msgpack_unpacker*). + */ +bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size); + +/** + * Destroys a streaming deserializer initialized by msgpack_unpacker_init(msgpack_unpacker*, size_t). + */ +void msgpack_unpacker_destroy(msgpack_unpacker* mpac); + + +/** + * Creates a streaming deserializer. + * The created deserializer must be destroyed by msgpack_unpacker_free(msgpack_unpacker*). + */ +msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size); + +/** + * Frees a streaming deserializer created by msgpack_unpacker_new(size_t). + */ +void msgpack_unpacker_free(msgpack_unpacker* mpac); + + +#ifndef MSGPACK_UNPACKER_RESERVE_SIZE +#define MSGPACK_UNPACKER_RESERVE_SIZE (32*1024) +#endif + +/** + * Reserves free space of the internal buffer. + * Use this function to fill the internal buffer with + * msgpack_unpacker_buffer(msgpack_unpacker*), + * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*) and + * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). + */ +static inline bool msgpack_unpacker_reserve_buffer(msgpack_unpacker* mpac, size_t size); + +/** + * Gets pointer to the free space of the internal buffer. + * Use this function to fill the internal buffer with + * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), + * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*) and + * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). + */ +static inline char* msgpack_unpacker_buffer(msgpack_unpacker* mpac); + +/** + * Gets size of the free space of the internal buffer. + * Use this function to fill the internal buffer with + * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), + * msgpack_unpacker_buffer(const msgpack_unpacker*) and + * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). + */ +static inline size_t msgpack_unpacker_buffer_capacity(const msgpack_unpacker* mpac); + +/** + * Notifies the deserializer that the internal buffer filled. + * Use this function to fill the internal buffer with + * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), + * msgpack_unpacker_buffer(msgpack_unpacker*) and + * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*). + */ +static inline void msgpack_unpacker_buffer_consumed(msgpack_unpacker* mpac, size_t size); + + +/** + * Deserializes one object. + * Returns true if it successes. Otherwise false is returned. + * @param pac pointer to an initialized msgpack_unpacked object. + */ +bool msgpack_unpacker_next(msgpack_unpacker* mpac, msgpack_unpacked* pac); + +/** + * Initializes a msgpack_unpacked object. + * The initialized object must be destroyed by msgpack_unpacked_destroy(msgpack_unpacker*). + * Use the object with msgpack_unpacker_next(msgpack_unpacker*, msgpack_unpacked*) or + * msgpack_unpack_next(msgpack_unpacked*, const char*, size_t, size_t*). + */ +static inline void msgpack_unpacked_init(msgpack_unpacked* result); + +/** + * Destroys a streaming deserializer initialized by msgpack_unpacked(). + */ +static inline void msgpack_unpacked_destroy(msgpack_unpacked* result); + +/** + * Releases the memory zone from msgpack_unpacked object. + * The released zone must be freed by msgpack_zone_free(msgpack_zone*). + */ +static inline msgpack_zone* msgpack_unpacked_release_zone(msgpack_unpacked* result); + + +int msgpack_unpacker_execute(msgpack_unpacker* mpac); + +msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac); + +msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac); + +void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac); + +void msgpack_unpacker_reset(msgpack_unpacker* mpac); + +static inline size_t msgpack_unpacker_message_size(const msgpack_unpacker* mpac); + + +/** @} */ + + +// obsolete +typedef enum { + MSGPACK_UNPACK_SUCCESS = 2, + MSGPACK_UNPACK_EXTRA_BYTES = 1, + MSGPACK_UNPACK_CONTINUE = 0, + MSGPACK_UNPACK_PARSE_ERROR = -1, +} msgpack_unpack_return; + +// obsolete +msgpack_unpack_return +msgpack_unpack(const char* data, size_t len, size_t* off, + msgpack_zone* result_zone, msgpack_object* result); + + +static inline size_t msgpack_unpacker_parsed_size(const msgpack_unpacker* mpac); + +bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac); + +bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size); + +bool msgpack_unpacker_reserve_buffer(msgpack_unpacker* mpac, size_t size) +{ + if(mpac->free >= size) { return true; } + return msgpack_unpacker_expand_buffer(mpac, size); +} + +char* msgpack_unpacker_buffer(msgpack_unpacker* mpac) +{ + return mpac->buffer + mpac->used; +} + +size_t msgpack_unpacker_buffer_capacity(const msgpack_unpacker* mpac) +{ + return mpac->free; +} + +void msgpack_unpacker_buffer_consumed(msgpack_unpacker* mpac, size_t size) +{ + mpac->used += size; + mpac->free -= size; +} + +size_t msgpack_unpacker_message_size(const msgpack_unpacker* mpac) +{ + return mpac->parsed - mpac->off + mpac->used; +} + +size_t msgpack_unpacker_parsed_size(const msgpack_unpacker* mpac) +{ + return mpac->parsed; +} + + +void msgpack_unpacked_init(msgpack_unpacked* result) +{ + memset(result, 0, sizeof(msgpack_unpacked)); +} + +void msgpack_unpacked_destroy(msgpack_unpacked* result) +{ + if(result->zone != NULL) { + msgpack_zone_free(result->zone); + result->zone = NULL; + memset(&result->data, 0, sizeof(msgpack_object)); + } +} + +msgpack_zone* msgpack_unpacked_release_zone(msgpack_unpacked* result) +{ + if(result->zone != NULL) { + msgpack_zone* z = result->zone; + result->zone = NULL; + return z; + } + return NULL; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/unpack.h */ + diff --git a/msgpack/src/msgpack/unpack.hpp b/msgpack/src/msgpack/unpack.hpp new file mode 100644 index 00000000..4d9de32f --- /dev/null +++ b/msgpack/src/msgpack/unpack.hpp @@ -0,0 +1,374 @@ +// +// MessagePack for C++ deserializing routine +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_UNPACK_HPP__ +#define MSGPACK_UNPACK_HPP__ + +#include "unpack.h" +#include "object.hpp" +#include "zone.hpp" +#include +#include + +// backward compatibility +#ifndef MSGPACK_UNPACKER_DEFAULT_INITIAL_BUFFER_SIZE +#define MSGPACK_UNPACKER_DEFAULT_INITIAL_BUFFER_SIZE MSGPACK_UNPACKER_INIT_BUFFER_SIZE +#endif + +namespace msgpack { + + +struct unpack_error : public std::runtime_error { + unpack_error(const std::string& msg) : + std::runtime_error(msg) { } +}; + + +class unpacked { +public: + unpacked() { } + + unpacked(object obj, std::auto_ptr z) : + m_obj(obj), m_zone(z) { } + + object& get() + { return m_obj; } + + const object& get() const + { return m_obj; } + + std::auto_ptr& zone() + { return m_zone; } + + const std::auto_ptr& zone() const + { return m_zone; } + +private: + object m_obj; + std::auto_ptr m_zone; +}; + + +class unpacker : public msgpack_unpacker { +public: + unpacker(size_t init_buffer_size = MSGPACK_UNPACKER_INIT_BUFFER_SIZE); + ~unpacker(); + +public: + /*! 1. reserve buffer. at least `size' bytes of capacity will be ready */ + void reserve_buffer(size_t size = MSGPACK_UNPACKER_RESERVE_SIZE); + + /*! 2. read data to the buffer() up to buffer_capacity() bytes */ + char* buffer(); + size_t buffer_capacity() const; + + /*! 3. specify the number of bytes actually copied */ + void buffer_consumed(size_t size); + + /*! 4. repeat next() until it retunrs false */ + bool next(unpacked* result); + + /*! 5. check if the size of message doesn't exceed assumption. */ + size_t message_size() const; + + // Basic usage of the unpacker is as following: + // + // msgpack::unpacker pac; + // while( /* input is readable */ ) { + // + // // 1. + // pac.reserve_buffer(32*1024); + // + // // 2. + // size_t bytes = input.readsome(pac.buffer(), pac.buffer_capacity()); + // + // // error handling ... + // + // // 3. + // pac.buffer_consumed(bytes); + // + // // 4. + // msgpack::unpacked result; + // while(pac.next(&result)) { + // // do some with the object with the zone. + // msgpack::object obj = result.get(); + // std::auto_ptr z = result.zone(); + // on_message(obj, z); + // + // //// boost::shared_ptr is also usable: + // // boost::shared_ptr life(z.release()); + // // on_message(result.get(), life); + // } + // + // // 5. + // if(pac.message_size() > 10*1024*1024) { + // throw std::runtime_error("message is too large"); + // } + // } + // + + /*! for backward compatibility */ + bool execute(); + + /*! for backward compatibility */ + object data(); + + /*! for backward compatibility */ + zone* release_zone(); + + /*! for backward compatibility */ + void reset_zone(); + + /*! for backward compatibility */ + void reset(); + +public: + // These functions are usable when non-MessagePack message follows after + // MessagePack message. + size_t parsed_size() const; + + /*! get address of the buffer that is not parsed */ + char* nonparsed_buffer(); + size_t nonparsed_size() const; + + /*! skip specified size of non-parsed buffer, leaving the buffer */ + // Note that the `size' argument must be smaller than nonparsed_size() + void skip_nonparsed_buffer(size_t size); + + /*! remove unparsed buffer from unpacker */ + // Note that reset() leaves non-parsed buffer. + void remove_nonparsed_buffer(); + +private: + typedef msgpack_unpacker base; + +private: + unpacker(const unpacker&); +}; + + +static void unpack(unpacked* result, + const char* data, size_t len, size_t* offset = NULL); + + +// obsolete +typedef enum { + UNPACK_SUCCESS = 2, + UNPACK_EXTRA_BYTES = 1, + UNPACK_CONTINUE = 0, + UNPACK_PARSE_ERROR = -1, +} unpack_return; + +// obsolete +static unpack_return unpack(const char* data, size_t len, size_t* off, + zone* z, object* result); + + +// obsolete +static object unpack(const char* data, size_t len, zone& z, size_t* off = NULL); + + +inline unpacker::unpacker(size_t initial_buffer_size) +{ + if(!msgpack_unpacker_init(this, initial_buffer_size)) { + throw std::bad_alloc(); + } +} + +inline unpacker::~unpacker() +{ + msgpack_unpacker_destroy(this); +} + + +inline void unpacker::reserve_buffer(size_t size) +{ + if(!msgpack_unpacker_reserve_buffer(this, size)) { + throw std::bad_alloc(); + } +} + +inline char* unpacker::buffer() +{ + return msgpack_unpacker_buffer(this); +} + +inline size_t unpacker::buffer_capacity() const +{ + return msgpack_unpacker_buffer_capacity(this); +} + +inline void unpacker::buffer_consumed(size_t size) +{ + return msgpack_unpacker_buffer_consumed(this, size); +} + +inline bool unpacker::next(unpacked* result) +{ + int ret = msgpack_unpacker_execute(this); + + if(ret < 0) { + throw unpack_error("parse error"); + } + + if(ret == 0) { + if (result->zone().get() != NULL) result->zone().reset(); + result->get() = object(); + return false; + + } else { + if (result->zone().get() != NULL) result->zone().reset( release_zone() ); + result->get() = data(); + reset(); + return true; + } +} + + +inline bool unpacker::execute() +{ + int ret = msgpack_unpacker_execute(this); + if(ret < 0) { + throw unpack_error("parse error"); + } else if(ret == 0) { + return false; + } else { + return true; + } +} + +inline object unpacker::data() +{ + return msgpack_unpacker_data(this); +} + +inline zone* unpacker::release_zone() +{ + return static_cast(msgpack_unpacker_release_zone(static_cast(this))); +} + +inline void unpacker::reset_zone() +{ + msgpack_unpacker_reset_zone(this); +} + +inline void unpacker::reset() +{ + msgpack_unpacker_reset(this); +} + + +inline size_t unpacker::message_size() const +{ + return msgpack_unpacker_message_size(this); +} + +inline size_t unpacker::parsed_size() const +{ + return msgpack_unpacker_parsed_size(this); +} + +inline char* unpacker::nonparsed_buffer() +{ + return base::buffer + base::off; +} + +inline size_t unpacker::nonparsed_size() const +{ + return base::used - base::off; +} + +inline void unpacker::skip_nonparsed_buffer(size_t size) +{ + base::off += size; +} + +inline void unpacker::remove_nonparsed_buffer() +{ + base::used = base::off; +} + + +inline void unpack(unpacked* result, + const char* data, size_t len, size_t* offset) +{ + msgpack::object obj; + std::auto_ptr z(new zone()); + + unpack_return ret = (unpack_return)msgpack_unpack( + data, len, offset, z.get(), + reinterpret_cast(&obj)); + + switch(ret) { + case UNPACK_SUCCESS: + result->get() = obj; + result->zone() = z; + return; + + case UNPACK_EXTRA_BYTES: + result->get() = obj; + result->zone() = z; + return; + + case UNPACK_CONTINUE: + throw unpack_error("insufficient bytes"); + + case UNPACK_PARSE_ERROR: + default: + throw unpack_error("parse error"); + } +} + + +// obsolete +inline unpack_return unpack(const char* data, size_t len, size_t* off, + zone* z, object* result) +{ + return (unpack_return)msgpack_unpack(data, len, off, + z, reinterpret_cast(result)); +} + +// obsolete +inline object unpack(const char* data, size_t len, zone& z, size_t* off) +{ + object result; + + switch( msgpack::unpack(data, len, off, &z, &result) ) { + case UNPACK_SUCCESS: + return result; + + case UNPACK_EXTRA_BYTES: + if(off) { + return result; + } else { + throw unpack_error("extra bytes"); + } + + case UNPACK_CONTINUE: + throw unpack_error("insufficient bytes"); + + case UNPACK_PARSE_ERROR: + default: + throw unpack_error("parse error"); + } +} + + +} // namespace msgpack + +#endif /* msgpack/unpack.hpp */ + diff --git a/msgpack/src/msgpack/version.h.in b/msgpack/src/msgpack/version.h.in new file mode 100644 index 00000000..f1feb331 --- /dev/null +++ b/msgpack/src/msgpack/version.h.in @@ -0,0 +1,40 @@ +/* + * MessagePack for C version information + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_VERSION_H__ +#define MSGPACK_VERSION_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +const char* msgpack_version(void); +int msgpack_version_major(void); +int msgpack_version_minor(void); + +#define MSGPACK_VERSION "@VERSION@" +#define MSGPACK_VERSION_MAJOR @VERSION_MAJOR@ +#define MSGPACK_VERSION_MINOR @VERSION_MINOR@ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/version.h */ + diff --git a/msgpack/src/msgpack/vrefbuffer.h b/msgpack/src/msgpack/vrefbuffer.h new file mode 100644 index 00000000..0643927e --- /dev/null +++ b/msgpack/src/msgpack/vrefbuffer.h @@ -0,0 +1,142 @@ +/* + * MessagePack for C zero-copy buffer implementation + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_VREFBUFFER_H__ +#define MSGPACK_VREFBUFFER_H__ + +#include "zone.h" +#include + +#ifndef _WIN32 +#include +#else +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_vrefbuffer Vectored Referencing buffer + * @ingroup msgpack_buffer + * @{ + */ + +struct msgpack_vrefbuffer_chunk; +typedef struct msgpack_vrefbuffer_chunk msgpack_vrefbuffer_chunk; + +typedef struct msgpack_vrefbuffer_inner_buffer { + size_t free; + char* ptr; + msgpack_vrefbuffer_chunk* head; +} msgpack_vrefbuffer_inner_buffer; + +typedef struct msgpack_vrefbuffer { + struct iovec* tail; + struct iovec* end; + struct iovec* array; + + size_t chunk_size; + size_t ref_size; + + msgpack_vrefbuffer_inner_buffer inner_buffer; +} msgpack_vrefbuffer; + + +#ifndef MSGPACK_VREFBUFFER_REF_SIZE +#define MSGPACK_VREFBUFFER_REF_SIZE 32 +#endif + +#ifndef MSGPACK_VREFBUFFER_CHUNK_SIZE +#define MSGPACK_VREFBUFFER_CHUNK_SIZE 8192 +#endif + +bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf, + size_t ref_size, size_t chunk_size); +void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf); + +static inline msgpack_vrefbuffer* msgpack_vrefbuffer_new(size_t ref_size, size_t chunk_size); +static inline void msgpack_vrefbuffer_free(msgpack_vrefbuffer* vbuf); + +static inline int msgpack_vrefbuffer_write(void* data, const char* buf, unsigned int len); + +static inline const struct iovec* msgpack_vrefbuffer_vec(const msgpack_vrefbuffer* vref); +static inline size_t msgpack_vrefbuffer_veclen(const msgpack_vrefbuffer* vref); + +int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf, + const char* buf, unsigned int len); + +int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf, + const char* buf, unsigned int len); + +int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to); + +void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vref); + +/** @} */ + + +msgpack_vrefbuffer* msgpack_vrefbuffer_new(size_t ref_size, size_t chunk_size) +{ + msgpack_vrefbuffer* vbuf = (msgpack_vrefbuffer*)malloc(sizeof(msgpack_vrefbuffer)); + if(!msgpack_vrefbuffer_init(vbuf, ref_size, chunk_size)) { + free(vbuf); + return NULL; + } + return vbuf; +} + +void msgpack_vrefbuffer_free(msgpack_vrefbuffer* vbuf) +{ + if(vbuf == NULL) { return; } + msgpack_vrefbuffer_destroy(vbuf); + free(vbuf); +} + +int msgpack_vrefbuffer_write(void* data, const char* buf, unsigned int len) +{ + msgpack_vrefbuffer* vbuf = (msgpack_vrefbuffer*)data; + + if(len < vbuf->ref_size) { + return msgpack_vrefbuffer_append_copy(vbuf, buf, len); + } else { + return msgpack_vrefbuffer_append_ref(vbuf, buf, len); + } +} + +const struct iovec* msgpack_vrefbuffer_vec(const msgpack_vrefbuffer* vref) +{ + return vref->array; +} + +size_t msgpack_vrefbuffer_veclen(const msgpack_vrefbuffer* vref) +{ + return vref->tail - vref->array; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/vrefbuffer.h */ + diff --git a/msgpack/src/msgpack/vrefbuffer.hpp b/msgpack/src/msgpack/vrefbuffer.hpp new file mode 100644 index 00000000..82335277 --- /dev/null +++ b/msgpack/src/msgpack/vrefbuffer.hpp @@ -0,0 +1,97 @@ +// +// MessagePack for C++ zero-copy buffer implementation +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_VREFBUFFER_HPP__ +#define MSGPACK_VREFBUFFER_HPP__ + +#include "vrefbuffer.h" +#include + +namespace msgpack { + + +class vrefbuffer : public msgpack_vrefbuffer { +public: + vrefbuffer(size_t ref_size = MSGPACK_VREFBUFFER_REF_SIZE, + size_t chunk_size = MSGPACK_VREFBUFFER_CHUNK_SIZE) + { + msgpack_vrefbuffer_init(this, ref_size, chunk_size); + } + + ~vrefbuffer() + { + msgpack_vrefbuffer_destroy(this); + } + +public: + void write(const char* buf, unsigned int len) + { + if(len < base::ref_size) { + append_copy(buf, len); + } else { + append_ref(buf, len); + } + } + + void append_ref(const char* buf, size_t len) + { + if(msgpack_vrefbuffer_append_ref(this, buf, len) < 0) { + throw std::bad_alloc(); + } + } + + void append_copy(const char* buf, size_t len) + { + if(msgpack_vrefbuffer_append_copy(this, buf, len) < 0) { + throw std::bad_alloc(); + } + } + + const struct iovec* vector() const + { + return msgpack_vrefbuffer_vec(this); + } + + size_t vector_size() const + { + return msgpack_vrefbuffer_veclen(this); + } + + void migrate(vrefbuffer* to) + { + if(msgpack_vrefbuffer_migrate(this, to) < 0) { + throw std::bad_alloc(); + } + } + + void clear() + { + msgpack_vrefbuffer_clear(this); + } + +private: + typedef msgpack_vrefbuffer base; + +private: + vrefbuffer(const vrefbuffer&); +}; + + +} // namespace msgpack + +#endif /* msgpack/vrefbuffer.hpp */ + diff --git a/msgpack/src/msgpack/zbuffer.h b/msgpack/src/msgpack/zbuffer.h new file mode 100644 index 00000000..efdd3049 --- /dev/null +++ b/msgpack/src/msgpack/zbuffer.h @@ -0,0 +1,207 @@ +/* + * MessagePack for C deflate buffer implementation + * + * Copyright (C) 2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_ZBUFFER_H__ +#define MSGPACK_ZBUFFER_H__ + +#include "sysdep.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_zbuffer Compressed buffer + * @ingroup msgpack_buffer + * @{ + */ + +typedef struct msgpack_zbuffer { + z_stream stream; + char* data; + size_t init_size; +} msgpack_zbuffer; + +#ifndef MSGPACK_ZBUFFER_INIT_SIZE +#define MSGPACK_ZBUFFER_INIT_SIZE 8192 +#endif + +static inline bool msgpack_zbuffer_init(msgpack_zbuffer* zbuf, + int level, size_t init_size); +static inline void msgpack_zbuffer_destroy(msgpack_zbuffer* zbuf); + +static inline msgpack_zbuffer* msgpack_zbuffer_new(int level, size_t init_size); +static inline void msgpack_zbuffer_free(msgpack_zbuffer* zbuf); + +static inline char* msgpack_zbuffer_flush(msgpack_zbuffer* zbuf); + +static inline const char* msgpack_zbuffer_data(const msgpack_zbuffer* zbuf); +static inline size_t msgpack_zbuffer_size(const msgpack_zbuffer* zbuf); + +static inline bool msgpack_zbuffer_reset(msgpack_zbuffer* zbuf); +static inline void msgpack_zbuffer_reset_buffer(msgpack_zbuffer* zbuf); +static inline char* msgpack_zbuffer_release_buffer(msgpack_zbuffer* zbuf); + + +#ifndef MSGPACK_ZBUFFER_RESERVE_SIZE +#define MSGPACK_ZBUFFER_RESERVE_SIZE 512 +#endif + +static inline int msgpack_zbuffer_write(void* data, const char* buf, unsigned int len); + +static inline bool msgpack_zbuffer_expand(msgpack_zbuffer* zbuf); + + +bool msgpack_zbuffer_init(msgpack_zbuffer* zbuf, + int level, size_t init_size) +{ + memset(zbuf, 0, sizeof(msgpack_zbuffer)); + zbuf->init_size = init_size; + if(deflateInit(&zbuf->stream, level) != Z_OK) { + free(zbuf->data); + return false; + } + return true; +} + +void msgpack_zbuffer_destroy(msgpack_zbuffer* zbuf) +{ + deflateEnd(&zbuf->stream); + free(zbuf->data); +} + +msgpack_zbuffer* msgpack_zbuffer_new(int level, size_t init_size) +{ + msgpack_zbuffer* zbuf = (msgpack_zbuffer*)malloc(sizeof(msgpack_zbuffer)); + if(!msgpack_zbuffer_init(zbuf, level, init_size)) { + free(zbuf); + return NULL; + } + return zbuf; +} + +void msgpack_zbuffer_free(msgpack_zbuffer* zbuf) +{ + if(zbuf == NULL) { return; } + msgpack_zbuffer_destroy(zbuf); + free(zbuf); +} + +bool msgpack_zbuffer_expand(msgpack_zbuffer* zbuf) +{ + size_t used = (char*)zbuf->stream.next_out - zbuf->data; + size_t csize = used + zbuf->stream.avail_out; + size_t nsize = (csize == 0) ? zbuf->init_size : csize * 2; + + char* tmp = (char*)realloc(zbuf->data, nsize); + if(tmp == NULL) { + return false; + } + + zbuf->data = tmp; + zbuf->stream.next_out = (Bytef*)(tmp + used); + zbuf->stream.avail_out = nsize - used; + + return true; +} + +int msgpack_zbuffer_write(void* data, const char* buf, unsigned int len) +{ + msgpack_zbuffer* zbuf = (msgpack_zbuffer*)data; + + zbuf->stream.next_in = (Bytef*)buf; + zbuf->stream.avail_in = len; + + do { + if(zbuf->stream.avail_out < MSGPACK_ZBUFFER_RESERVE_SIZE) { + if(!msgpack_zbuffer_expand(zbuf)) { + return -1; + } + } + + if(deflate(&zbuf->stream, Z_NO_FLUSH) != Z_OK) { + return -1; + } + } while(zbuf->stream.avail_in > 0); + + return 0; +} + +char* msgpack_zbuffer_flush(msgpack_zbuffer* zbuf) +{ + while(true) { + switch(deflate(&zbuf->stream, Z_FINISH)) { + case Z_STREAM_END: + return zbuf->data; + case Z_OK: + if(!msgpack_zbuffer_expand(zbuf)) { + return NULL; + } + break; + default: + return NULL; + } + } +} + +const char* msgpack_zbuffer_data(const msgpack_zbuffer* zbuf) +{ + return zbuf->data; +} + +size_t msgpack_zbuffer_size(const msgpack_zbuffer* zbuf) +{ + return (char*)zbuf->stream.next_out - zbuf->data; +} + +void msgpack_zbuffer_reset_buffer(msgpack_zbuffer* zbuf) +{ + zbuf->stream.avail_out += (char*)zbuf->stream.next_out - zbuf->data; + zbuf->stream.next_out = (Bytef*)zbuf->data; +} + +bool msgpack_zbuffer_reset(msgpack_zbuffer* zbuf) +{ + if(deflateReset(&zbuf->stream) != Z_OK) { + return false; + } + msgpack_zbuffer_reset_buffer(zbuf); + return true; +} + +char* msgpack_zbuffer_release_buffer(msgpack_zbuffer* zbuf) +{ + char* tmp = zbuf->data; + zbuf->data = NULL; + zbuf->stream.next_out = NULL; + zbuf->stream.avail_out = 0; + return tmp; +} + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/zbuffer.h */ + diff --git a/msgpack/src/msgpack/zbuffer.hpp b/msgpack/src/msgpack/zbuffer.hpp new file mode 100644 index 00000000..08e6fd03 --- /dev/null +++ b/msgpack/src/msgpack/zbuffer.hpp @@ -0,0 +1,100 @@ +// +// MessagePack for C++ deflate buffer implementation +// +// Copyright (C) 2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_ZBUFFER_HPP__ +#define MSGPACK_ZBUFFER_HPP__ + +#include "zbuffer.h" +#include + +namespace msgpack { + + +class zbuffer : public msgpack_zbuffer { +public: + zbuffer(int level = Z_DEFAULT_COMPRESSION, + size_t init_size = MSGPACK_ZBUFFER_INIT_SIZE) + { + msgpack_zbuffer_init(this, level, init_size); + } + + ~zbuffer() + { + msgpack_zbuffer_destroy(this); + } + +public: + void write(const char* buf, unsigned int len) + { + if(msgpack_zbuffer_write(this, buf, len) < 0) { + throw std::bad_alloc(); + } + } + + char* flush() + { + char* buf = msgpack_zbuffer_flush(this); + if(!buf) { + throw std::bad_alloc(); + } + return buf; + } + + char* data() + { + return base::data; + } + + const char* data() const + { + return base::data; + } + + size_t size() const + { + return msgpack_zbuffer_size(this); + } + + void reset() + { + if(!msgpack_zbuffer_reset(this)) { + throw std::bad_alloc(); + } + } + + void reset_buffer() + { + msgpack_zbuffer_reset_buffer(this); + } + + char* release_buffer() + { + return msgpack_zbuffer_release_buffer(this); + } + +private: + typedef msgpack_zbuffer base; + +private: + zbuffer(const zbuffer&); +}; + + +} // namespace msgpack + +#endif /* msgpack/zbuffer.hpp */ + diff --git a/msgpack/src/msgpack/zone.h b/msgpack/src/msgpack/zone.h new file mode 100644 index 00000000..0f5817f3 --- /dev/null +++ b/msgpack/src/msgpack/zone.h @@ -0,0 +1,147 @@ +/* + * MessagePack for C memory pool implementation + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_ZONE_H__ +#define MSGPACK_ZONE_H__ + +#include "sysdep.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @defgroup msgpack_zone Memory zone + * @ingroup msgpack + * @{ + */ + +typedef struct msgpack_zone_finalizer { + void (*func)(void* data); + void* data; +} msgpack_zone_finalizer; + +typedef struct msgpack_zone_finalizer_array { + msgpack_zone_finalizer* tail; + msgpack_zone_finalizer* end; + msgpack_zone_finalizer* array; +} msgpack_zone_finalizer_array; + +struct msgpack_zone_chunk; +typedef struct msgpack_zone_chunk msgpack_zone_chunk; + +typedef struct msgpack_zone_chunk_list { + size_t free; + char* ptr; + msgpack_zone_chunk* head; +} msgpack_zone_chunk_list; + +typedef struct msgpack_zone { + msgpack_zone_chunk_list chunk_list; + msgpack_zone_finalizer_array finalizer_array; + size_t chunk_size; +} msgpack_zone; + +#ifndef MSGPACK_ZONE_CHUNK_SIZE +#define MSGPACK_ZONE_CHUNK_SIZE 8192 +#endif + +bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size); +void msgpack_zone_destroy(msgpack_zone* zone); + +msgpack_zone* msgpack_zone_new(size_t chunk_size); +void msgpack_zone_free(msgpack_zone* zone); + +static inline void* msgpack_zone_malloc(msgpack_zone* zone, size_t size); +static inline void* msgpack_zone_malloc_no_align(msgpack_zone* zone, size_t size); + +static inline bool msgpack_zone_push_finalizer(msgpack_zone* zone, + void (*func)(void* data), void* data); + +static inline void msgpack_zone_swap(msgpack_zone* a, msgpack_zone* b); + +bool msgpack_zone_is_empty(msgpack_zone* zone); + +void msgpack_zone_clear(msgpack_zone* zone); + +/** @} */ + + +#ifndef MSGPACK_ZONE_ALIGN +#define MSGPACK_ZONE_ALIGN sizeof(int) +#endif + +void* msgpack_zone_malloc_expand(msgpack_zone* zone, size_t size); + +void* msgpack_zone_malloc_no_align(msgpack_zone* zone, size_t size) +{ + msgpack_zone_chunk_list* cl = &zone->chunk_list; + + if(zone->chunk_list.free < size) { + return msgpack_zone_malloc_expand(zone, size); + } + + char* ptr = cl->ptr; + cl->free -= size; + cl->ptr += size; + + return ptr; +} + +void* msgpack_zone_malloc(msgpack_zone* zone, size_t size) +{ + return msgpack_zone_malloc_no_align(zone, + ((size)+((MSGPACK_ZONE_ALIGN)-1)) & ~((MSGPACK_ZONE_ALIGN)-1)); +} + + +bool msgpack_zone_push_finalizer_expand(msgpack_zone* zone, + void (*func)(void* data), void* data); + +bool msgpack_zone_push_finalizer(msgpack_zone* zone, + void (*func)(void* data), void* data) +{ + msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; + msgpack_zone_finalizer* fin = fa->tail; + + if(fin == fa->end) { + return msgpack_zone_push_finalizer_expand(zone, func, data); + } + + fin->func = func; + fin->data = data; + + ++fa->tail; + + return true; +} + +void msgpack_zone_swap(msgpack_zone* a, msgpack_zone* b) +{ + msgpack_zone tmp = *a; + *a = *b; + *b = tmp; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/zone.h */ + diff --git a/msgpack/src/msgpack/zone.hpp.erb b/msgpack/src/msgpack/zone.hpp.erb new file mode 100644 index 00000000..c6f5481a --- /dev/null +++ b/msgpack/src/msgpack/zone.hpp.erb @@ -0,0 +1,155 @@ +// +// MessagePack for C++ memory pool +// +// Copyright (C) 2008-2010 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef MSGPACK_ZONE_HPP__ +#define MSGPACK_ZONE_HPP__ + +#include "zone.h" +#include +#include +#include + +<% GENERATION_LIMIT = 15 %> +namespace msgpack { + + +class zone : public msgpack_zone { +public: + zone(size_t chunk_size = MSGPACK_ZONE_CHUNK_SIZE); + ~zone(); + +public: + void* malloc(size_t size); + void* malloc_no_align(size_t size); + + void push_finalizer(void (*func)(void*), void* data); + + template + void push_finalizer(std::auto_ptr obj); + + void clear(); + + void swap(zone& o); + + <%0.upto(GENERATION_LIMIT) {|i|%> + template , typename A<%=j%><%}%>> + T* allocate(<%=(1..i).map{|j|"A#{j} a#{j}"}.join(', ')%>); + <%}%> + +private: + void undo_malloc(size_t size); + + template + static void object_destructor(void* obj); + + typedef msgpack_zone base; + +private: + zone(const zone&); +}; + + + +inline zone::zone(size_t chunk_size) +{ + msgpack_zone_init(this, chunk_size); +} + +inline zone::~zone() +{ + msgpack_zone_destroy(this); +} + +inline void* zone::malloc(size_t size) +{ + void* ptr = msgpack_zone_malloc(this, size); + if(!ptr) { + throw std::bad_alloc(); + } + return ptr; +} + +inline void* zone::malloc_no_align(size_t size) +{ + void* ptr = msgpack_zone_malloc_no_align(this, size); + if(!ptr) { + throw std::bad_alloc(); + } + return ptr; +} + +inline void zone::push_finalizer(void (*func)(void*), void* data) +{ + if(!msgpack_zone_push_finalizer(this, func, data)) { + throw std::bad_alloc(); + } +} + +template +inline void zone::push_finalizer(std::auto_ptr obj) +{ + if(!msgpack_zone_push_finalizer(this, &zone::object_destructor, obj.get())) { + throw std::bad_alloc(); + } + obj.release(); +} + +inline void zone::clear() +{ + msgpack_zone_clear(this); +} + +inline void zone::swap(zone& o) +{ + msgpack_zone_swap(this, &o); +} + +template +void zone::object_destructor(void* obj) +{ + reinterpret_cast(obj)->~T(); +} + +inline void zone::undo_malloc(size_t size) +{ + base::chunk_list.ptr -= size; + base::chunk_list.free += size; +} + +<%0.upto(GENERATION_LIMIT) {|i|%> +template , typename A<%=j%><%}%>> +T* zone::allocate(<%=(1..i).map{|j|"A#{j} a#{j}"}.join(', ')%>) +{ + void* x = malloc(sizeof(T)); + if(!msgpack_zone_push_finalizer(this, &zone::object_destructor, x)) { + undo_malloc(sizeof(T)); + throw std::bad_alloc(); + } + try { + return new (x) T(<%=(1..i).map{|j|"a#{j}"}.join(', ')%>); + } catch (...) { + --base::finalizer_array.tail; + undo_malloc(sizeof(T)); + throw; + } +} +<%}%> + +} // namespace msgpack + +#endif /* msgpack/zone.hpp */ + diff --git a/msgpack/src/object.cpp b/msgpack/src/object.cpp new file mode 100644 index 00000000..dfe32bbc --- /dev/null +++ b/msgpack/src/object.cpp @@ -0,0 +1,87 @@ +// +// MessagePack for C++ dynamic typed objects +// +// Copyright (C) 2008-2009 FURUHASHI Sadayuki +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "msgpack/object.hpp" + +namespace msgpack { + + +std::ostream& operator<< (std::ostream& s, const object o) +{ + switch(o.type) { + case type::NIL: + s << "nil"; + break; + + case type::BOOLEAN: + s << (o.via.boolean ? "true" : "false"); + break; + + case type::POSITIVE_INTEGER: + s << o.via.u64; + break; + + case type::NEGATIVE_INTEGER: + s << o.via.i64; + break; + + case type::DOUBLE: + s << o.via.dec; + break; + + case type::RAW: + (s << '"').write(o.via.raw.ptr, o.via.raw.size) << '"'; + break; + + case type::ARRAY: + s << "["; + if(o.via.array.size != 0) { + object* p(o.via.array.ptr); + s << *p; + ++p; + for(object* const pend(o.via.array.ptr + o.via.array.size); + p < pend; ++p) { + s << ", " << *p; + } + } + s << "]"; + break; + + case type::MAP: + s << "{"; + if(o.via.map.size != 0) { + object_kv* p(o.via.map.ptr); + s << p->key << "=>" << p->val; + ++p; + for(object_kv* const pend(o.via.map.ptr + o.via.map.size); + p < pend; ++p) { + s << ", " << p->key << "=>" << p->val; + } + } + s << "}"; + break; + + default: + // FIXME + s << "#"; + } + return s; +} + + +} // namespace msgpack + diff --git a/msgpack/src/objectc.c b/msgpack/src/objectc.c new file mode 100644 index 00000000..548e923e --- /dev/null +++ b/msgpack/src/objectc.c @@ -0,0 +1,237 @@ +/* + * MessagePack for C dynamic typing routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "msgpack/object.h" +#include "msgpack/pack.h" +#include +#include + +#ifndef _MSC_VER +#include +#else +#ifndef PRIu64 +#define PRIu64 "I64u" +#endif +#ifndef PRIi64 +#define PRIi64 "I64d" +#endif +#endif + + +int msgpack_pack_object(msgpack_packer* pk, msgpack_object d) +{ + switch(d.type) { + case MSGPACK_OBJECT_NIL: + return msgpack_pack_nil(pk); + + case MSGPACK_OBJECT_BOOLEAN: + if(d.via.boolean) { + return msgpack_pack_true(pk); + } else { + return msgpack_pack_false(pk); + } + + case MSGPACK_OBJECT_POSITIVE_INTEGER: + return msgpack_pack_uint64(pk, d.via.u64); + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + return msgpack_pack_int64(pk, d.via.i64); + + case MSGPACK_OBJECT_DOUBLE: + return msgpack_pack_double(pk, d.via.dec); + + case MSGPACK_OBJECT_RAW: + { + int ret = msgpack_pack_raw(pk, d.via.raw.size); + if(ret < 0) { return ret; } + return msgpack_pack_raw_body(pk, d.via.raw.ptr, d.via.raw.size); + } + + case MSGPACK_OBJECT_ARRAY: + { + int ret = msgpack_pack_array(pk, d.via.array.size); + if(ret < 0) { return ret; } + + msgpack_object* o = d.via.array.ptr; + msgpack_object* const oend = d.via.array.ptr + d.via.array.size; + for(; o != oend; ++o) { + ret = msgpack_pack_object(pk, *o); + if(ret < 0) { return ret; } + } + + return 0; + } + + case MSGPACK_OBJECT_MAP: + { + int ret = msgpack_pack_map(pk, d.via.map.size); + if(ret < 0) { return ret; } + + msgpack_object_kv* kv = d.via.map.ptr; + msgpack_object_kv* const kvend = d.via.map.ptr + d.via.map.size; + for(; kv != kvend; ++kv) { + ret = msgpack_pack_object(pk, kv->key); + if(ret < 0) { return ret; } + ret = msgpack_pack_object(pk, kv->val); + if(ret < 0) { return ret; } + } + + return 0; + } + + default: + return -1; + } +} + + +void msgpack_object_print(FILE* out, msgpack_object o) +{ + switch(o.type) { + case MSGPACK_OBJECT_NIL: + fprintf(out, "nil"); + break; + + case MSGPACK_OBJECT_BOOLEAN: + fprintf(out, (o.via.boolean ? "true" : "false")); + break; + + case MSGPACK_OBJECT_POSITIVE_INTEGER: + fprintf(out, "%"PRIu64, o.via.u64); + break; + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + fprintf(out, "%"PRIi64, o.via.i64); + break; + + case MSGPACK_OBJECT_DOUBLE: + fprintf(out, "%f", o.via.dec); + break; + + case MSGPACK_OBJECT_RAW: + fprintf(out, "\""); + fwrite(o.via.raw.ptr, o.via.raw.size, 1, out); + fprintf(out, "\""); + break; + + case MSGPACK_OBJECT_ARRAY: + fprintf(out, "["); + if(o.via.array.size != 0) { + msgpack_object* p = o.via.array.ptr; + msgpack_object_print(out, *p); + ++p; + msgpack_object* const pend = o.via.array.ptr + o.via.array.size; + for(; p < pend; ++p) { + fprintf(out, ", "); + msgpack_object_print(out, *p); + } + } + fprintf(out, "]"); + break; + + case MSGPACK_OBJECT_MAP: + fprintf(out, "{"); + if(o.via.map.size != 0) { + msgpack_object_kv* p = o.via.map.ptr; + msgpack_object_print(out, p->key); + fprintf(out, "=>"); + msgpack_object_print(out, p->val); + ++p; + msgpack_object_kv* const pend = o.via.map.ptr + o.via.map.size; + for(; p < pend; ++p) { + fprintf(out, ", "); + msgpack_object_print(out, p->key); + fprintf(out, "=>"); + msgpack_object_print(out, p->val); + } + } + fprintf(out, "}"); + break; + + default: + // FIXME + fprintf(out, "#", o.type, o.via.u64); + } +} + +bool msgpack_object_equal(const msgpack_object x, const msgpack_object y) +{ + if(x.type != y.type) { return false; } + + switch(x.type) { + case MSGPACK_OBJECT_NIL: + return true; + + case MSGPACK_OBJECT_BOOLEAN: + return x.via.boolean == y.via.boolean; + + case MSGPACK_OBJECT_POSITIVE_INTEGER: + return x.via.u64 == y.via.u64; + + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + return x.via.i64 == y.via.i64; + + case MSGPACK_OBJECT_DOUBLE: + return x.via.dec == y.via.dec; + + case MSGPACK_OBJECT_RAW: + return x.via.raw.size == y.via.raw.size && + memcmp(x.via.raw.ptr, y.via.raw.ptr, x.via.raw.size) == 0; + + case MSGPACK_OBJECT_ARRAY: + if(x.via.array.size != y.via.array.size) { + return false; + } else if(x.via.array.size == 0) { + return true; + } else { + msgpack_object* px = x.via.array.ptr; + msgpack_object* const pxend = x.via.array.ptr + x.via.array.size; + msgpack_object* py = y.via.array.ptr; + do { + if(!msgpack_object_equal(*px, *py)) { + return false; + } + ++px; + ++py; + } while(px < pxend); + return true; + } + + case MSGPACK_OBJECT_MAP: + if(x.via.map.size != y.via.map.size) { + return false; + } else if(x.via.map.size == 0) { + return true; + } else { + msgpack_object_kv* px = x.via.map.ptr; + msgpack_object_kv* const pxend = x.via.map.ptr + x.via.map.size; + msgpack_object_kv* py = y.via.map.ptr; + do { + if(!msgpack_object_equal(px->key, py->key) || !msgpack_object_equal(px->val, py->val)) { + return false; + } + ++px; + ++py; + } while(px < pxend); + return true; + } + + default: + return false; + } +} + diff --git a/msgpack/src/unpack.c b/msgpack/src/unpack.c new file mode 100644 index 00000000..4afc05e2 --- /dev/null +++ b/msgpack/src/unpack.c @@ -0,0 +1,468 @@ +/* + * MessagePack for C unpacking routine + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "msgpack/unpack.h" +#include "msgpack/unpack_define.h" +#include + +#ifdef _msgpack_atomic_counter_header +#include _msgpack_atomic_counter_header +#endif + + +typedef struct { + msgpack_zone* z; + bool referenced; +} unpack_user; + + +#define msgpack_unpack_struct(name) \ + struct template ## name + +#define msgpack_unpack_func(ret, name) \ + ret template ## name + +#define msgpack_unpack_callback(name) \ + template_callback ## name + +#define msgpack_unpack_object msgpack_object + +#define msgpack_unpack_user unpack_user + + +struct template_context; +typedef struct template_context template_context; + +static void template_init(template_context* ctx); + +static msgpack_object template_data(template_context* ctx); + +static int template_execute(template_context* ctx, + const char* data, size_t len, size_t* off); + + +static inline msgpack_object template_callback_root(unpack_user* u) +{ msgpack_object o = {}; return o; } + +static inline int template_callback_uint8(unpack_user* u, uint8_t d, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } + +static inline int template_callback_uint16(unpack_user* u, uint16_t d, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } + +static inline int template_callback_uint32(unpack_user* u, uint32_t d, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } + +static inline int template_callback_uint64(unpack_user* u, uint64_t d, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } + +static inline int template_callback_int8(unpack_user* u, int8_t d, msgpack_object* o) +{ if(d >= 0) { o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } + else { o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } + +static inline int template_callback_int16(unpack_user* u, int16_t d, msgpack_object* o) +{ if(d >= 0) { o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } + else { o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } + +static inline int template_callback_int32(unpack_user* u, int32_t d, msgpack_object* o) +{ if(d >= 0) { o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } + else { o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } + +static inline int template_callback_int64(unpack_user* u, int64_t d, msgpack_object* o) +{ if(d >= 0) { o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } + else { o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } + +static inline int template_callback_float(unpack_user* u, float d, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_DOUBLE; o->via.dec = d; return 0; } + +static inline int template_callback_double(unpack_user* u, double d, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_DOUBLE; o->via.dec = d; return 0; } + +static inline int template_callback_nil(unpack_user* u, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_NIL; return 0; } + +static inline int template_callback_true(unpack_user* u, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_BOOLEAN; o->via.boolean = true; return 0; } + +static inline int template_callback_false(unpack_user* u, msgpack_object* o) +{ o->type = MSGPACK_OBJECT_BOOLEAN; o->via.boolean = false; return 0; } + +static inline int template_callback_array(unpack_user* u, unsigned int n, msgpack_object* o) +{ + o->type = MSGPACK_OBJECT_ARRAY; + o->via.array.size = 0; + o->via.array.ptr = (msgpack_object*)msgpack_zone_malloc(u->z, n*sizeof(msgpack_object)); + if(o->via.array.ptr == NULL) { return -1; } + return 0; +} + +static inline int template_callback_array_item(unpack_user* u, msgpack_object* c, msgpack_object o) +{ c->via.array.ptr[c->via.array.size++] = o; return 0; } + +static inline int template_callback_map(unpack_user* u, unsigned int n, msgpack_object* o) +{ + o->type = MSGPACK_OBJECT_MAP; + o->via.map.size = 0; + o->via.map.ptr = (msgpack_object_kv*)msgpack_zone_malloc(u->z, n*sizeof(msgpack_object_kv)); + if(o->via.map.ptr == NULL) { return -1; } + return 0; +} + +static inline int template_callback_map_item(unpack_user* u, msgpack_object* c, msgpack_object k, msgpack_object v) +{ + c->via.map.ptr[c->via.map.size].key = k; + c->via.map.ptr[c->via.map.size].val = v; + ++c->via.map.size; + return 0; +} + +static inline int template_callback_raw(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o) +{ + o->type = MSGPACK_OBJECT_RAW; + o->via.raw.ptr = p; + o->via.raw.size = l; + u->referenced = true; + return 0; +} + +#include "msgpack/unpack_template.h" + + +#define CTX_CAST(m) ((template_context*)(m)) +#define CTX_REFERENCED(mpac) CTX_CAST((mpac)->ctx)->user.referenced + +#define COUNTER_SIZE (sizeof(_msgpack_atomic_counter_t)) + + +static inline void init_count(void* buffer) +{ + *(volatile _msgpack_atomic_counter_t*)buffer = 1; +} + +static inline void decl_count(void* buffer) +{ + // atomic if(--*(_msgpack_atomic_counter_t*)buffer == 0) { free(buffer); } + if(_msgpack_sync_decr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer) == 0) { + free(buffer); + } +} + +static inline void incr_count(void* buffer) +{ + // atomic ++*(_msgpack_atomic_counter_t*)buffer; + _msgpack_sync_incr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer); +} + +static inline _msgpack_atomic_counter_t get_count(void* buffer) +{ + return *(volatile _msgpack_atomic_counter_t*)buffer; +} + + + +bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size) +{ + if(initial_buffer_size < COUNTER_SIZE) { + initial_buffer_size = COUNTER_SIZE; + } + + char* buffer = (char*)malloc(initial_buffer_size); + if(buffer == NULL) { + return false; + } + + void* ctx = malloc(sizeof(template_context)); + if(ctx == NULL) { + free(buffer); + return false; + } + + msgpack_zone* z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); + if(z == NULL) { + free(ctx); + free(buffer); + return false; + } + + mpac->buffer = buffer; + mpac->used = COUNTER_SIZE; + mpac->free = initial_buffer_size - mpac->used; + mpac->off = COUNTER_SIZE; + mpac->parsed = 0; + mpac->initial_buffer_size = initial_buffer_size; + mpac->z = z; + mpac->ctx = ctx; + + init_count(mpac->buffer); + + template_init(CTX_CAST(mpac->ctx)); + CTX_CAST(mpac->ctx)->user.z = mpac->z; + CTX_CAST(mpac->ctx)->user.referenced = false; + + return true; +} + +void msgpack_unpacker_destroy(msgpack_unpacker* mpac) +{ + msgpack_zone_free(mpac->z); + free(mpac->ctx); + decl_count(mpac->buffer); +} + + +msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size) +{ + msgpack_unpacker* mpac = (msgpack_unpacker*)malloc(sizeof(msgpack_unpacker)); + if(mpac == NULL) { + return NULL; + } + + if(!msgpack_unpacker_init(mpac, initial_buffer_size)) { + free(mpac); + return NULL; + } + + return mpac; +} + +void msgpack_unpacker_free(msgpack_unpacker* mpac) +{ + msgpack_unpacker_destroy(mpac); + free(mpac); +} + +bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size) +{ + if(mpac->used == mpac->off && get_count(mpac->buffer) == 1 + && !CTX_REFERENCED(mpac)) { + // rewind buffer + mpac->free += mpac->used - COUNTER_SIZE; + mpac->used = COUNTER_SIZE; + mpac->off = COUNTER_SIZE; + + if(mpac->free >= size) { + return true; + } + } + + if(mpac->off == COUNTER_SIZE) { + size_t next_size = (mpac->used + mpac->free) * 2; // include COUNTER_SIZE + while(next_size < size + mpac->used) { + next_size *= 2; + } + + char* tmp = (char*)realloc(mpac->buffer, next_size); + if(tmp == NULL) { + return false; + } + + mpac->buffer = tmp; + mpac->free = next_size - mpac->used; + + } else { + size_t next_size = mpac->initial_buffer_size; // include COUNTER_SIZE + size_t not_parsed = mpac->used - mpac->off; + while(next_size < size + not_parsed + COUNTER_SIZE) { + next_size *= 2; + } + + char* tmp = (char*)malloc(next_size); + if(tmp == NULL) { + return false; + } + + init_count(tmp); + + memcpy(tmp+COUNTER_SIZE, mpac->buffer+mpac->off, not_parsed); + + if(CTX_REFERENCED(mpac)) { + if(!msgpack_zone_push_finalizer(mpac->z, decl_count, mpac->buffer)) { + free(tmp); + return false; + } + CTX_REFERENCED(mpac) = false; + } else { + decl_count(mpac->buffer); + } + + mpac->buffer = tmp; + mpac->used = not_parsed + COUNTER_SIZE; + mpac->free = next_size - mpac->used; + mpac->off = COUNTER_SIZE; + } + + return true; +} + +int msgpack_unpacker_execute(msgpack_unpacker* mpac) +{ + size_t off = mpac->off; + int ret = template_execute(CTX_CAST(mpac->ctx), + mpac->buffer, mpac->used, &mpac->off); + if(mpac->off > off) { + mpac->parsed += mpac->off - off; + } + return ret; +} + +msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac) +{ + return template_data(CTX_CAST(mpac->ctx)); +} + +msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac) +{ + if(!msgpack_unpacker_flush_zone(mpac)) { + return NULL; + } + + msgpack_zone* r = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); + if(r == NULL) { + return NULL; + } + + msgpack_zone* old = mpac->z; + mpac->z = r; + CTX_CAST(mpac->ctx)->user.z = mpac->z; + + return old; +} + +void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac) +{ + msgpack_zone_clear(mpac->z); +} + +bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac) +{ + if(CTX_REFERENCED(mpac)) { + if(!msgpack_zone_push_finalizer(mpac->z, decl_count, mpac->buffer)) { + return false; + } + CTX_REFERENCED(mpac) = false; + + incr_count(mpac->buffer); + } + + return true; +} + +void msgpack_unpacker_reset(msgpack_unpacker* mpac) +{ + template_init(CTX_CAST(mpac->ctx)); + // don't reset referenced flag + mpac->parsed = 0; +} + +bool msgpack_unpacker_next(msgpack_unpacker* mpac, msgpack_unpacked* result) +{ + if(result->zone != NULL) { + msgpack_zone_free(result->zone); + } + + int ret = msgpack_unpacker_execute(mpac); + + if(ret <= 0) { + result->zone = NULL; + memset(&result->data, 0, sizeof(msgpack_object)); + return false; + } + + result->zone = msgpack_unpacker_release_zone(mpac); + result->data = msgpack_unpacker_data(mpac); + msgpack_unpacker_reset(mpac); + + return true; +} + + +msgpack_unpack_return +msgpack_unpack(const char* data, size_t len, size_t* off, + msgpack_zone* result_zone, msgpack_object* result) +{ + size_t noff = 0; + if(off != NULL) { noff = *off; } + + if(len <= noff) { + // FIXME + return MSGPACK_UNPACK_CONTINUE; + } + + template_context ctx; + template_init(&ctx); + + ctx.user.z = result_zone; + ctx.user.referenced = false; + + int e = template_execute(&ctx, data, len, &noff); + if(e < 0) { + return MSGPACK_UNPACK_PARSE_ERROR; + } + + if(off != NULL) { *off = noff; } + + if(e == 0) { + return MSGPACK_UNPACK_CONTINUE; + } + + *result = template_data(&ctx); + + if(noff < len) { + return MSGPACK_UNPACK_EXTRA_BYTES; + } + + return MSGPACK_UNPACK_SUCCESS; +} + +bool msgpack_unpack_next(msgpack_unpacked* result, + const char* data, size_t len, size_t* off) +{ + msgpack_unpacked_destroy(result); + + size_t noff = 0; + if(off != NULL) { noff = *off; } + + if(len <= noff) { + return false; + } + + msgpack_zone* z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); + + template_context ctx; + template_init(&ctx); + + ctx.user.z = z; + ctx.user.referenced = false; + + int e = template_execute(&ctx, data, len, &noff); + if(e <= 0) { + msgpack_zone_free(z); + return false; + } + + if(off != NULL) { *off = noff; } + + result->zone = z; + result->data = template_data(&ctx); + + return true; +} + +// FIXME: Dirty hack to avoid a bus error caused by OS X's old gcc. +static void dummy_function_to_avoid_bus_error() +{ +} diff --git a/msgpack/src/version.c b/msgpack/src/version.c new file mode 100644 index 00000000..3d956f18 --- /dev/null +++ b/msgpack/src/version.c @@ -0,0 +1,17 @@ +#include "msgpack.h" + +const char* msgpack_version(void) +{ + return MSGPACK_VERSION; +} + +int msgpack_version_major(void) +{ + return MSGPACK_VERSION_MAJOR; +} + +int msgpack_version_minor(void) +{ + return MSGPACK_VERSION_MINOR; +} + diff --git a/msgpack/src/vrefbuffer.c b/msgpack/src/vrefbuffer.c new file mode 100644 index 00000000..a27b138e --- /dev/null +++ b/msgpack/src/vrefbuffer.c @@ -0,0 +1,220 @@ +/* + * MessagePack for C zero-copy buffer implementation + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "msgpack/vrefbuffer.h" +#include +#include + +struct msgpack_vrefbuffer_chunk { + struct msgpack_vrefbuffer_chunk* next; + /* data ... */ +}; + +bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf, + size_t ref_size, size_t chunk_size) +{ + vbuf->chunk_size = chunk_size; + vbuf->ref_size = ref_size; + + size_t nfirst = (sizeof(struct iovec) < 72/2) ? + 72 / sizeof(struct iovec) : 8; + + struct iovec* array = (struct iovec*)malloc( + sizeof(struct iovec) * nfirst); + if(array == NULL) { + return false; + } + + vbuf->tail = array; + vbuf->end = array + nfirst; + vbuf->array = array; + + msgpack_vrefbuffer_chunk* chunk = (msgpack_vrefbuffer_chunk*)malloc( + sizeof(msgpack_vrefbuffer_chunk) + chunk_size); + if(chunk == NULL) { + free(array); + return false; + } + + msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; + + ib->free = chunk_size; + ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); + ib->head = chunk; + chunk->next = NULL; + + return true; +} + +void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf) +{ + msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head; + while(true) { + msgpack_vrefbuffer_chunk* n = c->next; + free(c); + if(n != NULL) { + c = n; + } else { + break; + } + } + free(vbuf->array); +} + +void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vbuf) +{ + msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head->next; + msgpack_vrefbuffer_chunk* n; + while(c != NULL) { + n = c->next; + free(c); + c = n; + } + + msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; + msgpack_vrefbuffer_chunk* chunk = ib->head; + chunk->next = NULL; + ib->free = vbuf->chunk_size; + ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); + + vbuf->tail = vbuf->array; +} + +int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf, + const char* buf, unsigned int len) +{ + if(vbuf->tail == vbuf->end) { + const size_t nused = vbuf->tail - vbuf->array; + const size_t nnext = nused * 2; + + struct iovec* nvec = (struct iovec*)realloc( + vbuf->array, sizeof(struct iovec)*nnext); + if(nvec == NULL) { + return -1; + } + + vbuf->array = nvec; + vbuf->end = nvec + nnext; + vbuf->tail = nvec + nused; + } + + vbuf->tail->iov_base = (char*)buf; + vbuf->tail->iov_len = len; + ++vbuf->tail; + + return 0; +} + +int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf, + const char* buf, unsigned int len) +{ + msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; + + if(ib->free < len) { + size_t sz = vbuf->chunk_size; + if(sz < len) { + sz = len; + } + + msgpack_vrefbuffer_chunk* chunk = (msgpack_vrefbuffer_chunk*)malloc( + sizeof(msgpack_vrefbuffer_chunk) + sz); + if(chunk == NULL) { + return -1; + } + + chunk->next = ib->head; + ib->head = chunk; + ib->free = sz; + ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); + } + + char* m = ib->ptr; + memcpy(m, buf, len); + ib->free -= len; + ib->ptr += len; + + if(vbuf->tail != vbuf->array && m == + (const char*)((vbuf->tail-1)->iov_base) + (vbuf->tail-1)->iov_len) { + (vbuf->tail-1)->iov_len += len; + return 0; + } else { + return msgpack_vrefbuffer_append_ref(vbuf, m, len); + } +} + +int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to) +{ + size_t sz = vbuf->chunk_size; + + msgpack_vrefbuffer_chunk* empty = (msgpack_vrefbuffer_chunk*)malloc( + sizeof(msgpack_vrefbuffer_chunk) + sz); + if(empty == NULL) { + return -1; + } + + empty->next = NULL; + + + const size_t nused = vbuf->tail - vbuf->array; + if(to->tail + nused < vbuf->end) { + const size_t tosize = to->tail - to->array; + const size_t reqsize = nused + tosize; + size_t nnext = (to->end - to->array) * 2; + while(nnext < reqsize) { + nnext *= 2; + } + + struct iovec* nvec = (struct iovec*)realloc( + to->array, sizeof(struct iovec)*nnext); + if(nvec == NULL) { + free(empty); + return -1; + } + + to->array = nvec; + to->end = nvec + nnext; + to->tail = nvec + tosize; + } + + memcpy(to->tail, vbuf->array, sizeof(struct iovec)*nused); + + to->tail += nused; + vbuf->tail = vbuf->array; + + + msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; + msgpack_vrefbuffer_inner_buffer* const toib = &to->inner_buffer; + + msgpack_vrefbuffer_chunk* last = ib->head; + while(last->next != NULL) { + last = last->next; + } + last->next = toib->head; + toib->head = ib->head; + + if(toib->free < ib->free) { + toib->free = ib->free; + toib->ptr = ib->ptr; + } + + ib->head = empty; + ib->free = sz; + ib->ptr = ((char*)empty) + sizeof(msgpack_vrefbuffer_chunk); + + return 0; +} + diff --git a/msgpack/src/zone.c b/msgpack/src/zone.c new file mode 100644 index 00000000..8cc8b0de --- /dev/null +++ b/msgpack/src/zone.c @@ -0,0 +1,221 @@ +/* + * MessagePack for C memory pool implementation + * + * Copyright (C) 2008-2009 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "msgpack/zone.h" +#include +#include + +struct msgpack_zone_chunk { + struct msgpack_zone_chunk* next; + /* data ... */ +}; + +static inline bool init_chunk_list(msgpack_zone_chunk_list* cl, size_t chunk_size) +{ + msgpack_zone_chunk* chunk = (msgpack_zone_chunk*)malloc( + sizeof(msgpack_zone_chunk) + chunk_size); + if(chunk == NULL) { + return false; + } + + cl->head = chunk; + cl->free = chunk_size; + cl->ptr = ((char*)chunk) + sizeof(msgpack_zone_chunk); + chunk->next = NULL; + + return true; +} + +static inline void destroy_chunk_list(msgpack_zone_chunk_list* cl) +{ + msgpack_zone_chunk* c = cl->head; + while(true) { + msgpack_zone_chunk* n = c->next; + free(c); + if(n != NULL) { + c = n; + } else { + break; + } + } +} + +static inline void clear_chunk_list(msgpack_zone_chunk_list* cl, size_t chunk_size) +{ + msgpack_zone_chunk* c = cl->head; + while(true) { + msgpack_zone_chunk* n = c->next; + if(n != NULL) { + free(c); + c = n; + } else { + break; + } + } + cl->head->next = NULL; + cl->free = chunk_size; + cl->ptr = ((char*)cl->head) + sizeof(msgpack_zone_chunk); +} + +void* msgpack_zone_malloc_expand(msgpack_zone* zone, size_t size) +{ + msgpack_zone_chunk_list* const cl = &zone->chunk_list; + + size_t sz = zone->chunk_size; + + while(sz < size) { + sz *= 2; + } + + msgpack_zone_chunk* chunk = (msgpack_zone_chunk*)malloc( + sizeof(msgpack_zone_chunk) + sz); + + char* ptr = ((char*)chunk) + sizeof(msgpack_zone_chunk); + + chunk->next = cl->head; + cl->head = chunk; + cl->free = sz - size; + cl->ptr = ptr + size; + + return ptr; +} + + +static inline void init_finalizer_array(msgpack_zone_finalizer_array* fa) +{ + fa->tail = NULL; + fa->end = NULL; + fa->array = NULL; +} + +static inline void call_finalizer_array(msgpack_zone_finalizer_array* fa) +{ + msgpack_zone_finalizer* fin = fa->tail; + for(; fin != fa->array; --fin) { + (*(fin-1)->func)((fin-1)->data); + } +} + +static inline void destroy_finalizer_array(msgpack_zone_finalizer_array* fa) +{ + call_finalizer_array(fa); + free(fa->array); +} + +static inline void clear_finalizer_array(msgpack_zone_finalizer_array* fa) +{ + call_finalizer_array(fa); + fa->tail = fa->array; +} + +bool msgpack_zone_push_finalizer_expand(msgpack_zone* zone, + void (*func)(void* data), void* data) +{ + msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; + + const size_t nused = fa->end - fa->array; + + size_t nnext; + if(nused == 0) { + nnext = (sizeof(msgpack_zone_finalizer) < 72/2) ? + 72 / sizeof(msgpack_zone_finalizer) : 8; + + } else { + nnext = nused * 2; + } + + msgpack_zone_finalizer* tmp = + (msgpack_zone_finalizer*)realloc(fa->array, + sizeof(msgpack_zone_finalizer) * nnext); + if(tmp == NULL) { + return false; + } + + fa->array = tmp; + fa->end = tmp + nnext; + fa->tail = tmp + nused; + + fa->tail->func = func; + fa->tail->data = data; + + ++fa->tail; + + return true; +} + + +bool msgpack_zone_is_empty(msgpack_zone* zone) +{ + msgpack_zone_chunk_list* const cl = &zone->chunk_list; + msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; + return cl->free == zone->chunk_size && cl->head->next == NULL && + fa->tail == fa->array; +} + + +void msgpack_zone_destroy(msgpack_zone* zone) +{ + destroy_finalizer_array(&zone->finalizer_array); + destroy_chunk_list(&zone->chunk_list); +} + +void msgpack_zone_clear(msgpack_zone* zone) +{ + clear_finalizer_array(&zone->finalizer_array); + clear_chunk_list(&zone->chunk_list, zone->chunk_size); +} + +bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size) +{ + zone->chunk_size = chunk_size; + + if(!init_chunk_list(&zone->chunk_list, chunk_size)) { + return false; + } + + init_finalizer_array(&zone->finalizer_array); + + return true; +} + +msgpack_zone* msgpack_zone_new(size_t chunk_size) +{ + msgpack_zone* zone = (msgpack_zone*)malloc( + sizeof(msgpack_zone) + chunk_size); + if(zone == NULL) { + return NULL; + } + + zone->chunk_size = chunk_size; + + if(!init_chunk_list(&zone->chunk_list, chunk_size)) { + free(zone); + return NULL; + } + + init_finalizer_array(&zone->finalizer_array); + + return zone; +} + +void msgpack_zone_free(msgpack_zone* zone) +{ + if(zone == NULL) { return; } + msgpack_zone_destroy(zone); + free(zone); +} + diff --git a/msgpack/sysdep.h b/msgpack/sysdep.h new file mode 100644 index 00000000..4fedbd8b --- /dev/null +++ b/msgpack/sysdep.h @@ -0,0 +1,195 @@ +/* + * MessagePack system dependencies + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_SYSDEP_H__ +#define MSGPACK_SYSDEP_H__ + +#include +#include +#if defined(_MSC_VER) && _MSC_VER < 1600 +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#elif defined(_MSC_VER) // && _MSC_VER >= 1600 +#include +#else +#include +#include +#endif + +#ifdef _WIN32 +#define _msgpack_atomic_counter_header +typedef long _msgpack_atomic_counter_t; +#define _msgpack_sync_decr_and_fetch(ptr) InterlockedDecrement(ptr) +#define _msgpack_sync_incr_and_fetch(ptr) InterlockedIncrement(ptr) +#elif defined(__GNUC__) && ((__GNUC__*10 + __GNUC_MINOR__) < 41) +#define _msgpack_atomic_counter_header "gcc_atomic.h" +#else +typedef unsigned int _msgpack_atomic_counter_t; +#define _msgpack_sync_decr_and_fetch(ptr) __sync_sub_and_fetch(ptr, 1) +#define _msgpack_sync_incr_and_fetch(ptr) __sync_add_and_fetch(ptr, 1) +#endif + +#ifdef _WIN32 + +#ifdef __cplusplus +/* numeric_limits::min,max */ +#ifdef max +#undef max +#endif +#ifdef min +#undef min +#endif +#endif + +#else +#include /* __BYTE_ORDER */ +#endif + +#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define __LITTLE_ENDIAN__ +#elif __BYTE_ORDER == __BIG_ENDIAN +#define __BIG_ENDIAN__ +#elif _WIN32 +#define __LITTLE_ENDIAN__ +#endif +#endif + + +#ifdef __LITTLE_ENDIAN__ + +#ifdef _WIN32 +# if defined(ntohs) +# define _msgpack_be16(x) ntohs(x) +# elif defined(_byteswap_ushort) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define _msgpack_be16(x) ((uint16_t)_byteswap_ushort((unsigned short)x)) +# else +# define _msgpack_be16(x) ( \ + ((((uint16_t)x) << 8) ) | \ + ((((uint16_t)x) >> 8) ) ) +# endif +#else +# define _msgpack_be16(x) ntohs(x) +#endif + +#ifdef _WIN32 +# if defined(ntohl) +# define _msgpack_be32(x) ntohl(x) +# elif defined(_byteswap_ulong) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define _msgpack_be32(x) ((uint32_t)_byteswap_ulong((unsigned long)x)) +# else +# define _msgpack_be32(x) \ + ( ((((uint32_t)x) << 24) ) | \ + ((((uint32_t)x) << 8) & 0x00ff0000U ) | \ + ((((uint32_t)x) >> 8) & 0x0000ff00U ) | \ + ((((uint32_t)x) >> 24) ) ) +# endif +#else +# define _msgpack_be32(x) ntohl(x) +#endif + +#if defined(_byteswap_uint64) || (defined(_MSC_VER) && _MSC_VER >= 1400) +# define _msgpack_be64(x) (_byteswap_uint64(x)) +#elif defined(bswap_64) +# define _msgpack_be64(x) bswap_64(x) +#elif defined(__DARWIN_OSSwapInt64) +# define _msgpack_be64(x) __DARWIN_OSSwapInt64(x) +#else +#define _msgpack_be64(x) \ + ( ((((uint64_t)x) << 56) ) | \ + ((((uint64_t)x) << 40) & 0x00ff000000000000ULL ) | \ + ((((uint64_t)x) << 24) & 0x0000ff0000000000ULL ) | \ + ((((uint64_t)x) << 8) & 0x000000ff00000000ULL ) | \ + ((((uint64_t)x) >> 8) & 0x00000000ff000000ULL ) | \ + ((((uint64_t)x) >> 24) & 0x0000000000ff0000ULL ) | \ + ((((uint64_t)x) >> 40) & 0x000000000000ff00ULL ) | \ + ((((uint64_t)x) >> 56) ) ) +#endif + +#define _msgpack_load16(cast, from) ((cast)( \ + (((uint16_t)((uint8_t*)(from))[0]) << 8) | \ + (((uint16_t)((uint8_t*)(from))[1]) ) )) + +#define _msgpack_load32(cast, from) ((cast)( \ + (((uint32_t)((uint8_t*)(from))[0]) << 24) | \ + (((uint32_t)((uint8_t*)(from))[1]) << 16) | \ + (((uint32_t)((uint8_t*)(from))[2]) << 8) | \ + (((uint32_t)((uint8_t*)(from))[3]) ) )) + +#define _msgpack_load64(cast, from) ((cast)( \ + (((uint64_t)((uint8_t*)(from))[0]) << 56) | \ + (((uint64_t)((uint8_t*)(from))[1]) << 48) | \ + (((uint64_t)((uint8_t*)(from))[2]) << 40) | \ + (((uint64_t)((uint8_t*)(from))[3]) << 32) | \ + (((uint64_t)((uint8_t*)(from))[4]) << 24) | \ + (((uint64_t)((uint8_t*)(from))[5]) << 16) | \ + (((uint64_t)((uint8_t*)(from))[6]) << 8) | \ + (((uint64_t)((uint8_t*)(from))[7]) ) )) + +#else + +#define _msgpack_be16(x) (x) +#define _msgpack_be32(x) (x) +#define _msgpack_be64(x) (x) + +#define _msgpack_load16(cast, from) ((cast)( \ + (((uint16_t)((uint8_t*)from)[0]) << 8) | \ + (((uint16_t)((uint8_t*)from)[1]) ) )) + +#define _msgpack_load32(cast, from) ((cast)( \ + (((uint32_t)((uint8_t*)from)[0]) << 24) | \ + (((uint32_t)((uint8_t*)from)[1]) << 16) | \ + (((uint32_t)((uint8_t*)from)[2]) << 8) | \ + (((uint32_t)((uint8_t*)from)[3]) ) )) + +#define _msgpack_load64(cast, from) ((cast)( \ + (((uint64_t)((uint8_t*)from)[0]) << 56) | \ + (((uint64_t)((uint8_t*)from)[1]) << 48) | \ + (((uint64_t)((uint8_t*)from)[2]) << 40) | \ + (((uint64_t)((uint8_t*)from)[3]) << 32) | \ + (((uint64_t)((uint8_t*)from)[4]) << 24) | \ + (((uint64_t)((uint8_t*)from)[5]) << 16) | \ + (((uint64_t)((uint8_t*)from)[6]) << 8) | \ + (((uint64_t)((uint8_t*)from)[7]) ) )) +#endif + + +#define _msgpack_store16(to, num) \ + do { uint16_t val = _msgpack_be16(num); memcpy(to, &val, 2); } while(0) +#define _msgpack_store32(to, num) \ + do { uint32_t val = _msgpack_be32(num); memcpy(to, &val, 4); } while(0) +#define _msgpack_store64(to, num) \ + do { uint64_t val = _msgpack_be64(num); memcpy(to, &val, 8); } while(0) + +/* +#define _msgpack_load16(cast, from) \ + ({ cast val; memcpy(&val, (char*)from, 2); _msgpack_be16(val); }) +#define _msgpack_load32(cast, from) \ + ({ cast val; memcpy(&val, (char*)from, 4); _msgpack_be32(val); }) +#define _msgpack_load64(cast, from) \ + ({ cast val; memcpy(&val, (char*)from, 8); _msgpack_be64(val); }) +*/ + + +#endif /* msgpack/sysdep.h */ + diff --git a/msgpack/test/Makefile.am b/msgpack/test/Makefile.am new file mode 100644 index 00000000..af56d3be --- /dev/null +++ b/msgpack/test/Makefile.am @@ -0,0 +1,54 @@ + +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src +AM_C_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src +AM_LDFLAGS = $(top_builddir)/src/libmsgpack.la -lgtest_main -pthread + +check_PROGRAMS = \ + zone \ + pack_unpack \ + pack_unpack_c \ + streaming \ + streaming_c \ + object \ + convert \ + buffer \ + cases \ + fixint \ + fixint_c \ + version \ + msgpackc_test \ + msgpack_test + +TESTS = $(check_PROGRAMS) + +zone_SOURCES = zone.cc + +pack_unpack_SOURCES = pack_unpack.cc + +pack_unpack_c_SOURCES = pack_unpack_c.cc + +streaming_SOURCES = streaming.cc + +streaming_c_SOURCES = streaming_c.cc + +object_SOURCES = object.cc + +convert_SOURCES = convert.cc + +buffer_SOURCES = buffer.cc +buffer_LDADD = -lz + +cases_SOURCES = cases.cc + +fixint_SOURCES = fixint.cc + +fixint_c_SOURCES = fixint_c.cc + +version_SOURCES = version.cc + +msgpackc_test_SOURCES = msgpackc_test.cpp + +msgpack_test_SOURCES = msgpack_test.cpp + +EXTRA_DIST = cases.mpac cases_compact.mpac + diff --git a/msgpack/test/buffer.cc b/msgpack/test/buffer.cc new file mode 100644 index 00000000..0b66f4e2 --- /dev/null +++ b/msgpack/test/buffer.cc @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +TEST(buffer, sbuffer) +{ + msgpack::sbuffer sbuf; + sbuf.write("a", 1); + sbuf.write("a", 1); + sbuf.write("a", 1); + + EXPECT_EQ(3, sbuf.size()); + EXPECT_TRUE( memcmp(sbuf.data(), "aaa", 3) == 0 ); + + sbuf.clear(); + sbuf.write("a", 1); + sbuf.write("a", 1); + sbuf.write("a", 1); + + EXPECT_EQ(3, sbuf.size()); + EXPECT_TRUE( memcmp(sbuf.data(), "aaa", 3) == 0 ); +} + + +TEST(buffer, vrefbuffer) +{ + msgpack::vrefbuffer vbuf; + vbuf.write("a", 1); + vbuf.write("a", 1); + vbuf.write("a", 1); + + const struct iovec* vec = vbuf.vector(); + size_t veclen = vbuf.vector_size(); + + msgpack::sbuffer sbuf; + for(size_t i=0; i < veclen; ++i) { + sbuf.write((const char*)vec[i].iov_base, vec[i].iov_len); + } + + EXPECT_EQ(3, sbuf.size()); + EXPECT_TRUE( memcmp(sbuf.data(), "aaa", 3) == 0 ); + + + vbuf.clear(); + vbuf.write("a", 1); + vbuf.write("a", 1); + vbuf.write("a", 1); + + vec = vbuf.vector(); + veclen = vbuf.vector_size(); + + sbuf.clear(); + for(size_t i=0; i < veclen; ++i) { + sbuf.write((const char*)vec[i].iov_base, vec[i].iov_len); + } + + EXPECT_EQ(3, sbuf.size()); + EXPECT_TRUE( memcmp(sbuf.data(), "aaa", 3) == 0 ); +} + + +TEST(buffer, zbuffer) +{ + msgpack::zbuffer zbuf; + zbuf.write("a", 1); + zbuf.write("a", 1); + zbuf.write("a", 1); + + zbuf.flush(); +} + diff --git a/msgpack/test/cases.cc b/msgpack/test/cases.cc new file mode 100644 index 00000000..eb1286c7 --- /dev/null +++ b/msgpack/test/cases.cc @@ -0,0 +1,38 @@ +#include +#include +#include + +static void feed_file(msgpack::unpacker& pac, const char* path) +{ + std::ifstream fin(path); + while(true) { + pac.reserve_buffer(32*1024); + fin.read(pac.buffer(), pac.buffer_capacity()); + if(fin.bad()) { + throw std::runtime_error("read failed"); + } + pac.buffer_consumed(fin.gcount()); + if(fin.fail()) { + break; + } + } +} + +TEST(cases, format) +{ + msgpack::unpacker pac; + msgpack::unpacker pac_compact; + + feed_file(pac, "cases.mpac"); + feed_file(pac_compact, "cases_compact.mpac"); + + msgpack::unpacked result; + while(pac.next(&result)) { + msgpack::unpacked result_compact; + EXPECT_TRUE( pac_compact.next(&result_compact) ); + EXPECT_EQ(result_compact.get(), result.get()); + } + + EXPECT_FALSE( pac_compact.next(&result) ); +} + diff --git a/msgpack/test/convert.cc b/msgpack/test/convert.cc new file mode 100644 index 00000000..f579f33a --- /dev/null +++ b/msgpack/test/convert.cc @@ -0,0 +1,76 @@ +#include +#include + +class compatibility { +public: + compatibility() : str1("default"), str2("default") { } + + std::string str1; + std::string str2; + + MSGPACK_DEFINE(str1, str2); +}; + +TEST(convert, compatibility_less) +{ + std::vector src(1); + src[0] = "kumofs"; + + msgpack::zone z; + msgpack::object obj(src, &z); + + compatibility c; + EXPECT_NO_THROW( obj.convert(&c) ); + + EXPECT_EQ("kumofs", c.str1); + EXPECT_EQ("default", c.str2); +} + +TEST(convert, compatibility_more) +{ + std::vector src(3); + src[0] = "kumofs"; + src[1] = "mpio"; + src[2] = "cloudy"; + + msgpack::zone z; + msgpack::object obj(src, &z); + + compatibility to; + EXPECT_NO_THROW( obj.convert(&to) ); + + EXPECT_EQ("kumofs", to.str1); + EXPECT_EQ("mpio", to.str2); +} + + +class enum_member { +public: + enum_member() : flag(A) { } + + enum flags_t { + A = 0, + B = 1, + }; + + flags_t flag; + + MSGPACK_DEFINE(flag); +}; + +MSGPACK_ADD_ENUM(enum_member::flags_t); + +TEST(convert, enum_member) +{ + enum_member src; + src.flag = enum_member::B; + + msgpack::zone z; + msgpack::object obj(src, &z); + + enum_member to; + EXPECT_NO_THROW( obj.convert(&to) ); + + EXPECT_EQ(enum_member::B, to.flag); +} + diff --git a/msgpack/test/fixint.cc b/msgpack/test/fixint.cc new file mode 100644 index 00000000..63288a1b --- /dev/null +++ b/msgpack/test/fixint.cc @@ -0,0 +1,55 @@ +#include +#include + +template +void check_size(size_t size) { + T v(0); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, v); + EXPECT_EQ(size, sbuf.size()); +} + +TEST(fixint, size) +{ + check_size(2); + check_size(3); + check_size(5); + check_size(9); + + check_size(2); + check_size(3); + check_size(5); + check_size(9); +} + + +template +void check_convert() { + T v1(-11); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, v1); + + msgpack::unpacked msg; + msgpack::unpack(&msg, sbuf.data(), sbuf.size()); + + T v2; + msg.get().convert(&v2); + + EXPECT_EQ(v1.get(), v2.get()); + + EXPECT_EQ(msg.get(), msgpack::object(T(v1.get()))); +} + +TEST(fixint, convert) +{ + check_convert(); + check_convert(); + check_convert(); + check_convert(); + + check_convert(); + check_convert(); + check_convert(); + check_convert(); +} + diff --git a/msgpack/test/fixint_c.cc b/msgpack/test/fixint_c.cc new file mode 100644 index 00000000..caa4d262 --- /dev/null +++ b/msgpack/test/fixint_c.cc @@ -0,0 +1,32 @@ +#include +#include + +TEST(fixint, size) +{ + msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); + msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); + + size_t sum = 0; + + EXPECT_EQ(0, msgpack_pack_fix_int8(pk, 0)); + EXPECT_EQ(sum+=2, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_int16(pk, 0)); + EXPECT_EQ(sum+=3, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_int32(pk, 0)); + EXPECT_EQ(sum+=5, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_int64(pk, 0)); + EXPECT_EQ(sum+=9, sbuf->size); + + EXPECT_EQ(0, msgpack_pack_fix_uint8(pk, 0)); + EXPECT_EQ(sum+=2, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_uint16(pk, 0)); + EXPECT_EQ(sum+=3, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_uint32(pk, 0)); + EXPECT_EQ(sum+=5, sbuf->size); + EXPECT_EQ(0, msgpack_pack_fix_uint64(pk, 0)); + EXPECT_EQ(sum+=9, sbuf->size); + + msgpack_sbuffer_free(sbuf); + msgpack_packer_free(pk); +} + diff --git a/msgpack/test/msgpack_test.cpp b/msgpack/test/msgpack_test.cpp new file mode 100644 index 00000000..0dd0ffc6 --- /dev/null +++ b/msgpack/test/msgpack_test.cpp @@ -0,0 +1,982 @@ +#include "msgpack.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +using namespace std; + +const unsigned int kLoop = 10000; +const unsigned int kElements = 100; +const double kEPS = 1e-10; + +#define GEN_TEST(test_type) \ + do { \ + vector v; \ + v.push_back(0); \ + v.push_back(1); \ + v.push_back(2); \ + v.push_back(numeric_limits::min()); \ + v.push_back(numeric_limits::max()); \ + for (unsigned int i = 0; i < kLoop; i++) \ + v.push_back(rand()); \ + for (unsigned int i = 0; i < v.size() ; i++) { \ + msgpack::sbuffer sbuf; \ + test_type val1 = v[i]; \ + msgpack::pack(sbuf, val1); \ + msgpack::zone z; \ + msgpack::object obj; \ + msgpack::unpack_return ret = \ + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); \ + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); \ + test_type val2; \ + obj.convert(&val2); \ + EXPECT_EQ(val1, val2); \ + } \ +} while(0) + +TEST(MSGPACK, simple_buffer_short) +{ + GEN_TEST(short); +} + +TEST(MSGPACK, simple_buffer_int) +{ + GEN_TEST(int); +} + +TEST(MSGPACK, simple_buffer_long) +{ + GEN_TEST(long); +} + +TEST(MSGPACK, simple_buffer_long_long) +{ + GEN_TEST(long long); +} + +TEST(MSGPACK, simple_buffer_unsigned_short) +{ + GEN_TEST(unsigned short); +} + +TEST(MSGPACK, simple_buffer_unsigned_int) +{ + GEN_TEST(unsigned int); +} + +TEST(MSGPACK, simple_buffer_unsigned_long) +{ + GEN_TEST(unsigned long); +} + +TEST(MSGPACK, simple_buffer_unsigned_long_long) +{ + GEN_TEST(unsigned long long); +} + +TEST(MSGPACK, simple_buffer_uint8) +{ + GEN_TEST(uint8_t); +} + +TEST(MSGPACK, simple_buffer_uint16) +{ + GEN_TEST(uint16_t); +} + +TEST(MSGPACK, simple_buffer_uint32) +{ + GEN_TEST(uint32_t); +} + +TEST(MSGPACK, simple_buffer_uint64) +{ + GEN_TEST(uint64_t); +} + +TEST(MSGPACK, simple_buffer_int8) +{ + GEN_TEST(int8_t); +} + +TEST(MSGPACK, simple_buffer_int16) +{ + GEN_TEST(int16_t); +} + +TEST(MSGPACK, simple_buffer_int32) +{ + GEN_TEST(int32_t); +} + +TEST(MSGPACK, simple_buffer_int64) +{ + GEN_TEST(int64_t); +} + +TEST(MSGPACK, simple_buffer_float) +{ + vector v; + v.push_back(0.0); + v.push_back(-0.0); + v.push_back(1.0); + v.push_back(-1.0); + v.push_back(numeric_limits::min()); + v.push_back(numeric_limits::max()); + v.push_back(nanf("tag")); + v.push_back(1.0/0.0); // inf + v.push_back(-(1.0/0.0)); // -inf + for (unsigned int i = 0; i < kLoop; i++) { + v.push_back(drand48()); + v.push_back(-drand48()); + } + for (unsigned int i = 0; i < v.size() ; i++) { + msgpack::sbuffer sbuf; + float val1 = v[i]; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + float val2; + obj.convert(&val2); + + if (isnan(val1)) + EXPECT_TRUE(isnan(val2)); + else if (isinf(val1)) + EXPECT_TRUE(isinf(val2)); + else + EXPECT_TRUE(fabs(val2 - val1) <= kEPS); + } +} + +TEST(MSGPACK, simple_buffer_double) +{ + vector v; + v.push_back(0.0); + v.push_back(-0.0); + v.push_back(1.0); + v.push_back(-1.0); + v.push_back(numeric_limits::min()); + v.push_back(numeric_limits::max()); + v.push_back(nanf("tag")); + v.push_back(1.0/0.0); // inf + v.push_back(-(1.0/0.0)); // -inf + for (unsigned int i = 0; i < kLoop; i++) { + v.push_back(drand48()); + v.push_back(-drand48()); + } + for (unsigned int i = 0; i < v.size() ; i++) { + msgpack::sbuffer sbuf; + double val1 = v[i]; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + double val2; + obj.convert(&val2); + + if (isnan(val1)) + EXPECT_TRUE(isnan(val2)); + else if (isinf(val1)) + EXPECT_TRUE(isinf(val2)); + else + EXPECT_TRUE(fabs(val2 - val1) <= kEPS); + } +} + +TEST(MSGPACK, simple_buffer_true) +{ + msgpack::sbuffer sbuf; + bool val1 = true; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + bool val2; + obj.convert(&val2); + EXPECT_EQ(val1, val2); +} + +TEST(MSGPACK, simple_buffer_false) +{ + msgpack::sbuffer sbuf; + bool val1 = false; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + bool val2; + obj.convert(&val2); + EXPECT_EQ(val1, val2); +} + +//----------------------------------------------------------------------------- + +// STL + +TEST(MSGPACK_STL, simple_buffer_string) +{ + for (unsigned int k = 0; k < kLoop; k++) { + string val1; + for (unsigned int i = 0; i < kElements; i++) + val1 += 'a' + rand() % 26; + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + string val2; + obj.convert(&val2); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_EQ(val1, val2); + } +} + +TEST(MSGPACK_STL, simple_buffer_vector) +{ + for (unsigned int k = 0; k < kLoop; k++) { + vector val1; + for (unsigned int i = 0; i < kElements; i++) + val1.push_back(rand()); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + vector val2; + obj.convert(&val2); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); + } +} + +TEST(MSGPACK_STL, simple_buffer_map) +{ + for (unsigned int k = 0; k < kLoop; k++) { + map val1; + for (unsigned int i = 0; i < kElements; i++) + val1[rand()] = rand(); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + map val2; + obj.convert(&val2); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); + } +} + +TEST(MSGPACK_STL, simple_buffer_deque) +{ + for (unsigned int k = 0; k < kLoop; k++) { + deque val1; + for (unsigned int i = 0; i < kElements; i++) + val1.push_back(rand()); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + deque val2; + obj.convert(&val2); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); + } +} + +TEST(MSGPACK_STL, simple_buffer_list) +{ + for (unsigned int k = 0; k < kLoop; k++) { + list val1; + for (unsigned int i = 0; i < kElements; i++) + val1.push_back(rand()); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + list val2; + obj.convert(&val2); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); + } +} + +TEST(MSGPACK_STL, simple_buffer_set) +{ + for (unsigned int k = 0; k < kLoop; k++) { + set val1; + for (unsigned int i = 0; i < kElements; i++) + val1.insert(rand()); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + set val2; + obj.convert(&val2); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); + } +} + +TEST(MSGPACK_STL, simple_buffer_pair) +{ + for (unsigned int k = 0; k < kLoop; k++) { + pair val1 = make_pair(rand(), rand()); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + pair val2; + obj.convert(&val2); + EXPECT_EQ(val1.first, val2.first); + EXPECT_EQ(val1.second, val2.second); + } +} + +TEST(MSGPACK_STL, simple_buffer_multimap) +{ + for (unsigned int k = 0; k < kLoop; k++) { + multimap val1; + for (unsigned int i = 0; i < kElements; i++) { + int i1 = rand(); + val1.insert(make_pair(i1, rand())); + val1.insert(make_pair(i1, rand())); + } + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + multimap val2; + obj.convert(&val2); + + vector > v1, v2; + multimap::const_iterator it; + for (it = val1.begin(); it != val1.end(); ++it) + v1.push_back(make_pair(it->first, it->second)); + for (it = val2.begin(); it != val2.end(); ++it) + v2.push_back(make_pair(it->first, it->second)); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_EQ(v1.size(), v2.size()); + sort(v1.begin(), v1.end()); + sort(v2.begin(), v2.end()); + EXPECT_TRUE(v1 == v2); + } +} + +TEST(MSGPACK_STL, simple_buffer_multiset) +{ + for (unsigned int k = 0; k < kLoop; k++) { + multiset val1; + for (unsigned int i = 0; i < kElements; i++) + val1.insert(rand()); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + multiset val2; + obj.convert(&val2); + + vector v1, v2; + multiset::const_iterator it; + for (it = val1.begin(); it != val1.end(); ++it) + v1.push_back(*it); + for (it = val2.begin(); it != val2.end(); ++it) + v2.push_back(*it); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_EQ(v1.size(), v2.size()); + sort(v1.begin(), v1.end()); + sort(v2.begin(), v2.end()); + EXPECT_TRUE(v1 == v2); + } +} + +// TR1 + +#ifdef HAVE_TR1_UNORDERED_MAP +#include +#include "msgpack/type/tr1/unordered_map.hpp" +TEST(MSGPACK_TR1, simple_buffer_unordered_map) +{ + for (unsigned int k = 0; k < kLoop; k++) { + tr1::unordered_map val1; + for (unsigned int i = 0; i < kElements; i++) + val1[rand()] = rand(); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + tr1::unordered_map val2; + obj.convert(&val2); + EXPECT_EQ(val1.size(), val2.size()); + tr1::unordered_map::const_iterator it; + for (it = val1.begin(); it != val1.end(); ++it) { + EXPECT_TRUE(val2.find(it->first) != val2.end()); + EXPECT_EQ(it->second, val2.find(it->first)->second); + } + } +} + +TEST(MSGPACK_TR1, simple_buffer_unordered_multimap) +{ + for (unsigned int k = 0; k < kLoop; k++) { + tr1::unordered_multimap val1; + for (unsigned int i = 0; i < kElements; i++) { + int i1 = rand(); + val1.insert(make_pair(i1, rand())); + val1.insert(make_pair(i1, rand())); + } + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + tr1::unordered_multimap val2; + obj.convert(&val2); + + vector > v1, v2; + tr1::unordered_multimap::const_iterator it; + for (it = val1.begin(); it != val1.end(); ++it) + v1.push_back(make_pair(it->first, it->second)); + for (it = val2.begin(); it != val2.end(); ++it) + v2.push_back(make_pair(it->first, it->second)); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_EQ(v1.size(), v2.size()); + sort(v1.begin(), v1.end()); + sort(v2.begin(), v2.end()); + EXPECT_TRUE(v1 == v2); + } +} +#endif + +#ifdef HAVE_TR1_UNORDERED_SET +#include +#include "msgpack/type/tr1/unordered_set.hpp" +TEST(MSGPACK_TR1, simple_buffer_unordered_set) +{ + for (unsigned int k = 0; k < kLoop; k++) { + tr1::unordered_set val1; + for (unsigned int i = 0; i < kElements; i++) + val1.insert(rand()); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + tr1::unordered_set val2; + obj.convert(&val2); + EXPECT_EQ(val1.size(), val2.size()); + tr1::unordered_set::const_iterator it; + for (it = val1.begin(); it != val1.end(); ++it) + EXPECT_TRUE(val2.find(*it) != val2.end()); + } +} + +TEST(MSGPACK_TR1, simple_buffer_unordered_multiset) +{ + for (unsigned int k = 0; k < kLoop; k++) { + tr1::unordered_multiset val1; + for (unsigned int i = 0; i < kElements; i++) + val1.insert(rand()); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + tr1::unordered_multiset val2; + obj.convert(&val2); + + vector v1, v2; + tr1::unordered_multiset::const_iterator it; + for (it = val1.begin(); it != val1.end(); ++it) + v1.push_back(*it); + for (it = val2.begin(); it != val2.end(); ++it) + v2.push_back(*it); + EXPECT_EQ(val1.size(), val2.size()); + EXPECT_EQ(v1.size(), v2.size()); + sort(v1.begin(), v1.end()); + sort(v2.begin(), v2.end()); + EXPECT_TRUE(v1 == v2); + } +} +#endif + +// User-Defined Structures + +class TestClass +{ +public: + TestClass() : i(0), s("kzk") {} + int i; + string s; + MSGPACK_DEFINE(i, s); +}; + +TEST(MSGPACK_USER_DEFINED, simple_buffer_class) +{ + for (unsigned int k = 0; k < kLoop; k++) { + TestClass val1; + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + TestClass val2; + val2.i = -1; + val2.s = ""; + obj.convert(&val2); + EXPECT_EQ(val1.i, val2.i); + EXPECT_EQ(val1.s, val2.s); + } +} + +class TestClass2 +{ +public: + TestClass2() : i(0), s("kzk") { + for (unsigned int i = 0; i < kElements; i++) + v.push_back(rand()); + } + int i; + string s; + vector v; + MSGPACK_DEFINE(i, s, v); +}; + +TEST(MSGPACK_USER_DEFINED, simple_buffer_class_old_to_new) +{ + for (unsigned int k = 0; k < kLoop; k++) { + TestClass val1; + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + TestClass2 val2; + val2.i = -1; + val2.s = ""; + val2.v = vector(); + obj.convert(&val2); + EXPECT_EQ(val1.i, val2.i); + EXPECT_EQ(val1.s, val2.s); + EXPECT_FALSE(val2.s.empty()); + } +} + +TEST(MSGPACK_USER_DEFINED, simple_buffer_class_new_to_old) +{ + for (unsigned int k = 0; k < kLoop; k++) { + TestClass2 val1; + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + TestClass val2; + val2.i = -1; + val2.s = ""; + obj.convert(&val2); + EXPECT_EQ(val1.i, val2.i); + EXPECT_EQ(val1.s, val2.s); + EXPECT_FALSE(val2.s.empty()); + } +} + +class TestEnumMemberClass +{ +public: + TestEnumMemberClass() + : t1(STATE_A), t2(STATE_B), t3(STATE_C) {} + + enum TestEnumType { + STATE_INVALID = 0, + STATE_A = 1, + STATE_B = 2, + STATE_C = 3 + }; + TestEnumType t1; + TestEnumType t2; + TestEnumType t3; + + MSGPACK_DEFINE((int&)t1, (int&)t2, (int&)t3); +}; + +TEST(MSGPACK_USER_DEFINED, simple_buffer_enum_member) +{ + TestEnumMemberClass val1; + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + TestEnumMemberClass val2; + val2.t1 = TestEnumMemberClass::STATE_INVALID; + val2.t2 = TestEnumMemberClass::STATE_INVALID; + val2.t3 = TestEnumMemberClass::STATE_INVALID; + obj.convert(&val2); + EXPECT_EQ(val1.t1, val2.t1); + EXPECT_EQ(val1.t2, val2.t2); + EXPECT_EQ(val1.t3, val2.t3); +} + +class TestUnionMemberClass +{ +public: + TestUnionMemberClass() {} + TestUnionMemberClass(double f) { + is_double = true; + value.f = f; + } + TestUnionMemberClass(int i) { + is_double = false; + value.i = i; + } + + union { + double f; + int i; + } value; + bool is_double; + + template + void msgpack_pack(Packer& pk) const + { + if (is_double) + pk.pack(msgpack::type::tuple(true, value.f)); + else + pk.pack(msgpack::type::tuple(false, value.i)); + } + + void msgpack_unpack(msgpack::object o) + { + msgpack::type::tuple tuple; + o.convert(&tuple); + + is_double = tuple.get<0>(); + if (is_double) + tuple.get<1>().convert(&value.f); + else + tuple.get<1>().convert(&value.i); + } +}; + +TEST(MSGPACK_USER_DEFINED, simple_buffer_union_member) +{ + { + // double + TestUnionMemberClass val1(1.0); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + TestUnionMemberClass val2; + obj.convert(&val2); + EXPECT_EQ(val1.is_double, val2.is_double); + EXPECT_TRUE(fabs(val1.value.f - val2.value.f) < kEPS); + } + { + // int + TestUnionMemberClass val1(1); + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, val1); + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); + TestUnionMemberClass val2; + obj.convert(&val2); + EXPECT_EQ(val1.is_double, val2.is_double); + EXPECT_EQ(val1.value.i, 1); + EXPECT_EQ(val1.value.i, val2.value.i); + } +} + +//----------------------------------------------------------------------------- + +#define GEN_TEST_VREF(test_type) \ + do { \ + vector v; \ + v.push_back(0); \ + for (unsigned int i = 0; i < v.size(); i++) { \ + test_type val1 = v[i]; \ + msgpack::vrefbuffer vbuf; \ + msgpack::pack(vbuf, val1); \ + msgpack::sbuffer sbuf; \ + const struct iovec* cur = vbuf.vector(); \ + const struct iovec* end = cur + vbuf.vector_size(); \ + for(; cur != end; ++cur) \ + sbuf.write((const char*)cur->iov_base, cur->iov_len); \ + msgpack::zone z; \ + msgpack::object obj; \ + msgpack::unpack_return ret = \ + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); \ + EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); \ + test_type val2; \ + obj.convert(&val2); \ + EXPECT_EQ(val1, val2); \ + } \ + } while(0); + +TEST(MSGPACK, vrefbuffer_short) +{ + GEN_TEST_VREF(short); +} + +TEST(MSGPACK, vrefbuffer_int) +{ + GEN_TEST_VREF(int); +} + +TEST(MSGPACK, vrefbuffer_long) +{ + GEN_TEST_VREF(long); +} + +TEST(MSGPACK, vrefbuffer_long_long) +{ + GEN_TEST_VREF(long long); +} + +TEST(MSGPACK, vrefbuffer_unsigned_short) +{ + GEN_TEST_VREF(unsigned short); +} + +TEST(MSGPACK, vrefbuffer_unsigned_int) +{ + GEN_TEST_VREF(unsigned int); +} + +TEST(MSGPACK, vrefbuffer_unsigned_long) +{ + GEN_TEST_VREF(unsigned long); +} + +TEST(MSGPACK, vrefbuffer_unsigned_long_long) +{ + GEN_TEST_VREF(unsigned long long); +} + +TEST(MSGPACK, vrefbuffer_uint8) +{ + GEN_TEST_VREF(uint8_t); +} + +TEST(MSGPACK, vrefbuffer_uint16) +{ + GEN_TEST_VREF(uint16_t); +} + +TEST(MSGPACK, vrefbuffer_uint32) +{ + GEN_TEST_VREF(uint32_t); +} + +TEST(MSGPACK, vrefbuffer_uint64) +{ + GEN_TEST_VREF(uint64_t); +} + +TEST(MSGPACK, vrefbuffer_int8) +{ + GEN_TEST_VREF(int8_t); +} + +TEST(MSGPACK, vrefbuffer_int16) +{ + GEN_TEST_VREF(int16_t); +} + +TEST(MSGPACK, vrefbuffer_int32) +{ + GEN_TEST_VREF(int32_t); +} + +TEST(MSGPACK, vrefbuffer_int64) +{ + GEN_TEST_VREF(int64_t); +} + +//----------------------------------------------------------------------------- + +#define GEN_TEST_STREAM(test_type) \ + for (unsigned int k = 0; k < kLoop; k++) { \ + msgpack::sbuffer sbuf; \ + msgpack::packer pk(sbuf); \ + typedef std::vector vec_type; \ + vec_type vec; \ + for(unsigned int i = 0; i < rand() % kLoop; ++i) { \ + vec_type::value_type r = rand(); \ + vec.push_back(r); \ + pk.pack(r); \ + } \ + msgpack::unpacker pac; \ + vec_type::const_iterator it = vec.begin(); \ + const char *p = sbuf.data(); \ + const char * const pend = p + sbuf.size(); \ + while (p < pend) { \ + const size_t sz = std::min(pend - p, rand() % 128); \ + pac.reserve_buffer(sz); \ + memcpy(pac.buffer(), p, sz); \ + pac.buffer_consumed(sz); \ + while (pac.execute()) { \ + if (it == vec.end()) goto out; \ + msgpack::object obj = pac.data(); \ + msgpack::zone *life = pac.release_zone(); \ + EXPECT_TRUE(life != NULL); \ + pac.reset(); \ + vec_type::value_type val; \ + obj.convert(&val); \ + EXPECT_EQ(*it, val); \ + ++it; \ + delete life; \ + } \ + p += sz; \ + } \ + out: \ + ; \ + } + +TEST(MSGPACK, stream_short) +{ + GEN_TEST_STREAM(short); +} + +TEST(MSGPACK, stream_int) +{ + GEN_TEST_STREAM(int); +} + +TEST(MSGPACK, stream_long) +{ + GEN_TEST_STREAM(long); +} + +TEST(MSGPACK, stream_long_long) +{ + GEN_TEST_STREAM(long long); +} + +TEST(MSGPACK, stream_unsigned_short) +{ + GEN_TEST_STREAM(unsigned short); +} + +TEST(MSGPACK, stream_unsigned_int) +{ + GEN_TEST_STREAM(unsigned int); +} + +TEST(MSGPACK, stream_unsigned_long) +{ + GEN_TEST_STREAM(unsigned long); +} + +TEST(MSGPACK, stream_unsigned_long_long) +{ + GEN_TEST_STREAM(unsigned long long); +} + +TEST(MSGPACK, stream_uint8) +{ + GEN_TEST_STREAM(uint8_t); +} + +TEST(MSGPACK, stream_uint16) +{ + GEN_TEST_STREAM(uint16_t); +} + +TEST(MSGPACK, stream_uint32) +{ + GEN_TEST_STREAM(uint32_t); +} + +TEST(MSGPACK, stream_uint64) +{ + GEN_TEST_STREAM(uint64_t); +} + +TEST(MSGPACK, stream_int8) +{ + GEN_TEST_STREAM(int8_t); +} + +TEST(MSGPACK, stream_int16) +{ + GEN_TEST_STREAM(int16_t); +} + +TEST(MSGPACK, stream_int32) +{ + GEN_TEST_STREAM(int32_t); +} + +TEST(MSGPACK, stream_int64) +{ + GEN_TEST_STREAM(int64_t); +} diff --git a/msgpack/test/msgpackc_test.cpp b/msgpack/test/msgpackc_test.cpp new file mode 100644 index 00000000..f5646ea7 --- /dev/null +++ b/msgpack/test/msgpackc_test.cpp @@ -0,0 +1,424 @@ +#include "msgpack.h" + +#include +#include +#include + +#include + +using namespace std; + +const unsigned int kLoop = 10000; +const double kEPS = 1e-10; + +#define GEN_TEST_SIGNED(test_type, func_type) \ + do { \ + vector v; \ + v.push_back(0); \ + v.push_back(1); \ + v.push_back(-1); \ + v.push_back(numeric_limits::min()); \ + v.push_back(numeric_limits::max()); \ + for (unsigned int i = 0; i < kLoop; i++) \ + v.push_back(rand()); \ + for (unsigned int i = 0; i < v.size() ; i++) { \ + test_type val = v[i]; \ + msgpack_sbuffer sbuf; \ + msgpack_sbuffer_init(&sbuf); \ + msgpack_packer pk; \ + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); \ + msgpack_pack_##func_type(&pk, val); \ + msgpack_zone z; \ + msgpack_zone_init(&z, 2048); \ + msgpack_object obj; \ + msgpack_unpack_return ret = \ + msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); \ + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); \ + if (val < 0) { \ + EXPECT_EQ(MSGPACK_OBJECT_NEGATIVE_INTEGER, obj.type); \ + EXPECT_EQ(val, obj.via.i64); \ + } else { \ + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); \ + EXPECT_EQ(val, obj.via.u64); \ + } \ + msgpack_zone_destroy(&z); \ + msgpack_sbuffer_destroy(&sbuf); \ + } \ + } while(0) + +#define GEN_TEST_UNSIGNED(test_type, func_type) \ + do { \ + vector v; \ + v.push_back(0); \ + v.push_back(1); \ + v.push_back(2); \ + v.push_back(numeric_limits::min()); \ + v.push_back(numeric_limits::max()); \ + for (unsigned int i = 0; i < kLoop; i++) \ + v.push_back(rand()); \ + for (unsigned int i = 0; i < v.size() ; i++) { \ + test_type val = v[i]; \ + msgpack_sbuffer sbuf; \ + msgpack_sbuffer_init(&sbuf); \ + msgpack_packer pk; \ + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); \ + msgpack_pack_##func_type(&pk, val); \ + msgpack_zone z; \ + msgpack_zone_init(&z, 2048); \ + msgpack_object obj; \ + msgpack_unpack_return ret = \ + msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); \ + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); \ + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); \ + EXPECT_EQ(val, obj.via.u64); \ + msgpack_zone_destroy(&z); \ + msgpack_sbuffer_destroy(&sbuf); \ + } \ + } while(0) + +TEST(MSGPACKC, simple_buffer_short) +{ + GEN_TEST_SIGNED(short, short); +} + +TEST(MSGPACKC, simple_buffer_int) +{ + GEN_TEST_SIGNED(int, int); +} + +TEST(MSGPACKC, simple_buffer_long) +{ + GEN_TEST_SIGNED(long, long); +} + +TEST(MSGPACKC, simple_buffer_long_long) +{ + GEN_TEST_SIGNED(long long, long_long); +} + +TEST(MSGPACKC, simple_buffer_unsigned_short) +{ + GEN_TEST_UNSIGNED(unsigned short, unsigned_short); +} + +TEST(MSGPACKC, simple_buffer_unsigned_int) +{ + GEN_TEST_UNSIGNED(unsigned int, unsigned_int); +} + +TEST(MSGPACKC, simple_buffer_unsigned_long) +{ + GEN_TEST_UNSIGNED(unsigned long, unsigned_long); +} + +TEST(MSGPACKC, simple_buffer_unsigned_long_long) +{ + GEN_TEST_UNSIGNED(unsigned long long, unsigned_long_long); +} + +TEST(MSGPACKC, simple_buffer_uint8) +{ + GEN_TEST_UNSIGNED(uint8_t, uint8); +} + +TEST(MSGPACKC, simple_buffer_uint16) +{ + GEN_TEST_UNSIGNED(uint16_t, uint16); +} + +TEST(MSGPACKC, simple_buffer_uint32) +{ + GEN_TEST_UNSIGNED(uint32_t, uint32); +} + +TEST(MSGPACKC, simple_buffer_uint64) +{ + GEN_TEST_UNSIGNED(uint64_t, uint64); +} + +TEST(MSGPACKC, simple_buffer_int8) +{ + GEN_TEST_SIGNED(int8_t, int8); +} + +TEST(MSGPACKC, simple_buffer_int16) +{ + GEN_TEST_SIGNED(int16_t, int16); +} + +TEST(MSGPACKC, simple_buffer_int32) +{ + GEN_TEST_SIGNED(int32_t, int32); +} + +TEST(MSGPACKC, simple_buffer_int64) +{ + GEN_TEST_SIGNED(int64_t, int64); +} + +TEST(MSGPACKC, simple_buffer_float) +{ + vector v; + v.push_back(0.0); + v.push_back(1.0); + v.push_back(-1.0); + v.push_back(numeric_limits::min()); + v.push_back(numeric_limits::max()); + v.push_back(nanf("tag")); + v.push_back(1.0/0.0); // inf + v.push_back(-(1.0/0.0)); // -inf + for (unsigned int i = 0; i < kLoop; i++) { + v.push_back(drand48()); + v.push_back(-drand48()); + } + + for (unsigned int i = 0; i < v.size() ; i++) { + float val = v[i]; + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + msgpack_pack_float(&pk, val); + msgpack_zone z; + msgpack_zone_init(&z, 2048); + msgpack_object obj; + msgpack_unpack_return ret = + msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); + EXPECT_EQ(MSGPACK_OBJECT_DOUBLE, obj.type); + if (isnan(val)) + EXPECT_TRUE(isnan(obj.via.dec)); + else if (isinf(val)) + EXPECT_TRUE(isinf(obj.via.dec)); + else + EXPECT_TRUE(fabs(obj.via.dec - val) <= kEPS); + msgpack_zone_destroy(&z); + msgpack_sbuffer_destroy(&sbuf); + } +} + +TEST(MSGPACKC, simple_buffer_double) +{ + vector v; + v.push_back(0.0); + v.push_back(-0.0); + v.push_back(1.0); + v.push_back(-1.0); + v.push_back(numeric_limits::min()); + v.push_back(numeric_limits::max()); + v.push_back(nan("tag")); + v.push_back(1.0/0.0); // inf + v.push_back(-(1.0/0.0)); // -inf + for (unsigned int i = 0; i < kLoop; i++) { + v.push_back(drand48()); + v.push_back(-drand48()); + } + + for (unsigned int i = 0; i < v.size() ; i++) { + double val = v[i]; + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + msgpack_pack_double(&pk, val); + msgpack_zone z; + msgpack_zone_init(&z, 2048); + msgpack_object obj; + msgpack_unpack_return ret = + msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); + EXPECT_EQ(MSGPACK_OBJECT_DOUBLE, obj.type); + if (isnan(val)) + EXPECT_TRUE(isnan(obj.via.dec)); + else if (isinf(val)) + EXPECT_TRUE(isinf(obj.via.dec)); + else + EXPECT_TRUE(fabs(obj.via.dec - val) <= kEPS); + msgpack_zone_destroy(&z); + msgpack_sbuffer_destroy(&sbuf); + } +} + +TEST(MSGPACKC, simple_buffer_nil) +{ + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + msgpack_pack_nil(&pk); + msgpack_zone z; + msgpack_zone_init(&z, 2048); + msgpack_object obj; + msgpack_unpack_return ret = + msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); + EXPECT_EQ(MSGPACK_OBJECT_NIL, obj.type); + msgpack_zone_destroy(&z); + msgpack_sbuffer_destroy(&sbuf); +} + +TEST(MSGPACKC, simple_buffer_true) +{ + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + msgpack_pack_true(&pk); + msgpack_zone z; + msgpack_zone_init(&z, 2048); + msgpack_object obj; + msgpack_unpack_return ret = + msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); + EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, obj.type); + EXPECT_EQ(true, obj.via.boolean); + msgpack_zone_destroy(&z); + msgpack_sbuffer_destroy(&sbuf); +} + +TEST(MSGPACKC, simple_buffer_false) +{ + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + msgpack_pack_false(&pk); + msgpack_zone z; + msgpack_zone_init(&z, 2048); + msgpack_object obj; + msgpack_unpack_return ret = + msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); + EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, obj.type); + EXPECT_EQ(false, obj.via.boolean); + msgpack_zone_destroy(&z); + msgpack_sbuffer_destroy(&sbuf); +} + +TEST(MSGPACKC, simple_buffer_array) +{ + unsigned int array_size = 5; + + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + msgpack_pack_array(&pk, array_size); + msgpack_pack_nil(&pk); + msgpack_pack_true(&pk); + msgpack_pack_false(&pk); + msgpack_pack_int(&pk, 10); + msgpack_pack_int(&pk, -10); + + msgpack_zone z; + msgpack_zone_init(&z, 2048); + msgpack_object obj; + msgpack_unpack_return ret; + ret = msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); + EXPECT_EQ(MSGPACK_OBJECT_ARRAY, obj.type); + EXPECT_EQ(array_size, obj.via.array.size); + + for (unsigned int i = 0; i < obj.via.array.size; i++) { + msgpack_object o = obj.via.array.ptr[i]; + switch (i) { + case 0: + EXPECT_EQ(MSGPACK_OBJECT_NIL, o.type); + break; + case 1: + EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, o.type); + EXPECT_EQ(true, o.via.boolean); + break; + case 2: + EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, o.type); + EXPECT_EQ(false, o.via.boolean); + break; + case 3: + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, o.type); + EXPECT_EQ(10, o.via.u64); + break; + case 4: + EXPECT_EQ(MSGPACK_OBJECT_NEGATIVE_INTEGER, o.type); + EXPECT_EQ(-10, o.via.i64); + break; + } + } + + msgpack_zone_destroy(&z); + msgpack_sbuffer_destroy(&sbuf); +} + +TEST(MSGPACKC, simple_buffer_map) +{ + unsigned int map_size = 2; + + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + msgpack_pack_map(&pk, map_size); + msgpack_pack_true(&pk); + msgpack_pack_false(&pk); + msgpack_pack_int(&pk, 10); + msgpack_pack_int(&pk, -10); + + msgpack_zone z; + msgpack_zone_init(&z, 2048); + msgpack_object obj; + msgpack_unpack_return ret; + ret = msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); + EXPECT_EQ(MSGPACK_OBJECT_MAP, obj.type); + EXPECT_EQ(map_size, obj.via.map.size); + + for (unsigned int i = 0; i < map_size; i++) { + msgpack_object key = obj.via.map.ptr[i].key; + msgpack_object val = obj.via.map.ptr[i].val; + switch (i) { + case 0: + EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, key.type); + EXPECT_EQ(true, key.via.boolean); + EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, val.type); + EXPECT_EQ(false, val.via.boolean); + break; + case 1: + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, key.type); + EXPECT_EQ(10, key.via.u64); + EXPECT_EQ(MSGPACK_OBJECT_NEGATIVE_INTEGER, val.type); + EXPECT_EQ(-10, val.via.i64); + break; + } + } + + msgpack_zone_destroy(&z); + msgpack_sbuffer_destroy(&sbuf); +} + +TEST(MSGPACKC, simple_buffer_raw) +{ + unsigned int raw_size = 7; + + msgpack_sbuffer sbuf; + msgpack_sbuffer_init(&sbuf); + msgpack_packer pk; + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + msgpack_pack_raw(&pk, raw_size); + msgpack_pack_raw_body(&pk, "fr", 2); + msgpack_pack_raw_body(&pk, "syuki", 5); + // invalid data + msgpack_pack_raw_body(&pk, "", 0); + msgpack_pack_raw_body(&pk, "kzk", 0); + + msgpack_zone z; + msgpack_zone_init(&z, 2048); + msgpack_object obj; + msgpack_unpack_return ret; + ret = msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); + EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); + EXPECT_EQ(MSGPACK_OBJECT_RAW, obj.type); + EXPECT_EQ(raw_size, obj.via.raw.size); + EXPECT_EQ(0, memcmp("frsyuki", obj.via.raw.ptr, raw_size)); + + msgpack_zone_destroy(&z); + msgpack_sbuffer_destroy(&sbuf); +} diff --git a/msgpack/test/object.cc b/msgpack/test/object.cc new file mode 100644 index 00000000..5390c4ae --- /dev/null +++ b/msgpack/test/object.cc @@ -0,0 +1,134 @@ +#include +#include + +struct myclass { + myclass() : num(0), str("default") { } + + myclass(int num, const std::string& str) : + num(0), str("default") { } + + ~myclass() { } + + int num; + std::string str; + + MSGPACK_DEFINE(num, str); + + bool operator==(const myclass& o) const + { + return num == o.num && str == o.str; + } +}; + +std::ostream& operator<<(std::ostream& o, const myclass& m) +{ + return o << "myclass("<()); +} + + +TEST(object, print) +{ + msgpack::object obj; + std::cout << obj << std::endl; +} + + +TEST(object, is_nil) +{ + msgpack::object obj; + EXPECT_TRUE(obj.is_nil()); +} + + +TEST(object, type_error) +{ + msgpack::object obj(1); + EXPECT_THROW(obj.as(), msgpack::type_error); + EXPECT_THROW(obj.as >(), msgpack::type_error); + EXPECT_EQ(1, obj.as()); + EXPECT_EQ(1, obj.as()); + EXPECT_EQ(1u, obj.as()); + EXPECT_EQ(1u, obj.as()); +} + + +TEST(object, equal_primitive) +{ + msgpack::object obj_nil; + EXPECT_EQ(obj_nil, msgpack::object()); + + msgpack::object obj_int(1); + EXPECT_EQ(obj_int, msgpack::object(1)); + EXPECT_EQ(obj_int, 1); + + msgpack::object obj_double(1.2); + EXPECT_EQ(obj_double, msgpack::object(1.2)); + EXPECT_EQ(obj_double, 1.2); + + msgpack::object obj_bool(true); + EXPECT_EQ(obj_bool, msgpack::object(true)); + EXPECT_EQ(obj_bool, true); +} + + +TEST(object, construct_primitive) +{ + msgpack::object obj_nil; + EXPECT_EQ(msgpack::type::NIL, obj_nil.type); + + msgpack::object obj_uint(1); + EXPECT_EQ(msgpack::type::POSITIVE_INTEGER, obj_uint.type); + EXPECT_EQ(1u, obj_uint.via.u64); + + msgpack::object obj_int(-1); + EXPECT_EQ(msgpack::type::NEGATIVE_INTEGER, obj_int.type); + EXPECT_EQ(-1, obj_int.via.i64); + + msgpack::object obj_double(1.2); + EXPECT_EQ(msgpack::type::DOUBLE, obj_double.type); + EXPECT_EQ(1.2, obj_double.via.dec); + + msgpack::object obj_bool(true); + EXPECT_EQ(msgpack::type::BOOLEAN, obj_bool.type); + EXPECT_EQ(true, obj_bool.via.boolean); +} + diff --git a/msgpack/test/pack_unpack.cc b/msgpack/test/pack_unpack.cc new file mode 100644 index 00000000..fe4625a5 --- /dev/null +++ b/msgpack/test/pack_unpack.cc @@ -0,0 +1,123 @@ +#include +#include +#include + +TEST(pack, num) +{ + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, 1); +} + + +TEST(pack, vector) +{ + msgpack::sbuffer sbuf; + std::vector vec; + vec.push_back(1); + vec.push_back(2); + vec.push_back(3); + msgpack::pack(sbuf, vec); +} + + +TEST(pack, to_ostream) +{ + std::ostringstream stream; + msgpack::pack(stream, 1); +} + + +struct myclass { + myclass() : num(0), str("default") { } + + myclass(int num, const std::string& str) : + num(0), str("default") { } + + ~myclass() { } + + int num; + std::string str; + + MSGPACK_DEFINE(num, str); +}; + + +TEST(pack, myclass) +{ + msgpack::sbuffer sbuf; + myclass m(1, "msgpack"); + msgpack::pack(sbuf, m); +} + + +TEST(unpack, myclass) +{ + msgpack::sbuffer sbuf; + myclass m1(1, "phraser"); + msgpack::pack(sbuf, m1); + + msgpack::zone z; + msgpack::object obj; + + msgpack::unpack_return ret = + msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); + + EXPECT_EQ(ret, msgpack::UNPACK_SUCCESS); + + myclass m2 = obj.as(); + EXPECT_EQ(m1.num, m2.num); + EXPECT_EQ(m1.str, m2.str); +} + + +TEST(unpack, sequence) +{ + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, 1); + msgpack::pack(sbuf, 2); + msgpack::pack(sbuf, 3); + + size_t offset = 0; + + msgpack::unpacked msg; + + msgpack::unpack(&msg, sbuf.data(), sbuf.size(), &offset); + EXPECT_EQ(1, msg.get().as()); + + msgpack::unpack(&msg, sbuf.data(), sbuf.size(), &offset); + EXPECT_EQ(2, msg.get().as()); + + msgpack::unpack(&msg, sbuf.data(), sbuf.size(), &offset); + EXPECT_EQ(3, msg.get().as()); +} + + +TEST(unpack, sequence_compat) +{ + msgpack::sbuffer sbuf; + msgpack::pack(sbuf, 1); + msgpack::pack(sbuf, 2); + msgpack::pack(sbuf, 3); + + size_t offset = 0; + + msgpack::zone z; + msgpack::object obj; + msgpack::unpack_return ret; + + ret = msgpack::unpack(sbuf.data(), sbuf.size(), &offset, &z, &obj); + EXPECT_TRUE(ret >= 0); + EXPECT_EQ(ret, msgpack::UNPACK_EXTRA_BYTES); + EXPECT_EQ(1, obj.as()); + + ret = msgpack::unpack(sbuf.data(), sbuf.size(), &offset, &z, &obj); + EXPECT_TRUE(ret >= 0); + EXPECT_EQ(ret, msgpack::UNPACK_EXTRA_BYTES); + EXPECT_EQ(2, obj.as()); + + ret = msgpack::unpack(sbuf.data(), sbuf.size(), &offset, &z, &obj); + EXPECT_TRUE(ret >= 0); + EXPECT_EQ(ret, msgpack::UNPACK_SUCCESS); + EXPECT_EQ(3, obj.as()); +} + diff --git a/msgpack/test/pack_unpack_c.cc b/msgpack/test/pack_unpack_c.cc new file mode 100644 index 00000000..e9a03892 --- /dev/null +++ b/msgpack/test/pack_unpack_c.cc @@ -0,0 +1,70 @@ +#include +#include +#include + +TEST(pack, num) +{ + msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); + msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); + + EXPECT_EQ(0, msgpack_pack_int(pk, 1)); + + msgpack_sbuffer_free(sbuf); + msgpack_packer_free(pk); +} + + +TEST(pack, array) +{ + msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); + msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); + + EXPECT_EQ(0, msgpack_pack_array(pk, 3)); + EXPECT_EQ(0, msgpack_pack_int(pk, 1)); + EXPECT_EQ(0, msgpack_pack_int(pk, 2)); + EXPECT_EQ(0, msgpack_pack_int(pk, 3)); + + msgpack_sbuffer_free(sbuf); + msgpack_packer_free(pk); +} + + +TEST(unpack, sequence) +{ + msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); + msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); + + EXPECT_EQ(0, msgpack_pack_int(pk, 1)); + EXPECT_EQ(0, msgpack_pack_int(pk, 2)); + EXPECT_EQ(0, msgpack_pack_int(pk, 3)); + + msgpack_packer_free(pk); + + bool success; + size_t offset = 0; + + msgpack_unpacked msg; + msgpack_unpacked_init(&msg); + + success = msgpack_unpack_next(&msg, sbuf->data, sbuf->size, &offset); + EXPECT_TRUE(success); + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, msg.data.type); + EXPECT_EQ(1, msg.data.via.u64); + + success = msgpack_unpack_next(&msg, sbuf->data, sbuf->size, &offset); + EXPECT_TRUE(success); + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, msg.data.type); + EXPECT_EQ(2, msg.data.via.u64); + + success = msgpack_unpack_next(&msg, sbuf->data, sbuf->size, &offset); + EXPECT_TRUE(success); + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, msg.data.type); + EXPECT_EQ(3, msg.data.via.u64); + + success = msgpack_unpack_next(&msg, sbuf->data, sbuf->size, &offset); + EXPECT_FALSE(success); + + msgpack_sbuffer_free(sbuf); + msgpack_unpacked_destroy(&msg); +} + diff --git a/msgpack/test/streaming.cc b/msgpack/test/streaming.cc new file mode 100644 index 00000000..e80c671b --- /dev/null +++ b/msgpack/test/streaming.cc @@ -0,0 +1,220 @@ +#include +#include +#include + +TEST(streaming, basic) +{ + msgpack::sbuffer buffer; + + msgpack::packer pk(&buffer); + pk.pack(1); + pk.pack(2); + pk.pack(3); + + const char* input = buffer.data(); + const char* const eof = input + buffer.size(); + + msgpack::unpacker pac; + msgpack::unpacked result; + + int count = 0; + while(count < 3) { + pac.reserve_buffer(32*1024); + + // read buffer into pac.buffer() upto + // pac.buffer_capacity() bytes. + size_t len = 1; + memcpy(pac.buffer(), input, len); + input += len; + + pac.buffer_consumed(len); + + while(pac.next(&result)) { + msgpack::object obj = result.get(); + switch(count++) { + case 0: + EXPECT_EQ(1, obj.as()); + break; + case 1: + EXPECT_EQ(2, obj.as()); + break; + case 2: + EXPECT_EQ(3, obj.as()); + return; + } + } + + EXPECT_TRUE(input < eof); + } +} + + +class event_handler { +public: + event_handler(std::istream& input) : input(input) { } + ~event_handler() { } + + void on_read() + { + while(true) { + pac.reserve_buffer(32*1024); + + size_t len = input.readsome(pac.buffer(), pac.buffer_capacity()); + + if(len == 0) { + return; + } + + pac.buffer_consumed(len); + + msgpack::unpacked result; + while(pac.next(&result)) { + on_message(result.get(), result.zone()); + } + + if(pac.message_size() > 10*1024*1024) { + throw std::runtime_error("message is too large"); + } + } + } + + void on_message(msgpack::object obj, std::auto_ptr z) + { + EXPECT_EQ(expect, obj.as()); + } + + int expect; + +private: + std::istream& input; + msgpack::unpacker pac; +}; + +TEST(streaming, event) +{ + std::stringstream stream; + msgpack::packer pk(&stream); + + event_handler handler(stream); + + pk.pack(1); + handler.expect = 1; + handler.on_read(); + + pk.pack(2); + handler.expect = 2; + handler.on_read(); + + pk.pack(3); + handler.expect = 3; + handler.on_read(); +} + + +// backward compatibility +TEST(streaming, basic_compat) +{ + std::ostringstream stream; + msgpack::packer pk(&stream); + + pk.pack(1); + pk.pack(2); + pk.pack(3); + + std::istringstream input(stream.str()); + + msgpack::unpacker pac; + + int count = 0; + while(count < 3) { + pac.reserve_buffer(32*1024); + + size_t len = input.readsome(pac.buffer(), pac.buffer_capacity()); + pac.buffer_consumed(len); + + while(pac.execute()) { + std::auto_ptr z(pac.release_zone()); + msgpack::object obj = pac.data(); + pac.reset(); + + switch(count++) { + case 0: + EXPECT_EQ(1, obj.as()); + break; + case 1: + EXPECT_EQ(2, obj.as()); + break; + case 2: + EXPECT_EQ(3, obj.as()); + return; + } + + } + } +} + + +// backward compatibility +class event_handler_compat { +public: + event_handler_compat(std::istream& input) : input(input) { } + ~event_handler_compat() { } + + void on_read() + { + while(true) { + pac.reserve_buffer(32*1024); + + size_t len = input.readsome(pac.buffer(), pac.buffer_capacity()); + + if(len == 0) { + return; + } + + pac.buffer_consumed(len); + + while(pac.execute()) { + std::auto_ptr z(pac.release_zone()); + msgpack::object obj = pac.data(); + pac.reset(); + on_message(obj, z); + } + + if(pac.message_size() > 10*1024*1024) { + throw std::runtime_error("message is too large"); + } + } + } + + void on_message(msgpack::object obj, std::auto_ptr z) + { + EXPECT_EQ(expect, obj.as()); + } + + int expect; + +private: + std::istream& input; + msgpack::unpacker pac; +}; + +TEST(streaming, event_compat) +{ + std::stringstream stream; + msgpack::packer pk(&stream); + + event_handler_compat handler(stream); + + pk.pack(1); + handler.expect = 1; + handler.on_read(); + + pk.pack(2); + handler.expect = 2; + handler.on_read(); + + pk.pack(3); + handler.expect = 3; + handler.on_read(); +} + diff --git a/msgpack/test/streaming_c.cc b/msgpack/test/streaming_c.cc new file mode 100644 index 00000000..1b7ad8b6 --- /dev/null +++ b/msgpack/test/streaming_c.cc @@ -0,0 +1,98 @@ +#include +#include +#include + +TEST(streaming, basic) +{ + msgpack_sbuffer* buffer = msgpack_sbuffer_new(); + + msgpack_packer* pk = msgpack_packer_new(buffer, msgpack_sbuffer_write); + + // 1, 2, 3, "raw", ["data"], {0.3: 0.4} + EXPECT_EQ(0, msgpack_pack_int(pk, 1)); + EXPECT_EQ(0, msgpack_pack_int(pk, 2)); + EXPECT_EQ(0, msgpack_pack_int(pk, 3)); + EXPECT_EQ(0, msgpack_pack_raw(pk, 3)); + EXPECT_EQ(0, msgpack_pack_raw_body(pk, "raw", 3)); + EXPECT_EQ(0, msgpack_pack_array(pk, 1)); + EXPECT_EQ(0, msgpack_pack_raw(pk, 4)); + EXPECT_EQ(0, msgpack_pack_raw_body(pk, "data", 4)); + EXPECT_EQ(0, msgpack_pack_map(pk, 1)); + EXPECT_EQ(0, msgpack_pack_float(pk, 0.4)); + EXPECT_EQ(0, msgpack_pack_double(pk, 0.8)); + int max_count = 6; + + msgpack_packer_free(pk); + + const char* input = buffer->data; + const char* const eof = input + buffer->size; + + msgpack_unpacker pac; + msgpack_unpacker_init(&pac, MSGPACK_UNPACKER_INIT_BUFFER_SIZE); + + msgpack_unpacked result; + msgpack_unpacked_init(&result); + + int count = 0; + while(count < max_count) { + bool unpacked = false; + + msgpack_unpacker_reserve_buffer(&pac, 32*1024); + + while(!unpacked) { + /* read buffer into msgpack_unapcker_buffer(&pac) upto + * msgpack_unpacker_buffer_capacity(&pac) bytes. */ + memcpy(msgpack_unpacker_buffer(&pac), input, 1); + input += 1; + + EXPECT_TRUE(input <= eof); + + msgpack_unpacker_buffer_consumed(&pac, 1); + + while(msgpack_unpacker_next(&pac, &result)) { + unpacked = 1; + msgpack_object obj = result.data; + msgpack_object e; + switch(count++) { + case 0: + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); + EXPECT_EQ(1, obj.via.u64); + break; + case 1: + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); + EXPECT_EQ(2, obj.via.u64); + break; + case 2: + EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); + EXPECT_EQ(3, obj.via.u64); + break; + case 3: + EXPECT_EQ(MSGPACK_OBJECT_RAW, obj.type); + EXPECT_EQ(std::string("raw",3), std::string(obj.via.raw.ptr, obj.via.raw.size)); + break; + case 4: + EXPECT_EQ(MSGPACK_OBJECT_ARRAY, obj.type); + EXPECT_EQ(1, obj.via.array.size); + e = obj.via.array.ptr[0]; + EXPECT_EQ(MSGPACK_OBJECT_RAW, e.type); + EXPECT_EQ(std::string("data",4), std::string(e.via.raw.ptr, e.via.raw.size)); + break; + case 5: + EXPECT_EQ(MSGPACK_OBJECT_MAP, obj.type); + EXPECT_EQ(1, obj.via.map.size); + e = obj.via.map.ptr[0].key; + EXPECT_EQ(MSGPACK_OBJECT_DOUBLE, e.type); + ASSERT_FLOAT_EQ(0.4, (float)e.via.dec); + e = obj.via.map.ptr[0].val; + EXPECT_EQ(MSGPACK_OBJECT_DOUBLE, e.type); + ASSERT_DOUBLE_EQ(0.8, e.via.dec); + break; + } + } + } + } + + msgpack_unpacker_destroy(&pac); + msgpack_unpacked_destroy(&result); +} + diff --git a/msgpack/test/version.cc b/msgpack/test/version.cc new file mode 100644 index 00000000..9357271e --- /dev/null +++ b/msgpack/test/version.cc @@ -0,0 +1,13 @@ +#include +#include + +TEST(version, print) +{ + printf("MSGPACK_VERSION : %s\n", MSGPACK_VERSION); + printf("MSGPACK_VERSION_MAJOR : %d\n", MSGPACK_VERSION_MAJOR); + printf("MSGPACK_VERSION_MINOR : %d\n", MSGPACK_VERSION_MINOR); + printf("msgpack_version() : %s\n", msgpack_version()); + printf("msgpack_version_major() : %d\n", msgpack_version_major()); + printf("msgpack_version_minor() : %d\n", msgpack_version_minor()); +} + diff --git a/msgpack/test/zone.cc b/msgpack/test/zone.cc new file mode 100644 index 00000000..5274e9f0 --- /dev/null +++ b/msgpack/test/zone.cc @@ -0,0 +1,78 @@ +#include +#include + +TEST(zone, malloc) +{ + msgpack::zone z; + char* buf1 = (char*)z.malloc(4); + memcpy(buf1, "test", 4); + char* buf2 = (char*)z.malloc(4); + memcpy(buf2, "test", 4); +} + + +class myclass { +public: + myclass() : num(0), str("default") { } + + myclass(int num, const std::string& str) : + num(num), str(str) { } + + ~myclass() { } + + int num; + std::string str; + +private: + myclass(const myclass&); +}; + + +TEST(zone, allocate) +{ + msgpack::zone z; + myclass* m = z.allocate(); + EXPECT_EQ(m->num, 0); + EXPECT_EQ(m->str, "default"); +} + + +TEST(zone, allocate_constructor) +{ + msgpack::zone z; + myclass* m = z.allocate(7, "msgpack"); + EXPECT_EQ(m->num, 7); + EXPECT_EQ(m->str, "msgpack"); +} + + +static void custom_finalizer_func(void* user) +{ + myclass* m = (myclass*)user; + delete m; +} + +TEST(zone, push_finalizer) +{ + msgpack::zone z; + myclass* m = new myclass(); + z.push_finalizer(custom_finalizer_func, (void*)m); +} + + +TEST(zone, push_finalizer_auto_ptr) +{ + msgpack::zone z; + std::auto_ptr am(new myclass()); + z.push_finalizer(am); +} + + +TEST(zone, malloc_no_align) +{ + msgpack::zone z; + char* buf1 = (char*)z.malloc_no_align(4); + char* buf2 = (char*)z.malloc_no_align(4); + EXPECT_EQ(buf1+4, buf2); +} + diff --git a/msgpack/unpack_define.h b/msgpack/unpack_define.h new file mode 100644 index 00000000..959d3519 --- /dev/null +++ b/msgpack/unpack_define.h @@ -0,0 +1,93 @@ +/* + * MessagePack unpacking routine template + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MSGPACK_UNPACK_DEFINE_H__ +#define MSGPACK_UNPACK_DEFINE_H__ + +#include "msgpack/sysdep.h" +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef MSGPACK_EMBED_STACK_SIZE +#define MSGPACK_EMBED_STACK_SIZE 32 +#endif + + +typedef enum { + CS_HEADER = 0x00, // nil + + //CS_ = 0x01, + //CS_ = 0x02, // false + //CS_ = 0x03, // true + + //CS_ = 0x04, + //CS_ = 0x05, + //CS_ = 0x06, + //CS_ = 0x07, + + //CS_ = 0x08, + //CS_ = 0x09, + CS_FLOAT = 0x0a, + CS_DOUBLE = 0x0b, + CS_UINT_8 = 0x0c, + CS_UINT_16 = 0x0d, + CS_UINT_32 = 0x0e, + CS_UINT_64 = 0x0f, + CS_INT_8 = 0x10, + CS_INT_16 = 0x11, + CS_INT_32 = 0x12, + CS_INT_64 = 0x13, + + //CS_ = 0x14, + //CS_ = 0x15, + //CS_BIG_INT_16 = 0x16, + //CS_BIG_INT_32 = 0x17, + //CS_BIG_FLOAT_16 = 0x18, + //CS_BIG_FLOAT_32 = 0x19, + CS_RAW_16 = 0x1a, + CS_RAW_32 = 0x1b, + CS_ARRAY_16 = 0x1c, + CS_ARRAY_32 = 0x1d, + CS_MAP_16 = 0x1e, + CS_MAP_32 = 0x1f, + + //ACS_BIG_INT_VALUE, + //ACS_BIG_FLOAT_VALUE, + ACS_RAW_VALUE, +} msgpack_unpack_state; + + +typedef enum { + CT_ARRAY_ITEM, + CT_MAP_KEY, + CT_MAP_VALUE, +} msgpack_container_type; + + +#ifdef __cplusplus +} +#endif + +#endif /* msgpack/unpack_define.h */ + diff --git a/msgpack/unpack_template.h b/msgpack/unpack_template.h new file mode 100644 index 00000000..711b163a --- /dev/null +++ b/msgpack/unpack_template.h @@ -0,0 +1,413 @@ +/* + * MessagePack unpacking routine template + * + * Copyright (C) 2008-2010 FURUHASHI Sadayuki + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef msgpack_unpack_func +#error msgpack_unpack_func template is not defined +#endif + +#ifndef msgpack_unpack_callback +#error msgpack_unpack_callback template is not defined +#endif + +#ifndef msgpack_unpack_struct +#error msgpack_unpack_struct template is not defined +#endif + +#ifndef msgpack_unpack_struct_decl +#define msgpack_unpack_struct_decl(name) msgpack_unpack_struct(name) +#endif + +#ifndef msgpack_unpack_object +#error msgpack_unpack_object type is not defined +#endif + +#ifndef msgpack_unpack_user +#error msgpack_unpack_user type is not defined +#endif + +#ifndef USE_CASE_RANGE +#if !defined(_MSC_VER) +#define USE_CASE_RANGE +#endif +#endif + +msgpack_unpack_struct_decl(_stack) { + msgpack_unpack_object obj; + size_t count; + unsigned int ct; + msgpack_unpack_object map_key; +}; + +msgpack_unpack_struct_decl(_context) { + msgpack_unpack_user user; + unsigned int cs; + unsigned int trail; + unsigned int top; + /* + msgpack_unpack_struct(_stack)* stack; + unsigned int stack_size; + msgpack_unpack_struct(_stack) embed_stack[MSGPACK_EMBED_STACK_SIZE]; + */ + msgpack_unpack_struct(_stack) stack[MSGPACK_EMBED_STACK_SIZE]; +}; + + +msgpack_unpack_func(void, _init)(msgpack_unpack_struct(_context)* ctx) +{ + ctx->cs = CS_HEADER; + ctx->trail = 0; + ctx->top = 0; + /* + ctx->stack = ctx->embed_stack; + ctx->stack_size = MSGPACK_EMBED_STACK_SIZE; + */ + ctx->stack[0].obj = msgpack_unpack_callback(_root)(&ctx->user); +} + +/* +msgpack_unpack_func(void, _destroy)(msgpack_unpack_struct(_context)* ctx) +{ + if(ctx->stack_size != MSGPACK_EMBED_STACK_SIZE) { + free(ctx->stack); + } +} +*/ + +msgpack_unpack_func(msgpack_unpack_object, _data)(msgpack_unpack_struct(_context)* ctx) +{ + return (ctx)->stack[0].obj; +} + + +msgpack_unpack_func(int, _execute)(msgpack_unpack_struct(_context)* ctx, const char* data, size_t len, size_t* off) +{ + assert(len >= *off); + + const unsigned char* p = (unsigned char*)data + *off; + const unsigned char* const pe = (unsigned char*)data + len; + const void* n = NULL; + + unsigned int trail = ctx->trail; + unsigned int cs = ctx->cs; + unsigned int top = ctx->top; + msgpack_unpack_struct(_stack)* stack = ctx->stack; + /* + unsigned int stack_size = ctx->stack_size; + */ + msgpack_unpack_user* user = &ctx->user; + + msgpack_unpack_object obj; + msgpack_unpack_struct(_stack)* c = NULL; + + int ret; + +#define push_simple_value(func) \ + if(msgpack_unpack_callback(func)(user, &obj) < 0) { goto _failed; } \ + goto _push +#define push_fixed_value(func, arg) \ + if(msgpack_unpack_callback(func)(user, arg, &obj) < 0) { goto _failed; } \ + goto _push +#define push_variable_value(func, base, pos, len) \ + if(msgpack_unpack_callback(func)(user, \ + (const char*)base, (const char*)pos, len, &obj) < 0) { goto _failed; } \ + goto _push + +#define again_fixed_trail(_cs, trail_len) \ + trail = trail_len; \ + cs = _cs; \ + goto _fixed_trail_again +#define again_fixed_trail_if_zero(_cs, trail_len, ifzero) \ + trail = trail_len; \ + if(trail == 0) { goto ifzero; } \ + cs = _cs; \ + goto _fixed_trail_again + +#define start_container(func, count_, ct_) \ + if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ \ + if(msgpack_unpack_callback(func)(user, count_, &stack[top].obj) < 0) { goto _failed; } \ + if((count_) == 0) { obj = stack[top].obj; goto _push; } \ + stack[top].ct = ct_; \ + stack[top].count = count_; \ + ++top; \ + /*printf("container %d count %d stack %d\n",stack[top].obj,count_,top);*/ \ + /*printf("stack push %d\n", top);*/ \ + /* FIXME \ + if(top >= stack_size) { \ + if(stack_size == MSGPACK_EMBED_STACK_SIZE) { \ + size_t csize = sizeof(msgpack_unpack_struct(_stack)) * MSGPACK_EMBED_STACK_SIZE; \ + size_t nsize = csize * 2; \ + msgpack_unpack_struct(_stack)* tmp = (msgpack_unpack_struct(_stack)*)malloc(nsize); \ + if(tmp == NULL) { goto _failed; } \ + memcpy(tmp, ctx->stack, csize); \ + ctx->stack = stack = tmp; \ + ctx->stack_size = stack_size = MSGPACK_EMBED_STACK_SIZE * 2; \ + } else { \ + size_t nsize = sizeof(msgpack_unpack_struct(_stack)) * ctx->stack_size * 2; \ + msgpack_unpack_struct(_stack)* tmp = (msgpack_unpack_struct(_stack)*)realloc(ctx->stack, nsize); \ + if(tmp == NULL) { goto _failed; } \ + ctx->stack = stack = tmp; \ + ctx->stack_size = stack_size = stack_size * 2; \ + } \ + } \ + */ \ + goto _header_again + +#define NEXT_CS(p) \ + ((unsigned int)*p & 0x1f) + +#ifdef USE_CASE_RANGE +#define SWITCH_RANGE_BEGIN switch(*p) { +#define SWITCH_RANGE(FROM, TO) case FROM ... TO: +#define SWITCH_RANGE_DEFAULT default: +#define SWITCH_RANGE_END } +#else +#define SWITCH_RANGE_BEGIN { if(0) { +#define SWITCH_RANGE(FROM, TO) } else if(FROM <= *p && *p <= TO) { +#define SWITCH_RANGE_DEFAULT } else { +#define SWITCH_RANGE_END } } +#endif + + if(p == pe) { goto _out; } + do { + switch(cs) { + case CS_HEADER: + SWITCH_RANGE_BEGIN + SWITCH_RANGE(0x00, 0x7f) // Positive Fixnum + push_fixed_value(_uint8, *(uint8_t*)p); + SWITCH_RANGE(0xe0, 0xff) // Negative Fixnum + push_fixed_value(_int8, *(int8_t*)p); + SWITCH_RANGE(0xc0, 0xdf) // Variable + switch(*p) { + case 0xc0: // nil + push_simple_value(_nil); + //case 0xc1: // string + // again_terminal_trail(NEXT_CS(p), p+1); + case 0xc2: // false + push_simple_value(_false); + case 0xc3: // true + push_simple_value(_true); + //case 0xc4: + //case 0xc5: + //case 0xc6: + //case 0xc7: + //case 0xc8: + //case 0xc9: + case 0xca: // float + case 0xcb: // double + case 0xcc: // unsigned int 8 + case 0xcd: // unsigned int 16 + case 0xce: // unsigned int 32 + case 0xcf: // unsigned int 64 + case 0xd0: // signed int 8 + case 0xd1: // signed int 16 + case 0xd2: // signed int 32 + case 0xd3: // signed int 64 + again_fixed_trail(NEXT_CS(p), 1 << (((unsigned int)*p) & 0x03)); + //case 0xd4: + //case 0xd5: + //case 0xd6: // big integer 16 + //case 0xd7: // big integer 32 + //case 0xd8: // big float 16 + //case 0xd9: // big float 32 + case 0xda: // raw 16 + case 0xdb: // raw 32 + case 0xdc: // array 16 + case 0xdd: // array 32 + case 0xde: // map 16 + case 0xdf: // map 32 + again_fixed_trail(NEXT_CS(p), 2 << (((unsigned int)*p) & 0x01)); + default: + goto _failed; + } + SWITCH_RANGE(0xa0, 0xbf) // FixRaw + again_fixed_trail_if_zero(ACS_RAW_VALUE, ((unsigned int)*p & 0x1f), _raw_zero); + SWITCH_RANGE(0x90, 0x9f) // FixArray + start_container(_array, ((unsigned int)*p) & 0x0f, CT_ARRAY_ITEM); + SWITCH_RANGE(0x80, 0x8f) // FixMap + start_container(_map, ((unsigned int)*p) & 0x0f, CT_MAP_KEY); + + SWITCH_RANGE_DEFAULT + goto _failed; + SWITCH_RANGE_END + // end CS_HEADER + + + _fixed_trail_again: + ++p; + + default: + if((size_t)(pe - p) < trail) { goto _out; } + n = p; p += trail - 1; + switch(cs) { + //case CS_ + //case CS_ + case CS_FLOAT: { + union { uint32_t i; float f; } mem; + mem.i = _msgpack_load32(uint32_t,n); + push_fixed_value(_float, mem.f); } + case CS_DOUBLE: { + union { uint64_t i; double f; } mem; + mem.i = _msgpack_load64(uint64_t,n); +#if defined(__arm__) && !(__ARM_EABI__) // arm-oabi + // https://github.com/msgpack/msgpack-perl/pull/1 + mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL); +#endif + push_fixed_value(_double, mem.f); } + case CS_UINT_8: + push_fixed_value(_uint8, *(uint8_t*)n); + case CS_UINT_16: + push_fixed_value(_uint16, _msgpack_load16(uint16_t,n)); + case CS_UINT_32: + push_fixed_value(_uint32, _msgpack_load32(uint32_t,n)); + case CS_UINT_64: + push_fixed_value(_uint64, _msgpack_load64(uint64_t,n)); + + case CS_INT_8: + push_fixed_value(_int8, *(int8_t*)n); + case CS_INT_16: + push_fixed_value(_int16, _msgpack_load16(int16_t,n)); + case CS_INT_32: + push_fixed_value(_int32, _msgpack_load32(int32_t,n)); + case CS_INT_64: + push_fixed_value(_int64, _msgpack_load64(int64_t,n)); + + //case CS_ + //case CS_ + //case CS_BIG_INT_16: + // again_fixed_trail_if_zero(ACS_BIG_INT_VALUE, _msgpack_load16(uint16_t,n), _big_int_zero); + //case CS_BIG_INT_32: + // again_fixed_trail_if_zero(ACS_BIG_INT_VALUE, _msgpack_load32(uint32_t,n), _big_int_zero); + //case ACS_BIG_INT_VALUE: + //_big_int_zero: + // // FIXME + // push_variable_value(_big_int, data, n, trail); + + //case CS_BIG_FLOAT_16: + // again_fixed_trail_if_zero(ACS_BIG_FLOAT_VALUE, _msgpack_load16(uint16_t,n), _big_float_zero); + //case CS_BIG_FLOAT_32: + // again_fixed_trail_if_zero(ACS_BIG_FLOAT_VALUE, _msgpack_load32(uint32_t,n), _big_float_zero); + //case ACS_BIG_FLOAT_VALUE: + //_big_float_zero: + // // FIXME + // push_variable_value(_big_float, data, n, trail); + + case CS_RAW_16: + again_fixed_trail_if_zero(ACS_RAW_VALUE, _msgpack_load16(uint16_t,n), _raw_zero); + case CS_RAW_32: + again_fixed_trail_if_zero(ACS_RAW_VALUE, _msgpack_load32(uint32_t,n), _raw_zero); + case ACS_RAW_VALUE: + _raw_zero: + push_variable_value(_raw, data, n, trail); + + case CS_ARRAY_16: + start_container(_array, _msgpack_load16(uint16_t,n), CT_ARRAY_ITEM); + case CS_ARRAY_32: + /* FIXME security guard */ + start_container(_array, _msgpack_load32(uint32_t,n), CT_ARRAY_ITEM); + + case CS_MAP_16: + start_container(_map, _msgpack_load16(uint16_t,n), CT_MAP_KEY); + case CS_MAP_32: + /* FIXME security guard */ + start_container(_map, _msgpack_load32(uint32_t,n), CT_MAP_KEY); + + default: + goto _failed; + } + } + +_push: + if(top == 0) { goto _finish; } + c = &stack[top-1]; + switch(c->ct) { + case CT_ARRAY_ITEM: + if(msgpack_unpack_callback(_array_item)(user, &c->obj, obj) < 0) { goto _failed; } + if(--c->count == 0) { + obj = c->obj; + --top; + /*printf("stack pop %d\n", top);*/ + goto _push; + } + goto _header_again; + case CT_MAP_KEY: + c->map_key = obj; + c->ct = CT_MAP_VALUE; + goto _header_again; + case CT_MAP_VALUE: + if(msgpack_unpack_callback(_map_item)(user, &c->obj, c->map_key, obj) < 0) { goto _failed; } + if(--c->count == 0) { + obj = c->obj; + --top; + /*printf("stack pop %d\n", top);*/ + goto _push; + } + c->ct = CT_MAP_KEY; + goto _header_again; + + default: + goto _failed; + } + +_header_again: + cs = CS_HEADER; + ++p; + } while(p != pe); + goto _out; + + +_finish: + stack[0].obj = obj; + ++p; + ret = 1; + /*printf("-- finish --\n"); */ + goto _end; + +_failed: + /*printf("** FAILED **\n"); */ + ret = -1; + goto _end; + +_out: + ret = 0; + goto _end; + +_end: + ctx->cs = cs; + ctx->trail = trail; + ctx->top = top; + *off = p - (const unsigned char*)data; + + return ret; +} + + +#undef msgpack_unpack_func +#undef msgpack_unpack_callback +#undef msgpack_unpack_struct +#undef msgpack_unpack_object +#undef msgpack_unpack_user + +#undef push_simple_value +#undef push_fixed_value +#undef push_variable_value +#undef again_fixed_trail +#undef again_fixed_trail_if_zero +#undef start_container + +#undef NEXT_CS + From 864124f4c023199dd16a45db310ccf7e7a68cd31 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 10 Jun 2013 02:48:42 -0400 Subject: [PATCH 004/703] Public key auth no longer need to be blocking (libssh 0.6) --- tmate-ssh-client.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 581ca772..1d52e6fa 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -47,18 +47,6 @@ static void register_input_stream_event(struct tmate_ssh_client *client) } } -static int __ssh_userauth_autopubkey(ssh_session session, const char *passphrase) -{ - int ret; - - /* For some reason, auth doesn't work in blocking mode :( */ - ssh_set_blocking(session, 1); - ret = ssh_userauth_autopubkey(session, passphrase); - ssh_set_blocking(session, 0); - - return ret; -} - static void consume_channel(struct tmate_ssh_client *client) { char *buf; @@ -134,7 +122,7 @@ static void on_session_event(struct tmate_ssh_client *client) /* TODO Authenticate server */ case SSH_AUTH: - switch (__ssh_userauth_autopubkey(session, NULL)) { + switch (ssh_userauth_autopubkey(session, NULL)) { case SSH_AUTH_AGAIN: return; case SSH_AUTH_PARTIAL: From a75e70f86bfc9bcc44068487c6b458c8834542ce Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 10 Jun 2013 04:29:40 -0400 Subject: [PATCH 005/703] Fix error message --- tmate-ssh-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 1d52e6fa..fa09c551 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -146,7 +146,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - tmate_debug("Error opening session: %s", ssh_get_error(session)); + tmate_debug("Error opening channel: %s", ssh_get_error(session)); reconnect_session(client); return; case SSH_OK: From 8131143fa8a07a8a77d56b2283aa6a9fa1e5601d Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 10 Jun 2013 04:30:02 -0400 Subject: [PATCH 006/703] gitignore update --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7721edac..67204eec 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ configure tmate cscope.* ctags +*.log From 7cdb1e2016535dc254d0ea23e4e62ec68bed41b1 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 10 Jun 2013 03:39:13 -0400 Subject: [PATCH 007/703] [libssh] bug fix in channel_open() --- libssh/src/channels.c | 2 +- libssh/src/session.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libssh/src/channels.c b/libssh/src/channels.c index 6e9b2eb0..902e04b0 100644 --- a/libssh/src/channels.c +++ b/libssh/src/channels.c @@ -314,7 +314,7 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, pending: /* wait until channel is opened by server */ err = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, ssh_channel_open_termination, channel); - if (err != SSH_OK || session->session_state == SSH_SESSION_STATE_ERROR) + if (session->session_state == SSH_SESSION_STATE_ERROR) err = SSH_ERROR; end: if(channel->state == SSH_CHANNEL_STATE_OPEN) diff --git a/libssh/src/session.c b/libssh/src/session.c index 4e713948..fe5d897c 100644 --- a/libssh/src/session.c +++ b/libssh/src/session.c @@ -537,8 +537,10 @@ int ssh_handle_packets_termination(ssh_session session, int timeout, ret = ssh_handle_packets(session, tm); if(ret == SSH_ERROR) break; - if(ssh_timeout_elapsed(&ts,timeout)) + if(ssh_timeout_elapsed(&ts,timeout)) { + ret = fct(user) ? SSH_OK : SSH_AGAIN; break; + } tm = ssh_timeout_update(&ts, timeout); } return ret; From 56cee30ee8701a8b153d975af57a54f2dc404c37 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 10 Jun 2013 05:59:21 -0400 Subject: [PATCH 008/703] [libssh] fix server side compression --- libssh/src/wrapper.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libssh/src/wrapper.c b/libssh/src/wrapper.c index b8a489d4..c04322a4 100644 --- a/libssh/src/wrapper.c +++ b/libssh/src/wrapper.c @@ -314,23 +314,24 @@ int crypt_set_algorithms_server(ssh_session session){ } /* compression */ - method = session->next_crypto->kex_methods[SSH_CRYPT_C_S]; + method = session->next_crypto->kex_methods[SSH_COMP_C_S]; if(strcmp(method,"zlib") == 0){ ssh_log(session,SSH_LOG_PACKET,"enabling C->S compression"); session->next_crypto->do_compress_in=1; } if(strcmp(method,"zlib@openssh.com") == 0){ - ssh_set_error(session,SSH_FATAL,"zlib@openssh.com not supported"); - goto error; + ssh_log(session,SSH_LOG_PACKET,"enabling C->S compression"); + session->next_crypto->delayed_compress_in=1; } - method = session->next_crypto->kex_methods[SSH_CRYPT_S_C]; + + method = session->next_crypto->kex_methods[SSH_COMP_S_C]; if(strcmp(method,"zlib") == 0){ ssh_log(session,SSH_LOG_PACKET,"enabling S->C compression\n"); session->next_crypto->do_compress_out=1; } if(strcmp(method,"zlib@openssh.com") == 0){ - ssh_set_error(session,SSH_FATAL,"zlib@openssh.com not supported"); - goto error; + ssh_log(session,SSH_LOG_PACKET,"enabling S->C delayed compression\n"); + session->next_crypto->delayed_compress_out=1; } method = session->next_crypto->kex_methods[SSH_HOSTKEYS]; From 6988b99fb41789be378e51211c2724fbdd8322d7 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 10 Jun 2013 05:51:11 -0400 Subject: [PATCH 009/703] Enable compression --- tmate-ssh-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index fa09c551..3c5ff53f 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -98,6 +98,7 @@ static void on_session_event(struct tmate_ssh_client *client) ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); ssh_options_set(session, SSH_OPTIONS_PORT, &port); ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); + ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); tmate_debug("Connecting..."); client->state = SSH_CONNECT; From f89b98e1c326e4ca7bbf99989069c80961a0dced Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 11 Jun 2013 13:30:25 -0400 Subject: [PATCH 010/703] Use tmate.io --- tmate-ssh-client.c | 4 ++-- tmate.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 3c5ff53f..497b7128 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -72,7 +72,7 @@ static void consume_channel(struct tmate_ssh_client *client) static void on_session_event(struct tmate_ssh_client *client) { int verbosity = SSH_LOG_RARE; - int port = 2200; + int port = TMATE_PORT; ssh_session session = client->session; ssh_channel channel = client->channel; @@ -97,7 +97,7 @@ static void on_session_event(struct tmate_ssh_client *client) ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); ssh_options_set(session, SSH_OPTIONS_PORT, &port); - ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); + ssh_options_set(session, SSH_OPTIONS_USER, TMATE_HOST); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); tmate_debug("Connecting..."); diff --git a/tmate.h b/tmate.h index 78d0e422..d95c8efa 100644 --- a/tmate.h +++ b/tmate.h @@ -57,6 +57,9 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); /* tmate-ssh-client.c */ +#define TMATE_HOST "tmate.io" +#define TMATE_PORT 22 + typedef struct ssh_session_struct* ssh_session; typedef struct ssh_channel_struct* ssh_channel; From b7371802bf2e2c51ff9fff6915e646471c782ff7 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 11 Jun 2013 17:00:50 -0400 Subject: [PATCH 011/703] Developer Environement setup --- Makefile.am | 4 ++++ configure.ac | 7 +++++++ tmate.h | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/Makefile.am b/Makefile.am index 51b15ed8..9cf3fe46 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,6 +24,10 @@ endif CFLAGS += -Wno-unused-parameter -Wno-unused-variable CFLAGS += -Ilibssh/include/ -Imsgpack/src +if IS_DEVENV +CFLAGS += -DDEVENV +endif + # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. if IS_GCC diff --git a/configure.ac b/configure.ac index 013cd655..5be42271 100644 --- a/configure.ac +++ b/configure.ac @@ -40,6 +40,13 @@ AC_CHECK_HEADERS( ] ) +AC_ARG_ENABLE( + devenv, + AC_HELP_STRING(--enable-devenv, "dev env (localhost, port 2200, no auth checks)"), + found_devenv=$enable_devenv +) +AM_CONDITIONAL(IS_DEVENV, test "x$found_devenv" = xyes) + # Is this a debug build? #found_debug=yes AC_ARG_ENABLE( diff --git a/tmate.h b/tmate.h index d95c8efa..ed3f847b 100644 --- a/tmate.h +++ b/tmate.h @@ -57,8 +57,13 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); /* tmate-ssh-client.c */ +#ifdef DEVENV +#define TMATE_HOST "localhost" +#define TMATE_PORT 2200 +#else #define TMATE_HOST "tmate.io" #define TMATE_PORT 22 +#endif typedef struct ssh_session_struct* ssh_session; typedef struct ssh_channel_struct* ssh_channel; From 839c4e3dd9b7d5ebfaacbc5a446bcb9ef6f9b63b Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 11 Jun 2013 22:07:04 -0400 Subject: [PATCH 012/703] Replicate remote client commands --- tmate-decoder.c | 88 ++++++++++++++++++++++++++++++++++++++++++++----- tmate.h | 3 +- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/tmate-decoder.c b/tmate-decoder.c index 476077af..774c8c0d 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -10,6 +10,7 @@ struct tmate_unpacker { static void decoder_error(void) { + /* TODO Don't kill the session, disconnect */ tmate_fatal("Received a bad message"); } @@ -42,22 +43,64 @@ static int64_t unpack_int(struct tmate_unpacker *uk) return val; } -static void tmate_client_key(struct tmate_unpacker *uk) +static void unpack_raw(struct tmate_unpacker *uk, + const char **buf, size_t *len) { - struct client *c; + if (uk->argc == 0) + decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_RAW) + decoder_error(); + + *len = uk->argv[0].via.raw.size; + *buf = uk->argv[0].via.raw.ptr; + + uk->argv++; + uk->argc--; +} + +static char *unpack_string(struct tmate_unpacker *uk) +{ + const char *buf; + char *alloc_buf; + size_t len; + + unpack_raw(uk, &buf, &len); + + alloc_buf = xmalloc(len + 1); + memcpy(alloc_buf, buf, len); + alloc_buf[len] = '\0'; + + return alloc_buf; +} + + +static void tmate_client_pane_key(struct tmate_unpacker *uk) +{ + struct session *s; + struct window *w; + struct window_pane *wp; + int key = unpack_int(uk); - /* Very gross. other clients cannot even detach */ + s = RB_MIN(sessions, &sessions); + if (!s) + return; - if (ARRAY_LENGTH(&clients) > 0) { - c = ARRAY_ITEM(&clients, 0); - server_client_handle_key(c, key); - } + w = s->curw->window; + if (!w) + return; + + wp = w->active; + if (!wp) + return; + + window_pane_key(wp, s, key); } static void tmate_client_resize(struct tmate_unpacker *uk) { - /* A bit gross as well */ + /* TODO This is sad, we might want our own client. */ tmate_sx = unpack_int(uk); tmate_sy = unpack_int(uk); recalculate_sizes(); @@ -65,6 +108,32 @@ static void tmate_client_resize(struct tmate_unpacker *uk) /* TODO Handle reconnection cases */ } +static void tmate_client_cmd(struct tmate_unpacker *uk) +{ + struct cmd_q *cmd_q; + struct cmd_list *cmdlist; + unsigned int argc = 0; + char *argv[1024]; + char *cmd_str; + char *cause; + int i; + + cmd_str = unpack_string(uk); + tmate_debug("received command from remote client: %s", cmd_str); + + if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) { + free(cause); + goto out; + } + + cmd_q = cmdq_new(NULL); + cmdq_run(cmd_q, cmdlist); + cmd_list_free(cmdlist); + cmdq_free(cmd_q); +out: + free(cmd_str); +} + static void handle_message(msgpack_object obj) { struct tmate_unpacker _uk; @@ -74,8 +143,9 @@ static void handle_message(msgpack_object obj) init_unpacker(uk, obj); switch (unpack_int(uk)) { - case TMATE_CLIENT_KEY: tmate_client_key(uk); break; + case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; + case TMATE_CLIENT_CMD: tmate_client_cmd(uk); break; default: decoder_error(); } } diff --git a/tmate.h b/tmate.h index ed3f847b..a9caf337 100644 --- a/tmate.h +++ b/tmate.h @@ -39,8 +39,9 @@ extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); /* tmate-decoder.c */ enum tmate_notifications { - TMATE_CLIENT_KEY, + TMATE_CLIENT_PANE_KEY, TMATE_CLIENT_RESIZE, + TMATE_CLIENT_CMD, }; struct tmate_decoder { From 35daf6d8058a3ee68b6007df1d594323cde9debe Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 11 Jun 2013 22:45:33 -0400 Subject: [PATCH 013/703] Replication of bind/unbind commands --- cmd-queue.c | 6 ++++++ server.c | 5 +++-- tmate-decoder.c | 5 ----- tmate-encoder.c | 27 +++++++++++++++++++++++++-- tmate.h | 5 ++++- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index c5f75f40..8a274305 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -23,6 +23,7 @@ #include #include "tmux.h" +#include "tmate.h" /* Create new command queue. */ struct cmd_q * @@ -225,6 +226,11 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->time = time(NULL); cmdq->number++; +#ifdef TMATE + if (tmate_should_replicate_cmd(cmdq->cmd->entry)) + tmate_cmd(s); +#endif + guard = cmdq_guard(cmdq, "begin"); retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); if (guard) { diff --git a/server.c b/server.c index 352255f7..6a636825 100644 --- a/server.c +++ b/server.c @@ -186,6 +186,9 @@ server_start(int lockfd, char *lockfile) ARRAY_ADD(&cfg_causes, cause); } } + + tmate_client_start(); + cmdq_continue(cfg_cmd_q); server_add_accept(0); @@ -197,8 +200,6 @@ server_start(int lockfd, char *lockfile) set_signals(server_signal_callback); - tmate_client_start(); - server_loop(); exit(0); } diff --git a/tmate-decoder.c b/tmate-decoder.c index 774c8c0d..1abe700e 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -112,15 +112,10 @@ static void tmate_client_cmd(struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; - unsigned int argc = 0; - char *argv[1024]; char *cmd_str; char *cause; - int i; cmd_str = unpack_string(uk); - tmate_debug("received command from remote client: %s", cmd_str); - if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) { free(cause); goto out; diff --git a/tmate-encoder.c b/tmate-encoder.c index a4449e5c..6995371c 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -1,5 +1,3 @@ -#include - #include "tmate.h" static int msgpack_write(void *data, const char *buf, unsigned int len) @@ -87,3 +85,28 @@ void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) len -= to_write; } } + +static const struct cmd_entry *replicated_cmds[] = { + &cmd_bind_key_entry, + &cmd_unbind_key_entry, + &cmd_set_option_entry, + &cmd_set_window_option_entry, + NULL +}; + +int tmate_should_replicate_cmd(const struct cmd_entry *cmd) +{ + const struct cmd_entry **ptr; + + for (ptr = replicated_cmds; *ptr; ptr++) + if (*ptr == cmd) + return 1; + return 0; +} + +void tmate_cmd(const char *cmd) +{ + pack(array, 2); + pack(int, TMATE_CMD); + pack(string, cmd); +} diff --git a/tmate.h b/tmate.h index a9caf337..13defe29 100644 --- a/tmate.h +++ b/tmate.h @@ -22,6 +22,7 @@ enum tmate_commands { TMATE_HEADER, TMATE_SYNC_WINDOW, TMATE_PTY_DATA, + TMATE_CMD, }; struct tmate_encoder { @@ -35,10 +36,12 @@ extern void tmate_encoder_init(struct tmate_encoder *encoder); extern void tmate_write_header(void); extern void tmate_sync_window(struct window *w); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); +extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); +extern void tmate_cmd(const char *cmd); /* tmate-decoder.c */ -enum tmate_notifications { +enum tmate_client_commands { TMATE_CLIENT_PANE_KEY, TMATE_CLIENT_RESIZE, TMATE_CLIENT_CMD, From 141428691e7028a3988d4345e7c429e2929fc075 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 11 Jun 2013 23:50:16 -0400 Subject: [PATCH 014/703] Status bar sync --- status.c | 3 +++ tmate-encoder.c | 19 +++++++++++++++++++ tmate.h | 2 ++ 3 files changed, 24 insertions(+) diff --git a/status.c b/status.c index f120c38a..f7a3420e 100644 --- a/status.c +++ b/status.c @@ -28,6 +28,7 @@ #include #include "tmux.h" +#include "tmate.h" char *status_redraw_get_left( struct client *, time_t, int, struct grid_cell *, size_t *); @@ -206,6 +207,8 @@ status_redraw(struct client *c) memcpy(&rgc, &stdgc, sizeof rgc); right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen); + tmate_status(left, right); + /* * Figure out how much space we have for the window list. If there * isn't enough space, just show a blank status line. diff --git a/tmate-encoder.c b/tmate-encoder.c index 6995371c..976edc36 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -110,3 +110,22 @@ void tmate_cmd(const char *cmd) pack(int, TMATE_CMD); pack(string, cmd); } + +void tmate_status(const char *left, const char *right) +{ + static char *old_left, *old_right; + + if (old_left && !strcmp(old_left, left) && + old_right && !strcmp(old_right, right)) + return; + + pack(array, 3); + pack(int, TMATE_STATUS); + pack(string, left); + pack(string, right); + + free(old_left); + free(old_right); + old_left = xstrdup(left); + old_right = xstrdup(right); +} diff --git a/tmate.h b/tmate.h index 13defe29..46800aa5 100644 --- a/tmate.h +++ b/tmate.h @@ -23,6 +23,7 @@ enum tmate_commands { TMATE_SYNC_WINDOW, TMATE_PTY_DATA, TMATE_CMD, + TMATE_STATUS, }; struct tmate_encoder { @@ -38,6 +39,7 @@ extern void tmate_sync_window(struct window *w); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); extern void tmate_cmd(const char *cmd); +extern void tmate_status(const char *left, const char *right); /* tmate-decoder.c */ From bf4edb40561674d0f0b4bae1202691af294ab0af Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 01:28:01 -0400 Subject: [PATCH 015/703] Authenticating the server --- tmate-ssh-client.c | 61 ++++++++++++++++++++++++++++++++++++++++++---- tmate.h | 6 ++++- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 497b7128..abc23fa3 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -71,6 +71,13 @@ static void consume_channel(struct tmate_ssh_client *client) static void on_session_event(struct tmate_ssh_client *client) { + ssh_key pubkey; + int key_type; + unsigned char *hash; + ssize_t hash_len; + char *hash_str; + int match; + int verbosity = SSH_LOG_RARE; int port = TMATE_PORT; @@ -94,10 +101,10 @@ static void on_session_event(struct tmate_ssh_client *client) } ssh_set_blocking(session, 0); - ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_HOST, TMATE_HOST); ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); ssh_options_set(session, SSH_OPTIONS_PORT, &port); - ssh_options_set(session, SSH_OPTIONS_USER, TMATE_HOST); + ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); tmate_debug("Connecting..."); @@ -116,13 +123,57 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_OK: register_session_fd_event(client); tmate_debug("Connected"); - client->state = SSH_AUTH; + client->state = SSH_AUTH_SERVER; /* fall through */ } - /* TODO Authenticate server */ + case SSH_AUTH_SERVER: + if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) { + tmate_debug("Cannnot authenticate server"); + disconnect_session(client); + return; + } - case SSH_AUTH: + hash_str = ssh_get_hexa(hash, hash_len); + if (!hash_str) + tmate_fatal("malloc failed"); + + if (ssh_get_publickey(session, &pubkey) < 0) + tmate_fatal("ssh_get_publickey"); + +#ifdef DEVENV + match = 1; +#else + key_type = ssh_key_type(pubkey); + switch (key_type) { + case SSH_KEYTYPE_DSS: + match = !strcmp(hash_str, TMATE_HOST_DSA_KEY); + break; + case SSH_KEYTYPE_RSA: + match = !strcmp(hash_str, TMATE_HOST_RSA_KEY); + break; + case SSH_KEYTYPE_ECDSA: + match = !strcmp(hash_str, TMATE_HOST_ECDSA_KEY); + break; + default: + match = 0; + } +#endif + + ssh_key_free(pubkey); + ssh_clean_pubkey_hash(&hash); + free(hash_str); + + if (!match) { + tmate_debug("Cannnot authenticate server"); + disconnect_session(client); + return; + } + + client->state = SSH_AUTH_CLIENT; + /* fall through */ + + case SSH_AUTH_CLIENT: switch (ssh_userauth_autopubkey(session, NULL)) { case SSH_AUTH_AGAIN: return; diff --git a/tmate.h b/tmate.h index 46800aa5..3acabed4 100644 --- a/tmate.h +++ b/tmate.h @@ -69,6 +69,9 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); #else #define TMATE_HOST "tmate.io" #define TMATE_PORT 22 +#define TMATE_HOST_DSA_KEY "f5:26:31:c3:8a:78:6e:5c:77:74:0f:41:5b:5f:21:88" +#define TMATE_HOST_RSA_KEY "af:2d:81:c1:fe:49:70:2d:7f:09:a9:d7:4b:32:e3:be" +#define TMATE_HOST_ECDSA_KEY "c7:a1:51:36:d2:bb:35:4b:0a:1a:c0:43:97:74:ea:42" #endif typedef struct ssh_session_struct* ssh_session; @@ -78,7 +81,8 @@ enum tmate_ssh_client_state_types { SSH_NONE, SSH_INIT, SSH_CONNECT, - SSH_AUTH, + SSH_AUTH_SERVER, + SSH_AUTH_CLIENT, SSH_OPEN_CHANNEL, SSH_BOOTSTRAP, SSH_READY, From 69bc1bfde5cafeba60e9c91a178a731f840ea288 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 02:53:44 -0400 Subject: [PATCH 016/703] Sync all session windows --- server-client.c | 7 +++- session.c | 12 +++++++ tmate-encoder.c | 89 +++++++++++++++++++++++++++++++++++++------------ tmate.h | 4 +-- window.c | 4 +-- 5 files changed, 88 insertions(+), 28 deletions(-) diff --git a/server-client.c b/server-client.c index 241e1bed..89a4faa6 100644 --- a/server-client.c +++ b/server-client.c @@ -491,6 +491,8 @@ server_client_loop(void) struct window_pane *wp; u_int i; + int tmate_should_sync_layout = 0; + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL) @@ -513,7 +515,7 @@ server_client_loop(void) continue; if (w->flags & WINDOW_REDRAW) - tmate_sync_window(w); + tmate_should_sync_layout = 1; w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { @@ -522,6 +524,9 @@ server_client_loop(void) wp->flags &= ~PANE_REDRAW; } } + + if (tmate_should_sync_layout) + tmate_sync_layout(); } /* Check if pane should be resized. */ diff --git a/session.c b/session.c index d3d82ea2..ebe1178d 100644 --- a/session.c +++ b/session.c @@ -91,10 +91,12 @@ session_create(const char *name, const char *cmd, const char *cwd, { struct session *s; +#ifdef TMATE if (next_session_id != 0) { xasprintf(cause, "multi sessions is not supported with tmate"); return NULL; } +#endif s = xmalloc(sizeof *s); s->references = 0; @@ -270,6 +272,11 @@ session_new(struct session *s, options_set_number(&w->options, "remain-on-exit", 1); session_group_synchronize_from(s); + +#ifdef TMATE + tmate_sync_layout(); +#endif + return (wl); } @@ -423,6 +430,11 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_push(&s->lastw, s->curw); s->curw = wl; winlink_clear_flags(wl); + +#ifdef TMATE + tmate_sync_layout(); +#endif + return (0); } diff --git a/tmate-encoder.c b/tmate-encoder.c index 976edc36..57f6c110 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -35,36 +35,81 @@ void tmate_write_header(void) pack(int, TMATE_PROTOCOL_VERSION); } -void tmate_sync_window(struct window *w) +void tmate_sync_layout(void) { + struct session *s; + struct winlink *wl; + struct window *w; struct window_pane *wp; int num_panes = 0; + int num_windows = 0; int active_pane_id = -1; + int active_window_id = -1; - pack(array, 7); - pack(int, TMATE_SYNC_WINDOW); + /* + * We only allow one session, it makes our lives easier. + * Especially when the HTML5 client will come along. + * We make no distinction between a winlink and its window. + * TODO send the winlink in the current session stack order. + */ - pack(int, w->id); - pack(string, w->name); - pack(int, w->sx); - pack(int, w->sy); + s = RB_MIN(sessions, &sessions); + if (!s) + return; - TAILQ_FOREACH(wp, &w->panes, entry) - num_panes++; - - pack(array, num_panes); - TAILQ_FOREACH(wp, &w->panes, entry) { - pack(array, 5); - pack(int, wp->id); - pack(int, wp->sx); - pack(int, wp->sy); - pack(int, wp->xoff); - pack(int, wp->yoff); - - if (wp == w->active) - active_pane_id = wp->id; + num_windows = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window) + num_windows++; } - pack(int, active_pane_id); + + if (!num_windows) + return; + + pack(array, 5); + pack(int, TMATE_SYNC_LAYOUT); + + pack(int, s->sx); + pack(int, s->sy); + + pack(array, num_windows); + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + if (!w) + continue; + + pack(array, 4); + pack(int, w->id); + pack(string, w->name); + + num_panes = 0; + TAILQ_FOREACH(wp, &w->panes, entry) + num_panes++; + + pack(array, num_panes); + TAILQ_FOREACH(wp, &w->panes, entry) { + pack(array, 5); + pack(int, wp->id); + pack(int, wp->sx); + pack(int, wp->sy); + pack(int, wp->xoff); + pack(int, wp->yoff); + + if (wp == w->active) + active_pane_id = wp->id; + } + pack(int, active_pane_id); + + if (wl == s->curw) + active_window_id = w->id; + } + + if (active_window_id == -1) { + wl = RB_MIN(winlinks, &s->windows); + active_window_id = wl->window->id; + } + + pack(int, active_window_id); } void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) diff --git a/tmate.h b/tmate.h index 3acabed4..7a07facd 100644 --- a/tmate.h +++ b/tmate.h @@ -20,7 +20,7 @@ enum tmate_commands { TMATE_HEADER, - TMATE_SYNC_WINDOW, + TMATE_SYNC_LAYOUT, TMATE_PTY_DATA, TMATE_CMD, TMATE_STATUS, @@ -35,7 +35,7 @@ struct tmate_encoder { extern void tmate_encoder_init(struct tmate_encoder *encoder); extern void tmate_write_header(void); -extern void tmate_sync_window(struct window *w); +extern void tmate_sync_layout(void); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); extern void tmate_cmd(const char *cmd); diff --git a/window.c b/window.c index f4bccadb..10629042 100644 --- a/window.c +++ b/window.c @@ -329,8 +329,6 @@ window_create(const char *name, const char *cmd, const char *shell, } else w->name = default_window_name(w); - tmate_sync_window(w); - return (w); } @@ -377,7 +375,7 @@ window_set_name(struct window *w, const char *new_name) free(w->name); w->name = xstrdup(new_name); notify_window_renamed(w); - tmate_sync_window(w); + tmate_sync_layout(); } void From a4e196366e72c92af069cc602b415fd59cbd5f83 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 16:19:23 -0400 Subject: [PATCH 017/703] Quick README --- README => README-tmux | 0 README.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) rename README => README-tmux (100%) create mode 100644 README.md diff --git a/README b/README-tmux similarity index 100% rename from README rename to README-tmux diff --git a/README.md b/README.md new file mode 100644 index 00000000..54a6d058 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +tmate +===== + +What is it? +----------- + +Tmate is a fork of tmux. It provides an instant pairing solution. + +License +------- + +tmate is built on top of tmux, libssh and msgpack. Their respective licenses are +in the sources. tmate is MIT licensed. + +Copyright (c) 2013 Nicolas Viennot + +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 OR 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. From e70a5f8b7febfa19a689d4978943602d625eaba3 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 17:57:53 -0400 Subject: [PATCH 018/703] Add strack trace debug function --- Makefile.am | 1 + tmate-debug.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ tmate.h | 3 +++ 3 files changed, 79 insertions(+) create mode 100644 tmate-debug.c diff --git a/Makefile.am b/Makefile.am index 9cf3fe46..beff8484 100644 --- a/Makefile.am +++ b/Makefile.am @@ -177,6 +177,7 @@ dist_tmate_SOURCES = \ session.c \ signal.c \ status.c \ + tmate-debug.c \ tmate-ssh-client.c \ tmate-encoder.c \ tmate-decoder.c \ diff --git a/tmate-debug.c b/tmate-debug.c new file mode 100644 index 00000000..c480e658 --- /dev/null +++ b/tmate-debug.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include "tmate.h" + +#if DEBUG + +static int print_resolved_stack_frame(const char *frame) +{ + char file[100]; + char cmd[200]; + char output[300]; + char address[20]; + char *line; + FILE *ps; + + static regex_t _regex; + static regex_t *regex; + regmatch_t matches[3]; + + if (!regex) { + if (regcomp(&_regex, "(.+)\\(\\) \\[([^]]+)\\]", REG_EXTENDED)) + return -1; + regex = &_regex; + } + + if (regexec(regex, frame, 3, matches, 0)) + return -1; + + memcpy(file, &frame[matches[1].rm_so], matches[1].rm_eo - matches[1].rm_so); + file[matches[1].rm_eo - matches[1].rm_so] = 0; + + memcpy(address, &frame[matches[2].rm_so], matches[2].rm_eo - matches[2].rm_so); + address[matches[2].rm_eo - matches[2].rm_so] = 0; + + sprintf(cmd, "addr2line -e %s %s -f -p -s", file, address); + + ps = popen(cmd, "r"); + if (!ps) + return -1; + + line = fgets(output, sizeof(output), ps); + pclose(ps); + + if (!line) + return -1; + + line[strlen(line)-1] = 0; /* remove \n */ + tmate_debug("%s(%s) [%s]", file, line, address); + return 0; +} +#endif + +void tmate_print_trace(void) +{ + void *array[20]; + size_t size; + char **strings; + size_t i; + + size = backtrace (array, 20); + strings = backtrace_symbols (array, size); + + tmate_debug ("============ %zd stack frames ============", size); + + for (i = 1; i < size; i++) { +#if DEBUG + if (print_resolved_stack_frame(strings[i]) < 0) +#endif + tmate_debug("%s", strings[i]); + } + + free (strings); +} diff --git a/tmate.h b/tmate.h index 7a07facd..c9848045 100644 --- a/tmate.h +++ b/tmate.h @@ -109,4 +109,7 @@ extern void tmate_ssh_client_init(struct tmate_ssh_client *client, extern struct tmate_encoder *tmate_encoder; extern void tmate_client_start(void); +/* tmate-debug.c */ +extern void tmate_print_trace (void); + #endif From 2dca2c0fd5a592875260ef353f46cf81f9cb87f0 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 17:58:31 -0400 Subject: [PATCH 019/703] Synching winlinks idx instead of window ids --- cmd-link-window.c | 5 +++++ cmd-move-window.c | 5 +++++ cmd-swap-pane.c | 7 +++++++ cmd-swap-window.c | 5 +++++ cmd-unlink-window.c | 5 +++++ session.c | 13 +++++++++---- tmate-encoder.c | 22 ++++++++++------------ window.c | 10 ++++++++++ 8 files changed, 56 insertions(+), 16 deletions(-) diff --git a/cmd-link-window.c b/cmd-link-window.c index 2be8ace0..761dad18 100644 --- a/cmd-link-window.c +++ b/cmd-link-window.c @@ -41,6 +41,10 @@ const struct cmd_entry cmd_link_window_entry = { enum cmd_retval cmd_link_window_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE + cmdq_error(cmdq, "link window is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct session *src, *dst; struct winlink *wl; @@ -62,4 +66,5 @@ cmd_link_window_exec(struct cmd *self, struct cmd_q *cmdq) recalculate_sizes(); return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-move-window.c b/cmd-move-window.c index 1a147c7e..3c675b03 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -41,6 +41,10 @@ const struct cmd_entry cmd_move_window_entry = { enum cmd_retval cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE + cmdq_error(cmdq, "move window is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct session *src, *dst, *s; struct winlink *wl; @@ -73,4 +77,5 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) recalculate_sizes(); return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index d484f4e2..6990b5d4 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -85,6 +85,13 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) } server_unzoom_window(src_w); +#ifdef TMATE + if (src_w != dst_w) { + cmdq_error(cmdq, "swap pane on different window is not supported with tmate"); + return (CMD_RETURN_ERROR); + } +#endif + if (src_wp == dst_wp) return (CMD_RETURN_NORMAL); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index f9a2cb1b..a4eadfd9 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -41,6 +41,10 @@ const struct cmd_entry cmd_swap_window_entry = { enum cmd_retval cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE + cmdq_error(cmdq, "swap window is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; const char *target_src, *target_dst; struct session *src, *dst; @@ -84,4 +88,5 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) recalculate_sizes(); return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-unlink-window.c b/cmd-unlink-window.c index 39cdd8ed..46e9df36 100644 --- a/cmd-unlink-window.c +++ b/cmd-unlink-window.c @@ -39,6 +39,10 @@ const struct cmd_entry cmd_unlink_window_entry = { enum cmd_retval cmd_unlink_window_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE + cmdq_error(cmdq, "unlink window is not supported with tmate"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct winlink *wl; struct window *w; @@ -67,4 +71,5 @@ cmd_unlink_window_exec(struct cmd *self, struct cmd_q *cmdq) recalculate_sizes(); return (CMD_RETURN_NORMAL); +#endif } diff --git a/session.c b/session.c index ebe1178d..25bcdf11 100644 --- a/session.c +++ b/session.c @@ -273,10 +273,6 @@ session_new(struct session *s, session_group_synchronize_from(s); -#ifdef TMATE - tmate_sync_layout(); -#endif - return (wl); } @@ -293,6 +289,10 @@ session_attach(struct session *s, struct window *w, int idx, char **cause) winlink_set_window(wl, w); notify_window_linked(s, w); +#ifdef TMATE + tmate_sync_layout(); +#endif + session_group_synchronize_from(s); return (wl); } @@ -309,6 +309,11 @@ session_detach(struct session *s, struct winlink *wl) notify_window_unlinked(s, wl->window); winlink_stack_remove(&s->lastw, wl); winlink_remove(&s->windows, wl); + +#ifdef TMATE + tmate_sync_layout(); +#endif + session_group_synchronize_from(s); if (RB_EMPTY(&s->windows)) { session_destroy(s); diff --git a/tmate-encoder.c b/tmate-encoder.c index 57f6c110..cf5d1e29 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -44,13 +44,13 @@ void tmate_sync_layout(void) int num_panes = 0; int num_windows = 0; int active_pane_id = -1; - int active_window_id = -1; + int active_window_idx = -1; /* * We only allow one session, it makes our lives easier. * Especially when the HTML5 client will come along. - * We make no distinction between a winlink and its window. - * TODO send the winlink in the current session stack order. + * We make no distinction between a winlink and its window except + * that we send the winlink idx to draw the status bar properly. */ s = RB_MIN(sessions, &sessions); @@ -78,8 +78,11 @@ void tmate_sync_layout(void) if (!w) continue; + if (active_window_idx == -1) + active_window_idx = wl->idx; + pack(array, 4); - pack(int, w->id); + pack(int, wl->idx); pack(string, w->name); num_panes = 0; @@ -99,17 +102,12 @@ void tmate_sync_layout(void) active_pane_id = wp->id; } pack(int, active_pane_id); - - if (wl == s->curw) - active_window_id = w->id; } - if (active_window_id == -1) { - wl = RB_MIN(winlinks, &s->windows); - active_window_id = wl->window->id; - } + if (s->curw) + active_window_idx = s->curw->idx; - pack(int, active_window_id); + pack(int, active_window_idx); } void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) diff --git a/window.c b/window.c index 10629042..2108e001 100644 --- a/window.c +++ b/window.c @@ -372,6 +372,16 @@ window_remove_ref(struct window *w) void window_set_name(struct window *w, const char *new_name) { +#ifdef TMATE + /* + * We don't want to sync the layout too much. + * We might want to have some sort of timer for when to + * sync the layout. + */ + if (!strcmp(w->name, new_name)) + return; +#endif + free(w->name); w->name = xstrdup(new_name); notify_window_renamed(w); From a1d7bf7dc09726c4405a91f741996609aafa79fd Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 18:01:58 -0400 Subject: [PATCH 020/703] No reconnection for now --- tmate-ssh-client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index abc23fa3..ccb147ed 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -316,9 +316,12 @@ static void reconnect_session(struct tmate_ssh_client *client) disconnect_session(client); + /* Not yet implemented... */ +#if 0 tv.tv_sec = 1; tv.tv_usec = 0; evtimer_add(&client->ev_ssh_reconnect, &tv); +#endif } void tmate_ssh_client_init(struct tmate_ssh_client *client, From 844451c6cedc1a7a8660c56c12bc7fcaf1fb12ee Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 19:40:30 -0400 Subject: [PATCH 021/703] Show tmate messages in the status bar --- Makefile.am | 1 + options-table.c | 7 +++++ resize.c | 4 +++ server-client.c | 10 ++++++ status.c | 10 ++++++ tmate-decoder.c | 8 +++++ tmate-msg.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++ tmate-ssh-client.c | 69 +++++++++++++++++++++++----------------- tmate.h | 6 ++++ tmux.h | 3 ++ 10 files changed, 168 insertions(+), 28 deletions(-) create mode 100644 tmate-msg.c diff --git a/Makefile.am b/Makefile.am index beff8484..fa327d6c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -181,6 +181,7 @@ dist_tmate_SOURCES = \ tmate-ssh-client.c \ tmate-encoder.c \ tmate-decoder.c \ + tmate-msg.c \ tmate.c \ tmux.c \ tty-acs.c \ diff --git a/options-table.c b/options-table.c index 76a61619..7d0119e9 100644 --- a/options-table.c +++ b/options-table.c @@ -169,6 +169,13 @@ const struct options_table_entry session_options_table[] = { .default_num = 750 }, + { .name = "tmate-display-time", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 30000 + }, + { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, diff --git a/resize.c b/resize.c index 7f2ed236..91a8fa97 100644 --- a/resize.c +++ b/resize.c @@ -62,6 +62,10 @@ recalculate_sizes(void) if (c == NULL || c->flags & CLIENT_SUSPENDED) continue; if (c->session == s) { +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) + has_status = 1; +#endif if (c->tty.sx < ssx) ssx = c->tty.sx; if (has_status && diff --git a/server-client.c b/server-client.c index 89a4faa6..1a7e3ca0 100644 --- a/server-client.c +++ b/server-client.c @@ -271,6 +271,9 @@ server_client_status_timer(void) s = c->session; if (!options_get_number(&s->options, "status")) +#ifdef TMATE + if (!(c->flags & CLIENT_FORCE_STATUS)) +#endif continue; interval = options_get_number(&s->options, "status-interval"); @@ -395,6 +398,9 @@ server_client_handle_key(struct client *c, int key) /* Handle status line. */ if (!(c->flags & CLIENT_READONLY)) { +#ifdef TMATE + if (!(c->flags & CLIENT_FORCE_STATUS)) +#endif status_message_clear(c); server_clear_identify(c); } @@ -635,6 +641,10 @@ server_client_reset_state(struct client *c) tty_region(&c->tty, 0, c->tty.sy - 1); status = options_get_number(oo, "status"); +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) + status = 1; +#endif if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) tty_cursor(&c->tty, 0, 0); else { diff --git a/status.c b/status.c index f7a3420e..3eaa933d 100644 --- a/status.c +++ b/status.c @@ -171,6 +171,9 @@ status_redraw(struct client *c) /* No status line? */ if (c->tty.sy == 0 || !options_get_number(&s->options, "status")) +#ifdef TMATE + if (c->tty.sy == 0 || !(c->flags & CLIENT_FORCE_STATUS)) +#endif return (1); left = right = NULL; larrow = rarrow = 0; @@ -802,6 +805,7 @@ status_message_set(struct client *c, const char *fmt, ...) } } + /* FIXME tmux: session can be NULL */ delay = options_get_number(&c->session->options, "display-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; @@ -826,6 +830,12 @@ status_message_clear(struct client *c) c->message_string = NULL; c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); +#ifdef TMATE + if (c->flags & CLIENT_FORCE_STATUS) { + c->flags &= ~CLIENT_FORCE_STATUS; + recalculate_sizes(); + } +#endif c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ screen_reinit(&c->status); diff --git a/tmate-decoder.c b/tmate-decoder.c index 1abe700e..00c4890f 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -74,6 +74,13 @@ static char *unpack_string(struct tmate_unpacker *uk) return alloc_buf; } +static void tmate_reply_header(struct tmate_unpacker *uk) +{ + unsigned long flags = unpack_int(uk); + char *remote_session = unpack_string(uk); + + tmate_status_message("Remote session: %s", remote_session); +} static void tmate_client_pane_key(struct tmate_unpacker *uk) { @@ -138,6 +145,7 @@ static void handle_message(msgpack_object obj) init_unpacker(uk, obj); switch (unpack_int(uk)) { + case TMATE_REPLY_HEADER: tmate_reply_header(uk); break; case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; case TMATE_CLIENT_CMD: tmate_client_cmd(uk); break; diff --git a/tmate-msg.c b/tmate-msg.c new file mode 100644 index 00000000..7fe0c0b8 --- /dev/null +++ b/tmate-msg.c @@ -0,0 +1,78 @@ +#include +#include "tmate.h" + +void status_message_callback(int, short, void *); + +/* Very similar to status.c:status_message_set */ + +static void tmate_status_message_client(struct client *c, const char *message) +{ + struct timeval tv; + struct session *s = c->session; + struct message_entry *msg; + int delay; + u_int i, limit; + + status_prompt_clear(c); + status_message_clear(c); + + xasprintf(&c->message_string, "tmate: %s", message); + + ARRAY_EXPAND(&c->message_log, 1); + msg = &ARRAY_LAST(&c->message_log); + msg->msg_time = time(NULL); + msg->msg = xstrdup(c->message_string); + + if (!s) + return; + + limit = options_get_number(&s->options, "message-limit"); + if (ARRAY_LENGTH(&c->message_log) > limit) { + limit = ARRAY_LENGTH(&c->message_log) - limit; + for (i = 0; i < limit; i++) { + msg = &ARRAY_FIRST(&c->message_log); + free(msg->msg); + ARRAY_REMOVE(&c->message_log, 0); + } + } + + delay = options_get_number(&c->session->options, "tmate-display-time"); + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; + + if (event_initialized (&c->message_timer)) + evtimer_del(&c->message_timer); + evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); + + c->flags |= CLIENT_STATUS | CLIENT_FORCE_STATUS; + + recalculate_sizes(); +} + +void __tmate_status_message(const char *fmt, va_list ap) +{ + struct client *c; + unsigned int i; + char *message; + + xvasprintf(&message, fmt, ap); + tmate_debug("%s", message); + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c && !(c->flags & CLIENT_READONLY)) + tmate_status_message_client(c, message); + } + + free(message); +} + +void printflike1 tmate_status_message(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __tmate_status_message(fmt, ap); + va_end(ap); +} diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index ccb147ed..4ca1a914 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -11,8 +11,10 @@ static void consume_channel(struct tmate_ssh_client *client); static void flush_input_stream(struct tmate_ssh_client *client); static void __flush_input_stream(evutil_socket_t fd, short what, void *arg); static void __on_session_event(evutil_socket_t fd, short what, void *arg); -static void disconnect_session(struct tmate_ssh_client *client); -static void reconnect_session(struct tmate_ssh_client *client); +static void printflike2 disconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...); +static void printflike2 reconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...); static void log_function(ssh_session session, int priority, const char *message, void *userdata) @@ -56,9 +58,8 @@ static void consume_channel(struct tmate_ssh_client *client) tmate_decoder_get_buffer(client->decoder, &buf, &len); len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); if (len < 0) { - tmate_debug("Error reading from channel: %s", - ssh_get_error(client->session)); - reconnect_session(client); + reconnect_session(client, "Error reading from channel: %s", + ssh_get_error(client->session)); break; } @@ -107,7 +108,7 @@ static void on_session_event(struct tmate_ssh_client *client) ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); - tmate_debug("Connecting..."); + tmate_status_message("Connecting to %s...", TMATE_HOST); client->state = SSH_CONNECT; /* fall through */ @@ -117,8 +118,8 @@ static void on_session_event(struct tmate_ssh_client *client) register_session_fd_event(client); return; case SSH_ERROR: - tmate_debug("Error connecting: %s", ssh_get_error(session)); - reconnect_session(client); + reconnect_session(client, "Error connecting: %s", + ssh_get_error(session)); return; case SSH_OK: register_session_fd_event(client); @@ -129,8 +130,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_SERVER: if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) { - tmate_debug("Cannnot authenticate server"); - disconnect_session(client); + disconnect_session(client, "Cannnot authenticate server"); return; } @@ -165,8 +165,7 @@ static void on_session_event(struct tmate_ssh_client *client) free(hash_str); if (!match) { - tmate_debug("Cannnot authenticate server"); - disconnect_session(client); + disconnect_session(client, "Cannnot authenticate server"); return; } @@ -180,12 +179,11 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: - tmate_debug("Access denied. Try again later."); - disconnect_session(client); + disconnect_session(client, "Access denied. Try again later."); return; case SSH_AUTH_ERROR: - tmate_debug("Auth error: %s", ssh_get_error(session)); - reconnect_session(client); + reconnect_session(client, "Auth error: %s", + ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: tmate_debug("Auth successful"); @@ -198,8 +196,8 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - tmate_debug("Error opening channel: %s", ssh_get_error(session)); - reconnect_session(client); + reconnect_session(client, "Error opening channel: %s", + ssh_get_error(session)); return; case SSH_OK: tmate_debug("Session opened, initalizing tmate"); @@ -212,8 +210,8 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - tmate_debug("Error initializing tmate: %s", ssh_get_error(session)); - reconnect_session(client); + reconnect_session(client, "Error initializing tmate: %s", + ssh_get_error(session)); return; case SSH_OK: tmate_debug("Ready"); @@ -230,8 +228,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_READY: consume_channel(client); if (!ssh_is_connected(session)) { - tmate_debug("Disconnected"); - reconnect_session(client); + reconnect_session(client, "Disconnected"); return; } } @@ -255,9 +252,8 @@ static void flush_input_stream(struct tmate_ssh_client *client) written = ssh_channel_write(client->channel, buf, len); if (written < 0) { - tmate_debug("Error writing to channel: %s", - ssh_get_error(client->session)); - reconnect_session(client); + reconnect_session(client, "Error writing to channel: %s", + ssh_get_error(client->session)); return; } @@ -275,8 +271,11 @@ static void __on_session_event(evutil_socket_t fd, short what, void *arg) on_session_event(arg); } -static void disconnect_session(struct tmate_ssh_client *client) +static void __disconnect_session(struct tmate_ssh_client *client, + const char *fmt, va_list va) { + __tmate_status_message(fmt, va); + if (event_initialized(&client->ev_ssh)) { event_del(&client->ev_ssh); client->ev_ssh.ev_flags = 0; @@ -297,6 +296,16 @@ static void disconnect_session(struct tmate_ssh_client *client) client->state = SSH_NONE; } +static void printflike2 disconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __disconnect_session(client, fmt, ap); + va_end(ap); +} + static void connect_session(struct tmate_ssh_client *client) { if (!client->session) { @@ -310,11 +319,15 @@ static void on_reconnect_timer(evutil_socket_t fd, short what, void *arg) connect_session(arg); } -static void reconnect_session(struct tmate_ssh_client *client) +static void printflike2 reconnect_session(struct tmate_ssh_client *client, + const char *fmt, ...) { struct timeval tv; + va_list ap; - disconnect_session(client); + va_start(ap, fmt); + __disconnect_session(client, fmt, ap); + va_end(ap); /* Not yet implemented... */ #if 0 diff --git a/tmate.h b/tmate.h index c9848045..612fc184 100644 --- a/tmate.h +++ b/tmate.h @@ -44,6 +44,7 @@ extern void tmate_status(const char *left, const char *right); /* tmate-decoder.c */ enum tmate_client_commands { + TMATE_REPLY_HEADER, TMATE_CLIENT_PANE_KEY, TMATE_CLIENT_RESIZE, TMATE_CLIENT_CMD, @@ -112,4 +113,9 @@ extern void tmate_client_start(void); /* tmate-debug.c */ extern void tmate_print_trace (void); +/* tmate-msg.c */ + +extern void __tmate_status_message(const char *fmt, va_list ap); +extern void printflike1 tmate_status_message(const char *fmt, ...); + #endif diff --git a/tmux.h b/tmux.h index c8afa93e..7a3cbb3e 100644 --- a/tmux.h +++ b/tmux.h @@ -1332,6 +1332,9 @@ struct client { #define CLIENT_REDRAWWINDOW 0x1000 #define CLIENT_CONTROL 0x2000 #define CLIENT_FOCUSED 0x4000 +#ifdef TMATE +#define CLIENT_FORCE_STATUS 0x800000 +#endif int flags; struct event identify_timer; From e52312ec3c404883a51d0112aa85ecf215b3c8cf Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 20:09:43 -0400 Subject: [PATCH 022/703] Report failed commands to slave which will in turn notify the proper client --- cmd-queue.c | 9 ++++++++- tmate-decoder.c | 21 +++++++++++++++++---- tmate-encoder.c | 12 ++++++++++-- tmate.h | 8 +++++--- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 8a274305..7fe2793a 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -137,7 +137,14 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) va_end(ap); if (c == NULL) { +#ifdef TMATE + if (cmd->file && cmd->line) + xasprintf(&cause, "%s:%u: %s", cmd->file, cmd->line, msg); + else + xasprintf(&cause, "%s", msg); +#else xasprintf(&cause, "%s:%u: %s", cmd->file, cmd->line, msg); +#endif ARRAY_ADD(&cfg_causes, cause); } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { evbuffer_add(c->stderr_data, msg, msglen); @@ -228,7 +235,7 @@ cmdq_continue(struct cmd_q *cmdq) #ifdef TMATE if (tmate_should_replicate_cmd(cmdq->cmd->entry)) - tmate_cmd(s); + tmate_exec_cmd(s); #endif guard = cmdq_guard(cmdq, "begin"); diff --git a/tmate-decoder.c b/tmate-decoder.c index 00c4890f..0bdca7d4 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -115,23 +115,36 @@ static void tmate_client_resize(struct tmate_unpacker *uk) /* TODO Handle reconnection cases */ } -static void tmate_client_cmd(struct tmate_unpacker *uk) +static void tmate_client_exec_cmd(struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; - char *cmd_str; char *cause; - cmd_str = unpack_string(uk); + int client_id = unpack_int(uk); + char *cmd_str = unpack_string(uk); + if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) { + tmate_failed_cmd(client_id, cause); free(cause); goto out; } + /* error messages land in cfg_causes */ + ARRAY_FREE(&cfg_causes); + cmd_q = cmdq_new(NULL); cmdq_run(cmd_q, cmdlist); cmd_list_free(cmdlist); cmdq_free(cmd_q); + + if (!ARRAY_EMPTY(&cfg_causes)) { + cause = ARRAY_ITEM(&cfg_causes, 0); + tmate_failed_cmd(client_id, cause); + free(cause); + ARRAY_FREE(&cfg_causes); + } + out: free(cmd_str); } @@ -148,7 +161,7 @@ static void handle_message(msgpack_object obj) case TMATE_REPLY_HEADER: tmate_reply_header(uk); break; case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; - case TMATE_CLIENT_CMD: tmate_client_cmd(uk); break; + case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(uk); break; default: decoder_error(); } } diff --git a/tmate-encoder.c b/tmate-encoder.c index cf5d1e29..0e4a2c09 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -147,13 +147,21 @@ int tmate_should_replicate_cmd(const struct cmd_entry *cmd) return 0; } -void tmate_cmd(const char *cmd) +void tmate_exec_cmd(const char *cmd) { pack(array, 2); - pack(int, TMATE_CMD); + pack(int, TMATE_EXEC_CMD); pack(string, cmd); } +void tmate_failed_cmd(int client_id, const char *cause) +{ + pack(array, 3); + pack(int, TMATE_FAILED_CMD); + pack(int, client_id); + pack(string, cause); +} + void tmate_status(const char *left, const char *right) { static char *old_left, *old_right; diff --git a/tmate.h b/tmate.h index 612fc184..0ff4cd23 100644 --- a/tmate.h +++ b/tmate.h @@ -22,7 +22,8 @@ enum tmate_commands { TMATE_HEADER, TMATE_SYNC_LAYOUT, TMATE_PTY_DATA, - TMATE_CMD, + TMATE_EXEC_CMD, + TMATE_FAILED_CMD, TMATE_STATUS, }; @@ -38,7 +39,8 @@ extern void tmate_write_header(void); extern void tmate_sync_layout(void); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); -extern void tmate_cmd(const char *cmd); +extern void tmate_exec_cmd(const char *cmd); +extern void tmate_failed_cmd(int client_id, const char *cause); extern void tmate_status(const char *left, const char *right); /* tmate-decoder.c */ @@ -47,7 +49,7 @@ enum tmate_client_commands { TMATE_REPLY_HEADER, TMATE_CLIENT_PANE_KEY, TMATE_CLIENT_RESIZE, - TMATE_CLIENT_CMD, + TMATE_CLIENT_EXEC_CMD, }; struct tmate_decoder { From fc4eaeb89f5ce931461e1ba666de758dfc5863a5 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 13 Jun 2013 02:28:46 -0400 Subject: [PATCH 023/703] TERM defaults to screen-256color --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 7d0119e9..504bcfca 100644 --- a/options-table.c +++ b/options-table.c @@ -132,7 +132,7 @@ const struct options_table_entry session_options_table[] = { { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, - .default_str = "screen" + .default_str = "screen-256color" }, { .name = "destroy-unattached", From edd194c23cb965eb1cce38907e3b57ea3faeb299 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 12 Jun 2013 22:38:41 -0400 Subject: [PATCH 024/703] Synchronize copy-mode --- tmate-decoder.c | 1 - tmate-encoder.c | 36 ++++++++++++++++++++ tmate.h | 2 ++ tmux.h | 64 +++++++++++++++++++++++++++++++++++ window-copy.c | 88 +++++++++++++------------------------------------ window.c | 2 ++ 6 files changed, 126 insertions(+), 67 deletions(-) diff --git a/tmate-decoder.c b/tmate-decoder.c index 0bdca7d4..e18e6184 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -153,7 +153,6 @@ static void handle_message(msgpack_object obj) { struct tmate_unpacker _uk; struct tmate_unpacker *uk = &_uk; - int cmd; init_unpacker(uk, obj); diff --git a/tmate-encoder.c b/tmate-encoder.c index 0e4a2c09..7cd11dbb 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -180,3 +180,39 @@ void tmate_status(const char *left, const char *right) old_left = xstrdup(left); old_right = xstrdup(right); } + +void tmate_sync_copy_mode(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + pack(array, 3); + pack(int, TMATE_SYNC_COPY_MODE); + + pack(int, wp->id); + + if (wp->mode != &window_copy_mode) { + pack(array, 0); + return; + } + pack(array, 5); + + pack(int, data->oy); + pack(int, data->cx); + pack(int, data->cy); + + if (data->screen.sel.flag) { + pack(array, 3); + pack(int, data->selx); + pack(int, data->sely); + pack(int, data->rectflag); + } else + pack(array, 0); + + if (data->inputprompt) { + pack(array, 3); + pack(int, data->inputtype); + pack(string, data->inputprompt); + pack(string, data->inputstr); + } else + pack(array, 0); +} diff --git a/tmate.h b/tmate.h index 0ff4cd23..9030c90e 100644 --- a/tmate.h +++ b/tmate.h @@ -25,6 +25,7 @@ enum tmate_commands { TMATE_EXEC_CMD, TMATE_FAILED_CMD, TMATE_STATUS, + TMATE_SYNC_COPY_MODE, }; struct tmate_encoder { @@ -42,6 +43,7 @@ extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); extern void tmate_exec_cmd(const char *cmd); extern void tmate_failed_cmd(int client_id, const char *cause); extern void tmate_status(const char *left, const char *right); +extern void tmate_sync_copy_mode(struct window_pane *wp); /* tmate-decoder.c */ diff --git a/tmux.h b/tmux.h index 7a3cbb3e..c28cb9d9 100644 --- a/tmux.h +++ b/tmux.h @@ -2235,6 +2235,70 @@ void layout_set_active_changed(struct window *); extern const struct window_mode window_clock_mode; /* window-copy.c */ +enum window_copy_input_type { + WINDOW_COPY_OFF, + WINDOW_COPY_NUMERICPREFIX, + WINDOW_COPY_SEARCHUP, + WINDOW_COPY_SEARCHDOWN, + WINDOW_COPY_JUMPFORWARD, + WINDOW_COPY_JUMPBACK, + WINDOW_COPY_JUMPTOFORWARD, + WINDOW_COPY_JUMPTOBACK, + WINDOW_COPY_GOTOLINE, +}; + +/* + * Copy-mode's visible screen (the "screen" field) is filled from one of + * two sources: the original contents of the pane (used when we + * actually enter via the "copy-mode" command, to copy the contents of + * the current pane), or else a series of lines containing the output + * from an output-writing tmux command (such as any of the "show-*" or + * "list-*" commands). + * + * In either case, the full content of the copy-mode grid is pointed at + * by the "backing" field, and is copied into "screen" as needed (that + * is, when scrolling occurs). When copy-mode is backed by a pane, + * backing points directly at that pane's screen structure (&wp->base); + * when backed by a list of output-lines from a command, it points at + * a newly-allocated screen structure (which is deallocated when the + * mode ends). + */ +struct window_copy_mode_data { + struct screen screen; + + struct screen *backing; + int backing_written; /* backing display has started */ + + struct mode_key_data mdata; + + u_int oy; + + u_int selx; + u_int sely; + + u_int rectflag; /* are we in rectangle copy mode? */ + + u_int cx; + u_int cy; + + u_int lastcx; /* position in last line with content */ + u_int lastsx; /* size of last line with content */ + + enum window_copy_input_type inputtype; + const char *inputprompt; + char *inputstr; + + int numprefix; + + enum window_copy_input_type searchtype; + char *searchstr; + + enum window_copy_input_type jumptype; + char jumpchar; +}; + + + extern const struct window_mode window_copy_mode; void window_copy_init_from_pane(struct window_pane *); void window_copy_init_for_output(struct window_pane *); diff --git a/window-copy.c b/window-copy.c index 51a8f108..b19f77b5 100644 --- a/window-copy.c +++ b/window-copy.c @@ -22,6 +22,7 @@ #include #include "tmux.h" +#include "tmate.h" struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); @@ -89,68 +90,6 @@ const struct window_mode window_copy_mode = { NULL, }; -enum window_copy_input_type { - WINDOW_COPY_OFF, - WINDOW_COPY_NUMERICPREFIX, - WINDOW_COPY_SEARCHUP, - WINDOW_COPY_SEARCHDOWN, - WINDOW_COPY_JUMPFORWARD, - WINDOW_COPY_JUMPBACK, - WINDOW_COPY_JUMPTOFORWARD, - WINDOW_COPY_JUMPTOBACK, - WINDOW_COPY_GOTOLINE, -}; - -/* - * Copy-mode's visible screen (the "screen" field) is filled from one of - * two sources: the original contents of the pane (used when we - * actually enter via the "copy-mode" command, to copy the contents of - * the current pane), or else a series of lines containing the output - * from an output-writing tmux command (such as any of the "show-*" or - * "list-*" commands). - * - * In either case, the full content of the copy-mode grid is pointed at - * by the "backing" field, and is copied into "screen" as needed (that - * is, when scrolling occurs). When copy-mode is backed by a pane, - * backing points directly at that pane's screen structure (&wp->base); - * when backed by a list of output-lines from a command, it points at - * a newly-allocated screen structure (which is deallocated when the - * mode ends). - */ -struct window_copy_mode_data { - struct screen screen; - - struct screen *backing; - int backing_written; /* backing display has started */ - - struct mode_key_data mdata; - - u_int oy; - - u_int selx; - u_int sely; - - u_int rectflag; /* are we in rectangle copy mode? */ - - u_int cx; - u_int cy; - - u_int lastcx; /* position in last line with content */ - u_int lastsx; /* size of last line with content */ - - enum window_copy_input_type inputtype; - const char *inputprompt; - char *inputstr; - - int numprefix; - - enum window_copy_input_type searchtype; - char *searchstr; - - enum window_copy_input_type jumptype; - char jumpchar; -}; - struct screen * window_copy_init(struct window_pane *wp) { @@ -223,6 +162,8 @@ window_copy_init_from_pane(struct window_pane *wp) window_copy_write_line(wp, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy); screen_write_stop(&ctx); + + tmate_sync_copy_mode(wp); } void @@ -359,8 +300,8 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) window_copy_redraw_screen(wp); } -void -window_copy_key(struct window_pane *wp, struct session *sess, int key) +static void +__window_copy_key(struct window_pane *wp, struct session *sess, int key) { const char *word_separators; struct window_copy_mode_data *data = wp->modedata; @@ -739,6 +680,13 @@ input_off: window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); } +void +window_copy_key(struct window_pane *wp, struct session *sess, int key) +{ + __window_copy_key(wp, sess, key); + tmate_sync_copy_mode(wp); +} + int window_copy_key_input(struct window_pane *wp, int key) { @@ -826,8 +774,8 @@ window_copy_key_numeric_prefix(struct window_pane *wp, int key) return (0); } -void -window_copy_mouse( +static void +__window_copy_mouse( struct window_pane *wp, struct session *sess, struct mouse_event *m) { struct window_copy_mode_data *data = wp->modedata; @@ -888,6 +836,14 @@ reset_mode: } } +void +window_copy_mouse( + struct window_pane *wp, struct session *sess, struct mouse_event *m) +{ + __window_copy_mouse(wp, sess, m); + tmate_sync_copy_mode(wp); +} + void window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) { diff --git a/window.c b/window.c index 2108e001..5757cf88 100644 --- a/window.c +++ b/window.c @@ -1045,6 +1045,8 @@ window_pane_reset_mode(struct window_pane *wp) wp->screen = &wp->base; wp->flags |= PANE_REDRAW; + + tmate_sync_copy_mode(wp); } void From a50dcb09b56f6ee673d7a90c2682738cb884ba99 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 13 Jun 2013 04:31:25 -0400 Subject: [PATCH 025/703] Notification messages --- tmate-decoder.c | 11 +++++------ tmate-msg.c | 2 +- tmate.h | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/tmate-decoder.c b/tmate-decoder.c index e18e6184..3e780dc5 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -74,12 +74,11 @@ static char *unpack_string(struct tmate_unpacker *uk) return alloc_buf; } -static void tmate_reply_header(struct tmate_unpacker *uk) +static void tmate_notify(struct tmate_unpacker *uk) { - unsigned long flags = unpack_int(uk); - char *remote_session = unpack_string(uk); - - tmate_status_message("Remote session: %s", remote_session); + char *msg = unpack_string(uk); + tmate_status_message("%s", msg); + free(msg); } static void tmate_client_pane_key(struct tmate_unpacker *uk) @@ -157,7 +156,7 @@ static void handle_message(msgpack_object obj) init_unpacker(uk, obj); switch (unpack_int(uk)) { - case TMATE_REPLY_HEADER: tmate_reply_header(uk); break; + case TMATE_NOTIFY: tmate_notify(uk); break; case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(uk); break; diff --git a/tmate-msg.c b/tmate-msg.c index 7fe0c0b8..2ab663f6 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -16,7 +16,7 @@ static void tmate_status_message_client(struct client *c, const char *message) status_prompt_clear(c); status_message_clear(c); - xasprintf(&c->message_string, "tmate: %s", message); + xasprintf(&c->message_string, "[tmate] %s", message); ARRAY_EXPAND(&c->message_log, 1); msg = &ARRAY_LAST(&c->message_log); diff --git a/tmate.h b/tmate.h index 9030c90e..63cbb4b6 100644 --- a/tmate.h +++ b/tmate.h @@ -48,7 +48,7 @@ extern void tmate_sync_copy_mode(struct window_pane *wp); /* tmate-decoder.c */ enum tmate_client_commands { - TMATE_REPLY_HEADER, + TMATE_NOTIFY, TMATE_CLIENT_PANE_KEY, TMATE_CLIENT_RESIZE, TMATE_CLIENT_EXEC_CMD, From 5d847fb0aa1f1f3ac49b4f880dac2fc57acc09fe Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 15:35:39 -0400 Subject: [PATCH 026/703] Random socket path (we don't support multi sessions) --- tmux.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tmux.c b/tmux.c index 65c94389..20c6b474 100644 --- a/tmux.c +++ b/tmux.c @@ -165,6 +165,7 @@ makesocketpath(const char *label) char base[MAXPATHLEN], realbase[MAXPATHLEN], *path, *s; struct stat sb; u_int uid; + int do_random_label = 1; uid = getuid(); if ((s = getenv("TMPDIR")) == NULL || *s == '\0') @@ -189,7 +190,16 @@ makesocketpath(const char *label) if (realpath(base, realbase) == NULL) strlcpy(realbase, base, sizeof realbase); + if (!label) { + do_random_label = 1; + label = "XXXXXX"; + } + xasprintf(&path, "%s/%s", realbase, label); + + if (do_random_label) + mktemp(path); + return (path); } @@ -381,16 +391,12 @@ main(int argc, char **argv) if (label == NULL) { if (environ_path != NULL) path = xstrdup(environ_path); - else - label = xstrdup("default"); } /* -L or default set. */ - if (label != NULL) { - if ((path = makesocketpath(label)) == NULL) { - fprintf(stderr, "can't create socket\n"); - exit(1); - } + if ((path = makesocketpath(label)) == NULL) { + fprintf(stderr, "can't create socket\n"); + exit(1); } } free(label); From 12d681d8cc78cb59b3f71afc2a1158d528a4e124 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 21:09:58 -0400 Subject: [PATCH 027/703] Change connection string tmate.io -> master.tmate.io --- tmate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate.h b/tmate.h index 63cbb4b6..2e0606a1 100644 --- a/tmate.h +++ b/tmate.h @@ -72,7 +72,7 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); #define TMATE_HOST "localhost" #define TMATE_PORT 2200 #else -#define TMATE_HOST "tmate.io" +#define TMATE_HOST "master.tmate.io" #define TMATE_PORT 22 #define TMATE_HOST_DSA_KEY "f5:26:31:c3:8a:78:6e:5c:77:74:0f:41:5b:5f:21:88" #define TMATE_HOST_RSA_KEY "af:2d:81:c1:fe:49:70:2d:7f:09:a9:d7:4b:32:e3:be" From 16dd13979e0d65119410cd280af226d5db63e6b1 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 21:23:04 -0400 Subject: [PATCH 028/703] Ubuntu complains without the README --- README | 1 + 1 file changed, 1 insertion(+) create mode 120000 README diff --git a/README b/README new file mode 120000 index 00000000..42061c01 --- /dev/null +++ b/README @@ -0,0 +1 @@ +README.md \ No newline at end of file From 53355a0b92ed5d11a465fb31f87bb29d55652388 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 22:38:39 -0400 Subject: [PATCH 029/703] Bug fix: $TMUX wasn't being reused properly --- tmux.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tmux.c b/tmux.c index 20c6b474..7063d912 100644 --- a/tmux.c +++ b/tmux.c @@ -394,9 +394,11 @@ main(int argc, char **argv) } /* -L or default set. */ - if ((path = makesocketpath(label)) == NULL) { - fprintf(stderr, "can't create socket\n"); - exit(1); + if (!path) { + if ((path = makesocketpath(label)) == NULL) { + fprintf(stderr, "can't create socket\n"); + exit(1); + } } } free(label); From 80f9e84b83bd4193a320d70cf77930853f7752d1 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 22:10:56 -0400 Subject: [PATCH 030/703] Release 1.8.3 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5be42271..468dd16a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmux, 1.8) +AC_INIT(tmate, 1.8.3) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) From 8f19552af036e9868d1cf761aba8ceedc7d54956 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 23:10:24 -0400 Subject: [PATCH 031/703] Better error message when SSH keys are not setup --- tmate-ssh-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 4ca1a914..ef01777f 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -179,7 +179,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: - disconnect_session(client, "Access denied. Try again later."); + disconnect_session(client, "Access denied. Check your SSH keys."); return; case SSH_AUTH_ERROR: reconnect_session(client, "Auth error: %s", From 7316ed33e1114f27db77790f55984caede5318fe Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 23:52:31 -0400 Subject: [PATCH 032/703] [libssh] auth: If the agent fails, fall back to regular path It's causing issues on MacOSX when ssh_agent_get_ident_count() reports "Agent count: 0". --- libssh/src/auth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libssh/src/auth.c b/libssh/src/auth.c index 9d099a53..5b8f748f 100644 --- a/libssh/src/auth.c +++ b/libssh/src/auth.c @@ -1109,7 +1109,7 @@ int ssh_userauth_publickey_auto(ssh_session session, #ifndef _WIN32 /* Try authentication with ssh-agent first */ rc = ssh_userauth_agent(session, username); - if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_SUCCESS) { + if (rc == SSH_AUTH_SUCCESS) { return rc; } if (rc == SSH_AUTH_AGAIN) From 632553bdc932acb8594426a49d2d05c0d3f894b6 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 23:58:08 -0400 Subject: [PATCH 033/703] SSH log level is tweakable with -v --- tmate-ssh-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index ef01777f..f4e55e27 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -79,7 +79,7 @@ static void on_session_event(struct tmate_ssh_client *client) char *hash_str; int match; - int verbosity = SSH_LOG_RARE; + int verbosity = SSH_LOG_NOLOG + debug_level; int port = TMATE_PORT; ssh_session session = client->session; From edea6cf141b8413276a202ec65a2b3797505627f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 14 Jun 2013 23:59:26 -0400 Subject: [PATCH 034/703] Release 1.8.4 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 468dd16a..20914916 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmate, 1.8.3) +AC_INIT(tmate, 1.8.4) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) From ddf0a9dd553bba444d3b39b90ee1dbd19cf9e735 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 17 Jun 2013 17:34:34 -0400 Subject: [PATCH 035/703] Fix compile bug libssh/include/libssh/libssh.h:115: error: previous declaration of 'ssh_session' was here tmate.h:83: error: redefinition of typedef 'ssh_channel' libssh/include/libssh/libssh.h:110: error: previous declaration of 'ssh_channel' was here make: *** [tmate-ssh-client.o] Error 1 --- tmate.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tmate.h b/tmate.h index 2e0606a1..899a5f17 100644 --- a/tmate.h +++ b/tmate.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "tmux.h" @@ -79,9 +80,6 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); #define TMATE_HOST_ECDSA_KEY "c7:a1:51:36:d2:bb:35:4b:0a:1a:c0:43:97:74:ea:42" #endif -typedef struct ssh_session_struct* ssh_session; -typedef struct ssh_channel_struct* ssh_channel; - enum tmate_ssh_client_state_types { SSH_NONE, SSH_INIT, From 59cab76dd89d96cdcc446567711462eb1c53ca35 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 26 Jun 2013 00:54:27 -0400 Subject: [PATCH 036/703] Fix copy mode when the buffer size is smaller than 2000 --- tmate-encoder.c | 3 ++- tmate.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tmate-encoder.c b/tmate-encoder.c index 7cd11dbb..3db8e8d7 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -203,7 +203,8 @@ void tmate_sync_copy_mode(struct window_pane *wp) if (data->screen.sel.flag) { pack(array, 3); pack(int, data->selx); - pack(int, data->sely); + pack(int, -data->sely + screen_hsize(data->backing) + + screen_size_y(data->backing) - 1); pack(int, data->rectflag); } else pack(array, 0); diff --git a/tmate.h b/tmate.h index 899a5f17..dea7e56d 100644 --- a/tmate.h +++ b/tmate.h @@ -17,7 +17,7 @@ #define TMATE_MAX_MESSAGE_SIZE (16*1024) -#define TMATE_PROTOCOL_VERSION 1 +#define TMATE_PROTOCOL_VERSION 2 enum tmate_commands { TMATE_HEADER, From c9a6e2560af951ed450732d80844309d8778eaef Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 26 Jun 2013 02:05:13 -0400 Subject: [PATCH 037/703] Synchronize the list binding pane (bind-key + ?) --- cmd-list-keys.c | 10 ++++++++++ cmd-queue.c | 1 + tmate-encoder.c | 11 ++++++++++- tmate.h | 2 ++ window-copy.c | 10 ++++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 78998b66..f0bcece3 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -49,6 +49,16 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) size_t used; int width, keywidth; +#ifdef TMATE + /* XXX TODO Really nasty hack, we really need our own client instance... */ + struct client fake_client; + if (!cmdq->client) { + cmdq->client = &fake_client; + cmdq->client->flags = 0; + cmdq->client->session = RB_MIN(sessions, &sessions); + } +#endif + if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); diff --git a/cmd-queue.c b/cmd-queue.c index 7fe2793a..44602114 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -82,6 +82,7 @@ cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) window_pane_reset_mode(w->active); window_pane_set_mode(w->active, &window_copy_mode); window_copy_init_for_output(w->active); + tmate_sync_copy_mode(w->active); } window_copy_vadd(w->active, fmt, ap); } diff --git a/tmate-encoder.c b/tmate-encoder.c index 3db8e8d7..6ac9a590 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -194,7 +194,8 @@ void tmate_sync_copy_mode(struct window_pane *wp) pack(array, 0); return; } - pack(array, 5); + pack(array, 6); + pack(int, data->backing == &wp->base); pack(int, data->oy); pack(int, data->cx); @@ -217,3 +218,11 @@ void tmate_sync_copy_mode(struct window_pane *wp) } else pack(array, 0); } + +void tmate_write_copy_mode(struct window_pane *wp, const char *str) +{ + pack(array, 3); + pack(int, TMATE_WRITE_COPY_MODE); + pack(int, wp->id); + pack(string, str); +} diff --git a/tmate.h b/tmate.h index dea7e56d..f4dcde04 100644 --- a/tmate.h +++ b/tmate.h @@ -27,6 +27,7 @@ enum tmate_commands { TMATE_FAILED_CMD, TMATE_STATUS, TMATE_SYNC_COPY_MODE, + TMATE_WRITE_COPY_MODE, }; struct tmate_encoder { @@ -45,6 +46,7 @@ extern void tmate_exec_cmd(const char *cmd); extern void tmate_failed_cmd(int client_id, const char *cause); extern void tmate_status(const char *left, const char *right); extern void tmate_sync_copy_mode(struct window_pane *wp); +extern void tmate_write_copy_mode(struct window_pane *wp, const char *str); /* tmate-decoder.c */ diff --git a/window-copy.c b/window-copy.c index b19f77b5..d3af1efb 100644 --- a/window-copy.c +++ b/window-copy.c @@ -216,6 +216,9 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) struct grid_cell gc; int utf8flag; u_int old_hsize; +#ifdef TMATE + char *msg; +#endif if (backing == &wp->base) return; @@ -234,7 +237,14 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) screen_write_linefeed(&back_ctx, 0); } else data->backing_written = 1; +#ifdef TMATE + xvasprintf(&msg, fmt, ap); + screen_write_nputs(&back_ctx, 0, &gc, utf8flag, "%s", msg); + tmate_write_copy_mode(wp, msg); + free(msg); +#else screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap); +#endif screen_write_stop(&back_ctx); data->oy += screen_hsize(data->backing) - old_hsize; From 7be65e79dbd078fc508313e454f1729c0d9361ed Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 11 Jul 2013 03:19:19 -0400 Subject: [PATCH 038/703] localhost localhost -> 127.0.0.1 for dev mode (ipv6) --- tmate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate.h b/tmate.h index f4dcde04..57d5bcc5 100644 --- a/tmate.h +++ b/tmate.h @@ -72,7 +72,7 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); /* tmate-ssh-client.c */ #ifdef DEVENV -#define TMATE_HOST "localhost" +#define TMATE_HOST "127.0.0.1" #define TMATE_PORT 2200 #else #define TMATE_HOST "master.tmate.io" From 2e3661a0f6eb1138644907db32de592c22c59c42 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 11 Jul 2013 03:46:02 -0400 Subject: [PATCH 039/703] Update libssh --- libssh/examples/libssh_scp.c | 18 ++- libssh/examples/sample.c | 7 +- libssh/examples/samplesshd-tty.c | 3 + libssh/examples/scp_download.c | 14 +- libssh/include/libssh/pki.h | 1 + libssh/include/libssh/server.h | 2 + libssh/include/libssh/socket.h | 4 +- libssh/src/agent.c | 30 ++++- libssh/src/auth.c | 7 +- libssh/src/channels1.c | 7 +- libssh/src/client.c | 5 +- libssh/src/connect.c | 36 +++++- libssh/src/dh.c | 10 +- libssh/src/ecdh.c | 162 +++++++++++++++++------- libssh/src/kex.c | 1 + libssh/src/kex1.c | 5 +- libssh/src/known_hosts.c | 2 +- libssh/src/messages.c | 5 +- libssh/src/packet.c | 14 +- libssh/src/packet1.c | 2 +- libssh/src/pki.c | 37 ++++-- libssh/src/pki_crypto.c | 3 + libssh/src/server.c | 40 +++++- libssh/src/socket.c | 26 ++-- libssh/tests/client/torture_session.c | 3 +- libssh/tests/torture.c | 1 + libssh/tests/unittests/torture_buffer.c | 30 +++-- libssh/tests/unittests/torture_pki.c | 4 +- 28 files changed, 347 insertions(+), 132 deletions(-) diff --git a/libssh/examples/libssh_scp.c b/libssh/examples/libssh_scp.c index d443f8f2..b7471da6 100644 --- a/libssh/examples/libssh_scp.c +++ b/libssh/examples/libssh_scp.c @@ -174,12 +174,16 @@ static int do_copy(struct location *src, struct location *dest, int recursive){ char buffer[16384]; int total=0; int mode; - char *filename; + char *filename = NULL; /* recursive mode doesn't work yet */ (void)recursive; /* Get the file name and size*/ if(!src->is_ssh){ - fd=fileno(src->file); + fd = fileno(src->file); + if (fd < 0) { + fprintf(stderr, "Invalid file pointer, error: %s\n", strerror(errno)); + return -1; + } fstat(fd,&s); size=s.st_size; mode = s.st_mode & ~S_IFMT; @@ -201,6 +205,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive){ } if(r==SSH_ERROR){ fprintf(stderr,"Error: %s\n",ssh_get_error(src->session)); + ssh_string_free_char(filename); return -1; } } while(r != SSH_SCP_REQUEST_NEWFILE); @@ -211,6 +216,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive){ // snprintf(buffer,sizeof(buffer),"C0644 %d %s\n",size,src->path); if(r==SSH_ERROR){ fprintf(stderr,"error: %s\n",ssh_get_error(dest->session)); + ssh_string_free_char(filename); ssh_scp_free(dest->scp); return -1; } @@ -221,6 +227,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive){ fprintf(stderr,"Cannot open %s for writing: %s\n",filename,strerror(errno)); if(src->is_ssh) ssh_scp_deny_request(src->scp,"Cannot open local file"); + ssh_string_free_char(filename); return -1; } } @@ -233,6 +240,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive){ r=ssh_scp_read(src->scp,buffer,sizeof(buffer)); if(r==SSH_ERROR){ fprintf(stderr,"Error reading scp: %s\n",ssh_get_error(src->session)); + ssh_string_free_char(filename); return -1; } if(r==0) @@ -243,6 +251,7 @@ static int do_copy(struct location *src, struct location *dest, int recursive){ break; if(r<0){ fprintf(stderr,"Error reading file: %s\n",strerror(errno)); + ssh_string_free_char(filename); return -1; } } @@ -252,18 +261,21 @@ static int do_copy(struct location *src, struct location *dest, int recursive){ fprintf(stderr,"Error writing in scp: %s\n",ssh_get_error(dest->session)); ssh_scp_free(dest->scp); dest->scp=NULL; + ssh_string_free_char(filename); return -1; } } else { w=fwrite(buffer,r,1,dest->file); if(w<=0){ fprintf(stderr,"Error writing in local file: %s\n",strerror(errno)); + ssh_string_free_char(filename); return -1; } } total+=r; } while(total < size); + ssh_string_free_char(filename); printf("wrote %d bytes\n",total); return 0; } @@ -286,7 +298,7 @@ int main(int argc, char **argv){ break; } } - if(dest->is_ssh){ + if (dest->is_ssh && dest->scp != NULL) { r=ssh_scp_close(dest->scp); if(r == SSH_ERROR){ fprintf(stderr,"Error closing scp: %s\n",ssh_get_error(dest->session)); diff --git a/libssh/examples/sample.c b/libssh/examples/sample.c index cfe4b3a6..93634d80 100644 --- a/libssh/examples/sample.c +++ b/libssh/examples/sample.c @@ -60,9 +60,12 @@ struct ssh_callbacks_struct cb = { static void add_cmd(char *cmd){ int n; - for(n=0;cmds[n] && (nversion) { case 1: @@ -287,9 +288,14 @@ int ssh_agent_get_ident_count(struct ssh_session_struct *session) { /* send message to the agent requesting the list of identities */ request = ssh_buffer_new(); + if (request == NULL) { + ssh_set_error_oom(request); + return -1; + } if (buffer_add_u8(request, c1) < 0) { - ssh_set_error(session, SSH_FATAL, "Not enough space"); - return -1; + ssh_set_error_oom(request); + ssh_buffer_free(request); + return -1; } reply = ssh_buffer_new(); @@ -307,16 +313,26 @@ int ssh_agent_get_ident_count(struct ssh_session_struct *session) { ssh_buffer_free(request); /* get message type and verify the answer */ - buffer_get_u8(reply, (uint8_t *) &type); + rc = buffer_get_u8(reply, (uint8_t *) &type); + if (rc != sizeof(uint8_t)) { + ssh_set_error(session, SSH_FATAL, + "Bad authentication reply size: %d", rc); + ssh_buffer_free(reply); + return -1; + } + SSH_LOG(session, SSH_LOG_WARN, "Answer type: %d, expected answer: %d", type, c2); + if (agent_failed(type)) { - return 0; + ssh_buffer_free(reply); + return 0; } else if (type != c2) { - ssh_set_error(session, SSH_FATAL, - "Bad authentication reply message type: %d", type); - return -1; + ssh_set_error(session, SSH_FATAL, + "Bad authentication reply message type: %d", type); + ssh_buffer_free(reply); + return -1; } buffer_get_u32(reply, (uint32_t *) buf); diff --git a/libssh/src/auth.c b/libssh/src/auth.c index 5b8f748f..bdad4e42 100644 --- a/libssh/src/auth.c +++ b/libssh/src/auth.c @@ -1107,12 +1107,9 @@ int ssh_userauth_publickey_auto(ssh_session session, state = session->auth_auto_state; if (state->state == SSH_AUTH_AUTO_STATE_NONE) { #ifndef _WIN32 - /* Try authentication with ssh-agent first */ + /* Try authentication with ssh-agent first */ rc = ssh_userauth_agent(session, username); - if (rc == SSH_AUTH_SUCCESS) { - return rc; - } - if (rc == SSH_AUTH_AGAIN) + if (rc == SSH_AUTH_SUCCESS) return rc; #endif state->state = SSH_AUTH_AUTO_STATE_PUBKEY; diff --git a/libssh/src/channels1.c b/libssh/src/channels1.c index 5d0158d2..62faf4ae 100644 --- a/libssh/src/channels1.c +++ b/libssh/src/channels1.c @@ -288,6 +288,8 @@ SSH_PACKET_CALLBACK(ssh_packet_data1){ SSH_PACKET_CALLBACK(ssh_packet_close1){ ssh_channel channel = ssh_get_channel1(session); uint32_t status; + int rc; + (void)type; (void)user; @@ -305,7 +307,10 @@ SSH_PACKET_CALLBACK(ssh_packet_close1){ channel->state = SSH_CHANNEL_STATE_CLOSED; channel->remote_eof = 1; - buffer_add_u8(session->out_buffer, SSH_CMSG_EXIT_CONFIRMATION); + rc = buffer_add_u8(session->out_buffer, SSH_CMSG_EXIT_CONFIRMATION); + if (rc < 0) { + return SSH_PACKET_NOT_USED; + } packet_send(session); return SSH_PACKET_USED; diff --git a/libssh/src/client.c b/libssh/src/client.c index ac1b83db..6203bc4c 100644 --- a/libssh/src/client.c +++ b/libssh/src/client.c @@ -405,6 +405,7 @@ static void ssh_client_connection_callback(ssh_session session){ if (dh_handshake(session) == SSH_ERROR) { goto error; } + /* FALL THROUGH */ case SSH_SESSION_STATE_DH: if(session->dh_handshake_state==DH_STATE_FINISHED){ set_status(session,1.0f); @@ -617,7 +618,7 @@ void ssh_disconnect(ssh_session session) { enter_function(); - if (ssh_socket_is_open(session->socket)) { + if (session->socket != NULL && ssh_socket_is_open(session->socket)) { if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { goto error; } @@ -642,7 +643,7 @@ void ssh_disconnect(ssh_session session) { } error: session->alive = 0; - if(session->socket){ + if (session->socket != NULL){ ssh_socket_reset(session->socket); } session->opts.fd = SSH_INVALID_SOCKET; diff --git a/libssh/src/connect.c b/libssh/src/connect.c index ae55b140..91e6b6a7 100644 --- a/libssh/src/connect.c +++ b/libssh/src/connect.c @@ -139,6 +139,7 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, int timeout_ms; ssh_pollfd_t fds; int rc = 0; + int ret; socklen_t len = sizeof(rc); enter_function(); @@ -148,7 +149,13 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, */ timeout_ms=timeout * 1000 + usec / 1000; - ssh_socket_set_nonblocking(s); + rc = ssh_socket_set_nonblocking(s); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, + "Failed to set socket non-blocking for %s:%d", host, port); + ssh_connect_socket_close(s); + return -1; + } ssh_log(session, SSH_LOG_RARE, "Trying to connect to host: %s:%d with " "timeout %d ms", host, port, timeout_ms); @@ -181,11 +188,11 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, leave_function(); return -1; } - rc = 0; + rc = -1; /* Get connect(2) return code. Zero means no error */ - getsockopt(s, SOL_SOCKET, SO_ERROR,(char *) &rc, &len); - if (rc != 0) { + ret = getsockopt(s, SOL_SOCKET, SO_ERROR,(char *) &rc, &len); + if (ret < 0 || rc != 0) { ssh_set_error(session, SSH_FATAL, "Connect to %s:%d failed: %s", host, port, strerror(rc)); ssh_connect_socket_close(s); @@ -195,7 +202,14 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, /* s is connected ? */ ssh_log(session, SSH_LOG_PACKET, "Socket connected with timeout\n"); - ssh_socket_set_blocking(s); + ret = ssh_socket_set_blocking(s); + if (ret < 0) { + ssh_set_error(session, SSH_FATAL, + "Failed to set socket as blocking connecting to %s:%d failed: %s", + host, port, strerror(errno)); + ssh_connect_socket_close(s); + return -1; + } leave_function(); return s; @@ -344,7 +358,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, "Failed to resolve bind address %s (%s)", bind_addr, gai_strerror(rc)); - close(s); + ssh_connect_socket_close(s); s=-1; break; } @@ -367,7 +381,15 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, continue; } } - ssh_socket_set_nonblocking(s); + + rc = ssh_socket_set_nonblocking(s); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, + "Failed to set socket non-blocking for %s:%d", host, port); + ssh_connect_socket_close(s); + s = -1; + continue; + } connect(s, itr->ai_addr, itr->ai_addrlen); break; diff --git a/libssh/src/dh.c b/libssh/src/dh.c index e4f2062b..16c15e6f 100644 --- a/libssh/src/dh.c +++ b/libssh/src/dh.c @@ -765,8 +765,14 @@ int make_sessionid(ssh_session session) { ssh_log(session,SSH_LOG_WARNING,"ECDH parameted missing"); goto error; } - buffer_add_ssh_string(buf,session->next_crypto->ecdh_client_pubkey); - buffer_add_ssh_string(buf,session->next_crypto->ecdh_server_pubkey); + rc = buffer_add_ssh_string(buf,session->next_crypto->ecdh_client_pubkey); + if (rc < 0) { + goto error; + } + rc = buffer_add_ssh_string(buf,session->next_crypto->ecdh_server_pubkey); + if (rc < 0) { + goto error; + } #endif } num = make_bignum_string(session->next_crypto->k); diff --git a/libssh/src/ecdh.c b/libssh/src/ecdh.c index bc0b03ce..075810a9 100644 --- a/libssh/src/ecdh.c +++ b/libssh/src/ecdh.c @@ -38,37 +38,57 @@ * @brief Starts ecdh-sha2-nistp256 key exchange */ int ssh_client_ecdh_init(ssh_session session){ - EC_KEY *key=NULL; + EC_KEY *key; const EC_GROUP *group; const EC_POINT *pubkey; ssh_string client_pubkey; int len; int rc; - bignum_CTX ctx=BN_CTX_new(); - enter_function(); - if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT) < 0) { - goto error; + bignum_CTX ctx = BN_CTX_new(); + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT); + if (rc < 0) { + BN_CTX_free(ctx); + return SSH_ERROR; } + key = EC_KEY_new_by_curve_name(NISTP256); + if (key == NULL) { + BN_CTX_free(ctx); + return SSH_ERROR; + } group = EC_KEY_get0_group(key); + EC_KEY_generate_key(key); + pubkey=EC_KEY_get0_public_key(key); len = EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED, NULL,0,ctx); - client_pubkey=ssh_string_new(len); + + client_pubkey = ssh_string_new(len); + if (client_pubkey == NULL) { + BN_CTX_free(ctx); + EC_KEY_free(key); + return SSH_ERROR; + } EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED, ssh_string_data(client_pubkey),len,ctx); - buffer_add_ssh_string(session->out_buffer,client_pubkey); BN_CTX_free(ctx); + + rc = buffer_add_ssh_string(session->out_buffer,client_pubkey); + if (rc < 0) { + EC_KEY_free(key); + ssh_string_free(client_pubkey); + return SSH_ERROR; + } + session->next_crypto->ecdh_privkey = key; session->next_crypto->ecdh_client_pubkey = client_pubkey; + rc = packet_send(session); - leave_function(); + return rc; -error: - leave_function(); - return SSH_ERROR; } static void ecdh_import_pubkey(ssh_session session, ssh_string pubkey_string) { @@ -180,9 +200,9 @@ error: int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ /* ECDH keys */ - ssh_string q_c_string = NULL; - ssh_string q_s_string = NULL; - EC_KEY *ecdh_key=NULL; + ssh_string q_c_string; + ssh_string q_s_string; + EC_KEY *ecdh_key; const EC_GROUP *group; const EC_POINT *ecdh_pubkey; bignum_CTX ctx; @@ -192,14 +212,11 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ int len; int rc; - enter_function(); - /* Extract the client pubkey from the init packet */ - q_c_string = buffer_get_ssh_string(packet); if (q_c_string == NULL) { - ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); - goto error; + ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); + return SSH_ERROR; } session->next_crypto->ecdh_client_pubkey = q_c_string; @@ -207,45 +224,94 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ ctx = BN_CTX_new(); ecdh_key = EC_KEY_new_by_curve_name(NISTP256); + if (ecdh_key == NULL) { + ssh_set_error_oom(session); + BN_CTX_free(ctx); + return SSH_ERROR; + } + group = EC_KEY_get0_group(ecdh_key); EC_KEY_generate_key(ecdh_key); - ecdh_pubkey=EC_KEY_get0_public_key(ecdh_key); - len = EC_POINT_point2oct(group,ecdh_pubkey,POINT_CONVERSION_UNCOMPRESSED, - NULL,0,ctx); - q_s_string=ssh_string_new(len); - EC_POINT_point2oct(group,ecdh_pubkey,POINT_CONVERSION_UNCOMPRESSED, - ssh_string_data(q_s_string),len,ctx); + ecdh_pubkey = EC_KEY_get0_public_key(ecdh_key); + len = EC_POINT_point2oct(group, + ecdh_pubkey, + POINT_CONVERSION_UNCOMPRESSED, + NULL, + 0, + ctx); + q_s_string = ssh_string_new(len); + if (q_s_string == NULL) { + EC_KEY_free(ecdh_key); + BN_CTX_free(ctx); + return SSH_ERROR; + } + + EC_POINT_point2oct(group, + ecdh_pubkey, + POINT_CONVERSION_UNCOMPRESSED, + ssh_string_data(q_s_string), + len, + ctx); BN_CTX_free(ctx); + session->next_crypto->ecdh_privkey = ecdh_key; session->next_crypto->ecdh_server_pubkey = q_s_string; - buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY); - /* build k and session_id */ - if (ecdh_build_k(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - goto error; + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; } - if (ssh_get_key_params(session, &privkey) == SSH_ERROR) - goto error; - if (make_sessionid(session) != SSH_OK) { - ssh_set_error(session, SSH_FATAL, "Could not create a session id"); - goto error; + + /* build k and session_id */ + rc = ecdh_build_k(session); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + return SSH_ERROR; + } + + /* privkey is not allocated */ + rc = ssh_get_key_params(session, &privkey); + if (rc == SSH_ERROR) { + return SSH_ERROR; + } + + rc = make_sessionid(session); + if (rc != SSH_OK) { + ssh_set_error(session, SSH_FATAL, "Could not create a session id"); + return SSH_ERROR; } /* add host's public key */ - buffer_add_ssh_string(session->out_buffer, session->next_crypto->server_pubkey); + rc = buffer_add_ssh_string(session->out_buffer, + session->next_crypto->server_pubkey); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + /* add ecdh public key */ - buffer_add_ssh_string(session->out_buffer,q_s_string); + rc = buffer_add_ssh_string(session->out_buffer, q_s_string); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } /* add signature blob */ sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); - goto error; + return SSH_ERROR; } - buffer_add_ssh_string(session->out_buffer, sig_blob); + + rc = buffer_add_ssh_string(session->out_buffer, sig_blob); ssh_string_free(sig_blob); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + /* Free private keys as they should not be readable after this point */ if (session->srv.rsa_key) { ssh_key_free(session->srv.rsa_key); @@ -258,19 +324,21 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ ssh_log(session,SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent"); rc = packet_send(session); - if (rc == SSH_ERROR) - goto error; + if (rc == SSH_ERROR) { + return SSH_ERROR; + } /* Send the MSG_NEWKEYS */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); + if (rc < 0) { + return SSH_ERROR;; } - session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; - rc=packet_send(session); + + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + rc = packet_send(session); ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + return rc; - error: - return SSH_ERROR; } #endif /* WITH_SERVER */ diff --git a/libssh/src/kex.c b/libssh/src/kex.c index e900a91a..c678731e 100644 --- a/libssh/src/kex.c +++ b/libssh/src/kex.c @@ -460,6 +460,7 @@ int ssh_send_kex(ssh_session session, int server_kex) { goto error; } ssh_string_free(str); + str = NULL; } if (buffer_add_u8(session->out_buffer, 0) < 0) { diff --git a/libssh/src/kex1.c b/libssh/src/kex1.c index b11cf007..b396a719 100644 --- a/libssh/src/kex1.c +++ b/libssh/src/kex1.c @@ -47,6 +47,9 @@ static ssh_string make_rsa1_string(ssh_string e, ssh_string n){ buffer = ssh_buffer_new(); rsa = ssh_string_from_char("ssh-rsa1"); + if (rsa == NULL) { + goto error; + } if (buffer_add_ssh_string(buffer, rsa) < 0) { goto error; @@ -375,7 +378,7 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1){ goto error; } hostkey = make_rsa1_string(host_exp,host_mod); - if (serverkey == NULL) { + if (hostkey == NULL) { goto error; } if (build_session_id1(session, server_mod, host_mod) < 0) { diff --git a/libssh/src/known_hosts.c b/libssh/src/known_hosts.c index 2d281e72..a9ae38c1 100644 --- a/libssh/src/known_hosts.c +++ b/libssh/src/known_hosts.c @@ -133,7 +133,7 @@ static char **ssh_get_knownhost_line(ssh_session session, FILE **file, *ptr = '\0'; } - if (!buffer[0] || buffer[0] == '#') { + if (buffer[0] == '\0' || buffer[0] == '#') { continue; /* skip empty lines */ } diff --git a/libssh/src/messages.c b/libssh/src/messages.c index 1ef8a745..5fcdd04e 100644 --- a/libssh/src/messages.c +++ b/libssh/src/messages.c @@ -135,7 +135,9 @@ void ssh_message_queue(ssh_session session, ssh_message message){ } session->ssh_message_list = ssh_list_new(); } - ssh_list_append(session->ssh_message_list, message); + if (session->ssh_message_list != NULL) { + ssh_list_append(session->ssh_message_list, message); + } } } @@ -1252,6 +1254,7 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){ msg = ssh_message_new(session); if (msg == NULL) { + ssh_string_free_char(request); return SSH_PACKET_NOT_USED; } msg->type = SSH_REQUEST_GLOBAL; diff --git a/libssh/src/packet.c b/libssh/src/packet.c index 2256f113..440e47c6 100644 --- a/libssh/src/packet.c +++ b/libssh/src/packet.c @@ -187,6 +187,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) /* saves the status of the current operations */ session->in_packet.len = len; session->packet_state = PACKET_STATE_SIZEREAD; + /* FALL TROUGH */ case PACKET_STATE_SIZEREAD: len = session->in_packet.len; to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize; @@ -305,10 +306,12 @@ void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){ * @brief sets the callbacks for the packet layer */ void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks){ - if(session->packet_callbacks == NULL){ - session->packet_callbacks = ssh_list_new(); - } - ssh_list_append(session->packet_callbacks, callbacks); + if(session->packet_callbacks == NULL){ + session->packet_callbacks = ssh_list_new(); + } + if (session->packet_callbacks != NULL) { + ssh_list_append(session->packet_callbacks, callbacks); + } } /** @internal @@ -526,6 +529,3 @@ int packet_send(ssh_session session) { #endif return packet_send2(session); } - - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/packet1.c b/libssh/src/packet1.c index 5c8b81e5..56bfb346 100644 --- a/libssh/src/packet1.c +++ b/libssh/src/packet1.c @@ -144,6 +144,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user session->in_packet.len = len; session->packet_state = PACKET_STATE_SIZEREAD; + /* FALL THROUGH */ case PACKET_STATE_SIZEREAD: len = session->in_packet.len; /* SSH-1 has a fixed padding lenght */ @@ -158,7 +159,6 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user packet = (char *)data + processed; if (buffer_add_data(session->in_buffer,packet,to_be_read) < 0) { - SAFE_FREE(packet); goto error; } processed += to_be_read; diff --git a/libssh/src/pki.c b/libssh/src/pki.c index 87d7e765..a3616c2f 100644 --- a/libssh/src/pki.c +++ b/libssh/src/pki.c @@ -421,8 +421,16 @@ int ssh_pki_import_privkey_file(const char *filename, return SSH_ERROR; } - rc = stat(filename, &sb); + file = fopen(filename, "rb"); + if (file == NULL) { + ssh_pki_log("Error opening %s: %s", + filename, strerror(errno)); + return SSH_EOF; + } + + rc = fstat(fileno(file), &sb); if (rc < 0) { + fclose(file); ssh_pki_log("Error getting stat of %s: %s", filename, strerror(errno)); switch (errno) { @@ -434,11 +442,10 @@ int ssh_pki_import_privkey_file(const char *filename, return SSH_ERROR; } - file = fopen(filename, "rb"); - if (file == NULL) { - ssh_pki_log("Error opening %s: %s", - filename, strerror(errno)); - return SSH_EOF; + if (sb.st_size > MAX_PRIVKEY_SIZE) { + ssh_pki_log("Private key is bigger than 4M."); + fclose(file); + return SSH_ERROR; } key_buf = malloc(sb.st_size + 1); @@ -804,8 +811,16 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey) return SSH_ERROR; } - rc = stat(filename, &sb); + file = fopen(filename, "r"); + if (file == NULL) { + ssh_pki_log("Error opening %s: %s", + filename, strerror(errno)); + return SSH_EOF; + } + + rc = fstat(fileno(file), &sb); if (rc < 0) { + fclose(file); ssh_pki_log("Error gettint stat of %s: %s", filename, strerror(errno)); switch (errno) { @@ -817,16 +832,10 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey) } if (sb.st_size > MAX_PUBKEY_SIZE) { + fclose(file); return SSH_ERROR; } - file = fopen(filename, "r"); - if (file == NULL) { - ssh_pki_log("Error opening %s: %s", - filename, strerror(errno)); - return SSH_EOF; - } - key_buf = malloc(sb.st_size + 1); if (key_buf == NULL) { fclose(file); diff --git a/libssh/src/pki_crypto.c b/libssh/src/pki_crypto.c index 0ec05d33..92c37c85 100644 --- a/libssh/src/pki_crypto.c +++ b/libssh/src/pki_crypto.c @@ -411,8 +411,10 @@ int pki_key_generate_ecdsa(ssh_key key, int parameter) { switch (parameter) { case 384: nid = NID_secp384r1; + break; case 512: nid = NID_secp521r1; + break; case 256: default: nid = NID_X9_62_prime256v1; @@ -850,6 +852,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) e = make_ecpoint_string(EC_KEY_get0_group(key->ecdsa), EC_KEY_get0_public_key(key->ecdsa)); if (e == NULL) { + ssh_buffer_free(buffer); return NULL; } diff --git a/libssh/src/server.c b/libssh/src/server.c index db8f8152..6862370f 100644 --- a/libssh/src/server.c +++ b/libssh/src/server.c @@ -927,7 +927,7 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name, r = buffer_add_ssh_string(msg->session->out_buffer, tmp); ssh_string_free(tmp); if (r < 0) { - goto error; + return SSH_ERROR; } /* echo[i] */ @@ -1003,9 +1003,6 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name, } return r; -error: - if(tmp) ssh_string_free(tmp); - return SSH_ERROR; } int ssh_message_auth_reply_success(ssh_message msg, int partial) { @@ -1200,6 +1197,41 @@ int ssh_execute_message_callbacks(ssh_session session){ return SSH_OK; } +int ssh_send_keepalive(ssh_session session) +{ + /* TODO check the reply and all that */ + struct ssh_string_struct *req; + int reply = 1; + int rc = SSH_ERROR; + + enter_function(); + req = ssh_string_from_char("keepalive@openssh.com"); + if (req == NULL) { + ssh_set_error_oom(session); + goto out; + } + + if (buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST) < 0 || + buffer_add_ssh_string(session->out_buffer, req) < 0 || + buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { + ssh_set_error_oom(session); + goto out; + } + + if (packet_send(session) == SSH_ERROR) + goto out; + + ssh_handle_packets(session, 0); + + ssh_log(session, SSH_LOG_PACKET, "Sent a keepalive"); + rc = SSH_OK; + +out: + ssh_string_free(req); + leave_function(); + return rc; +} + /** @} */ /* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/socket.c b/libssh/src/socket.c index 85b87b77..0f6c0a83 100644 --- a/libssh/src/socket.c +++ b/libssh/src/socket.c @@ -230,7 +230,10 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int r /* Check if we are in a connecting state */ if(s->state==SSH_SOCKET_CONNECTING){ s->state=SSH_SOCKET_ERROR; - getsockopt(fd,SOL_SOCKET,SO_ERROR,(char *)&err,&errlen); + r = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); + if (r < 0) { + err = errno; + } s->last_errno=err; ssh_socket_close(s); if(s->callbacks && s->callbacks->connected) @@ -305,7 +308,10 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int r ssh_log(s->session,SSH_LOG_PACKET,"Received POLLOUT in connecting state"); s->state = SSH_SOCKET_CONNECTED; ssh_poll_set_events(p,POLLOUT | POLLIN); - ssh_socket_set_blocking(ssh_socket_get_fd_in(s)); + r = ssh_socket_set_blocking(ssh_socket_get_fd_in(s)); + if (r < 0) { + return -1; + } if(s->callbacks && s->callbacks->connected) s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); return 0; @@ -711,23 +717,23 @@ int ssh_socket_get_status(ssh_socket s) { } #ifdef _WIN32 -void ssh_socket_set_nonblocking(socket_t fd) { +int ssh_socket_set_nonblocking(socket_t fd) { u_long nonblocking = 1; - ioctlsocket(fd, FIONBIO, &nonblocking); + return ioctlsocket(fd, FIONBIO, &nonblocking); } -void ssh_socket_set_blocking(socket_t fd) { +int ssh_socket_set_blocking(socket_t fd) { u_long nonblocking = 0; - ioctlsocket(fd, FIONBIO, &nonblocking); + return ioctlsocket(fd, FIONBIO, &nonblocking); } #else /* _WIN32 */ -void ssh_socket_set_nonblocking(socket_t fd) { - fcntl(fd, F_SETFL, O_NONBLOCK); +int ssh_socket_set_nonblocking(socket_t fd) { + return fcntl(fd, F_SETFL, O_NONBLOCK); } -void ssh_socket_set_blocking(socket_t fd) { - fcntl(fd, F_SETFL, 0); +int ssh_socket_set_blocking(socket_t fd) { + return fcntl(fd, F_SETFL, 0); } #endif /* _WIN32 */ diff --git a/libssh/tests/client/torture_session.c b/libssh/tests/client/torture_session.c index d66901da..4e2f6477 100644 --- a/libssh/tests/client/torture_session.c +++ b/libssh/tests/client/torture_session.c @@ -83,7 +83,8 @@ static void torture_channel_read_error(void **state) { assert_true(rc == SSH_OK); /* send crap and for server to send us a disconnect */ - write(ssh_get_fd(session),"AAAA", 4); + rc = write(ssh_get_fd(session),"AAAA", 4); + assert_int_equal(rc, 4); for (i=0;i<20;++i){ rc = ssh_channel_read(channel,buffer,sizeof(buffer),0); diff --git a/libssh/tests/torture.c b/libssh/tests/torture.c index a75b0a94..2a49f0d7 100644 --- a/libssh/tests/torture.c +++ b/libssh/tests/torture.c @@ -110,6 +110,7 @@ int torture_rmdirs(const char *path) { len = strlen(path) + strlen(dp->d_name) + 2; fname = malloc(len); if (fname == NULL) { + closedir(d); return -1; } snprintf(fname, len, "%s/%s", path, dp->d_name); diff --git a/libssh/tests/unittests/torture_buffer.c b/libssh/tests/unittests/torture_buffer.c index 511cdf45..dee6e7d4 100644 --- a/libssh/tests/unittests/torture_buffer.c +++ b/libssh/tests/unittests/torture_buffer.c @@ -66,22 +66,25 @@ static void torture_buffer_prepend(void **state) { buffer_add_data(buffer,"abcdef",6); buffer_prepend_data(buffer,"xyz",3); assert_int_equal(buffer_get_rest_len(buffer),9); - assert_int_equal(memcmp(buffer_get_rest(buffer), "xyzabcdef", 9), 0); -// Now remove 4 bytes and see if we can replace them + assert_memory_equal(buffer_get_rest(buffer), "xyzabcdef", 9); + + /* Now remove 4 bytes and see if we can replace them */ buffer_get_u32(buffer,&v); assert_int_equal(buffer_get_rest_len(buffer),5); - assert_int_equal(memcmp(buffer_get_rest(buffer), "bcdef", 5), 0); + assert_memory_equal(buffer_get_rest(buffer), "bcdef", 5); + buffer_prepend_data(buffer,"aris",4); assert_int_equal(buffer_get_rest_len(buffer),9); - assert_int_equal(memcmp(buffer_get_rest(buffer), "arisbcdef", 9), 0); + assert_memory_equal(buffer_get_rest(buffer), "arisbcdef", 9); + /* same thing but we add 5 bytes now */ buffer_get_u32(buffer,&v); assert_int_equal(buffer_get_rest_len(buffer),5); - assert_int_equal(memcmp(buffer_get_rest(buffer), "bcdef", 5), 0); + assert_memory_equal(buffer_get_rest(buffer), "bcdef", 5); + buffer_prepend_data(buffer,"12345",5); assert_int_equal(buffer_get_rest_len(buffer),10); - assert_int_equal(memcmp(buffer_get_rest(buffer), "12345bcdef", 10), 0); - + assert_memory_equal(buffer_get_rest(buffer), "12345bcdef", 10); } /* @@ -89,7 +92,7 @@ static void torture_buffer_prepend(void **state) { */ static void torture_buffer_get_ssh_string(void **state) { ssh_buffer buffer; - int i,j,k,l; + int i,j,k,l, rc; /* some values that can go wrong */ uint32_t values[] = {0xffffffff, 0xfffffffe, 0xfffffffc, 0xffffff00, 0x80000000, 0x80000004, 0x7fffffff}; @@ -100,13 +103,18 @@ static void torture_buffer_get_ssh_string(void **state) { for(j=0; j< (int)sizeof(data);++j){ for(k=1;k<5;++k){ buffer=buffer_new(); + assert_non_null(buffer); + for(l=0;l Date: Mon, 22 Jul 2013 17:06:28 -0400 Subject: [PATCH 040/703] Support for multiple IP on DNS --- Makefile.am | 2 +- configure.ac | 2 +- server.c | 2 +- tmate-debug.c | 14 ++++++ tmate-encoder.c | 7 ++- tmate-session.c | 107 ++++++++++++++++++++++++++++++++++++++++++ tmate-ssh-client.c | 113 ++++++++++++++++++++++++++++++++------------- tmate.c | 18 -------- tmate.h | 57 ++++++++++++++++++----- 9 files changed, 254 insertions(+), 68 deletions(-) create mode 100644 tmate-session.c delete mode 100644 tmate.c diff --git a/Makefile.am b/Makefile.am index fa327d6c..2268de88 100644 --- a/Makefile.am +++ b/Makefile.am @@ -182,7 +182,7 @@ dist_tmate_SOURCES = \ tmate-encoder.c \ tmate-decoder.c \ tmate-msg.c \ - tmate.c \ + tmate-session.c \ tmux.c \ tty-acs.c \ tty-keys.c \ diff --git a/configure.ac b/configure.ac index 20914916..a86799f0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmate, 1.8.4) +AC_INIT(tmate, 1.8.6) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) diff --git a/server.c b/server.c index 6a636825..c99ce8e4 100644 --- a/server.c +++ b/server.c @@ -187,7 +187,7 @@ server_start(int lockfd, char *lockfile) } } - tmate_client_start(); + tmate_session_start(); cmdq_continue(cfg_cmd_q); diff --git a/tmate-debug.c b/tmate-debug.c index c480e658..15dd31a6 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -73,3 +73,17 @@ void tmate_print_trace(void) free (strings); } + + +static void handle_sigsegv(int sig) +{ + /* TODO send stack trace to server */ + tmate_info("CRASH, printing stack trace"); + tmate_print_trace(); + tmate_fatal("CRASHED"); +} + +void tmate_catch_sigsegv(void) +{ + signal(SIGSEGV, handle_sigsegv); +} diff --git a/tmate-encoder.c b/tmate-encoder.c index 6ac9a590..e4a95a66 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -1,5 +1,7 @@ #include "tmate.h" +#define DEFAULT_ENCODER (&tmate_session.encoder) + static int msgpack_write(void *data, const char *buf, unsigned int len) { struct tmate_encoder *encoder = data; @@ -26,13 +28,14 @@ void tmate_encoder_init(struct tmate_encoder *encoder) msgpack_pack_raw_body(pk, str, __strlen); \ } while(0) -#define pack(what, ...) msgpack_pack_##what(&tmate_encoder->pk, __VA_ARGS__) +#define pack(what, ...) msgpack_pack_##what(&DEFAULT_ENCODER->pk, __VA_ARGS__) void tmate_write_header(void) { - pack(array, 2); + pack(array, 3); pack(int, TMATE_HEADER); pack(int, TMATE_PROTOCOL_VERSION); + pack(string, VERSION); } void tmate_sync_layout(void) diff --git a/tmate-session.c b/tmate-session.c new file mode 100644 index 00000000..93f82955 --- /dev/null +++ b/tmate-session.c @@ -0,0 +1,107 @@ +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "tmate.h" + +#define TMATE_DNS_RETRY_TIMEOUT 10 + +struct tmate_session tmate_session; + +static struct evdns_base *ev_dnsbase; +static struct event ev_dns_retry; +static void lookup_and_connect(void); + +static void on_dns_retry(evutil_socket_t fd, short what, void *arg) +{ + lookup_and_connect(); +} + +static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) +{ + struct tmate_ssh_client *client; + struct evutil_addrinfo *ai; + struct timeval tv; + + if (errcode) { + tmate_status_message("%s lookup failure. Retrying in %d seconds (%s)", + TMATE_HOST, TMATE_DNS_RETRY_TIMEOUT, + evutil_gai_strerror(errcode)); + + tv.tv_sec = TMATE_DNS_RETRY_TIMEOUT; + tv.tv_usec = 0; + + evtimer_assign(&ev_dns_retry, ev_base, on_dns_retry, NULL); + evtimer_add(&ev_dns_retry, &tv); + + return; + } + + tmate_status_message("Connecting to %s...", TMATE_HOST); + + for (ai = addr; ai; ai = ai->ai_next) { + char buf[128]; + const char *ip = NULL; + if (ai->ai_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; + ip = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128); + } else if (ai->ai_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; + ip = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128); + } + + tmate_debug("Trying server %s", ip); + + /* + * Note: We don't deal with the client list. Clients manage it + * and free client structs when necessary. + */ + (void)tmate_ssh_client_alloc(&tmate_session, ip); + } + + evutil_freeaddrinfo(addr); + + evdns_base_free(ev_dnsbase, 0); + ev_dnsbase = NULL; +} + +static void lookup_and_connect(void) +{ + struct evutil_addrinfo hints; + + if (!ev_dnsbase) + ev_dnsbase = evdns_base_new(ev_base, 1); + if (!ev_dnsbase) + tmate_fatal("Cannot initialize the DNS lookup service"); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = 0; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + tmate_status_message("Looking up %s...", TMATE_HOST); + (void)evdns_getaddrinfo(ev_dnsbase, TMATE_HOST, NULL, + &hints, dns_cb, NULL); +} + +void tmate_session_start(void) +{ + tmate_catch_sigsegv(); + + TAILQ_INIT(&tmate_session.clients); + tmate_encoder_init(&tmate_session.encoder); + tmate_decoder_init(&tmate_session.decoder); + + lookup_and_connect(); + + /* The header will be written as soon as the first client connects */ + tmate_write_header(); +} diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index f4e55e27..2429391f 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -1,9 +1,8 @@ -#include -#include #include #include #include #include +#include #include "tmate.h" @@ -19,14 +18,10 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client, static void log_function(ssh_session session, int priority, const char *message, void *userdata) { - tmate_debug("[%d] %s", priority, message); + struct tmate_ssh_client *client = userdata; + tmate_debug("[%s] [%d] %s", client->server_ip, priority, message); } -static struct ssh_callbacks_struct ssh_session_callbacks = { - .log_function = log_function -}; - - static void register_session_fd_event(struct tmate_ssh_client *client) { if (!event_initialized(&client->ev_ssh)) { @@ -42,20 +37,24 @@ static void register_session_fd_event(struct tmate_ssh_client *client) static void register_input_stream_event(struct tmate_ssh_client *client) { - if (!event_initialized(&client->encoder->ev_readable)) { - event_assign(&client->encoder->ev_readable, ev_base, -1, + struct tmate_encoder *encoder = &client->tmate_session->encoder; + + if (!event_initialized(&encoder->ev_readable)) { + event_assign(&encoder->ev_readable, ev_base, -1, EV_READ | EV_PERSIST, __flush_input_stream, client); - event_add(&client->encoder->ev_readable, NULL); + event_add(&encoder->ev_readable, NULL); + client->has_encoder = 1; } } static void consume_channel(struct tmate_ssh_client *client) { + struct tmate_decoder *decoder = &client->tmate_session->decoder; char *buf; ssize_t len; for (;;) { - tmate_decoder_get_buffer(client->decoder, &buf, &len); + tmate_decoder_get_buffer(decoder, &buf, &len); len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); if (len < 0) { reconnect_session(client, "Error reading from channel: %s", @@ -66,7 +65,21 @@ static void consume_channel(struct tmate_ssh_client *client) if (len == 0) break; - tmate_decoder_commit(client->decoder, len); + tmate_decoder_commit(decoder, len); + } +} + +static void connection_complete(struct tmate_ssh_client *connected_client) +{ + struct tmate_session *session = connected_client->tmate_session; + struct tmate_ssh_client *client, *tmp_client; + + TAILQ_FOREACH_SAFE(client, &session->clients, node, tmp_client) { + if (client == connected_client) + continue; + + assert(!client->has_encoder); + tmate_ssh_client_free(client); } } @@ -93,7 +106,7 @@ static void on_session_event(struct tmate_ssh_client *client) return; } - ssh_set_callbacks(session, &ssh_session_callbacks); + ssh_set_callbacks(session, &client->ssh_callbacks); client->channel = channel = ssh_channel_new(session); if (!channel) { @@ -102,13 +115,12 @@ static void on_session_event(struct tmate_ssh_client *client) } ssh_set_blocking(session, 0); - ssh_options_set(session, SSH_OPTIONS_HOST, TMATE_HOST); + ssh_options_set(session, SSH_OPTIONS_HOST, client->server_ip); ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); ssh_options_set(session, SSH_OPTIONS_PORT, &port); ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); - tmate_status_message("Connecting to %s...", TMATE_HOST); client->state = SSH_CONNECT; /* fall through */ @@ -123,14 +135,14 @@ static void on_session_event(struct tmate_ssh_client *client) return; case SSH_OK: register_session_fd_event(client); - tmate_debug("Connected"); + tmate_debug("Establishing connection to %s", client->server_ip); client->state = SSH_AUTH_SERVER; /* fall through */ } case SSH_AUTH_SERVER: if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) { - disconnect_session(client, "Cannnot authenticate server"); + disconnect_session(client, "Cannot authenticate server"); return; } @@ -165,11 +177,20 @@ static void on_session_event(struct tmate_ssh_client *client) free(hash_str); if (!match) { - disconnect_session(client, "Cannnot authenticate server"); + disconnect_session(client, "Cannot authenticate server"); return; } + /* + * At this point, we abort other connection attempts to the + * other tmate servers, since we have reached the fastest one. + * We need to do it before we ask the user its passphrase, + * otherwise the speed test would be biased. + */ + tmate_debug("Connected to %s", client->server_ip); + connection_complete(client); client->state = SSH_AUTH_CLIENT; + /* fall through */ case SSH_AUTH_CLIENT: @@ -179,7 +200,8 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: - disconnect_session(client, "Access denied. Check your SSH keys."); + disconnect_session(client, "Access denied. Check your SSH keys " + "(passphrases are not supported yet)."); return; case SSH_AUTH_ERROR: reconnect_session(client, "Auth error: %s", @@ -236,7 +258,8 @@ static void on_session_event(struct tmate_ssh_client *client) static void flush_input_stream(struct tmate_ssh_client *client) { - struct evbuffer *evb = client->encoder->buffer; + struct tmate_encoder *encoder = &client->tmate_session->encoder; + struct evbuffer *evb = encoder->buffer; ssize_t len, written; char *buf; @@ -274,16 +297,23 @@ static void __on_session_event(evutil_socket_t fd, short what, void *arg) static void __disconnect_session(struct tmate_ssh_client *client, const char *fmt, va_list va) { - __tmate_status_message(fmt, va); + struct tmate_encoder *encoder; + + if (fmt) + __tmate_status_message(fmt, va); + else + tmate_debug("Disconnecting %s", client->server_ip); if (event_initialized(&client->ev_ssh)) { event_del(&client->ev_ssh); client->ev_ssh.ev_flags = 0; } - if (event_initialized(&client->encoder->ev_readable)) { - event_del(&client->encoder->ev_readable); - client->encoder->ev_readable.ev_flags = 0; + if (client->has_encoder) { + encoder = &client->tmate_session->encoder; + event_del(&encoder->ev_readable); + encoder->ev_readable.ev_flags = 0; + client->has_encoder = 0; } if (client->session) { @@ -337,21 +367,38 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client, #endif } -void tmate_ssh_client_init(struct tmate_ssh_client *client, - struct tmate_encoder *encoder, - struct tmate_decoder *decoder) -{ - ssh_callbacks_init(&ssh_session_callbacks); +struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, + const char *server_ip) +{ + struct tmate_ssh_client *client; + client = xmalloc(sizeof(*client)); + + ssh_callbacks_init(&client->ssh_callbacks); + client->ssh_callbacks.log_function = log_function; + client->ssh_callbacks.userdata = client; + + client->tmate_session = session; + TAILQ_INSERT_TAIL(&session->clients, client, node); + + client->server_ip = xstrdup(server_ip); client->state = SSH_NONE; client->session = NULL; client->channel = NULL; - - client->encoder = encoder; - client->decoder = decoder; + client->has_encoder = 0; evtimer_assign(&client->ev_ssh_reconnect, ev_base, on_reconnect_timer, client); connect_session(client); + + return client; +} + +void tmate_ssh_client_free(struct tmate_ssh_client *client) +{ + disconnect_session(client, NULL); + TAILQ_REMOVE(&client->tmate_session->clients, client, node); + free(client->server_ip); + free(client); } diff --git a/tmate.c b/tmate.c deleted file mode 100644 index c670d39c..00000000 --- a/tmate.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "tmate.h" - -struct tmate_encoder *tmate_encoder; - -static struct tmate_ssh_client client; -static struct tmate_encoder encoder; -static struct tmate_decoder decoder; - -void tmate_client_start(void) -{ - tmate_encoder_init(&encoder); - tmate_decoder_init(&decoder); - tmate_encoder = &encoder; - - tmate_ssh_client_init(&client, &encoder, &decoder); - - tmate_write_header(); -} diff --git a/tmate.h b/tmate.h index 57d5bcc5..7969dbfe 100644 --- a/tmate.h +++ b/tmate.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "tmux.h" @@ -17,7 +18,7 @@ #define TMATE_MAX_MESSAGE_SIZE (16*1024) -#define TMATE_PROTOCOL_VERSION 2 +#define TMATE_PROTOCOL_VERSION 3 enum tmate_commands { TMATE_HEADER, @@ -72,7 +73,7 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); /* tmate-ssh-client.c */ #ifdef DEVENV -#define TMATE_HOST "127.0.0.1" +#define TMATE_HOST "localhost" #define TMATE_PORT 2200 #else #define TMATE_HOST "master.tmate.io" @@ -94,28 +95,60 @@ enum tmate_ssh_client_state_types { }; struct tmate_ssh_client { + /* XXX The "session" word is used for three things: + * - the ssh session + * - the tmate sesssion + * - the tmux session + * A tmux session is associated 1:1 with a tmate session. + * An ssh session belongs to a tmate session, and a tmate session + * has one ssh session, except during bootstrapping where + * there is one ssh session per tmate server, and the first one wins. + */ + struct tmate_session *tmate_session; + TAILQ_ENTRY(tmate_ssh_client) node; + + char *server_ip; + + int has_encoder; int state; + + /* + * ssh_callbacks is allocated because the libssh API sucks (userdata + * has to be in the struct itself). + */ + struct ssh_callbacks_struct ssh_callbacks; ssh_session session; ssh_channel channel; - struct tmate_encoder *encoder; - struct tmate_decoder *decoder; - struct event ev_ssh; struct event ev_ssh_reconnect; }; +TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); -extern void tmate_ssh_client_init(struct tmate_ssh_client *client, - struct tmate_encoder *encoder, - struct tmate_decoder *decoder); +extern struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, + const char *server_ip); +extern void tmate_ssh_client_free(struct tmate_ssh_client *client); -/* tmate.c */ +/* tmate-session.c */ -extern struct tmate_encoder *tmate_encoder; -extern void tmate_client_start(void); +struct tmate_session { + struct tmate_encoder encoder; + struct tmate_decoder decoder; + + /* + * This list contains one connection per IP. The first connected + * client wins, and saved in *client. When we have a winner, the + * losers are disconnected and killed. + */ + struct tmate_ssh_clients clients; +}; + +extern struct tmate_session tmate_session; +extern void tmate_session_start(void); /* tmate-debug.c */ -extern void tmate_print_trace (void); +extern void tmate_print_trace(void); +extern void tmate_catch_sigsegv(void); /* tmate-msg.c */ From ebc4e5ecdf177119d9d2602b6521ff8969c74998 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 22 Jul 2013 18:05:44 -0400 Subject: [PATCH 041/703] User can specify his SSH identity with the tmate-identity global option --- options-table.c | 5 +++++ server.c | 4 ++-- tmate-session.c | 15 ++++++++++++--- tmate-ssh-client.c | 22 ++++++++++++++++++++++ tmate.h | 1 + 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/options-table.c b/options-table.c index 504bcfca..342fd172 100644 --- a/options-table.c +++ b/options-table.c @@ -176,6 +176,11 @@ const struct options_table_entry session_options_table[] = { .default_num = 30000 }, + { .name = "tmate-identity", + .type = OPTIONS_TABLE_STRING, + .default_str = "" + }, + { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, diff --git a/server.c b/server.c index c99ce8e4..6620c78b 100644 --- a/server.c +++ b/server.c @@ -187,8 +187,7 @@ server_start(int lockfd, char *lockfile) } } - tmate_session_start(); - + tmate_session_init(); cmdq_continue(cfg_cmd_q); server_add_accept(0); @@ -200,6 +199,7 @@ server_start(int lockfd, char *lockfile) set_signals(server_signal_callback); + tmate_session_start(); server_loop(); exit(0); } diff --git a/tmate-session.c b/tmate-session.c index 93f82955..bbca4c12 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -92,7 +92,7 @@ static void lookup_and_connect(void) &hints, dns_cb, NULL); } -void tmate_session_start(void) +void tmate_session_init(void) { tmate_catch_sigsegv(); @@ -100,8 +100,17 @@ void tmate_session_start(void) tmate_encoder_init(&tmate_session.encoder); tmate_decoder_init(&tmate_session.decoder); - lookup_and_connect(); - /* The header will be written as soon as the first client connects */ tmate_write_header(); } + +void tmate_session_start(void) +{ + /* We split init and start because: + * - We need to process the tmux config file during the connection as + * we are setting up the tmate identity. + * - While we are parsing the config file, we need to be able to + * serialize it, and so we need a worker encoder. + */ + lookup_and_connect(); +} diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 2429391f..50842d97 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -83,8 +83,25 @@ static void connection_complete(struct tmate_ssh_client *connected_client) } } +static char *get_identity(void) +{ + char *identity; + + identity = options_get_string(&global_s_options, "tmate-identity"); + if (!strlen(identity)) + return NULL; + + if (strchr(identity, '/')) + identity = xstrdup(identity); + else + xasprintf(&identity, "%%d/%s", identity); + + return identity; +} + static void on_session_event(struct tmate_ssh_client *client) { + char *identity; ssh_key pubkey; int key_type; unsigned char *hash; @@ -121,6 +138,11 @@ static void on_session_event(struct tmate_ssh_client *client) ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); + if ((identity = get_identity())) { + ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity); + free(identity); + } + client->state = SSH_CONNECT; /* fall through */ diff --git a/tmate.h b/tmate.h index 7969dbfe..687fd7e6 100644 --- a/tmate.h +++ b/tmate.h @@ -144,6 +144,7 @@ struct tmate_session { }; extern struct tmate_session tmate_session; +extern void tmate_session_init(void); extern void tmate_session_start(void); /* tmate-debug.c */ From b97568cac283b6ff847521014524bd310714ebb4 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 23 Jul 2013 16:21:52 -0400 Subject: [PATCH 042/703] Fix status bar timeout when the client is not yet connected to the session --- tmate-msg.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tmate-msg.c b/tmate-msg.c index 2ab663f6..001c7cef 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -23,10 +23,15 @@ static void tmate_status_message_client(struct client *c, const char *message) msg->msg_time = time(NULL); msg->msg = xstrdup(c->message_string); - if (!s) - return; + if (s) { + limit = options_get_number(&s->options, "message-limit"); + delay = options_get_number(&s->options, "tmate-display-time"); + } else { + /* Very early in the connection process we won't have a session */ + limit = options_get_number(&global_s_options, "message-limit"); + delay = options_get_number(&global_s_options, "tmate-display-time"); + } - limit = options_get_number(&s->options, "message-limit"); if (ARRAY_LENGTH(&c->message_log) > limit) { limit = ARRAY_LENGTH(&c->message_log) - limit; for (i = 0; i < limit; i++) { @@ -36,7 +41,6 @@ static void tmate_status_message_client(struct client *c, const char *message) } } - delay = options_get_number(&c->session->options, "tmate-display-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; From 13d3439933410669c21916ac60977719ea469a11 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 22 Jul 2013 19:45:34 -0400 Subject: [PATCH 043/703] SSH key passphrase support Closes #6 --- tmate-encoder.c | 1 + tmate-session.c | 8 ++- tmate-ssh-client.c | 122 ++++++++++++++++++++++++++++++++++++--------- tmate.h | 4 +- tmux.h | 17 +++++++ window-copy.c | 42 +++++++++++++++- 6 files changed, 165 insertions(+), 29 deletions(-) diff --git a/tmate-encoder.c b/tmate-encoder.c index e4a95a66..4ec59447 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -19,6 +19,7 @@ static int msgpack_write(void *data, const char *buf, unsigned int len) void tmate_encoder_init(struct tmate_encoder *encoder) { msgpack_packer_init(&encoder->pk, encoder, &msgpack_write); + encoder->ev_readable.ev_flags = 0; encoder->buffer = evbuffer_new(); } diff --git a/tmate-session.c b/tmate-session.c index bbca4c12..02469c59 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -87,7 +87,7 @@ static void lookup_and_connect(void) hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - tmate_status_message("Looking up %s...", TMATE_HOST); + tmate_info("Looking up %s...", TMATE_HOST); (void)evdns_getaddrinfo(ev_dnsbase, TMATE_HOST, NULL, &hints, dns_cb, NULL); } @@ -96,10 +96,14 @@ void tmate_session_init(void) { tmate_catch_sigsegv(); - TAILQ_INIT(&tmate_session.clients); tmate_encoder_init(&tmate_session.encoder); tmate_decoder_init(&tmate_session.decoder); + TAILQ_INIT(&tmate_session.clients); + + tmate_session.need_passphrase = 0; + tmate_session.passphrase = NULL; + /* The header will be written as soon as the first client connects */ tmate_write_header(); } diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 50842d97..a930e28c 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -10,10 +10,11 @@ static void consume_channel(struct tmate_ssh_client *client); static void flush_input_stream(struct tmate_ssh_client *client); static void __flush_input_stream(evutil_socket_t fd, short what, void *arg); static void __on_session_event(evutil_socket_t fd, short what, void *arg); -static void printflike2 disconnect_session(struct tmate_ssh_client *client, - const char *fmt, ...); +static void printflike2 kill_session(struct tmate_ssh_client *client, + const char *fmt, ...); static void printflike2 reconnect_session(struct tmate_ssh_client *client, const char *fmt, ...); +static void on_session_event(struct tmate_ssh_client *client); static void log_function(ssh_session session, int priority, const char *message, void *userdata) @@ -79,7 +80,7 @@ static void connection_complete(struct tmate_ssh_client *connected_client) continue; assert(!client->has_encoder); - tmate_ssh_client_free(client); + kill_session(client, NULL); } } @@ -99,6 +100,66 @@ static char *get_identity(void) return identity; } +static int passphrase_callback(const char *prompt, char *buf, size_t len, + int echo, int verify, void *userdata) +{ + struct tmate_ssh_client *client = userdata; + + client->tmate_session->need_passphrase = 1; + + if (client->tmate_session->passphrase) + strncpy(buf, client->tmate_session->passphrase, len); + else + strcpy(buf, ""); + + return 0; +} + +static void on_passphrase_read(const char *passphrase, void *private) +{ + struct tmate_ssh_client *client = private; + + client->tmate_session->passphrase = xstrdup(passphrase); + on_session_event(client); +} + +static void request_passphrase(struct tmate_ssh_client *client) +{ + struct window_pane *wp; + struct window_copy_mode_data *data; + + /* + * We'll display the prompt on the first pane. + * It doesn't make much sense, but it's simpler to reuse the copy mode + * and its key parsing logic compared to rolling something on our own. + */ + wp = RB_MIN(window_pane_tree, &all_window_panes); + + if (wp->mode) { + data = wp->modedata; + if (data->inputtype == WINDOW_COPY_PASSWORD) { + /* We are already requesting the passphrase */ + return; + } + window_pane_reset_mode(wp); + } + + window_pane_set_mode(wp, &window_copy_mode); + window_copy_init_from_pane(wp); + data = wp->modedata; + + data->inputtype = WINDOW_COPY_PASSWORD; + data->inputprompt = "SSH key passphrase"; + + mode_key_init(&data->mdata, &mode_key_tree_vi_edit); + + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + + data->password_cb = on_passphrase_read; + data->password_cb_private = client; +} + static void on_session_event(struct tmate_ssh_client *client) { char *identity; @@ -139,6 +200,11 @@ static void on_session_event(struct tmate_ssh_client *client) ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); if ((identity = get_identity())) { + /* + * FIXME libssh will continue with the next set of + * keys if the identity has a passphrase and the + * regular one doesn't. + */ ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity); free(identity); } @@ -164,7 +230,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_SERVER: if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) { - disconnect_session(client, "Cannot authenticate server"); + kill_session(client, "Cannot authenticate server"); return; } @@ -199,7 +265,7 @@ static void on_session_event(struct tmate_ssh_client *client) free(hash_str); if (!match) { - disconnect_session(client, "Cannot authenticate server"); + kill_session(client, "Cannot authenticate server"); return; } @@ -216,14 +282,18 @@ static void on_session_event(struct tmate_ssh_client *client) /* fall through */ case SSH_AUTH_CLIENT: - switch (ssh_userauth_autopubkey(session, NULL)) { + client->tried_passphrase = client->tmate_session->passphrase; + switch (ssh_userauth_autopubkey(session, client->tried_passphrase)) { case SSH_AUTH_AGAIN: return; case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: - disconnect_session(client, "Access denied. Check your SSH keys " - "(passphrases are not supported yet)."); + if (client->tmate_session->need_passphrase && + !client->tried_passphrase) + request_passphrase(client); + else + kill_session(client, "Access denied. Check your SSH keys."); return; case SSH_AUTH_ERROR: reconnect_session(client, "Auth error: %s", @@ -316,12 +386,12 @@ static void __on_session_event(evutil_socket_t fd, short what, void *arg) on_session_event(arg); } -static void __disconnect_session(struct tmate_ssh_client *client, - const char *fmt, va_list va) +static void __kill_session(struct tmate_ssh_client *client, + const char *fmt, va_list va) { struct tmate_encoder *encoder; - if (fmt) + if (fmt && TAILQ_EMPTY(&client->tmate_session->clients)) __tmate_status_message(fmt, va); else tmate_debug("Disconnecting %s", client->server_ip); @@ -348,14 +418,19 @@ static void __disconnect_session(struct tmate_ssh_client *client, client->state = SSH_NONE; } -static void printflike2 disconnect_session(struct tmate_ssh_client *client, - const char *fmt, ...) +static void printflike2 kill_session(struct tmate_ssh_client *client, + const char *fmt, ...) { va_list ap; + TAILQ_REMOVE(&client->tmate_session->clients, client, node); + va_start(ap, fmt); - __disconnect_session(client, fmt, ap); + __kill_session(client, fmt, ap); va_end(ap); + + free(client->server_ip); + free(client); } static void connect_session(struct tmate_ssh_client *client) @@ -377,8 +452,12 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client, struct timeval tv; va_list ap; +#if 1 + TAILQ_REMOVE(&client->tmate_session->clients, client, node); +#endif + va_start(ap, fmt); - __disconnect_session(client, fmt, ap); + __kill_session(client, fmt, ap); va_end(ap); /* Not yet implemented... */ @@ -389,16 +468,17 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client, #endif } - struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, const char *server_ip) { struct tmate_ssh_client *client; client = xmalloc(sizeof(*client)); + memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks)); ssh_callbacks_init(&client->ssh_callbacks); client->ssh_callbacks.log_function = log_function; client->ssh_callbacks.userdata = client; + client->ssh_callbacks.auth_function = passphrase_callback; client->tmate_session = session; TAILQ_INSERT_TAIL(&session->clients, client, node); @@ -409,6 +489,8 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, client->channel = NULL; client->has_encoder = 0; + client->ev_ssh.ev_flags = 0; + evtimer_assign(&client->ev_ssh_reconnect, ev_base, on_reconnect_timer, client); @@ -416,11 +498,3 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, return client; } - -void tmate_ssh_client_free(struct tmate_ssh_client *client) -{ - disconnect_session(client, NULL); - TAILQ_REMOVE(&client->tmate_session->clients, client, node); - free(client->server_ip); - free(client); -} diff --git a/tmate.h b/tmate.h index 687fd7e6..673f3a6d 100644 --- a/tmate.h +++ b/tmate.h @@ -117,6 +117,7 @@ struct tmate_ssh_client { * has to be in the struct itself). */ struct ssh_callbacks_struct ssh_callbacks; + char *tried_passphrase; ssh_session session; ssh_channel channel; @@ -127,7 +128,6 @@ TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); extern struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, const char *server_ip); -extern void tmate_ssh_client_free(struct tmate_ssh_client *client); /* tmate-session.c */ @@ -141,6 +141,8 @@ struct tmate_session { * losers are disconnected and killed. */ struct tmate_ssh_clients clients; + int need_passphrase; + char *passphrase; }; extern struct tmate_session tmate_session; diff --git a/tmux.h b/tmux.h index c28cb9d9..94858f6d 100644 --- a/tmux.h +++ b/tmux.h @@ -2245,6 +2245,10 @@ enum window_copy_input_type { WINDOW_COPY_JUMPTOFORWARD, WINDOW_COPY_JUMPTOBACK, WINDOW_COPY_GOTOLINE, + +#ifdef TMATE + WINDOW_COPY_PASSWORD, +#endif }; /* @@ -2263,6 +2267,11 @@ enum window_copy_input_type { * a newly-allocated screen structure (which is deallocated when the * mode ends). */ + +#ifdef TMATE +typedef void (*copy_password_callback)(const char *password, void *private); +#endif + struct window_copy_mode_data { struct screen screen; @@ -2295,6 +2304,11 @@ struct window_copy_mode_data { enum window_copy_input_type jumptype; char jumpchar; + +#ifdef TMATE + copy_password_callback password_cb; + void *password_cb_private; +#endif }; @@ -2306,6 +2320,9 @@ void printflike2 window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_pageup(struct window_pane *); +int window_copy_update_selection(struct window_pane *); +void window_copy_redraw_screen(struct window_pane *); + /* window-choose.c */ extern const struct window_mode window_choose_mode; void window_choose_add(struct window_pane *, diff --git a/window-copy.c b/window-copy.c index d3af1efb..3cb7eb38 100644 --- a/window-copy.c +++ b/window-copy.c @@ -34,7 +34,6 @@ void window_copy_mouse( struct window_pane *, struct session *, struct mouse_event *); void window_copy_redraw_lines(struct window_pane *, u_int, u_int); -void window_copy_redraw_screen(struct window_pane *); void window_copy_write_line( struct window_pane *, struct screen_write_ctx *, u_int); void window_copy_write_lines( @@ -52,7 +51,6 @@ void window_copy_search_down(struct window_pane *, const char *); void window_copy_goto_line(struct window_pane *, const char *); void window_copy_update_cursor(struct window_pane *, u_int, u_int); void window_copy_start_selection(struct window_pane *); -int window_copy_update_selection(struct window_pane *); void *window_copy_get_selection(struct window_pane *, size_t *); void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); void window_copy_copy_pipe( @@ -136,6 +134,10 @@ window_copy_init(struct window_pane *wp) data->backing = NULL; +#ifdef TMATE + data->password_cb = NULL; +#endif + return (s); } @@ -614,7 +616,10 @@ __window_copy_key(struct window_pane *wp, struct session *sess, int key) case WINDOW_COPY_JUMPTOFORWARD: case WINDOW_COPY_JUMPTOBACK: case WINDOW_COPY_NUMERICPREFIX: +#ifdef TMATE + case WINDOW_COPY_PASSWORD: break; +#endif case WINDOW_COPY_SEARCHUP: if (cmd == MODEKEYCOPY_SEARCHAGAIN) { for (; np != 0; np--) { @@ -746,7 +751,18 @@ window_copy_key_input(struct window_pane *wp, int key) window_copy_goto_line(wp, data->inputstr); *data->inputstr = '\0'; break; +#ifdef TMATE + case WINDOW_COPY_PASSWORD: + if (data->password_cb) { + data->password_cb(data->inputstr, + data->password_cb_private); + } + *data->inputstr = '\0'; + window_copy_copy_selection(wp, -1); + window_pane_reset_mode(wp); +#endif } + data->numprefix = -1; return (1); case MODEKEY_OTHER: @@ -1081,24 +1097,46 @@ window_copy_write_line( struct screen *s = &data->screen; struct options *oo = &wp->window->options; struct grid_cell gc; +#ifdef TMATE + char hdr[256]; +#else char hdr[32]; +#endif size_t last, xoff = 0, size = 0; window_mode_attrs(&gc, oo); last = screen_size_y(s) - 1; if (py == 0) { +#ifdef TMATE + if (data->inputtype != WINDOW_COPY_PASSWORD) { +#endif size = xsnprintf(hdr, sizeof hdr, "[%u/%u]", data->oy, screen_hsize(data->backing)); if (size > screen_size_x(s)) size = screen_size_x(s); screen_write_cursormove(ctx, screen_size_x(s) - size, 0); screen_write_puts(ctx, &gc, "%s", hdr); +#ifdef TMATE + } +#endif } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { xoff = size = xsnprintf(hdr, sizeof hdr, "Repeat: %u", data->numprefix); } else { + +#ifdef TMATE + if (data->inputtype == WINDOW_COPY_PASSWORD) { + int password_len = strlen(data->inputstr); + xoff = size = xsnprintf(hdr, sizeof hdr, "%s: ", data->inputprompt); + memset(hdr+xoff, '*', password_len); + xoff += password_len; + size += password_len; + hdr[xoff] = '\0'; + } + else +#endif xoff = size = xsnprintf(hdr, sizeof hdr, "%s: %s", data->inputprompt, data->inputstr); } From 089a594a68c95e1cbad62700fd50ce7ef9d63996 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 29 Jul 2013 09:50:42 -0400 Subject: [PATCH 044/703] Better stack traces --- Makefile.am | 1 + tmate-debug.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2268de88..78916ecc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,7 @@ endif CFLAGS += -Wno-unused-parameter -Wno-unused-variable CFLAGS += -Ilibssh/include/ -Imsgpack/src +CFLAGS += -rdynamic # for stack traces if IS_DEVENV CFLAGS += -DDEVENV diff --git a/tmate-debug.c b/tmate-debug.c index 15dd31a6..9e160c43 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -62,13 +62,13 @@ void tmate_print_trace(void) size = backtrace (array, 20); strings = backtrace_symbols (array, size); - tmate_debug ("============ %zd stack frames ============", size); + tmate_info ("============ %zd stack frames ============", size); for (i = 1; i < size; i++) { #if DEBUG if (print_resolved_stack_frame(strings[i]) < 0) #endif - tmate_debug("%s", strings[i]); + tmate_info("%s", strings[i]); } free (strings); From 31326220faf803a75c9780f9320c7a6e075918ec Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 13 Aug 2013 15:41:41 -0400 Subject: [PATCH 045/703] [server lost] bug fix: The DNS resolver must not be freed on MacOSX --- tmate-session.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tmate-session.c b/tmate-session.c index 02469c59..c14def66 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -68,8 +68,12 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) evutil_freeaddrinfo(addr); - evdns_base_free(ev_dnsbase, 0); - ev_dnsbase = NULL; + /* + * XXX For some reason, freeing the DNS resolver makes MacOSX flip out... + * not sure what's going on... + * evdns_base_free(ev_dnsbase, 0); + * ev_dnsbase = NULL; + */ } static void lookup_and_connect(void) From aab28e379685c1c2b839c105662047b606a3d595 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 13 Aug 2013 15:43:11 -0400 Subject: [PATCH 046/703] Update version to 1.8.7 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a86799f0..7d1d1864 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmate, 1.8.6) +AC_INIT(tmate, 1.8.7) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) From 246bec30bc92c8cbb08eed7cadc6257608cc8491 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 16 Aug 2013 13:24:09 -0400 Subject: [PATCH 047/703] Updated libssh --- libssh/CMakeLists.txt | 5 + libssh/CTestConfig.cmake | 2 +- libssh/ChangeLog | 76 +- libssh/ConfigureChecks.cmake | 63 +- libssh/DefineOptions.cmake | 1 + libssh/INSTALL | 8 +- .../cmake/Modules/DefineCompilerFlags.cmake | 6 +- libssh/cmake/Modules/FindCMocka.cmake | 19 +- libssh/cmake/Modules/FindGSSAPI.cmake | 324 ++++++ libssh/cmake/Modules/FindNSIS.cmake | 32 +- libssh/cmake/Modules/UseDoxygen.cmake | 42 +- libssh/config.h.cmake | 14 + libssh/doc/TracFooter.html | 1 - libssh/doc/TracHeader.html | 4 - libssh/doc/doxy.config.in | 190 +++- libssh/doc/linking.dox | 12 +- libssh/doc/mainpage.dox | 41 +- libssh/examples/CMakeLists.txt | 38 +- libssh/examples/authentication.c | 9 + libssh/examples/libssh_scp.c | 15 +- libssh/examples/proxy.c | 347 +++++++ libssh/examples/sample.c | 28 +- libssh/examples/samplesftp.c | 4 +- libssh/examples/samplesshd-cb.c | 306 ++++++ libssh/examples/samplesshd-kbdint.c | 4 +- libssh/examples/samplesshd-tty.c | 10 +- libssh/examples/sshnetcat.c | 14 +- libssh/include/libssh/agent.h | 1 + libssh/include/libssh/auth.h | 9 +- libssh/include/libssh/callbacks.h | 395 ++++++++ libssh/include/libssh/channels.h | 2 - libssh/include/libssh/gssapi.h | 45 + libssh/include/libssh/kex.h | 2 +- libssh/include/libssh/libssh.h | 22 +- libssh/include/libssh/libsshpp.hpp | 2 +- libssh/include/libssh/messages.h | 6 +- libssh/include/libssh/pki.h | 6 +- libssh/include/libssh/priv.h | 97 +- libssh/include/libssh/server.h | 5 +- libssh/include/libssh/session.h | 12 +- libssh/include/libssh/sftp.h | 25 +- libssh/include/libssh/ssh2.h | 7 + libssh/src/CMakeLists.txt | 19 + libssh/src/agent.c | 129 ++- libssh/src/auth.c | 174 ++-- libssh/src/auth1.c | 22 +- libssh/src/bind.c | 8 +- libssh/src/callbacks.c | 50 +- libssh/src/channels.c | 417 ++++---- libssh/src/channels1.c | 24 +- libssh/src/client.c | 90 +- libssh/src/config.c | 4 +- libssh/src/connect.c | 63 +- libssh/src/dh.c | 20 +- libssh/src/ecdh.c | 70 +- libssh/src/error.c | 10 +- libssh/src/getpass.c | 6 + libssh/src/gssapi.c | 947 ++++++++++++++++++ libssh/src/kex.c | 28 +- libssh/src/kex1.c | 40 +- libssh/src/known_hosts.c | 52 +- libssh/src/legacy.c | 25 +- libssh/src/log.c | 127 ++- libssh/src/messages.c | 499 +++++++-- libssh/src/misc.c | 6 +- libssh/src/options.c | 27 +- libssh/src/packet.c | 75 +- libssh/src/packet1.c | 30 +- libssh/src/packet_cb.c | 17 +- libssh/src/pki.c | 17 +- libssh/src/poll.c | 16 +- libssh/src/scp.c | 44 +- libssh/src/server.c | 203 ++-- libssh/src/session.c | 107 +- libssh/src/sftp.c | 73 +- libssh/src/sftpserver.c | 35 +- libssh/src/socket.c | 34 +- libssh/src/wrapper.c | 67 +- libssh/tests/client/torture_session.c | 2 + libssh/tests/torture.c | 5 +- libssh/tests/unittests/torture_options.c | 18 + libssh/tests/unittests/torture_pki.c | 18 +- libssh/tests/valgrind.supp | 11 + 83 files changed, 4634 insertions(+), 1246 deletions(-) create mode 100644 libssh/cmake/Modules/FindGSSAPI.cmake delete mode 100644 libssh/doc/TracFooter.html delete mode 100644 libssh/doc/TracHeader.html create mode 100644 libssh/examples/proxy.c create mode 100644 libssh/examples/samplesshd-cb.c create mode 100644 libssh/include/libssh/gssapi.h create mode 100644 libssh/src/gssapi.c diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt index 81ac5884..e783c2b4 100644 --- a/libssh/CMakeLists.txt +++ b/libssh/CMakeLists.txt @@ -67,6 +67,10 @@ endif(WITH_GCRYPT) set(CMAKE_THREAD_PREFER_PTHREADS ON) find_package(Threads) +if (WITH_GSSAPI) + find_package(GSSAPI) +endif (WITH_GSSAPI) + # config.h checks include(ConfigureChecks.cmake) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) @@ -122,6 +126,7 @@ message(STATUS "libgcrypt support: ${WITH_GCRYPT}") message(STATUS "SSH-1 support: ${WITH_SSH1}") message(STATUS "SFTP support: ${WITH_SFTP}") message(STATUS "Server support : ${WITH_SERVER}") +message(STATUS "GSSAPI support : ${WITH_GSSAPI}") message(STATUS "Pcap debugging support : ${WITH_PCAP}") message(STATUS "With static library: ${WITH_STATIC_LIB}") message(STATUS "Unit testing: ${WITH_TESTING}") diff --git a/libssh/CTestConfig.cmake b/libssh/CTestConfig.cmake index d8a41831..20d2e8f5 100644 --- a/libssh/CTestConfig.cmake +++ b/libssh/CTestConfig.cmake @@ -1,7 +1,7 @@ set(UPDATE_TYPE "true") set(CTEST_PROJECT_NAME "libssh") -set(CTEST_NIGHTLY_START_TIME "01:00:00 CET") +set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "test.libssh.org") diff --git a/libssh/ChangeLog b/libssh/ChangeLog index 5bc0784a..6f4d906c 100644 --- a/libssh/ChangeLog +++ b/libssh/ChangeLog @@ -1,13 +1,75 @@ ChangeLog ========== -version 0.5.x (released 2012-xx-xx) - * Added new PKI infrastructure. - * Added simplified user auth functions. - * Added ECDSA pubkey support. - * Added ECDSA hostkey support. - * Added diffie-hellman-group14-sha1 support. - * Fixed a ton of bugs. +version 0.6.0 (released 2013-XX-XX) + * Added new publicy key API. + * Added new userauth API. + * Added gssapi-mic userauth. + * Added new callback based server API. + * Added Elliptic Curve DSA (ECDSA) support (with OpenSSL). + * Added Elliptic Curve Diffie Hellman (ECDH) support. + * Added improved logging system. + * Added SSH-agent forwarding. + * Added key-reexchange. + * Improved documentation. + * Fixed timeout handling. + +version 0.5.5 (released 2013-07-26) + * BUG 103: Fix ProxyCommand parsing. + * Fix setting -D_FORTIFY_SOURCE=2. + * Fix pollset error return if emtpy. + * Fix NULL pointer checks in channel functions. + * Several bugfixes. + +version 0.5.4 (released 2013-01-22) + * CVE-2013-0176 - NULL dereference leads to denial of service + * Fixed several NULL pointer dereferences in SSHv1. + * Fixed a free crash bug in options parsing. + +version 0.5.3 (released 2012-11-20) + * CVE-2012-4559 Fixed multiple double free() flaws. + * CVE-2012-4560 Fixed multiple buffer overflow flaws. + * CVE-2012-4561 Fixed multiple invalid free() flaws. + * BUG #84 - Fix bug in sftp_mkdir not returning on error. + * BUG #85 - Fixed a possible channel infinite loop if the connection dropped. + * BUG #88 - Added missing channel request_state and set it to accepted. + * BUG #89 - Reset error state to no error on successful SSHv1 authentiction. + * Fixed a possible use after free in ssh_free(). + * Fixed multiple possible NULL pointer dereferences. + * Fixed multiple memory leaks in error paths. + * Fixed timeout handling. + * Fixed regression in pre-connected socket setting. + * Handle all unknown global messages. + +version 0.5.2 (released 2011-09-17) + * Increased window size x10. + * Fixed SSHv1. + * Fixed bugged lists. + * Fixed use-after-free + inconsistent callbacks call in poll. + * Fixed scp documentation. + * Fixed possible infinite loop in channel_read(). + * Fixed handling of short reads of sftp_async_read(). + * Fixed handling request service timeout in blocking mode. + * Fixed ssh_auth_list() documentation. + * Fixed incorrect return values in ssh_channel_write(). + * Fixed an infinite loop in the termination callback. + * Fixed handling of SSH_AGAIN in channel_open(). + * Fixed "status -5 inflating zlib packet" + +version 0.5.1 (released 2011-08-09) + * Added checks for NULL pointers in string.c. + * Set the channel max packet size to 32768. + * Don't (de)compress empty buffers. + * Fixed ssh_scp_write so it works when doing recursive copy. + * Fixed another source of endless wait. + * Fixed an endless loop in case of a channel_open error. + * Fixed session timeout handling. + * Fixed ssh_channel_from_local() loop. + * Fixed permissions of scp example when we copy a file. + * Workaround ssh_get_user_home_dir on LDAP users. + * Added pkg-config support for libssh_threads. + * Fixed compilation without server and sftp modes. + * Fix static .lib overwriting on Windows. version 0.5.0 (released 2011-06-01) * Added ssh_ prefix to all functions. diff --git a/libssh/ConfigureChecks.cmake b/libssh/ConfigureChecks.cmake index b0485d98..1c89c4c7 100644 --- a/libssh/ConfigureChecks.cmake +++ b/libssh/ConfigureChecks.cmake @@ -49,6 +49,7 @@ endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) check_include_file(argp.h HAVE_ARGP_H) check_include_file(pty.h HAVE_PTY_H) check_include_file(termios.h HAVE_TERMIOS_H) +check_include_file(unistd.h HAVE_UNISTD_H) if (WIN32) check_include_files("winsock2.h;ws2tcpip.h;wspiapi.h" HAVE_WSPIAPI_H) @@ -56,12 +57,6 @@ if (WIN32) message(STATUS "WARNING: Without wspiapi.h, this build will only work on Windows XP and newer versions") endif (NOT HAVE_WSPIAPI_H) check_include_files("winsock2.h;ws2tcpip.h" HAVE_WS2TCPIP_H) - if (HAVE_WSPIAPI_H OR HAVE_WS2TCPIP_H) - set(HAVE_GETADDRINFO TRUE) - set(HAVE_GETHOSTBYNAME TRUE) - endif (HAVE_WSPIAPI_H OR HAVE_WS2TCPIP_H) - - set(HAVE_SELECT TRUE) endif (WIN32) set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) @@ -101,12 +96,30 @@ endif (NOT WITH_GCRYPT) check_function_exists(strncpy HAVE_STRNCPY) check_function_exists(vsnprintf HAVE_VSNPRINTF) check_function_exists(snprintf HAVE_SNPRINTF) +check_function_exists(poll HAVE_POLL) +check_function_exists(select HAVE_SELECT) +check_function_exists(getaddrinfo HAVE_GETADDRINFO) +check_function_exists(ntohll HAVE_NTOHLL) +check_function_exists(htonll HAVE_HTONLL) if (WIN32) + check_function_exists(_strtoui64 HAVE__STRTOUI64) + check_function_exists(_vsnprintf_s HAVE__VSNPRINTF_S) check_function_exists(_vsnprintf HAVE__VSNPRINTF) check_function_exists(_snprintf HAVE__SNPRINTF) check_function_exists(_snprintf_s HAVE__SNPRINTF_S) + + if (HAVE_WSPIAPI_H OR HAVE_WS2TCPIP_H) + set(HAVE_GETADDRINFO TRUE) + set(HAVE_GETHOSTBYNAME TRUE) + if (MSVC) + set(HAVE_NTOHLL TRUE) + set(HAVE_HTONLL TRUE) + endif (MSVC) + endif (HAVE_WSPIAPI_H OR HAVE_WS2TCPIP_H) + + set(HAVE_SELECT TRUE) endif (WIN32) if (UNIX) @@ -114,7 +127,8 @@ if (UNIX) # libsocket (Solaris) check_library_exists(socket getaddrinfo "" HAVE_LIBSOCKET) if (HAVE_LIBSOCKET) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} socket) + set(HAVE_GETADDRINFO TRUE) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} socket) endif (HAVE_LIBSOCKET) # libnsl/inet_pton (Solaris) @@ -133,12 +147,7 @@ if (UNIX) endif (HAVE_LIBRT OR HAVE_CLOCK_GETTIME) check_library_exists(util forkpty "" HAVE_LIBUTIL) - check_function_exists(getaddrinfo HAVE_GETADDRINFO) - check_function_exists(poll HAVE_POLL) - check_function_exists(select HAVE_SELECT) check_function_exists(cfmakeraw HAVE_CFMAKERAW) - check_function_exists(ntohll HAVE_NTOHLL) - check_function_exists(htonll HAVE_HTONLL) check_function_exists(strtoull HAVE_STRTOULL) check_function_exists(__strtoull HAVE___STRTOULL) endif (UNIX) @@ -165,6 +174,32 @@ if (CMAKE_HAVE_THREADS_LIBRARY) endif (CMAKE_HAVE_THREADS_LIBRARY) # OPTIONS +check_c_source_compiles(" +__thread int tls; + +int main(void) { + return 0; +}" HAVE_GCC_THREAD_LOCAL_STORAGE) + +check_c_source_compiles(" +__declspec(thread) int tls; + +int main(void) { + return 0; +}" HAVE_MSC_THREAD_LOCAL_STORAGE) + +check_c_source_compiles(" +#include + +int main(void) +{ + char buf[] = \"This is some content\"; + + memset(buf, '\\\\0', sizeof(buf)); __asm__ volatile(\"\" : : \"r\"(&buf) : \"memory\"); + + return 0; +}" HAVE_GCC_VOLATILE_MEMORY_PROTECTION) + if (WITH_DEBUG_CRYPTO) set(DEBUG_CRYPTO 1) endif (WITH_DEBUG_CRYPTO) @@ -173,6 +208,10 @@ if (WITH_DEBUG_CALLTRACE) set(DEBUG_CALLTRACE 1) endif (WITH_DEBUG_CALLTRACE) +if (WITH_GSSAPI AND NOT GSSAPI_FOUND) + set(WITH_GSSAPI 0) +endif (WITH_GSSAPI AND NOT GSSAPI_FOUND) + # ENDIAN if (NOT WIN32) test_big_endian(WORDS_BIGENDIAN) diff --git a/libssh/DefineOptions.cmake b/libssh/DefineOptions.cmake index ea8265c0..6913f040 100644 --- a/libssh/DefineOptions.cmake +++ b/libssh/DefineOptions.cmake @@ -1,3 +1,4 @@ +option(WITH_GSSAPI "Build with GSSAPI support" ON) option(WITH_ZLIB "Build with ZLIB support" ON) option(WITH_SSH1 "Build with SSH1 support" OFF) option(WITH_SFTP "Build with SFTP support" ON) diff --git a/libssh/INSTALL b/libssh/INSTALL index a772b824..25960367 100644 --- a/libssh/INSTALL +++ b/libssh/INSTALL @@ -21,7 +21,7 @@ build and run libssh successfully with an older version, please let us know. Windows binaries known to be working: - http://www.slproweb.com/products/Win32OpenSSL.html -- http://www.winimage.com/zLibDll/index.html +- http://zlib.net/ -> zlib compiled DLL We installed them in C:\Program Files @@ -34,7 +34,9 @@ GNU/Linux, MacOS X, MSYS/MinGW: cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug .. make -On Windows you should choose a makefile gernerator with -G. +On Windows you should choose a makefile gernerator with -G or use + + cmake-gui.exe .. ### CMake standard options Here is a list of the most interesting options provided out of the box by @@ -86,7 +88,7 @@ If you want to install libssh after compilation run: ## Running -The libssh binary can be found in the `build/libssh` directory. +The libssh binary can be found in the `build/src` directory. You can use `build/examples/samplessh` which is a sample client to test libssh on UNIX. diff --git a/libssh/cmake/Modules/DefineCompilerFlags.cmake b/libssh/cmake/Modules/DefineCompilerFlags.cmake index 582ea1ca..0ab8802c 100644 --- a/libssh/cmake/Modules/DefineCompilerFlags.cmake +++ b/libssh/cmake/Modules/DefineCompilerFlags.cmake @@ -28,10 +28,10 @@ if (UNIX AND NOT WIN32) if (CMAKE_BUILD_TYPE) string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) - if (NOT CMAKE_BUILD_TYPE_LOWER MATCHES debug) - check_c_compiler_flag("-D_FORTIFY_SOURCE=2" WITH_FORTIFY_SOURCE) + if (CMAKE_BUILD_TYPE_LOWER MATCHES (release|relwithdebinfo|minsizerel)) + check_c_compiler_flag("-Wp,-D_FORTIFY_SOURCE=2" WITH_FORTIFY_SOURCE) if (WITH_FORTIFY_SOURCE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wp,-D_FORTIFY_SOURCE=2") endif (WITH_FORTIFY_SOURCE) endif() endif() diff --git a/libssh/cmake/Modules/FindCMocka.cmake b/libssh/cmake/Modules/FindCMocka.cmake index 2dd9fc5f..76b4ba74 100644 --- a/libssh/cmake/Modules/FindCMocka.cmake +++ b/libssh/cmake/Modules/FindCMocka.cmake @@ -21,6 +21,23 @@ #============================================================================= # +set(_CMOCKA_ROOT_HINTS +) + +set(_CMOCKA_ROOT_PATHS + "$ENV{PROGRAMFILES}/cmocka" +) + +find_path(CMOCKA_ROOT_DIR + NAMES + include/cmocka.h + HINTS + ${_CMOCKA_ROOT_HINTS} + PATHS + ${_CMOCKA_ROOT_PATHS} +) +mark_as_advanced(CMOCKA_ROOT_DIR) + find_path(CMOCKA_INCLUDE_DIR NAMES cmocka.h @@ -32,7 +49,7 @@ find_library(CMOCKA_LIBRARY NAMES cmocka PATHS - ${CMOCKA_ROOT_DIR}/include + ${CMOCKA_ROOT_DIR}/lib ) if (CMOCKA_LIBRARY) diff --git a/libssh/cmake/Modules/FindGSSAPI.cmake b/libssh/cmake/Modules/FindGSSAPI.cmake new file mode 100644 index 00000000..8520d35d --- /dev/null +++ b/libssh/cmake/Modules/FindGSSAPI.cmake @@ -0,0 +1,324 @@ +# - Try to find GSSAPI +# Once done this will define +# +# KRB5_CONFIG - Path to krb5-config +# GSSAPI_ROOT_DIR - Set this variable to the root installation of GSSAPI +# +# Read-Only variables: +# GSSAPI_FLAVOR_MIT - set to TURE if MIT Kerberos has been found +# GSSAPI_FLAVOR_HEIMDAL - set to TRUE if Heimdal Keberos has been found +# GSSAPI_FOUND - system has GSSAPI +# GSSAPI_INCLUDE_DIR - the GSSAPI include directory +# GSSAPI_LIBRARIES - Link these to use GSSAPI +# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI +# +#============================================================================= +# Copyright (c) 2013 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# + +find_path(GSSAPI_ROOT_DIR + NAMES + include/gssapi.h + include/gssapi/gssapi.h + HINTS + ${_GSSAPI_ROOT_HINTS} + PATHS + ${_GSSAPI_ROOT_PATHS} +) +mark_as_advanced(GSSAPI_ROOT_DIR) + +if (UNIX) + find_program(KRB5_CONFIG + NAMES + krb5-config + PATHS + ${GSSAPI_ROOT_DIR}/bin + /opt/local/bin) + mark_as_advanced(KRB5_CONFIG) + + if (KRB5_CONFIG) + # Check if we have MIT KRB5 + execute_process( + COMMAND + ${KRB5_CONFIG} --vendor + RESULT_VARIABLE + _GSSAPI_VENDOR_RESULT + OUTPUT_VARIABLE + _GSSAPI_VENDOR_STRING) + + if (_GSSAPI_VENDOR_STRING MATCHES ".*Massachusetts.*") + set(GSSAPI_FLAVOR_MIT TRUE) + else() + execute_process( + COMMAND + ${KRB5_CONFIG} --libs gssapi + RESULT_VARIABLE + _GSSAPI_LIBS_RESULT + OUTPUT_VARIABLE + _GSSAPI_LIBS_STRING) + + if (_GSSAPI_LIBS_STRING MATCHES ".*roken.*") + set(GSSAPI_FLAVOR_HEIMDAL TRUE) + endif() + endif() + + # Get the include dir + execute_process( + COMMAND + ${KRB5_CONFIG} --cflags gssapi + RESULT_VARIABLE + _GSSAPI_INCLUDE_RESULT + OUTPUT_VARIABLE + _GSSAPI_INCLUDE_STRING) + string(REGEX REPLACE "(\r?\n)+$" "" _GSSAPI_INCLUDE_STRING "${_GSSAPI_INCLUDE_STRING}") + string(REGEX REPLACE " *-I" "" _GSSAPI_INCLUDEDIR "${_GSSAPI_INCLUDE_STRING}") + endif() + + if (NOT GSSAPI_FLAVOR_MIT AND NOT GSSAPI_FLAVOR_HEIMDAL) + # Check for HEIMDAL + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(_GSSAPI heimdal-gssapi) + endif (PKG_CONFIG_FOUND) + + if (_GSSAPI_FOUND) + set(GSSAPI_FLAVOR_HEIMDAL TRUE) + else() + find_path(_GSSAPI_ROKEN + NAMES + roken.h + PATHS + ${GSSAPI_ROOT_DIR}/include + ${_GSSAPI_INCLUDEDIR}) + if (_GSSAPI_ROKEN) + set(GSSAPI_FLAVOR_HEIMDAL TRUE) + endif() + endif () + endif() +endif (UNIX) + +find_path(GSSAPI_INCLUDE_DIR + NAMES + gssapi.h + gssapi/gssapi.h + PATHS + ${GSSAPI_ROOT_DIR}/include + ${_GSSAPI_INCLUDEDIR} +) + +if (GSSAPI_FLAVOR_MIT) + find_library(GSSAPI_LIBRARY + NAMES + gssapi_krb5 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(KRB5_LIBRARY + NAMES + krb5 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(K5CRYPTO_LIBRARY + NAMES + k5crypto + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(COM_ERR_LIBRARY + NAMES + com_err + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + if (GSSAPI_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${GSSAPI_LIBRARY} + ) + endif (GSSAPI_LIBRARY) + + if (KRB5_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${KRB5_LIBRARY} + ) + endif (KRB5_LIBRARY) + + if (K5CRYPTO_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${K5CRYPTO_LIBRARY} + ) + endif (K5CRYPTO_LIBRARY) + + if (COM_ERR_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${COM_ERR_LIBRARY} + ) + endif (COM_ERR_LIBRARY) +endif (GSSAPI_FLAVOR_MIT) + +if (GSSAPI_FLAVOR_HEIMDAL) + find_library(GSSAPI_LIBRARY + NAMES + gssapi + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(KRB5_LIBRARY + NAMES + krb5 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(HCRYPTO_LIBRARY + NAMES + hcrypto + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(COM_ERR_LIBRARY + NAMES + com_err + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(HEIMNTLM_LIBRARY + NAMES + heimntlm + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(HX509_LIBRARY + NAMES + hx509 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(ASN1_LIBRARY + NAMES + asn1 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(WIND_LIBRARY + NAMES + wind + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(ROKEN_LIBRARY + NAMES + roken + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + if (GSSAPI_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${GSSAPI_LIBRARY} + ) + endif (GSSAPI_LIBRARY) + + if (KRB5_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${KRB5_LIBRARY} + ) + endif (KRB5_LIBRARY) + + if (HCRYPTO_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${HCRYPTO_LIBRARY} + ) + endif (HCRYPTO_LIBRARY) + + if (COM_ERR_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${COM_ERR_LIBRARY} + ) + endif (COM_ERR_LIBRARY) + + if (HEIMNTLM_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${HEIMNTLM_LIBRARY} + ) + endif (HEIMNTLM_LIBRARY) + + if (HX509_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${HX509_LIBRARY} + ) + endif (HX509_LIBRARY) + + if (ASN1_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${ASN1_LIBRARY} + ) + endif (ASN1_LIBRARY) + + if (WIND_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${WIND_LIBRARY} + ) + endif (WIND_LIBRARY) + + if (ROKEN_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${WIND_LIBRARY} + ) + endif (ROKEN_LIBRARY) +endif (GSSAPI_FLAVOR_HEIMDAL) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR) + +if (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES) + set(GSSAPI_FOUND TRUE) +endif (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES) + +# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view +mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES) diff --git a/libssh/cmake/Modules/FindNSIS.cmake b/libssh/cmake/Modules/FindNSIS.cmake index 98a17c78..21f80d86 100644 --- a/libssh/cmake/Modules/FindNSIS.cmake +++ b/libssh/cmake/Modules/FindNSIS.cmake @@ -1,14 +1,15 @@ # - Try to find NSIS # Once done this will define # -# NSIS_ROOT_DIR - Set this variable to the root installation of ZLIB +# NSIS_ROOT_PATH - Set this variable to the root installation of NSIS # # Read-Only variables: +# # NSIS_FOUND - system has NSIS # NSIS_MAKE - NSIS creator executable # #============================================================================= -# Copyright (c) 2010-2011 Andreas Schneider +# Copyright (c) 2010-2013 Andreas Schneider # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. @@ -19,21 +20,36 @@ #============================================================================= # -set(_NSIS_ROOT_PATHS - C:/NSIS/Bin - "$ENV{PROGRAMFILES}/NSIS" -) +if (WIN32) + set(_NSIS_ROOT_HINTS + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS;Default]") + + set(_NSIS_ROOT_PATHS + $ENV{PROGRAMFILES}/NSIS) + + find_path(NSIS_ROOT_PATH + NAMES + Include/Library.nsh + HINTS + ${_NSIS_ROOT_HINTS} + PATHS + ${_NSIS_ROOT_PATHS} + ) + mark_as_advanced(NSIS_ROOT_PATH) +endif (WIN32) find_program(NSIS_MAKE NAMES makensis PATHS ${NSIS_ROOT_PATH} - ${NSIS_ROOT_PATH}/Bin - ${_NSIS_ROOT_PATHS} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(NSIS DEFAULT_MSG NSIS_MAKE) +if (NSIS_MAKE) + set(NSIS_FOUND TRUE) +endif (NSIS_MAKE) + mark_as_advanced(NSIS_MAKE) diff --git a/libssh/cmake/Modules/UseDoxygen.cmake b/libssh/cmake/Modules/UseDoxygen.cmake index c4ab7ccc..861afa51 100644 --- a/libssh/cmake/Modules/UseDoxygen.cmake +++ b/libssh/cmake/Modules/UseDoxygen.cmake @@ -63,27 +63,27 @@ if(DOXYGEN_FOUND AND DOXYFILE_IN_FOUND) set(DOXYFILE_PDFLATEX FALSE) set(DOXYFILE_DOT FALSE) - find_package(LATEX) - if(LATEX_COMPILER AND MAKEINDEX_COMPILER) - set(DOXYFILE_LATEX TRUE) - usedoxygen_set_default(DOXYFILE_LATEX_DIR "latex") - - set_property(DIRECTORY APPEND PROPERTY - ADDITIONAL_MAKE_CLEAN_FILES - "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") - - if(PDFLATEX_COMPILER) - set(DOXYFILE_PDFLATEX TRUE) - endif() - if(DOXYGEN_DOT_EXECUTABLE) - set(DOXYFILE_DOT TRUE) - endif() - - add_custom_command(TARGET doxygen - POST_BUILD - COMMAND ${CMAKE_MAKE_PROGRAM} - WORKING_DIRECTORY "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") - endif() + #find_package(LATEX) + #if(LATEX_COMPILER AND MAKEINDEX_COMPILER) + # set(DOXYFILE_LATEX TRUE) + # usedoxygen_set_default(DOXYFILE_LATEX_DIR "latex") + # + # set_property(DIRECTORY APPEND PROPERTY + # ADDITIONAL_MAKE_CLEAN_FILES + # "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") + # + # if(PDFLATEX_COMPILER) + # set(DOXYFILE_PDFLATEX TRUE) + # endif() + # if(DOXYGEN_DOT_EXECUTABLE) + # set(DOXYFILE_DOT TRUE) + # endif() + # + # add_custom_command(TARGET doxygen + # POST_BUILD + # COMMAND ${CMAKE_MAKE_PROGRAM} + # WORKING_DIRECTORY "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") + #endif() configure_file(${DOXYFILE_IN} ${CMAKE_CURRENT_BINARY_DIR}/doxy.config ESCAPE_QUOTES IMMEDIATE @ONLY) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/doxy.trac.in) diff --git a/libssh/config.h.cmake b/libssh/config.h.cmake index 2014e8d9..f7f8957f 100644 --- a/libssh/config.h.cmake +++ b/libssh/config.h.cmake @@ -23,6 +23,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_TERMIOS_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_OPENSSL_AES_H 1 @@ -106,6 +109,9 @@ /* Define to 1 if you have the `__strtoull' function. */ #cmakedefine HAVE___STRTOULL 1 +/* Define to 1 if you have the `_strtoui64' function. */ +#cmakedefine HAVE__STRTOUI64 1 + /*************************** LIBRARIES ***************************/ /* Define to 1 if you have the `crypto' library (-lcrypto). */ @@ -120,6 +126,14 @@ /**************************** OPTIONS ****************************/ +#cmakedefine HAVE_GCC_THREAD_LOCAL_STORAGE 1 +#cmakedefine HAVE_MSC_THREAD_LOCAL_STORAGE 1 + +#cmakedefine HAVE_GCC_VOLATILE_MEMORY_PROTECTION 1 + +/* Define to 1 if you want to enable GSSAPI */ +#cmakedefine WITH_GSSAPI 1 + /* Define to 1 if you want to enable ZLIB */ #cmakedefine WITH_ZLIB 1 diff --git a/libssh/doc/TracFooter.html b/libssh/doc/TracFooter.html deleted file mode 100644 index 867baf0e..00000000 --- a/libssh/doc/TracFooter.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/libssh/doc/TracHeader.html b/libssh/doc/TracHeader.html deleted file mode 100644 index 280a869b..00000000 --- a/libssh/doc/TracHeader.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/libssh/doc/doxy.config.in b/libssh/doc/doxy.config.in index 9810518f..276a2611 100644 --- a/libssh/doc/doxy.config.in +++ b/libssh/doc/doxy.config.in @@ -1,8 +1,10 @@ -# Doxyfile 1.8.2 +# Doxyfile 1.8.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # +# All text after a double hash (##) is considered a comment and is placed +# in front of the TAG it is preceding . # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] @@ -70,9 +72,9 @@ CREATE_SUBDIRS = NO # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. +# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, +# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, +# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English @@ -262,10 +264,10 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES -# When enabled doxygen tries to link words that correspond to documented classes, -# or namespaces to their corresponding documentation. Such a link can be -# prevented in individual cases by by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES @@ -289,7 +291,12 @@ CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter and setter methods for a property. Setting this option to YES (the default) will make doxygen replace the get and set methods by a property in the documentation. This will only work if the methods are indeed getting or setting a simple type. If this is not the case, or you want to show the methods anyway, you should set this option to NO. +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES @@ -316,11 +323,11 @@ SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and -# unions with only public data fields will be shown inline in the documentation -# of the scope in which they are defined (i.e. file, namespace, or group -# documentation), provided this scope is documented. If set to NO (the default), -# structs, classes, and unions are shown on a separate page (for HTML and Man -# pages) or section (for LaTeX and RTF). +# unions with only public data fields or simple typedef fields will be shown +# inline in the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO (the default), structs, classes, and unions are shown on a separate +# page (for HTML and Man pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO @@ -334,30 +341,14 @@ INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = YES -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols. - -SYMBOL_CACHE_SIZE = 0 - -# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be -# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given -# their name and scope. Since this can be an expensive process and often the -# same symbol appear multiple times in the code, doxygen keeps a cache of -# pre-resolved symbols. If the cache is too small doxygen will become slower. -# If the cache is too large, memory is wasted. The cache size is given by this -# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols. +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can +# be an expensive process and often the same symbol appear multiple times in +# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too +# small doxygen will become slower. If the cache is too large, memory is wasted. +# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid +# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 +# symbols. LOOKUP_CACHE_SIZE = 0 @@ -368,7 +359,7 @@ LOOKUP_CACHE_SIZE = 0 # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES +# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO @@ -549,7 +540,8 @@ GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = @@ -607,7 +599,8 @@ LAYOUT_FILE = # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this -# feature you need bibtex and perl available in the search path. +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = @@ -776,8 +769,10 @@ IMAGE_PATH = # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. +# If FILTER_PATTERNS is specified, this tag will be ignored. +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. INPUT_FILTER = @@ -806,6 +801,13 @@ FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- @@ -947,7 +949,7 @@ HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the -# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. @@ -1215,6 +1217,13 @@ FORMULA_TRANSPARENT = YES USE_MATHJAX = NO +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax @@ -1232,6 +1241,11 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript +# pieces of code that will be used on startup of the MathJax code. + +MATHJAX_CODEFILE = + # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using @@ -1243,15 +1257,55 @@ MATHJAX_EXTENSIONS = SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvantages are that it is more difficult to setup -# and does not have live searching capabilities. +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. SERVER_BASED_SEARCH = NO +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search +# engine library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- @@ -1259,7 +1313,7 @@ SERVER_BASED_SEARCH = NO # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. -GENERATE_LATEX = @DOXYFILE_LATEX@ +GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be @@ -1289,7 +1343,7 @@ COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4wide will be used. +# executive. If left blank a4 will be used. PAPER_TYPE = a4 @@ -1312,6 +1366,13 @@ LATEX_HEADER = LATEX_FOOTER = +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images +# or other source files which should be copied to the LaTeX output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. + +LATEX_EXTRA_FILES = + # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references @@ -1456,6 +1517,21 @@ XML_DTD = XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files +# that can be used to generate PDF. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. If left blank docbook will be used as the default path. + +DOCBOOK_OUTPUT = docbook + #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -1605,6 +1681,12 @@ ALLEXTERNALS = YES EXTERNAL_GROUPS = YES +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed +# in the related pages index. If set to NO, only the current project's +# pages will be listed. + +EXTERNAL_PAGES = YES + # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). @@ -1659,7 +1741,7 @@ DOT_NUM_THREADS = 0 # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. -DOT_FONTNAME = FreeSans +DOT_FONTNAME = # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. @@ -1701,7 +1783,7 @@ UML_LOOK = NO # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more -# managable. Set this to 0 for no limit. Note that the threshold may be +# manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 diff --git a/libssh/doc/linking.dox b/libssh/doc/linking.dox index 16dfab98..8cbc415c 100644 --- a/libssh/doc/linking.dox +++ b/libssh/doc/linking.dox @@ -17,8 +17,14 @@ On UNIX systems linking against the static version of the library is the same as linking against the shared library. Both have the same name. Some build system require to use the full path to the static library. -On Windows you need to define LIBSSH_STATIC in the compiler command -line. This is required cause the dynamic library needs to specify the -dllimport attribute. +To be able to compile the application you're developing you need to either pass +LIBSSH_STATIC as a define in the compiler command line or define it before you +include libssh.h. This is required cause the dynamic library needs to specify +the dllimport attribute. + +@code +#define LIBSSH_STATIC 1 +#include +@endcode */ diff --git a/libssh/doc/mainpage.dox b/libssh/doc/mainpage.dox index fc65e413..5fb695a6 100644 --- a/libssh/doc/mainpage.dox +++ b/libssh/doc/mainpage.dox @@ -19,24 +19,29 @@ the interesting functions as you go. The libssh library provides: - - Full C library functions for manipulating a client-side SSH connection - - SSH2 and SSH1 protocol compliant - - Fully configurable sessions - - Server support - - SSH agent authentication support - - Support for AES-128, AES-192, AES-256, Blowfish, 3DES in CBC mode, and AES in CTR mode - - Supports OpenSSL and GCrypt - - Use multiple SSH connections in a same process, at same time - - Use multiple channels in the same connection - - Thread safety when using different sessions at same time - - POSIX-like SFTP (Secure File Transfer) implementation with openssh extension support - - SCP implementation - - Large file system support (files bigger than 4GB) - - RSA and DSS server public key supported - - Compression support (with zlib) - - Public key (RSA and DSS), password and keyboard-interactive authentication - - Full poll()/WSAPoll() support and a poll-emulation for Win32. - - Runs and tested under x86_64, x86, ARM, Sparc32, PPC under Linux, BSD, MacOSX, Solaris and Windows + - Key Exchange Methods: ecdh-sha2-nistp256, diffie-hellman-group1-sha1, diffie-hellman-group14-sha1 + - Hostkey Types: ecdsa-sha2-nistp256, ssh-dss, ssh-rsa + - Ciphers: aes256-ctr, aes192-ctr, aes128-ctr, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, des-cbc-ssh1, blowfish-cbc, none + - Compression Schemes: zlib, zlib@openssh.com, none + - MAC hashes: hmac-sha1, none + - Authentication: none, password, public-key, hostbased, keyboard-interactive, gssapi-with-mic + - Channels: shell, exec (incl. SCP wrapper), direct-tcpip, subsystem, auth-agent-req@openssh.com + - Global Requests: tcpip-forward, forwarded-tcpip + - Channel Requests: x11, pty, exit-status, signal, exit-signal, keepalive@openssh.com, auth-agent-req@openssh.com + - Subsystems: sftp(version 3), publickey(version 2), OpenSSH Extensions + - SFTP: statvfs@openssh.com, fstatvfs@openssh.com + - Thread-safe: Just don't share sessions + - Non-blocking: it can be used both blocking and non-blocking + - Your sockets: the app hands over the socket, or uses libssh sockets + - OpenSSL or gcrypt: builds with either + +@section main-additional-features Additional Features + + - Client and server support + - SSHv2 and SSHv1 protocol support + - Supports Linux, UNIX, BSD, Solaris, OS/2 and Windows + - Automated test cases with nightly tests + - Event model based on poll(2), or a poll(2)-emulation. @section main-copyright Copyright Policy diff --git a/libssh/examples/CMakeLists.txt b/libssh/examples/CMakeLists.txt index 5513b758..fc1c9341 100644 --- a/libssh/examples/CMakeLists.txt +++ b/libssh/examples/CMakeLists.txt @@ -11,38 +11,52 @@ include_directories( ${CMAKE_BINARY_DIR} ) -if (LINUX) +if (BSD OR SOLARIS) + find_package(Argp) +endif (BSD OR SOLARIS) + +if (UNIX AND NOT WIN32) add_executable(libssh_scp libssh_scp.c ${examples_SRCS}) target_link_libraries(libssh_scp ${LIBSSH_SHARED_LIBRARY}) add_executable(scp_download scp_download.c ${examples_SRCS}) target_link_libraries(scp_download ${LIBSSH_SHARED_LIBRARY}) - add_executable(samplessh sample.c ${examples_SRCS}) - target_link_libraries(samplessh ${LIBSSH_SHARED_LIBRARY}) - add_executable(sshnetcat sshnetcat.c ${examples_SRCS}) target_link_libraries(sshnetcat ${LIBSSH_SHARED_LIBRARY}) + if (WITH_SERVER) + if (HAVE_LIBUTIL) + add_executable(samplesshd-tty samplesshd-tty.c) + target_link_libraries(samplesshd-tty ${LIBSSH_SHARED_LIBRARY} util) + endif (HAVE_LIBUTIL) + endif (WITH_SERVER) + if (WITH_SFTP) add_executable(samplesftp samplesftp.c ${examples_SRCS}) target_link_libraries(samplesftp ${LIBSSH_SHARED_LIBRARY}) endif (WITH_SFTP) + add_executable(samplessh sample.c ${examples_SRCS}) + target_link_libraries(samplessh ${LIBSSH_SHARED_LIBRARY}) + if (WITH_SERVER) add_executable(samplesshd samplesshd.c) - target_link_libraries(samplesshd ${LIBSSH_SHARED_LIBRARY}) + target_link_libraries(samplesshd ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES}) + + if (WITH_GSSAPI AND GSSAPI_FOUND) + add_executable(samplesshd-cb samplesshd-cb.c) + target_link_libraries(samplesshd-cb ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES}) + + add_executable(proxy proxy.c) + target_link_libraries(proxy ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES}) + endif (WITH_GSSAPI AND GSSAPI_FOUND) add_executable(samplesshd-kbdint samplesshd-kbdint.c) - target_link_libraries(samplesshd-kbdint ${LIBSSH_SHARED_LIBRARY}) - - if (HAVE_LIBUTIL) - add_executable(samplesshd-tty samplesshd-tty.c) - target_link_libraries(samplesshd-tty ${LIBSSH_SHARED_LIBRARY} util) - endif (HAVE_LIBUTIL) + target_link_libraries(samplesshd-kbdint ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES}) endif (WITH_SERVER) -endif (LINUX) +endif (UNIX AND NOT WIN32) add_executable(exec exec.c ${examples_SRCS}) target_link_libraries(exec ${LIBSSH_SHARED_LIBRARY}) diff --git a/libssh/examples/authentication.c b/libssh/examples/authentication.c index 0e749e54..ab5e64d6 100644 --- a/libssh/examples/authentication.c +++ b/libssh/examples/authentication.c @@ -118,6 +118,15 @@ int authenticate_console(ssh_session session){ method = ssh_auth_list(session); while (rc != SSH_AUTH_SUCCESS) { + if (method & SSH_AUTH_METHOD_GSSAPI_MIC){ + rc = ssh_userauth_gssapi(session); + if(rc == SSH_AUTH_ERROR) { + error(session); + return rc; + } else if (rc == SSH_AUTH_SUCCESS) { + break; + } + } // Try to authenticate with public key first if (method & SSH_AUTH_METHOD_PUBLICKEY) { rc = ssh_userauth_autopubkey(session, NULL); diff --git a/libssh/examples/libssh_scp.c b/libssh/examples/libssh_scp.c index b7471da6..99281db8 100644 --- a/libssh/examples/libssh_scp.c +++ b/libssh/examples/libssh_scp.c @@ -84,9 +84,15 @@ static int opts(int argc, char **argv){ } static struct location *parse_location(char *loc){ - struct location *location=malloc(sizeof(struct location)); + struct location *location; char *ptr; + location = malloc(sizeof(struct location)); + if (location == NULL) { + return NULL; + } + memset(location, 0, sizeof(struct location)); + location->host=location->user=NULL; ptr=strchr(loc,':'); if(ptr != NULL){ @@ -123,6 +129,7 @@ static int open_location(struct location *loc, int flag){ if(ssh_scp_init(loc->scp)==SSH_ERROR){ fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); ssh_scp_free(loc->scp); + loc->scp = NULL; return -1; } return 0; @@ -140,6 +147,7 @@ static int open_location(struct location *loc, int flag){ if(ssh_scp_init(loc->scp)==SSH_ERROR){ fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); ssh_scp_free(loc->scp); + loc->scp = NULL; return -1; } return 0; @@ -184,7 +192,10 @@ static int do_copy(struct location *src, struct location *dest, int recursive){ fprintf(stderr, "Invalid file pointer, error: %s\n", strerror(errno)); return -1; } - fstat(fd,&s); + r = fstat(fd, &s); + if (r < 0) { + return -1; + } size=s.st_size; mode = s.st_mode & ~S_IFMT; filename=ssh_basename(src->path); diff --git a/libssh/examples/proxy.c b/libssh/examples/proxy.c new file mode 100644 index 00000000..dcf4d0d7 --- /dev/null +++ b/libssh/examples/proxy.c @@ -0,0 +1,347 @@ +/* This is a sample implementation of a libssh based SSH proxy */ +/* +Copyright 2003-2013 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_ARGP_H +#include +#endif +#include +#include +#include + +#define USER "myuser" +#define PASSWORD "mypassword" + +static int authenticated=0; +static int tries = 0; +static int error = 0; +static ssh_channel chan=NULL; +static char *username; +static ssh_gssapi_creds client_creds = NULL; + +static int auth_password(ssh_session session, const char *user, + const char *password, void *userdata){ + + (void)userdata; + + printf("Authenticating user %s pwd %s\n",user, password); + if(strcmp(user,USER) == 0 && strcmp(password, PASSWORD) == 0){ + authenticated = 1; + printf("Authenticated\n"); + return SSH_AUTH_SUCCESS; + } + if (tries >= 3){ + printf("Too many authentication tries\n"); + ssh_disconnect(session); + error = 1; + return SSH_AUTH_DENIED; + } + tries++; + return SSH_AUTH_DENIED; +} + +static int auth_gssapi_mic(ssh_session session, const char *user, const char *principal, void *userdata){ + (void)userdata; + client_creds = ssh_gssapi_get_creds(session); + printf("Authenticating user %s with gssapi principal %s\n",user, principal); + if (client_creds != NULL) + printf("Received some gssapi credentials\n"); + else + printf("Not received any forwardable creds\n"); + printf("authenticated\n"); + authenticated = 1; + username = strdup(principal); + return SSH_AUTH_SUCCESS; +} + +static int pty_request(ssh_session session, ssh_channel channel, const char *term, + int x,int y, int px, int py, void *userdata){ + (void) session; + (void) channel; + (void) term; + (void) x; + (void) y; + (void) px; + (void) py; + (void) userdata; + printf("Allocated terminal\n"); + return 0; +} + +static int shell_request(ssh_session session, ssh_channel channel, void *userdata){ + (void)session; + (void)channel; + (void)userdata; + printf("Allocated shell\n"); + return 0; +} +struct ssh_channel_callbacks_struct channel_cb = { + .channel_pty_request_function = pty_request, + .channel_shell_request_function = shell_request +}; + +static ssh_channel new_session_channel(ssh_session session, void *userdata){ + (void) session; + (void) userdata; + if(chan != NULL) + return NULL; + printf("Allocated session channel\n"); + chan = ssh_channel_new(session); + ssh_callbacks_init(&channel_cb); + ssh_set_channel_callbacks(chan, &channel_cb); + return chan; +} + + +#ifdef HAVE_ARGP_H +const char *argp_program_version = "libssh proxy example " +SSH_STRINGIFY(LIBSSH_VERSION); +const char *argp_program_bug_address = ""; + +/* Program documentation. */ +static char doc[] = "libssh -- a Secure Shell protocol implementation"; + +/* A description of the arguments we accept. */ +static char args_doc[] = "BINDADDR"; + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "port", + .key = 'p', + .arg = "PORT", + .flags = 0, + .doc = "Set the port to bind.", + .group = 0 + }, + { + .name = "hostkey", + .key = 'k', + .arg = "FILE", + .flags = 0, + .doc = "Set the host key.", + .group = 0 + }, + { + .name = "dsakey", + .key = 'd', + .arg = "FILE", + .flags = 0, + .doc = "Set the dsa key.", + .group = 0 + }, + { + .name = "rsakey", + .key = 'r', + .arg = "FILE", + .flags = 0, + .doc = "Set the rsa key.", + .group = 0 + }, + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Get verbose output.", + .group = 0 + }, + {NULL, 0, NULL, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + ssh_bind sshbind = state->input; + + switch (key) { + case 'p': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); + break; + case 'd': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); + break; + case 'k': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); + break; + case 'r': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); + break; + case 'v': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + /* Too many arguments. */ + argp_usage (state); + } + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); + break; + case ARGP_KEY_END: + if (state->arg_num < 1) { + /* Not enough arguments. */ + argp_usage (state); + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Our argp parser. */ +static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; +#endif /* HAVE_ARGP_H */ + +int main(int argc, char **argv){ + ssh_session session; + ssh_bind sshbind; + ssh_event mainloop; + ssh_session client_session; + + struct ssh_server_callbacks_struct cb = { + .userdata = NULL, + .auth_password_function = auth_password, + .auth_gssapi_mic_function = auth_gssapi_mic, + .channel_open_request_session_function = new_session_channel + }; + + char buf[2048]; + char host[128]=""; + char *ptr; + int i,r, rc; + + sshbind=ssh_bind_new(); + session=ssh_new(); + + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, "sshd_rsa"); + +#ifdef HAVE_ARGP_H + /* + * Parse our arguments; every option seen by parse_opt will + * be reflected in arguments. + */ + argp_parse (&argp, argc, argv, 0, 0, sshbind); +#else + (void) argc; + (void) argv; +#endif + + if(ssh_bind_listen(sshbind)<0){ + printf("Error listening to socket: %s\n",ssh_get_error(sshbind)); + return 1; + } + r=ssh_bind_accept(sshbind,session); + if(r==SSH_ERROR){ + printf("error accepting a connection : %s\n",ssh_get_error(sshbind)); + return 1; + } + ssh_callbacks_init(&cb); + ssh_set_server_callbacks(session, &cb); + + if (ssh_handle_key_exchange(session)) { + printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); + return 1; + } + ssh_set_auth_methods(session,SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_GSSAPI_MIC); + mainloop = ssh_event_new(); + ssh_event_add_session(mainloop, session); + + while (!(authenticated && chan != NULL)){ + if(error) + break; + r = ssh_event_dopoll(mainloop, -1); + if (r == SSH_ERROR){ + printf("Error : %s\n",ssh_get_error(session)); + ssh_disconnect(session); + return 1; + } + } + if(error){ + printf("Error, exiting loop\n"); + return 1; + } else + printf("Authenticated and got a channel\n"); + if (!client_creds){ + snprintf(buf,sizeof(buf), "Sorry, but you do not have forwardable tickets. Try again with -K\r\n"); + ssh_channel_write(chan,buf,strlen(buf)); + printf("%s",buf); + ssh_disconnect(session); + return 1; + } + snprintf(buf,sizeof(buf), "Hello %s, welcome to the Sample SSH proxy.\r\nPlease select your destination: ", username); + ssh_channel_write(chan, buf, strlen(buf)); + do{ + i=ssh_channel_read(chan,buf, 2048, 0); + if(i>0) { + ssh_channel_write(chan, buf, i); + if(strlen(host) + i < sizeof(host)){ + strncat(host, buf, i); + } + if (strchr(host, '\x0d')) { + *strchr(host, '\x0d')='\0'; + ssh_channel_write(chan, "\n", 1); + break; + } + } else { + printf ("Error: %s\n", ssh_get_error(session) ); + return 1; + } + } while (i>0); + snprintf(buf,sizeof(buf),"Trying to connect to \"%s\"\r\n", host); + ssh_channel_write(chan, buf, strlen(buf)); + printf("%s",buf); + + client_session = ssh_new(); + + /* ssh servers expect username without realm */ + ptr = strchr(username,'@'); + if(ptr) + *ptr= '\0'; + ssh_options_set(client_session, SSH_OPTIONS_HOST, host); + ssh_options_set(client_session, SSH_OPTIONS_USER, username); + ssh_gssapi_set_creds(client_session, client_creds); + rc = ssh_connect(client_session); + if (rc != SSH_OK){ + printf("Error connecting to %s: %s", host, ssh_get_error(client_session)); + return 1; + } + rc = ssh_userauth_none(client_session, NULL); + if(rc == SSH_AUTH_SUCCESS){ + printf("Authenticated using method none\n"); + } else { + rc = ssh_userauth_gssapi(client_session); + if(rc != SSH_AUTH_SUCCESS){ + printf("GSSAPI Authentication failed: %s\n",ssh_get_error(client_session)); + return 1; + } + } + snprintf(buf,sizeof(buf), "Authentication success\r\n"); + printf("%s",buf); + ssh_channel_write(chan,buf,strlen(buf)); + ssh_disconnect(client_session); + ssh_disconnect(session); + ssh_bind_free(sshbind); + ssh_finalize(); + return 0; +} + diff --git a/libssh/examples/sample.c b/libssh/examples/sample.c index 93634d80..616372cb 100644 --- a/libssh/examples/sample.c +++ b/libssh/examples/sample.c @@ -14,24 +14,31 @@ clients must be made or how a client should react. #include "config.h" #include -#include #include #include -#include #include #include + +#ifdef HAVE_TERMIOS_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif #ifdef HAVE_PTY_H #include #endif + #include #include #include +#include + #include #include #include -#include #include "examples_common.h" #define MAXCMD 10 @@ -229,8 +236,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ // we already looked for input from stdin. Now, we are looking for input from the channel if(channel && ssh_channel_is_closed(channel)){ - ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); - ssh_channel_free(channel); channel=NULL; channels[0]=NULL; @@ -244,9 +249,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ return; } if(lus==0){ - ssh_log(session,SSH_LOG_RARE,"EOF received"); - ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); - ssh_channel_free(channel); channel=channels[0]=NULL; } else @@ -263,8 +265,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ return; } if(lus==0){ - ssh_log(session,SSH_LOG_RARE,"EOF received"); - ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); ssh_channel_free(channel); channel=channels[0]=NULL; } else @@ -322,8 +322,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ } } if(channel && ssh_channel_is_closed(channel)){ - ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); - ssh_channel_free(channel); channel=NULL; channels[0]=NULL; @@ -337,9 +335,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ return; } if(lus==0){ - ssh_log(session,SSH_LOG_RARE,"EOF received"); - ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); - ssh_channel_free(channel); channel=channels[0]=NULL; } else @@ -356,8 +351,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ return; } if(lus==0){ - ssh_log(session,SSH_LOG_RARE,"EOF received"); - ssh_log(session,SSH_LOG_RARE,"exit-status : %d",ssh_channel_get_exit_status(channel)); ssh_channel_free(channel); channel=channels[0]=NULL; } else @@ -461,7 +454,6 @@ static int client(ssh_session session){ if(auth != SSH_AUTH_SUCCESS){ return -1; } - ssh_log(session, SSH_LOG_FUNCTIONS, "Authentication success"); if(!cmds[0]) shell(session); else diff --git a/libssh/examples/samplesftp.c b/libssh/examples/samplesftp.c index 968624e8..457e60a4 100644 --- a/libssh/examples/samplesftp.c +++ b/libssh/examples/samplesftp.c @@ -15,11 +15,13 @@ clients must be made or how a client should react. #include #include -#include #include #include #include #include +#ifdef HAVE_UNISTD_H +#include +#endif #include #include diff --git a/libssh/examples/samplesshd-cb.c b/libssh/examples/samplesshd-cb.c new file mode 100644 index 00000000..f93ab4b4 --- /dev/null +++ b/libssh/examples/samplesshd-cb.c @@ -0,0 +1,306 @@ +/* This is a sample implementation of a libssh based SSH server */ +/* +Copyright 2003-2009 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_ARGP_H +#include +#endif +#include +#include +#include + +#ifndef KEYS_FOLDER +#ifdef _WIN32 +#define KEYS_FOLDER +#else +#define KEYS_FOLDER "/etc/ssh/" +#endif +#endif + +#define USER "myuser" +#define PASSWORD "mypassword" + +static int authenticated=0; +static int tries = 0; +static int error = 0; +static ssh_channel chan=NULL; + +static int auth_password(ssh_session session, const char *user, + const char *password, void *userdata){ + (void)userdata; + printf("Authenticating user %s pwd %s\n",user, password); + if(strcmp(user,USER) == 0 && strcmp(password, PASSWORD) == 0){ + authenticated = 1; + printf("Authenticated\n"); + return SSH_AUTH_SUCCESS; + } + if (tries >= 3){ + printf("Too many authentication tries\n"); + ssh_disconnect(session); + error = 1; + return SSH_AUTH_DENIED; + } + tries++; + return SSH_AUTH_DENIED; +} + +static int auth_gssapi_mic(ssh_session session, const char *user, const char *principal, void *userdata){ + ssh_gssapi_creds creds = ssh_gssapi_get_creds(session); + (void)userdata; + printf("Authenticating user %s with gssapi principal %s\n",user, principal); + if (creds != NULL) + printf("Received some gssapi credentials\n"); + else + printf("Not received any forwardable creds\n"); + printf("authenticated\n"); + authenticated = 1; + return SSH_AUTH_SUCCESS; +} + +static int pty_request(ssh_session session, ssh_channel channel, const char *term, + int x,int y, int px, int py, void *userdata){ + (void) session; + (void) channel; + (void) term; + (void) x; + (void) y; + (void) px; + (void) py; + (void) userdata; + printf("Allocated terminal\n"); + return 0; +} + +static int shell_request(ssh_session session, ssh_channel channel, void *userdata){ + (void)session; + (void)channel; + (void)userdata; + printf("Allocated shell\n"); + return 0; +} +struct ssh_channel_callbacks_struct channel_cb = { + .channel_pty_request_function = pty_request, + .channel_shell_request_function = shell_request +}; + +static ssh_channel new_session_channel(ssh_session session, void *userdata){ + (void) session; + (void) userdata; + if(chan != NULL) + return NULL; + printf("Allocated session channel\n"); + chan = ssh_channel_new(session); + ssh_callbacks_init(&channel_cb); + ssh_set_channel_callbacks(chan, &channel_cb); + return chan; +} + + +#ifdef HAVE_ARGP_H +const char *argp_program_version = "libssh server example " +SSH_STRINGIFY(LIBSSH_VERSION); +const char *argp_program_bug_address = ""; + +/* Program documentation. */ +static char doc[] = "libssh -- a Secure Shell protocol implementation"; + +/* A description of the arguments we accept. */ +static char args_doc[] = "BINDADDR"; + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "port", + .key = 'p', + .arg = "PORT", + .flags = 0, + .doc = "Set the port to bind.", + .group = 0 + }, + { + .name = "hostkey", + .key = 'k', + .arg = "FILE", + .flags = 0, + .doc = "Set the host key.", + .group = 0 + }, + { + .name = "dsakey", + .key = 'd', + .arg = "FILE", + .flags = 0, + .doc = "Set the dsa key.", + .group = 0 + }, + { + .name = "rsakey", + .key = 'r', + .arg = "FILE", + .flags = 0, + .doc = "Set the rsa key.", + .group = 0 + }, + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Get verbose output.", + .group = 0 + }, + {NULL, 0, NULL, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + ssh_bind sshbind = state->input; + + switch (key) { + case 'p': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); + break; + case 'd': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); + break; + case 'k': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); + break; + case 'r': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); + break; + case 'v': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + /* Too many arguments. */ + argp_usage (state); + } + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); + break; + case ARGP_KEY_END: + if (state->arg_num < 1) { + /* Not enough arguments. */ + argp_usage (state); + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +/* Our argp parser. */ +static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; +#endif /* HAVE_ARGP_H */ + +int main(int argc, char **argv){ + ssh_session session; + ssh_bind sshbind; + ssh_event mainloop; + struct ssh_server_callbacks_struct cb = { + .userdata = NULL, + .auth_password_function = auth_password, + .auth_gssapi_mic_function = auth_gssapi_mic, + .channel_open_request_session_function = new_session_channel + }; + + char buf[2048]; + int i; + int r; + + sshbind=ssh_bind_new(); + session=ssh_new(); + + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key"); + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key"); + +#ifdef HAVE_ARGP_H + /* + * Parse our arguments; every option seen by parse_opt will + * be reflected in arguments. + */ + argp_parse (&argp, argc, argv, 0, 0, sshbind); +#else + (void) argc; + (void) argv; +#endif + + if(ssh_bind_listen(sshbind)<0){ + printf("Error listening to socket: %s\n",ssh_get_error(sshbind)); + return 1; + } + r=ssh_bind_accept(sshbind,session); + if(r==SSH_ERROR){ + printf("error accepting a connection : %s\n",ssh_get_error(sshbind)); + return 1; + } + ssh_callbacks_init(&cb); + ssh_set_server_callbacks(session, &cb); + + if (ssh_handle_key_exchange(session)) { + printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); + return 1; + } + ssh_set_auth_methods(session,SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_GSSAPI_MIC); + mainloop = ssh_event_new(); + ssh_event_add_session(mainloop, session); + + while (!(authenticated && chan != NULL)){ + if(error) + break; + r = ssh_event_dopoll(mainloop, -1); + if (r == SSH_ERROR){ + printf("Error : %s\n",ssh_get_error(session)); + ssh_disconnect(session); + return 1; + } + } + if(error){ + printf("Error, exiting loop\n"); + } else + printf("Authenticated and got a channel\n"); + do{ + i=ssh_channel_read(chan,buf, 2048, 0); + if(i>0) { + ssh_channel_write(chan, buf, i); + if (write(1,buf,i) < 0) { + printf("error writing to buffer\n"); + return 1; + } + if (buf[0] == '\x0d') { + if (write(1, "\n", 1) < 0) { + printf("error writing to buffer\n"); + return 1; + } + ssh_channel_write(chan, "\n", 1); + } + } + } while (i>0); + ssh_disconnect(session); + ssh_bind_free(sshbind); + ssh_finalize(); + return 0; +} + diff --git a/libssh/examples/samplesshd-kbdint.c b/libssh/examples/samplesshd-kbdint.c index faecfd9b..5c2c461e 100644 --- a/libssh/examples/samplesshd-kbdint.c +++ b/libssh/examples/samplesshd-kbdint.c @@ -35,6 +35,8 @@ clients must be made or how a client should react. #endif #endif +static int port = 22; + #ifdef WITH_PCAP static const char *pcap_file = "debug.server.pcap"; static ssh_pcap_file pcap; @@ -77,8 +79,6 @@ static char doc[] = "libssh -- a Secure Shell protocol implementation"; /* A description of the arguments we accept. */ static char args_doc[] = "BINDADDR"; -static int port = 22; - /* The options we understand. */ static struct argp_option options[] = { { diff --git a/libssh/examples/samplesshd-tty.c b/libssh/examples/samplesshd-tty.c index a0e79e43..7ed70d3d 100644 --- a/libssh/examples/samplesshd-tty.c +++ b/libssh/examples/samplesshd-tty.c @@ -286,7 +286,7 @@ static int main_loop(ssh_channel chan) { pid_t childpid; ssh_event event; short events; - + int rc; childpid = forkpty(&fd, NULL, term, win); if(childpid == 0) { @@ -318,7 +318,13 @@ static int main_loop(ssh_channel chan) { } do { - ssh_event_dopoll(event, 1000); + rc = ssh_event_dopoll(event, 1000); + if (rc == SSH_ERROR){ + fprintf(stderr, "Error : %s\n", ssh_get_error(session)); + ssh_event_free(event); + ssh_disconnect(session); + return -1; + } } while(!ssh_channel_is_closed(chan)); ssh_event_remove_fd(event, fd); diff --git a/libssh/examples/sshnetcat.c b/libssh/examples/sshnetcat.c index cad777bb..052cabf4 100644 --- a/libssh/examples/sshnetcat.c +++ b/libssh/examples/sshnetcat.c @@ -13,10 +13,14 @@ clients must be made or how a client should react. #include "config.h" #include -#include #include #include +#ifdef HAVE_TERMIOS_H #include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif #include #include @@ -105,8 +109,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ } } if(channel && channel_is_closed(channel)){ - ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel)); - channel_free(channel); channel=NULL; channels[0]=NULL; @@ -120,9 +122,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ return; } if(lus==0){ - ssh_log(session,SSH_LOG_RARE,"EOF received\n"); - ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel)); - channel_free(channel); channel=channels[0]=NULL; } else { @@ -142,8 +141,6 @@ static void select_loop(ssh_session session,ssh_channel channel){ return; } if(lus==0){ - ssh_log(session,SSH_LOG_RARE,"EOF received\n"); - ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel)); channel_free(channel); channel=channels[0]=NULL; } else @@ -202,7 +199,6 @@ static int client(ssh_session session){ if(auth != SSH_AUTH_SUCCESS){ return -1; } - ssh_log(session, SSH_LOG_FUNCTIONS, "Authentication success"); forwarding(session); return 0; } diff --git a/libssh/include/libssh/agent.h b/libssh/include/libssh/agent.h index 4641c9e2..77209d0f 100644 --- a/libssh/include/libssh/agent.h +++ b/libssh/include/libssh/agent.h @@ -71,6 +71,7 @@ struct ssh_agent_struct { struct ssh_socket_struct *sock; ssh_buffer ident; unsigned int count; + ssh_channel channel; }; #ifndef _WIN32 diff --git a/libssh/include/libssh/auth.h b/libssh/include/libssh/auth.h index 3a6012ec..2c0012b0 100644 --- a/libssh/include/libssh/auth.h +++ b/libssh/include/libssh/auth.h @@ -83,8 +83,13 @@ enum ssh_auth_state_e { /** Last state was a public key accepted for authentication */ SSH_AUTH_STATE_PK_OK, /** We asked for a keyboard-interactive authentication */ - SSH_AUTH_STATE_KBDINT_SENT - + SSH_AUTH_STATE_KBDINT_SENT, + /** We have sent an userauth request with gssapi-with-mic */ + SSH_AUTH_STATE_GSSAPI_REQUEST_SENT, + /** We are exchanging tokens until authentication */ + SSH_AUTH_STATE_GSSAPI_TOKEN, + /** We have sent the MIC and expecting to be authenticated */ + SSH_AUTH_STATE_GSSAPI_MIC_SENT, }; /** @internal diff --git a/libssh/include/libssh/callbacks.h b/libssh/include/libssh/callbacks.h index e15a0bd8..7525b73d 100644 --- a/libssh/include/libssh/callbacks.h +++ b/libssh/include/libssh/callbacks.h @@ -74,6 +74,24 @@ typedef int (*ssh_channel_callback_data) (ssh_channel channel, int code, void *d typedef void (*ssh_log_callback) (ssh_session session, int priority, const char *message, void *userdata); +/** + * @brief SSH log callback. + * + * All logging messages will go through this callback. + * + * @param priority Priority of the log, the smaller being the more important. + * + * @param function The function name calling the the logging fucntions. + * + * @param message The actual message + * + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_logging_callback) (int priority, + const char *function, + const char *buffer, + void *userdata); + /** * @brief SSH Connection status callback. * @param session Current session handler @@ -94,6 +112,18 @@ typedef void (*ssh_status_callback) (ssh_session session, float status, typedef void (*ssh_global_request_callback) (ssh_session session, ssh_message message, void *userdata); +/** + * @brief Handles an SSH new channel open X11 request. This happens when the server + * sends back an X11 connection attempt. This is a client-side API + * @param session current session handler + * @param userdata Userdata to be passed to the callback function. + * @returns a valid ssh_channel handle if the request is to be allowed + * @returns NULL if the request should not be allowed + * @warning The channel pointer returned by this callback must be closed by the application. + */ +typedef ssh_channel (*ssh_channel_open_request_x11_callback) (ssh_session session, + const char * originator_address, int originator_port, void *userdata); + /** * The structure to replace libssh functions with appropriate callbacks. */ @@ -121,9 +151,211 @@ struct ssh_callbacks_struct { * This function will be called each time a global request is received. */ ssh_global_request_callback global_request_function; + /** This function will be called when an incoming X11 request is received. + */ + ssh_channel_open_request_x11_callback channel_open_request_x11_function; }; typedef struct ssh_callbacks_struct *ssh_callbacks; +/** These are callbacks used specifically in SSH servers. + */ + +/** + * @brief SSH authentication callback. + * @param session Current session handler + * @param user User that wants to authenticate + * @param password Password used for authentication + * @param userdata Userdata to be passed to the callback function. + * @returns SSH_AUTH_OK Authentication is accepted. + * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. + * @returns SSH_AUTH_DENIED Authentication failed. + */ +typedef int (*ssh_auth_password_callback) (ssh_session session, const char *user, const char *password, + void *userdata); + +/** + * @brief SSH authentication callback. Tries to authenticates user with the "none" method + * which is anonymous or passwordless. + * @param session Current session handler + * @param user User that wants to authenticate + * @param userdata Userdata to be passed to the callback function. + * @returns SSH_AUTH_OK Authentication is accepted. + * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. + * @returns SSH_AUTH_DENIED Authentication failed. + */ +typedef int (*ssh_auth_none_callback) (ssh_session session, const char *user, void *userdata); + +/** + * @brief SSH authentication callback. Tries to authenticates user with the "gssapi-with-mic" method + * @param session Current session handler + * @param user Username of the user (can be spoofed) + * @param principal Authenticated principal of the user, including realm. + * @param userdata Userdata to be passed to the callback function. + * @returns SSH_AUTH_OK Authentication is accepted. + * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. + * @returns SSH_AUTH_DENIED Authentication failed. + * @warning Implementations should verify that parameter user matches in some way the principal. + * user and principal can be different. Only the latter is guaranteed to be safe. + */ +typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *user, const char *principal, + void *userdata); + +/** + * @brief SSH authentication callback. + * @param session Current session handler + * @param user User that wants to authenticate + * @param pubkey public key used for authentication + * @param signature_state SSH_PUBLICKEY_STATE_NONE if the key is not signed (simple public key probe), + * SSH_PUBLICKEY_STATE_VALID if the signature is valid. Others values should be + * replied with a SSH_AUTH_DENIED. + * @param userdata Userdata to be passed to the callback function. + * @returns SSH_AUTH_OK Authentication is accepted. + * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. + * @returns SSH_AUTH_DENIED Authentication failed. + */ +typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user, struct ssh_key_struct *pubkey, + char signature_state, void *userdata); + + +/** + * @brief Handles an SSH service request + * @param session current session handler + * @param service name of the service (e.g. "ssh-userauth") requested + * @param userdata Userdata to be passed to the callback function. + * @returns 0 if the request is to be allowed + * @returns -1 if the request should not be allowed + */ + +typedef int (*ssh_service_request_callback) (ssh_session session, const char *service, void *userdata); + +/** + * @brief Handles an SSH new channel open session request + * @param session current session handler + * @param userdata Userdata to be passed to the callback function. + * @returns a valid ssh_channel handle if the request is to be allowed + * @returns NULL if the request should not be allowed + * @warning The channel pointer returned by this callback must be closed by the application. + */ +typedef ssh_channel (*ssh_channel_open_request_session_callback) (ssh_session session, void *userdata); + +/* + * @brief handle the beginning of a GSSAPI authentication, server side. + * @param session current session handler + * @param user the username of the client + * @param n_oid number of available oids + * @param oids OIDs provided by the client + * @returns an ssh_string containing the chosen OID, that's supported by both + * client and server. + * @warning It is not necessary to fill this callback in if libssh is linked + * with libgssapi. + */ +typedef ssh_string (*ssh_gssapi_select_oid_callback) (ssh_session session, const char *user, + int n_oid, ssh_string *oids, void *userdata); + +/* + * @brief handle the negociation of a security context, server side. + * @param session current session handler + * @param[in] input_token input token provided by client + * @param[out] output_token output of the gssapi accept_sec_context method, + * NULL after completion. + * @returns SSH_OK if the token was generated correctly or accept_sec_context + * returned GSS_S_COMPLETE + * @returns SSH_ERROR in case of error + * @warning It is not necessary to fill this callback in if libssh is linked + * with libgssapi. + */ +typedef int (*ssh_gssapi_accept_sec_ctx_callback) (ssh_session session, + ssh_string input_token, ssh_string *output_token, void *userdata); + +/* + * @brief Verify and authenticates a MIC, server side. + * @param session current session handler + * @param[in] mic input mic to be verified provided by client + * @param[in] mic_buffer buffer of data to be signed. + * @param[in] mic_buffer_size size of mic_buffer + * @returns SSH_OK if the MIC was authenticated correctly + * @returns SSH_ERROR in case of error + * @warning It is not necessary to fill this callback in if libssh is linked + * with libgssapi. + */ +typedef int (*ssh_gssapi_verify_mic_callback) (ssh_session session, + ssh_string mic, void *mic_buffer, size_t mic_buffer_size, void *userdata); + + +/** + * This structure can be used to implement a libssh server, with appropriate callbacks. + */ + +struct ssh_server_callbacks_struct { + /** DON'T SET THIS use ssh_callbacks_init() instead. */ + size_t size; + /** + * User-provided data. User is free to set anything he wants here + */ + void *userdata; + /** This function gets called when a client tries to authenticate through + * password method. + */ + ssh_auth_password_callback auth_password_function; + + /** This function gets called when a client tries to authenticate through + * none method. + */ + ssh_auth_none_callback auth_none_function; + + /** This function gets called when a client tries to authenticate through + * gssapi-mic method. + */ + ssh_auth_gssapi_mic_callback auth_gssapi_mic_function; + + /** this function gets called when a client tries to authenticate or offer + * a public key. + */ + ssh_auth_pubkey_callback auth_pubkey_function; + + /** This functions gets called when a service request is issued by the + * client + */ + ssh_service_request_callback service_request_function; + /** This functions gets called when a new channel request is issued by + * the client + */ + ssh_channel_open_request_session_callback channel_open_request_session_function; + /** This function will be called when a new gssapi authentication is attempted. + */ + ssh_gssapi_select_oid_callback gssapi_select_oid_function; + /** This function will be called when a gssapi token comes in. + */ + ssh_gssapi_accept_sec_ctx_callback gssapi_accept_sec_ctx_function; + /* This function will be called when a MIC needs to be verified. + */ + ssh_gssapi_verify_mic_callback gssapi_verify_mic_function; +}; +typedef struct ssh_server_callbacks_struct *ssh_server_callbacks; + +/** + * @brief Set the session server callback functions. + * + * This functions sets the callback structure to use your own callback + * functions for user authentication, new channels and requests. + * + * @code + * struct ssh_server_callbacks_struct cb = { + * .userdata = data, + * .auth_password_function = my_auth_function + * }; + * ssh_callbacks_init(&cb); + * ssh_set_server_callbacks(session, &cb); + * @endcode + * + * @param session The session to set the callback structure. + * + * @param cb The callback structure itself. + * + * @return SSH_OK on success, SSH_ERROR on error. + */ +LIBSSH_API int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb); + /** * These are the callbacks exported by the socket structure * They are called by the socket module when a socket event appears @@ -332,6 +564,120 @@ typedef void (*ssh_channel_exit_signal_callback) (ssh_session session, const char *lang, void *userdata); +/** + * @brief SSH channel PTY request from a client. + * @param channel the channel + * @param term The type of terminal emulation + * @param width width of the terminal, in characters + * @param height height of the terminal, in characters + * @param pxwidth width of the terminal, in pixels + * @param pxheight height of the terminal, in pixels + * @param userdata Userdata to be passed to the callback function. + * @returns 0 if the pty request is accepted + * @returns -1 if the request is denied + */ +typedef int (*ssh_channel_pty_request_callback) (ssh_session session, + ssh_channel channel, + const char *term, + int width, int height, + int pxwidth, int pwheight, + void *userdata); + +/** + * @brief SSH channel Shell request from a client. + * @param channel the channel + * @param userdata Userdata to be passed to the callback function. + * @returns 0 if the shell request is accepted + * @returns 1 if the request is denied + */ +typedef int (*ssh_channel_shell_request_callback) (ssh_session session, + ssh_channel channel, + void *userdata); +/** + * @brief SSH auth-agent-request from the client. This request is + * sent by a client when agent forwarding is available. + * Server is free to ignore this callback, no answer is expected. + * @param channel the channel + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_channel_auth_agent_req_callback) (ssh_session session, + ssh_channel channel, + void *userdata); + +/** + * @brief SSH X11 request from the client. This request is + * sent by a client when X11 forwarding is requested(and available). + * Server is free to ignore this callback, no answer is expected. + * @param channel the channel + * @param userdata Userdata to be passed to the callback function. + */ +typedef void (*ssh_channel_x11_req_callback) (ssh_session session, + ssh_channel channel, + int single_connection, + const char *auth_protocol, + const char *auth_cookie, + uint32_t screen_number, + void *userdata); +/** + * @brief SSH channel PTY windows change (terminal size) from a client. + * @param channel the channel + * @param width width of the terminal, in characters + * @param height height of the terminal, in characters + * @param pxwidth width of the terminal, in pixels + * @param pxheight height of the terminal, in pixels + * @param userdata Userdata to be passed to the callback function. + * @returns 0 if the pty request is accepted + * @returns -1 if the request is denied + */ +typedef int (*ssh_channel_pty_window_change_callback) (ssh_session session, + ssh_channel channel, + int width, int height, + int pxwidth, int pwheight, + void *userdata); + +/** + * @brief SSH channel Exec request from a client. + * @param channel the channel + * @param command the shell command to be executed + * @param userdata Userdata to be passed to the callback function. + * @returns 0 if the exec request is accepted + * @returns 1 if the request is denied + */ +typedef int (*ssh_channel_exec_request_callback) (ssh_session session, + ssh_channel channel, + const char *command, + void *userdata); + +/** + * @brief SSH channel environment request from a client. + * @param channel the channel + * @param env_name name of the environment value to be set + * @param env_value value of the environment value to be set + * @param userdata Userdata to be passed to the callback function. + * @returns 0 if the env request is accepted + * @returns 1 if the request is denied + * @warning some environment variables can be dangerous if changed (e.g. + * LD_PRELOAD) and should not be fulfilled. + */ +typedef int (*ssh_channel_env_request_callback) (ssh_session session, + ssh_channel channel, + const char *env_name, + const char *env_value, + void *userdata); +/** + * @brief SSH channel subsystem request from a client. + * @param channel the channel + * @param subsystem the subsystem required + * @param userdata Userdata to be passed to the callback function. + * @returns 0 if the subsystem request is accepted + * @returns 1 if the request is denied + */ +typedef int (*ssh_channel_subsystem_request_callback) (ssh_session session, + ssh_channel channel, + const char *subsystem, + void *userdata); + + struct ssh_channel_callbacks_struct { /** DON'T SET THIS use ssh_callbacks_init() instead. */ size_t size; @@ -363,7 +709,40 @@ struct ssh_channel_callbacks_struct { * This functions will be called when an exit signal has been received */ ssh_channel_exit_signal_callback channel_exit_signal_function; + /** + * This function will be called when a client requests a PTY + */ + ssh_channel_pty_request_callback channel_pty_request_function; + /** + * This function will be called when a client requests a shell + */ + ssh_channel_shell_request_callback channel_shell_request_function; + /** This function will be called when a client requests agent + * authentication forwarding. + */ + ssh_channel_auth_agent_req_callback channel_auth_agent_req_function; + /** This function will be called when a client requests X11 + * forwarding. + */ + ssh_channel_x11_req_callback channel_x11_req_function; + /** This function will be called when a client requests a + * window change. + */ + ssh_channel_pty_window_change_callback channel_pty_window_change_function; + /** This function will be called when a client requests a + * command execution. + */ + ssh_channel_exec_request_callback channel_exec_request_function; + /** This function will be called when a client requests an environment + * variable to be set. + */ + ssh_channel_env_request_callback channel_env_request_function; + /** This function will be called when a client requests a subsystem + * (like sftp). + */ + ssh_channel_subsystem_request_callback channel_subsystem_request_function; }; + typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks; /** @@ -437,6 +816,22 @@ LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void); */ LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void); +/** + * @brief Set the logging callback function. + * + * @param[in] cb The callback to set. + * + * @return 0 on success, < 0 on errror. + */ +LIBSSH_API int ssh_set_log_callback(ssh_logging_callback cb); + +/** + * @brief Get the pointer to the logging callback function. + * + * @return The pointer the the callback or NULL if none set. + */ +LIBSSH_API ssh_logging_callback ssh_get_log_callback(void); + /** @} */ #ifdef __cplusplus } diff --git a/libssh/include/libssh/channels.h b/libssh/include/libssh/channels.h index 45152236..4c726453 100644 --- a/libssh/include/libssh/channels.h +++ b/libssh/include/libssh/channels.h @@ -96,8 +96,6 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, int ssh_channel_flush(ssh_channel channel); uint32_t ssh_channel_new_id(ssh_session session); ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id); -int channel_write_common(ssh_channel channel, const void *data, - uint32_t len, int is_stderr); void ssh_channel_do_free(ssh_channel channel); #ifdef WITH_SSH1 SSH_PACKET_CALLBACK(ssh_packet_data1); diff --git a/libssh/include/libssh/gssapi.h b/libssh/include/libssh/gssapi.h new file mode 100644 index 00000000..ccd83664 --- /dev/null +++ b/libssh/include/libssh/gssapi.h @@ -0,0 +1,45 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GSSAPI_H_ +#define GSSAPI_H_ + +#include "config.h" +#include "session.h" + +/* all OID begin with the tag identifier + length */ +#define SSH_OID_TAG 06 + +typedef struct ssh_gssapi_struct *ssh_gssapi; + +#ifdef WITH_SERVER +int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids); +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server); +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic); +#endif /* WITH_SERVER */ + +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token); +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client); +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response); + + +int ssh_gssapi_auth_mic(ssh_session session); + +#endif /* GSSAPI_H */ diff --git a/libssh/include/libssh/kex.h b/libssh/include/libssh/kex.h index 37d4be8e..1a5b6d41 100644 --- a/libssh/include/libssh/kex.h +++ b/libssh/include/libssh/kex.h @@ -37,7 +37,7 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1); #endif int ssh_send_kex(ssh_session session, int server_kex); -void ssh_list_kex(ssh_session session, struct ssh_kex_struct *kex); +void ssh_list_kex(struct ssh_kex_struct *kex); int set_client_kex(ssh_session session); int ssh_kex_select_methods(ssh_session session); int verify_existing_algo(int algo, const char *name); diff --git a/libssh/include/libssh/libssh.h b/libssh/include/libssh/libssh.h index d9cc8478..a451620e 100644 --- a/libssh/include/libssh/libssh.h +++ b/libssh/include/libssh/libssh.h @@ -115,6 +115,7 @@ typedef struct ssh_scp_struct* ssh_scp; typedef struct ssh_session_struct* ssh_session; typedef struct ssh_string_struct* ssh_string; typedef struct ssh_event_struct* ssh_event; +typedef void* ssh_gssapi_creds; /* Socket type */ #ifdef _WIN32 @@ -164,6 +165,7 @@ enum ssh_auth_e { #define SSH_AUTH_METHOD_PUBLICKEY 0x0004 #define SSH_AUTH_METHOD_HOSTBASED 0x0008 #define SSH_AUTH_METHOD_INTERACTIVE 0x0010 +#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020 /* messages */ enum ssh_requests_e { @@ -360,9 +362,11 @@ LIBSSH_API int ssh_channel_is_closed(ssh_channel channel); LIBSSH_API int ssh_channel_is_eof(ssh_channel channel); LIBSSH_API int ssh_channel_is_open(ssh_channel channel); LIBSSH_API ssh_channel ssh_channel_new(ssh_session session); +LIBSSH_API int ssh_channel_open_auth_agent(ssh_channel channel); LIBSSH_API int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, int remoteport, const char *sourcehost, int localport); LIBSSH_API int ssh_channel_open_session(ssh_channel channel); +LIBSSH_API int ssh_channel_open_x11(ssh_channel channel, const char *orig_addr, int orig_port); LIBSSH_API int ssh_channel_poll(ssh_channel channel, int is_stderr); LIBSSH_API int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr); LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); @@ -414,10 +418,19 @@ LIBSSH_API int ssh_is_blocking(ssh_session session); LIBSSH_API int ssh_is_connected(ssh_session session); LIBSSH_API int ssh_is_server_known(ssh_session session); +/* LOGGING */ +LIBSSH_API int ssh_set_log_level(int level); +LIBSSH_API int ssh_get_log_level(void); +LIBSSH_API void *ssh_get_log_userdata(void); +LIBSSH_API int ssh_set_log_userdata(void *data); +LIBSSH_API void _ssh_log(int verbosity, + const char *function, + const char *format, ...) PRINTF_ATTRIBUTE(3, 4); + /* legacy */ -LIBSSH_API void ssh_log(ssh_session session, - int prioriry, - const char *format, ...) PRINTF_ATTRIBUTE(3, 4); +SSH_DEPRECATED LIBSSH_API void ssh_log(ssh_session session, + int prioriry, + const char *format, ...) PRINTF_ATTRIBUTE(3, 4); LIBSSH_API ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg); LIBSSH_API int ssh_message_channel_request_reply_success(ssh_message msg); @@ -497,6 +510,7 @@ LIBSSH_API int ssh_pki_export_pubkey_file(const ssh_key key, LIBSSH_API void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len); LIBSSH_API int ssh_send_ignore (ssh_session session, const char *data); LIBSSH_API int ssh_send_debug (ssh_session session, const char *message, int always_display); +LIBSSH_API void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds); LIBSSH_API int ssh_scp_accept_request(ssh_scp scp); LIBSSH_API int ssh_scp_close(ssh_scp scp); LIBSSH_API int ssh_scp_deny_request(ssh_scp scp, const char *reason); @@ -518,6 +532,7 @@ LIBSSH_API int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len); LIBSSH_API int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, fd_set *readfds, struct timeval *timeout); LIBSSH_API int ssh_service_request(ssh_session session, const char *service); +LIBSSH_API int ssh_set_agent_channel(ssh_session session, ssh_channel channel); LIBSSH_API void ssh_set_blocking(ssh_session session, int blocking); LIBSSH_API void ssh_set_fd_except(ssh_session session); LIBSSH_API void ssh_set_fd_toread(ssh_session session); @@ -554,6 +569,7 @@ LIBSSH_API int ssh_userauth_kbdint_getnanswers(ssh_session session); LIBSSH_API const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i); LIBSSH_API int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i, const char *answer); +LIBSSH_API int ssh_userauth_gssapi(ssh_session session); LIBSSH_API const char *ssh_version(int req_version); LIBSSH_API int ssh_write_knownhost(ssh_session session); diff --git a/libssh/include/libssh/libsshpp.hpp b/libssh/include/libssh/libsshpp.hpp index 16e27dd8..1a5d80a9 100644 --- a/libssh/include/libssh/libsshpp.hpp +++ b/libssh/include/libssh/libsshpp.hpp @@ -316,7 +316,7 @@ public: va_start(va, format); vsnprintf(buffer, sizeof(buffer), format, va); va_end(va); - ssh_log(c_session,priority, "%s", buffer); + _ssh_log(priority, "libsshpp", "%s", buffer); } /** @brief copies options from a session to another diff --git a/libssh/include/libssh/messages.h b/libssh/include/libssh/messages.h index f196c6f7..e766d08b 100644 --- a/libssh/include/libssh/messages.h +++ b/libssh/include/libssh/messages.h @@ -92,13 +92,17 @@ struct ssh_message_struct { }; SSH_PACKET_CALLBACK(ssh_packet_channel_open); +SSH_PACKET_CALLBACK(ssh_packet_global_request); + +#ifdef WITH_SERVER SSH_PACKET_CALLBACK(ssh_packet_service_request); SSH_PACKET_CALLBACK(ssh_packet_userauth_request); -SSH_PACKET_CALLBACK(ssh_packet_global_request); +#endif /* WITH_SERVER */ int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet, const char *request, uint8_t want_reply); void ssh_message_queue(ssh_session session, ssh_message message); ssh_message ssh_message_pop_head(ssh_session session); +int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_channel chan); #endif /* MESSAGES_H_ */ diff --git a/libssh/include/libssh/pki.h b/libssh/include/libssh/pki.h index 566700b8..96bacd52 100644 --- a/libssh/include/libssh/pki.h +++ b/libssh/include/libssh/pki.h @@ -108,7 +108,7 @@ int ssh_pki_export_pubkey_rsa1(const ssh_key key, /* SSH Signing Functions */ ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf, - ssh_key privatekey); + const ssh_key privatekey); ssh_string ssh_pki_do_sign_agent(ssh_session session, struct ssh_buffer_struct *buf, const ssh_key pubkey); @@ -116,7 +116,7 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, const ssh_key privkey); /* Temporary functions, to be removed after migration to ssh_key */ -ssh_public_key ssh_pki_convert_key_to_publickey(ssh_key key); -ssh_private_key ssh_pki_convert_key_to_privatekey(ssh_key key); +ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key); +ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key); #endif /* PKI_H_ */ diff --git a/libssh/include/libssh/priv.h b/libssh/include/libssh/priv.h index 912a1918..43f749bb 100644 --- a/libssh/include/libssh/priv.h +++ b/libssh/include/libssh/priv.h @@ -31,6 +31,18 @@ #include "config.h" +#if !defined(HAVE_STRTOULL) +# if defined(HAVE___STRTOULL) +# define strtoull __strtoull +# elif defined(HAVE__STRTOUI64) +# define strtoull _strtoui64 +# elif defined(__hpux) && defined(__LP64__) +# define strtoull strtoul +# else +# error "no strtoull function found" +# endif +#endif /* !defined(HAVE_STRTOULL) */ + #ifdef _WIN32 /* Imitate define of inttypes.h */ @@ -46,16 +58,6 @@ # endif /* __WORDSIZE */ # endif /* PRIu64 */ -#if !defined(HAVE_STRTOULL) -# if defined(HAVE___STRTOULL) -# define strtoull __strtoull -# elif defined(__hpux) && defined(__LP64__) -# define strtoull strtoul -# else -# error "no strtoull function found" -# endif -#endif /* !defined(HAVE_STRTOULL) */ - # ifdef _MSC_VER # include @@ -65,9 +67,6 @@ # define strcasecmp _stricmp # define strncasecmp _strnicmp -# if !defined(HAVE_STRTOULL) -# define strtoull _strtoui64 -# endif # define isblank(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n' || (ch) == '\r') # define usleep(X) Sleep(((X)+1000)/1000) @@ -105,6 +104,7 @@ # endif /* _MSC_VER */ +struct timeval; int gettimeofday(struct timeval *__p, void *__t); #else /* _WIN32 */ @@ -130,8 +130,24 @@ int gettimeofday(struct timeval *__p, void *__t); #endif #endif -#define enter_function() (void)session -#define leave_function() (void)session +#if defined(HAVE_GCC_THREAD_LOCAL_STORAGE) +# define LIBSSH_THREAD __thread +#elif defined(HAVE_MSC_THREAD_LOCAL_STORAGE) +# define LIBSSH_THREAD __declspec(thread) +#else +# define LIBSSH_THREAD +#endif + +/* + * This makes sure that the compiler doesn't optimize out the code + * + * Use it in a macro where the provided variable is 'x'. + */ +#if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION) +# define LIBSSH_MEM_PROTECTION __asm__ volatile("" : : "r"(&(x)) : "memory") +#else +# define LIBSSH_MEM_PROTECTION +#endif #ifdef HAVE_SYS_TIME_H #include @@ -144,15 +160,17 @@ struct ssh_kex_struct; int ssh_get_key_params(ssh_session session, ssh_key *privkey); /* LOGGING */ -#define SSH_LOG(session, priority, ...) \ - ssh_log_common(&session->common, priority, __FUNCTION__, __VA_ARGS__) +void ssh_log_function(int verbosity, + const char *function, + const char *buffer); +#define SSH_LOG(priority, ...) \ + _ssh_log(priority, __FUNCTION__, __VA_ARGS__) + +/* LEGACY */ void ssh_log_common(struct ssh_common_struct *common, int verbosity, const char *function, const char *format, ...) PRINTF_ATTRIBUTE(4, 5); -void ssh_log_function(int verbosity, - const char *function, - const char *buffer); /* ERROR HANDLING */ @@ -179,9 +197,11 @@ void _ssh_set_error_oom(void *error, const char *function); void _ssh_set_error_invalid(void *error, const char *function); - - - +/* server.c */ +#ifdef WITH_SERVER +int ssh_auth_reply_default(ssh_session session,int partial); +int ssh_auth_reply_success(ssh_session session, int partial); +#endif /* client.c */ int ssh_send_banner(ssh_session session, int is_server); @@ -203,8 +223,9 @@ int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen); /* match.c */ int match_hostname(const char *host, const char *pattern, unsigned int len); - - +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif /** Free memory space */ #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0) @@ -218,11 +239,33 @@ int match_hostname(const char *host, const char *pattern, unsigned int len); /** Get the size of an array */ #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +/* + * See http://llvm.org/bugs/show_bug.cgi?id=15495 + */ +#if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION) /** Overwrite a string with '\0' */ -#define BURN_STRING(x) do { if ((x) != NULL) memset((x), '\0', strlen((x))); __asm__ volatile ("" : : : "memory"); } while(0) +# define BURN_STRING(x) do { \ + if ((x) != NULL) \ + memset((x), '\0', strlen((x))); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ + } while(0) /** Overwrite the buffer with '\0' */ -#define BURN_BUFFER(x, size) do { if ((x) != NULL) memset((x), '\0', (size))); __asm__ volatile ("") : : : "memory"; } while(0) +# define BURN_BUFFER(x, size) do { \ + if ((x) != NULL) \ + memset((x), '\0', (size))); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ + } while(0) +#else /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */ +/** Overwrite a string with '\0' */ +# define BURN_STRING(x) do { \ + if ((x) != NULL) memset((x), '\0', strlen((x))); \ + } while(0) + +/** Overwrite the buffer with '\0' */ +# define BURN_BUFFER(x, size) do { \ + if ((x) != NULL) \ + memset((x), '\0', (size))); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ + } while(0) +#endif /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */ /** * This is a hack to fix warnings. The idea is to use this everywhere that we diff --git a/libssh/include/libssh/server.h b/libssh/include/libssh/server.h index 9fab8e3d..9d095feb 100644 --- a/libssh/include/libssh/server.h +++ b/libssh/include/libssh/server.h @@ -55,7 +55,6 @@ typedef struct ssh_bind_struct* ssh_bind; * @brief Incoming connection callback. This callback is called when a ssh_bind * has a new incoming connection. * @param sshbind Current sshbind session handler - * @param message the actual message * @param userdata Userdata to be passed to the callback function. */ typedef void (*ssh_bind_incoming_connection_callback) (ssh_bind sshbind, @@ -238,6 +237,8 @@ LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session); LIBSSH_API int ssh_bind_accept_fd(ssh_bind ssh_bind_o, ssh_session session, socket_t fd); +LIBSSH_API ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session); + /** * @brief Handles the key exchange and set up encryption * @@ -254,6 +255,8 @@ LIBSSH_API int ssh_handle_key_exchange(ssh_session session); */ LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o); +LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods); + /********************************************************** * SERVER MESSAGING **********************************************************/ diff --git a/libssh/include/libssh/session.h b/libssh/include/libssh/session.h index 6edf9e51..f0d580dc 100644 --- a/libssh/include/libssh/session.h +++ b/libssh/include/libssh/session.h @@ -59,7 +59,8 @@ enum ssh_pending_call_e { SSH_PENDING_CALL_AUTH_PUBKEY, SSH_PENDING_CALL_AUTH_AGENT, SSH_PENDING_CALL_AUTH_KBDINT_INIT, - SSH_PENDING_CALL_AUTH_KBDINT_SEND + SSH_PENDING_CALL_AUTH_KBDINT_SEND, + SSH_PENDING_CALL_AUTH_GSSAPI_MIC }; /* libssh calls may block an undefined amount of time */ @@ -69,8 +70,13 @@ enum ssh_pending_call_e { #define SSH_SESSION_FLAG_AUTHENTICATED 2 /* codes to use with ssh_handle_packets*() */ +/* Infinite timeout */ #define SSH_TIMEOUT_INFINITE -1 +/* Use the timeout defined by user if any. Mostly used with new connections */ #define SSH_TIMEOUT_USER -2 +/* Use the default timeout, depending on ssh_is_blocking() */ +#define SSH_TIMEOUT_DEFAULT -3 +/* Don't block at all */ #define SSH_TIMEOUT_NONBLOCKING 0 /* members that are common to ssh_session and ssh_bind */ @@ -78,7 +84,6 @@ struct ssh_common_struct { struct error_struct error; ssh_callbacks callbacks; /* Callbacks to user functions */ int log_verbosity; /* verbosity of the log functions */ - int log_indent; /* indentation level in enter_function logs */ }; struct ssh_session_struct { @@ -138,6 +143,7 @@ struct ssh_session_struct { /* keyb interactive data */ struct ssh_kbdint_struct *kbdint; + struct ssh_gssapi_struct *gssapi; int version; /* 1 or 2 */ /* server host keys */ struct { @@ -153,7 +159,7 @@ struct ssh_session_struct { struct ssh_list *ssh_message_list; /* list of delayed SSH messages */ int (*ssh_message_callback)( struct ssh_session_struct *session, ssh_message msg, void *userdata); void *ssh_message_callback_data; - + ssh_server_callbacks server_callbacks; void (*ssh_connection_callback)( struct ssh_session_struct *session); struct ssh_packet_callbacks_struct default_packet_callbacks; struct ssh_list *packet_callbacks; diff --git a/libssh/include/libssh/sftp.h b/libssh/include/libssh/sftp.h index 462e04c5..d370f0ec 100644 --- a/libssh/include/libssh/sftp.h +++ b/libssh/include/libssh/sftp.h @@ -132,6 +132,8 @@ struct sftp_client_message_struct { int attr_num; ssh_buffer attrbuf; /* used by sftp_reply_attrs */ ssh_string data; /* can be newpath of rename() */ + ssh_buffer complete_message; /* complete message in case of retransmission*/ + char *str_data; /* cstring version of data */ }; struct sftp_request_queue_struct { @@ -202,6 +204,19 @@ struct sftp_statvfs_struct { */ LIBSSH_API sftp_session sftp_new(ssh_session session); +/** + * @brief Start a new sftp session with an existing channel. + * + * @param session The ssh session to use. + * @param channel An open session channel with subsystem already allocated + * + * @return A new sftp session or NULL on error. + * + * @see sftp_free() + */ +LIBSSH_API sftp_session sftp_new_channel(ssh_session session, ssh_channel channel); + + /** * @brief Close and deallocate a sftp session. * @@ -824,8 +839,14 @@ int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr); sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf,int expectname); /* sftpserver.c */ -sftp_client_message sftp_get_client_message(sftp_session sftp); -void sftp_client_message_free(sftp_client_message msg); +LIBSSH_API sftp_client_message sftp_get_client_message(sftp_session sftp); +LIBSSH_API void sftp_client_message_free(sftp_client_message msg); +LIBSSH_API uint8_t sftp_client_message_get_type(sftp_client_message msg); +LIBSSH_API const char *sftp_client_message_get_filename(sftp_client_message msg); +LIBSSH_API void sftp_client_message_set_filename(sftp_client_message msg, const char *newname); +LIBSSH_API const char *sftp_client_message_get_data(sftp_client_message msg); +LIBSSH_API uint32_t sftp_client_message_get_flags(sftp_client_message msg); +LIBSSH_API int sftp_send_client_message(sftp_session sftp, sftp_client_message msg); int sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr); int sftp_reply_handle(sftp_client_message msg, ssh_string handle); diff --git a/libssh/include/libssh/ssh2.h b/libssh/include/libssh/ssh2.h index f66dd2a9..8b39b9a6 100644 --- a/libssh/include/libssh/ssh2.h +++ b/libssh/include/libssh/ssh2.h @@ -30,7 +30,14 @@ #define SSH2_MSG_USERAUTH_PK_OK 60 #define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 #define SSH2_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 #define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 +#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 +#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 +#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 +#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 +#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 + #define SSH2_MSG_GLOBAL_REQUEST 80 #define SSH2_MSG_REQUEST_SUCCESS 81 #define SSH2_MSG_REQUEST_FAILURE 82 diff --git a/libssh/src/CMakeLists.txt b/libssh/src/CMakeLists.txt index 1171c90d..06b239fa 100644 --- a/libssh/src/CMakeLists.txt +++ b/libssh/src/CMakeLists.txt @@ -64,6 +64,18 @@ if (WITH_ZLIB) ) endif (WITH_ZLIB) +if (WITH_GSSAPI AND GSSAPI_FOUND) + set(LIBSSH_PRIVATE_INCLUDE_DIRS + ${LIBSSH_PRIVATE_INCLUDE_DIRS} + ${GSSAPI_INCLUDE_DIR} + ) + + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + ${GSSAPI_LIBRARIES} + ) +endif (WITH_GSSAPI AND GSSAPI_FOUND) + set(LIBSSH_LINK_LIBRARIES ${LIBSSH_LINK_LIBRARIES} CACHE INTERNAL "libssh link libraries" @@ -173,6 +185,13 @@ if (WITH_ZLIB) ) endif(WITH_ZLIB) +if (WITH_GSSAPI AND GSSAPI_FOUND) + set(libssh_SRCS + ${libssh_SRCS} + gssapi.c + ) +endif (WITH_GSSAPI AND GSSAPI_FOUND) + include_directories( ${LIBSSH_PUBLIC_INCLUDE_DIRS} ${LIBSSH_PRIVATE_INCLUDE_DIRS} diff --git a/libssh/src/agent.c b/libssh/src/agent.c index b962a8e3..1b094ed4 100644 --- a/libssh/src/agent.c +++ b/libssh/src/agent.c @@ -42,12 +42,12 @@ #include #include +#ifdef HAVE_UNISTD_H #include +#endif -#ifndef _WIN32 #include #include -#endif #include "libssh/agent.h" #include "libssh/priv.h" @@ -83,23 +83,27 @@ static void agent_put_u32(void *vp, uint32_t v) { p[3] = (uint8_t)v & 0xff; } -static size_t atomicio(ssh_socket s, void *buf, size_t n, int do_read) { +static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int do_read) { char *b = buf; size_t pos = 0; ssize_t res; ssh_pollfd_t pfd; - socket_t fd = ssh_socket_get_fd_in(s); + ssh_channel channel = agent->channel; + socket_t fd; - pfd.fd = fd; - pfd.events = do_read ? POLLIN : POLLOUT; + /* Using a socket ? */ + if (channel == NULL) { + fd = ssh_socket_get_fd_in(agent->sock); + pfd.fd = fd; + pfd.events = do_read ? POLLIN : POLLOUT; - while (n > pos) { - if (do_read) { - res = read(fd, b + pos, n - pos); - } else { - res = write(fd, b + pos, n - pos); - } - switch (res) { + while (n > pos) { + if (do_read) { + res = read(fd, b + pos, n - pos); + } else { + res = write(fd, b + pos, n - pos); + } + switch (res) { case -1: if (errno == EINTR) { continue; @@ -107,22 +111,36 @@ static size_t atomicio(ssh_socket s, void *buf, size_t n, int do_read) { #ifdef EWOULDBLOCK if (errno == EAGAIN || errno == EWOULDBLOCK) { #else - if (errno == EAGAIN) { + if (errno == EAGAIN) { #endif - (void) ssh_poll(&pfd, 1, -1); - continue; + (void) ssh_poll(&pfd, 1, -1); + continue; + } + return 0; + case 0: + /* read returns 0 on end-of-file */ + errno = do_read ? 0 : EPIPE; + return pos; + default: + pos += (size_t) res; } - return 0; - case 0: - /* read returns 0 on end-of-file */ - errno = do_read ? 0 : EPIPE; + } + return pos; + } else { + /* using an SSH channel */ + while (n > pos){ + if (do_read) + res = ssh_channel_read(channel,b + pos, n-pos, 0); + else + res = ssh_channel_write(channel, b+pos, n-pos); + if (res == SSH_AGAIN) + continue; + if (res == SSH_ERROR) + return 0; + pos += (size_t)res; + } return pos; - default: - pos += (size_t) res; } - } - - return pos; } ssh_agent agent_new(struct ssh_session_struct *session) { @@ -140,10 +158,34 @@ ssh_agent agent_new(struct ssh_session_struct *session) { SAFE_FREE(agent); return NULL; } - + agent->channel = NULL; return agent; } +static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channel){ + agent->channel = channel; +} + +/** @brief sets the SSH agent channel. + * The SSH agent channel will be used to authenticate this client using + * an agent through a channel, from another session. The most likely use + * is to implement SSH Agent forwarding into a SSH proxy. + * @param[in] channel a SSH channel from another session. + * @returns SSH_OK in case of success + * SSH_ERROR in case of an error + */ +int ssh_set_agent_channel(ssh_session session, ssh_channel channel){ + if (!session) + return SSH_ERROR; + if (!session->agent){ + ssh_set_error(session, SSH_REQUEST_DENIED, "Session has no active agent"); + return SSH_ERROR; + } + agent_set_channel(session->agent, channel); + return SSH_OK; +} + + void agent_close(struct ssh_agent_struct *agent) { if (agent == NULL) { return; @@ -174,6 +216,9 @@ static int agent_connect(ssh_session session) { return -1; } + if (session->agent->channel != NULL) + return 0; + auth_sock = getenv("SSH_AUTH_SOCK"); if (auth_sock && *auth_sock) { @@ -212,27 +257,27 @@ static int agent_talk(struct ssh_session_struct *session, uint8_t payload[1024] = {0}; len = buffer_get_rest_len(request); - SSH_LOG(session, SSH_LOG_TRACE, "Request length: %u", len); + SSH_LOG(SSH_LOG_TRACE, "Request length: %u", len); agent_put_u32(payload, len); /* send length and then the request packet */ - if (atomicio(session->agent->sock, payload, 4, 0) == 4) { - if (atomicio(session->agent->sock, buffer_get_rest(request), len, 0) + if (atomicio(session->agent, payload, 4, 0) == 4) { + if (atomicio(session->agent, buffer_get_rest(request), len, 0) != len) { - SSH_LOG(session, SSH_LOG_WARN, "atomicio sending request failed: %s", + SSH_LOG(SSH_LOG_WARN, "atomicio sending request failed: %s", strerror(errno)); return -1; } } else { - SSH_LOG(session, SSH_LOG_WARN, + SSH_LOG(SSH_LOG_WARN, "atomicio sending request length failed: %s", strerror(errno)); return -1; } /* wait for response, read the length of the response packet */ - if (atomicio(session->agent->sock, payload, 4, 1) != 4) { - SSH_LOG(session, SSH_LOG_WARN, "atomicio read response length failed: %s", + if (atomicio(session->agent, payload, 4, 1) != 4) { + SSH_LOG(SSH_LOG_WARN, "atomicio read response length failed: %s", strerror(errno)); return -1; } @@ -243,20 +288,20 @@ static int agent_talk(struct ssh_session_struct *session, "Authentication response too long: %u", len); return -1; } - SSH_LOG(session, SSH_LOG_TRACE, "Response length: %u", len); + SSH_LOG(SSH_LOG_TRACE, "Response length: %u", len); while (len > 0) { size_t n = len; if (n > sizeof(payload)) { n = sizeof(payload); } - if (atomicio(session->agent->sock, payload, n, 1) != n) { - SSH_LOG(session, SSH_LOG_WARN, + if (atomicio(session->agent, payload, n, 1) != n) { + SSH_LOG(SSH_LOG_WARN, "Error reading response from authentication socket."); return -1; } if (buffer_add_data(reply, payload, n) < 0) { - SSH_LOG(session, SSH_LOG_WARN, "Not enough space"); + SSH_LOG(SSH_LOG_WARN, "Not enough space"); return -1; } len -= n; @@ -289,11 +334,11 @@ int ssh_agent_get_ident_count(struct ssh_session_struct *session) { /* send message to the agent requesting the list of identities */ request = ssh_buffer_new(); if (request == NULL) { - ssh_set_error_oom(request); + ssh_set_error_oom(session); return -1; } if (buffer_add_u8(request, c1) < 0) { - ssh_set_error_oom(request); + ssh_set_error_oom(session); ssh_buffer_free(request); return -1; } @@ -321,7 +366,7 @@ int ssh_agent_get_ident_count(struct ssh_session_struct *session) { return -1; } - SSH_LOG(session, SSH_LOG_WARN, + SSH_LOG(SSH_LOG_WARN, "Answer type: %d, expected answer: %d", type, c2); @@ -337,7 +382,7 @@ int ssh_agent_get_ident_count(struct ssh_session_struct *session) { buffer_get_u32(reply, (uint32_t *) buf); session->agent->count = agent_get_u32(buf); - SSH_LOG(session, SSH_LOG_DEBUG, "Agent count: %d", + SSH_LOG(SSH_LOG_DEBUG, "Agent count: %d", session->agent->count); if (session->agent->count > 1024) { ssh_set_error(session, SSH_FATAL, @@ -512,7 +557,7 @@ ssh_string ssh_agent_sign_data(ssh_session session, } if (agent_failed(type)) { - SSH_LOG(session, SSH_LOG_WARN, "Agent reports failure in signing the key"); + SSH_LOG(SSH_LOG_WARN, "Agent reports failure in signing the key"); ssh_buffer_free(reply); return NULL; } else if (type != SSH2_AGENT_SIGN_RESPONSE) { diff --git a/libssh/src/auth.c b/libssh/src/auth.c index bdad4e42..de7965d2 100644 --- a/libssh/src/auth.c +++ b/libssh/src/auth.c @@ -42,7 +42,7 @@ #include "libssh/keys.h" #include "libssh/auth.h" #include "libssh/pki.h" - +#include "libssh/gssapi.h" #include "libssh/legacy.h" /** @@ -70,7 +70,7 @@ static int ssh_userauth_request_service(ssh_session session) { rc = ssh_service_request(session, "ssh-userauth"); if (rc != SSH_OK) { - SSH_LOG(session, SSH_LOG_WARN, + SSH_LOG(SSH_LOG_WARN, "Failed to request \"ssh-userauth\" service"); } @@ -82,6 +82,9 @@ static int ssh_auth_response_termination(void *user){ switch(session->auth_state){ case SSH_AUTH_STATE_NONE: case SSH_AUTH_STATE_KBDINT_SENT: + case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT: + case SSH_AUTH_STATE_GSSAPI_TOKEN: + case SSH_AUTH_STATE_GSSAPI_MIC_SENT: return 0; default: return 1; @@ -107,11 +110,9 @@ static int ssh_userauth_get_response(ssh_session session) { rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, ssh_auth_response_termination, session); if (rc == SSH_ERROR) { - leave_function(); return SSH_AUTH_ERROR; } if (!ssh_auth_response_termination(session)){ - leave_function(); return SSH_AUTH_AGAIN; } @@ -132,7 +133,10 @@ static int ssh_userauth_get_response(ssh_session session) { case SSH_AUTH_STATE_SUCCESS: rc = SSH_AUTH_SUCCESS; break; - case SSH_AUTH_STATE_KBDINT_SENT: + case SSH_AUTH_STATE_KBDINT_SENT: + case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT: + case SSH_AUTH_STATE_GSSAPI_TOKEN: + case SSH_AUTH_STATE_GSSAPI_MIC_SENT: case SSH_AUTH_STATE_NONE: /* not reached */ rc = SSH_AUTH_ERROR; @@ -153,19 +157,19 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_banner){ ssh_string banner; (void)type; (void)user; - enter_function(); + banner = buffer_get_ssh_string(packet); if (banner == NULL) { - SSH_LOG(session, SSH_LOG_WARN, + SSH_LOG(SSH_LOG_WARN, "Invalid SSH_USERAUTH_BANNER packet"); } else { - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Received SSH_USERAUTH_BANNER packet"); if(session->banner != NULL) ssh_string_free(session->banner); session->banner = banner; } - leave_function(); + return SSH_PACKET_USED; } @@ -182,7 +186,6 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure){ uint8_t partial = 0; (void) type; (void) user; - enter_function(); auth = buffer_get_ssh_string(packet); if (auth == NULL || buffer_get_u8(packet, &partial) != 1) { @@ -200,12 +203,12 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure){ if (partial) { session->auth_state=SSH_AUTH_STATE_PARTIAL; - SSH_LOG(session, SSH_LOG_INFO, + SSH_LOG(SSH_LOG_INFO, "Partial success. Authentication that can continue: %s", auth_methods); } else { session->auth_state=SSH_AUTH_STATE_FAILED; - SSH_LOG(session, SSH_LOG_INFO, + SSH_LOG(SSH_LOG_INFO, "Access denied. Authentication that can continue: %s", auth_methods); ssh_set_error(session, SSH_REQUEST_DENIED, @@ -226,11 +229,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure){ if (strstr(auth_methods, "hostbased") != NULL) { session->auth_methods |= SSH_AUTH_METHOD_HOSTBASED; } + if (strstr(auth_methods, "gssapi-with-mic") != NULL) { + session->auth_methods |= SSH_AUTH_METHOD_GSSAPI_MIC; + } end: ssh_string_free(auth); SAFE_FREE(auth_methods); - leave_function(); + return SSH_PACKET_USED; } @@ -242,27 +248,26 @@ end: * It is also used to communicate the new to the upper levels. */ SSH_PACKET_CALLBACK(ssh_packet_userauth_success){ - enter_function(); (void)packet; (void)type; (void)user; - SSH_LOG(session, SSH_LOG_DEBUG, "Authentication successful"); - SSH_LOG(session, SSH_LOG_TRACE, "Received SSH_USERAUTH_SUCCESS"); + SSH_LOG(SSH_LOG_DEBUG, "Authentication successful"); + SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_SUCCESS"); session->auth_state=SSH_AUTH_STATE_SUCCESS; session->session_state=SSH_SESSION_STATE_AUTHENTICATED; session->flags |= SSH_SESSION_FLAG_AUTHENTICATED; if(session->current_crypto && session->current_crypto->delayed_compress_out){ - SSH_LOG(session, SSH_LOG_DEBUG, "Enabling delayed compression OUT"); + SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression OUT"); session->current_crypto->do_compress_out=1; } if(session->current_crypto && session->current_crypto->delayed_compress_in){ - SSH_LOG(session,SSH_LOG_DEBUG, "Enabling delayed compression IN"); + SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression IN"); session->current_crypto->do_compress_in=1; } - leave_function(); + return SSH_PACKET_USED; } @@ -276,21 +281,24 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_success){ */ SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok){ int rc; - enter_function(); - SSH_LOG(session, SSH_LOG_TRACE, "Received SSH_USERAUTH_PK_OK/INFO_REQUEST"); + SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_PK_OK/INFO_REQUEST/GSSAPI_RESPONSE"); if(session->auth_state==SSH_AUTH_STATE_KBDINT_SENT){ /* Assuming we are in keyboard-interactive context */ - SSH_LOG(session, SSH_LOG_TRACE, + SSH_LOG(SSH_LOG_TRACE, "keyboard-interactive context, assuming SSH_USERAUTH_INFO_REQUEST"); rc=ssh_packet_userauth_info_request(session,type,packet,user); +#ifdef WITH_GSSAPI + } else if (session->auth_state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT){ + rc = ssh_packet_userauth_gssapi_response(session, type, packet, user); +#endif } else { session->auth_state=SSH_AUTH_STATE_PK_OK; - SSH_LOG(session, SSH_LOG_TRACE, "Assuming SSH_USERAUTH_PK_OK"); + SSH_LOG(SSH_LOG_TRACE, "Assuming SSH_USERAUTH_PK_OK"); rc=SSH_PACKET_USED; } - leave_function(); + return rc; } @@ -981,7 +989,7 @@ int ssh_userauth_agent(ssh_session session, state->pubkey = ssh_agent_get_first_ident(session, &state->comment); while (state->pubkey != NULL) { if(state->state == SSH_AGENT_STATE_NONE){ - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Trying identity %s", state->comment); } if(state->state == SSH_AGENT_STATE_NONE || @@ -996,7 +1004,7 @@ int ssh_userauth_agent(ssh_session session, state->state = SSH_AGENT_STATE_PUBKEY; return rc; } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Public key of %s refused by server", state->comment); ssh_string_free_char(state->comment); ssh_key_free(state->pubkey); @@ -1005,7 +1013,7 @@ int ssh_userauth_agent(ssh_session session, continue; } - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Public key of %s accepted by server", state->comment); state->state = SSH_AGENT_STATE_AUTH; } @@ -1019,7 +1027,7 @@ int ssh_userauth_agent(ssh_session session, SAFE_FREE(session->agent_state); return rc; } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(session, SSH_LOG_INFO, + SSH_LOG(SSH_LOG_INFO, "Server accepted public key but refused the signature"); state->pubkey = ssh_agent_get_next_ident(session, &state->comment); state->state = SSH_AGENT_STATE_NONE; @@ -1109,8 +1117,9 @@ int ssh_userauth_publickey_auto(ssh_session session, #ifndef _WIN32 /* Try authentication with ssh-agent first */ rc = ssh_userauth_agent(session, username); - if (rc == SSH_AUTH_SUCCESS) + if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_AGAIN) { return rc; + } #endif state->state = SSH_AUTH_AUTO_STATE_PUBKEY; } @@ -1122,7 +1131,7 @@ int ssh_userauth_publickey_auto(ssh_session session, const char *privkey_file = state->it->data; char pubkey_file[1024] = {0}; if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY){ - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Trying to authenticate with %s", privkey_file); state->privkey = NULL; state->pubkey = NULL; @@ -1152,7 +1161,7 @@ int ssh_userauth_publickey_auto(ssh_session session, continue; } else if (rc == SSH_EOF) { /* If the file doesn't exist, continue */ - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Private key %s doesn't exist.", privkey_file); state->it=state->it->next; @@ -1168,8 +1177,7 @@ int ssh_userauth_publickey_auto(ssh_session session, rc = ssh_pki_export_pubkey_file(state->pubkey, pubkey_file); if (rc == SSH_ERROR) { - SSH_LOG(session, - SSH_LOG_WARN, + SSH_LOG(SSH_LOG_WARN, "Could not write public key to file: %s", pubkey_file); } @@ -1179,8 +1187,7 @@ int ssh_userauth_publickey_auto(ssh_session session, if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED){ rc = ssh_userauth_try_publickey(session, username, state->pubkey); if (rc == SSH_AUTH_ERROR) { - SSH_LOG(session, - SSH_LOG_WARN, + SSH_LOG(SSH_LOG_WARN, "Public key authentication error for %s", privkey_file); ssh_key_free(state->privkey); @@ -1190,8 +1197,7 @@ int ssh_userauth_publickey_auto(ssh_session session, } else if (rc == SSH_AUTH_AGAIN){ return rc; } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(session, - SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Public key for %s refused by server", privkey_file); ssh_key_free(state->privkey); @@ -1226,8 +1232,7 @@ int ssh_userauth_publickey_auto(ssh_session session, /* If the file doesn't exist, continue */ ssh_key_free(state->pubkey); state->pubkey=NULL; - SSH_LOG(session, - SSH_LOG_INFO, + SSH_LOG(SSH_LOG_INFO, "Private key %s doesn't exist.", privkey_file); state->it=state->it->next; @@ -1245,8 +1250,7 @@ int ssh_userauth_publickey_auto(ssh_session session, if (rc == SSH_AUTH_ERROR) { return rc; } else if (rc == SSH_AUTH_SUCCESS) { - SSH_LOG(session, - SSH_LOG_INFO, + SSH_LOG(SSH_LOG_INFO, "Successfully authenticated using %s", privkey_file); return rc; @@ -1254,16 +1258,14 @@ int ssh_userauth_publickey_auto(ssh_session session, return rc; } - SSH_LOG(session, - SSH_LOG_WARN, + SSH_LOG(SSH_LOG_WARN, "The server accepted the public key but refused the signature"); state->it=state->it->next; state->state=SSH_AUTH_AUTO_STATE_PUBKEY; /* continue */ } } - SSH_LOG(session, - SSH_LOG_INFO, + SSH_LOG(SSH_LOG_INFO, "Tried every public key, none matched"); SAFE_FREE(session->auth_auto_state); return SSH_AUTH_DENIED; @@ -1626,7 +1628,7 @@ static int ssh_userauth_kbdint_init(ssh_session session, session->auth_state = SSH_AUTH_STATE_KBDINT_SENT; session->pending_call_state = SSH_PENDING_CALL_AUTH_KBDINT_INIT; - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Sending keyboard-interactive init request"); rc = packet_send(session); @@ -1700,7 +1702,7 @@ static int ssh_userauth_kbdint_send(ssh_session session) ssh_kbdint_free(session->kbdint); session->kbdint = NULL; - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "Sending keyboard-interactive response packet"); rc = packet_send(session); @@ -1733,7 +1735,6 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { uint32_t i; (void)user; (void)type; - enter_function(); name = buffer_get_ssh_string(packet); instruction = buffer_get_ssh_string(packet); @@ -1747,7 +1748,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { ssh_string_free(name); ssh_string_free(instruction); ssh_set_error(session, SSH_FATAL, "Invalid USERAUTH_INFO_REQUEST msg"); - leave_function(); + return SSH_PACKET_USED; } @@ -1758,7 +1759,6 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { ssh_string_free(name); ssh_string_free(instruction); - leave_function(); return SSH_PACKET_USED; } } else { @@ -1771,7 +1771,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { ssh_set_error_oom(session); ssh_kbdint_free(session->kbdint); ssh_string_free(instruction); - leave_function(); + return SSH_PACKET_USED; } @@ -1781,12 +1781,12 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { ssh_set_error_oom(session); ssh_kbdint_free(session->kbdint); session->kbdint = NULL; - leave_function(); + return SSH_PACKET_USED; } nprompts = ntohl(nprompts); - SSH_LOG(session, SSH_LOG_DEBUG, + SSH_LOG(SSH_LOG_DEBUG, "%d keyboard-interactive prompts", nprompts); if (nprompts > KBDINT_MAX_PROMPT) { ssh_set_error(session, SSH_FATAL, @@ -1794,7 +1794,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { nprompts, nprompts); ssh_kbdint_free(session->kbdint); session->kbdint = NULL; - leave_function(); + return SSH_PACKET_USED; } @@ -1806,7 +1806,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { ssh_set_error_oom(session); ssh_kbdint_free(session->kbdint); session->kbdint = NULL; - leave_function(); + return SSH_PACKET_USED; } memset(session->kbdint->prompts, 0, nprompts * sizeof(char *)); @@ -1817,7 +1817,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { ssh_set_error_oom(session); ssh_kbdint_free(session->kbdint); session->kbdint = NULL; - leave_function(); + return SSH_PACKET_USED; } memset(session->kbdint->echo, 0, nprompts); @@ -1829,7 +1829,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { ssh_set_error(session, SSH_FATAL, "Short INFO_REQUEST packet"); ssh_kbdint_free(session->kbdint); session->kbdint = NULL; - leave_function(); + return SSH_PACKET_USED; } session->kbdint->prompts[i] = ssh_string_to_char(tmp); @@ -1839,12 +1839,12 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { session->kbdint->nprompts = i; ssh_kbdint_free(session->kbdint); session->kbdint = NULL; - leave_function(); + return SSH_PACKET_USED; } } session->auth_state=SSH_AUTH_STATE_INFO; - leave_function(); + return SSH_PACKET_USED; } @@ -2108,6 +2108,62 @@ int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i, return 0; } +/** + * @brief Try to authenticate through the "gssapi-with-mic" method. + * + * @param[in] session The ssh session to use. + * + * @returns SSH_AUTH_ERROR: A serious error happened\n + * SSH_AUTH_DENIED: Authentication failed : use another method\n + * SSH_AUTH_PARTIAL: You've been partially authenticated, you still + * have to use another method\n + * SSH_AUTH_SUCCESS: Authentication success\n + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + */ +int ssh_userauth_gssapi(ssh_session session) { + int rc = SSH_AUTH_DENIED; +#ifdef WITH_GSSAPI + switch(session->pending_call_state) { + case SSH_PENDING_CALL_NONE: + break; + case SSH_PENDING_CALL_AUTH_GSSAPI_MIC: + goto pending; + default: + ssh_set_error(session, + SSH_FATAL, + "Wrong state during pending SSH call"); + return SSH_ERROR; + } + + rc = ssh_userauth_request_service(session); + if (rc == SSH_AGAIN) { + return SSH_AUTH_AGAIN; + } else if (rc == SSH_ERROR) { + return SSH_AUTH_ERROR; + } + SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi-with-mic"); + session->auth_state = SSH_AUTH_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_AUTH_GSSAPI_MIC; + rc = ssh_gssapi_auth_mic(session); + + if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_DENIED) { + session->auth_state = SSH_AUTH_STATE_NONE; + session->pending_call_state = SSH_PENDING_CALL_NONE; + return rc; + } + +pending: + rc = ssh_userauth_get_response(session); + if (rc != SSH_AUTH_AGAIN) { + session->pending_call_state = SSH_PENDING_CALL_NONE; + } +#else + (void) session; /* unused */ +#endif + return rc; +} + /** @} */ /* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/auth1.c b/libssh/src/auth1.c index 0f3f096d..a65c4475 100644 --- a/libssh/src/auth1.c +++ b/libssh/src/auth1.c @@ -44,15 +44,14 @@ static int ssh_auth_status_termination(void *s){ } static int wait_auth1_status(ssh_session session) { - enter_function(); /* wait for a packet */ if (ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, ssh_auth_status_termination, session) != SSH_OK){ - leave_function(); + return SSH_AUTH_ERROR; } - ssh_log(session,SSH_LOG_PROTOCOL,"Auth state : %d",session->auth_state); - leave_function(); + SSH_LOG(SSH_LOG_PROTOCOL,"Auth state : %d",session->auth_state); + switch(session->auth_state) { case SSH_AUTH_STATE_SUCCESS: return SSH_AUTH_SUCCESS; @@ -153,8 +152,7 @@ int ssh_userauth1_offer_pubkey(ssh_session session, const char *username, (void) username; (void) type; (void) pubkey; - enter_function(); - leave_function(); + return SSH_AUTH_DENIED; } @@ -162,10 +160,9 @@ int ssh_userauth1_password(ssh_session session, const char *username, const char *password) { ssh_string pwd = NULL; int rc; - enter_function(); + rc = send_username(session, username); if (rc != SSH_AUTH_DENIED) { - leave_function(); return rc; } if (session->pending_call_state == SSH_PENDING_CALL_AUTH_PASSWORD) @@ -181,7 +178,6 @@ int ssh_userauth1_password(ssh_session session, const char *username, /* not risky to disclose the size of such a big password .. */ pwd = ssh_string_from_char(password); if (pwd == NULL) { - leave_function(); return SSH_AUTH_ERROR; } } else { @@ -194,7 +190,6 @@ int ssh_userauth1_password(ssh_session session, const char *username, */ pwd = ssh_string_new(sizeof(buf)); if (pwd == NULL) { - leave_function(); return SSH_AUTH_ERROR; } ssh_get_random(buf, sizeof(buf), 0); @@ -205,13 +200,13 @@ int ssh_userauth1_password(ssh_session session, const char *username, if (buffer_add_u8(session->out_buffer, SSH_CMSG_AUTH_PASSWORD) < 0) { ssh_string_burn(pwd); ssh_string_free(pwd); - leave_function(); + return SSH_AUTH_ERROR; } if (buffer_add_ssh_string(session->out_buffer, pwd) < 0) { ssh_string_burn(pwd); ssh_string_free(pwd); - leave_function(); + return SSH_AUTH_ERROR; } @@ -220,14 +215,13 @@ int ssh_userauth1_password(ssh_session session, const char *username, session->auth_state=SSH_AUTH_STATE_NONE; session->pending_call_state = SSH_PENDING_CALL_AUTH_PASSWORD; if (packet_send(session) == SSH_ERROR) { - leave_function(); return SSH_AUTH_ERROR; } pending: rc = wait_auth1_status(session); if (rc != SSH_AUTH_AGAIN) session->pending_call_state = SSH_PENDING_CALL_NONE; - leave_function(); + return rc; } diff --git a/libssh/src/bind.c b/libssh/src/bind.c index add5a702..e4c9327e 100644 --- a/libssh/src/bind.c +++ b/libssh/src/bind.c @@ -169,7 +169,7 @@ int ssh_bind_listen(ssh_bind sshbind) { NULL, NULL, &sshbind->ecdsa); - if (rc == SSH_ERROR) { + if (rc == SSH_ERROR || rc == SSH_EOF) { ssh_set_error(sshbind, SSH_FATAL, "Failed to import private ECDSA host key"); return SSH_ERROR; @@ -190,7 +190,7 @@ int ssh_bind_listen(ssh_bind sshbind) { NULL, NULL, &sshbind->dsa); - if (rc == SSH_ERROR) { + if (rc == SSH_ERROR || rc == SSH_EOF) { ssh_set_error(sshbind, SSH_FATAL, "Failed to import private DSA host key"); return SSH_ERROR; @@ -211,7 +211,7 @@ int ssh_bind_listen(ssh_bind sshbind) { NULL, NULL, &sshbind->rsa); - if (rc == SSH_ERROR) { + if (rc == SSH_ERROR || rc == SSH_EOF) { ssh_set_error(sshbind, SSH_FATAL, "Failed to import private RSA host key"); return SSH_ERROR; @@ -250,7 +250,7 @@ int ssh_bind_listen(ssh_bind sshbind) { return -1; } } else { - SSH_LOG(sshbind, SSH_LOG_INFO, "Using app-provided bind socket"); + SSH_LOG(SSH_LOG_INFO, "Using app-provided bind socket"); } return 0; } diff --git a/libssh/src/callbacks.c b/libssh/src/callbacks.c index 5a61180d..afc45019 100644 --- a/libssh/src/callbacks.c +++ b/libssh/src/callbacks.c @@ -26,19 +26,41 @@ #include "libssh/callbacks.h" #include "libssh/session.h" + +/* LEGACY */ +static void ssh_legacy_log_callback(int priority, + const char *function, + const char *buffer, + void *userdata) +{ + ssh_session session = (ssh_session)userdata; + ssh_log_callback log_fn = session->common.callbacks->log_function; + void *log_data = session->common.callbacks->userdata; + + (void)function; /* unused */ + + log_fn(session, priority, buffer, log_data); +} + int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) { if (session == NULL || cb == NULL) { return SSH_ERROR; } - enter_function(); + if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ ssh_set_error(session,SSH_FATAL, "Invalid callback passed in (badly initialized)"); - leave_function(); + return SSH_ERROR; } session->common.callbacks = cb; - leave_function(); + + /* LEGACY */ + if (ssh_get_log_callback() == NULL && cb->log_function) { + ssh_set_log_callback(ssh_legacy_log_callback); + ssh_set_log_userdata(session); + } + return 0; } @@ -48,14 +70,30 @@ int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb) { return SSH_ERROR; } session = channel->session; - enter_function(); + if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ ssh_set_error(session,SSH_FATAL, "Invalid channel callback passed in (badly initialized)"); - leave_function(); + return SSH_ERROR; } channel->callbacks = cb; - leave_function(); + return 0; } + +int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb){ + if (session == NULL || cb == NULL) { + return SSH_ERROR; + } + + if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ + ssh_set_error(session,SSH_FATAL, + "Invalid callback passed in (badly initialized)"); + + return SSH_ERROR; + } + session->server_callbacks = cb; + + return 0; +} diff --git a/libssh/src/channels.c b/libssh/src/channels.c index 902e04b0..f4376e83 100644 --- a/libssh/src/channels.c +++ b/libssh/src/channels.c @@ -143,8 +143,8 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ ssh_channel channel; (void)type; (void)user; - enter_function(); - ssh_log(session,SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"); + + SSH_LOG(SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"); buffer_get_u32(packet, &channelid); channelid=ntohl(channelid); @@ -154,7 +154,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ "Unknown channel id %lu", (long unsigned int) channelid); /* TODO: Set error marking in channel object */ - leave_function(); + return SSH_PACKET_USED; } @@ -167,18 +167,18 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ buffer_get_u32(packet,&tmp); channel->remote_maxpacket=ntohl(tmp); - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d", channel->local_channel, channel->remote_channel); - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Remote window : %lu, maxpacket : %lu", (long unsigned int) channel->remote_window, (long unsigned int) channel->remote_maxpacket); channel->state = SSH_CHANNEL_STATE_OPEN; channel->flags = channel->flags & ~SSH_CHANNEL_FLAG_NOT_BOUND; - leave_function(); + return SSH_PACKET_USED; } @@ -197,7 +197,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){ (void)type; channel=channel_from_msg(session,packet); if(channel==NULL){ - ssh_log(session,SSH_LOG_RARE,"Invalid channel in packet"); + SSH_LOG(SSH_LOG_RARE,"Invalid channel in packet"); return SSH_PACKET_USED; } buffer_get_u32(packet, &code); @@ -254,7 +254,6 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, ssh_string type = NULL; int err=SSH_ERROR; - enter_function(); switch(channel->state){ case SSH_CHANNEL_STATE_NOT_OPEN: break; @@ -271,14 +270,14 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, channel->local_maxpacket = maxpacket; channel->local_window = window; - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Creating a channel %d with %d window and %d max packet", channel->local_channel, window, maxpacket); type = ssh_string_from_char(type_c); if (type == NULL) { ssh_set_error_oom(session); - leave_function(); + return err; } @@ -289,7 +288,7 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, buffer_add_u32(session->out_buffer, htonl(channel->local_maxpacket)) < 0) { ssh_set_error_oom(session); ssh_string_free(type); - leave_function(); + return err; } @@ -298,17 +297,17 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, if (payload != NULL) { if (buffer_add_buffer(session->out_buffer, payload) < 0) { ssh_set_error_oom(session); - leave_function(); + return err; } } channel->state = SSH_CHANNEL_STATE_OPENING; if (packet_send(session) == SSH_ERROR) { - leave_function(); + return err; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d", type_c, channel->local_channel); pending: @@ -319,7 +318,7 @@ pending: end: if(channel->state == SSH_CHANNEL_STATE_OPEN) err=SSH_OK; - leave_function(); + return err; } @@ -351,20 +350,19 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) { static int grow_window(ssh_session session, ssh_channel channel, int minimumsize) { uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE; - enter_function(); #ifdef WITH_SSH1 if (session->version == 1){ channel->remote_window = new_window; - leave_function(); + return SSH_OK; } #endif if(new_window <= channel->local_window){ - ssh_log(session,SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)", channel->local_channel, channel->remote_channel, new_window, channel->local_window); - leave_function(); + return SSH_OK; } /* WINDOW_ADJUST packet needs a relative increment rather than an absolute @@ -381,7 +379,7 @@ static int grow_window(ssh_session session, ssh_channel channel, int minimumsize goto error; } - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "growing window (channel %d:%d) to %d bytes", channel->local_channel, channel->remote_channel, @@ -389,12 +387,10 @@ static int grow_window(ssh_session session, ssh_channel channel, int minimumsize channel->local_window = new_window; - leave_function(); return SSH_OK; error: buffer_reinit(session->out_buffer); - leave_function(); return SSH_ERROR; } @@ -443,23 +439,22 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) { int rc; (void)user; (void)type; - enter_function(); channel = channel_from_msg(session,packet); if (channel == NULL) { - ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); } rc = buffer_get_u32(packet, &bytes); if (channel == NULL || rc != sizeof(uint32_t)) { - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Error getting a window adjust message: invalid packet"); - leave_function(); + return SSH_PACKET_USED; } bytes = ntohl(bytes); - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Adding %d bytes to channel (%d:%d) (from %d bytes)", bytes, channel->local_channel, @@ -468,7 +463,6 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) { channel->remote_window += bytes; - leave_function(); return SSH_PACKET_USED; } @@ -481,7 +475,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ int is_stderr; int rest; (void)user; - enter_function(); + if(type==SSH2_MSG_CHANNEL_DATA) is_stderr=0; else @@ -489,9 +483,9 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ channel = channel_from_msg(session,packet); if (channel == NULL) { - ssh_log(session, SSH_LOG_FUNCTIONS, + SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - leave_function(); + return SSH_PACKET_USED; } @@ -503,13 +497,13 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ str = buffer_get_ssh_string(packet); if (str == NULL) { - ssh_log(session, SSH_LOG_PACKET, "Invalid data packet!"); - leave_function(); + SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!"); + return SSH_PACKET_USED; } len = ssh_string_len(str); - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Channel receiving %" PRIdS " bytes data in %d (local win=%d remote win=%d)", len, is_stderr, @@ -518,7 +512,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ /* What shall we do in this case? Let's accept it anyway */ if (len > channel->local_window) { - ssh_log(session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "Data packet too big for our window(%" PRIdS " vs %d)", len, channel->local_window); @@ -527,7 +521,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ if (channel_default_bufferize(channel, ssh_string_data(str), len, is_stderr) < 0) { ssh_string_free(str); - leave_function(); + return SSH_PACKET_USED; } @@ -537,7 +531,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ channel->local_window = 0; /* buggy remote */ } - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Channel windows are now (local win=%d remote win=%d)", channel->local_window, channel->remote_window); @@ -561,13 +555,11 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ } if (channel->local_window + buffer_get_rest_len(buf) < WINDOWLIMIT) { if (grow_window(session, channel, 0) < 0) { - leave_function(); return -1; } } } - leave_function(); return SSH_PACKET_USED; } @@ -575,16 +567,15 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) { ssh_channel channel; (void)user; (void)type; - enter_function(); channel = channel_from_msg(session,packet); if (channel == NULL) { - ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - leave_function(); + SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + return SSH_PACKET_USED; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received eof on channel (%d:%d)", channel->local_channel, channel->remote_channel); @@ -597,7 +588,6 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) { channel->callbacks->userdata); } - leave_function(); return SSH_PACKET_USED; } @@ -605,16 +595,15 @@ SSH_PACKET_CALLBACK(channel_rcv_close) { ssh_channel channel; (void)user; (void)type; - enter_function(); channel = channel_from_msg(session,packet); if (channel == NULL) { - ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - leave_function(); + SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + return SSH_PACKET_USED; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received close on channel (%d:%d)", channel->local_channel, channel->remote_channel); @@ -628,7 +617,7 @@ SSH_PACKET_CALLBACK(channel_rcv_close) { channel->state = SSH_CHANNEL_STATE_CLOSED; } if (channel->remote_eof == 0) { - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Remote host not polite enough to send an eof before close"); } channel->remote_eof = 1; @@ -645,7 +634,7 @@ SSH_PACKET_CALLBACK(channel_rcv_close) { channel->flags &= SSH_CHANNEL_FLAG_CLOSED_REMOTE; if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL) ssh_channel_do_free(channel); - leave_function(); + return SSH_PACKET_USED; } @@ -658,26 +647,24 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { (void)user; (void)type; - enter_function(); - channel = channel_from_msg(session,packet); if (channel == NULL) { - ssh_log(session, SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session)); - leave_function(); + SSH_LOG(SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session)); + return SSH_PACKET_USED; } request_s = buffer_get_ssh_string(packet); if (request_s == NULL) { - ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - leave_function(); + SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + return SSH_PACKET_USED; } request = ssh_string_to_char(request_s); ssh_string_free(request_s); if (request == NULL) { - leave_function(); + return SSH_PACKET_USED; } @@ -689,7 +676,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { SAFE_FREE(request); buffer_get_u32(packet, &exit_status); channel->exit_status = ntohl(exit_status); - ssh_log(session, SSH_LOG_PACKET, "received exit-status %d", channel->exit_status); + SSH_LOG(SSH_LOG_PACKET, "received exit-status %d", channel->exit_status); if(ssh_callbacks_exists(channel->callbacks, channel_exit_status_function)) { channel->callbacks->channel_exit_status_function(channel->session, @@ -698,7 +685,6 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { channel->callbacks->userdata); } - leave_function(); return SSH_PACKET_USED; } @@ -707,24 +693,24 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { char *sig; SAFE_FREE(request); - ssh_log(session, SSH_LOG_PACKET, "received signal"); + SSH_LOG(SSH_LOG_PACKET, "received signal"); signal_str = buffer_get_ssh_string(packet); if (signal_str == NULL) { - ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - leave_function(); + SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + return SSH_PACKET_USED; } sig = ssh_string_to_char(signal_str); ssh_string_free(signal_str); if (sig == NULL) { - leave_function(); + return SSH_PACKET_USED; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Remote connection sent a signal SIG %s", sig); if(ssh_callbacks_exists(channel->callbacks, channel_signal_function)) { channel->callbacks->channel_signal_function(channel->session, @@ -734,7 +720,6 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { } SAFE_FREE(sig); - leave_function(); return SSH_PACKET_USED; } @@ -750,15 +735,15 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { tmp = buffer_get_ssh_string(packet); if (tmp == NULL) { - ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - leave_function(); + SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + return SSH_PACKET_USED; } sig = ssh_string_to_char(tmp); ssh_string_free(tmp); if (sig == NULL) { - leave_function(); + return SSH_PACKET_USED; } @@ -769,9 +754,9 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { tmp = buffer_get_ssh_string(packet); if (tmp == NULL) { - ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); SAFE_FREE(sig); - leave_function(); + return SSH_PACKET_USED; } @@ -779,16 +764,16 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { ssh_string_free(tmp); if (errmsg == NULL) { SAFE_FREE(sig); - leave_function(); + return SSH_PACKET_USED; } tmp = buffer_get_ssh_string(packet); if (tmp == NULL) { - ssh_log(session, SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); + SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); SAFE_FREE(errmsg); SAFE_FREE(sig); - leave_function(); + return SSH_PACKET_USED; } @@ -797,11 +782,11 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { if (lang == NULL) { SAFE_FREE(errmsg); SAFE_FREE(sig); - leave_function(); + return SSH_PACKET_USED; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Remote connection closed by signal SIG %s %s", sig, core); if(ssh_callbacks_exists(channel->callbacks, channel_exit_signal_function)) { channel->callbacks->channel_exit_signal_function(channel->session, @@ -814,12 +799,11 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { SAFE_FREE(errmsg); SAFE_FREE(sig); - leave_function(); return SSH_PACKET_USED; } if(strcmp(request,"keepalive@openssh.com")==0){ SAFE_FREE(request); - ssh_log(session, SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive"); + SSH_LOG(SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive"); rc = buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_FAILURE); if (rc < 0) { return SSH_PACKET_USED; @@ -829,19 +813,32 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { return SSH_PACKET_USED; } packet_send(session); - leave_function(); + return SSH_PACKET_USED; } + if (strcmp(request, "auth-agent-req@openssh.com") == 0) { + SAFE_FREE(request); + SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request"); + if(ssh_callbacks_exists(channel->callbacks, channel_auth_agent_req_function)) { + channel->callbacks->channel_auth_agent_req_function(channel->session, channel, + channel->callbacks->userdata); + } + + return SSH_PACKET_USED; + } +#ifdef WITH_SERVER /* If we are here, that means we have a request that is not in the understood * client requests. That means we need to create a ssh message to be passed * to the user code handling ssh messages */ ssh_message_handle_channel_request(session,channel,packet,request,status); - +#else + SSH_LOG(SSH_LOG_WARNING, "Unhandled channel request %s", request); +#endif + SAFE_FREE(request); - leave_function(); return SSH_PACKET_USED; } @@ -866,7 +863,7 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, return -1; } - ssh_log(session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "placing %d bytes into channel buffer (stderr=%d)", len, is_stderr); if (is_stderr == 0) { /* stdout */ @@ -938,6 +935,40 @@ int ssh_channel_open_session(ssh_channel channel) { NULL); } +/** + * @brief Open an agent authentication forwarding channel. This type of channel + * can be opened by a server towards a client in order to provide SSH-Agent services + * to the server-side process. This channel can only be opened if the client + * claimed support by sending a channel request beforehand. + * + * @param[in] channel An allocated channel. + * + * @return SSH_OK on success, + * SSH_ERROR if an error occurred, + * SSH_AGAIN if in nonblocking mode and call has + * to be done again. + * + * @see channel_open_forward() + */ +int ssh_channel_open_auth_agent(ssh_channel channel){ + if(channel == NULL) { + return SSH_ERROR; + } + +#ifdef WITH_SSH1 + if (channel->session->version == 1) { + return SSH_ERROR; + } +#endif + + return channel_open(channel, + "auth-agent@openssh.com", + CHANNEL_INITIAL_WINDOW, + CHANNEL_MAX_PACKET, + NULL); +} + + /** * @brief Open a TCP/IP forwarding channel. * @@ -981,8 +1012,6 @@ int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, return rc; } - enter_function(); - payload = ssh_buffer_new(); if (payload == NULL) { ssh_set_error_oom(session); @@ -1023,7 +1052,6 @@ error: ssh_buffer_free(payload); ssh_string_free(str); - leave_function(); return rc; } @@ -1043,8 +1071,6 @@ void ssh_channel_free(ssh_channel channel) { } session = channel->session; - enter_function(); - if (session->alive && channel->state == SSH_CHANNEL_STATE_OPEN) { ssh_channel_close(channel); } @@ -1059,7 +1085,6 @@ void ssh_channel_free(ssh_channel channel) { || (channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)){ ssh_channel_do_free(channel); } - leave_function(); } /** @@ -1103,7 +1128,6 @@ int ssh_channel_send_eof(ssh_channel channel){ } session = channel->session; - enter_function(); if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_EOF) < 0) { ssh_set_error_oom(session); @@ -1114,19 +1138,17 @@ int ssh_channel_send_eof(ssh_channel channel){ goto error; } rc = packet_send(session); - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sent a EOF on client channel (%d:%d)", channel->local_channel, channel->remote_channel); channel->local_eof = 1; - leave_function(); return rc; error: buffer_reinit(session->out_buffer); - leave_function(); return rc; } @@ -1152,14 +1174,12 @@ int ssh_channel_close(ssh_channel channel){ } session = channel->session; - enter_function(); if (channel->local_eof == 0) { rc = ssh_channel_send_eof(channel); } if (rc != SSH_OK) { - leave_function(); return rc; } @@ -1170,7 +1190,7 @@ int ssh_channel_close(ssh_channel channel){ } rc = packet_send(session); - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sent a close on client channel (%d:%d)", channel->local_channel, channel->remote_channel); @@ -1179,12 +1199,10 @@ int ssh_channel_close(ssh_channel channel){ channel->state=SSH_CHANNEL_STATE_CLOSED; } - leave_function(); return rc; error: buffer_reinit(session->out_buffer); - leave_function(); return rc; } @@ -1222,11 +1240,13 @@ static int ssh_waitsession_unblocked(void *s){ * SSH_AGAIN Timeout elapsed (or in nonblocking mode) */ int ssh_channel_flush(ssh_channel channel){ - return ssh_blocking_flush(channel->session, SSH_TIMEOUT_USER); + return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT); } -int channel_write_common(ssh_channel channel, const void *data, - uint32_t len, int is_stderr) { +static int channel_write_common(ssh_channel channel, + const void *data, + uint32_t len, int is_stderr) +{ ssh_session session; uint32_t origlen = len; size_t effectivelen; @@ -1243,13 +1263,11 @@ int channel_write_common(ssh_channel channel, const void *data, } if (len > INT_MAX) { - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Length (%u) is bigger than INT_MAX", len); return SSH_ERROR; } - enter_function(); - /* * Handle the max packet len from remote side, be nice * 10 bytes for the headers @@ -1261,98 +1279,117 @@ int channel_write_common(ssh_channel channel, const void *data, "Can't write to channel %d:%d after EOF was sent", channel->local_channel, channel->remote_channel); - leave_function(); return -1; } if (channel->state != SSH_CHANNEL_STATE_OPEN || channel->delayed_close != 0) { ssh_set_error(session, SSH_REQUEST_DENIED, "Remote channel is closed"); - leave_function(); + return -1; } - if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ - leave_function(); + + if (channel->session->session_state == SSH_SESSION_STATE_ERROR) { return SSH_ERROR; } #ifdef WITH_SSH1 if (channel->version == 1) { rc = channel_write1(channel, data, len); - leave_function(); + return rc; } #endif if (ssh_waitsession_unblocked(session) == 0){ - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT, ssh_waitsession_unblocked, session); if (rc == SSH_ERROR || !ssh_waitsession_unblocked(session)) goto out; } while (len > 0) { if (channel->remote_window < len) { - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Remote window is %d bytes. going to write %d bytes", channel->remote_window, len); /* What happens when the channel window is zero? */ if(channel->remote_window == 0) { /* nothing can be written */ - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Wait for a growing window message..."); - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT, ssh_channel_waitwindow_termination,channel); if (rc == SSH_ERROR || !ssh_channel_waitwindow_termination(channel)) goto out; continue; } - effectivelen = len > channel->remote_window ? channel->remote_window : len; + effectivelen = MIN(len, channel->remote_window); } else { effectivelen = len; } - effectivelen = effectivelen > maxpacketlen ? maxpacketlen : effectivelen; - if (buffer_add_u8(session->out_buffer, is_stderr ? - SSH2_MSG_CHANNEL_EXTENDED_DATA : SSH2_MSG_CHANNEL_DATA) < 0 || - buffer_add_u32(session->out_buffer, - htonl(channel->remote_channel)) < 0) { + + effectivelen = MIN(effectivelen, maxpacketlen);; + + rc = buffer_add_u8(session->out_buffer, + is_stderr ? SSH2_MSG_CHANNEL_EXTENDED_DATA + : SSH2_MSG_CHANNEL_DATA); + if (rc < 0) { ssh_set_error_oom(session); goto error; } + + rc = buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)); + if (rc < 0) { + ssh_set_error_oom(session); + goto error; + } + /* stderr message has an extra field */ - if (is_stderr && - buffer_add_u32(session->out_buffer, htonl(SSH2_EXTENDED_DATA_STDERR)) < 0) { + if (is_stderr) { + rc = buffer_add_u32(session->out_buffer, + htonl(SSH2_EXTENDED_DATA_STDERR)); + if (rc < 0) { + ssh_set_error_oom(session); + goto error; + } + } + + /* append payload data */ + rc = buffer_add_u32(session->out_buffer, htonl(effectivelen)); + if (rc < 0) { ssh_set_error_oom(session); goto error; } - /* append payload data */ - if (buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 || - buffer_add_data(session->out_buffer, data, effectivelen) < 0) { - ssh_set_error_oom(session); - goto error; + + rc = buffer_add_data(session->out_buffer, data, effectivelen); + if (rc < 0) { + ssh_set_error_oom(session); + goto error; } - if (packet_send(session) == SSH_ERROR) { - leave_function(); - return SSH_ERROR; + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_ERROR; } - ssh_log(session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "channel_write wrote %ld bytes", (long int) effectivelen); channel->remote_window -= effectivelen; len -= effectivelen; data = ((uint8_t*)data + effectivelen); } + /* it's a good idea to flush the socket now */ rc = ssh_channel_flush(channel); - if(rc == SSH_ERROR) - goto error; + if (rc == SSH_ERROR) { + goto error; + } + out: - leave_function(); return (int)(origlen - len); error: buffer_reinit(session->out_buffer); - leave_function(); return SSH_ERROR; } @@ -1459,26 +1496,24 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_success){ ssh_channel channel; (void)type; (void)user; - enter_function(); + channel=channel_from_msg(session,packet); if (channel == NULL) { - ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - leave_function(); + SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); return SSH_PACKET_USED; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received SSH_CHANNEL_SUCCESS on channel (%d:%d)", channel->local_channel, channel->remote_channel); if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){ - ssh_log(session, SSH_LOG_RARE, "SSH_CHANNEL_SUCCESS received in incorrect state %d", + SSH_LOG(SSH_LOG_RARE, "SSH_CHANNEL_SUCCESS received in incorrect state %d", channel->request_state); } else { channel->request_state=SSH_CHANNEL_REQ_STATE_ACCEPTED; } - leave_function(); return SSH_PACKET_USED; } @@ -1493,25 +1528,25 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_failure){ ssh_channel channel; (void)type; (void)user; - enter_function(); + channel=channel_from_msg(session,packet); if (channel == NULL) { - ssh_log(session, SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - leave_function(); + SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); + return SSH_PACKET_USED; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received SSH_CHANNEL_FAILURE on channel (%d:%d)", channel->local_channel, channel->remote_channel); if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){ - ssh_log(session, SSH_LOG_RARE, "SSH_CHANNEL_FAILURE received in incorrect state %d", + SSH_LOG(SSH_LOG_RARE, "SSH_CHANNEL_FAILURE received in incorrect state %d", channel->request_state); } else { channel->request_state=SSH_CHANNEL_REQ_STATE_DENIED; } - leave_function(); + return SSH_PACKET_USED; } @@ -1530,7 +1565,6 @@ static int channel_request(ssh_channel channel, const char *request, ssh_string req = NULL; int rc = SSH_ERROR; - enter_function(); switch(channel->request_state){ case SSH_CHANNEL_REQ_STATE_NONE: break; @@ -1563,15 +1597,13 @@ static int channel_request(ssh_channel channel, const char *request, } channel->request_state = SSH_CHANNEL_REQ_STATE_PENDING; if (packet_send(session) == SSH_ERROR) { - leave_function(); return rc; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sent a SSH_MSG_CHANNEL_REQUEST %s", request); if (reply == 0) { channel->request_state = SSH_CHANNEL_REQ_STATE_NONE; - leave_function(); return SSH_OK; } pending: @@ -1590,13 +1622,12 @@ pending: rc=SSH_ERROR; break; case SSH_CHANNEL_REQ_STATE_ACCEPTED: - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Channel request %s success",request); rc=SSH_OK; break; case SSH_CHANNEL_REQ_STATE_PENDING: rc = SSH_AGAIN; - leave_function(); return rc; case SSH_CHANNEL_REQ_STATE_NONE: /* Never reached */ @@ -1605,12 +1636,11 @@ pending: break; } channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - leave_function(); + return rc; error: buffer_reinit(session->out_buffer); - leave_function(); return rc; } @@ -1647,11 +1677,10 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, return rc; } - enter_function(); #ifdef WITH_SSH1 if (channel->version==1) { rc = channel_request_pty_size1(channel,terminal, col, row); - leave_function(); + return rc; } #endif @@ -1690,7 +1719,6 @@ error: ssh_buffer_free(buffer); ssh_string_free(term); - leave_function(); return rc; } @@ -1730,12 +1758,10 @@ int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) { ssh_buffer buffer = NULL; int rc = SSH_ERROR; - enter_function(); - #ifdef WITH_SSH1 if (channel->version == 1) { rc = channel_change_pty_size1(channel,cols,rows); - leave_function(); + return rc; } #endif @@ -1758,7 +1784,6 @@ int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) { error: ssh_buffer_free(buffer); - leave_function(); return rc; } @@ -2013,18 +2038,16 @@ SSH_PACKET_CALLBACK(ssh_request_success){ (void)type; (void)user; (void)packet; - enter_function(); - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received SSH_REQUEST_SUCCESS"); if(session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING){ - ssh_log(session, SSH_LOG_RARE, "SSH_REQUEST_SUCCESS received in incorrect state %d", + SSH_LOG(SSH_LOG_RARE, "SSH_REQUEST_SUCCESS received in incorrect state %d", session->global_req_state); } else { session->global_req_state=SSH_CHANNEL_REQ_STATE_ACCEPTED; } - leave_function(); return SSH_PACKET_USED; } @@ -2038,18 +2061,16 @@ SSH_PACKET_CALLBACK(ssh_request_denied){ (void)type; (void)user; (void)packet; - enter_function(); - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received SSH_REQUEST_FAILURE"); if(session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING){ - ssh_log(session, SSH_LOG_RARE, "SSH_REQUEST_DENIED received in incorrect state %d", + SSH_LOG(SSH_LOG_RARE, "SSH_REQUEST_DENIED received in incorrect state %d", session->global_req_state); } else { session->global_req_state=SSH_CHANNEL_REQ_STATE_DENIED; } - leave_function(); return SSH_PACKET_USED; } @@ -2087,7 +2108,6 @@ static int global_request(ssh_session session, const char *request, ssh_string req = NULL; int rc = SSH_ERROR; - enter_function(); if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) goto pending; req = ssh_string_from_char(request); @@ -2114,15 +2134,14 @@ static int global_request(ssh_session session, const char *request, } session->global_req_state = SSH_CHANNEL_REQ_STATE_PENDING; if (packet_send(session) == SSH_ERROR) { - leave_function(); return rc; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sent a SSH_MSG_GLOBAL_REQUEST %s", request); if (reply == 0) { session->global_req_state=SSH_CHANNEL_REQ_STATE_NONE; - leave_function(); + return SSH_OK; } pending: @@ -2133,11 +2152,11 @@ pending: } switch(session->global_req_state){ case SSH_CHANNEL_REQ_STATE_ACCEPTED: - ssh_log(session, SSH_LOG_PROTOCOL, "Global request %s success",request); + SSH_LOG(SSH_LOG_PROTOCOL, "Global request %s success",request); rc=SSH_OK; break; case SSH_CHANNEL_REQ_STATE_DENIED: - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Global request %s failed", request); ssh_set_error(session, SSH_REQUEST_DENIED, "Global request %s failed", request); @@ -2152,11 +2171,10 @@ pending: break; } - leave_function(); return rc; error: ssh_string_free(req); - leave_function(); + return rc; } @@ -2547,30 +2565,26 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, return SSH_ERROR; } - enter_function(); buffer_reinit(buffer); if(count==0){ do { r=ssh_channel_poll(channel, is_stderr); if(r < 0){ - leave_function(); return r; } if(r > 0){ r=ssh_channel_read(channel, buffer_tmp, r, is_stderr); if(r < 0){ - leave_function(); return r; } if(buffer_add_data(buffer,buffer_tmp,r) < 0){ ssh_set_error_oom(session); r = SSH_ERROR; } - leave_function(); + return r; } if(ssh_channel_is_eof(channel)){ - leave_function(); return 0; } ssh_handle_packets(channel->session, SSH_TIMEOUT_INFINITE); @@ -2579,21 +2593,19 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, while(total < count){ r=ssh_channel_read(channel, buffer_tmp, sizeof(buffer_tmp), is_stderr); if(r<0){ - leave_function(); return r; } if(r==0){ - leave_function(); return total; } if(buffer_add_data(buffer,buffer_tmp,r) < 0){ ssh_set_error_oom(session); - leave_function(); + return SSH_ERROR; } total += r; } - leave_function(); + return total; } @@ -2653,10 +2665,8 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std session = channel->session; stdbuf = channel->stdout_buffer; - enter_function(); if (count == 0) { - leave_function(); return 0; } @@ -2668,7 +2678,7 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std * We may have problem if the window is too small to accept as much data * as asked */ - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Read (%d) buffered : %d bytes. Window: %d", count, buffer_get_rest_len(stdbuf), @@ -2676,29 +2686,25 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std if (count > buffer_get_rest_len(stdbuf) + channel->local_window) { if (grow_window(session, channel, count - buffer_get_rest_len(stdbuf)) < 0) { - leave_function(); return -1; } } - /* block reading until all bytes are read + /* block reading until at least one byte has been read * and ignore the trivial case count=0 */ ctx.channel = channel; ctx.buffer = stdbuf; - ctx.count = count; - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, + ctx.count = 1; + rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT, ssh_channel_read_termination, &ctx); if (rc == SSH_ERROR){ - leave_function(); return rc; } if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ - leave_function(); return SSH_ERROR; } if (channel->remote_eof && buffer_get_rest_len(stdbuf) == 0) { - leave_function(); return 0; } len = buffer_get_rest_len(stdbuf); @@ -2709,12 +2715,10 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std /* Authorize some buffering while userapp is busy */ if (channel->local_window < WINDOWLIMIT) { if (grow_window(session, channel, 0) < 0) { - leave_function(); return -1; } } - leave_function(); return len; } @@ -2755,16 +2759,14 @@ int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count } session = channel->session; - enter_function(); to_read = ssh_channel_poll(channel, is_stderr); if (to_read <= 0) { if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ - leave_function(); return SSH_ERROR; } - leave_function(); + return to_read; /* may be an error code */ } @@ -2775,7 +2777,7 @@ int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count ssh_set_blocking(session, 0); rc = ssh_channel_read(channel, dest, to_read, is_stderr); ssh_set_blocking(session,blocking); - leave_function(); + return rc; } @@ -2794,16 +2796,13 @@ int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count * @see channel_is_eof() */ int ssh_channel_poll(ssh_channel channel, int is_stderr){ - ssh_session session; ssh_buffer stdbuf; if(channel == NULL) { return SSH_ERROR; } - session = channel->session; stdbuf = channel->stdout_buffer; - enter_function(); if (is_stderr) { stdbuf = channel->stderr_buffer; @@ -2811,26 +2810,21 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr){ if (buffer_get_rest_len(stdbuf) == 0 && channel->remote_eof == 0) { if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ - leave_function(); return SSH_ERROR; } if (ssh_handle_packets(channel->session, SSH_TIMEOUT_NONBLOCKING)==SSH_ERROR) { - leave_function(); return SSH_ERROR; } } if (buffer_get_rest_len(stdbuf) > 0){ - leave_function(); return buffer_get_rest_len(stdbuf); } if (channel->remote_eof) { - leave_function(); return SSH_EOF; } - leave_function(); return buffer_get_rest_len(stdbuf); } @@ -2865,7 +2859,6 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){ session = channel->session; stdbuf = channel->stdout_buffer; - enter_function(); if (is_stderr) { stdbuf = channel->stderr_buffer; @@ -2885,7 +2878,6 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){ if (channel->remote_eof) rc = SSH_EOF; end: - leave_function(); return rc; } @@ -3204,7 +3196,6 @@ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost session = channel->session; - enter_function(); if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN) goto pending; payload = ssh_buffer_new(); @@ -3247,7 +3238,6 @@ error: ssh_buffer_free(payload); ssh_string_free(str); - leave_function(); return rc; } @@ -3284,8 +3274,6 @@ int ssh_channel_open_x11(ssh_channel channel, } session = channel->session; - enter_function(); - if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN) goto pending; @@ -3317,7 +3305,6 @@ error: ssh_buffer_free(payload); ssh_string_free(str); - leave_function(); return rc; } diff --git a/libssh/src/channels1.c b/libssh/src/channels1.c index 62faf4ae..bc66488d 100644 --- a/libssh/src/channels1.c +++ b/libssh/src/channels1.c @@ -27,6 +27,8 @@ #include #ifndef _WIN32 #include +#endif +#ifdef HAVE_UNISTD_H #include #endif @@ -72,7 +74,7 @@ int channel_open_session1(ssh_channel chan) { chan->state = SSH_CHANNEL_STATE_OPEN; chan->local_maxpacket = 32000; chan->local_window = 64000; - ssh_log(session, SSH_LOG_PACKET, "Opened a SSH1 channel session"); + SSH_LOG(SSH_LOG_PACKET, "Opened a SSH1 channel session"); return 0; } @@ -124,7 +126,7 @@ int channel_request_pty_size1(ssh_channel channel, const char *terminal, int col return -1; } - ssh_log(session, SSH_LOG_FUNCTIONS, "Opening a ssh1 pty"); + SSH_LOG(SSH_LOG_FUNCTIONS, "Opening a ssh1 pty"); channel->request_state = SSH_CHANNEL_REQ_STATE_PENDING; if (packet_send(session) == SSH_ERROR) { return -1; @@ -142,13 +144,13 @@ int channel_request_pty_size1(ssh_channel channel, const char *terminal, int col return SSH_ERROR; case SSH_CHANNEL_REQ_STATE_ACCEPTED: channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - ssh_log(session, SSH_LOG_RARE, "PTY: Success"); + SSH_LOG(SSH_LOG_RARE, "PTY: Success"); return SSH_OK; case SSH_CHANNEL_REQ_STATE_DENIED: channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; ssh_set_error(session, SSH_REQUEST_DENIED, "Server denied PTY allocation"); - ssh_log(session, SSH_LOG_RARE, "PTY: denied\n"); + SSH_LOG(SSH_LOG_RARE, "PTY: denied\n"); return SSH_ERROR; } // Not reached @@ -179,7 +181,7 @@ int channel_change_pty_size1(ssh_channel channel, int cols, int rows) { return SSH_ERROR; } - ssh_log(session, SSH_LOG_PROTOCOL, "Change pty size send"); + SSH_LOG(SSH_LOG_PROTOCOL, "Change pty size send"); while(channel->request_state==SSH_CHANNEL_REQ_STATE_PENDING){ ssh_handle_packets(session, SSH_TIMEOUT_INFINITE); } @@ -191,11 +193,11 @@ int channel_change_pty_size1(ssh_channel channel, int cols, int rows) { return SSH_ERROR; case SSH_CHANNEL_REQ_STATE_ACCEPTED: channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - ssh_log(session, SSH_LOG_PROTOCOL, "pty size changed"); + SSH_LOG(SSH_LOG_PROTOCOL, "pty size changed"); return SSH_OK; case SSH_CHANNEL_REQ_STATE_DENIED: channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - ssh_log(session, SSH_LOG_RARE, "pty size change denied"); + SSH_LOG(SSH_LOG_RARE, "pty size change denied"); ssh_set_error(session, SSH_REQUEST_DENIED, "pty size change denied"); return SSH_ERROR; } @@ -220,7 +222,7 @@ int channel_request_shell1(ssh_channel channel) { return -1; } - ssh_log(session, SSH_LOG_RARE, "Launched a shell"); + SSH_LOG(SSH_LOG_RARE, "Launched a shell"); return 0; } @@ -250,7 +252,7 @@ int channel_request_exec1(ssh_channel channel, const char *cmd) { return -1; } - ssh_log(session, SSH_LOG_RARE, "Executing %s ...", cmd); + SSH_LOG(SSH_LOG_RARE, "Executing %s ...", cmd); return 0; } @@ -267,11 +269,11 @@ SSH_PACKET_CALLBACK(ssh_packet_data1){ str = buffer_get_ssh_string(packet); if (str == NULL) { - ssh_log(session, SSH_LOG_FUNCTIONS, "Invalid data packet !\n"); + SSH_LOG(SSH_LOG_FUNCTIONS, "Invalid data packet !\n"); return SSH_PACKET_USED; } - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Adding %" PRIdS " bytes data in %d", ssh_string_len(str), is_stderr); diff --git a/libssh/src/client.c b/libssh/src/client.c index 6203bc4c..99a300b1 100644 --- a/libssh/src/client.c +++ b/libssh/src/client.c @@ -59,14 +59,14 @@ */ static void socket_callback_connected(int code, int errno_code, void *user){ ssh_session session=(ssh_session)user; - enter_function(); + if(session->session_state != SSH_SESSION_STATE_CONNECTING){ ssh_set_error(session,SSH_FATAL, "Wrong state in socket_callback_connected : %d", session->session_state); - leave_function(); + return; } - ssh_log(session,SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code); + SSH_LOG(SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code); if(code == SSH_SOCKET_CONNECTED_OK) session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED; else { @@ -74,7 +74,6 @@ static void socket_callback_connected(int code, int errno_code, void *user){ ssh_set_error(session,SSH_FATAL,"%s",strerror(errno_code)); } session->ssh_connection_callback(session); - leave_function(); } /** @@ -94,10 +93,10 @@ static int callback_receive_banner(const void *data, size_t len, void *user) { char *str = NULL; size_t i; int ret=0; - enter_function(); + if(session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED){ ssh_set_error(session,SSH_FATAL,"Wrong state in callback_receive_banner : %d",session->session_state); - leave_function(); + return SSH_ERROR; } for(i=0;iserverbanner=str; session->session_state=SSH_SESSION_STATE_BANNER_RECEIVED; - ssh_log(session,SSH_LOG_PACKET,"Received banner: %s",str); + SSH_LOG(SSH_LOG_PACKET,"Received banner: %s",str); session->ssh_connection_callback(session); - leave_function(); + return ret; } if(i>127){ /* Too big banner */ session->session_state=SSH_SESSION_STATE_ERROR; ssh_set_error(session,SSH_FATAL,"Receiving banner: too large banner"); - leave_function(); + return 0; } } - leave_function(); + return ret; } @@ -146,8 +145,6 @@ int ssh_send_banner(ssh_session session, int server) { char buffer[128] = {0}; int err=SSH_ERROR; - enter_function(); - banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2; if (server) { @@ -173,7 +170,7 @@ int ssh_send_banner(ssh_session session, int server) { #endif err=SSH_OK; end: - leave_function(); + return err; } @@ -188,8 +185,6 @@ static int dh_handshake(ssh_session session) { int rc = SSH_AGAIN; - enter_function(); - switch (session->dh_handshake_state) { case DH_STATE_INIT: switch(session->next_crypto->kex_type){ @@ -203,12 +198,11 @@ static int dh_handshake(ssh_session session) { break; #endif default: - rc=SSH_ERROR; - goto error; + rc = SSH_ERROR; } if (rc == SSH_ERROR) { - goto error; + return SSH_ERROR; } session->dh_handshake_state = DH_STATE_INIT_SENT; @@ -219,16 +213,14 @@ static int dh_handshake(ssh_session session) { /* wait until ssh_packet_newkeys is called */ break; case DH_STATE_FINISHED: - leave_function(); return SSH_OK; default: ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d", session->dh_handshake_state); - leave_function(); + return SSH_ERROR; } -error: - leave_function(); + return rc; } @@ -259,36 +251,37 @@ static int ssh_service_request_termination(void *s){ int ssh_service_request(ssh_session session, const char *service) { ssh_string service_s = NULL; int rc=SSH_ERROR; - enter_function(); + if(session->auth_service_state != SSH_AUTH_SERVICE_NONE) goto pending; if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_REQUEST) < 0) { - goto error; + return SSH_ERROR; } service_s = ssh_string_from_char(service); if (service_s == NULL) { - goto error; + return SSH_ERROR; } if (buffer_add_ssh_string(session->out_buffer,service_s) < 0) { ssh_string_free(service_s); - goto error; + return SSH_ERROR; } ssh_string_free(service_s); session->auth_service_state=SSH_AUTH_SERVICE_SENT; if (packet_send(session) == SSH_ERROR) { ssh_set_error(session, SSH_FATAL, "Sending SSH2_MSG_SERVICE_REQUEST failed."); - goto error; + return SSH_ERROR; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sent SSH_MSG_SERVICE_REQUEST (service %s)", service); pending: rc=ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, ssh_service_request_termination, session); - if(rc == SSH_ERROR) - goto error; + if (rc == SSH_ERROR) { + return SSH_ERROR; + } switch(session->auth_service_state){ case SSH_AUTH_SERVICE_DENIED: ssh_set_error(session,SSH_FATAL,"ssh_auth_service request denied"); @@ -305,8 +298,7 @@ pending: rc=SSH_ERROR; break; } -error: - leave_function(); + return rc; } @@ -324,7 +316,7 @@ error: */ static void ssh_client_connection_callback(ssh_session session){ int ssh1,ssh2; - enter_function(); + switch(session->session_state){ case SSH_SESSION_STATE_NONE: case SSH_SESSION_STATE_CONNECTING: @@ -335,7 +327,7 @@ static void ssh_client_connection_callback(ssh_session session){ goto error; } set_status(session, 0.4f); - ssh_log(session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "SSH server banner: %s", session->serverbanner); /* Here we analyze the different protocols the server allows. */ @@ -391,7 +383,7 @@ static void ssh_client_connection_callback(ssh_session session){ break; case SSH_SESSION_STATE_KEXINIT_RECEIVED: set_status(session,0.6f); - ssh_list_kex(session, &session->next_crypto->server_kex); + ssh_list_kex(&session->next_crypto->server_kex); if (set_client_kex(session) < 0) { goto error; } @@ -423,13 +415,13 @@ static void ssh_client_connection_callback(ssh_session session){ default: ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); } - leave_function(); + return; - error: +error: ssh_socket_close(session->socket); session->alive = 0; session->session_state=SSH_SESSION_STATE_ERROR; - leave_function(); + } /** @internal @@ -466,7 +458,6 @@ int ssh_connect(ssh_session session) { return SSH_ERROR; } - enter_function(); switch(session->pending_call_state){ case SSH_PENDING_CALL_NONE: break; @@ -474,31 +465,28 @@ int ssh_connect(ssh_session session) { goto pending; default: ssh_set_error(session,SSH_FATAL,"Bad call during pending SSH call in ssh_connect"); - leave_function(); + return SSH_ERROR; } session->alive = 0; session->client = 1; if (ssh_init() < 0) { - leave_function(); return SSH_ERROR; } if (session->opts.fd == SSH_INVALID_SOCKET && session->opts.host == NULL && session->opts.ProxyCommand == NULL) { ssh_set_error(session, SSH_FATAL, "Hostname required"); - leave_function(); return SSH_ERROR; } ret = ssh_options_apply(session); if (ret < 0) { ssh_set_error(session, SSH_FATAL, "Couldn't apply options"); - leave_function(); return SSH_ERROR; } - ssh_log(session,SSH_LOG_RARE,"libssh %s, using threading %s", ssh_copyright(), ssh_threads_get_type()); + SSH_LOG(SSH_LOG_RARE,"libssh %s, using threading %s", ssh_copyright(), ssh_threads_get_type()); session->ssh_connection_callback = ssh_client_connection_callback; session->session_state=SSH_SESSION_STATE_CONNECTING; ssh_socket_set_callbacks(session->socket,&session->socket_callbacks); @@ -521,14 +509,13 @@ int ssh_connect(ssh_session session) { session->opts.bindaddr); } if (ret == SSH_ERROR) { - leave_function(); return SSH_ERROR; } set_status(session, 0.2f); session->alive = 1; - ssh_log(session,SSH_LOG_PROTOCOL,"Socket connecting, now waiting for the callbacks to work"); + SSH_LOG(SSH_LOG_PROTOCOL,"Socket connecting, now waiting for the callbacks to work"); pending: session->pending_call_state=SSH_PENDING_CALL_CONNECT; if(ssh_is_blocking(session)) { @@ -537,7 +524,7 @@ pending: if (timeout == 0) { timeout = 10 * 1000; } - ssh_log(session,SSH_LOG_PACKET,"ssh_connect: Actual timeout : %d", timeout); + SSH_LOG(SSH_LOG_PACKET,"ssh_connect: Actual timeout : %d", timeout); ret = ssh_handle_packets_termination(session, timeout, ssh_connect_termination, session); if (ret == SSH_ERROR || !ssh_connect_termination(session)) { ssh_set_error(session, SSH_FATAL, @@ -554,12 +541,11 @@ pending: session->session_state = SSH_SESSION_STATE_ERROR; } } - ssh_log(session,SSH_LOG_PACKET,"ssh_connect: Actual state : %d",session->session_state); + SSH_LOG(SSH_LOG_PACKET,"ssh_connect: Actual state : %d",session->session_state); if(!ssh_is_blocking(session) && !ssh_connect_termination(session)){ - leave_function(); return SSH_AGAIN; } - leave_function(); + session->pending_call_state=SSH_PENDING_CALL_NONE; if(session->session_state == SSH_SESSION_STATE_ERROR || session->session_state == SSH_SESSION_STATE_DISCONNECTED) return SSH_ERROR; @@ -616,8 +602,6 @@ void ssh_disconnect(ssh_session session) { return; } - enter_function(); - if (session->socket != NULL && ssh_socket_is_open(session->socket)) { if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { goto error; @@ -683,8 +667,6 @@ error: ssh_list_free(session->packet_callbacks); session->packet_callbacks=NULL; } - - leave_function(); } const char *ssh_copyright(void) { diff --git a/libssh/src/config.c b/libssh/src/config.c index 632a50bb..7935e884 100644 --- a/libssh/src/config.c +++ b/libssh/src/config.c @@ -324,7 +324,7 @@ static int ssh_config_parse_line(ssh_session session, const char *line, } break; case SOC_UNSUPPORTED: - ssh_log(session, SSH_LOG_RARE, "Unsupported option: %s, line: %d\n", + SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d\n", keyword, count); break; default: @@ -350,7 +350,7 @@ int ssh_config_parse_file(ssh_session session, const char *filename) { return 0; } - ssh_log(session, SSH_LOG_RARE, "Reading configuration data from %s", filename); + SSH_LOG(SSH_LOG_RARE, "Reading configuration data from %s", filename); parsing = 1; while (fgets(line, sizeof(line), f)) { diff --git a/libssh/src/connect.c b/libssh/src/connect.c index 91e6b6a7..f7965cbe 100644 --- a/libssh/src/connect.c +++ b/libssh/src/connect.c @@ -104,7 +104,7 @@ static int ssh_connect_socket_close(socket_t s){ } -static int getai(ssh_session session, const char *host, int port, struct addrinfo **ai) { +static int getai(const char *host, int port, struct addrinfo **ai) { const char *service = NULL; struct addrinfo hints; char s_port[10]; @@ -127,7 +127,7 @@ static int getai(ssh_session session, const char *host, int port, struct addrinf if (ssh_is_ipaddr(host)) { /* this is an IP address */ - ssh_log(session,SSH_LOG_PACKET,"host %s matches an IP address",host); + SSH_LOG(SSH_LOG_PACKET,"host %s matches an IP address",host); hints.ai_flags |= AI_NUMERICHOST; } @@ -142,8 +142,6 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, int ret; socklen_t len = sizeof(rc); - enter_function(); - /* I know we're losing some precision. But it's not like poll-like family * type of mechanisms are precise up to the microsecond. */ @@ -157,7 +155,7 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, return -1; } - ssh_log(session, SSH_LOG_RARE, "Trying to connect to host: %s:%d with " + SSH_LOG(SSH_LOG_RARE, "Trying to connect to host: %s:%d with " "timeout %d ms", host, port, timeout_ms); /* The return value is checked later */ @@ -177,7 +175,7 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, ssh_set_error(session, SSH_FATAL, "Timeout while connecting to %s:%d", host, port); ssh_connect_socket_close(s); - leave_function(); + return -1; } @@ -185,7 +183,7 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, ssh_set_error(session, SSH_FATAL, "poll error: %s", strerror(errno)); ssh_connect_socket_close(s); - leave_function(); + return -1; } rc = -1; @@ -196,12 +194,12 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, ssh_set_error(session, SSH_FATAL, "Connect to %s:%d failed: %s", host, port, strerror(rc)); ssh_connect_socket_close(s); - leave_function(); + return -1; } /* s is connected ? */ - ssh_log(session, SSH_LOG_PACKET, "Socket connected with timeout\n"); + SSH_LOG(SSH_LOG_PACKET, "Socket connected with timeout\n"); ret = ssh_socket_set_blocking(s); if (ret < 0) { ssh_set_error(session, SSH_FATAL, @@ -211,7 +209,6 @@ static int ssh_connect_ai_timeout(ssh_session session, const char *host, return -1; } - leave_function(); return s; } @@ -230,13 +227,11 @@ socket_t ssh_connect_host(ssh_session session, const char *host, struct addrinfo *ai; struct addrinfo *itr; - enter_function(); - - rc = getai(session,host, port, &ai); + rc = getai(host, port, &ai); if (rc != 0) { ssh_set_error(session, SSH_FATAL, "Failed to resolve hostname %s (%s)", host, gai_strerror(rc)); - leave_function(); + return -1; } @@ -253,9 +248,9 @@ socket_t ssh_connect_host(ssh_session session, const char *host, struct addrinfo *bind_ai; struct addrinfo *bind_itr; - ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr); + SSH_LOG(SSH_LOG_PACKET, "Resolving %s\n", bind_addr); - rc = getai(session,bind_addr, 0, &bind_ai); + rc = getai(bind_addr, 0, &bind_ai); if (rc != 0) { ssh_set_error(session, SSH_FATAL, "Failed to resolve bind address %s (%s)", @@ -263,7 +258,7 @@ socket_t ssh_connect_host(ssh_session session, const char *host, gai_strerror(rc)); freeaddrinfo(ai); close(s); - leave_function(); + return -1; } @@ -289,7 +284,7 @@ socket_t ssh_connect_host(ssh_session session, const char *host, if (timeout || usec) { socket_t ret = ssh_connect_ai_timeout(session, host, port, itr, timeout, usec, s); - leave_function(); + return ret; } @@ -297,7 +292,6 @@ socket_t ssh_connect_host(ssh_session session, const char *host, ssh_set_error(session, SSH_FATAL, "Connect failed: %s", strerror(errno)); ssh_connect_socket_close(s); s = -1; - leave_function(); continue; } else { /* We are connected */ @@ -306,7 +300,6 @@ socket_t ssh_connect_host(ssh_session session, const char *host, } freeaddrinfo(ai); - leave_function(); return s; } @@ -327,13 +320,11 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, struct addrinfo *ai; struct addrinfo *itr; - enter_function(); - - rc = getai(session,host, port, &ai); + rc = getai(host, port, &ai); if (rc != 0) { ssh_set_error(session, SSH_FATAL, "Failed to resolve hostname %s (%s)", host, gai_strerror(rc)); - leave_function(); + return -1; } @@ -350,9 +341,9 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, struct addrinfo *bind_ai; struct addrinfo *bind_itr; - ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr); + SSH_LOG(SSH_LOG_PACKET, "Resolving %s\n", bind_addr); - rc = getai(session,bind_addr, 0, &bind_ai); + rc = getai(bind_addr, 0, &bind_ai); if (rc != 0) { ssh_set_error(session, SSH_FATAL, "Failed to resolve bind address %s (%s)", @@ -396,7 +387,6 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, } freeaddrinfo(ai); - leave_function(); return s; } @@ -446,6 +436,7 @@ static int ssh_select_cb (socket_t fd, int revents, void *userdata){ */ int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, fd_set *readfds, struct timeval *timeout) { + socket_t fd; int i,j; int rc; int base_tm, tm; @@ -457,10 +448,11 @@ int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, for (i=0 ; channels[i] != NULL; ++i){ ssh_event_add_session(event, channels[i]->session); } - for (i=0; iout_buffer, SSH2_MSG_KEXDH_INIT) < 0) { goto error; } @@ -592,7 +592,6 @@ int ssh_client_dh_init(ssh_session session){ ssh_string_free(e); } - leave_function(); return SSH_ERROR; } @@ -639,7 +638,7 @@ int ssh_client_dh_reply(ssh_session session, ssh_buffer packet){ } rc=packet_send(session); - ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); return rc; error: return SSH_ERROR; @@ -664,8 +663,6 @@ int make_sessionid(ssh_session session) { uint32_t len; int rc = SSH_ERROR; - enter_function(); - buf = ssh_buffer_new(); if (buf == NULL) { return rc; @@ -762,7 +759,7 @@ int make_sessionid(ssh_session session) { } else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256){ if(session->next_crypto->ecdh_client_pubkey == NULL || session->next_crypto->ecdh_server_pubkey == NULL){ - ssh_log(session,SSH_LOG_WARNING,"ECDH parameted missing"); + SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing"); goto error; } rc = buffer_add_ssh_string(buf,session->next_crypto->ecdh_client_pubkey); @@ -828,7 +825,7 @@ int make_sessionid(ssh_session session) { session->next_crypto->digest_len); } #ifdef DEBUG_CRYPTO - printf("Session hash: "); + printf("Session hash: \n"); ssh_print_hexa("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len); ssh_print_hexa("session id", session->next_crypto->session_id, session->next_crypto->digest_len); #endif @@ -845,8 +842,6 @@ error: ssh_string_free(str); ssh_string_free(num); - leave_function(); - return rc; } @@ -920,8 +915,6 @@ int generate_session_keys(ssh_session session) { struct ssh_crypto_struct *crypto = session->next_crypto; int rc = -1; - enter_function(); - k_string = make_bignum_string(crypto->k); if (k_string == NULL) { ssh_set_error_oom(session); @@ -983,7 +976,7 @@ int generate_session_keys(ssh_session session) { goto error; } ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); - ssh_mac_update(ctx, crypto->session_id, + ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); ssh_mac_update(ctx, crypto->encryptkey, crypto->digest_len); ssh_mac_final(crypto->encryptkey + crypto->digest_len, ctx); @@ -995,7 +988,7 @@ int generate_session_keys(ssh_session session) { goto error; ctx = ssh_mac_ctx_init(crypto->mac_type); ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); - ssh_mac_update(ctx, crypto->session_id, + ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); ssh_mac_update(ctx, crypto->decryptkey, crypto->digest_len); ssh_mac_final(crypto->decryptkey + crypto->digest_len, ctx); @@ -1030,7 +1023,6 @@ int generate_session_keys(ssh_session session) { rc = 0; error: ssh_string_free(k_string); - leave_function(); return rc; } diff --git a/libssh/src/ecdh.c b/libssh/src/ecdh.c index 075810a9..3f065e7e 100644 --- a/libssh/src/ecdh.c +++ b/libssh/src/ecdh.c @@ -99,6 +99,7 @@ static int ecdh_build_k(ssh_session session) { const EC_GROUP *group = EC_KEY_get0_group(session->next_crypto->ecdh_privkey); EC_POINT *pubkey; void *buffer; + int rc; int len = (EC_GROUP_get_degree(group) + 7) / 8; bignum_CTX ctx = bignum_ctx_new(); if (ctx == NULL) { @@ -117,19 +118,48 @@ static int ecdh_build_k(ssh_session session) { return -1; } - if (session->server) - EC_POINT_oct2point(group,pubkey,ssh_string_data(session->next_crypto->ecdh_client_pubkey), - ssh_string_len(session->next_crypto->ecdh_client_pubkey),ctx); - else - EC_POINT_oct2point(group,pubkey,ssh_string_data(session->next_crypto->ecdh_server_pubkey), - ssh_string_len(session->next_crypto->ecdh_server_pubkey),ctx); + if (session->server) { + rc = EC_POINT_oct2point(group, + pubkey, + ssh_string_data(session->next_crypto->ecdh_client_pubkey), + ssh_string_len(session->next_crypto->ecdh_client_pubkey), + ctx); + } else { + rc = EC_POINT_oct2point(group, + pubkey, + ssh_string_data(session->next_crypto->ecdh_server_pubkey), + ssh_string_len(session->next_crypto->ecdh_server_pubkey), + ctx); + } + bignum_ctx_free(ctx); + if (rc <= 0) { + EC_POINT_clear_free(pubkey); + return -1; + } + buffer = malloc(len); - ECDH_compute_key(buffer,len,pubkey,session->next_crypto->ecdh_privkey,NULL); - EC_POINT_free(pubkey); - BN_bin2bn(buffer,len,session->next_crypto->k); + if (buffer == NULL) { + EC_POINT_clear_free(pubkey); + return -1; + } + + rc = ECDH_compute_key(buffer, + len, + pubkey, + session->next_crypto->ecdh_privkey, + NULL); + EC_POINT_clear_free(pubkey); + if (rc <= 0) { + free(buffer); + return -1; + } + + BN_bin2bn(buffer, len, session->next_crypto->k); free(buffer); + EC_KEY_free(session->next_crypto->ecdh_privkey); - session->next_crypto->ecdh_privkey=NULL; + session->next_crypto->ecdh_privkey = NULL; + #ifdef DEBUG_CRYPTO ssh_print_hexa("Session server cookie", session->next_crypto->server_kex.cookie, 16); @@ -138,10 +168,6 @@ static int ecdh_build_k(ssh_session session) { ssh_print_bignum("Shared secret key", session->next_crypto->k); #endif -#ifdef HAVE_LIBCRYPTO - bignum_ctx_free(ctx); -#endif - return 0; } @@ -186,7 +212,7 @@ int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet){ } rc=packet_send(session); - ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); return rc; error: return SSH_ERROR; @@ -312,17 +338,7 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ return SSH_ERROR; } - /* Free private keys as they should not be readable after this point */ - if (session->srv.rsa_key) { - ssh_key_free(session->srv.rsa_key); - session->srv.rsa_key = NULL; - } - if (session->srv.dsa_key) { - ssh_key_free(session->srv.dsa_key); - session->srv.dsa_key = NULL; - } - - ssh_log(session,SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent"); + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent"); rc = packet_send(session); if (rc == SSH_ERROR) { return SSH_ERROR; @@ -336,7 +352,7 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; rc = packet_send(session); - ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); return rc; } diff --git a/libssh/src/error.c b/libssh/src/error.c index f4a2af00..fbe0e787 100644 --- a/libssh/src/error.c +++ b/libssh/src/error.c @@ -61,11 +61,11 @@ void _ssh_set_error(void *error, va_end(va); err->error.error_code = code; - ssh_log_common(err, - SSH_LOG_WARN, - function, - "Error: %s", - err->error.error_buffer); + if (ssh_get_log_level() >= SSH_LOG_WARN) { + ssh_log_function(SSH_LOG_WARN, + function, + err->error.error_buffer); + } } /** diff --git a/libssh/src/getpass.c b/libssh/src/getpass.c index cc6d33ca..0ffb955d 100644 --- a/libssh/src/getpass.c +++ b/libssh/src/getpass.c @@ -21,6 +21,8 @@ * MA 02111-1307, USA. */ +#include "config.h" + #include #include #include @@ -163,8 +165,12 @@ int ssh_getpass(const char *prompt, #else #include +#ifdef HAVE_TERMIOS_H #include +#endif +#ifdef HAVE_UNISTD_H #include +#endif /** * @ingroup libssh_misc diff --git a/libssh/src/gssapi.c b/libssh/src/gssapi.c new file mode 100644 index 00000000..c92c66cb --- /dev/null +++ b/libssh/src/gssapi.c @@ -0,0 +1,947 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** current state of an GSSAPI authentication */ +enum ssh_gssapi_state_e { + SSH_GSSAPI_STATE_NONE, /* no status */ + SSH_GSSAPI_STATE_RCV_TOKEN, /* Expecting a token */ + SSH_GSSAPI_STATE_RCV_MIC, /* Expecting a MIC */ +}; + +struct ssh_gssapi_struct{ + enum ssh_gssapi_state_e state; /* current state */ + struct gss_OID_desc_struct mech; /* mechanism being elected for auth */ + gss_cred_id_t server_creds; /* credentials of server */ + gss_cred_id_t client_creds; /* creds of the client */ + gss_ctx_id_t ctx; /* the authentication context */ + gss_name_t client_name; /* Identity of the client */ + char *user; /* username of client */ + char *canonic_user; /* canonic form of the client's username */ + char *service; /* name of the service */ + struct { + gss_name_t server_name; /* identity of server */ + gss_OID oid; /* mech being used for authentication */ + gss_cred_id_t client_deleg_creds; /* delegated creds (const, not freeable) */ + } client; +}; + + +/** @internal + * @initializes a gssapi context for authentication + */ +static int ssh_gssapi_init(ssh_session session){ + if (session->gssapi != NULL) + return SSH_OK; + session->gssapi = malloc(sizeof(struct ssh_gssapi_struct)); + if(!session->gssapi){ + ssh_set_error_oom(session); + return SSH_ERROR; + } + ZERO_STRUCTP(session->gssapi); + session->gssapi->server_creds = GSS_C_NO_CREDENTIAL; + session->gssapi->client_creds = GSS_C_NO_CREDENTIAL; + session->gssapi->ctx = GSS_C_NO_CONTEXT; + session->gssapi->state = SSH_GSSAPI_STATE_NONE; + return SSH_OK; +} + +/** @internal + * @frees a gssapi context + */ +static void ssh_gssapi_free(ssh_session session){ + OM_uint32 min; + if (session->gssapi == NULL) + return; + if (session->gssapi->mech.elements) + SAFE_FREE(session->gssapi->mech.elements); + if (session->gssapi->user) + SAFE_FREE(session->gssapi->user); + if (session->gssapi->server_creds) + gss_release_cred(&min,&session->gssapi->server_creds); + SAFE_FREE(session->gssapi); +} + +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token){ +#ifdef WITH_SERVER + if(session->server) + return ssh_packet_userauth_gssapi_token_server(session, type, packet, user); +#endif + return ssh_packet_userauth_gssapi_token_client(session, type, packet, user); +} +#ifdef WITH_SERVER + +/** @internal + * @brief sends a SSH_MSG_USERAUTH_GSSAPI_RESPONSE packet + * @param[in] oid the OID that was selected for authentication + */ +static int ssh_gssapi_send_response(ssh_session session, ssh_string oid){ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) < 0 || + buffer_add_ssh_string(session->out_buffer,oid) < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + packet_send(session); + SSH_LOG(SSH_LOG_PACKET, + "Sent SSH_MSG_USERAUTH_GSSAPI_RESPONSE"); + return SSH_OK; +} + +#endif /* WITH_SERVER */ + +static void ssh_gssapi_log_error(int verb, const char *msg, int maj_stat){ + gss_buffer_desc buffer; + OM_uint32 dummy, message_context; + gss_display_status(&dummy,maj_stat,GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &buffer); + SSH_LOG(verb, "GSSAPI(%s): %s", msg, (const char *)buffer.value); +} + +#ifdef WITH_SERVER + +/** @internal + * @brief handles an user authentication using GSSAPI + */ +int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids){ + char service_name[]="host"; + gss_buffer_desc name_buf; + gss_name_t server_name; /* local server fqdn */ + OM_uint32 maj_stat, min_stat; + unsigned int i; + char *ptr; + gss_OID_set supported; /* oids supported by server */ + gss_OID_set both_supported; /* oids supported by both client and server */ + gss_OID_set selected; /* oid selected for authentication */ + int present=0; + int oid_count=0; + struct gss_OID_desc_struct oid; + int rc; + + if (ssh_callbacks_exists(session->server_callbacks, gssapi_select_oid_function)){ + ssh_string oid_s = session->server_callbacks->gssapi_select_oid_function(session, + user, n_oid, oids, + session->server_callbacks->userdata); + if (oid_s != NULL){ + if (ssh_gssapi_init(session) == SSH_ERROR) + return SSH_ERROR; + session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN; + rc = ssh_gssapi_send_response(session, oid_s); + ssh_string_free(oid_s); + return rc; + } else { + return ssh_auth_reply_default(session,0); + } + } + gss_create_empty_oid_set(&min_stat, &both_supported); + + maj_stat = gss_indicate_mechs(&min_stat, &supported); + for (i=0; i < supported->count; ++i){ + ptr = ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length); + SSH_LOG(SSH_LOG_DEBUG, "Supported mech %d: %s\n", i, ptr); + free(ptr); + } + + for (i=0 ; i< n_oid ; ++i){ + unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]); + size_t len = ssh_string_len(oids[i]); + if(len < 2 || oid_s[0] != SSH_OID_TAG || ((size_t)oid_s[1]) != len - 2){ + SSH_LOG(SSH_LOG_WARNING,"GSSAPI: received invalid OID"); + continue; + } + oid.elements = &oid_s[2]; + oid.length = len - 2; + gss_test_oid_set_member(&min_stat,&oid,supported,&present); + if(present){ + gss_add_oid_set_member(&min_stat,&oid,&both_supported); + oid_count++; + } + } + gss_release_oid_set(&min_stat, &supported); + if (oid_count == 0){ + SSH_LOG(SSH_LOG_PROTOCOL,"GSSAPI: no OID match"); + ssh_auth_reply_default(session, 0); + gss_release_oid_set(&min_stat, &both_supported); + return SSH_OK; + } + /* from now we have room for context */ + if (ssh_gssapi_init(session) == SSH_ERROR) + return SSH_ERROR; + + name_buf.value = service_name; + name_buf.length = strlen(name_buf.value) + 1; + maj_stat = gss_import_name(&min_stat, &name_buf, + (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name); + if (maj_stat != GSS_S_COMPLETE) { + SSH_LOG(0, "importing name %d, %d", maj_stat, min_stat); + ssh_gssapi_log_error(0, "importing name", maj_stat); + return -1; + } + + maj_stat = gss_acquire_cred(&min_stat, server_name, 0, + both_supported, GSS_C_ACCEPT, + &session->gssapi->server_creds, &selected, NULL); + gss_release_name(&min_stat, &server_name); + gss_release_oid_set(&min_stat, &both_supported); + + if (maj_stat != GSS_S_COMPLETE) { + SSH_LOG(0, "error acquiring credentials %d, %d", maj_stat, min_stat); + ssh_gssapi_log_error(0, "acquiring creds", maj_stat); + ssh_auth_reply_default(session,0); + return SSH_ERROR; + } + + SSH_LOG(0, "acquiring credentials %d, %d", maj_stat, min_stat); + + /* finding which OID from client we selected */ + for (i=0 ; i< n_oid ; ++i){ + unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]); + size_t len = ssh_string_len(oids[i]); + if(len < 2 || oid_s[0] != SSH_OID_TAG || ((size_t)oid_s[1]) != len - 2){ + SSH_LOG(SSH_LOG_WARNING,"GSSAPI: received invalid OID"); + continue; + } + oid.elements = &oid_s[2]; + oid.length = len - 2; + gss_test_oid_set_member(&min_stat,&oid,selected,&present); + if(present){ + SSH_LOG(SSH_LOG_PACKET, "Selected oid %d", i); + break; + } + } + session->gssapi->mech.length = oid.length; + session->gssapi->mech.elements = malloc(oid.length); + if (session->gssapi->mech.elements == NULL){ + ssh_set_error_oom(session); + return SSH_ERROR; + } + memcpy(session->gssapi->mech.elements, oid.elements, oid.length); + gss_release_oid_set(&min_stat, &selected); + session->gssapi->user = strdup(user); + session->gssapi->service = service_name; + session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN; + return ssh_gssapi_send_response(session, oids[i]); +} + +static char *ssh_gssapi_name_to_char(gss_name_t name){ + gss_buffer_desc buffer; + OM_uint32 maj_stat, min_stat; + char *ptr; + maj_stat = gss_display_name(&min_stat, name, &buffer, NULL); + ssh_gssapi_log_error(0, "converting name", maj_stat); + ptr=malloc(buffer.length + 1); + memcpy(ptr, buffer.value, buffer.length); + ptr[buffer.length] = '\0'; + gss_release_buffer(&min_stat, &buffer); + return ptr; + +} + +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ + ssh_string token; + char *hexa; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER; + gss_name_t client_name = GSS_C_NO_NAME; + OM_uint32 ret_flags=0; + gss_channel_bindings_t input_bindings=GSS_C_NO_CHANNEL_BINDINGS; + int rc; + + (void)user; + (void)type; + + SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_USERAUTH_GSSAPI_TOKEN"); + if (!session->gssapi || session->gssapi->state != SSH_GSSAPI_STATE_RCV_TOKEN){ + ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_USERAUTH_GSSAPI_TOKEN in invalid state"); + return SSH_PACKET_USED; + } + token = buffer_get_ssh_string(packet); + + if (token == NULL){ + ssh_set_error(session, SSH_REQUEST_DENIED, "ssh_packet_userauth_gssapi_token: invalid packet"); + return SSH_PACKET_USED; + } + + if (ssh_callbacks_exists(session->server_callbacks, gssapi_accept_sec_ctx_function)){ + ssh_string out_token=NULL; + rc = session->server_callbacks->gssapi_accept_sec_ctx_function(session, + token, &out_token, session->server_callbacks->userdata); + if (rc == SSH_ERROR){ + ssh_auth_reply_default(session, 0); + ssh_gssapi_free(session); + session->gssapi=NULL; + return SSH_PACKET_USED; + } + if (ssh_string_len(out_token) != 0){ + rc = buffer_add_u8(session->out_buffer, + SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_PACKET_USED; + } + rc = buffer_add_ssh_string(session->out_buffer, out_token); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_PACKET_USED; + } + packet_send(session); + ssh_string_free(out_token); + } else { + session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC; + } + return SSH_PACKET_USED; + } + hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token)); + SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa); + SAFE_FREE(hexa); + input_token.length = ssh_string_len(token); + input_token.value = ssh_string_data(token); + + maj_stat = gss_accept_sec_context(&min_stat, &session->gssapi->ctx, session->gssapi->server_creds, + &input_token, input_bindings, &client_name, NULL /*mech_oid*/, &output_token, &ret_flags, + NULL /*time*/, &session->gssapi->client_creds); + ssh_gssapi_log_error(0, "accepting token", maj_stat); + ssh_string_free(token); + if (client_name != GSS_C_NO_NAME){ + session->gssapi->client_name = client_name; + session->gssapi->canonic_user = ssh_gssapi_name_to_char(client_name); + } + if (GSS_ERROR(maj_stat)){ + ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "Gssapi error", maj_stat); + ssh_auth_reply_default(session,0); + ssh_gssapi_free(session); + session->gssapi=NULL; + return SSH_PACKET_USED; + } + + if (output_token.length != 0){ + hexa = ssh_get_hexa(output_token.value, output_token.length); + SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); + SAFE_FREE(hexa); + token = ssh_string_new(output_token.length); + ssh_string_fill(token, output_token.value, output_token.length); + buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + buffer_add_ssh_string(session->out_buffer,token); + packet_send(session); + ssh_string_free(token); + } + if(maj_stat == GSS_S_COMPLETE){ + session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC; + } + return SSH_PACKET_USED; +} + +#endif /* WITH_SERVER */ + +static ssh_buffer ssh_gssapi_build_mic(ssh_session session){ + ssh_buffer mic_buffer; + ssh_string str; + int rc; + + str = ssh_string_new(session->current_crypto->digest_len); + if (str == NULL) { + return NULL; + } + ssh_string_fill(str, session->current_crypto->session_id, + session->current_crypto->digest_len); + + mic_buffer = ssh_buffer_new(); + if (mic_buffer == NULL) { + ssh_string_free(str); + return NULL; + } + + rc = buffer_add_ssh_string(mic_buffer, str); + ssh_string_free(str); + if (rc < 0) { + ssh_buffer_free(mic_buffer); + return NULL; + } + + rc = buffer_add_u8(mic_buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + ssh_buffer_free(mic_buffer); + return NULL; + } + + str = ssh_string_from_char(session->gssapi->user); + if (str == NULL) { + ssh_buffer_free(mic_buffer); + return NULL; + } + + rc = buffer_add_ssh_string(mic_buffer, str); + ssh_string_free(str); + if (rc < 0) { + ssh_buffer_free(mic_buffer); + return NULL; + } + + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + ssh_buffer_free(mic_buffer); + return NULL; + } + rc = buffer_add_ssh_string(mic_buffer, str); + ssh_string_free(str); + if (rc < 0) { + ssh_buffer_free(mic_buffer); + return NULL; + } + + str = ssh_string_from_char("gssapi-with-mic"); + if (str == NULL) { + ssh_buffer_free(mic_buffer); + return NULL; + } + + rc = buffer_add_ssh_string(mic_buffer, str); + ssh_string_free(str); + if (rc < 0) { + ssh_buffer_free(mic_buffer); + return NULL; + } + + return mic_buffer; +} + +#ifdef WITH_SERVER + +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic) +{ + ssh_string mic_token; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER; + gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER; + ssh_buffer mic_buffer = NULL; + + (void)user; + (void)type; + + SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_USERAUTH_GSSAPI_MIC"); + mic_token = buffer_get_ssh_string(packet); + if (mic_token == NULL) { + ssh_set_error(session, SSH_FATAL, "Missing MIC in packet"); + goto error; + } + if (session->gssapi == NULL + || session->gssapi->state != SSH_GSSAPI_STATE_RCV_MIC) { + ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_USERAUTH_GSSAPI_MIC in invalid state"); + goto error; + } + + mic_buffer = ssh_gssapi_build_mic(session); + if (mic_buffer == NULL) { + ssh_set_error_oom(session); + goto error; + } + if (ssh_callbacks_exists(session->server_callbacks, gssapi_verify_mic_function)){ + int rc = session->server_callbacks->gssapi_verify_mic_function(session, mic_token, + ssh_buffer_get_begin(mic_buffer), ssh_buffer_get_len(mic_buffer), + session->server_callbacks->userdata); + if (rc != SSH_OK) { + goto error; + } + } else { + mic_buf.length = ssh_buffer_get_len(mic_buffer); + mic_buf.value = ssh_buffer_get_begin(mic_buffer); + mic_token_buf.length = ssh_string_len(mic_token); + mic_token_buf.value = ssh_string_data(mic_token); + + maj_stat = gss_verify_mic(&min_stat, session->gssapi->ctx, &mic_buf, &mic_token_buf, NULL); + ssh_gssapi_log_error(0, "verifying MIC", maj_stat); + ssh_gssapi_log_error(0, "verifying MIC (min stat)", min_stat); + if (maj_stat == GSS_S_DEFECTIVE_TOKEN || GSS_ERROR(maj_stat)) { + goto error; + } + } + + if (ssh_callbacks_exists(session->server_callbacks, auth_gssapi_mic_function)){ + switch(session->server_callbacks->auth_gssapi_mic_function(session, + session->gssapi->user, session->gssapi->canonic_user, + session->server_callbacks->userdata)){ + case SSH_AUTH_SUCCESS: + ssh_auth_reply_success(session, 0); + break; + case SSH_AUTH_PARTIAL: + ssh_auth_reply_success(session, 1); + break; + default: + ssh_auth_reply_default(session, 0); + break; + } + } + + goto end; + +error: + ssh_auth_reply_default(session,0); + +end: + ssh_gssapi_free(session); + if (mic_buffer != NULL) { + ssh_buffer_free(mic_buffer); + } + if (mic_token != NULL) { + ssh_string_free(mic_token); + } + + return SSH_PACKET_USED; +} + +/** @brief returns the client credentials of the connected client. + * If the client has given a forwardable token, the SSH server will + * retrieve it. + * @returns gssapi credentials handle. + * @returns NULL if no forwardable token is available. + */ +ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session){ + if (!session || !session->gssapi || session->gssapi->client_creds == GSS_C_NO_CREDENTIAL) + return NULL; + return (ssh_gssapi_creds)session->gssapi->client_creds; +} + +/** + * @brief Set the forwadable ticket to be given to the server for authentication. + * + * @param[in] creds gssapi credentials handle. + */ +void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds) +{ + if (session == NULL) { + return; + } + if (session->gssapi == NULL) { + ssh_gssapi_init(session); + if (session->gssapi == NULL) { + return; + } + } + + session->gssapi->client.client_deleg_creds = (gss_cred_id_t)creds; +} + +#endif /* SERVER */ + +static int ssh_gssapi_send_auth_mic(ssh_session session, ssh_string *oid_set, int n_oid){ + ssh_string str; + int rc; + int i; + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); + if (rc < 0) { + goto fail; + } + /* username */ + str = ssh_string_from_char(session->opts.username); + if (str == NULL) { + goto fail; + } + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + /* service */ + str = ssh_string_from_char("ssh-connection"); + if (str == NULL) { + goto fail; + } + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + /* method */ + str = ssh_string_from_char("gssapi-with-mic"); + if (str == NULL) { + goto fail; + } + rc = buffer_add_ssh_string(session->out_buffer, str); + ssh_string_free(str); + if (rc < 0) { + goto fail; + } + + rc = buffer_add_u32(session->out_buffer, htonl(n_oid)); + if (rc < 0) { + goto fail; + } + + for (i=0; iout_buffer, oid_set[i]); + if (rc < 0) { + goto fail; + } + } + + session->auth_state = SSH_AUTH_STATE_GSSAPI_REQUEST_SENT; + return packet_send(session); +fail: + buffer_reinit(session->out_buffer); + return SSH_ERROR; +} + +/** @brief returns the OIDs of the mechs that work with both + * hostname and username + */ +static int ssh_gssapi_match(ssh_session session, char *hostname, char *username, gss_OID_set *valid_oids, int deleg){ + gss_buffer_desc host_namebuf, user_namebuf; + gss_name_t host_name, user_name; + OM_uint32 maj_stat, min_stat; + gss_OID_set supported; + gss_OID oid; + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + gss_cred_id_t client_creds = GSS_C_NO_CREDENTIAL; + unsigned int i; + char *ptr; + char hostname_buf[256]; + + + gss_create_empty_oid_set(&min_stat, valid_oids); + maj_stat = gss_indicate_mechs(&min_stat, &supported); + for (i=0; i < supported->count; ++i){ + ptr=ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length); + SSH_LOG(SSH_LOG_DEBUG, "GSSAPI oid supported %d : %s\n",i, ptr); + SAFE_FREE(ptr); + } + + user_namebuf.value = username; + user_namebuf.length = strlen(username) + 1; + maj_stat = gss_import_name(&min_stat, &user_namebuf, + (gss_OID) GSS_C_NT_USER_NAME, &user_name); + if (maj_stat != GSS_S_COMPLETE) { + SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat); + ssh_gssapi_log_error(SSH_LOG_DEBUG, "importing name", maj_stat); + return -1; + } + + snprintf(hostname_buf, sizeof(hostname_buf),"host@%s", hostname); + host_namebuf.value = hostname_buf; + host_namebuf.length = strlen(hostname_buf) + 1; + maj_stat = gss_import_name(&min_stat, &host_namebuf, + (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &host_name); + if (maj_stat != GSS_S_COMPLETE) { + SSH_LOG(0, "importing name %d, %d", maj_stat, min_stat); + ssh_gssapi_log_error(0, "importing name", maj_stat); + return -1; + } + + ssh_gssapi_init(session); + session->gssapi->client_name = user_name; + session->gssapi->client.server_name = host_name; + session->gssapi->user = strdup(username); + for (i=0; icount; ++i){ + oid = &supported->elements[i]; + maj_stat = gss_init_sec_context(&min_stat, + session->gssapi->client.client_deleg_creds, &ctx, host_name, oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | (deleg ? GSS_C_DELEG_FLAG : 0), + 0, NULL, &input_token, NULL, &output_token, NULL, NULL); + if (!GSS_ERROR(maj_stat)){ + gss_OID_set tmp; + if (session->gssapi->client.client_deleg_creds != GSS_C_NO_CREDENTIAL){ + /* we know the oid is ok since init_sec_context worked */ + gss_add_oid_set_member(&min_stat, oid, valid_oids); + SSH_LOG(SSH_LOG_PROTOCOL, "Matched oid %u for server (with forwarding)", i); + } else { + gss_create_empty_oid_set(&min_stat, &tmp); + gss_add_oid_set_member(&min_stat, oid, &tmp); + maj_stat = gss_acquire_cred(&min_stat, user_name, 0, + tmp, GSS_C_INITIATE, + &client_creds, NULL, NULL); + gss_release_oid_set(&min_stat, &tmp); + if (!GSS_ERROR(maj_stat)){ + gss_release_cred(&min_stat, &client_creds); + gss_add_oid_set_member(&min_stat,oid,valid_oids); + SSH_LOG(SSH_LOG_PROTOCOL, "Matched oid %u for server", i); + } + } + } + gss_delete_sec_context(&min_stat,&ctx, &output_token); + ctx = GSS_C_NO_CONTEXT; + } + + return SSH_OK; +} + +/** + * @brief launches a gssapi-with-mic auth request + * @returns SSH_AUTH_ERROR: A serious error happened\n + * SSH_AUTH_DENIED: Authentication failed : use another method\n + * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again + * later. + */ +int ssh_gssapi_auth_mic(ssh_session session){ + int i; + gss_OID_set selected; /* oid selected for authentication */ + ssh_string *oids; + int rc; + int n_oids = 0; + + if (ssh_gssapi_init(session) == SSH_ERROR) + return SSH_AUTH_ERROR; + + + SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi to host %s with user %s", + session->opts.host, session->opts.username); + rc = ssh_gssapi_match(session,session->opts.host, session->opts.username, &selected, 0); + if (rc == SSH_ERROR) + return SSH_AUTH_DENIED; + + n_oids = selected->count; + SSH_LOG(SSH_LOG_PROTOCOL, "Sending %d oids", n_oids); + + oids = calloc(n_oids, sizeof(ssh_string)); + + for (i=0; ielements[i].length + 2); + ((unsigned char *)oids[i]->data)[0] = SSH_OID_TAG; + ((unsigned char *)oids[i]->data)[1] = selected->elements[i].length; + memcpy((unsigned char *)oids[i]->data + 2, selected->elements[i].elements, + selected->elements[i].length); + } + + rc = ssh_gssapi_send_auth_mic(session, oids, n_oids); + for (i = 0; i < n_oids; i++) { + ssh_string_free(oids[i]); + } + free(oids); + if (rc != SSH_ERROR) { + return SSH_AUTH_AGAIN; + } + + return SSH_AUTH_ERROR; +} + +static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s){ + gss_OID ret = malloc(sizeof (gss_OID_desc)); + unsigned char *data = ssh_string_data(oid_s); + size_t len = ssh_string_len(oid_s); + if(len > 256 || len <= 2){ + SAFE_FREE(ret); + return NULL; + } + if(data[0] != SSH_OID_TAG || data[1] != len - 2){ + SAFE_FREE(ret); + return NULL; + } + ret->elements = malloc(len - 2); + memcpy(ret->elements, &data[2], len-2); + ret->length = len-2; + return ret; +} + +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){ + ssh_string oid_s; + gss_OID oid; + gss_uint32 maj_stat, min_stat; + int deleg = 0; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + gss_OID_set tmp; + char *hexa; + ssh_string token; + gss_cred_id_t creds = GSS_C_NO_CREDENTIAL; + (void)type; + (void)user; + + SSH_LOG(SSH_LOG_PACKET, "Received SSH_USERAUTH_GSSAPI_RESPONSE"); + if (session->auth_state != SSH_AUTH_STATE_GSSAPI_REQUEST_SENT){ + ssh_set_error(session, SSH_FATAL, "Invalid state in ssh_packet_userauth_gssapi_response"); + return SSH_PACKET_USED; + } + oid_s = buffer_get_ssh_string(packet); + if (!oid_s){ + ssh_set_error(session, SSH_FATAL, "Missing OID"); + return SSH_PACKET_USED; + } + oid = ssh_gssapi_oid_from_string(oid_s); + ssh_string_free(oid_s); + if (!oid) { + ssh_set_error(session, SSH_FATAL, "Invalid OID"); + return SSH_PACKET_USED; + } + if (session->gssapi->client.client_deleg_creds != GSS_C_NO_CREDENTIAL) + creds = session->gssapi->client.client_deleg_creds; + if (creds == GSS_C_NO_CREDENTIAL){ + gss_create_empty_oid_set(&min_stat, &tmp); + gss_add_oid_set_member(&min_stat, oid, &tmp); + maj_stat = gss_acquire_cred(&min_stat, session->gssapi->client_name, 0, + tmp, GSS_C_INITIATE, + &session->gssapi->client_creds, NULL, NULL); + gss_release_oid_set(&min_stat, &tmp); + if (GSS_ERROR(maj_stat)){ + ssh_gssapi_log_error(SSH_LOG_WARNING,"Error acquiring credentials",maj_stat); + return SSH_PACKET_USED; + } + } + /* prepare the first TOKEN response */ + maj_stat = gss_init_sec_context(&min_stat, + creds, &session->gssapi->ctx, session->gssapi->client.server_name, oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | (deleg ? GSS_C_DELEG_FLAG : 0), + 0, NULL, &input_token, NULL, &output_token, NULL, NULL); + if(GSS_ERROR(maj_stat)){ + ssh_gssapi_log_error(SSH_LOG_WARNING, "Initializing gssapi context", maj_stat); + return SSH_PACKET_USED; + } + if (output_token.length != 0){ + hexa = ssh_get_hexa(output_token.value, output_token.length); + SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); + SAFE_FREE(hexa); + token = ssh_string_new(output_token.length); + ssh_string_fill(token, output_token.value, output_token.length); + buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + buffer_add_ssh_string(session->out_buffer,token); + packet_send(session); + ssh_string_free(token); + session->auth_state = SSH_AUTH_STATE_GSSAPI_TOKEN; + } + session->gssapi->client.oid = oid; + return SSH_PACKET_USED; +} + +static int ssh_gssapi_send_mic(ssh_session session){ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER; + gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER; + ssh_buffer mic_buffer; + int rc; + + SSH_LOG(SSH_LOG_PACKET,"Sending SSH_MSG_USERAUTH_GSSAPI_MIC"); + + mic_buffer = ssh_gssapi_build_mic(session); + if (mic_buffer == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + mic_buf.length = ssh_buffer_get_len(mic_buffer); + mic_buf.value = ssh_buffer_get_begin(mic_buffer); + + maj_stat = gss_get_mic(&min_stat,session->gssapi->ctx, GSS_C_QOP_DEFAULT, &mic_buf, &mic_token_buf); + if (GSS_ERROR(maj_stat)){ + ssh_buffer_free(mic_buffer); + ssh_gssapi_log_error(0, "generating MIC", maj_stat); + return SSH_ERROR; + } + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_MIC); + if (rc < 0) { + ssh_buffer_free(mic_buffer); + ssh_set_error_oom(session); + return SSH_ERROR; + } + + rc = buffer_add_u32(session->out_buffer, htonl(mic_token_buf.length)); + if (rc < 0) { + ssh_buffer_free(mic_buffer); + ssh_set_error_oom(session); + return SSH_ERROR; + } + + rc = buffer_add_data(session->out_buffer, mic_token_buf.value, mic_token_buf.length); + ssh_buffer_free(mic_buffer); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + return packet_send(session); +} + +SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){ + ssh_string token; + char *hexa; + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER; + gss_cred_id_t creds = GSS_C_NO_CREDENTIAL; + int deleg = 0; + (void)user; + (void)type; + + SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_USERAUTH_GSSAPI_TOKEN"); + if (!session->gssapi || session->auth_state != SSH_AUTH_STATE_GSSAPI_TOKEN){ + ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_USERAUTH_GSSAPI_TOKEN in invalid state"); + return SSH_PACKET_USED; + } + token = buffer_get_ssh_string(packet); + + if (token == NULL){ + ssh_set_error(session, SSH_REQUEST_DENIED, "ssh_packet_userauth_gssapi_token: invalid packet"); + return SSH_PACKET_USED; + } + hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token)); + SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa); + SAFE_FREE(hexa); + input_token.length = ssh_string_len(token); + input_token.value = ssh_string_data(token); + if (session->gssapi->client.client_deleg_creds != GSS_C_NO_CREDENTIAL) + creds = session->gssapi->client.client_deleg_creds; + else + creds = session->gssapi->client_creds; + maj_stat = gss_init_sec_context(&min_stat, + creds, &session->gssapi->ctx, session->gssapi->client.server_name, session->gssapi->client.oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | (deleg ? GSS_C_DELEG_FLAG : 0), + 0, NULL, &input_token, NULL, &output_token, NULL, NULL); + + ssh_gssapi_log_error(0, "accepting token", maj_stat); + ssh_string_free(token); + if (GSS_ERROR(maj_stat)){ + ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "Gssapi error", maj_stat); + ssh_gssapi_free(session); + session->gssapi=NULL; + return SSH_PACKET_USED; + } + + if (output_token.length != 0){ + hexa = ssh_get_hexa(output_token.value, output_token.length); + SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); + SAFE_FREE(hexa); + token = ssh_string_new(output_token.length); + ssh_string_fill(token, output_token.value, output_token.length); + buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_TOKEN); + buffer_add_ssh_string(session->out_buffer,token); + packet_send(session); + ssh_string_free(token); + } + if(maj_stat == GSS_S_COMPLETE){ + session->auth_state = SSH_AUTH_STATE_NONE; + ssh_gssapi_send_mic(session); + } + return SSH_PACKET_USED; +} diff --git a/libssh/src/kex.c b/libssh/src/kex.c index c678731e..4de5a658 100644 --- a/libssh/src/kex.c +++ b/libssh/src/kex.c @@ -272,12 +272,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit){ char *strings[KEX_METHODS_SIZE]; int i; - enter_function(); (void)type; (void)user; memset(strings, 0, sizeof(strings)); if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED){ - ssh_log(session,SSH_LOG_WARNING, "Other side initiating key re-exchange"); + SSH_LOG(SSH_LOG_WARNING, "Other side initiating key re-exchange"); } else if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){ ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); goto error; @@ -335,7 +334,6 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit){ } } - leave_function(); session->session_state=SSH_SESSION_STATE_KEXINIT_RECEIVED; session->dh_handshake_state=DH_STATE_INIT; session->ssh_connection_callback(session); @@ -347,11 +345,11 @@ error: } session->session_state = SSH_SESSION_STATE_ERROR; - leave_function(); + return SSH_PACKET_USED; } -void ssh_list_kex(ssh_session session, struct ssh_kex_struct *kex) { +void ssh_list_kex(struct ssh_kex_struct *kex) { int i = 0; #ifdef DEBUG_CRYPTO @@ -362,7 +360,7 @@ void ssh_list_kex(ssh_session session, struct ssh_kex_struct *kex) { if (kex->methods[i] == NULL) { continue; } - ssh_log(session, SSH_LOG_FUNCTIONS, "%s: %s", + SSH_LOG(SSH_LOG_FUNCTIONS, "%s: %s", ssh_kex_descriptions[i], kex->methods[i]); } } @@ -395,17 +393,14 @@ int set_client_kex(ssh_session session){ int ssh_kex_select_methods (ssh_session session){ struct ssh_kex_struct *server = &session->next_crypto->server_kex; struct ssh_kex_struct *client = &session->next_crypto->client_kex; - int rc = SSH_ERROR; int i; - enter_function(); - for (i = 0; i < KEX_METHODS_SIZE; i++) { session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]); if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){ ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]", ssh_kex_descriptions[i],server->methods[i],client->methods[i]); - goto error; + return SSH_ERROR; } else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) { /* we can safely do that for languages */ session->next_crypto->kex_methods[i] = strdup(""); @@ -418,10 +413,8 @@ int ssh_kex_select_methods (ssh_session session){ } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){ session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256; } - rc = SSH_OK; -error: - leave_function(); - return rc; + + return SSH_OK; } @@ -432,8 +425,6 @@ int ssh_send_kex(ssh_session session, int server_kex) { ssh_string str = NULL; int i; - enter_function(); - if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXINIT) < 0) { goto error; } @@ -445,7 +436,7 @@ int ssh_send_kex(ssh_session session, int server_kex) { goto error; } - ssh_list_kex(session, kex); + ssh_list_kex(kex); for (i = 0; i < KEX_METHODS_SIZE; i++) { str = ssh_string_from_char(kex->methods[i]); @@ -471,18 +462,15 @@ int ssh_send_kex(ssh_session session, int server_kex) { } if (packet_send(session) == SSH_ERROR) { - leave_function(); return -1; } - leave_function(); return 0; error: buffer_reinit(session->out_buffer); buffer_reinit(session->out_hashbuf); ssh_string_free(str); - leave_function(); return -1; } diff --git a/libssh/src/kex1.c b/libssh/src/kex1.c index b396a719..22899429 100644 --- a/libssh/src/kex1.c +++ b/libssh/src/kex1.c @@ -247,7 +247,7 @@ static ssh_string encrypt_session_key(ssh_session session, ssh_public_key srvkey } ssh_string_fill(data1, buffer, 32); if (ABS(hlen - slen) < 128){ - ssh_log(session, SSH_LOG_FUNCTIONS, + SSH_LOG(SSH_LOG_FUNCTIONS, "Difference between server modulus and host modulus is only %d. " "It's illegal and may not work", ABS(hlen - slen)); @@ -317,10 +317,10 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1){ int ko; uint32_t support_3DES = 0; uint32_t support_DES = 0; - enter_function(); + (void)type; (void)user; - ssh_log(session, SSH_LOG_PROTOCOL, "Got a SSH_SMSG_PUBLIC_KEY"); + SSH_LOG(SSH_LOG_PROTOCOL, "Got a SSH_SMSG_PUBLIC_KEY"); if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){ ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); goto error; @@ -354,7 +354,7 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1){ if ((ko != sizeof(uint32_t)) || !host_mod || !host_exp || !server_mod || !server_exp) { - ssh_log(session, SSH_LOG_RARE, "Invalid SSH_SMSG_PUBLIC_KEY packet"); + SSH_LOG(SSH_LOG_RARE, "Invalid SSH_SMSG_PUBLIC_KEY packet"); ssh_set_error(session, SSH_FATAL, "Invalid SSH_SMSG_PUBLIC_KEY packet"); goto error; } @@ -364,7 +364,7 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1){ protocol_flags = ntohl(protocol_flags); supported_ciphers_mask = ntohl(supported_ciphers_mask); supported_authentications_mask = ntohl(supported_authentications_mask); - ssh_log(session, SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PROTOCOL, "Server bits: %d; Host bits: %d; Protocol flags: %.8lx; " "Cipher mask: %.8lx; Auth mask: %.8lx", server_bits, @@ -409,7 +409,7 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1){ ssh_set_error(session, SSH_FATAL, "Remote server doesn't accept 3DES"); goto error; } - ssh_log(session, SSH_LOG_PROTOCOL, "Sending SSH_CMSG_SESSION_KEY"); + SSH_LOG(SSH_LOG_PROTOCOL, "Sending SSH_CMSG_SESSION_KEY"); if (buffer_add_u8(session->out_buffer, SSH_CMSG_SESSION_KEY) < 0) { goto error; @@ -427,7 +427,7 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1){ } bits = ssh_string_len(enc_session) * 8 - 7; - ssh_log(session, SSH_LOG_PROTOCOL, "%d bits, %" PRIdS " bytes encrypted session", + SSH_LOG(SSH_LOG_PROTOCOL, "%d bits, %" PRIdS " bytes encrypted session", bits, ssh_string_len(enc_session)); bits = htons(bits); /* the encrypted mpint */ @@ -470,30 +470,28 @@ end: publickey_free(srv); publickey_free(host); - leave_function(); return SSH_PACKET_USED; } int ssh_get_kex1(ssh_session session) { - int ret=SSH_ERROR; - enter_function(); - ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_PUBLIC_KEY"); + SSH_LOG(SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_PUBLIC_KEY"); + /* Here the callback is called */ while(session->session_state==SSH_SESSION_STATE_INITIAL_KEX){ ssh_handle_packets(session, SSH_TIMEOUT_USER); } - if(session->session_state==SSH_SESSION_STATE_ERROR) - goto error; - ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_SUCCESS"); + if (session->session_state==SSH_SESSION_STATE_ERROR) { + return SSH_ERROR; + } + SSH_LOG(SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_SUCCESS"); /* Waiting for SSH_SMSG_SUCCESS */ while(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){ ssh_handle_packets(session, SSH_TIMEOUT_USER); } - if(session->session_state==SSH_SESSION_STATE_ERROR) - goto error; - ssh_log(session, SSH_LOG_PROTOCOL, "received SSH_SMSG_SUCCESS\n"); - ret=SSH_OK; -error: - leave_function(); - return ret; + if(session->session_state==SSH_SESSION_STATE_ERROR) { + return SSH_ERROR; + } + SSH_LOG(SSH_LOG_PROTOCOL, "received SSH_SMSG_SUCCESS\n"); + + return SSH_OK; } diff --git a/libssh/src/known_hosts.c b/libssh/src/known_hosts.c index a9ae38c1..6e9eb915 100644 --- a/libssh/src/known_hosts.c +++ b/libssh/src/known_hosts.c @@ -106,18 +106,15 @@ static void tokens_free(char **tokens) { * free that value. NULL if no match was found or the file * was not found. */ -static char **ssh_get_knownhost_line(ssh_session session, FILE **file, - const char *filename, const char **found_type) { +static char **ssh_get_knownhost_line(FILE **file, const char *filename, + const char **found_type) { char buffer[4096] = {0}; char *ptr; char **tokens; - enter_function(); - if(*file == NULL){ *file = fopen(filename,"r"); if (*file == NULL) { - leave_function(); return NULL; } } @@ -141,7 +138,7 @@ static char **ssh_get_knownhost_line(ssh_session session, FILE **file, if (tokens == NULL) { fclose(*file); *file = NULL; - leave_function(); + return NULL; } @@ -168,7 +165,7 @@ static char **ssh_get_knownhost_line(ssh_session session, FILE **file, continue; } } - leave_function(); + return tokens; } @@ -176,7 +173,6 @@ static char **ssh_get_knownhost_line(ssh_session session, FILE **file, *file = NULL; /* we did not find anything, end of file*/ - leave_function(); return NULL; } @@ -293,8 +289,8 @@ static int check_public_key(ssh_session session, char **tokens) { * * @returns 1 if it matches, 0 otherwise. */ -static int match_hashed_host(ssh_session session, const char *host, - const char *sourcehash) { +static int match_hashed_host(const char *host, const char *sourcehash) +{ /* Openssh hash structure : * |1|base64 encoded salt|base64 encoded hash * hash is produced that way : @@ -309,16 +305,12 @@ static int match_hashed_host(ssh_session session, const char *host, int match; unsigned int size; - enter_function(); - if (strncmp(sourcehash, "|1|", 3) != 0) { - leave_function(); return 0; } source = strdup(sourcehash + 3); if (source == NULL) { - leave_function(); return 0; } @@ -326,7 +318,7 @@ static int match_hashed_host(ssh_session session, const char *host, if (b64hash == NULL) { /* Invalid hash */ SAFE_FREE(source); - leave_function(); + return 0; } @@ -336,7 +328,7 @@ static int match_hashed_host(ssh_session session, const char *host, salt = base64_to_bin(source); if (salt == NULL) { SAFE_FREE(source); - leave_function(); + return 0; } @@ -344,7 +336,7 @@ static int match_hashed_host(ssh_session session, const char *host, SAFE_FREE(source); if (hash == NULL) { ssh_buffer_free(salt); - leave_function(); + return 0; } @@ -352,7 +344,7 @@ static int match_hashed_host(ssh_session session, const char *host, if (mac == NULL) { ssh_buffer_free(salt); ssh_buffer_free(hash); - leave_function(); + return 0; } size = sizeof(buffer); @@ -369,10 +361,9 @@ static int match_hashed_host(ssh_session session, const char *host, ssh_buffer_free(salt); ssh_buffer_free(hash); - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Matching a hashed host: %s match=%d", host, match); - leave_function(); return match; } @@ -421,13 +412,11 @@ int ssh_is_server_known(ssh_session session) { int match; int ret = SSH_SERVER_NOT_KNOWN; - enter_function(); - if (session->opts.knownhosts == NULL) { if (ssh_options_apply(session) < 0) { ssh_set_error(session, SSH_REQUEST_DENIED, "Can't find a known_hosts file"); - leave_function(); + return SSH_SERVER_FILE_NOT_FOUND; } } @@ -435,14 +424,14 @@ int ssh_is_server_known(ssh_session session) { if (session->opts.host == NULL) { ssh_set_error(session, SSH_FATAL, "Can't verify host in known hosts if the hostname isn't known"); - leave_function(); + return SSH_SERVER_ERROR; } if (session->current_crypto == NULL){ ssh_set_error(session, SSH_FATAL, "ssh_is_host_known called without cryptographic context"); - leave_function(); + return SSH_SERVER_ERROR; } host = ssh_lowercase(session->opts.host); @@ -451,13 +440,12 @@ int ssh_is_server_known(ssh_session session) { ssh_set_error_oom(session); SAFE_FREE(host); SAFE_FREE(hostport); - leave_function(); + return SSH_SERVER_ERROR; } do { - tokens = ssh_get_knownhost_line(session, - &file, + tokens = ssh_get_knownhost_line(&file, session->opts.knownhosts, &type); @@ -465,7 +453,7 @@ int ssh_is_server_known(ssh_session session) { if (tokens == NULL) { break; } - match = match_hashed_host(session, host, tokens[0]); + match = match_hashed_host(host, tokens[0]); if (match == 0){ match = match_hostname(hostport, tokens[0], strlen(tokens[0])); } @@ -473,13 +461,12 @@ int ssh_is_server_known(ssh_session session) { match = match_hostname(host, tokens[0], strlen(tokens[0])); } if (match == 0) { - match = match_hashed_host(session, hostport, tokens[0]); + match = match_hashed_host(hostport, tokens[0]); } if (match) { /* We got a match. Now check the key type */ if (strcmp(session->current_crypto->server_pubkey_type, type) != 0) { - ssh_log(session, - SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "ssh_is_server_known: server type [%s] doesn't match the " "type [%s] in known_hosts file", session->current_crypto->server_pubkey_type, @@ -523,7 +510,6 @@ int ssh_is_server_known(ssh_session session) { } /* Return the current state at end of file */ - leave_function(); return ret; } diff --git a/libssh/src/legacy.c b/libssh/src/legacy.c index 6ad4fdc2..6e7bfffc 100644 --- a/libssh/src/legacy.c +++ b/libssh/src/legacy.c @@ -108,23 +108,21 @@ int ssh_userauth_privatekey_file(ssh_session session, int rc = SSH_AUTH_ERROR; size_t klen = strlen(filename) + 4 + 1; - enter_function(); - pubkeyfile = malloc(klen); if (pubkeyfile == NULL) { ssh_set_error_oom(session); - leave_function(); + return SSH_AUTH_ERROR; } snprintf(pubkeyfile, klen, "%s.pub", filename); pubkey = publickey_from_file(session, pubkeyfile, &type); if (pubkey == NULL) { - ssh_log(session, SSH_LOG_RARE, "Public key file %s not found. Trying to generate it.", pubkeyfile); + SSH_LOG(SSH_LOG_RARE, "Public key file %s not found. Trying to generate it.", pubkeyfile); /* auto-detect the key type with type=0 */ privkey = privatekey_from_file(session, filename, 0, passphrase); } else { - ssh_log(session, SSH_LOG_RARE, "Public key file %s loaded.", pubkeyfile); + SSH_LOG(SSH_LOG_RARE, "Public key file %s loaded.", pubkeyfile); privkey = privatekey_from_file(session, filename, type, passphrase); } if (privkey == NULL) { @@ -138,7 +136,6 @@ error: SAFE_FREE(pubkeyfile); ssh_string_free(pubkey); - leave_function(); return rc; } @@ -610,8 +607,8 @@ int ssh_publickey_to_file(ssh_session session, SAFE_FREE(pubkey_64); SAFE_FREE(user); - ssh_log(session, SSH_LOG_RARE, "Trying to write public key file: %s", file); - ssh_log(session, SSH_LOG_PACKET, "public key file content: %s", buffer); + SSH_LOG(SSH_LOG_RARE, "Trying to write public key file: %s", file); + SSH_LOG(SSH_LOG_PACKET, "public key file content: %s", buffer); fp = fopen(file, "w+"); if (fp == NULL) { @@ -652,9 +649,9 @@ int ssh_try_publickey_from_file(ssh_session session, } } - ssh_log(session, SSH_LOG_PACKET, "Trying to open privatekey %s", keyfile); + SSH_LOG(SSH_LOG_PACKET, "Trying to open privatekey %s", keyfile); if (!ssh_file_readaccess_ok(keyfile)) { - ssh_log(session, SSH_LOG_PACKET, "Failed to open privatekey %s", keyfile); + SSH_LOG(SSH_LOG_PACKET, "Failed to open privatekey %s", keyfile); return -1; } @@ -665,16 +662,16 @@ int ssh_try_publickey_from_file(ssh_session session, } snprintf(pubkey_file, len, "%s.pub", keyfile); - ssh_log(session, SSH_LOG_PACKET, "Trying to open publickey %s", + SSH_LOG(SSH_LOG_PACKET, "Trying to open publickey %s", pubkey_file); if (!ssh_file_readaccess_ok(pubkey_file)) { - ssh_log(session, SSH_LOG_PACKET, "Failed to open publickey %s", + SSH_LOG(SSH_LOG_PACKET, "Failed to open publickey %s", pubkey_file); SAFE_FREE(pubkey_file); return 1; } - ssh_log(session, SSH_LOG_PACKET, "Success opening public and private key"); + SSH_LOG(SSH_LOG_PACKET, "Success opening public and private key"); /* * We are sure both the private and public key file is readable. We return @@ -682,7 +679,7 @@ int ssh_try_publickey_from_file(ssh_session session, */ pubkey_string = publickey_from_file(session, pubkey_file, &pubkey_type); if (pubkey_string == NULL) { - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Wasn't able to open public key file %s: %s", pubkey_file, ssh_get_error(session)); diff --git a/libssh/src/log.c b/libssh/src/log.c index 687afabc..fba5fdc4 100644 --- a/libssh/src/log.c +++ b/libssh/src/log.c @@ -35,6 +35,10 @@ #include "libssh/misc.h" #include "libssh/session.h" +LIBSSH_THREAD int ssh_log_level; +LIBSSH_THREAD ssh_logging_callback ssh_log_cb; +LIBSSH_THREAD void *ssh_log_userdata; + /** * @defgroup libssh_log The SSH logging functions. * @ingroup libssh @@ -70,46 +74,60 @@ static int current_timestring(int hires, char *buf, size_t len) return 0; } -void ssh_log_function(int verbosity, - const char *function, - const char *buffer) +static void ssh_log_stderr(int verbosity, + const char *function, + const char *buffer) { char date[64] = {0}; int rc; rc = current_timestring(1, date, sizeof(date)); if (rc == 0) { - fprintf(stderr, "[%s, %d] %s", date, verbosity, function); + fprintf(stderr, "[%s, %d] %s:", date, verbosity, function); } else { fprintf(stderr, "[%d] %s", verbosity, function); } + fprintf(stderr, " %s\n", buffer); } -/** @internal - * @brief do the actual work of logging an event - */ - -static void do_ssh_log(struct ssh_common_struct *common, - int verbosity, - const char *function, - const char *buffer) { - if (common->callbacks && common->callbacks->log_function) { +void ssh_log_function(int verbosity, + const char *function, + const char *buffer) +{ + ssh_logging_callback log_fn = ssh_get_log_callback(); + if (log_fn) { char buf[1024]; snprintf(buf, sizeof(buf), "%s: %s", function, buffer); - common->callbacks->log_function((ssh_session)common, - verbosity, - buf, - common->callbacks->userdata); + log_fn(verbosity, + function, + buf, + ssh_get_log_userdata()); return; } - ssh_log_function(verbosity, function, buffer); + ssh_log_stderr(verbosity, function, buffer); } -/* legacy function */ +void _ssh_log(int verbosity, + const char *function, + const char *format, ...) +{ + char buffer[1024]; + va_list va; + + if (verbosity <= ssh_get_log_level()) { + va_start(va, format); + vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + ssh_log_function(verbosity, function, buffer); + } +} + +/* LEGACY */ + void ssh_log(ssh_session session, int verbosity, const char *format, ...) @@ -121,7 +139,7 @@ void ssh_log(ssh_session session, va_start(va, format); vsnprintf(buffer, sizeof(buffer), format, va); va_end(va); - do_ssh_log(&session->common, verbosity, "", buffer); + ssh_log_function(verbosity, "", buffer); } } @@ -143,10 +161,77 @@ void ssh_log_common(struct ssh_common_struct *common, va_start(va, format); vsnprintf(buffer, sizeof(buffer), format, va); va_end(va); - do_ssh_log(common, verbosity, function, buffer); + ssh_log_function(verbosity, function, buffer); } } + +/* PUBLIC */ + +/** + * @brief Set the log level of the library. + * + * @param[in] level The level to set. + * + * @return SSH_OK on success, SSH_ERROR on error. + */ +int ssh_set_log_level(int level) { + if (level < 0) { + return SSH_ERROR; + } + + ssh_log_level = level; + + return SSH_OK; +} + +/** + * @brief Get the log level of the library. + * + * @return The value of the log level. + */ +int ssh_get_log_level(void) { + return ssh_log_level; +} + +int ssh_set_log_callback(ssh_logging_callback cb) { + if (cb == NULL) { + return SSH_ERROR; + } + + ssh_log_cb = cb; + + return SSH_OK; +} + +ssh_logging_callback ssh_get_log_callback(void) { + return ssh_log_cb; +} + +/** + * @brief Get the userdata of the logging function. + * + * @return The userdata if set or NULL. + */ +void *ssh_get_log_userdata(void) +{ + return ssh_log_userdata; +} + +/** + * @brief Set the userdata for the logging function. + * + * @param[in] data The userdata to set. + * + * @return SSH_OK on success. + */ +int ssh_set_log_userdata(void *data) +{ + ssh_log_userdata = data; + + return 0; +} + /** @} */ /* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/messages.c b/libssh/src/messages.c index 5fcdd04e..003ecf8e 100644 --- a/libssh/src/messages.c +++ b/libssh/src/messages.c @@ -44,6 +44,7 @@ #include "libssh/messages.h" #ifdef WITH_SERVER #include "libssh/server.h" +#include "libssh/gssapi.h" #endif /** @@ -78,7 +79,7 @@ static ssh_message ssh_message_new(ssh_session session){ * SSH_MSG_UNIMPLEMENTED */ static int ssh_message_reply_default(ssh_message msg) { - ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Reporting unknown packet"); + SSH_LOG(SSH_LOG_FUNCTIONS, "Reporting unknown packet"); if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_UNIMPLEMENTED) < 0) goto error; @@ -92,8 +93,217 @@ static int ssh_message_reply_default(ssh_message msg) { #endif +#ifdef WITH_SERVER + +static int ssh_execute_server_request(ssh_session session, ssh_message msg) +{ + ssh_channel channel = NULL; + int rc; + + switch(msg->type) { + case SSH_REQUEST_AUTH: + if (msg->auth_request.method == SSH_AUTH_METHOD_PASSWORD && + ssh_callbacks_exists(session->server_callbacks, auth_password_function)) { + rc = session->server_callbacks->auth_password_function(session, + msg->auth_request.username, msg->auth_request.password, + session->server_callbacks->userdata); + if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) { + ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } else if(msg->auth_request.method == SSH_AUTH_METHOD_PUBLICKEY && + ssh_callbacks_exists(session->server_callbacks, auth_pubkey_function)) { + rc = session->server_callbacks->auth_pubkey_function(session, + msg->auth_request.username, msg->auth_request.pubkey, + msg->auth_request.signature_state, + session->server_callbacks->userdata); + if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL){ + ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } + break; + case SSH_REQUEST_CHANNEL_OPEN: + if (msg->channel_request_open.type == SSH_CHANNEL_SESSION && + ssh_callbacks_exists(session->server_callbacks, channel_open_request_session_function)) { + channel = session->server_callbacks->channel_open_request_session_function(session, + session->server_callbacks->userdata); + if (channel != NULL) { + rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel); + return SSH_OK; + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } + break; + case SSH_REQUEST_CHANNEL: + channel = msg->channel_request.channel; + + if (msg->channel_request.type == SSH_CHANNEL_REQUEST_PTY && + ssh_callbacks_exists(channel->callbacks, channel_pty_request_function)) { + rc = channel->callbacks->channel_pty_request_function(session, channel, + msg->channel_request.TERM, + msg->channel_request.width, msg->channel_request.height, + msg->channel_request.pxwidth, msg->channel_request.pxheight, + channel->callbacks->userdata); + if (rc == 0) { + ssh_message_channel_request_reply_success(msg); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SHELL && + ssh_callbacks_exists(channel->callbacks, channel_shell_request_function)) { + rc = channel->callbacks->channel_shell_request_function(session, + channel, + channel->callbacks->userdata); + if (rc == 0) { + ssh_message_channel_request_reply_success(msg); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_X11 && + ssh_callbacks_exists(channel->callbacks, channel_x11_req_function)) { + channel->callbacks->channel_x11_req_function(session, + channel, + msg->channel_request.x11_single_connection, + msg->channel_request.x11_auth_protocol, + msg->channel_request.x11_auth_cookie, + msg->channel_request.x11_screen_number, + channel->callbacks->userdata); + ssh_message_channel_request_reply_success(msg); + + return SSH_OK; + } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_WINDOW_CHANGE && + ssh_callbacks_exists(channel->callbacks, channel_pty_window_change_function)) { + rc = channel->callbacks->channel_pty_window_change_function(session, + channel, + msg->channel_request.height, msg->channel_request.width, + msg->channel_request.pxheight, msg->channel_request.pxwidth, + channel->callbacks->userdata); + } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_EXEC && + ssh_callbacks_exists(channel->callbacks, channel_exec_request_function)) { + rc = channel->callbacks->channel_exec_request_function(session, + channel, + msg->channel_request.command, + channel->callbacks->userdata); + if (rc == 0) { + ssh_message_channel_request_reply_success(msg); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_ENV && + ssh_callbacks_exists(channel->callbacks, channel_env_request_function)) { + rc = channel->callbacks->channel_env_request_function(session, + channel, + msg->channel_request.var_name, msg->channel_request.var_value, + channel->callbacks->userdata); + if (rc == 0) { + ssh_message_channel_request_reply_success(msg); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SUBSYSTEM && + ssh_callbacks_exists(channel->callbacks, channel_subsystem_request_function)) { + rc = channel->callbacks->channel_subsystem_request_function(session, + channel, + msg->channel_request.subsystem, + channel->callbacks->userdata); + if (rc == 0) { + ssh_message_channel_request_reply_success(msg); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } + break; + case SSH_REQUEST_SERVICE: + if (ssh_callbacks_exists(session->server_callbacks, service_request_function)) { + rc = session->server_callbacks->service_request_function(session, + msg->service_request.service, session->server_callbacks->userdata); + if (rc == 0) { + ssh_message_reply_default(msg); + } else { + ssh_disconnect(session); + } + + return SSH_OK; + } + + return SSH_AGAIN; + case SSH_REQUEST_GLOBAL: + break; + } + + return SSH_AGAIN; +} + +static int ssh_execute_client_request(ssh_session session, ssh_message msg) +{ + ssh_channel channel = NULL; + int rc = SSH_AGAIN; + + if (msg->type == SSH_REQUEST_CHANNEL_OPEN + && msg->channel_request_open.type == SSH_CHANNEL_X11 + && ssh_callbacks_exists(session->common.callbacks, channel_open_request_x11_function)) { + channel = session->common.callbacks->channel_open_request_x11_function (session, + msg->channel_request_open.originator, + msg->channel_request_open.originator_port, + session->common.callbacks->userdata); + if (channel != NULL) { + rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel); + + return rc; + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; + } + + return rc; +} + +/** @internal + * Executes the callbacks defined in session->server_callbacks, out of an ssh_message + * I don't like ssh_message interface but it works. + * @returns SSH_OK if the message has been handled, or SSH_AGAIN otherwise. + */ +static int ssh_execute_server_callbacks(ssh_session session, ssh_message msg){ + int rc = SSH_AGAIN; + + if (session->server_callbacks != NULL){ + rc = ssh_execute_server_request(session, msg); + } + + /* This one is in fact a client callback... */ + if (session->common.callbacks != NULL) { + rc = ssh_execute_client_request(session, msg); + } + + return rc; +} + +#endif /* WITH_SERVER */ + static int ssh_execute_message_callback(ssh_session session, ssh_message msg) { - int ret; + int ret; if(session->ssh_message_callback != NULL) { ret = session->ssh_message_callback(session, msg, session->ssh_message_callback_data); @@ -116,23 +326,43 @@ static int ssh_execute_message_callback(ssh_session session, ssh_message msg) { return SSH_OK; } - /** * @internal * - * @brief Add a message to the current queue of messages to be parsed. + * @brief Add a message to the current queue of messages to be parsed and/or call + * the various callback functions. * * @param[in] session The SSH session to add the message. * * @param[in] message The message to add to the queue. */ void ssh_message_queue(ssh_session session, ssh_message message){ - if(message) { + if (message != NULL) { +#ifdef WITH_SERVER + int ret; + /* probably not the best place to execute server callbacks, but still better + * than nothing. + */ + ret = ssh_execute_server_callbacks(session, message); + if (ret == SSH_OK){ + ssh_message_free(message); + return; + } +#endif /* WITH_SERVER */ + if(session->ssh_message_callback != NULL) { + ssh_execute_message_callback(session, message); + return; + } + if (session->server_callbacks != NULL){ + /* if we have server callbacks, but nothing was executed, it means we are + * in non-synchronous mode, and we just don't care about the message we + * received. Just send a default response. Do not queue it. + */ + ssh_message_reply_default(message); + ssh_message_free(message); + return; + } if(session->ssh_message_list == NULL) { - if(session->ssh_message_callback != NULL) { - ssh_execute_message_callback(session, message); - return; - } session->ssh_message_list = ssh_list_new(); } if (session->ssh_message_list != NULL) { @@ -189,11 +419,9 @@ static int ssh_message_termination(void *s){ ssh_message ssh_message_get(ssh_session session) { ssh_message msg = NULL; int rc; - enter_function(); msg=ssh_message_pop_head(session); if(msg) { - leave_function(); return msg; } if(session->ssh_message_list == NULL) { @@ -204,7 +432,7 @@ ssh_message ssh_message_get(ssh_session session) { if(rc || session->session_state == SSH_SESSION_STATE_ERROR) return NULL; msg=ssh_list_pop_head(ssh_message, session->ssh_message_list); - leave_function(); + return msg; } @@ -292,12 +520,13 @@ void ssh_message_free(ssh_message msg){ SAFE_FREE(msg); } +#ifdef WITH_SERVER + SSH_PACKET_CALLBACK(ssh_packet_service_request){ ssh_string service = NULL; char *service_c = NULL; ssh_message msg=NULL; - enter_function(); (void)type; (void)user; service = buffer_get_ssh_string(packet); @@ -310,7 +539,7 @@ SSH_PACKET_CALLBACK(ssh_packet_service_request){ if (service_c == NULL) { goto error; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received a SERVICE_REQUEST for service %s", service_c); msg=ssh_message_new(session); if(!msg){ @@ -323,7 +552,7 @@ error: ssh_string_free(service); if(msg != NULL) ssh_message_queue(session,msg); - leave_function(); + return SSH_PACKET_USED; } @@ -458,8 +687,6 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ char *method = NULL; uint32_t method_size = 0; - enter_function(); - (void)user; (void)type; @@ -501,7 +728,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ goto error; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Auth request for service %s, method %s for user '%s'", service, method, msg->auth_request.username); @@ -598,7 +825,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ sig_blob = buffer_get_ssh_string(packet); if(sig_blob == NULL) { - ssh_log(session, SSH_LOG_PACKET, "Invalid signature packet from peer"); + SSH_LOG(SSH_LOG_PACKET, "Invalid signature packet from peer"); msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_ERROR; goto error; } @@ -606,7 +833,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ digest = ssh_msg_userauth_build_digest(session, msg, service); if (digest == NULL) { ssh_string_free(sig_blob); - ssh_log(session, SSH_LOG_PACKET, "Failed to get digest"); + SSH_LOG(SSH_LOG_PACKET, "Failed to get digest"); msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG; goto error; } @@ -619,19 +846,69 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ ssh_string_free(sig_blob); ssh_buffer_free(digest); if (rc < 0) { - ssh_log(session, + SSH_LOG( SSH_LOG_PACKET, "Received an invalid signature from peer"); msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG; goto error; } - ssh_log(session, SSH_LOG_PACKET, "Valid signature received"); + SSH_LOG(SSH_LOG_PACKET, "Valid signature received"); msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_VALID; } goto end; } +#ifdef WITH_GSSAPI + if (strncmp(method, "gssapi-with-mic", method_size) == 0) { + uint32_t n_oid; + ssh_string *oids; + ssh_string oid; + char *hexa; + int i; + buffer_get_u32(packet, &n_oid); + n_oid=ntohl(n_oid); + if(n_oid > 100){ + ssh_set_error(session, SSH_FATAL, "USERAUTH_REQUEST: gssapi-with-mic OID count too big (%d)",n_oid); + goto error; + } + SSH_LOG(SSH_LOG_PACKET, "gssapi: %d OIDs", n_oid); + oids = calloc(n_oid, sizeof(ssh_string)); + if (oids == NULL){ + ssh_set_error_oom(session); + goto error; + } + for (i=0;i<(int) n_oid;++i){ + oid=buffer_get_ssh_string(packet); + if(oid == NULL){ + for(i=i-1;i>=0;--i){ + SAFE_FREE(oids[i]); + } + SAFE_FREE(oids); + ssh_set_error(session, SSH_LOG_PACKET, "USERAUTH_REQUEST: gssapi-with-mic missing OID"); + goto error; + } + oids[i] = oid; + if(session->common.log_verbosity >= SSH_LOG_PACKET){ + hexa = ssh_get_hexa(ssh_string_data(oid), ssh_string_len(oid)); + SSH_LOG(SSH_LOG_PACKET,"gssapi: OID %d: %s",i, hexa); + SAFE_FREE(hexa); + } + } + ssh_gssapi_handle_userauth(session, msg->auth_request.username, n_oid, oids); + + for(i=0;i<(int)n_oid;++i){ + SAFE_FREE(oids[i]); + } + SAFE_FREE(oids); + /* bypass the message queue thing */ + SAFE_FREE(service); + SAFE_FREE(method); + ssh_message_free(msg); + + return SSH_PACKET_USED; + } +#endif msg->auth_request.method = SSH_AUTH_METHOD_UNKNOWN; SAFE_FREE(method); @@ -642,17 +919,17 @@ error: ssh_message_free(msg); - leave_function(); return SSH_PACKET_USED; end: SAFE_FREE(service); SAFE_FREE(method); ssh_message_queue(session,msg); - leave_function(); + return SSH_PACKET_USED; } +#endif /* WITH_SERVER */ /** * @internal * @@ -667,7 +944,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ (void)user; return SSH_PACKET_USED; } -#else +#else /* WITH_SERVER */ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ uint32_t nanswers; uint32_t i; @@ -675,8 +952,12 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ ssh_message msg = NULL; - enter_function(); - + /* GSSAPI_TOKEN has same packed number. XXX fix this */ +#ifdef WITH_GSSAPI + if (session->gssapi != NULL) { + return ssh_packet_userauth_gssapi_token(session, type, packet, user); + } +#endif (void)user; (void)type; @@ -698,7 +979,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ buffer_get_u32(packet, &nanswers); if (session->kbdint == NULL) { - ssh_log(session, SSH_LOG_PROTOCOL, "Warning: Got a keyboard-interactive " + SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a keyboard-interactive " "response but it seems we didn't send the request."); session->kbdint = ssh_kbdint_new(); @@ -710,7 +991,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ } nanswers = ntohl(nanswers); - ssh_log(session,SSH_LOG_PACKET,"kbdint: %d answers",nanswers); + SSH_LOG(SSH_LOG_PACKET,"kbdint: %d answers",nanswers); if (nanswers > KBDINT_MAX_PROMPT) { ssh_set_error(session, SSH_FATAL, "Too much answers received from client: %u (0x%.4x)", @@ -723,7 +1004,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ if(nanswers != session->kbdint->nprompts) { /* warn but let the application handle this case */ - ssh_log(session, SSH_LOG_PROTOCOL, "Warning: Number of prompts and answers" + SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Number of prompts and answers" " mismatch: p=%u a=%u", session->kbdint->nprompts, nanswers); } session->kbdint->nanswers = nanswers; @@ -761,16 +1042,15 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ } ssh_message_queue(session,msg); - leave_function(); + return SSH_PACKET_USED; error: ssh_message_free(msg); - leave_function(); return SSH_PACKET_USED; } -#endif +#endif /* WITH_SERVER */ SSH_PACKET_CALLBACK(ssh_packet_channel_open){ ssh_message msg = NULL; @@ -778,7 +1058,6 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){ char *type_c = NULL; uint32_t sender, window, packet_size, originator_port, destination_port; - enter_function(); (void)type; (void)user; msg = ssh_message_new(session); @@ -800,7 +1079,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){ goto error; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Clients wants to open a %s channel", type_c); ssh_string_free(type_s); type_s=NULL; @@ -813,6 +1092,11 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){ msg->channel_request_open.window = ntohl(window); msg->channel_request_open.packet_size = ntohl(packet_size); + if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED){ + ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)"); + goto error; + } + if (strcmp(type_c,"session") == 0) { msg->channel_request_open.type = SSH_CHANNEL_SESSION; SAFE_FREE(type_c); @@ -926,68 +1210,82 @@ end: SAFE_FREE(type_c); if(msg != NULL) ssh_message_queue(session,msg); - leave_function(); + return SSH_PACKET_USED; } -/* TODO: make this function accept a ssh_channel */ +int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_channel chan) { + ssh_session session; + int rc; + + if (msg == NULL) { + return SSH_ERROR; + } + + session = msg->session; + + chan->local_channel = ssh_channel_new_id(session); + chan->local_maxpacket = 35000; + chan->local_window = 32000; + chan->remote_channel = msg->channel_request_open.sender; + chan->remote_maxpacket = msg->channel_request_open.packet_size; + chan->remote_window = msg->channel_request_open.window; + chan->state = SSH_CHANNEL_STATE_OPEN; + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); + if (rc < 0) { + return SSH_ERROR; + } + + rc = buffer_add_u32(session->out_buffer, htonl(chan->remote_channel)); + if (rc < 0) { + return SSH_ERROR; + } + + rc =buffer_add_u32(session->out_buffer, htonl(chan->local_channel)); + if (rc < 0) { + return SSH_ERROR; + } + + rc = buffer_add_u32(session->out_buffer, htonl(chan->local_window)); + if (rc < 0) { + return SSH_ERROR; + } + + rc = buffer_add_u32(session->out_buffer, htonl(chan->local_maxpacket)); + if (rc < 0) { + return SSH_ERROR; + } + + SSH_LOG(SSH_LOG_PACKET, + "Accepting a channel request_open for chan %d", + chan->remote_channel); + + rc = packet_send(session); + + return rc; +} + + ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg) { - ssh_session session; - ssh_channel chan = NULL; + ssh_channel chan; + int rc; - enter_function(); + if (msg == NULL) { + return NULL; + } - if (msg == NULL) { - leave_function(); - return NULL; - } + chan = ssh_channel_new(msg->session); + if (chan == NULL) { + return NULL; + } + rc = ssh_message_channel_request_open_reply_accept_channel(msg, chan); + if (rc < 0) { + ssh_channel_free(chan); + chan = NULL; + } + return chan; - session = msg->session; - - chan = ssh_channel_new(session); - if (chan == NULL) { - leave_function(); - return NULL; - } - - chan->local_channel = ssh_channel_new_id(session); - chan->local_maxpacket = 35000; - chan->local_window = 32000; - chan->remote_channel = msg->channel_request_open.sender; - chan->remote_maxpacket = msg->channel_request_open.packet_size; - chan->remote_window = msg->channel_request_open.window; - chan->state = SSH_CHANNEL_STATE_OPEN; - - if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) < 0) { - goto error; - } - if (buffer_add_u32(session->out_buffer, htonl(chan->remote_channel)) < 0) { - goto error; - } - if (buffer_add_u32(session->out_buffer, htonl(chan->local_channel)) < 0) { - goto error; - } - if (buffer_add_u32(session->out_buffer, htonl(chan->local_window)) < 0) { - goto error; - } - if (buffer_add_u32(session->out_buffer, htonl(chan->local_maxpacket)) < 0) { - goto error; - } - - ssh_log(session, SSH_LOG_PACKET, - "Accepting a channel request_open for chan %d", chan->remote_channel); - - if (packet_send(session) == SSH_ERROR) { - goto error; - } - - leave_function(); - return chan; -error: - ssh_channel_free(chan); - - leave_function(); - return NULL; } /** @@ -1012,14 +1310,14 @@ error: int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet, const char *request, uint8_t want_reply) { ssh_message msg = NULL; - enter_function(); + msg = ssh_message_new(session); if (msg == NULL) { ssh_set_error_oom(session); goto error; } - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received a %s channel_request for channel (%d:%d) (want_reply=%hhd)", request, channel->local_channel, channel->remote_channel, want_reply); @@ -1190,12 +1488,11 @@ int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, msg->channel_request.type = SSH_CHANNEL_REQUEST_UNKNOWN; end: ssh_message_queue(session,msg); - leave_function(); + return SSH_OK; error: ssh_message_free(msg); - leave_function(); return SSH_ERROR; } @@ -1209,7 +1506,7 @@ int ssh_message_channel_request_reply_success(ssh_message msg) { if (msg->channel_request.want_reply) { channel = msg->channel_request.channel->remote_channel; - ssh_log(msg->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sending a channel_request success to channel %d", channel); if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_CHANNEL_SUCCESS) < 0) { @@ -1222,7 +1519,7 @@ int ssh_message_channel_request_reply_success(ssh_message msg) { return packet_send(msg->session); } - ssh_log(msg->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "The client doesn't want to know the request succeeded"); return SSH_OK; @@ -1250,7 +1547,7 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){ buffer_get_u8(packet, &want_reply); - ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_MSG_GLOBAL_REQUEST packet"); + SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH_MSG_GLOBAL_REQUEST packet"); msg = ssh_message_new(session); if (msg == NULL) { @@ -1274,10 +1571,10 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){ msg->global_request.bind_address = bind_addr; msg->global_request.bind_port = bind_port; - ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) { - ssh_log(session, SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + SSH_LOG(SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata); } else { ssh_message_reply_default(msg); @@ -1296,7 +1593,7 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){ msg->global_request.bind_address = bind_addr; msg->global_request.bind_port = bind_port; - ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) { session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata); @@ -1304,7 +1601,7 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){ ssh_message_reply_default(msg); } } else { - ssh_log(session, SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s %d", request, want_reply); + SSH_LOG(SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s %d", request, want_reply); rc = SSH_PACKET_NOT_USED; } diff --git a/libssh/src/misc.c b/libssh/src/misc.c index 99f60b48..18a59d60 100644 --- a/libssh/src/misc.c +++ b/libssh/src/misc.c @@ -219,7 +219,7 @@ char *ssh_get_user_home_dir(void) { return NULL; } memset(buf, 0, sizeof(buf)); - snprintf(buf, sizeof(buf), "%s", getenv("HOME")); + snprintf(buf, sizeof(buf), "%s", szPath); return strdup(buf); } @@ -839,7 +839,7 @@ int ssh_analyze_banner(ssh_session session, int server, int *ssh1, int *ssh2) { return -1; } - ssh_log(session, SSH_LOG_RARE, "Analyzing banner: %s", banner); + SSH_LOG(SSH_LOG_RARE, "Analyzing banner: %s", banner); switch(banner[4]) { case '1': @@ -874,7 +874,7 @@ int ssh_analyze_banner(ssh_session session, int server, int *ssh1, int *ssh2) { major = strtol(openssh + 8, (char **) NULL, 10); minor = strtol(openssh + 10, (char **) NULL, 10); session->openssh = SSH_VERSION_INT(major, minor, 0); - ssh_log(session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "We are talking to an OpenSSH client version: %d.%d (%x)", major, minor, session->openssh); } diff --git a/libssh/src/options.c b/libssh/src/options.c index 931cb31e..f8055780 100644 --- a/libssh/src/options.c +++ b/libssh/src/options.c @@ -635,6 +635,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, } session->common.log_verbosity = *x & 0xffff; + ssh_set_log_level(*x & 0xffff); } break; case SSH_OPTIONS_LOG_VERBOSITY_STR: @@ -780,11 +781,15 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } else { SAFE_FREE(session->opts.ProxyCommand); - q = strdup(v); - if (q == NULL) { - return -1; + /* Setting the command to 'none' disables this option. */ + rc = strcasecmp(v, "none"); + if (rc != 0) { + q = strdup(v); + if (q == NULL) { + return -1; + } + session->opts.ProxyCommand = q; } - session->opts.ProxyCommand = q; } break; default: @@ -931,7 +936,8 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv) { char *cipher = NULL; char *identity = NULL; char *port = NULL; - char **save = NULL, **tmp; + char **save = NULL; + char **tmp = NULL; int i = 0; int argc = *argcptr; int debuglevel = 0; @@ -1013,6 +1019,13 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv) { } /* switch */ } /* while */ opterr = saveopterr; + tmp = realloc(save, (current + (argc - optind)) * sizeof(char*)); + if (tmp == NULL) { + SAFE_FREE(save); + ssh_set_error_oom(session); + return -1; + } + save = tmp; while (optind < argc) { tmp = realloc(save, (current + 1) * sizeof(char*)); if (tmp == NULL) { @@ -1031,7 +1044,7 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv) { cont = 0; } - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &debuglevel); + ssh_set_log_level(debuglevel); optind = saveoptind; @@ -1349,7 +1362,7 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, return -1; } else { int *x = (int *) value; - sshbind->common.log_verbosity = *x & 0xffff; + ssh_set_log_level(*x & 0xffff); } break; case SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: diff --git a/libssh/src/packet.c b/libssh/src/packet.c index 440e47c6..3302847a 100644 --- a/libssh/src/packet.c +++ b/libssh/src/packet.c @@ -46,6 +46,7 @@ #include "libssh/pcap.h" #include "libssh/kex.h" #include "libssh/auth.h" +#include "libssh/gssapi.h" #define MACSIZE SHA_DIGEST_LEN @@ -54,7 +55,11 @@ static ssh_packet_callback default_packet_handlers[]= { ssh_packet_ignore_callback, // SSH2_MSG_IGNORE 2 ssh_packet_unimplemented, // SSH2_MSG_UNIMPLEMENTED 3 ssh_packet_ignore_callback, // SSH2_MSG_DEBUG 4 +#if WITH_SERVER ssh_packet_service_request, // SSH2_MSG_SERVICE_REQUEST 5 +#else + NULL, +#endif ssh_packet_service_accept, // SSH2_MSG_SERVICE_ACCEPT 6 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 7-19 @@ -76,18 +81,33 @@ static ssh_packet_callback default_packet_handlers[]= { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 35-49 +#if WITH_SERVER ssh_packet_userauth_request, // SSH2_MSG_USERAUTH_REQUEST 50 +#else + NULL, +#endif ssh_packet_userauth_failure, // SSH2_MSG_USERAUTH_FAILURE 51 ssh_packet_userauth_success, // SSH2_MSG_USERAUTH_SUCCESS 52 ssh_packet_userauth_banner, // SSH2_MSG_USERAUTH_BANNER 53 NULL,NULL,NULL,NULL,NULL,NULL, // 54-59 ssh_packet_userauth_pk_ok, // SSH2_MSG_USERAUTH_PK_OK 60 // SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 - // SSH2_MSG_USERAUTH_INFO_REQUEST 60 + // SSH2_MSG_USERAUTH_INFO_REQUEST 60 + // SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 ssh_packet_userauth_info_response, // SSH2_MSG_USERAUTH_INFO_RESPONSE 61 + // SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 + NULL, // 62 + NULL, // SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 + NULL, // SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 + NULL, // SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 +#if defined(WITH_GSSAPI) && defined(WITH_SERVER) + ssh_packet_userauth_gssapi_mic, // SSH2_MSG_USERAUTH_GSSAPI_MIC 66 +#else /* WITH_GSSAPI && WITH_SERVER */ + NULL, +#endif /* WITH_GSSAPI && WITH_SERVER */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, // 62-79 + NULL, NULL, NULL, NULL, // 67-79 #ifdef WITH_SERVER ssh_packet_global_request, // SSH2_MSG_GLOBAL_REQUEST 80 #else /* WITH_SERVER */ @@ -139,14 +159,12 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) goto error; } - enter_function(); if (session->session_state == SSH_SESSION_STATE_ERROR) goto error; switch(session->packet_state) { case PACKET_STATE_INIT: if(receivedlen < blocksize){ /* We didn't receive enough data to read at least one block size, give up */ - leave_function(); return 0; } memset(&session->in_packet, 0, sizeof(PACKET)); @@ -195,7 +213,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) if (to_be_read != 0) { if(receivedlen - processed < (unsigned int)to_be_read){ /* give up, not enough data in buffer */ - ssh_log(session,SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len); + SSH_LOG(SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len); return processed; } @@ -262,7 +280,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) /* We don't want to rewrite a new packet while still executing the packet callbacks */ session->packet_state = PACKET_STATE_PROCESSING; ssh_packet_parse_type(session); - ssh_log(session,SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", session->in_packet.type, len, padding, compsize, payloadsize); /* execute callbacks */ @@ -270,16 +288,16 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) session->packet_state = PACKET_STATE_INIT; if(processed < receivedlen){ /* Handle a potential packet left in socket buffer */ - ssh_log(session,SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", + SSH_LOG(SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", receivedlen-processed); rc = ssh_packet_socket_callback(((unsigned char *)data) + processed, receivedlen - processed,user); processed += rc; } - leave_function(); + return processed; case PACKET_STATE_PROCESSING: - ssh_log(session, SSH_LOG_RARE, "Nested packet processing. Delaying."); + SSH_LOG(SSH_LOG_RARE, "Nested packet processing. Delaying."); return 0; } @@ -289,7 +307,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) error: session->session_state= SSH_SESSION_STATE_ERROR; - leave_function(); + return processed; } @@ -339,11 +357,12 @@ void ssh_packet_process(ssh_session session, uint8_t type){ struct ssh_iterator *i; int r=SSH_PACKET_NOT_USED; ssh_packet_callbacks cb; - enter_function(); - ssh_log(session,SSH_LOG_PACKET, "Dispatching handler for packet type %d",type); + + SSH_LOG(SSH_LOG_PACKET, "Dispatching handler for packet type %d",type); if(session->packet_callbacks == NULL){ - ssh_log(session,SSH_LOG_RARE,"Packet callback is not initialized !"); - goto error; + SSH_LOG(SSH_LOG_RARE,"Packet callback is not initialized !"); + + return; } i=ssh_list_get_iterator(session->packet_callbacks); while(i != NULL){ @@ -362,11 +381,9 @@ void ssh_packet_process(ssh_session session, uint8_t type){ break; } if(r==SSH_PACKET_NOT_USED){ - ssh_log(session,SSH_LOG_RARE,"Couldn't do anything with packet type %d",type); + SSH_LOG(SSH_LOG_RARE,"Couldn't do anything with packet type %d",type); ssh_packet_send_unimplemented(session, session->recv_seq-1); } -error: - leave_function(); } /** @internal @@ -377,7 +394,7 @@ error: */ int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum){ int r; - enter_function(); + r = buffer_add_u8(session->out_buffer, SSH2_MSG_UNIMPLEMENTED); if (r < 0) { return SSH_ERROR; @@ -387,7 +404,7 @@ int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum){ return SSH_ERROR; } r = packet_send(session); - leave_function(); + return r; } @@ -396,11 +413,12 @@ int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum){ */ SSH_PACKET_CALLBACK(ssh_packet_unimplemented){ uint32_t seq; + (void)session; /* unused */ (void)type; (void)user; buffer_get_u32(packet,&seq); seq=ntohl(seq); - ssh_log(session,SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "Received SSH_MSG_UNIMPLEMENTED (sequence number %d)",seq); return SSH_PACKET_USED; } @@ -409,23 +427,18 @@ SSH_PACKET_CALLBACK(ssh_packet_unimplemented){ * @parse the "Type" header field of a packet and updates the session */ int ssh_packet_parse_type(ssh_session session) { - enter_function(); - memset(&session->in_packet, 0, sizeof(PACKET)); if(session->in_buffer == NULL) { - leave_function(); return SSH_ERROR; } if(buffer_get_u8(session->in_buffer, &session->in_packet.type) == 0) { ssh_set_error(session, SSH_FATAL, "Packet too short to read type"); - leave_function(); return SSH_ERROR; } session->in_packet.valid = 1; - leave_function(); return SSH_OK; } @@ -436,12 +449,10 @@ int ssh_packet_parse_type(ssh_session session) { static int ssh_packet_write(ssh_session session) { int rc = SSH_ERROR; - enter_function(); - rc=ssh_socket_write(session->socket, buffer_get_rest(session->out_buffer), buffer_get_rest_len(session->out_buffer)); - leave_function(); + return rc; } @@ -455,8 +466,6 @@ static int packet_send2(ssh_session session) { uint32_t finallen,payloadsize,compsize; uint8_t padding; - enter_function(); - payloadsize = currentlen; #ifdef WITH_ZLIB if (session->current_crypto @@ -509,14 +518,14 @@ static int packet_send2(ssh_session session) { rc = ssh_packet_write(session); session->send_seq++; - ssh_log(session,SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "packet: wrote [len=%d,padding=%hhd,comp=%d,payload=%d]", ntohl(finallen), padding, compsize, payloadsize); if (buffer_reinit(session->out_buffer) < 0) { rc = SSH_ERROR; } error: - leave_function(); + return rc; /* SSH_OK, AGAIN or ERROR */ } diff --git a/libssh/src/packet1.c b/libssh/src/packet1.c index 56bfb346..7ac8318d 100644 --- a/libssh/src/packet1.c +++ b/libssh/src/packet1.c @@ -108,7 +108,6 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user uint32_t crc; uint32_t len; ssh_session session=(ssh_session)user; - enter_function(); switch (session->packet_state){ case PACKET_STATE_INIT: @@ -126,7 +125,6 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user } /* must have at least enough bytes for size */ if(receivedlen < sizeof(uint32_t)){ - leave_function(); return 0; } memcpy(&len,data,sizeof(uint32_t)); @@ -140,7 +138,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user goto error; } - ssh_log(session, SSH_LOG_PACKET, "Reading a %d bytes packet", len); + SSH_LOG(SSH_LOG_PACKET, "Reading a %d bytes packet", len); session->in_packet.len = len; session->packet_state = PACKET_STATE_SIZEREAD; @@ -152,7 +150,6 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user to_be_read = len + padding; if(to_be_read + processed > receivedlen){ /* wait for rest of packet */ - leave_function(); return processed; } /* it is _not_ possible that to_be_read be < 8. */ @@ -182,10 +179,10 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user ssh_print_hexa("read packet decrypted:", ssh_buffer_get_begin(session->in_buffer), ssh_buffer_get_len(session->in_buffer)); #endif - ssh_log(session, SSH_LOG_PACKET, "%d bytes padding", padding); + SSH_LOG(SSH_LOG_PACKET, "%d bytes padding", padding); if(((len + padding) != buffer_get_rest_len(session->in_buffer)) || ((len + padding) < sizeof(uint32_t))) { - ssh_log(session, SSH_LOG_RARE, "no crc32 in packet"); + SSH_LOG(SSH_LOG_RARE, "no crc32 in packet"); ssh_set_error(session, SSH_FATAL, "no crc32 in packet"); goto error; } @@ -201,7 +198,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user ssh_print_hexa("crc32 on",buffer_get_rest(session->in_buffer), len + padding - sizeof(uint32_t)); #endif - ssh_log(session, SSH_LOG_RARE, "Invalid crc32"); + SSH_LOG(SSH_LOG_RARE, "Invalid crc32"); ssh_set_error(session, SSH_FATAL, "Invalid crc32: expected %.8x, got %.8x", crc, @@ -211,7 +208,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user } /* pass the padding */ buffer_pass_bytes(session->in_buffer, padding); - ssh_log(session, SSH_LOG_PACKET, "The packet is valid"); + SSH_LOG(SSH_LOG_PACKET, "The packet is valid"); /* TODO FIXME #ifdef WITH_ZLIB @@ -230,22 +227,22 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user if(processed < receivedlen){ int rc; /* Handle a potential packet left in socket buffer */ - ssh_log(session,SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", + SSH_LOG(SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", receivedlen-processed); rc = ssh_packet_socket_callback1((char *)data + processed, receivedlen - processed,user); processed += rc; } - leave_function(); + return processed; case PACKET_STATE_PROCESSING: - ssh_log(session, SSH_LOG_RARE, "Nested packet processing. Delaying."); + SSH_LOG(SSH_LOG_RARE, "Nested packet processing. Delaying."); return 0; } error: session->session_state=SSH_SESSION_STATE_ERROR; - leave_function(); + return processed; } @@ -260,8 +257,7 @@ int packet_send1(ssh_session session) { uint32_t crc; uint8_t padding; - enter_function(); - ssh_log(session,SSH_LOG_PACKET,"Sending a %d bytes long packet",currentlen); + SSH_LOG(SSH_LOG_PACKET,"Sending a %d bytes long packet",currentlen); /* TODO FIXME #ifdef WITH_ZLIB @@ -281,7 +277,7 @@ int packet_send1(ssh_session session) { } finallen = htonl(currentlen); - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "%d bytes after comp + %d padding bytes = %d bytes packet", currentlen, padding, ntohl(finallen)); @@ -323,7 +319,7 @@ int packet_send1(ssh_session session) { rc = SSH_ERROR; } error: - leave_function(); + return rc; /* SSH_OK, AGAIN or ERROR */ } @@ -331,7 +327,7 @@ SSH_PACKET_CALLBACK(ssh_packet_disconnect1){ (void)packet; (void)user; (void)type; - ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT"); + SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT"); ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_DISCONNECT"); ssh_socket_close(session->socket); session->alive = 0; diff --git a/libssh/src/packet_cb.c b/libssh/src/packet_cb.c index 41d0985c..4a8beb54 100644 --- a/libssh/src/packet_cb.c +++ b/libssh/src/packet_cb.c @@ -53,7 +53,7 @@ SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){ error = ssh_string_to_char(error_s); ssh_string_free(error_s); } - ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s",code, + SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s",code, error != NULL ? error : "no error"); ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_DISCONNECT: %d:%s",code, @@ -73,10 +73,11 @@ SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){ * @brief Handle a SSH_IGNORE and SSH_DEBUG packet. */ SSH_PACKET_CALLBACK(ssh_packet_ignore_callback){ + (void)session; /* unused */ (void)user; (void)type; (void)packet; - ssh_log(session,SSH_LOG_PROTOCOL,"Received %s packet",type==SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG"); + SSH_LOG(SSH_LOG_PROTOCOL,"Received %s packet",type==SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG"); /* TODO: handle a graceful disconnect */ return SSH_PACKET_USED; } @@ -85,7 +86,7 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_reply){ int rc; (void)type; (void)user; - ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY"); + SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY"); if(session->session_state!= SSH_SESSION_STATE_DH && session->dh_handshake_state != DH_STATE_INIT_SENT){ ssh_set_error(session,SSH_FATAL,"ssh_packet_dh_reply called in wrong state : %d:%d", @@ -121,7 +122,7 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){ (void)packet; (void)user; (void)type; - ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_NEWKEYS"); + SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_NEWKEYS"); if(session->session_state!= SSH_SESSION_STATE_DH && session->dh_handshake_state != DH_STATE_NEWKEYS_SENT){ ssh_set_error(session,SSH_FATAL,"ssh_packet_newkeys called in wrong state : %d:%d", @@ -191,7 +192,7 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){ if (rc == SSH_ERROR) { goto error; } - ssh_log(session,SSH_LOG_PROTOCOL,"Signature verified and valid"); + SSH_LOG(SSH_LOG_PROTOCOL,"Signature verified and valid"); /* * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and @@ -235,10 +236,10 @@ SSH_PACKET_CALLBACK(ssh_packet_service_accept){ (void)packet; (void)type; (void)user; - enter_function(); + session->auth_service_state=SSH_AUTH_SERVICE_ACCEPTED; - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_SERVICE_ACCEPT"); - leave_function(); + return SSH_PACKET_USED; } diff --git a/libssh/src/pki.c b/libssh/src/pki.c index a3616c2f..dd3d915a 100644 --- a/libssh/src/pki.c +++ b/libssh/src/pki.c @@ -1220,8 +1220,7 @@ int ssh_pki_signature_verify_blob(ssh_session session, return SSH_ERROR; } - ssh_log(session, - SSH_LOG_FUNCTIONS, + SSH_LOG(SSH_LOG_FUNCTIONS, "Going to verify a %s type signature", key->type_c); @@ -1260,7 +1259,7 @@ int ssh_pki_signature_verify_blob(ssh_session session, } /* - * This function signs the session id (known as H) as a string then + * This function signs the session id as a string then * the content of sigbuf */ ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf, @@ -1284,7 +1283,7 @@ ssh_string ssh_pki_do_sign(ssh_session session, return NULL; } ssh_string_fill(session_id, crypto->session_id, crypto->digest_len); - + /* TODO: change when supporting ECDSA keys */ ctx = sha1_init(); if (ctx == NULL) { ssh_string_free(session_id); @@ -1381,18 +1380,18 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, if (session == NULL || privkey == NULL || !ssh_key_is_private(privkey)) { return NULL; } - crypto = session->current_crypto ? session->current_crypto : - session->next_crypto; + crypto = session->next_crypto ? session->next_crypto : + session->current_crypto; ctx = sha1_init(); if (ctx == NULL) { return NULL; } - if (crypto->session_id == NULL){ - ssh_set_error(session,SSH_FATAL,"Missing session_id"); + if (crypto->secret_hash == NULL){ + ssh_set_error(session,SSH_FATAL,"Missing secret_hash"); return NULL; } - sha1_update(ctx, crypto->session_id, crypto->digest_len); + sha1_update(ctx, crypto->secret_hash, crypto->digest_len); sha1_final(hash, ctx); #ifdef DEBUG_CRYPTO diff --git a/libssh/src/poll.c b/libssh/src/poll.c index 932c4918..bde0198d 100644 --- a/libssh/src/poll.c +++ b/libssh/src/poll.c @@ -69,6 +69,7 @@ struct ssh_poll_handle_struct { size_t idx; } x; short events; + int lock; ssh_poll_callback cb; void *cb_data; }; @@ -114,10 +115,13 @@ static poll_fn ssh_poll_emu; #else /* _WIN32 */ #include #include -#include #include #endif /* _WIN32 */ +#ifdef HAVE_UNISTD_H +#include +#endif + /* * This is a poll(2)-emulation using select for systems not providing a native @@ -336,7 +340,7 @@ short ssh_poll_get_events(ssh_poll_handle p) { */ void ssh_poll_set_events(ssh_poll_handle p, short events) { p->events = events; - if (p->ctx != NULL) { + if (p->ctx != NULL && !p->lock) { p->ctx->pollfds[p->x.idx].events = events; } } @@ -595,7 +599,7 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout) { return SSH_AGAIN; used = ctx->polls_used; for (i = 0; i < used && rc > 0; ) { - if (!ctx->pollfds[i].revents) { + if (!ctx->pollfds[i].revents || ctx->pollptrs[i]->lock) { i++; } else { int ret; @@ -603,7 +607,9 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout) { p = ctx->pollptrs[i]; fd = ctx->pollfds[i].fd; revents = ctx->pollfds[i].revents; - + /* avoid having any event caught during callback */ + ctx->pollfds[i].events = 0; + p->lock = 1; if (p->cb && (ret = p->cb(p, fd, revents, p->cb_data)) < 0) { if (ret == -2) { return -1; @@ -613,6 +619,8 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout) { i=0; } else { ctx->pollfds[i].revents = 0; + ctx->pollfds[i].events = p->events; + p->lock = 0; i++; } diff --git a/libssh/src/scp.c b/libssh/src/scp.c index df4f5753..6838a3cd 100644 --- a/libssh/src/scp.c +++ b/libssh/src/scp.c @@ -83,7 +83,17 @@ ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location){ return scp; } -int ssh_scp_init(ssh_scp scp){ +/** + * @brief Initialize the scp channel. + * + * @param[in] scp The scp context to initialize. + * + * @return SSH_OK on success or an SSH error code. + * + * @see ssh_scp_new() + */ +int ssh_scp_init(ssh_scp scp) +{ int r; char execbuffer[1024]; uint8_t code; @@ -93,7 +103,7 @@ int ssh_scp_init(ssh_scp scp){ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_init called under invalid state"); return SSH_ERROR; } - ssh_log(scp->session,SSH_LOG_PROTOCOL,"Initializing scp session %s %son location '%s'", + SSH_LOG(SSH_LOG_PROTOCOL,"Initializing scp session %s %son location '%s'", scp->mode==SSH_SCP_WRITE?"write":"read", scp->recursive?"recursive ":"", scp->location); @@ -139,7 +149,17 @@ int ssh_scp_init(ssh_scp scp){ return SSH_OK; } -int ssh_scp_close(ssh_scp scp){ +/** + * @brief Close the scp channel. + * + * @param[in] scp The scp context to close. + * + * @return SSH_OK on success or an SSH error code. + * + * @see ssh_scp_init() + */ +int ssh_scp_close(ssh_scp scp) +{ char buffer[128]; int err; if(scp==NULL) @@ -169,7 +189,15 @@ int ssh_scp_close(ssh_scp scp){ return SSH_OK; } -void ssh_scp_free(ssh_scp scp){ +/** + * @brief Free a scp context. + * + * @param[in] scp The context to free. + * + * @see ssh_scp_new() + */ +void ssh_scp_free(ssh_scp scp) +{ if(scp==NULL) return; if(scp->state != SSH_SCP_NEW) @@ -300,7 +328,7 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int mo } file=ssh_basename(filename); perms=ssh_scp_string_mode(mode); - ssh_log(scp->session,SSH_LOG_PROTOCOL,"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",file,size,perms); + SSH_LOG(SSH_LOG_PROTOCOL,"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",file,size,perms); snprintf(buffer, sizeof(buffer), "C%s %" PRIu64 " %s\n", perms, size, file); SAFE_FREE(file); SAFE_FREE(perms); @@ -379,7 +407,7 @@ int ssh_scp_response(ssh_scp scp, char **response){ /* Warning */ if(code == 1){ ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Warning: status code 1 received: %s", msg); - ssh_log(scp->session,SSH_LOG_RARE,"SCP: Warning: status code 1 received: %s", msg); + SSH_LOG(SSH_LOG_RARE,"SCP: Warning: status code 1 received: %s", msg); if(response) *response=strdup(msg); return 1; @@ -540,7 +568,7 @@ int ssh_scp_pull_request(ssh_scp scp){ p=strchr(buffer,'\n'); if(p!=NULL) *p='\0'; - ssh_log(scp->session,SSH_LOG_PROTOCOL,"Received SCP request: '%s'",buffer); + SSH_LOG(SSH_LOG_PROTOCOL,"Received SCP request: '%s'",buffer); switch(buffer[0]){ case 'C': /* File */ @@ -751,7 +779,7 @@ int ssh_scp_request_get_permissions(ssh_scp scp){ size_t ssh_scp_request_get_size(ssh_scp scp){ if(scp==NULL) return 0; - return scp->filelen; + return (size_t)scp->filelen; } /** @brief Get the size of the file being pushed from the other party. diff --git a/libssh/src/server.c b/libssh/src/server.c index 6862370f..a2104642 100644 --- a/libssh/src/server.c +++ b/libssh/src/server.c @@ -167,10 +167,10 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){ int rc; (void)type; (void)user; - enter_function(); - ssh_log(session,SSH_LOG_PACKET,"Received SSH_MSG_KEXDH_INIT"); + + SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_KEXDH_INIT"); if(session->dh_handshake_state != DH_STATE_INIT){ - ssh_log(session,SSH_LOG_RARE,"Invalid state for SSH_MSG_KEXDH_INIT"); + SSH_LOG(SSH_LOG_RARE,"Invalid state for SSH_MSG_KEXDH_INIT"); goto error; } switch(session->next_crypto->kex_type){ @@ -190,7 +190,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){ if (rc == SSH_ERROR) session->session_state = SSH_SESSION_STATE_ERROR; error: - leave_function(); + return SSH_PACKET_USED; } @@ -278,22 +278,6 @@ static int dh_handshake_server(ssh_session session) { return -1; } - /* Free private keys as they should not be readable after this point */ - if (session->srv.rsa_key) { - ssh_key_free(session->srv.rsa_key); - session->srv.rsa_key = NULL; - } - if (session->srv.dsa_key) { - ssh_key_free(session->srv.dsa_key); - session->srv.dsa_key = NULL; - } -#ifdef HAVE_ECC - if (session->srv.ecdsa_key) { - ssh_key_free(session->srv.ecdsa_key); - session->srv.ecdsa_key = NULL; - } -#endif - if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY) < 0 || buffer_add_ssh_string(session->out_buffer, session->next_crypto->server_pubkey) < 0 || @@ -319,7 +303,7 @@ static int dh_handshake_server(ssh_session session) { if (packet_send(session) == SSH_ERROR) { return -1; } - ssh_log(session, SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent"); + SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent"); session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; return 0; @@ -333,7 +317,7 @@ static int dh_handshake_server(ssh_session session) { */ static void ssh_server_connection_callback(ssh_session session){ int ssh1,ssh2; - enter_function(); + switch(session->session_state){ case SSH_SESSION_STATE_NONE: case SSH_SESSION_STATE_CONNECTING: @@ -344,7 +328,7 @@ static void ssh_server_connection_callback(ssh_session session){ goto error; } set_status(session, 0.4f); - ssh_log(session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "SSH client banner: %s", session->clientbanner); /* Here we analyze the different protocols the server allows. */ @@ -400,7 +384,14 @@ static void ssh_server_connection_callback(ssh_session session){ break; case SSH_SESSION_STATE_KEXINIT_RECEIVED: set_status(session,0.6f); - ssh_list_kex(session, &session->next_crypto->client_kex); // log client kex + if(session->next_crypto->server_kex.methods[0]==NULL){ + if(server_set_kex(session) == SSH_ERROR) + goto error; + /* We are in a rekeying, so we need to send the server kex */ + if(ssh_send_kex(session, 1) < 0) + goto error; + } + ssh_list_kex(&session->next_crypto->client_kex); // log client kex if (ssh_kex_select_methods(session) < 0) { goto error; } @@ -429,10 +420,20 @@ static void ssh_server_connection_callback(ssh_session session){ if (session->next_crypto == NULL) { goto error; } - set_status(session,1.0f); - session->connected = 1; - session->session_state=SSH_SESSION_STATE_AUTHENTICATING; - } + session->next_crypto->session_id = malloc(session->current_crypto->digest_len); + if (session->next_crypto->session_id == NULL) { + ssh_set_error_oom(session); + goto error; + } + memcpy(session->next_crypto->session_id, session->current_crypto->session_id, + session->current_crypto->digest_len); + + set_status(session,1.0f); + session->connected = 1; + session->session_state=SSH_SESSION_STATE_AUTHENTICATING; + if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) + session->session_state = SSH_SESSION_STATE_AUTHENTICATED; + } break; case SSH_SESSION_STATE_AUTHENTICATING: break; @@ -441,13 +442,12 @@ static void ssh_server_connection_callback(ssh_session session){ default: ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); } - leave_function(); + return; - error: +error: ssh_socket_close(session->socket); session->alive = 0; session->session_state=SSH_SESSION_STATE_ERROR; - leave_function(); } /** @@ -468,8 +468,6 @@ static int callback_receive_banner(const void *data, size_t len, void *user) { size_t i; int ret=0; - enter_function(); - for (i = 0; i < len; i++) { #ifdef WITH_PCAP if(session->pcap_ctx && buffer[i] == '\n') { @@ -492,10 +490,9 @@ static int callback_receive_banner(const void *data, size_t len, void *user) { ret = i + 1; session->clientbanner = str; session->session_state = SSH_SESSION_STATE_BANNER_RECEIVED; - ssh_log(session, SSH_LOG_PACKET, "Received banner: %s", str); + SSH_LOG(SSH_LOG_PACKET, "Received banner: %s", str); session->ssh_connection_callback(session); - leave_function(); return ret; } @@ -504,12 +501,10 @@ static int callback_receive_banner(const void *data, size_t len, void *user) { session->session_state = SSH_SESSION_STATE_ERROR; ssh_set_error(session, SSH_FATAL, "Receiving banner: too large banner"); - leave_function(); return 0; } } - leave_function(); return ret; } @@ -524,6 +519,17 @@ static int ssh_server_kex_termination(void *s){ return 1; } +/** Set the acceptable authentication methods to be sent to + * client. + * @param[in] session the SSH server session + * @param[in] auth_methods Bitfield of authentication methods + * to be accepted, e.g. SSH_AUTH_METHOD_PUBLICKEY + */ +void ssh_set_auth_methods(ssh_session session, int auth_methods){ + /* accept only methods in range */ + session->auth_methods = auth_methods & 0x3f; +} + /* Do the banner and key exchange */ int ssh_handle_key_exchange(ssh_session session) { int rc; @@ -550,7 +556,7 @@ int ssh_handle_key_exchange(ssh_session session) { pending: rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, ssh_server_kex_termination,session); - ssh_log(session,SSH_LOG_PACKET, "ssh_handle_key_exchange: Actual state : %d", + SSH_LOG(SSH_LOG_PACKET, "ssh_handle_key_exchange: current state : %d", session->session_state); if (rc != SSH_OK) return rc; @@ -564,14 +570,14 @@ int ssh_handle_key_exchange(ssh_session session) { /* messages */ -static int ssh_message_auth_reply_default(ssh_message msg,int partial) { - ssh_session session = msg->session; +/** @internal + * replies to an SSH_AUTH packet with a default (denied) response. + */ +int ssh_auth_reply_default(ssh_session session,int partial) { char methods_c[128] = {0}; ssh_string methods = NULL; int rc = SSH_ERROR; - enter_function(); - if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_FAILURE) < 0) { return rc; } @@ -583,6 +589,10 @@ static int ssh_message_auth_reply_default(ssh_message msg,int partial) { strncat(methods_c, "publickey,", sizeof(methods_c) - strlen(methods_c) - 1); } + if (session->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC){ + strncat(methods_c,"gssapi-with-mic,", + sizeof(methods_c) - strlen(methods_c) - 1); + } if (session->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { strncat(methods_c, "keyboard-interactive,", sizeof(methods_c) - strlen(methods_c) - 1); @@ -603,7 +613,7 @@ static int ssh_message_auth_reply_default(ssh_message msg,int partial) { /* Strip the comma. */ methods_c[strlen(methods_c) - 1] = '\0'; // strip the comma. We are sure there is at - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sending a auth failure. methods that can continue: %s", methods_c); methods = ssh_string_from_char(methods_c); @@ -611,7 +621,7 @@ static int ssh_message_auth_reply_default(ssh_message msg,int partial) { goto error; } - if (buffer_add_ssh_string(msg->session->out_buffer, methods) < 0) { + if (buffer_add_ssh_string(session->out_buffer, methods) < 0) { goto error; } @@ -625,16 +635,15 @@ static int ssh_message_auth_reply_default(ssh_message msg,int partial) { } } - rc = packet_send(msg->session); + rc = packet_send(session); error: ssh_string_free(methods); - leave_function(); return rc; } static int ssh_message_channel_request_open_reply_default(ssh_message msg) { - ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Refusing a channel"); + SSH_LOG(SSH_LOG_FUNCTIONS, "Refusing a channel"); if (buffer_add_u8(msg->session->out_buffer , SSH2_MSG_CHANNEL_OPEN_FAILURE) < 0) { @@ -668,7 +677,7 @@ static int ssh_message_channel_request_reply_default(ssh_message msg) { if (msg->channel_request.want_reply) { channel = msg->channel_request.channel->remote_channel; - ssh_log(msg->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sending a default channel_request denied to channel %d", channel); if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_CHANNEL_FAILURE) < 0) { @@ -681,7 +690,7 @@ static int ssh_message_channel_request_reply_default(ssh_message msg) { return packet_send(msg->session); } - ssh_log(msg->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "The client doesn't want to know the request failed!"); return SSH_OK; @@ -701,7 +710,7 @@ int ssh_message_service_reply_success(ssh_message msg) { } session = msg->session; - ssh_log(session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sending a SERVICE_ACCEPT for service %s", msg->service_request.service); if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_ACCEPT) < 0) { return -1; @@ -720,7 +729,7 @@ int ssh_message_service_reply_success(ssh_message msg) { } int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) { - ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Accepting a global request"); + SSH_LOG(SSH_LOG_FUNCTIONS, "Accepting a global request"); if (msg->global_request.want_reply) { if (buffer_add_u8(msg->session->out_buffer @@ -740,7 +749,7 @@ int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_por if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD && msg->global_request.bind_port == 0) { - ssh_log(msg->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "The client doesn't want to know the remote port!"); } @@ -750,7 +759,7 @@ error: } static int ssh_message_global_request_reply_default(ssh_message msg) { - ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Refusing a global request"); + SSH_LOG(SSH_LOG_FUNCTIONS, "Refusing a global request"); if (msg->global_request.want_reply) { if (buffer_add_u8(msg->session->out_buffer @@ -759,7 +768,7 @@ static int ssh_message_global_request_reply_default(ssh_message msg) { } return packet_send(msg->session); } - ssh_log(msg->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "The client doesn't want to know the request failed!"); return SSH_OK; @@ -774,7 +783,7 @@ int ssh_message_reply_default(ssh_message msg) { switch(msg->type) { case SSH_REQUEST_AUTH: - return ssh_message_auth_reply_default(msg, 0); + return ssh_auth_reply_default(msg->session, 0); case SSH_REQUEST_CHANNEL_OPEN: return ssh_message_channel_request_open_reply_default(msg); case SSH_REQUEST_CHANNEL: @@ -784,7 +793,7 @@ int ssh_message_reply_default(ssh_message msg) { case SSH_REQUEST_GLOBAL: return ssh_message_global_request_reply_default(msg); default: - ssh_log(msg->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Don't know what to default reply to %d type", msg->type); break; @@ -940,7 +949,7 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name, /* fill in the kbdint structure */ if (msg->session->kbdint == NULL) { - ssh_log(msg->session, SSH_LOG_PROTOCOL, "Warning: Got a " + SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a " "keyboard-interactive response but it " "seems we didn't send the request."); @@ -1005,33 +1014,42 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name, return r; } -int ssh_message_auth_reply_success(ssh_message msg, int partial) { +int ssh_auth_reply_success(ssh_session session, int partial) { int r; - if (msg == NULL) { - return SSH_ERROR; + if (session == NULL) { + return SSH_ERROR; } if (partial) { - return ssh_message_auth_reply_default(msg, partial); + return ssh_auth_reply_default(session, partial); } + + session->session_state = SSH_SESSION_STATE_AUTHENTICATED; + session->flags |= SSH_SESSION_FLAG_AUTHENTICATED; - if (buffer_add_u8(msg->session->out_buffer,SSH2_MSG_USERAUTH_SUCCESS) < 0) { + if (buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_SUCCESS) < 0) { return SSH_ERROR; } - r = packet_send(msg->session); - if(msg->session->current_crypto && msg->session->current_crypto->delayed_compress_out){ - ssh_log(msg->session,SSH_LOG_PROTOCOL,"Enabling delayed compression OUT"); - msg->session->current_crypto->do_compress_out=1; + r = packet_send(session); + if(session->current_crypto && session->current_crypto->delayed_compress_out){ + SSH_LOG(SSH_LOG_PROTOCOL,"Enabling delayed compression OUT"); + session->current_crypto->do_compress_out=1; } - if(msg->session->current_crypto && msg->session->current_crypto->delayed_compress_in){ - ssh_log(msg->session,SSH_LOG_PROTOCOL,"Enabling delayed compression IN"); - msg->session->current_crypto->do_compress_in=1; + if(session->current_crypto && session->current_crypto->delayed_compress_in){ + SSH_LOG(SSH_LOG_PROTOCOL,"Enabling delayed compression IN"); + session->current_crypto->do_compress_in=1; } return r; } +int ssh_message_auth_reply_success(ssh_message msg, int partial) { + if(msg == NULL) + return SSH_ERROR; + return ssh_auth_reply_success(msg->session, partial); +} + /* Answer OK to a pubkey auth request */ int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) { if (msg == NULL) { @@ -1197,39 +1215,38 @@ int ssh_execute_message_callbacks(ssh_session session){ return SSH_OK; } + int ssh_send_keepalive(ssh_session session) { - /* TODO check the reply and all that */ - struct ssh_string_struct *req; - int reply = 1; - int rc = SSH_ERROR; + /* TODO check the reply and all that */ + struct ssh_string_struct *req; + int reply = 1; + int rc = SSH_ERROR; - enter_function(); - req = ssh_string_from_char("keepalive@openssh.com"); - if (req == NULL) { - ssh_set_error_oom(session); - goto out; - } + req = ssh_string_from_char("keepalive@openssh.com"); + if (req == NULL) { + ssh_set_error_oom(session); + goto out; + } - if (buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST) < 0 || - buffer_add_ssh_string(session->out_buffer, req) < 0 || - buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { - ssh_set_error_oom(session); - goto out; - } + if (buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST) < 0 || + buffer_add_ssh_string(session->out_buffer, req) < 0 || + buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { + ssh_set_error_oom(session); + goto out; + } - if (packet_send(session) == SSH_ERROR) - goto out; + if (packet_send(session) == SSH_ERROR) + goto out; - ssh_handle_packets(session, 0); + ssh_handle_packets(session, 0); - ssh_log(session, SSH_LOG_PACKET, "Sent a keepalive"); - rc = SSH_OK; + SSH_LOG(SSH_LOG_PACKET, "Sent a keepalive"); + rc = SSH_OK; out: - ssh_string_free(req); - leave_function(); - return rc; + ssh_string_free(req); + return rc; } /** @} */ diff --git a/libssh/src/session.c b/libssh/src/session.c index fe5d897c..477e5ce4 100644 --- a/libssh/src/session.c +++ b/libssh/src/session.c @@ -89,7 +89,6 @@ ssh_session ssh_new(void) { session->alive = 0; session->auth_methods = 0; ssh_set_blocking(session, 1); - session->common.log_indent = 0; session->maxchannel = FIRST_CHANNEL; #ifndef _WIN32 @@ -286,8 +285,6 @@ const char* ssh_get_serverbanner(ssh_session session) { * @param[in] session The SSH session to disconnect. */ void ssh_silent_disconnect(ssh_session session) { - enter_function(); - if (session == NULL) { return; } @@ -295,7 +292,6 @@ void ssh_silent_disconnect(ssh_session session) { ssh_socket_close(session->socket); session->alive = 0; ssh_disconnect(session); - leave_function(); } /** @@ -347,20 +343,21 @@ static int ssh_flush_termination(void *c){ */ int ssh_blocking_flush(ssh_session session, int timeout){ - int rc; - if(!session) - return SSH_ERROR; - enter_function(); + int rc; + if (session == NULL) { + return SSH_ERROR; + } - rc = ssh_handle_packets_termination(session, timeout, - ssh_flush_termination, session); - if (rc == SSH_ERROR) - goto end; - if (!ssh_flush_termination(session)) - rc = SSH_AGAIN; -end: - leave_function(); - return rc; + rc = ssh_handle_packets_termination(session, timeout, + ssh_flush_termination, session); + if (rc == SSH_ERROR) { + return rc; + } + if (!ssh_flush_termination(session)) { + rc = SSH_AGAIN; + } + + return rc; } /** @@ -464,7 +461,6 @@ int ssh_handle_packets(ssh_session session, int timeout) { if (session == NULL || session->socket == NULL) { return SSH_ERROR; } - enter_function(); spoll_in = ssh_socket_get_poll_handle_in(session->socket); spoll_out = ssh_socket_get_poll_handle_out(session->socket); @@ -493,7 +489,6 @@ int ssh_handle_packets(ssh_session session, int timeout) { session->session_state = SSH_SESSION_STATE_ERROR; } - leave_function(); return rc; } @@ -512,6 +507,7 @@ int ssh_handle_packets(ssh_session session, int timeout) { * (-1) means an infinite timeout. * Specifying SSH_TIMEOUT_USER means to use the timeout * specified in options. 0 means poll will return immediately. + * SSH_TIMEOUT_DEFAULT uses blocking parameters of the session. * This parameter is passed to the poll() function. * * @param[in] fct Termination function to be used to determine if it is @@ -519,31 +515,46 @@ int ssh_handle_packets(ssh_session session, int timeout) { * @param[in] user User parameter to be passed to fct termination function. * @return SSH_OK on success, SSH_ERROR otherwise. */ -int ssh_handle_packets_termination(ssh_session session, int timeout, - ssh_termination_function fct, void *user){ - int ret = SSH_OK; - struct ssh_timestamp ts; - int tm; - if (timeout == SSH_TIMEOUT_USER) { - if (ssh_is_blocking(session)) - timeout = ssh_make_milliseconds(session->opts.timeout, - session->opts.timeout_usec); - else - timeout = SSH_TIMEOUT_NONBLOCKING; - } - ssh_timestamp_init(&ts); - tm = timeout; - while(!fct(user)){ - ret = ssh_handle_packets(session, tm); - if(ret == SSH_ERROR) - break; - if(ssh_timeout_elapsed(&ts,timeout)) { - ret = fct(user) ? SSH_OK : SSH_AGAIN; - break; - } - tm = ssh_timeout_update(&ts, timeout); - } - return ret; +int ssh_handle_packets_termination(ssh_session session, + int timeout, + ssh_termination_function fct, + void *user) +{ + struct ssh_timestamp ts; + int ret = SSH_OK; + int tm; + + if (timeout == SSH_TIMEOUT_USER) { + if (ssh_is_blocking(session)) { + timeout = ssh_make_milliseconds(session->opts.timeout, + session->opts.timeout_usec); + } else { + timeout = SSH_TIMEOUT_NONBLOCKING; + } + } else if (timeout == SSH_TIMEOUT_DEFAULT) { + if (ssh_is_blocking(session)) { + timeout = SSH_TIMEOUT_INFINITE; + } else { + timeout = SSH_TIMEOUT_NONBLOCKING; + } + } + + ssh_timestamp_init(&ts); + tm = timeout; + while(!fct(user)) { + ret = ssh_handle_packets(session, tm); + if (ret == SSH_ERROR) { + break; + } + if (ssh_timeout_elapsed(&ts,timeout)) { + ret = fct(user) ? SSH_OK : SSH_AGAIN; + break; + } + + tm = ssh_timeout_update(&ts, timeout); + } + + return ret; } /** @@ -575,7 +586,8 @@ int ssh_get_status(ssh_session session) { if (socketstate & SSH_WRITE_PENDING) { r |= SSH_WRITE_PENDING; } - if (session->closed && (socketstate & SSH_CLOSED_ERROR)) { + if ((session->closed && (socketstate & SSH_CLOSED_ERROR)) || + session->session_state == SSH_SESSION_STATE_ERROR) { r |= SSH_CLOSED_ERROR; } @@ -636,12 +648,11 @@ int ssh_get_version(ssh_session session) { */ void ssh_socket_exception_callback(int code, int errno_code, void *user){ ssh_session session=(ssh_session)user; - enter_function(); - ssh_log(session,SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code); + + SSH_LOG(SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code); session->session_state=SSH_SESSION_STATE_ERROR; ssh_set_error(session,SSH_FATAL,"Socket error: %s",strerror(errno_code)); session->ssh_connection_callback(session); - leave_function(); } /** diff --git a/libssh/src/sftp.c b/libssh/src/sftp.c index ee86107b..62460b4d 100644 --- a/libssh/src/sftp.c +++ b/libssh/src/sftp.c @@ -106,12 +106,11 @@ sftp_session sftp_new(ssh_session session){ if (session == NULL) { return NULL; } - enter_function(); sftp = malloc(sizeof(struct sftp_session_struct)); if (sftp == NULL) { ssh_set_error_oom(session); - leave_function(); + return NULL; } ZERO_STRUCTP(sftp); @@ -120,7 +119,7 @@ sftp_session sftp_new(ssh_session session){ if (sftp->ext == NULL) { ssh_set_error_oom(session); SAFE_FREE(sftp); - leave_function(); + return NULL; } @@ -128,24 +127,52 @@ sftp_session sftp_new(ssh_session session){ sftp->channel = ssh_channel_new(session); if (sftp->channel == NULL) { SAFE_FREE(sftp); - leave_function(); + return NULL; } if (ssh_channel_open_session(sftp->channel)) { ssh_channel_free(sftp->channel); SAFE_FREE(sftp); - leave_function(); + return NULL; } if (ssh_channel_request_sftp(sftp->channel)) { sftp_free(sftp); - leave_function(); + return NULL; } - leave_function(); + return sftp; +} + +sftp_session sftp_new_channel(ssh_session session, ssh_channel channel){ + sftp_session sftp; + + if (session == NULL) { + return NULL; + } + + sftp = malloc(sizeof(struct sftp_session_struct)); + if (sftp == NULL) { + ssh_set_error_oom(session); + + return NULL; + } + ZERO_STRUCTP(sftp); + + sftp->ext = sftp_ext_new(); + if (sftp->ext == NULL) { + ssh_set_error_oom(session); + SAFE_FREE(sftp); + + return NULL; + } + + sftp->session = session; + sftp->channel = channel; + return sftp; } @@ -186,11 +213,11 @@ int sftp_server_init(sftp_session sftp){ return -1; } - ssh_log(session, SSH_LOG_PACKET, "Received SSH_FXP_INIT"); + SSH_LOG(SSH_LOG_PACKET, "Received SSH_FXP_INIT"); buffer_get_u32(packet->payload, &version); version = ntohl(version); - ssh_log(session, SSH_LOG_PACKET, "Client version: %d", version); + SSH_LOG(SSH_LOG_PACKET, "Client version: %d", version); sftp->client_version = version; sftp_packet_free(packet); @@ -213,7 +240,7 @@ int sftp_server_init(sftp_session sftp){ } ssh_buffer_free(reply); - ssh_log(session, SSH_LOG_RARE, "Server version sent"); + SSH_LOG(SSH_LOG_RARE, "Server version sent"); if (version > LIBSFTP_VERSION) { sftp->version = LIBSFTP_VERSION; @@ -271,7 +298,7 @@ int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload){ if (size < 0) { return -1; } else if((uint32_t) size != buffer_get_rest_len(payload)) { - ssh_log(sftp->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Had to write %d bytes, wrote only %d", buffer_get_rest_len(payload), size); @@ -419,7 +446,7 @@ static sftp_message sftp_get_message(sftp_packet packet) { return NULL; } - ssh_log(packet->sftp->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Packet with id %d type %d", msg->id, msg->packet_type); @@ -508,7 +535,7 @@ int sftp_init(sftp_session sftp) { /* TODO: are we sure there are 4 bytes ready? */ buffer_get_u32(packet->payload, &version); version = ntohl(version); - ssh_log(sftp->session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "SFTP server version %d", version); @@ -533,7 +560,7 @@ int sftp_init(sftp_session sftp) { ssh_string_free(ext_data_s); return -1; } - ssh_log(sftp->session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "SFTP server extension: %s, version: %s", ext_name, ext_data); @@ -674,7 +701,7 @@ static int sftp_enqueue(sftp_session sftp, sftp_message msg) { return -1; } - ssh_log(sftp->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Queued msg type %d id %d", msg->id, msg->packet_type); @@ -715,7 +742,7 @@ static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){ } msg = queue->message; request_queue_free(queue); - ssh_log(sftp->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Dequeued msg id %d type %d", msg->id, msg->packet_type); @@ -1191,7 +1218,7 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, break; } - ssh_log(sftp->session, SSH_LOG_RARE, "Name: %s", attr->name); + SSH_LOG(SSH_LOG_RARE, "Name: %s", attr->name); longname = buffer_get_ssh_string(buf); if (longname == NULL) { @@ -1222,7 +1249,7 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, } flags = ntohl(flags); attr->flags = flags; - ssh_log(sftp->session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "Flags: %.8lx\n", (long unsigned int) flags); if (flags & SSH_FILEXFER_ATTR_SIZE) { @@ -1230,7 +1257,7 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, break; } attr->size = ntohll(attr->size); - ssh_log(sftp->session, SSH_LOG_RARE, + SSH_LOG(SSH_LOG_RARE, "Size: %llu\n", (long long unsigned int) attr->size); } @@ -1419,7 +1446,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) { } ssh_buffer_free(payload); - ssh_log(sftp->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Sent a ssh_fxp_readdir with id %d", id); while (msg == NULL) { @@ -1476,7 +1503,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) { return NULL; } - ssh_log(sftp->session, SSH_LOG_RARE, "Count is %d", dir->count); + SSH_LOG(SSH_LOG_RARE, "Count is %d", dir->count); attr = sftp_parse_attr(sftp, dir->buffer, 1); if (attr == NULL) { @@ -1652,7 +1679,7 @@ sftp_file sftp_open(sftp_session sftp, const char *file, int flags, sftp_flags |= SSH_FXF_TRUNC; if (flags & O_EXCL) sftp_flags |= SSH_FXF_EXCL; - ssh_log(sftp->session,SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags); + SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags); id = sftp_get_new_id(sftp); if (buffer_add_u32(buffer, id) < 0 || buffer_add_ssh_string(buffer, filename) < 0) { @@ -1971,7 +1998,7 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { if (len < 0) { return -1; } else if (len != packetlen) { - ssh_log(sftp->session, SSH_LOG_PACKET, + SSH_LOG(SSH_LOG_PACKET, "Could not write as much data as expected"); } diff --git a/libssh/src/sftpserver.c b/libssh/src/sftpserver.c index 3ae93a99..0986b6ce 100644 --- a/libssh/src/sftpserver.c +++ b/libssh/src/sftpserver.c @@ -62,6 +62,10 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { msg->type = packet->type; msg->sftp = sftp; + /* take a copy of the whole packet */ + msg->complete_message = ssh_buffer_new(); + buffer_add_data(msg->complete_message, buffer_get_rest(payload), buffer_get_rest_len(payload)); + buffer_get_u32(payload, &msg->id); switch(msg->type) { @@ -241,6 +245,34 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { return msg; } +/* Send an sftp client message. Can be used in cas of proxying */ +int sftp_send_client_message(sftp_session sftp, sftp_client_message msg){ + return sftp_packet_write(sftp, msg->type, msg->complete_message); +} + +uint8_t sftp_client_message_get_type(sftp_client_message msg){ + return msg->type; +} + +const char *sftp_client_message_get_filename(sftp_client_message msg){ + return msg->filename; +} + +void sftp_client_message_set_filename(sftp_client_message msg, const char *newname){ + free(msg->filename); + msg->filename = strdup(newname); +} + +const char *sftp_client_message_get_data(sftp_client_message msg){ + if (msg->str_data == NULL) + msg->str_data = ssh_string_to_char(msg->data); + return msg->str_data; +} + +uint32_t sftp_client_message_get_flags(sftp_client_message msg){ + return msg->flags; +} + void sftp_client_message_free(sftp_client_message msg) { if (msg == NULL) { return; @@ -250,7 +282,8 @@ void sftp_client_message_free(sftp_client_message msg) { ssh_string_free(msg->data); ssh_string_free(msg->handle); sftp_attributes_free(msg->attr); - + ssh_buffer_free(msg->complete_message); + SAFE_FREE(msg->str_data); ZERO_STRUCTP(msg); SAFE_FREE(msg); } diff --git a/libssh/src/socket.c b/libssh/src/socket.c index 0f6c0a83..0dbbe2bc 100644 --- a/libssh/src/socket.c +++ b/libssh/src/socket.c @@ -305,7 +305,7 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int r #endif /* First, POLLOUT is a sign we may be connected */ if(s->state == SSH_SOCKET_CONNECTING){ - ssh_log(s->session,SSH_LOG_PACKET,"Received POLLOUT in connecting state"); + SSH_LOG(SSH_LOG_PACKET,"Received POLLOUT in connecting state"); s->state = SSH_SOCKET_CONNECTED; ssh_poll_set_events(p,POLLOUT | POLLIN); r = ssh_socket_set_blocking(ssh_socket_get_fd_in(s)); @@ -543,7 +543,7 @@ static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, s->write_wontblock = 0; /* Reactive the POLLOUT detector in the poll multiplexer system */ if(s->poll_out){ - ssh_log(s->session, SSH_LOG_PACKET, "Enabling POLLOUT for socket"); + SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket"); ssh_poll_set_events(s->poll_out,ssh_poll_get_events(s->poll_out) | POLLOUT); } if (w < 0) { @@ -593,8 +593,6 @@ void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) { * \warning has no effect on socket before a flush */ int ssh_socket_write(ssh_socket s, const void *buffer, int len) { - ssh_session session = s->session; - enter_function(); if(len > 0) { if (buffer_add_data(s->out_buffer, buffer, len) < 0) { ssh_set_error_oom(s->session); @@ -602,7 +600,7 @@ int ssh_socket_write(ssh_socket s, const void *buffer, int len) { } ssh_socket_nonblocking_flush(s); } - leave_function(); + return SSH_OK; } @@ -616,8 +614,6 @@ int ssh_socket_nonblocking_flush(ssh_socket s) { uint32_t len; int w; - enter_function(); - if (!ssh_socket_is_open(s)) { session->alive = 0; /* FIXME use ssh_socket_get_errno */ @@ -625,7 +621,6 @@ int ssh_socket_nonblocking_flush(ssh_socket s) { "Writing packet: error on socket (or connection closed): %s", strerror(s->last_errno)); - leave_function(); return SSH_ERROR; } @@ -633,7 +628,7 @@ int ssh_socket_nonblocking_flush(ssh_socket s) { if (!s->write_wontblock && s->poll_out && len > 0) { /* force the poll system to catch pollout events */ ssh_poll_add_events(s->poll_out, POLLOUT); - leave_function(); + return SSH_AGAIN; } if (s->write_wontblock && len > 0) { @@ -646,7 +641,7 @@ int ssh_socket_nonblocking_flush(ssh_socket s) { ssh_set_error(session, SSH_FATAL, "Writing packet: error on socket (or connection closed): %s", strerror(s->last_errno)); - leave_function(); + return SSH_ERROR; } buffer_pass_bytes(s->out_buffer, w); @@ -657,12 +652,11 @@ int ssh_socket_nonblocking_flush(ssh_socket s) { if (s->poll_out && len > 0) { /* force the poll system to catch pollout events */ ssh_poll_add_events(s->poll_out, POLLOUT); - leave_function(); + return SSH_AGAIN; } /* all data written */ - leave_function(); return SSH_OK; } @@ -754,15 +748,14 @@ int ssh_socket_set_blocking(socket_t fd) { int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr){ socket_t fd; - ssh_session session=s->session; - enter_function(); + if(s->state != SSH_SOCKET_NONE) { ssh_set_error(s->session, SSH_FATAL, "ssh_socket_connect called on socket not unconnected"); return SSH_ERROR; } fd=ssh_connect_host_nonblocking(s->session,host,bind_addr,port); - ssh_log(session,SSH_LOG_PROTOCOL,"Nonblocking connection socket: %d",fd); + SSH_LOG(SSH_LOG_PROTOCOL,"Nonblocking connection socket: %d",fd); if(fd == SSH_INVALID_SOCKET) return SSH_ERROR; ssh_socket_set_fd(s,fd); @@ -772,7 +765,7 @@ int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bin #ifdef _WIN32 ssh_poll_add_events(ssh_socket_get_poll_handle_in(s),POLLWRNORM); #endif - leave_function(); + return SSH_OK; } @@ -811,8 +804,7 @@ int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){ socket_t out_pipe[2]; int pid; int rc; - ssh_session session=s->session; - enter_function(); + if(s->state != SSH_SOCKET_NONE) return SSH_ERROR; @@ -825,14 +817,14 @@ int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){ return SSH_ERROR; } - ssh_log(session,SSH_LOG_PROTOCOL,"Executing proxycommand '%s'",command); + SSH_LOG(SSH_LOG_PROTOCOL,"Executing proxycommand '%s'",command); pid = fork(); if(pid == 0){ ssh_execute_command(command,out_pipe[0],in_pipe[1]); } close(in_pipe[1]); close(out_pipe[0]); - ssh_log(session,SSH_LOG_PROTOCOL,"ProxyCommand connection pipe: [%d,%d]",in_pipe[0],out_pipe[1]); + SSH_LOG(SSH_LOG_PROTOCOL,"ProxyCommand connection pipe: [%d,%d]",in_pipe[0],out_pipe[1]); ssh_socket_set_fd_in(s,in_pipe[0]); ssh_socket_set_fd_out(s,out_pipe[1]); s->state=SSH_SOCKET_CONNECTED; @@ -842,7 +834,7 @@ int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){ ssh_poll_set_events(ssh_socket_get_poll_handle_out(s),POLLOUT); if(s->callbacks && s->callbacks->connected) s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); - leave_function(); + return SSH_OK; } diff --git a/libssh/src/wrapper.c b/libssh/src/wrapper.c index c04322a4..66593461 100644 --- a/libssh/src/wrapper.c +++ b/libssh/src/wrapper.c @@ -166,10 +166,8 @@ void crypto_free(struct ssh_crypto_struct *crypto){ static int crypt_set_algorithms2(ssh_session session){ const char *wanted; int i = 0; - int rc = SSH_ERROR; struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); - enter_function(); /* we must scan the kex entries to find crypto algorithms and set their appropriate structure */ /* out */ wanted = session->next_crypto->kex_methods[SSH_CRYPT_C_S]; @@ -181,14 +179,14 @@ static int crypt_set_algorithms2(ssh_session session){ ssh_set_error(session, SSH_FATAL, "crypt_set_algorithms2: no crypto algorithm function found for %s", wanted); - goto error; + return SSH_ERROR; } - ssh_log(session, SSH_LOG_PACKET, "Set output algorithm to %s", wanted); + SSH_LOG(SSH_LOG_PACKET, "Set output algorithm to %s", wanted); session->next_crypto->out_cipher = cipher_new(i); if (session->next_crypto->out_cipher == NULL) { - ssh_set_error_oom(session); - goto error; + ssh_set_error_oom(session); + return SSH_ERROR; } i = 0; @@ -199,17 +197,17 @@ static int crypt_set_algorithms2(ssh_session session){ } if (ssh_ciphertab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, - "Crypt_set_algorithms: no crypto algorithm function found for %s", - wanted); - goto error; + ssh_set_error(session, SSH_FATAL, + "Crypt_set_algorithms: no crypto algorithm function found for %s", + wanted); + return SSH_ERROR; } - ssh_log(session, SSH_LOG_PACKET, "Set input algorithm to %s", wanted); + SSH_LOG(SSH_LOG_PACKET, "Set input algorithm to %s", wanted); session->next_crypto->in_cipher = cipher_new(i); if (session->next_crypto->in_cipher == NULL) { - ssh_set_error_oom(session); - goto error; + ssh_set_error_oom(session); + return SSH_ERROR; } /* compression */ @@ -225,10 +223,8 @@ static int crypt_set_algorithms2(ssh_session session){ if (strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib@openssh.com") == 0) { session->next_crypto->delayed_compress_in = 1; } - rc = SSH_OK; -error: - leave_function(); - return rc; + + return SSH_OK; } static int crypt_set_algorithms1(ssh_session session, enum ssh_des_e des_type) { @@ -270,15 +266,16 @@ int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type) { int crypt_set_algorithms_server(ssh_session session){ char *method = NULL; int i = 0; - int rc = SSH_ERROR; struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); if (session == NULL) { return SSH_ERROR; } - /* we must scan the kex entries to find crypto algorithms and set their appropriate structure */ - enter_function(); + /* + * We must scan the kex entries to find crypto algorithms and set their + * appropriate structure + */ /* out */ method = session->next_crypto->kex_methods[SSH_CRYPT_S_C]; while(ssh_ciphertab[i].name && strcmp(method,ssh_ciphertab[i].name)) @@ -286,14 +283,14 @@ int crypt_set_algorithms_server(ssh_session session){ if(!ssh_ciphertab[i].name){ ssh_set_error(session,SSH_FATAL,"crypt_set_algorithms_server : " "no crypto algorithm function found for %s",method); - goto error; + return SSH_ERROR; } - ssh_log(session,SSH_LOG_PACKET,"Set output algorithm %s",method); + SSH_LOG(SSH_LOG_PACKET,"Set output algorithm %s",method); session->next_crypto->out_cipher = cipher_new(i); if (session->next_crypto->out_cipher == NULL) { - ssh_set_error_oom(session); - goto error; + ssh_set_error_oom(session); + return SSH_ERROR; } i=0; /* in */ @@ -303,43 +300,41 @@ int crypt_set_algorithms_server(ssh_session session){ if(!ssh_ciphertab[i].name){ ssh_set_error(session,SSH_FATAL,"Crypt_set_algorithms_server :" "no crypto algorithm function found for %s",method); - goto error; + return SSH_ERROR; } - ssh_log(session,SSH_LOG_PACKET,"Set input algorithm %s",method); + SSH_LOG(SSH_LOG_PACKET,"Set input algorithm %s",method); session->next_crypto->in_cipher = cipher_new(i); if (session->next_crypto->in_cipher == NULL) { - ssh_set_error_oom(session); - goto error; + ssh_set_error_oom(session); + return SSH_ERROR; } /* compression */ method = session->next_crypto->kex_methods[SSH_COMP_C_S]; if(strcmp(method,"zlib") == 0){ - ssh_log(session,SSH_LOG_PACKET,"enabling C->S compression"); + SSH_LOG(SSH_LOG_PACKET,"enabling C->S compression"); session->next_crypto->do_compress_in=1; } if(strcmp(method,"zlib@openssh.com") == 0){ - ssh_log(session,SSH_LOG_PACKET,"enabling C->S compression"); + SSH_LOG(SSH_LOG_PACKET,"enabling C->S compression"); session->next_crypto->delayed_compress_in=1; } method = session->next_crypto->kex_methods[SSH_COMP_S_C]; if(strcmp(method,"zlib") == 0){ - ssh_log(session,SSH_LOG_PACKET,"enabling S->C compression\n"); + SSH_LOG(SSH_LOG_PACKET, "enabling S->C compression\n"); session->next_crypto->do_compress_out=1; } if(strcmp(method,"zlib@openssh.com") == 0){ - ssh_log(session,SSH_LOG_PACKET,"enabling S->C delayed compression\n"); + SSH_LOG(SSH_LOG_PACKET,"enabling S->C delayed compression\n"); session->next_crypto->delayed_compress_out=1; } method = session->next_crypto->kex_methods[SSH_HOSTKEYS]; session->srv.hostkey = ssh_key_type_from_name(method); - rc = SSH_OK; - error: - leave_function(); - return rc; + + return SSH_OK; } #endif /* WITH_SERVER */ diff --git a/libssh/tests/client/torture_session.c b/libssh/tests/client/torture_session.c index 4e2f6477..f2e062b4 100644 --- a/libssh/tests/client/torture_session.c +++ b/libssh/tests/client/torture_session.c @@ -92,6 +92,8 @@ static void torture_channel_read_error(void **state) { break; } assert_true(rc == SSH_ERROR); + + ssh_channel_free(channel); } int torture_run_tests(void) { diff --git a/libssh/tests/torture.c b/libssh/tests/torture.c index 2a49f0d7..c7031fcb 100644 --- a/libssh/tests/torture.c +++ b/libssh/tests/torture.c @@ -30,7 +30,10 @@ # include # include # include -# include +#endif + +#ifdef HAVE_UNISTD_H +#include #endif #include "torture.h" diff --git a/libssh/tests/unittests/torture_options.c b/libssh/tests/unittests/torture_options.c index 76da3edb..6f5df1bb 100644 --- a/libssh/tests/unittests/torture_options.c +++ b/libssh/tests/unittests/torture_options.c @@ -176,6 +176,23 @@ static void torture_options_get_identity(void **state) { free(identity); } +static void torture_options_proxycommand(void **state) { + ssh_session session = *state; + int rc; + + /* Enable ProxyCommand */ + rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, "ssh -q -A -X -W %h:%p JUMPHOST"); + assert_int_equal(rc, 0); + + assert_string_equal(session->opts.ProxyCommand, "ssh -q -A -X -W %h:%p JUMPHOST"); + + /* Disable ProxyCommand */ + rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, "none"); + assert_int_equal(rc, 0); + + assert_null(session->opts.ProxyCommand); +} + int torture_run_tests(void) { int rc; const UnitTest tests[] = { @@ -188,6 +205,7 @@ int torture_run_tests(void) { unit_test_setup_teardown(torture_options_get_user, setup, teardown), unit_test_setup_teardown(torture_options_set_identity, setup, teardown), unit_test_setup_teardown(torture_options_get_identity, setup, teardown), + unit_test_setup_teardown(torture_options_proxycommand, setup, teardown), }; ssh_init(); diff --git a/libssh/tests/unittests/torture_pki.c b/libssh/tests/unittests/torture_pki.c index 20bf5c60..7324177e 100644 --- a/libssh/tests/unittests/torture_pki.c +++ b/libssh/tests/unittests/torture_pki.c @@ -86,22 +86,22 @@ static char *read_file(const char *filename) { int fd; int size; int rc; - struct stat buf; + struct stat sb; assert_true(filename != NULL); assert_true(*filename != '\0'); - rc = stat(filename, &buf); - assert_int_equal(rc, 0); - - key = malloc(buf.st_size + 1); - assert_true(key != NULL); - fd = open(filename, O_RDONLY); assert_true(fd >= 0); - size = read(fd, key, buf.st_size); - assert_true(size == buf.st_size); + rc = fstat(fd, &sb); + assert_int_equal(rc, 0); + + key = malloc(sb.st_size + 1); + assert_true(key != NULL); + + size = read(fd, key, sb.st_size); + assert_true(size == sb.st_size); close(fd); diff --git a/libssh/tests/valgrind.supp b/libssh/tests/valgrind.supp index 4b553ead..a4276c3b 100644 --- a/libssh/tests/valgrind.supp +++ b/libssh/tests/valgrind.supp @@ -17,6 +17,17 @@ fun:ssh_connect_host_nonblocking } +{ + glibc_dlopen_getdelim_selinux + Memcheck:Leak + fun:malloc + fun:getdelim + obj:/lib64/libselinux.so.1 + fun:call_init + fun:_dl_init + obj:/lib64/ld-2.15.so +} + ### OPENSSL { openssl_crypto_value8 From 604c43bfab404c4dde0d982e687f407ae39a4b35 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 16 Aug 2013 13:32:38 -0400 Subject: [PATCH 048/703] Updated to 1.8.8 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7d1d1864..47717977 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmate, 1.8.7) +AC_INIT(tmate, 1.8.8) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) From 8a87170c8ac10bfea9c2edfec6f0ece296edd7f3 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 16 Aug 2013 18:31:20 -0400 Subject: [PATCH 049/703] Remove GSSAPI support in libssh --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 78916ecc..46484175 100644 --- a/Makefile.am +++ b/Makefile.am @@ -252,7 +252,7 @@ tmate_LDADD = \ *.c: $(tmate_LDADD) libssh/build/src/libssh.a: - cd libssh/build && cmake .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON + cd libssh/build && cmake .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF +make -C libssh/build ssh_static msgpack/src/.libs/libmsgpackc.a: From bb5634ce20ccf339d71ff6d5b7a4e8d668fd9c96 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 4 Oct 2013 16:34:20 -0400 Subject: [PATCH 050/703] Update libssh --- libssh/CMakeLists.txt | 4 +- libssh/DefineOptions.cmake | 1 + libssh/doc/curve25519-sha256@libssh.org.txt | 119 ++++++++ libssh/include/libssh/callbacks.h | 27 +- libssh/include/libssh/crypto.h | 10 +- libssh/include/libssh/curve25519.h | 46 ++++ libssh/include/libssh/session.h | 3 - libssh/src/CMakeLists.txt | 19 ++ libssh/src/channels.c | 36 ++- libssh/src/client.c | 13 + libssh/src/curve25519.c | 285 ++++++++++++++++++++ libssh/src/dh.c | 13 + libssh/src/kex.c | 20 +- libssh/src/messages.c | 11 + libssh/src/packet_cb.c | 6 + libssh/src/pki.c | 32 ++- libssh/src/server.c | 6 + libssh/src/session.c | 26 +- 18 files changed, 626 insertions(+), 51 deletions(-) create mode 100644 libssh/doc/curve25519-sha256@libssh.org.txt create mode 100644 libssh/include/libssh/curve25519.h create mode 100644 libssh/src/curve25519.c diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt index e783c2b4..2c5c7232 100644 --- a/libssh/CMakeLists.txt +++ b/libssh/CMakeLists.txt @@ -109,7 +109,9 @@ install( # in tree build settings configure_file(libssh-build-tree-settings.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-build-tree-settings.cmake @ONLY) -add_subdirectory(examples) +if (WITH_EXAMPLES) + add_subdirectory(examples) +endif (WITH_EXAMPLES) if (WITH_TESTING) find_package(CMocka REQUIRED) diff --git a/libssh/DefineOptions.cmake b/libssh/DefineOptions.cmake index 6913f040..756b948a 100644 --- a/libssh/DefineOptions.cmake +++ b/libssh/DefineOptions.cmake @@ -12,6 +12,7 @@ option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF) option(WITH_TESTING "Build with unit tests" OFF) option(WITH_CLIENT_TESTING "Build with client tests; requires a running sshd" OFF) option(WITH_BENCHMARKS "Build benchmarks tools" OFF) +option(WITH_EXAMPLES "Build examples" ON) if (WITH_ZLIB) set(WITH_LIBZ ON) diff --git a/libssh/doc/curve25519-sha256@libssh.org.txt b/libssh/doc/curve25519-sha256@libssh.org.txt new file mode 100644 index 00000000..e68dc5e6 --- /dev/null +++ b/libssh/doc/curve25519-sha256@libssh.org.txt @@ -0,0 +1,119 @@ +curve25519-sha256@libssh.org.txt Aris Adamantiadis + 21/9/2013 + +1. Introduction + +This document describes the key exchange methode curve25519-sha256@libssh.org +for SSH version 2 protocol. It is provided as an alternative to the existing +key exchange mechanisms based on either Diffie-Hellman or Elliptic Curve Diffie- +Hellman [RFC5656]. +The reason is the following : During summer of 2013, revelations from ex- +consultant at NSA Edward Snowden gave proof that NSA willingly inserts backdoors +into softwares, hardware components and published standards. While it is still +believed that the mathematics behind ECC cryptography are still sound and solid, +some people (including Bruce Schneier [SCHNEIER]), showed their lack of confidence +in NIST-published curves such as nistp256, nistp384, nistp521, for which constant +parameters (including the generator point) are defined without explanation. It +is also believed that NSA had a word to say in their definition. These curves +are not the most secure or fastest possible for their key sizes [DJB], and +researchers think it is possible that NSA have ways of cracking NIST curves. +It is also interesting to note that SSH belongs to the list of protocols the NSA +claims to be able to eavesdrop. Having a secure replacement would make passive +attacks much harder if such a backdoor exists. + +However an alternative exists in the form of Curve25519. This algorithm has been +proposed in 2006 by DJB [Curve25519]. Its main stengths are its speed, its +constant-time run time (and resistance against side-channel attacks), and its +lack of nebulous hard-coded constants. + +The reference version being used in this document is the one described in +[Curve25519] as implemented in the library NaCl [NaCl]. +This document does not attempts to provide alternatives to the ecdsa-sha1-* +authentication keys. + +2. Key exchange + +The key exchange procedure is very similar to the one described chapter 4 of +[RFC5656]. Public ephemeral keys are transmitted over SSH encapsulated into +standard SSH strings. + +The following is an overview of the key exchange process: + +Client Server +------ ------ +Generate ephemeral key pair. +SSH_MSG_KEX_ECDH_INIT --------> + Verify that client public key + length is 32 bytes. + Generate ephemeral key pair. + Compute shared secret. + Generate and sign exchange hash. + <-------- SSH_MSG_KEX_ECDH_REPLY +Verify that server public key length is 32 bytes. +* Verify host keys belong to server. +Compute shared secret. +Generate exchange hash. +Verify server's signature. + +* Optional but strongly recommanded as this protects against MITM attacks. + +This is implemented using the same messages as described in RFC5656 chapter 4 + +3. Method Name + +The name of this key exchange method is "curve25519-sha256@libssh.org". + +4. Implementation considerations + +The whole method is based on the curve25519 scalar multiplication. In this +method, a private key is a scalar of 256 bits, and a public key is a point +of 256 bits. + +4.1. Private key generation + +A 32 bytes private key should be generated for each new connection, + using a secure PRNG. The following actions must be done on the private key: + mysecret[0] &= 248; + mysecret[31] &= 127; + mysecret[31] |= 64; +In order to keep the key valid. However, many cryptographic libraries will do +this automatically. +It should be noted that, in opposition to NIST curves, no special validation +should be done to ensure the result is a valid and secure private key. + +4.2 Public key generation + +The 32 bytes public key of either a client or a server must be generated using +the 32 bytes private key and a common generator base. This base is defined as 9 +followed by all zeroes: + const unsigned char basepoint[32] = {9}; + +The public key is calculated using the cryptographic scalar multiplication: + const unsigned char privkey[32]; + unsigned char pubkey[32]; + crypto_scalarmult (pubkey, privkey, basepoint); +However some cryptographic libraries may provide a combined function: + crypto_scalarmult_base (pubkey, privkey); + +It should be noted that, in opposition to NIST curves, no special validation +should be done to ensure the received public keys are valid curves point. The +Curve25519 algorithm ensure that every possible public key maps to a valid +ECC Point. + +4.3 Shared secret generation + +The shared secret, k, is defined in SSH specifications to be a big integer. +This number is calculated using the following procedure: + + X is the 32 bytes point obtained by the scalar multiplication of the other + side's public key and the local private key scalar. + + The whole 32 bytes of the number X are then converted into a big integer k. + This conversion follows the network byte order. This step differs from + RFC5656. + +[RFC5656] http://tools.ietf.org/html/rfc5656 +[SCHNEIER] https://www.schneier.com/blog/archives/2013/09/the_nsa_is_brea.html#c1675929 +[DJB] http://cr.yp.to/talks/2013.05.31/slides-dan+tanja-20130531-4x3.pdf +[Curve25519] "Curve25519: new Diffie-Hellman speed records." + http://cr.yp.to/ecdh/curve25519-20060209.pdf \ No newline at end of file diff --git a/libssh/include/libssh/callbacks.h b/libssh/include/libssh/callbacks.h index 7525b73d..131e8229 100644 --- a/libssh/include/libssh/callbacks.h +++ b/libssh/include/libssh/callbacks.h @@ -788,12 +788,17 @@ struct ssh_threads_callbacks_struct { }; /** - * @brief sets the thread callbacks necessary if your program is using - * libssh in a multithreaded fashion. This function must be called first, - * outside of any threading context (in your main() for instance), before - * ssh_init(). - * @param cb pointer to a ssh_threads_callbacks_struct structure, which contains - * the different callbacks to be set. + * @brief Set the thread callbacks structure. + * + * This is necessary if your program is using libssh in a multithreaded fashion. + * This function must be called first, outside of any threading context (in your + * main() function for instance), before you call ssh_init(). + * + * @param[in] cb A pointer to a ssh_threads_callbacks_struct structure, which + * contains the different callbacks to be set. + * + * @returns Always returns SSH_OK. + * * @see ssh_threads_callbacks_struct * @see SSH_THREADS_PTHREAD */ @@ -809,9 +814,13 @@ LIBSSH_API int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void); /** - * @brief returns a pointer on the noop threads callbacks, to be used with - * ssh_threads_set_callbacks. These callbacks do nothing and are being used by - * default. + * @brief Get the noop threads callbacks structure + * + * This can be used with ssh_threads_set_callbacks. These callbacks do nothing + * and are being used by default. + * + * @return Always returns a valid pointer to the noop callbacks structure. + * * @see ssh_threads_set_callbacks */ LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void); diff --git a/libssh/include/libssh/crypto.h b/libssh/include/libssh/crypto.h index 5376ca61..eaff2ffd 100644 --- a/libssh/include/libssh/crypto.h +++ b/libssh/include/libssh/crypto.h @@ -44,6 +44,7 @@ #endif #include "libssh/ecdh.h" #include "libssh/kex.h" +#include "libssh/curve25519.h" enum ssh_key_exchange_e { /* diffie-hellman-group1-sha1 */ @@ -51,7 +52,9 @@ enum ssh_key_exchange_e { /* diffie-hellman-group14-sha1 */ SSH_KEX_DH_GROUP14_SHA1, /* ecdh-sha2-nistp256 */ - SSH_KEX_ECDH_SHA2_NISTP256 + SSH_KEX_ECDH_SHA2_NISTP256, + /* curve25519-sha256@libssh.org */ + SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG }; struct ssh_crypto_struct { @@ -60,6 +63,11 @@ struct ssh_crypto_struct { EC_KEY *ecdh_privkey; ssh_string ecdh_client_pubkey; ssh_string ecdh_server_pubkey; +#endif +#ifdef HAVE_CURVE25519 + ssh_curve25519_privkey curve25519_privkey; + ssh_curve25519_pubkey curve25519_client_pubkey; + ssh_curve25519_pubkey curve25519_server_pubkey; #endif ssh_string dh_server_signature; /* information used by dh_handshake. */ size_t digest_len; /* len of all the fields below */ diff --git a/libssh/include/libssh/curve25519.h b/libssh/include/libssh/curve25519.h new file mode 100644 index 00000000..004210cb --- /dev/null +++ b/libssh/include/libssh/curve25519.h @@ -0,0 +1,46 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CURVE25519_H_ +#define CURVE25519_H_ + +#include "config.h" +#include "libssh.h" + +#ifdef WITH_NACL + +#define HAVE_CURVE25519 +#include +#define CURVE25519_PUBKEY_SIZE crypto_scalarmult_curve25519_BYTES +#define CURVE25519_PRIVKEY_SIZE crypto_scalarmult_curve25519_SCALARBYTES + +typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE]; +typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE]; + +#endif /* WITH_NACL */ + +int ssh_client_curve25519_init(ssh_session session); +int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet); + +#ifdef WITH_SERVER +int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet); +#endif /* WITH_SERVER */ + +#endif /* CURVE25519_H_ */ diff --git a/libssh/include/libssh/session.h b/libssh/include/libssh/session.h index f0d580dc..281c7c66 100644 --- a/libssh/include/libssh/session.h +++ b/libssh/include/libssh/session.h @@ -97,9 +97,6 @@ struct ssh_session_struct { int openssh; uint32_t send_seq; uint32_t recv_seq; -/* status flags */ - int closed; - int closed_by_except; int connected; /* !=0 when the user got a session handle */ diff --git a/libssh/src/CMakeLists.txt b/libssh/src/CMakeLists.txt index 06b239fa..b4046805 100644 --- a/libssh/src/CMakeLists.txt +++ b/libssh/src/CMakeLists.txt @@ -76,6 +76,18 @@ if (WITH_GSSAPI AND GSSAPI_FOUND) ) endif (WITH_GSSAPI AND GSSAPI_FOUND) +if (WITH_NACL AND NACL_FOUND) + set(LIBSSH_PRIVATE_INCLUDE_DIRS + ${LIBSSH_PRIVATE_INCLUDE_DIRS} + ${NACL_INCLUDE_DIR} + ) + + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + ${NACL_LIBRARY} + ) +endif (WITH_NACL AND NACL_FOUND) + set(LIBSSH_LINK_LIBRARIES ${LIBSSH_LINK_LIBRARIES} CACHE INTERNAL "libssh link libraries" @@ -192,6 +204,13 @@ if (WITH_GSSAPI AND GSSAPI_FOUND) ) endif (WITH_GSSAPI AND GSSAPI_FOUND) +if (WITH_NACL) + set(libssh_SRCS + ${libssh_SRCS} + curve25519.c + ) +endif (WITH_NACL) + include_directories( ${LIBSSH_PUBLIC_INCLUDE_DIRS} ${LIBSSH_PRIVATE_INCLUDE_DIRS} diff --git a/libssh/src/channels.c b/libssh/src/channels.c index f4376e83..81b2bdfb 100644 --- a/libssh/src/channels.c +++ b/libssh/src/channels.c @@ -312,7 +312,11 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, type_c, channel->local_channel); pending: /* wait until channel is opened by server */ - err = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, ssh_channel_open_termination, channel); + err = ssh_handle_packets_termination(session, + SSH_TIMEOUT_DEFAULT, + ssh_channel_open_termination, + channel); + if (session->session_state == SSH_SESSION_STATE_ERROR) err = SSH_ERROR; end: @@ -1607,7 +1611,11 @@ static int channel_request(ssh_channel channel, const char *request, return SSH_OK; } pending: - rc = ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, ssh_channel_request_termination, channel); + rc = ssh_handle_packets_termination(session, + SSH_TIMEOUT_DEFAULT, + ssh_channel_request_termination, + channel); + if(session->session_state == SSH_SESSION_STATE_ERROR || rc == SSH_ERROR) { channel->request_state = SSH_CHANNEL_REQ_STATE_ERROR; } @@ -1983,8 +1991,11 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, struct ssh_iterator *iterator; int t; - for (t = timeout_ms; t >= 0; t -= 50) - { + /* + * We sleep for 50 ms in ssh_handle_packets() and later sleep for + * 50 ms. So we need to decrement by 100 ms. + */ + for (t = timeout_ms; t >= 0; t -= 100) { ssh_handle_packets(session, 50); if (session->ssh_message_list) { @@ -2145,8 +2156,11 @@ static int global_request(ssh_session session, const char *request, return SSH_OK; } pending: - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, - ssh_global_request_termination, session); + rc = ssh_handle_packets_termination(session, + SSH_TIMEOUT_DEFAULT, + ssh_global_request_termination, + session); + if(rc==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){ session->global_req_state = SSH_CHANNEL_REQ_STATE_ERROR; } @@ -2640,8 +2654,8 @@ static int ssh_channel_read_termination(void *s){ * @param[in] is_stderr A boolean value to mark reading from the stderr flow. * * @return The number of bytes read, 0 on end of file or SSH_ERROR - * on error. Can return 0 if nothing is available in nonblocking - * mode. + * on error. In nonblocking mode it Can return 0 if no data + * is available or SSH_AGAIN. * * @warning This function may return less than count bytes of data, and won't * block until count bytes have been read. @@ -2924,8 +2938,10 @@ int ssh_channel_get_exit_status(ssh_channel channel) { if(channel == NULL) { return SSH_ERROR; } - rc = ssh_handle_packets_termination(channel->session, SSH_TIMEOUT_USER, - ssh_channel_exit_status_termination, channel); + rc = ssh_handle_packets_termination(channel->session, + SSH_TIMEOUT_DEFAULT, + ssh_channel_exit_status_termination, + channel); if (rc == SSH_ERROR || channel->session->session_state == SSH_SESSION_STATE_ERROR) return SSH_ERROR; diff --git a/libssh/src/client.c b/libssh/src/client.c index 99a300b1..dad988ed 100644 --- a/libssh/src/client.c +++ b/libssh/src/client.c @@ -196,6 +196,11 @@ static int dh_handshake(ssh_session session) { case SSH_KEX_ECDH_SHA2_NISTP256: rc = ssh_client_ecdh_init(session); break; +#endif +#ifdef HAVE_CURVE25519 + case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: + rc = ssh_client_curve25519_init(session); + break; #endif default: rc = SSH_ERROR; @@ -579,6 +584,14 @@ char *ssh_get_issue_banner(ssh_session session) { * @param[in] session The SSH session to use. * * @return The version number if available, 0 otherwise. + * + * @code + * int openssh = ssh_get_openssh_version(); + * + * if (openssh == SSH_INT_VERSION(6, 1, 0)) { + * printf("Version match!\m"); + * } + * @endcode */ int ssh_get_openssh_version(ssh_session session) { if (session == NULL) { diff --git a/libssh/src/curve25519.c b/libssh/src/curve25519.c new file mode 100644 index 00000000..653beee0 --- /dev/null +++ b/libssh/src/curve25519.c @@ -0,0 +1,285 @@ +/* + * curve25519.c - Curve25519 ECDH functions for key exchange + * curve25519-sha256@libssh.org + * + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 2.1 of the License. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#include "libssh/curve25519.h" +#ifdef HAVE_CURVE25519 + +#include "nacl/crypto_scalarmult_curve25519.h" +#include "libssh/ssh2.h" +#include "libssh/buffer.h" +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/crypto.h" +#include "libssh/dh.h" +#include "libssh/pki.h" + +/** @internal + * @brief Starts curve25519-sha256@libssh.org key exchange + */ +int ssh_client_curve25519_init(ssh_session session){ + ssh_string client_pubkey; + int rc; + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT); + if (rc < 0) { + return SSH_ERROR; + } + + rc = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); + if (rc == 0){ + ssh_set_error(session, SSH_FATAL, "PRNG error"); + return SSH_ERROR; + } + + crypto_scalarmult_curve25519_base(session->next_crypto->curve25519_client_pubkey, + session->next_crypto->curve25519_privkey); + client_pubkey = ssh_string_new(CURVE25519_PUBKEY_SIZE); + if (client_pubkey == NULL) { + return SSH_ERROR; + } + ssh_string_fill(client_pubkey, session->next_crypto->curve25519_client_pubkey, + CURVE25519_PUBKEY_SIZE); + rc = buffer_add_ssh_string(session->out_buffer,client_pubkey); + if (rc < 0) { + ssh_string_free(client_pubkey); + return SSH_ERROR; + } + + rc = packet_send(session); + + return rc; +} + +static int ssh_curve25519_build_k(ssh_session session) { + ssh_curve25519_pubkey k; + session->next_crypto->k = bignum_new(); + + if (session->next_crypto->k == NULL) { + return SSH_ERROR; + } + + if (session->server) + crypto_scalarmult_curve25519(k, session->next_crypto->curve25519_privkey, + session->next_crypto->curve25519_client_pubkey); + else + crypto_scalarmult_curve25519(k, session->next_crypto->curve25519_privkey, + session->next_crypto->curve25519_server_pubkey); + + BN_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Session server cookie", + session->next_crypto->server_kex.cookie, 16); + ssh_print_hexa("Session client cookie", + session->next_crypto->client_kex.cookie, 16); + ssh_print_bignum("Shared secret key", session->next_crypto->k); +#endif + + return 0; +} + +/** @internal + * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back + * a SSH_MSG_NEWKEYS + */ +int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet){ + ssh_string q_s_string = NULL; + ssh_string pubkey = NULL; + ssh_string signature = NULL; + int rc; + pubkey = buffer_get_ssh_string(packet); + if (pubkey == NULL){ + ssh_set_error(session,SSH_FATAL, "No public key in packet"); + goto error; + } + /* this is the server host key */ + session->next_crypto->server_pubkey = pubkey; + pubkey = NULL; + + q_s_string = buffer_get_ssh_string(packet); + if (q_s_string == NULL) { + ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet"); + goto error; + } + if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){ + ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", + ssh_string_len(q_s_string)); + ssh_string_free(q_s_string); + goto error; + } + memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE); + + signature = buffer_get_ssh_string(packet); + if (signature == NULL) { + ssh_set_error(session, SSH_FATAL, "No signature in packet"); + goto error; + } + session->next_crypto->dh_server_signature = signature; + signature=NULL; /* ownership changed */ + /* TODO: verify signature now instead of waiting for NEWKEYS */ + if (ssh_curve25519_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + goto error; + } + + /* Send the MSG_NEWKEYS */ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { + goto error; + } + + rc=packet_send(session); + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + return rc; +error: + return SSH_ERROR; +} + +#ifdef WITH_SERVER + +/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a + * SSH_MSG_KEXDH_REPLY + */ +int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ + /* ECDH keys */ + ssh_string q_c_string; + ssh_string q_s_string; + + /* SSH host keys (rsa,dsa,ecdsa) */ + ssh_key privkey; + ssh_string sig_blob = NULL; + int rc; + + /* Extract the client pubkey from the init packet */ + q_c_string = buffer_get_ssh_string(packet); + if (q_c_string == NULL) { + ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); + return SSH_ERROR; + } + if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE){ + ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", + ssh_string_len(q_c_string)); + ssh_string_free(q_c_string); + return SSH_ERROR; + } + + memcpy(session->next_crypto->curve25519_client_pubkey, + ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE); + ssh_string_free(q_c_string); + /* Build server's keypair */ + + rc = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); + if (rc == 0){ + ssh_set_error(session, SSH_FATAL, "PRNG error"); + return SSH_ERROR; + } + + crypto_scalarmult_curve25519_base(session->next_crypto->curve25519_server_pubkey, + session->next_crypto->curve25519_privkey); + + q_s_string = ssh_string_new(CURVE25519_PUBKEY_SIZE); + if (q_s_string == NULL) { + return SSH_ERROR; + } + + ssh_string_fill(q_s_string, session->next_crypto->curve25519_server_pubkey, + CURVE25519_PUBKEY_SIZE); + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_REPLY); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + /* build k and session_id */ + rc = ssh_curve25519_build_k(session); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + return SSH_ERROR; + } + + /* privkey is not allocated */ + rc = ssh_get_key_params(session, &privkey); + if (rc == SSH_ERROR) { + return SSH_ERROR; + } + + rc = make_sessionid(session); + if (rc != SSH_OK) { + ssh_set_error(session, SSH_FATAL, "Could not create a session id"); + return SSH_ERROR; + } + + /* add host's public key */ + rc = buffer_add_ssh_string(session->out_buffer, + session->next_crypto->server_pubkey); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + /* add ecdh public key */ + rc = buffer_add_ssh_string(session->out_buffer, q_s_string); + ssh_string_free(q_s_string); + + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + /* add signature blob */ + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + if (sig_blob == NULL) { + ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); + return SSH_ERROR; + } + + rc = buffer_add_ssh_string(session->out_buffer, sig_blob); + ssh_string_free(sig_blob); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_ECDH_REPLY sent"); + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_ERROR; + } + + /* Send the MSG_NEWKEYS */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); + if (rc < 0) { + return SSH_ERROR;; + } + + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + rc = packet_send(session); + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + + return rc; +} + +#endif /* WITH_SERVER */ + +#endif /* HAVE_CURVE25519 */ diff --git a/libssh/src/dh.c b/libssh/src/dh.c index aa01c198..f96a94a3 100644 --- a/libssh/src/dh.c +++ b/libssh/src/dh.c @@ -770,6 +770,18 @@ int make_sessionid(ssh_session session) { if (rc < 0) { goto error; } +#endif +#ifdef HAVE_CURVE25519 + } else if(session->next_crypto->kex_type == SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG){ + rc = buffer_add_u32(buf, htonl(CURVE25519_PUBKEY_SIZE)); + rc += buffer_add_data(buf, session->next_crypto->curve25519_client_pubkey, + CURVE25519_PUBKEY_SIZE); + rc += buffer_add_u32(buf, htonl(CURVE25519_PUBKEY_SIZE)); + rc += buffer_add_data(buf, session->next_crypto->curve25519_server_pubkey, + CURVE25519_PUBKEY_SIZE); + if (rc != SSH_OK) { + goto error; + } #endif } num = make_bignum_string(session->next_crypto->k); @@ -800,6 +812,7 @@ int make_sessionid(ssh_session session) { session->next_crypto->secret_hash); break; case SSH_KEX_ECDH_SHA2_NISTP256: + case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: session->next_crypto->digest_len = SHA256_DIGEST_LENGTH; session->next_crypto->mac_type = SSH_MAC_SHA256; session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); diff --git a/libssh/src/kex.c b/libssh/src/kex.c index 4de5a658..7cd404f7 100644 --- a/libssh/src/kex.c +++ b/libssh/src/kex.c @@ -34,6 +34,7 @@ #include "libssh/session.h" #include "libssh/ssh2.h" #include "libssh/string.h" +#include "libssh/curve25519.h" #ifdef HAVE_LIBGCRYPT # define BLOWFISH "blowfish-cbc," @@ -63,14 +64,21 @@ #define ZLIB "none" #endif -#ifdef HAVE_ECDH -#define KEY_EXCHANGE "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" -#define HOSTKEYS "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss" +#ifdef HAVE_CURVE25519 +#define CURVE25519 "curve25519-sha256@libssh.org," #else -#define KEY_EXCHANGE "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" -#define HOSTKEYS "ssh-rsa,ssh-dss" +#define CURVE25519 "" #endif +#ifdef HAVE_ECDH +#define ECDH "ecdh-sha2-nistp256," +#define HOSTKEYS "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss" +#else +#define HOSTKEYS "ssh-rsa,ssh-dss" +#define ECDH "" +#endif + +#define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" #define KEX_METHODS_SIZE 10 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ @@ -412,6 +420,8 @@ int ssh_kex_select_methods (ssh_session session){ session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1; } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){ session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256; + } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){ + session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG; } return SSH_OK; diff --git a/libssh/src/messages.c b/libssh/src/messages.c index 003ecf8e..73f39974 100644 --- a/libssh/src/messages.c +++ b/libssh/src/messages.c @@ -127,6 +127,17 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg) } return SSH_OK; + } else if (msg->auth_request.method == SSH_AUTH_METHOD_NONE && + ssh_callbacks_exists(session->server_callbacks, auth_none_function)) { + rc = session->server_callbacks->auth_none_function(session, + msg->auth_request.username, session->server_callbacks->userdata); + if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL){ + ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; } break; case SSH_REQUEST_CHANNEL_OPEN: diff --git a/libssh/src/packet_cb.c b/libssh/src/packet_cb.c index 4a8beb54..f5d4f055 100644 --- a/libssh/src/packet_cb.c +++ b/libssh/src/packet_cb.c @@ -35,6 +35,7 @@ #include "libssh/session.h" #include "libssh/socket.h" #include "libssh/ssh2.h" +#include "libssh/curve25519.h" /** * @internal @@ -102,6 +103,11 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_reply){ case SSH_KEX_ECDH_SHA2_NISTP256: rc = ssh_client_ecdh_reply(session, packet); break; +#endif +#ifdef HAVE_CURVE25519 + case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: + rc = ssh_client_curve25519_reply(session, packet); + break; #endif default: ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_dh_reply"); diff --git a/libssh/src/pki.c b/libssh/src/pki.c index dd3d915a..770cfc72 100644 --- a/libssh/src/pki.c +++ b/libssh/src/pki.c @@ -349,8 +349,8 @@ void ssh_signature_free(ssh_signature sig) * * @param[in] auth_data Private data passed to the auth function. * - * @param[out] pkey A pointer where the key can be stored. You need - * to free the memory. + * @param[out] pkey A pointer where the allocated key can be stored. You + * need to free the memory. * * @return SSH_ERROR in case of error, SSH_OK otherwise. * @@ -397,8 +397,8 @@ int ssh_pki_import_privkey_base64(const char *b64_key, * * @param[in] auth_data Private data passed to the auth function. * - * @param[out] pkey A pointer to store the ssh_key. You need to free the - * key. + * @param[out] pkey A pointer to store the allocated ssh_key. You need to + * free the key. * * @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission * denied, SSH_ERROR otherwise. @@ -684,8 +684,8 @@ fail: * * @param[in] type The type of the key to format. * - * @param[out] pkey A pointer where the key can be stored. You need - * to free the memory. + * @param[out] pkey A pointer where the allocated key can be stored. You + * need to free the memory. * * @return SSH_OK on success, SSH_ERROR on error. * @@ -728,8 +728,8 @@ int ssh_pki_import_pubkey_base64(const char *b64_key, * @param[in] key_blob The key blob to import as specified in RFC 4253 section * 6.6 "Public Key Algorithms". * - * @param[out] pkey A pointer where the key can be stored. You need - * to free the memory. + * @param[out] pkey A pointer where the allocated key can be stored. You + * need to free the memory. * * @return SSH_OK on success, SSH_ERROR on error. * @@ -789,8 +789,8 @@ fail: * * @param[in] filename The path to the public key. * - * @param[out] pkey A pointer to store the public key. You need to free the - * memory. + * @param[out] pkey A pointer to store the allocated public key. You need to + * free the memory. * * @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission * denied, SSH_ERROR otherwise. @@ -875,17 +875,20 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey) /** * @brief Generates a keypair. + * * @param[in] type Type of key to create + * * @param[in] parameter Parameter to the creation of key: * rsa : length of the key in bits (e.g. 1024, 2048, 4096) * dsa : length of the key in bits (e.g. 1024, 2048, 3072) * ecdsa : bits of the key (e.g. 256, 384, 512) - * @param[out] pkey A pointer to store the private key. You need to free the - * memory. + * @param[out] pkey A pointer to store the allocated private key. You need + * to free the memory. + * * @return SSH_OK on success, SSH_ERROR on error. + * * @warning Generating a key pair may take some time. */ - int ssh_pki_generate(enum ssh_keytypes_e type, int parameter, ssh_key *pkey){ int rc; @@ -1000,7 +1003,8 @@ int ssh_pki_export_pubkey_blob(const ssh_key key, * * @param[in] key The key to hash * - * @param[out] b64_key A pointer to store the base64 hased key. + * @param[out] b64_key A pointer to store the allocated base64 hashed key. You + * need to free the buffer. * * @return SSH_OK on success, SSH_ERROR on error. * diff --git a/libssh/src/server.c b/libssh/src/server.c index a2104642..b31371fe 100644 --- a/libssh/src/server.c +++ b/libssh/src/server.c @@ -58,6 +58,7 @@ #include "libssh/dh.h" #include "libssh/messages.h" #include "libssh/options.h" +#include "libssh/curve25519.h" #define set_status(session, status) do {\ if (session->common.callbacks && session->common.callbacks->connect_status_function) \ @@ -182,6 +183,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){ case SSH_KEX_ECDH_SHA2_NISTP256: rc = ssh_server_ecdh_init(session, packet); break; + #endif + #ifdef HAVE_CURVE25519 + case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: + rc = ssh_server_curve25519_init(session, packet); + break; #endif default: ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_kexdh_init"); diff --git a/libssh/src/session.c b/libssh/src/session.c index 477e5ce4..fe11b416 100644 --- a/libssh/src/session.c +++ b/libssh/src/session.c @@ -115,6 +115,17 @@ ssh_session ssh_new(void) { goto err; } +#ifdef HAVE_ECC + id = strdup("%d/id_ecdsa"); + if (id == NULL) { + goto err; + } + rc = ssh_list_append(session->opts.identity, id); + if (rc == SSH_ERROR) { + goto err; + } +#endif + id = strdup("%d/id_rsa"); if (id == NULL) { goto err; @@ -268,7 +279,10 @@ void ssh_free(ssh_session session) { /** * @brief get the server banner + * * @param[in] session The SSH session + * + * @return Returns the server banner string or NULL. */ const char* ssh_get_serverbanner(ssh_session session) { if(!session) { @@ -300,8 +314,6 @@ void ssh_silent_disconnect(ssh_session session) { * @param[in] session The ssh session to change. * * @param[in] blocking Zero for nonblocking mode. - * - * \bug nonblocking code is in development and won't work as expected */ void ssh_set_blocking(ssh_session session, int blocking) { if (session == NULL) { @@ -577,7 +589,7 @@ int ssh_get_status(ssh_session session) { socketstate = ssh_socket_get_status(session->socket); - if (session->closed) { + if (session->session_state == SSH_SESSION_STATE_DISCONNECTED) { r |= SSH_CLOSED; } if (socketstate & SSH_READ_PENDING) { @@ -586,7 +598,8 @@ int ssh_get_status(ssh_session session) { if (socketstate & SSH_WRITE_PENDING) { r |= SSH_WRITE_PENDING; } - if ((session->closed && (socketstate & SSH_CLOSED_ERROR)) || + if ((session->session_state == SSH_SESSION_STATE_DISCONNECTED && + (socketstate & SSH_CLOSED_ERROR)) || session->session_state == SSH_SESSION_STATE_ERROR) { r |= SSH_CLOSED_ERROR; } @@ -610,12 +623,9 @@ const char *ssh_get_disconnect_message(ssh_session session) { return NULL; } - if (!session->closed) { + if (session->session_state != SSH_SESSION_STATE_DISCONNECTED) { ssh_set_error(session, SSH_REQUEST_DENIED, "Connection not closed yet"); - } else if(session->closed_by_except) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "Connection closed by socket error"); } else if(!session->discon_msg) { ssh_set_error(session, SSH_FATAL, "Connection correctly closed but no disconnect message"); From 0d182e707edb19bf16181a01de8d12f8739ec743 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 4 Oct 2013 17:36:46 -0400 Subject: [PATCH 051/703] Allow users to specify their own tmate servers Options: - tmate-server-host - tmate-server-port - tmate-server-dsa-fingerprint - tmate-server-rsa-fingerprint - tmate-server-ecdsa-fingerprint --- Makefile.am | 4 ---- configure.ac | 7 ------- options-table.c | 52 +++++++++++++++++++++++++++++++++++----------- tmate-session.c | 14 ++++++++----- tmate-ssh-client.c | 21 +++++++++++-------- tmate.h | 11 ---------- 6 files changed, 61 insertions(+), 48 deletions(-) diff --git a/Makefile.am b/Makefile.am index 46484175..63b67495 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,10 +25,6 @@ CFLAGS += -Wno-unused-parameter -Wno-unused-variable CFLAGS += -Ilibssh/include/ -Imsgpack/src CFLAGS += -rdynamic # for stack traces -if IS_DEVENV -CFLAGS += -DDEVENV -endif - # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. if IS_GCC diff --git a/configure.ac b/configure.ac index 47717977..878d3674 100644 --- a/configure.ac +++ b/configure.ac @@ -40,13 +40,6 @@ AC_CHECK_HEADERS( ] ) -AC_ARG_ENABLE( - devenv, - AC_HELP_STRING(--enable-devenv, "dev env (localhost, port 2200, no auth checks)"), - found_devenv=$enable_devenv -) -AM_CONDITIONAL(IS_DEVENV, test "x$found_devenv" = xyes) - # Is this a debug build? #found_debug=yes AC_ARG_ENABLE( diff --git a/options-table.c b/options-table.c index 342fd172..30c329c0 100644 --- a/options-table.c +++ b/options-table.c @@ -169,18 +169,6 @@ const struct options_table_entry session_options_table[] = { .default_num = 750 }, - { .name = "tmate-display-time", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 30000 - }, - - { .name = "tmate-identity", - .type = OPTIONS_TABLE_STRING, - .default_str = "" - }, - { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, @@ -463,6 +451,46 @@ const struct options_table_entry session_options_table[] = { .default_str = " -_@" }, + { .name = "tmate-display-time", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 30000 + }, + + { .name = "tmate-identity", + .type = OPTIONS_TABLE_STRING, + .default_str = "" + }, + + { .name = "tmate-server-host", + .type = OPTIONS_TABLE_STRING, + .default_str = "master.tmate.io" + }, + + { .name = "tmate-server-port", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = 65535, + .default_num = 22 + }, + + { .name = "tmate-server-dsa-fingerprint", + .type = OPTIONS_TABLE_STRING, + .default_str = "f5:26:31:c3:8a:78:6e:5c:77:74:0f:41:5b:5f:21:88" + }, + + { .name = "tmate-server-rsa-fingerprint", + .type = OPTIONS_TABLE_STRING, + .default_str = "af:2d:81:c1:fe:49:70:2d:7f:09:a9:d7:4b:32:e3:be" + }, + + { .name = "tmate-server-ecdsa-fingerprint", + .type = OPTIONS_TABLE_STRING, + .default_str = "c7:a1:51:36:d2:bb:35:4b:0a:1a:c0:43:97:74:ea:42" + }, + + { .name = NULL } }; diff --git a/tmate-session.c b/tmate-session.c index c14def66..4f8e77a5 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -29,10 +29,11 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) struct tmate_ssh_client *client; struct evutil_addrinfo *ai; struct timeval tv; + const char *host = ptr; if (errcode) { tmate_status_message("%s lookup failure. Retrying in %d seconds (%s)", - TMATE_HOST, TMATE_DNS_RETRY_TIMEOUT, + host, TMATE_DNS_RETRY_TIMEOUT, evutil_gai_strerror(errcode)); tv.tv_sec = TMATE_DNS_RETRY_TIMEOUT; @@ -44,7 +45,7 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) return; } - tmate_status_message("Connecting to %s...", TMATE_HOST); + tmate_status_message("Connecting to %s...", host); for (ai = addr; ai; ai = ai->ai_next) { char buf[128]; @@ -79,6 +80,7 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) static void lookup_and_connect(void) { struct evutil_addrinfo hints; + const char *tmate_server_host; if (!ev_dnsbase) ev_dnsbase = evdns_base_new(ev_base, 1); @@ -91,9 +93,11 @@ static void lookup_and_connect(void) hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - tmate_info("Looking up %s...", TMATE_HOST); - (void)evdns_getaddrinfo(ev_dnsbase, TMATE_HOST, NULL, - &hints, dns_cb, NULL); + tmate_server_host = options_get_string(&global_s_options, + "tmate-server-host"); + tmate_info("Looking up %s...", tmate_server_host); + (void)evdns_getaddrinfo(ev_dnsbase, tmate_server_host, NULL, + &hints, dns_cb, tmate_server_host); } void tmate_session_init(void) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index a930e28c..c4ad1de3 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -168,10 +168,11 @@ static void on_session_event(struct tmate_ssh_client *client) unsigned char *hash; ssize_t hash_len; char *hash_str; + char *server_hash_str; int match; int verbosity = SSH_LOG_NOLOG + debug_level; - int port = TMATE_PORT; + int port = options_get_number(&global_s_options, "tmate-server-port"); ssh_session session = client->session; ssh_channel channel = client->channel; @@ -241,24 +242,26 @@ static void on_session_event(struct tmate_ssh_client *client) if (ssh_get_publickey(session, &pubkey) < 0) tmate_fatal("ssh_get_publickey"); -#ifdef DEVENV - match = 1; -#else key_type = ssh_key_type(pubkey); + switch (key_type) { case SSH_KEYTYPE_DSS: - match = !strcmp(hash_str, TMATE_HOST_DSA_KEY); + server_hash_str = options_get_string(&global_s_options, + "tmate-server-dsa-fingerprint"); break; case SSH_KEYTYPE_RSA: - match = !strcmp(hash_str, TMATE_HOST_RSA_KEY); + server_hash_str = options_get_string(&global_s_options, + "tmate-server-rsa-fingerprint"); break; case SSH_KEYTYPE_ECDSA: - match = !strcmp(hash_str, TMATE_HOST_ECDSA_KEY); + server_hash_str = options_get_string(&global_s_options, + "tmate-server-ecdsa-fingerprint"); break; default: - match = 0; + server_hash_str = ""; } -#endif + + match = !strcmp(hash_str, server_hash_str); ssh_key_free(pubkey); ssh_clean_pubkey_hash(&hash); diff --git a/tmate.h b/tmate.h index 673f3a6d..ab202cf7 100644 --- a/tmate.h +++ b/tmate.h @@ -72,17 +72,6 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); /* tmate-ssh-client.c */ -#ifdef DEVENV -#define TMATE_HOST "localhost" -#define TMATE_PORT 2200 -#else -#define TMATE_HOST "master.tmate.io" -#define TMATE_PORT 22 -#define TMATE_HOST_DSA_KEY "f5:26:31:c3:8a:78:6e:5c:77:74:0f:41:5b:5f:21:88" -#define TMATE_HOST_RSA_KEY "af:2d:81:c1:fe:49:70:2d:7f:09:a9:d7:4b:32:e3:be" -#define TMATE_HOST_ECDSA_KEY "c7:a1:51:36:d2:bb:35:4b:0a:1a:c0:43:97:74:ea:42" -#endif - enum tmate_ssh_client_state_types { SSH_NONE, SSH_INIT, From ed4020b5890cdccf930e82a61a8ce264404235fb Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 4 Oct 2013 17:50:55 -0400 Subject: [PATCH 052/703] Use ~/.tmate.conf to load tmate specific settings Closes #10 --- server.c | 6 ++++++ tmux.c | 21 ++++++++++++++------- tmux.h | 3 ++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/server.c b/server.c index 6620c78b..739386ee 100644 --- a/server.c +++ b/server.c @@ -186,6 +186,12 @@ server_start(int lockfd, char *lockfile) ARRAY_ADD(&cfg_causes, cause); } } + if (tmate_cfg_file != NULL) { + if (load_cfg(tmate_cfg_file, cfg_cmd_q, &cause) == -1) { + xasprintf(&cause, "%s: %s", tmate_cfg_file, cause); + ARRAY_ADD(&cfg_causes, cause); + } + } tmate_session_init(); cmdq_continue(cfg_cmd_q); diff --git a/tmux.c b/tmux.c index 7063d912..b1b4f525 100644 --- a/tmux.c +++ b/tmux.c @@ -40,7 +40,7 @@ struct environ global_environ; struct event_base *ev_base; -char *cfg_file; +char *cfg_file, *tmate_cfg_file; char *shell_cmd; int debug_level; time_t start_time; @@ -367,13 +367,14 @@ main(int argc, char **argv) } /* Locate the configuration file. */ + home = getenv("HOME"); + if (home == NULL || *home == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + home = pw->pw_dir; + } + if (cfg_file == NULL) { - home = getenv("HOME"); - if (home == NULL || *home == '\0') { - pw = getpwuid(getuid()); - if (pw != NULL) - home = pw->pw_dir; - } xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { free(cfg_file); @@ -381,6 +382,12 @@ main(int argc, char **argv) } } + xasprintf(&tmate_cfg_file, "%s/%s", home, DEFAULT_TMATE_CFG); + if (access(tmate_cfg_file, R_OK) != 0 && errno == ENOENT) { + free(tmate_cfg_file); + tmate_cfg_file = NULL; + } + /* * Figure out the socket path. If specified on the command-line with -S * or -L, use it, otherwise try $TMUX or assume -L default. diff --git a/tmux.h b/tmux.h index 94858f6d..fa335c6a 100644 --- a/tmux.h +++ b/tmux.h @@ -43,6 +43,7 @@ extern char **environ; /* Default configuration files. */ #define DEFAULT_CFG ".tmux.conf" +#define DEFAULT_TMATE_CFG ".tmate.conf" #define SYSTEM_CFG "/etc/tmux.conf" /* Default prompt history length. */ @@ -1513,7 +1514,7 @@ extern struct options global_s_options; extern struct options global_w_options; extern struct environ global_environ; extern struct event_base *ev_base; -extern char *cfg_file; +extern char *cfg_file, *tmate_cfg_file; extern char *shell_cmd; extern int debug_level; extern time_t start_time; From 3ce7122aacf3dce4528de311dadfb1bc22f473bc Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 4 Oct 2013 17:53:02 -0400 Subject: [PATCH 053/703] Upgrade to 1.8.9 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 878d3674..fcc4ded1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmate, 1.8.8) +AC_INIT(tmate, 1.8.9) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) From a37b7d1ae57e4ee2b9861534c7c8d87c00676179 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 6 Nov 2013 19:08:45 -0500 Subject: [PATCH 054/703] Fix makefile --- Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 63b67495..e8b35802 100644 --- a/Makefile.am +++ b/Makefile.am @@ -248,9 +248,11 @@ tmate_LDADD = \ *.c: $(tmate_LDADD) libssh/build/src/libssh.a: - cd libssh/build && cmake .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF + cd libssh/build; ([ -f Makefile ] || cmake .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF) +make -C libssh/build ssh_static msgpack/src/.libs/libmsgpackc.a: - cd msgpack && ./bootstrap && ./configure + cd msgpack; ([ -f Makefile ] || (./bootstrap && ./configure)) +make -C msgpack/src libmsgpackc.la + +.PHONY: libssh/build/src/libssh.a msgpack/src/.libs/libmsgpackc.a From 92bf230b8ac78a5497464936e1da7be17a23f742 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 6 Nov 2013 19:07:47 -0500 Subject: [PATCH 055/703] Can't use the legacy logging callbacks of libssh, it's broken --- tmate-session.c | 7 +++++++ tmate-ssh-client.c | 8 -------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/tmate-session.c b/tmate-session.c index 4f8e77a5..40ac2d7a 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -100,8 +100,15 @@ static void lookup_and_connect(void) &hints, dns_cb, tmate_server_host); } +static void ssh_log_function(int priority, const char *function, + const char *buffer, void *userdata) +{ + tmate_debug("[%d] [%s] %s", priority, function, buffer); +} + void tmate_session_init(void) { + ssh_set_log_callback(ssh_log_function); tmate_catch_sigsegv(); tmate_encoder_init(&tmate_session.encoder); diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index c4ad1de3..1c346e85 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -16,13 +16,6 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client, const char *fmt, ...); static void on_session_event(struct tmate_ssh_client *client); -static void log_function(ssh_session session, int priority, - const char *message, void *userdata) -{ - struct tmate_ssh_client *client = userdata; - tmate_debug("[%s] [%d] %s", client->server_ip, priority, message); -} - static void register_session_fd_event(struct tmate_ssh_client *client) { if (!event_initialized(&client->ev_ssh)) { @@ -479,7 +472,6 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks)); ssh_callbacks_init(&client->ssh_callbacks); - client->ssh_callbacks.log_function = log_function; client->ssh_callbacks.userdata = client; client->ssh_callbacks.auth_function = passphrase_callback; From 2535b531bb73cd8c99e5bf9726d8b645108ddd02 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 6 Nov 2013 19:40:47 -0500 Subject: [PATCH 056/703] Update libssh --- libssh/CMakeLists.txt | 8 + libssh/ConfigureChecks.cmake | 2 + libssh/DefineOptions.cmake | 6 +- .../Modules/DefinePlatformDefaults.cmake | 4 + libssh/config.h.cmake | 10 +- libssh/doc/doxy.config.in | 2 +- libssh/doc/mainpage.dox | 10 +- libssh/doc/sftp.dox | 120 +++++---- libssh/examples/CMakeLists.txt | 6 +- libssh/examples/knownhosts.c | 20 +- libssh/examples/samplesshd-tty.c | 6 +- libssh/include/libssh/callbacks.h | 8 +- libssh/include/libssh/curve25519.h | 12 +- libssh/include/libssh/libcrypto.h | 5 + libssh/include/libssh/libgcrypt.h | 1 + libssh/include/libssh/libssh.h | 20 +- libssh/include/libssh/pki.h | 1 + libssh/include/libssh/priv.h | 18 +- libssh/include/libssh/wrapper.h | 3 + libssh/src/CMakeLists.txt | 7 +- libssh/src/auth.c | 12 +- libssh/src/bind.c | 3 +- libssh/src/channels.c | 86 ++++--- libssh/src/client.c | 24 +- libssh/src/connect.c | 11 +- libssh/src/curve25519.c | 15 +- libssh/src/dh.c | 235 ++++++++++++------ libssh/src/libcrypto.c | 24 ++ libssh/src/log.c | 2 +- libssh/src/messages.c | 18 +- libssh/src/options.c | 11 +- libssh/src/pki.c | 50 ++-- libssh/src/pki_crypto.c | 53 ++-- libssh/src/poll.c | 6 +- libssh/src/scp.c | 2 +- libssh/src/server.c | 41 +-- libssh/src/session.c | 2 +- libssh/src/socket.c | 6 +- libssh/src/wrapper.c | 18 +- libssh/tests/client/CMakeLists.txt | 3 + libssh/tests/unittests/CMakeLists.txt | 2 + 41 files changed, 615 insertions(+), 278 deletions(-) diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt index 2c5c7232..5998bc50 100644 --- a/libssh/CMakeLists.txt +++ b/libssh/CMakeLists.txt @@ -71,6 +71,13 @@ if (WITH_GSSAPI) find_package(GSSAPI) endif (WITH_GSSAPI) +if (WITH_NACL) + find_package(NaCl) + if (NOT NACL_FOUND) + set(WITH_NACL OFF) + endif (NOT NACL_FOUND) +endif (WITH_NACL) + # config.h checks include(ConfigureChecks.cmake) configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) @@ -125,6 +132,7 @@ message(STATUS "********** ${PROJECT_NAME} build options : **********") message(STATUS "zlib support: ${WITH_ZLIB}") message(STATUS "libgcrypt support: ${WITH_GCRYPT}") +message(STATUS "libnacl support: ${WITH_NACL}") message(STATUS "SSH-1 support: ${WITH_SSH1}") message(STATUS "SFTP support: ${WITH_SFTP}") message(STATUS "Server support : ${WITH_SERVER}") diff --git a/libssh/ConfigureChecks.cmake b/libssh/ConfigureChecks.cmake index 1c89c4c7..472fe79e 100644 --- a/libssh/ConfigureChecks.cmake +++ b/libssh/ConfigureChecks.cmake @@ -50,6 +50,7 @@ check_include_file(argp.h HAVE_ARGP_H) check_include_file(pty.h HAVE_PTY_H) check_include_file(termios.h HAVE_TERMIOS_H) check_include_file(unistd.h HAVE_UNISTD_H) +check_include_file(util.h HAVE_UTIL_H) if (WIN32) check_include_files("winsock2.h;ws2tcpip.h;wspiapi.h" HAVE_WSPIAPI_H) @@ -93,6 +94,7 @@ endif (NOT WITH_GCRYPT) # FUNCTIONS +check_function_exists(isblank HAVE_ISBLANK) check_function_exists(strncpy HAVE_STRNCPY) check_function_exists(vsnprintf HAVE_VSNPRINTF) check_function_exists(snprintf HAVE_SNPRINTF) diff --git a/libssh/DefineOptions.cmake b/libssh/DefineOptions.cmake index 756b948a..ab7819a5 100644 --- a/libssh/DefineOptions.cmake +++ b/libssh/DefineOptions.cmake @@ -13,7 +13,7 @@ option(WITH_TESTING "Build with unit tests" OFF) option(WITH_CLIENT_TESTING "Build with client tests; requires a running sshd" OFF) option(WITH_BENCHMARKS "Build benchmarks tools" OFF) option(WITH_EXAMPLES "Build examples" ON) - +option(WITH_NACL "Build with libnacl (curve25519" ON) if (WITH_ZLIB) set(WITH_LIBZ ON) else (WITH_ZLIB) @@ -27,3 +27,7 @@ endif(WITH_BENCHMARKS) if (WITH_TESTING) set(WITH_STATIC_LIB ON) endif (WITH_TESTING) + +if (WITH_NACL) + set(WITH_NACL ON) +endif (WITH_NACL) \ No newline at end of file diff --git a/libssh/cmake/Modules/DefinePlatformDefaults.cmake b/libssh/cmake/Modules/DefinePlatformDefaults.cmake index 502d936b..77f8a461 100644 --- a/libssh/cmake/Modules/DefinePlatformDefaults.cmake +++ b/libssh/cmake/Modules/DefinePlatformDefaults.cmake @@ -26,3 +26,7 @@ endif (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") if (CMAKE_SYSTEM_NAME MATCHES "OS2") set(OS2 TRUE) endif (CMAKE_SYSTEM_NAME MATCHES "OS2") + +if (CMAKE_SYSTEM_NAME MATCHES "Darwin") + set (OSX TRUE) +endif (CMAKE_SYSTEM_NAME MATCHES "Darwin") diff --git a/libssh/config.h.cmake b/libssh/config.h.cmake index f7f8957f..7e8cb6a8 100644 --- a/libssh/config.h.cmake +++ b/libssh/config.h.cmake @@ -20,6 +20,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PTY_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UTIL_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_TERMIOS_H 1 @@ -79,6 +82,9 @@ /* Define to 1 if you have the `_vsnprintf_s' function. */ #cmakedefine HAVE__VSNPRINTF_S 1 +/* Define to 1 if you have the `isblank' function. */ +#cmakedefine HAVE_ISBLANK 1 + /* Define to 1 if you have the `strncpy' function. */ #cmakedefine HAVE_STRNCPY 1 @@ -123,7 +129,6 @@ /* Define to 1 if you have the `pthread' library (-lpthread). */ #cmakedefine HAVE_PTHREAD 1 - /**************************** OPTIONS ****************************/ #cmakedefine HAVE_GCC_THREAD_LOCAL_STORAGE 1 @@ -155,6 +160,9 @@ /* Define to 1 if you want to enable calltrace debug output */ #cmakedefine DEBUG_CALLTRACE 1 +/* Define to 1 if you want to enable NaCl support */ +#cmakedefine WITH_NACL 1 + /*************************** ENDIAN *****************************/ /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most diff --git a/libssh/doc/doxy.config.in b/libssh/doc/doxy.config.in index 276a2611..4da9b2c9 100644 --- a/libssh/doc/doxy.config.in +++ b/libssh/doc/doxy.config.in @@ -1628,7 +1628,7 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = +PREDEFINED = WITH_SERVER WITH_SFTP WITH_PCAP # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/libssh/doc/mainpage.dox b/libssh/doc/mainpage.dox index 5fb695a6..70f774ad 100644 --- a/libssh/doc/mainpage.dox +++ b/libssh/doc/mainpage.dox @@ -19,7 +19,7 @@ the interesting functions as you go. The libssh library provides: - - Key Exchange Methods: ecdh-sha2-nistp256, diffie-hellman-group1-sha1, diffie-hellman-group14-sha1 + - Key Exchange Methods: curve25519-sha256@libssh.org, ecdh-sha2-nistp256, diffie-hellman-group1-sha1, diffie-hellman-group14-sha1 - Hostkey Types: ecdsa-sha2-nistp256, ssh-dss, ssh-rsa - Ciphers: aes256-ctr, aes192-ctr, aes128-ctr, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, des-cbc-ssh1, blowfish-cbc, none - Compression Schemes: zlib, zlib@openssh.com, none @@ -184,6 +184,8 @@ It was later modified and expanded by the following RFCs. Authentication and Key Exchange for the Secure Shell (SSH) Protocol - RFC 4716, The Secure Shell (SSH) Public Key File Format + - RFC 5647, + AES Galois Counter Mode for the Secure Shell Transport Layer Protocol - RFC 5656, Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer @@ -203,6 +205,12 @@ do the same in libssh. @subsection main-rfc-extensions Secure Shell Extensions +The libssh project has an extension to support Curve25519 which is also supported by +the OpenSSH project. + + - curve25519-sha256@libssh.org, + Curve25519-SHA256 for ECDH KEX + The OpenSSH project has defined some extensions to the protocol. We support some of them like the statvfs calls in SFTP or the ssh-agent. diff --git a/libssh/doc/sftp.dox b/libssh/doc/sftp.dox index 97f9afbb..8b7c7e1a 100644 --- a/libssh/doc/sftp.dox +++ b/libssh/doc/sftp.dox @@ -210,52 +210,63 @@ results to come. Synchronous read is done with sftp_read(). -The following example prints the contents of remote file "/etc/profile". For -each 1024 bytes of information read, it waits until the end of the read operation: +Files are normally transferred in chunks. A good chunk size is 16 KB. The following +example transfers the remote file "/etc/profile" in 16 KB chunks. For each chunk we +request, sftp_read blocks till the data has been received: @code +// Good chunk size +#define MAX_XFER_BUF_SIZE 16384 + int sftp_read_sync(ssh_session session, sftp_session sftp) { int access_type; sftp_file file; - char buffer[1024]; - int nbytes, rc; + char buffer[MAX_XFER_BUF_SIZE]; + int nbytes, nwritten, rc; + int fd; access_type = O_RDONLY; file = sftp_open(sftp, "/etc/profile", access_type, 0); - if (file == NULL) - { - fprintf(stderr, "Can't open file for reading: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - nbytes = sftp_read(file, buffer, sizeof(buffer)); - while (nbytes > 0) - { - if (write(1, buffer, nbytes) != nbytes) - { - sftp_close(file); + if (file == NULL) { + fprintf(stderr, "Can't open file for reading: %s\n", + ssh_get_error(session)); return SSH_ERROR; - } - nbytes = sftp_read(file, buffer, sizeof(buffer)); } - if (nbytes < 0) - { - fprintf(stderr, "Error while reading file: %s\n", - ssh_get_error(session)); - sftp_close(file); - return SSH_ERROR; + fd = open("/path/to/profile", O_CREAT); + if (fd < 0) { + fprintf(stderr, "Can't open file for writing: %s\n", + strerror(errno)); + return SSH_ERROR; + } + + for (;;) { + nbytes = sftp_read(file, buffer, sizeof(buffer)); + if (nbytes == 0) { + break; // EOF + } else if (nbytes < 0) { + fprintf(stderr, "Error while reading file: %s\n", + ssh_get_error(session)); + sftp_close(file); + return SSH_ERROR; + } + + nwritten = write(fd, buf, nbytes); + if (nwritten != nbytes) { + fprintf(stderr, "Error writing: %s\n", + strerror(errno)); + sftp_close(file); + return SSH_ERROR; + } } rc = sftp_close(file); - if (rc != SSH_OK) - { - fprintf(stderr, "Can't close the read file: %s\n", - ssh_get_error(session)); - return rc; + if (rc != SSH_OK) { + fprintf(stderr, "Can't close the read file: %s\n", + ssh_get_error(session)); + return rc; } return SSH_OK; @@ -274,11 +285,14 @@ The example below reads a very big file in asynchronous, nonblocking, mode. Each time the data are not ready yet, a counter is incrementer. @code +// Good chunk size +#define MAX_XFER_BUF_SIZE 16384 + int sftp_read_async(ssh_session session, sftp_session sftp) { int access_type; sftp_file file; - char buffer[1024]; + char buffer[MAX_XFER_BUF_SIZE]; int async_request; int nbytes; long counter; @@ -287,8 +301,7 @@ int sftp_read_async(ssh_session session, sftp_session sftp) access_type = O_RDONLY; file = sftp_open(sftp, "some_very_big_file", access_type, 0); - if (file == NULL) - { + if (file == NULL) { fprintf(stderr, "Can't open file for reading: %s\n", ssh_get_error(session)); return SSH_ERROR; @@ -298,27 +311,31 @@ int sftp_read_async(ssh_session session, sftp_session sftp) async_request = sftp_async_read_begin(file, sizeof(buffer)); counter = 0L; usleep(10000); - if (async_request >= 0) + if (async_request >= 0) { nbytes = sftp_async_read(file, buffer, sizeof(buffer), async_request); - else nbytes = -1; - while (nbytes > 0 || nbytes == SSH_AGAIN) - { - if (nbytes > 0) - { - write(1, buffer, nbytes); - async_request = sftp_async_read_begin(file, sizeof(buffer)); - } - else counter++; - usleep(10000); - if (async_request >= 0) - nbytes = sftp_async_read(file, buffer, sizeof(buffer), - async_request); - else nbytes = -1; + } else { + nbytes = -1; } - if (nbytes < 0) - { + while (nbytes > 0 || nbytes == SSH_AGAIN) { + if (nbytes > 0) { + write(1, buffer, nbytes); + async_request = sftp_async_read_begin(file, sizeof(buffer)); + } else { + counter++; + } + usleep(10000); + + if (async_request >= 0) { + nbytes = sftp_async_read(file, buffer, sizeof(buffer), + async_request); + } else { + nbytes = -1; + } + } + + if (nbytes < 0) { fprintf(stderr, "Error while reading file: %s\n", ssh_get_error(session)); sftp_close(file); @@ -328,8 +345,7 @@ int sftp_read_async(ssh_session session, sftp_session sftp) printf("The counter has reached value: %ld\n", counter); rc = sftp_close(file); - if (rc != SSH_OK) - { + if (rc != SSH_OK) { fprintf(stderr, "Can't close the read file: %s\n", ssh_get_error(session)); return rc; diff --git a/libssh/examples/CMakeLists.txt b/libssh/examples/CMakeLists.txt index fc1c9341..c155e097 100644 --- a/libssh/examples/CMakeLists.txt +++ b/libssh/examples/CMakeLists.txt @@ -11,9 +11,9 @@ include_directories( ${CMAKE_BINARY_DIR} ) -if (BSD OR SOLARIS) +if (BSD OR SOLARIS OR OSX) find_package(Argp) -endif (BSD OR SOLARIS) +endif (BSD OR SOLARIS OR OSX) if (UNIX AND NOT WIN32) add_executable(libssh_scp libssh_scp.c ${examples_SRCS}) @@ -28,7 +28,7 @@ if (UNIX AND NOT WIN32) if (WITH_SERVER) if (HAVE_LIBUTIL) add_executable(samplesshd-tty samplesshd-tty.c) - target_link_libraries(samplesshd-tty ${LIBSSH_SHARED_LIBRARY} util) + target_link_libraries(samplesshd-tty ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES} util) endif (HAVE_LIBUTIL) endif (WITH_SERVER) diff --git a/libssh/examples/knownhosts.c b/libssh/examples/knownhosts.c index 37c0ba4e..5097cd93 100644 --- a/libssh/examples/knownhosts.c +++ b/libssh/examples/knownhosts.c @@ -34,14 +34,26 @@ int verify_knownhost(ssh_session session){ int state; char buf[10]; unsigned char *hash = NULL; - int hlen; + size_t hlen; + ssh_key srv_pubkey; + int rc; state=ssh_is_server_known(session); - hlen = ssh_get_pubkey_hash(session, &hash); - if (hlen < 0) { - return -1; + rc = ssh_get_publickey(session, &srv_pubkey); + if (rc < 0) { + return -1; } + + rc = ssh_get_publickey_hash(srv_pubkey, + SSH_PUBLICKEY_HASH_SHA1, + &hash, + &hlen); + ssh_key_free(srv_pubkey); + if (rc < 0) { + return -1; + } + switch(state){ case SSH_SERVER_KNOWN_OK: break; /* ok */ diff --git a/libssh/examples/samplesshd-tty.c b/libssh/examples/samplesshd-tty.c index 7ed70d3d..83b75648 100644 --- a/libssh/examples/samplesshd-tty.c +++ b/libssh/examples/samplesshd-tty.c @@ -25,8 +25,12 @@ clients must be made or how a client should react. #include #include #include +#ifdef HAVE_PTY_H #include - +#endif +#ifdef HAVE_UTIL_H +#include +#endif #define SSHD_USER "libssh" #define SSHD_PASSWORD "libssh" diff --git a/libssh/include/libssh/callbacks.h b/libssh/include/libssh/callbacks.h index 131e8229..a841f2e5 100644 --- a/libssh/include/libssh/callbacks.h +++ b/libssh/include/libssh/callbacks.h @@ -166,7 +166,7 @@ typedef struct ssh_callbacks_struct *ssh_callbacks; * @param user User that wants to authenticate * @param password Password used for authentication * @param userdata Userdata to be passed to the callback function. - * @returns SSH_AUTH_OK Authentication is accepted. + * @returns SSH_AUTH_SUCCESS Authentication is accepted. * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. * @returns SSH_AUTH_DENIED Authentication failed. */ @@ -179,7 +179,7 @@ typedef int (*ssh_auth_password_callback) (ssh_session session, const char *user * @param session Current session handler * @param user User that wants to authenticate * @param userdata Userdata to be passed to the callback function. - * @returns SSH_AUTH_OK Authentication is accepted. + * @returns SSH_AUTH_SUCCESS Authentication is accepted. * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. * @returns SSH_AUTH_DENIED Authentication failed. */ @@ -191,7 +191,7 @@ typedef int (*ssh_auth_none_callback) (ssh_session session, const char *user, vo * @param user Username of the user (can be spoofed) * @param principal Authenticated principal of the user, including realm. * @param userdata Userdata to be passed to the callback function. - * @returns SSH_AUTH_OK Authentication is accepted. + * @returns SSH_AUTH_SUCCESS Authentication is accepted. * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. * @returns SSH_AUTH_DENIED Authentication failed. * @warning Implementations should verify that parameter user matches in some way the principal. @@ -209,7 +209,7 @@ typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *us * SSH_PUBLICKEY_STATE_VALID if the signature is valid. Others values should be * replied with a SSH_AUTH_DENIED. * @param userdata Userdata to be passed to the callback function. - * @returns SSH_AUTH_OK Authentication is accepted. + * @returns SSH_AUTH_SUCCESS Authentication is accepted. * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. * @returns SSH_AUTH_DENIED Authentication failed. */ diff --git a/libssh/include/libssh/curve25519.h b/libssh/include/libssh/curve25519.h index 004210cb..35e25be0 100644 --- a/libssh/include/libssh/curve25519.h +++ b/libssh/include/libssh/curve25519.h @@ -26,15 +26,23 @@ #ifdef WITH_NACL -#define HAVE_CURVE25519 #include #define CURVE25519_PUBKEY_SIZE crypto_scalarmult_curve25519_BYTES #define CURVE25519_PRIVKEY_SIZE crypto_scalarmult_curve25519_SCALARBYTES +#define crypto_scalarmult_base crypto_scalarmult_curve25519_base +#define crypto_scalarmult crypto_scalarmult_curve25519 +#else +#define CURVE25519_PUBKEY_SIZE 32 +#define CURVE25519_PRIVKEY_SIZE 32 +int crypto_scalarmult_base(unsigned char *q, const unsigned char *n); +int crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned char *p); +#endif /* WITH_NACL */ + +#define HAVE_CURVE25519 typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE]; typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE]; -#endif /* WITH_NACL */ int ssh_client_curve25519_init(ssh_session session); int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet); diff --git a/libssh/include/libssh/libcrypto.h b/libssh/include/libssh/libcrypto.h index 54c78b16..5cf2da28 100644 --- a/libssh/include/libssh/libcrypto.h +++ b/libssh/include/libssh/libcrypto.h @@ -38,6 +38,11 @@ typedef SHA_CTX* SHACTX; typedef SHA256_CTX* SHA256CTX; typedef MD5_CTX* MD5CTX; typedef HMAC_CTX* HMACCTX; +#ifdef HAVE_ECC +typedef EVP_MD_CTX *EVPCTX; +#else +typedef void *EVPCTX; +#endif #define SHA_DIGEST_LEN SHA_DIGEST_LENGTH #ifdef MD5_DIGEST_LEN diff --git a/libssh/include/libssh/libgcrypt.h b/libssh/include/libssh/libgcrypt.h index 54982063..c1844a53 100644 --- a/libssh/include/libssh/libgcrypt.h +++ b/libssh/include/libssh/libgcrypt.h @@ -29,6 +29,7 @@ typedef gcry_md_hd_t SHACTX; typedef gcry_md_hd_t MD5CTX; typedef gcry_md_hd_t HMACCTX; +typedef void *EVPCTX; #define SHA_DIGEST_LENGTH 20 #define SHA_DIGEST_LEN SHA_DIGEST_LENGTH #define MD5_DIGEST_LEN 16 diff --git a/libssh/include/libssh/libssh.h b/libssh/include/libssh/libssh.h index a451620e..3833adcd 100644 --- a/libssh/include/libssh/libssh.h +++ b/libssh/include/libssh/libssh.h @@ -208,10 +208,14 @@ enum ssh_publickey_state_e { SSH_PUBLICKEY_STATE_WRONG=2 }; -/* status flags */ +/* Status flags */ +/** Socket is closed */ #define SSH_CLOSED 0x01 +/** Reading to socket won't block */ #define SSH_READ_PENDING 0x02 +/** Session was closed due to an error */ #define SSH_CLOSED_ERROR 0x04 +/** Output buffer not empty */ #define SSH_WRITE_PENDING 0x08 enum ssh_server_known_e { @@ -408,8 +412,20 @@ LIBSSH_API socket_t ssh_get_fd(ssh_session session); LIBSSH_API char *ssh_get_hexa(const unsigned char *what, size_t len); LIBSSH_API char *ssh_get_issue_banner(ssh_session session); LIBSSH_API int ssh_get_openssh_version(ssh_session session); + LIBSSH_API int ssh_get_publickey(ssh_session session, ssh_key *key); -LIBSSH_API int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash); + +enum ssh_publickey_hash_type { + SSH_PUBLICKEY_HASH_SHA1, + SSH_PUBLICKEY_HASH_MD5 +}; +LIBSSH_API int ssh_get_publickey_hash(const ssh_key key, + enum ssh_publickey_hash_type type, + unsigned char **hash, + size_t *hlen); + +SSH_DEPRECATED LIBSSH_API int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash); + LIBSSH_API int ssh_get_random(void *where,int len,int strong); LIBSSH_API int ssh_get_version(ssh_session session); LIBSSH_API int ssh_get_status(ssh_session session); diff --git a/libssh/include/libssh/pki.h b/libssh/include/libssh/pki.h index 96bacd52..89a0f982 100644 --- a/libssh/include/libssh/pki.h +++ b/libssh/include/libssh/pki.h @@ -60,6 +60,7 @@ struct ssh_key_struct { struct ssh_signature_struct { enum ssh_keytypes_e type; + const char *type_c; #ifdef HAVE_LIBGCRYPT gcry_sexp_t dsa_sig; gcry_sexp_t rsa_sig; diff --git a/libssh/include/libssh/priv.h b/libssh/include/libssh/priv.h index 43f749bb..d8176d90 100644 --- a/libssh/include/libssh/priv.h +++ b/libssh/include/libssh/priv.h @@ -67,7 +67,9 @@ # define strcasecmp _stricmp # define strncasecmp _strnicmp -# define isblank(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n' || (ch) == '\r') +# if ! defined(HAVE_ISBLANK) +# define isblank(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n' || (ch) == '\r') +# endif # define usleep(X) Sleep(((X)+1000)/1000) @@ -153,6 +155,16 @@ int gettimeofday(struct timeval *__p, void *__t); #include #endif +/* + * get rid of deprecacy warnings on OSX when using OpenSSL + */ +#if defined(__APPLE__) + #ifdef MAC_OS_X_VERSION_MIN_REQUIRED + #undef MAC_OS_X_VERSION_MIN_REQUIRED + #endif + #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6 +#endif + /* forward declarations */ struct ssh_common_struct; struct ssh_kex_struct; @@ -252,7 +264,7 @@ int match_hostname(const char *host, const char *pattern, unsigned int len); /** Overwrite the buffer with '\0' */ # define BURN_BUFFER(x, size) do { \ if ((x) != NULL) \ - memset((x), '\0', (size))); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ + memset((x), '\0', (size)); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ } while(0) #else /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */ /** Overwrite a string with '\0' */ @@ -263,7 +275,7 @@ int match_hostname(const char *host, const char *pattern, unsigned int len); /** Overwrite the buffer with '\0' */ # define BURN_BUFFER(x, size) do { \ if ((x) != NULL) \ - memset((x), '\0', (size))); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ + memset((x), '\0', (size)); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ } while(0) #endif /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */ diff --git a/libssh/include/libssh/wrapper.h b/libssh/include/libssh/wrapper.h index 90c268d9..7374a88a 100644 --- a/libssh/include/libssh/wrapper.h +++ b/libssh/include/libssh/wrapper.h @@ -53,6 +53,9 @@ void sha1(unsigned char *digest,int len,unsigned char *hash); void sha256(unsigned char *digest, int len, unsigned char *hash); void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen); +EVPCTX evp_init(int nid); +void evp_update(EVPCTX ctx, const void *data, unsigned long len); +void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen); ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type); void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len); diff --git a/libssh/src/CMakeLists.txt b/libssh/src/CMakeLists.txt index b4046805..83435d0c 100644 --- a/libssh/src/CMakeLists.txt +++ b/libssh/src/CMakeLists.txt @@ -115,6 +115,7 @@ set(libssh_SRCS client.c config.c connect.c + curve25519.c dh.c ecdh.c error.c @@ -204,12 +205,12 @@ if (WITH_GSSAPI AND GSSAPI_FOUND) ) endif (WITH_GSSAPI AND GSSAPI_FOUND) -if (WITH_NACL) +if (NOT WITH_NACL) set(libssh_SRCS ${libssh_SRCS} - curve25519.c + curve25519_ref.c ) -endif (WITH_NACL) +endif (NOT WITH_NACL) include_directories( ${LIBSSH_PUBLIC_INCLUDE_DIRS} diff --git a/libssh/src/auth.c b/libssh/src/auth.c index de7965d2..6664c203 100644 --- a/libssh/src/auth.c +++ b/libssh/src/auth.c @@ -355,7 +355,7 @@ int ssh_userauth_list(ssh_session session, const char *username) * later. * * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_optoins_set() only + * authentication. The username should only be set with ssh_options_set() only * before you connect to the server. */ int ssh_userauth_none(ssh_session session, const char *username) { @@ -478,7 +478,7 @@ fail: * later. * * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_optoins_set() only + * authentication. The username should only be set with ssh_options_set() only * before you connect to the server. */ int ssh_userauth_try_publickey(ssh_session session, @@ -640,7 +640,7 @@ fail: * later. * * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_optoins_set() only + * authentication. The username should only be set with ssh_options_set() only * before you connect to the server. */ int ssh_userauth_publickey(ssh_session session, @@ -961,7 +961,7 @@ struct ssh_agent_state_struct { * later. * * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_optoins_set() only + * authentication. The username should only be set with ssh_options_set() only * before you connect to the server. */ int ssh_userauth_agent(ssh_session session, @@ -1083,7 +1083,7 @@ struct ssh_auth_auto_state_struct { * later. * * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_optoins_set() only + * authentication. The username should only be set with ssh_options_set() only * before you connect to the server. */ int ssh_userauth_publickey_auto(ssh_session session, @@ -1297,7 +1297,7 @@ int ssh_userauth_publickey_auto(ssh_session session, * later. * * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_optoins_set() only + * authentication. The username should only be set with ssh_options_set() only * before you connect to the server. * * @see ssh_userauth_none() diff --git a/libssh/src/bind.c b/libssh/src/bind.c index e4c9327e..8132e3e9 100644 --- a/libssh/src/bind.c +++ b/libssh/src/bind.c @@ -454,8 +454,7 @@ int ssh_bind_accept(ssh_bind sshbind, ssh_session session) { #else close(fd); #endif - if (session->socket) - ssh_socket_close(session->socket); + ssh_socket_free(session->socket); } return rc; } diff --git a/libssh/src/channels.c b/libssh/src/channels.c index 81b2bdfb..f7bcded2 100644 --- a/libssh/src/channels.c +++ b/libssh/src/channels.c @@ -2089,7 +2089,7 @@ SSH_PACKET_CALLBACK(ssh_request_denied){ static int ssh_global_request_termination(void *s){ ssh_session session = (ssh_session) s; if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING || - session->session_state != SSH_SESSION_STATE_ERROR) + session->session_state == SSH_SESSION_STATE_ERROR) return 1; else return 0; @@ -2117,43 +2117,66 @@ static int ssh_global_request_termination(void *s){ static int global_request(ssh_session session, const char *request, ssh_buffer buffer, int reply) { ssh_string req = NULL; - int rc = SSH_ERROR; + int rc; - if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) + switch (session->global_req_state) { + case SSH_CHANNEL_REQ_STATE_NONE: + break; + default: goto pending; + } + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST); + if (rc < 0) { + goto error; + } + req = ssh_string_from_char(request); if (req == NULL) { - ssh_set_error_oom(session); - goto error; + ssh_set_error_oom(session); + rc = SSH_ERROR; + goto error; } - if (buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST) < 0 || - buffer_add_ssh_string(session->out_buffer, req) < 0 || - buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { - ssh_set_error_oom(session); - goto error; - } + rc = buffer_add_ssh_string(session->out_buffer, req); ssh_string_free(req); - req=NULL; + if (rc < 0) { + ssh_set_error_oom(session); + rc = SSH_ERROR; + goto error; + } + + rc = buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1); + if (rc < 0) { + ssh_set_error_oom(session); + rc = SSH_ERROR; + goto error; + } if (buffer != NULL) { - if (buffer_add_data(session->out_buffer, buffer_get_rest(buffer), - buffer_get_rest_len(buffer)) < 0) { - ssh_set_error_oom(session); - goto error; - } + rc = buffer_add_data(session->out_buffer, + buffer_get_rest(buffer), + buffer_get_rest_len(buffer)); + if (rc < 0) { + ssh_set_error_oom(session); + rc = SSH_ERROR; + goto error; + } } + session->global_req_state = SSH_CHANNEL_REQ_STATE_PENDING; - if (packet_send(session) == SSH_ERROR) { - return rc; + rc = packet_send(session); + if (rc == SSH_ERROR) { + return rc; } SSH_LOG(SSH_LOG_PACKET, "Sent a SSH_MSG_GLOBAL_REQUEST %s", request); - if (reply == 0) { - session->global_req_state=SSH_CHANNEL_REQ_STATE_NONE; - return SSH_OK; + if (reply == 0) { + session->global_req_state = SSH_CHANNEL_REQ_STATE_NONE; + + return SSH_OK; } pending: rc = ssh_handle_packets_termination(session, @@ -2178,16 +2201,16 @@ pending: break; case SSH_CHANNEL_REQ_STATE_ERROR: case SSH_CHANNEL_REQ_STATE_NONE: - rc=SSH_ERROR; + rc = SSH_ERROR; break; case SSH_CHANNEL_REQ_STATE_PENDING: - rc=SSH_AGAIN; - break; + return SSH_AGAIN; } + session->global_req_state = SSH_CHANNEL_REQ_STATE_NONE; return rc; error: - ssh_string_free(req); + buffer_reinit(session->out_buffer); return rc; } @@ -3325,17 +3348,18 @@ error: } /** - * @brief Send the exit status to the remote process (as described in RFC 4254, section 6.10). + * @brief Send the exit status to the remote process * - * Sends the exit status to the remote process. + * Sends the exit status to the remote process (as described in RFC 4254, + * section 6.10). * Only SSH-v2 is supported (I'm not sure about SSH-v1). * * @param[in] channel The channel to send exit status. * - * @param[in] sig The exit status to send + * @param[in] exit_status The exit status to send * - * @return SSH_OK on success, SSH_ERROR if an error occurred - * (including attempts to send exit status via SSH-v1 session). + * @return SSH_OK on success, SSH_ERROR if an error occurred. + * (including attempts to send exit status via SSH-v1 session). */ int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) { ssh_buffer buffer = NULL; diff --git a/libssh/src/client.c b/libssh/src/client.c index dad988ed..1fb963d7 100644 --- a/libssh/src/client.c +++ b/libssh/src/client.c @@ -105,14 +105,18 @@ static int callback_receive_banner(const void *data, size_t len, void *user) { ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_IN,buffer,i+1,i+1); } #endif - if(buffer[i]=='\r') - buffer[i]='\0'; - if(buffer[i]=='\n'){ - buffer[i]='\0'; - str=strdup(buffer); - /* number of bytes read */ - ret=i+1; - session->serverbanner=str; + if(buffer[i]=='\r') { + buffer[i]='\0'; + } + if (buffer[i]=='\n') { + buffer[i] = '\0'; + str = strdup(buffer); + if (str == NULL) { + return SSH_ERROR; + } + /* number of bytes read */ + ret = i + 1; + session->serverbanner = str; session->session_state=SSH_SESSION_STATE_BANNER_RECEIVED; SSH_LOG(SSH_LOG_PACKET,"Received banner: %s",str); session->ssh_connection_callback(session); @@ -529,7 +533,7 @@ pending: if (timeout == 0) { timeout = 10 * 1000; } - SSH_LOG(SSH_LOG_PACKET,"ssh_connect: Actual timeout : %d", timeout); + SSH_LOG(SSH_LOG_PACKET,"Actual timeout : %d", timeout); ret = ssh_handle_packets_termination(session, timeout, ssh_connect_termination, session); if (ret == SSH_ERROR || !ssh_connect_termination(session)) { ssh_set_error(session, SSH_FATAL, @@ -546,7 +550,7 @@ pending: session->session_state = SSH_SESSION_STATE_ERROR; } } - SSH_LOG(SSH_LOG_PACKET,"ssh_connect: Actual state : %d",session->session_state); + SSH_LOG(SSH_LOG_PACKET,"current state : %d",session->session_state); if(!ssh_is_blocking(session) && !ssh_connect_termination(session)){ return SSH_AGAIN; } diff --git a/libssh/src/connect.c b/libssh/src/connect.c index f7965cbe..b299d41e 100644 --- a/libssh/src/connect.c +++ b/libssh/src/connect.c @@ -436,6 +436,7 @@ static int ssh_select_cb (socket_t fd, int revents, void *userdata){ */ int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, fd_set *readfds, struct timeval *timeout) { + fd_set origfds; socket_t fd; int i,j; int rc; @@ -449,9 +450,11 @@ int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, ssh_event_add_session(event, channels[i]->session); } + FD_ZERO(&origfds); for (fd = 0; fd < maxfd ; fd++) { if (FD_ISSET(fd, readfds)) { ssh_event_add_fd(event, fd, POLLIN, ssh_select_cb, readfds); + FD_SET(fd, &origfds); } } outchannels[0] = NULL; @@ -485,13 +488,17 @@ int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, /* since there's nothing, let's fire the polling */ rc = ssh_event_dopoll(event,tm); if (rc == SSH_ERROR){ - ssh_event_free(event); - return SSH_ERROR; + goto out; } tm = ssh_timeout_update(&ts, base_tm); firstround=0; } while (1); out: + for (fd = 0; fd < maxfd; fd++) { + if (FD_ISSET(fd, &origfds)) { + ssh_event_remove_fd(event, fd); + } + } ssh_event_free(event); return SSH_OK; } diff --git a/libssh/src/curve25519.c b/libssh/src/curve25519.c index 653beee0..153fbcd9 100644 --- a/libssh/src/curve25519.c +++ b/libssh/src/curve25519.c @@ -26,7 +26,10 @@ #include "libssh/curve25519.h" #ifdef HAVE_CURVE25519 +#ifdef WITH_NACL #include "nacl/crypto_scalarmult_curve25519.h" +#endif + #include "libssh/ssh2.h" #include "libssh/buffer.h" #include "libssh/priv.h" @@ -53,7 +56,7 @@ int ssh_client_curve25519_init(ssh_session session){ return SSH_ERROR; } - crypto_scalarmult_curve25519_base(session->next_crypto->curve25519_client_pubkey, + crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey, session->next_crypto->curve25519_privkey); client_pubkey = ssh_string_new(CURVE25519_PUBKEY_SIZE); if (client_pubkey == NULL) { @@ -81,10 +84,10 @@ static int ssh_curve25519_build_k(ssh_session session) { } if (session->server) - crypto_scalarmult_curve25519(k, session->next_crypto->curve25519_privkey, + crypto_scalarmult(k, session->next_crypto->curve25519_privkey, session->next_crypto->curve25519_client_pubkey); else - crypto_scalarmult_curve25519(k, session->next_crypto->curve25519_privkey, + crypto_scalarmult(k, session->next_crypto->curve25519_privkey, session->next_crypto->curve25519_server_pubkey); BN_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k); @@ -125,7 +128,7 @@ int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet){ } if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){ ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", - ssh_string_len(q_s_string)); + (int)ssh_string_len(q_s_string)); ssh_string_free(q_s_string); goto error; } @@ -179,7 +182,7 @@ int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ } if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE){ ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", - ssh_string_len(q_c_string)); + (int)ssh_string_len(q_c_string)); ssh_string_free(q_c_string); return SSH_ERROR; } @@ -195,7 +198,7 @@ int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ return SSH_ERROR; } - crypto_scalarmult_curve25519_base(session->next_crypto->curve25519_server_pubkey, + crypto_scalarmult_base(session->next_crypto->curve25519_server_pubkey, session->next_crypto->curve25519_privkey); q_s_string = ssh_string_new(CURVE25519_PUBKEY_SIZE); diff --git a/libssh/src/dh.c b/libssh/src/dh.c index f96a94a3..5ebbc91e 100644 --- a/libssh/src/dh.c +++ b/libssh/src/dh.c @@ -239,63 +239,6 @@ void ssh_print_bignum(const char *which, bignum num) { SAFE_FREE(hex); } -/** - * @brief Convert a buffer into a colon separated hex string. - * The caller has to free the memory. - * - * @param what What should be converted to a hex string. - * - * @param len Length of the buffer to convert. - * - * @return The hex string or NULL on error. - * - * @see ssh_string_free_char() - */ -char *ssh_get_hexa(const unsigned char *what, size_t len) { - const char h[] = "0123456789abcdef"; - char *hexa; - size_t i; - size_t hlen = len * 3; - - if (len > (UINT_MAX - 1) / 3) { - return NULL; - } - - hexa = malloc(hlen + 1); - if (hexa == NULL) { - return NULL; - } - - for (i = 0; i < len; i++) { - hexa[i * 3] = h[(what[i] >> 4) & 0xF]; - hexa[i * 3 + 1] = h[what[i] & 0xF]; - hexa[i * 3 + 2] = ':'; - } - hexa[hlen - 1] = '\0'; - - return hexa; -} - -/** - * @brief Print a buffer as colon separated hex string. - * - * @param descr Description printed in front of the hex string. - * - * @param what What should be converted to a hex string. - * - * @param len Length of the buffer to convert. - */ -void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) { - char *hexa = ssh_get_hexa(what, len); - - if (hexa == NULL) { - return; - } - printf("%s: %s\n", descr, hexa); - - free(hexa); -} - int dh_generate_x(ssh_session session) { session->next_crypto->x = bignum_new(); if (session->next_crypto->x == NULL) { @@ -1047,25 +990,7 @@ error: */ /** - * @brief Allocates a buffer with the MD5 hash of the server public key. - * - * This function allows you to get a MD5 hash of the public key. You can then - * print this hash in a human-readable form to the user so that he is able to - * verify it. Use ssh_get_hexa() or ssh_print_hexa() to display it. - * - * @param[in] session The SSH session to use. - * - * @param[in] hash The buffer to allocate. - * - * @return The bytes allocated or < 0 on error. - * - * @warning It is very important that you verify at some moment that the hash - * matches a known server. If you don't do it, cryptography wont help - * you at making things secure - * - * @see ssh_is_server_known() - * @see ssh_get_hexa() - * @see ssh_print_hexa() + * @deprecated Use ssh_get_publickey_hash() */ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash) { ssh_string pubkey; @@ -1142,6 +1067,164 @@ int ssh_get_publickey(ssh_session session, ssh_key *key) key); } +/** + * @brief Allocates a buffer with the hash of the public key. + * + * This function allows you to get a hash of the public key. You can then + * print this hash in a human-readable form to the user so that he is able to + * verify it. Use ssh_get_hexa() or ssh_print_hexa() to display it. + * + * @param[in] key The public key to create the hash for. + * + * @param[in] type The type of the hash you want. + * + * @param[in] hash A pointer to store the allocated buffer. It can be + * freed using ssh_clean_pubkey_hash(). + * + * @param[in] hlen The length of the hash. + * + * @return 0 on success, -1 if an error occured. + * + * @warning It is very important that you verify at some moment that the hash + * matches a known server. If you don't do it, cryptography wont help + * you at making things secure. + * OpenSSH uses SHA1 to print public key digests. + * + * @see ssh_is_server_known() + * @see ssh_get_hexa() + * @see ssh_print_hexa() + * @see ssh_clean_pubkey_hash() + */ +int ssh_get_publickey_hash(const ssh_key key, + enum ssh_publickey_hash_type type, + unsigned char **hash, + size_t *hlen) +{ + ssh_string blob; + unsigned char *h; + int rc; + + rc = ssh_pki_export_pubkey_blob(key, &blob); + if (rc < 0) { + return rc; + } + + switch (type) { + case SSH_PUBLICKEY_HASH_SHA1: + { + SHACTX ctx; + + h = malloc(SHA_DIGEST_LEN); + if (h == NULL) { + rc = -1; + goto out; + } + + ctx = sha1_init(); + if (ctx == NULL) { + free(h); + rc = -1; + goto out; + } + + sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); + sha1_final(h, ctx); + + *hlen = SHA_DIGEST_LEN; + } + break; + case SSH_PUBLICKEY_HASH_MD5: + { + MD5CTX ctx; + + h = malloc(MD5_DIGEST_LEN); + if (h == NULL) { + rc = -1; + goto out; + } + + ctx = md5_init(); + if (ctx == NULL) { + free(h); + rc = -1; + goto out; + } + + md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); + md5_final(h, ctx); + + *hlen = MD5_DIGEST_LEN; + } + break; + default: + rc = -1; + goto out; + } + + *hash = h; + rc = 0; +out: + ssh_string_free(blob); + return rc; +} + +/** + * @brief Convert a buffer into a colon separated hex string. + * The caller has to free the memory. + * + * @param what What should be converted to a hex string. + * + * @param len Length of the buffer to convert. + * + * @return The hex string or NULL on error. + * + * @see ssh_string_free_char() + */ +char *ssh_get_hexa(const unsigned char *what, size_t len) { + const char h[] = "0123456789abcdef"; + char *hexa; + size_t i; + size_t hlen = len * 3; + + if (len > (UINT_MAX - 1) / 3) { + return NULL; + } + + hexa = malloc(hlen + 1); + if (hexa == NULL) { + return NULL; + } + + for (i = 0; i < len; i++) { + hexa[i * 3] = h[(what[i] >> 4) & 0xF]; + hexa[i * 3 + 1] = h[what[i] & 0xF]; + hexa[i * 3 + 2] = ':'; + } + hexa[hlen - 1] = '\0'; + + return hexa; +} + +/** + * @brief Print a buffer as colon separated hex string. + * + * @param descr Description printed in front of the hex string. + * + * @param what What should be converted to a hex string. + * + * @param len Length of the buffer to convert. + */ +void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) { + char *hexa = ssh_get_hexa(what, len); + + if (hexa == NULL) { + return; + } + printf("%s: %s\n", descr, hexa); + + free(hexa); +} + /** @} */ /* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/libcrypto.c b/libssh/src/libcrypto.c index 44b0fb36..bb1d96ad 100644 --- a/libssh/src/libcrypto.c +++ b/libssh/src/libcrypto.c @@ -123,6 +123,30 @@ void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned EVP_DigestUpdate(&md, digest, len); EVP_DigestFinal(&md, hash, hlen); } + +EVPCTX evp_init(int nid) +{ + const EVP_MD *evp_md = nid_to_evpmd(nid); + + EVPCTX ctx = malloc(sizeof(EVP_MD_CTX)); + if (ctx == NULL) { + return NULL; + } + + EVP_DigestInit(ctx, evp_md); + + return ctx; +} + +void evp_update(EVPCTX ctx, const void *data, unsigned long len) +{ + EVP_DigestUpdate(ctx, data, len); +} + +void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen) +{ + EVP_DigestFinal(ctx, md, mdlen); +} #endif SHA256CTX sha256_init(void){ diff --git a/libssh/src/log.c b/libssh/src/log.c index fba5fdc4..4552b969 100644 --- a/libssh/src/log.c +++ b/libssh/src/log.c @@ -65,7 +65,7 @@ static int current_timestring(int hires, char *buf, size_t len) if (hires) { strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm); - snprintf(buf, len, "%s.%06ld", tbuf, tv.tv_usec); + snprintf(buf, len, "%s.%06ld", tbuf, (long)tv.tv_usec); } else { strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm); snprintf(buf, len, "%s", tbuf); diff --git a/libssh/src/messages.c b/libssh/src/messages.c index 73f39974..2c99311d 100644 --- a/libssh/src/messages.c +++ b/libssh/src/messages.c @@ -120,10 +120,18 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg) msg->auth_request.username, msg->auth_request.pubkey, msg->auth_request.signature_state, session->server_callbacks->userdata); - if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL){ + if (msg->auth_request.signature_state != SSH_PUBLICKEY_STATE_NONE) { + if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) { ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL); - } else { + } else { ssh_message_reply_default(msg); + } + } else { + if (rc == SSH_AUTH_SUCCESS) { + ssh_message_auth_reply_pk_ok_simple(msg); + } else { + ssh_message_reply_default(msg); + } } return SSH_OK; @@ -301,10 +309,8 @@ static int ssh_execute_server_callbacks(ssh_session session, ssh_message msg){ if (session->server_callbacks != NULL){ rc = ssh_execute_server_request(session, msg); - } - - /* This one is in fact a client callback... */ - if (session->common.callbacks != NULL) { + } else if (session->common.callbacks != NULL) { + /* This one is in fact a client callback... */ rc = ssh_execute_client_request(session, msg); } diff --git a/libssh/src/options.c b/libssh/src/options.c index f8055780..e02ad4df 100644 --- a/libssh/src/options.c +++ b/libssh/src/options.c @@ -856,6 +856,11 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) { * It may include "%s" which will be replaced by the * user home directory. * + * - SSH_OPTIONS_PROXYCOMMAND: + * Get the proxycommand necessary to log into the + * remote host. When not explicitly set, it will be read + * from the ~/.ssh/config file. + * * @param value The value to get into. As a char**, space will be * allocated by the function for the value, it is * your responsibility to free the memory using @@ -894,6 +899,10 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value) src = ssh_iterator_value(char *, it); break; } + case SSH_OPTIONS_PROXYCOMMAND: { + src = session->opts.ProxyCommand; + break; + } default: ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); return SSH_ERROR; @@ -1245,7 +1254,7 @@ static int ssh_bind_options_set_algo(ssh_bind sshbind, int algo, /** * @brief This function can set all possible ssh bind options. * - * @param session An allocated ssh option structure. + * @param sshbind An allocated ssh bind structure. * * @param type The option type to set. This could be one of the * following: diff --git a/libssh/src/pki.c b/libssh/src/pki.c index 770cfc72..ec5a6883 100644 --- a/libssh/src/pki.c +++ b/libssh/src/pki.c @@ -1118,7 +1118,7 @@ int ssh_pki_export_signature_blob(const ssh_signature sig, return SSH_ERROR; } - str = ssh_string_from_char(ssh_key_type_to_char(sig->type)); + str = ssh_string_from_char(sig->type_c); if (str == NULL) { ssh_buffer_free(buf); return SSH_ERROR; @@ -1271,11 +1271,9 @@ ssh_string ssh_pki_do_sign(ssh_session session, struct ssh_crypto_struct *crypto = session->current_crypto ? session->current_crypto : session->next_crypto; - unsigned char hash[SHA_DIGEST_LEN] = {0}; ssh_signature sig; ssh_string sig_blob; ssh_string session_id; - SHACTX ctx; int rc; if (privkey == NULL || !ssh_key_is_private(privkey)) { @@ -1287,24 +1285,46 @@ ssh_string ssh_pki_do_sign(ssh_session session, return NULL; } ssh_string_fill(session_id, crypto->session_id, crypto->digest_len); - /* TODO: change when supporting ECDSA keys */ - ctx = sha1_init(); - if (ctx == NULL) { - ssh_string_free(session_id); - return NULL; - } - sha1_update(ctx, session_id, ssh_string_len(session_id) + 4); - ssh_string_free(session_id); + if (privkey->type == SSH_KEYTYPE_ECDSA) { +#ifdef HAVE_ECC + unsigned char ehash[EVP_DIGEST_LEN] = {0}; + uint32_t elen; + EVPCTX ctx; - sha1_update(ctx, buffer_get_rest(sigbuf), buffer_get_rest_len(sigbuf)); - sha1_final(hash, ctx); + ctx = evp_init(privkey->ecdsa_nid); + if (ctx == NULL) { + ssh_string_free(session_id); + return NULL; + } + + evp_update(ctx, session_id, ssh_string_len(session_id) + 4); + evp_update(ctx, buffer_get_rest(sigbuf), buffer_get_rest_len(sigbuf)); + evp_final(ctx, ehash, &elen); + + sig = pki_do_sign(privkey, ehash, elen); +#endif + } else { + unsigned char hash[SHA_DIGEST_LEN] = {0}; + SHACTX ctx; + + ctx = sha1_init(); + if (ctx == NULL) { + ssh_string_free(session_id); + return NULL; + } + + sha1_update(ctx, session_id, ssh_string_len(session_id) + 4); + sha1_update(ctx, buffer_get_rest(sigbuf), buffer_get_rest_len(sigbuf)); + sha1_final(hash, ctx); #ifdef DEBUG_CRYPTO - ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN); + ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN); #endif - sig = pki_do_sign(privkey, hash, SHA_DIGEST_LEN); + sig = pki_do_sign(privkey, hash, SHA_DIGEST_LEN); + } + ssh_string_free(session_id); if (sig == NULL) { return NULL; } diff --git a/libssh/src/pki_crypto.c b/libssh/src/pki_crypto.c index 92c37c85..e87d7ace 100644 --- a/libssh/src/pki_crypto.c +++ b/libssh/src/pki_crypto.c @@ -25,6 +25,8 @@ #ifndef _PKI_CRYPTO_H #define _PKI_CRYPTO_H +#include "libssh/priv.h" + #include #include #include @@ -37,8 +39,6 @@ #include #endif - -#include "libssh/priv.h" #include "libssh/libssh.h" #include "libssh/buffer.h" #include "libssh/session.h" @@ -1018,36 +1018,52 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) break; case SSH_KEYTYPE_ECDSA: #ifdef HAVE_OPENSSL_ECC + { + ssh_buffer b; + int rc; + + b = ssh_buffer_new(); + if (b == NULL) { + return NULL; + } + r = make_bignum_string(sig->ecdsa_sig->r); if (r == NULL) { + ssh_buffer_free(b); return NULL; } + rc = buffer_add_ssh_string(b, r); + ssh_string_free(r); + if (rc < 0) { + ssh_buffer_free(b); + return NULL; + } + s = make_bignum_string(sig->ecdsa_sig->s); if (s == NULL) { - ssh_string_free(r); + ssh_buffer_free(b); return NULL; } - - memcpy(buffer, - ((char *)ssh_string_data(r)) + ssh_string_len(r) - 20, - 20); - memcpy(buffer + 20, - ((char *)ssh_string_data(s)) + ssh_string_len(s) - 20, - 20); - - ssh_string_free(r); + rc = buffer_add_ssh_string(b, s); ssh_string_free(s); - - sig_blob = ssh_string_new(40); - if (sig_blob == NULL) { + if (rc < 0) { + ssh_buffer_free(b); return NULL; } - ssh_string_fill(sig_blob, buffer, 40); + sig_blob = ssh_string_new(buffer_get_rest_len(b)); + if (sig_blob == NULL) { + ssh_buffer_free(b); + return NULL; + } + + ssh_string_fill(sig_blob, buffer_get_rest(b), buffer_get_rest_len(b)); + ssh_buffer_free(b); break; + } #endif case SSH_KEYTYPE_UNKNOWN: - ssh_pki_log("Unknown signature key type: %d", sig->type); + ssh_pki_log("Unknown signature key type: %s", sig->type_c); return NULL; } @@ -1070,6 +1086,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, } sig->type = type; + sig->type_c = ssh_key_type_to_char(type); len = ssh_string_len(sig_blob); @@ -1309,6 +1326,7 @@ ssh_signature pki_do_sign(const ssh_key privkey, } sig->type = privkey->type; + sig->type_c = privkey->type_c; switch(privkey->type) { case SSH_KEYTYPE_DSS: @@ -1368,6 +1386,7 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key, return NULL; } sig->type = key->type; + sig->type_c = key->type_c; switch(key->type) { case SSH_KEYTYPE_DSS: diff --git a/libssh/src/poll.c b/libssh/src/poll.c index bde0198d..8e21e0d5 100644 --- a/libssh/src/poll.c +++ b/libssh/src/poll.c @@ -450,7 +450,11 @@ void ssh_poll_ctx_free(ssh_poll_ctx ctx) { if (ctx->polls_allocated > 0) { while (ctx->polls_used > 0){ ssh_poll_handle p = ctx->pollptrs[0]; - ssh_poll_ctx_remove(ctx, p); + /* + * The free function calls ssh_poll_ctx_remove() and decrements + * ctx->polls_used + */ + ssh_poll_free(p); } SAFE_FREE(ctx->pollptrs); diff --git a/libssh/src/scp.c b/libssh/src/scp.c index 6838a3cd..db07aed4 100644 --- a/libssh/src/scp.c +++ b/libssh/src/scp.c @@ -814,7 +814,7 @@ int ssh_scp_integer_mode(const char *mode){ */ char *ssh_scp_string_mode(int mode){ char buffer[16]; - snprintf(buffer,sizeof(buffer),"%.4o",mode); + snprintf(buffer,sizeof(buffer),"%.4d",mode); return strdup(buffer); } diff --git a/libssh/src/server.c b/libssh/src/server.c index b31371fe..8629c8ba 100644 --- a/libssh/src/server.c +++ b/libssh/src/server.c @@ -1221,38 +1221,45 @@ int ssh_execute_message_callbacks(ssh_session session){ return SSH_OK; } - int ssh_send_keepalive(ssh_session session) { - /* TODO check the reply and all that */ struct ssh_string_struct *req; - int reply = 1; - int rc = SSH_ERROR; + int rc; + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST); + if (rc < 0) { + goto err; + } req = ssh_string_from_char("keepalive@openssh.com"); if (req == NULL) { - ssh_set_error_oom(session); - goto out; + goto err; } - if (buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST) < 0 || - buffer_add_ssh_string(session->out_buffer, req) < 0 || - buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { - ssh_set_error_oom(session); - goto out; + rc = buffer_add_ssh_string(session->out_buffer, req); + ssh_string_free(req); + if (rc < 0) { + goto err; } - if (packet_send(session) == SSH_ERROR) - goto out; + rc = buffer_add_u8(session->out_buffer, 1); + if (rc < 0) { + goto err; + } + + if (packet_send(session) == SSH_ERROR) { + goto err; + } ssh_handle_packets(session, 0); SSH_LOG(SSH_LOG_PACKET, "Sent a keepalive"); - rc = SSH_OK; + return SSH_OK; -out: - ssh_string_free(req); - return rc; +err: + ssh_set_error_oom(session); + buffer_reinit(session->out_buffer); + return SSH_ERROR; } /** @} */ diff --git a/libssh/src/session.c b/libssh/src/session.c index fe11b416..d4b3643f 100644 --- a/libssh/src/session.c +++ b/libssh/src/session.c @@ -273,7 +273,7 @@ void ssh_free(ssh_session session) { } /* burn connection, it could hang sensitive datas */ - ZERO_STRUCTP(session); + BURN_BUFFER(session, sizeof(struct ssh_session_struct)); SAFE_FREE(session); } diff --git a/libssh/src/socket.c b/libssh/src/socket.c index 0dbbe2bc..c76ef5ae 100644 --- a/libssh/src/socket.c +++ b/libssh/src/socket.c @@ -695,11 +695,11 @@ int ssh_socket_buffered_write_bytes(ssh_socket s){ int ssh_socket_get_status(ssh_socket s) { int r = 0; - if (s->read_wontblock) { - r |= SSH_READ_PENDING; + if (buffer_get_len(s->in_buffer) > 0) { + r |= SSH_READ_PENDING; } - if (s->write_wontblock) { + if (buffer_get_len(s->out_buffer) > 0) { r |= SSH_WRITE_PENDING; } diff --git a/libssh/src/wrapper.c b/libssh/src/wrapper.c index 66593461..51688753 100644 --- a/libssh/src/wrapper.c +++ b/libssh/src/wrapper.c @@ -158,7 +158,7 @@ void crypto_free(struct ssh_crypto_struct *crypto){ SAFE_FREE(crypto->kex_methods[i]); } - memset(crypto,0,sizeof(*crypto)); + BURN_BUFFER(crypto, sizeof(struct ssh_crypto_struct)); SAFE_FREE(crypto); } @@ -317,8 +317,13 @@ int crypt_set_algorithms_server(ssh_session session){ session->next_crypto->do_compress_in=1; } if(strcmp(method,"zlib@openssh.com") == 0){ - SSH_LOG(SSH_LOG_PACKET,"enabling C->S compression"); - session->next_crypto->delayed_compress_in=1; + SSH_LOG(SSH_LOG_PACKET,"enabling C->S delayed compression"); + + if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { + session->next_crypto->do_compress_in = 1; + } else { + session->next_crypto->delayed_compress_in = 1; + } } method = session->next_crypto->kex_methods[SSH_COMP_S_C]; @@ -328,7 +333,12 @@ int crypt_set_algorithms_server(ssh_session session){ } if(strcmp(method,"zlib@openssh.com") == 0){ SSH_LOG(SSH_LOG_PACKET,"enabling S->C delayed compression\n"); - session->next_crypto->delayed_compress_out=1; + + if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { + session->next_crypto->do_compress_out = 1; + } else { + session->next_crypto->delayed_compress_out = 1; + } } method = session->next_crypto->kex_methods[SSH_HOSTKEYS]; diff --git a/libssh/tests/client/CMakeLists.txt b/libssh/tests/client/CMakeLists.txt index e3bb45d3..616060c3 100644 --- a/libssh/tests/client/CMakeLists.txt +++ b/libssh/tests/client/CMakeLists.txt @@ -6,7 +6,10 @@ add_cmocka_test(torture_connect torture_connect.c ${TORTURE_LIBRARY}) add_cmocka_test(torture_knownhosts torture_knownhosts.c ${TORTURE_LIBRARY}) add_cmocka_test(torture_proxycommand torture_proxycommand.c ${TORTURE_LIBRARY}) add_cmocka_test(torture_session torture_session.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_forward torture_forward.c ${TORTURE_LIBRARY}) +add_cmocka_test(torture_request_env torture_request_env.c ${TORTURE_LIBRARY}) if (WITH_SFTP) add_cmocka_test(torture_sftp_static torture_sftp_static.c ${TORTURE_LIBRARY}) add_cmocka_test(torture_sftp_dir torture_sftp_dir.c ${TORTURE_LIBRARY}) + add_cmocka_test(torture_sftp_read torture_sftp_read.c ${TORTURE_LIBRARY}) endif (WITH_SFTP) diff --git a/libssh/tests/unittests/CMakeLists.txt b/libssh/tests/unittests/CMakeLists.txt index d8a6125f..38203991 100644 --- a/libssh/tests/unittests/CMakeLists.txt +++ b/libssh/tests/unittests/CMakeLists.txt @@ -13,4 +13,6 @@ if (UNIX AND NOT WIN32) add_cmocka_test(torture_pki torture_pki.c ${TORTURE_LIBRARY}) # requires pthread add_cmocka_test(torture_rand torture_rand.c ${TORTURE_LIBRARY}) + # requires /dev/null + add_cmocka_test(torture_channel torture_channel.c ${TORTURE_LIBRARY}) endif (UNIX AND NOT WIN32) From dfe63e9e738bff7764f65ffe4449088f4a13feca Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2013 02:46:15 -0500 Subject: [PATCH 057/703] Forgot some libssh files --- libssh/cmake/Modules/FindNaCl.cmake | 61 +++++ libssh/src/curve25519_ref.c | 272 ++++++++++++++++++++++ libssh/tests/client/torture_forward.c | 94 ++++++++ libssh/tests/client/torture_request_env.c | 114 +++++++++ libssh/tests/client/torture_sftp_read.c | 83 +++++++ libssh/tests/unittests/torture_channel.c | 48 ++++ 6 files changed, 672 insertions(+) create mode 100644 libssh/cmake/Modules/FindNaCl.cmake create mode 100644 libssh/src/curve25519_ref.c create mode 100644 libssh/tests/client/torture_forward.c create mode 100644 libssh/tests/client/torture_request_env.c create mode 100644 libssh/tests/client/torture_sftp_read.c create mode 100644 libssh/tests/unittests/torture_channel.c diff --git a/libssh/cmake/Modules/FindNaCl.cmake b/libssh/cmake/Modules/FindNaCl.cmake new file mode 100644 index 00000000..fa9c4090 --- /dev/null +++ b/libssh/cmake/Modules/FindNaCl.cmake @@ -0,0 +1,61 @@ +# - Try to find NaCl +# Once done this will define +# +# NACL_FOUND - system has NaCl +# NACL_INCLUDE_DIRS - the NaCl include directory +# NACL_LIBRARIES - Link these to use NaCl +# NACL_DEFINITIONS - Compiler switches required for using NaCl +# +# Copyright (c) 2010 Andreas Schneider +# Copyright (c) 2013 Aris Adamantiadis +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + + +if (NACL_LIBRARIES AND NACL_INCLUDE_DIRS) + # in cache already + set(NACL_FOUND TRUE) +else (NACL_LIBRARIES AND NACL_INCLUDE_DIRS) + + find_path(NACL_INCLUDE_DIR + NAMES + nacl/crypto_box_curve25519xsalsa20poly1305.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ) + + find_library(NACL_LIBRARY + NAMES + nacl + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + + set(NACL_INCLUDE_DIRS + ${NACL_INCLUDE_DIR} + ) + + if (NACL_LIBRARY) + set(NACL_LIBRARIES + ${NACL_LIBRARIES} + ${NACL_LIBRARY} + ) + endif (NACL_LIBRARY) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(NaCl DEFAULT_MSG NACL_LIBRARIES NACL_INCLUDE_DIRS) + + # show the NACL_INCLUDE_DIRS and NACL_LIBRARIES variables only in the advanced view + mark_as_advanced(NACL_INCLUDE_DIRS NACL_LIBRARIES) + +endif (NACL_LIBRARIES AND NACL_INCLUDE_DIRS) + diff --git a/libssh/src/curve25519_ref.c b/libssh/src/curve25519_ref.c new file mode 100644 index 00000000..aa4cfa2b --- /dev/null +++ b/libssh/src/curve25519_ref.c @@ -0,0 +1,272 @@ +/* +version 20081011 +Matthew Dempsky +Public domain. +Derived from public domain code by D. J. Bernstein. +*/ + +#include "libssh/curve25519.h" +static const unsigned char base[32] = {9}; + +int crypto_scalarmult_base(unsigned char *q, + const unsigned char *n) +{ + return crypto_scalarmult(q,n,base); +} + +static void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int j; + unsigned int u; + u = 0; + for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; } + u += a[31] + b[31]; out[31] = u; +} + +static void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int j; + unsigned int u; + u = 218; + for (j = 0;j < 31;++j) { + u += a[j] + 65280 - b[j]; + out[j] = u & 255; + u >>= 8; + } + u += a[31] - b[31]; + out[31] = u; +} + +static void squeeze(unsigned int a[32]) +{ + unsigned int j; + unsigned int u; + u = 0; + for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } + u += a[31]; a[31] = u & 127; + u = 19 * (u >> 7); + for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } + u += a[31]; a[31] = u; +} + +static const unsigned int minusp[32] = { + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 +} ; + +static void freeze(unsigned int a[32]) +{ + unsigned int aorig[32]; + unsigned int j; + unsigned int negative; + + for (j = 0;j < 32;++j) aorig[j] = a[j]; + add(a,a,minusp); + negative = -((a[31] >> 7) & 1); + for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]); +} + +static void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int i; + unsigned int j; + unsigned int u; + + for (i = 0;i < 32;++i) { + u = 0; + for (j = 0;j <= i;++j) u += a[j] * b[i - j]; + for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j]; + out[i] = u; + } + squeeze(out); +} + +static void mult121665(unsigned int out[32],const unsigned int a[32]) +{ + unsigned int j; + unsigned int u; + + u = 0; + for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; } + u += 121665 * a[31]; out[31] = u & 127; + u = 19 * (u >> 7); + for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; } + u += out[j]; out[j] = u; +} + +static void square(unsigned int out[32],const unsigned int a[32]) +{ + unsigned int i; + unsigned int j; + unsigned int u; + + for (i = 0;i < 32;++i) { + u = 0; + for (j = 0;j < i - j;++j) u += a[j] * a[i - j]; + for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j]; + u *= 2; + if ((i & 1) == 0) { + u += a[i / 2] * a[i / 2]; + u += 38 * a[i / 2 + 16] * a[i / 2 + 16]; + } + out[i] = u; + } + squeeze(out); +} + +static void c_select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b) +{ + unsigned int j; + unsigned int t; + unsigned int bminus1; + + bminus1 = b - 1; + for (j = 0;j < 64;++j) { + t = bminus1 & (r[j] ^ s[j]); + p[j] = s[j] ^ t; + q[j] = r[j] ^ t; + } +} + +static void mainloop(unsigned int work[64],const unsigned char e[32]) +{ + unsigned int xzm1[64]; + unsigned int xzm[64]; + unsigned int xzmb[64]; + unsigned int xzm1b[64]; + unsigned int xznb[64]; + unsigned int xzn1b[64]; + unsigned int a0[64]; + unsigned int a1[64]; + unsigned int b0[64]; + unsigned int b1[64]; + unsigned int c1[64]; + unsigned int r[32]; + unsigned int s[32]; + unsigned int t[32]; + unsigned int u[32]; + unsigned int j; + unsigned int b; + int pos; + + for (j = 0;j < 32;++j) xzm1[j] = work[j]; + xzm1[32] = 1; + for (j = 33;j < 64;++j) xzm1[j] = 0; + + xzm[0] = 1; + for (j = 1;j < 64;++j) xzm[j] = 0; + + for (pos = 254;pos >= 0;--pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + c_select(xzmb,xzm1b,xzm,xzm1,b); + add(a0,xzmb,xzmb + 32); + sub(a0 + 32,xzmb,xzmb + 32); + add(a1,xzm1b,xzm1b + 32); + sub(a1 + 32,xzm1b,xzm1b + 32); + square(b0,a0); + square(b0 + 32,a0 + 32); + mult(b1,a1,a0 + 32); + mult(b1 + 32,a1 + 32,a0); + add(c1,b1,b1 + 32); + sub(c1 + 32,b1,b1 + 32); + square(r,c1 + 32); + sub(s,b0,b0 + 32); + mult121665(t,s); + add(u,t,b0); + mult(xznb,b0,b0 + 32); + mult(xznb + 32,s,u); + square(xzn1b,c1); + mult(xzn1b + 32,r,work); + c_select(xzm,xzm1,xznb,xzn1b,b); + } + + for (j = 0;j < 64;++j) work[j] = xzm[j]; +} + +static void recip(unsigned int out[32],const unsigned int z[32]) +{ + unsigned int z2[32]; + unsigned int z9[32]; + unsigned int z11[32]; + unsigned int z2_5_0[32]; + unsigned int z2_10_0[32]; + unsigned int z2_20_0[32]; + unsigned int z2_50_0[32]; + unsigned int z2_100_0[32]; + unsigned int t0[32]; + unsigned int t1[32]; + int i; + + /* 2 */ square(z2,z); + /* 4 */ square(t1,z2); + /* 8 */ square(t0,t1); + /* 9 */ mult(z9,t0,z); + /* 11 */ mult(z11,z9,z2); + /* 22 */ square(t0,z11); + /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ square(t0,z2_5_0); + /* 2^7 - 2^2 */ square(t1,t0); + /* 2^8 - 2^3 */ square(t0,t1); + /* 2^9 - 2^4 */ square(t1,t0); + /* 2^10 - 2^5 */ square(t0,t1); + /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ square(t0,z2_10_0); + /* 2^12 - 2^2 */ square(t1,t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ square(t0,z2_20_0); + /* 2^22 - 2^2 */ square(t1,t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ square(t1,t0); + /* 2^42 - 2^2 */ square(t0,t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); } + /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ square(t0,z2_50_0); + /* 2^52 - 2^2 */ square(t1,t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ square(t1,z2_100_0); + /* 2^102 - 2^2 */ square(t0,t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); } + /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ square(t0,t1); + /* 2^202 - 2^2 */ square(t1,t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ square(t1,t0); + /* 2^252 - 2^2 */ square(t0,t1); + /* 2^253 - 2^3 */ square(t1,t0); + /* 2^254 - 2^4 */ square(t0,t1); + /* 2^255 - 2^5 */ square(t1,t0); + /* 2^255 - 21 */ mult(out,t1,z11); +} + +int crypto_scalarmult(unsigned char *q, + const unsigned char *n, + const unsigned char *p) +{ + unsigned int work[96]; + unsigned char e[32]; + unsigned int i; + for (i = 0;i < 32;++i) e[i] = n[i]; + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + for (i = 0;i < 32;++i) work[i] = p[i]; + mainloop(work,e); + recip(work + 32,work + 32); + mult(work + 64,work,work + 32); + freeze(work + 64); + for (i = 0;i < 32;++i) q[i] = work[64 + i]; + return 0; +} + diff --git a/libssh/tests/client/torture_forward.c b/libssh/tests/client/torture_forward.c new file mode 100644 index 00000000..1440552e --- /dev/null +++ b/libssh/tests/client/torture_forward.c @@ -0,0 +1,94 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#define LIBSSH_STATIC + +#include "torture.h" +#include + +static void setup(void **state) +{ + ssh_session session; + const char *host; + const char *user; + const char *password; + + host = getenv("TORTURE_HOST"); + if (host == NULL) { + host = "localhost"; + } + + user = getenv("TORTURE_USER"); + password = getenv("TORTURE_PASSWORD"); + + session = torture_ssh_session(host, user, password); + + assert_false(session == NULL); + *state = session; +} + +static void teardown(void **state) +{ + ssh_session session = *state; + + assert_false(session == NULL); + + if (ssh_is_connected(session)) { + ssh_disconnect(session); + } + ssh_free(session); +} + +static void torture_ssh_forward(void **state) +{ + ssh_session session = *state; +#if 0 + ssh_channel c; +#endif + int bound_port; + int rc; + + rc = ssh_forward_listen(session, "127.0.0.1", 8080, &bound_port); + assert_int_equal(rc, SSH_OK); + +#if 0 + c = ssh_forward_accept(session, 60000); + assert_non_null(c); + + ssh_channel_send_eof(c); + ssh_channel_close(c); +#endif +} + +int torture_run_tests(void) { + int rc; + + const UnitTest tests[] = { + unit_test_setup_teardown(torture_ssh_forward, setup, teardown), + }; + + ssh_init(); + + rc = run_tests(tests); + + ssh_finalize(); + return rc; +} diff --git a/libssh/tests/client/torture_request_env.c b/libssh/tests/client/torture_request_env.c new file mode 100644 index 00000000..dbe7fb21 --- /dev/null +++ b/libssh/tests/client/torture_request_env.c @@ -0,0 +1,114 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#define LIBSSH_STATIC + +#include "torture.h" +#include + +static void setup(void **state) +{ + ssh_session session; + const char *host; + const char *user; + const char *password; + + host = getenv("TORTURE_HOST"); + if (host == NULL) { + host = "localhost"; + } + + user = getenv("TORTURE_USER"); + password = getenv("TORTURE_PASSWORD"); + + session = torture_ssh_session(host, user, password); + + assert_false(session == NULL); + *state = session; +} + +static void teardown(void **state) +{ + ssh_session session = *state; + + assert_false(session == NULL); + + if (ssh_is_connected(session)) { + ssh_disconnect(session); + } + ssh_free(session); +} + +static void torture_request_env(void **state) +{ + ssh_session session = *state; + ssh_channel c; + char buffer[4096]; + int nbytes; + int rc; + int lang_found = 0; + + c = ssh_channel_new(session); + assert_non_null(c); + + rc = ssh_channel_open_session(c); + assert_int_equal(rc, SSH_OK); + + rc = ssh_channel_request_env(c, "LANG", "LIBSSH"); + assert_int_equal(rc, SSH_OK); + + rc = ssh_channel_request_exec(c, "bash -c export"); + assert_int_equal(rc, SSH_OK); + + nbytes = ssh_channel_read(c, buffer, sizeof(buffer), 0); + while (nbytes > 0) { +#if 0 + rc = fwrite(buffer, 1, nbytes, stdout); + assert_int_equal(rc, nbytes); +#endif + + if (strstr(buffer, "LANG=\"LIBSSH\"")) { + lang_found = 1; + break; + } + + nbytes = ssh_channel_read(c, buffer, sizeof(buffer), 0); + } + assert_int_equal(lang_found, 1); + + ssh_channel_close(c); +} + +int torture_run_tests(void) { + int rc; + + const UnitTest tests[] = { + unit_test_setup_teardown(torture_request_env, setup, teardown), + }; + + ssh_init(); + + rc = run_tests(tests); + + ssh_finalize(); + return rc; +} + diff --git a/libssh/tests/client/torture_sftp_read.c b/libssh/tests/client/torture_sftp_read.c new file mode 100644 index 00000000..8efaafe9 --- /dev/null +++ b/libssh/tests/client/torture_sftp_read.c @@ -0,0 +1,83 @@ +#define LIBSSH_STATIC + +#include "torture.h" +#include "sftp.c" + +#define MAX_XFER_BUF_SIZE 16384 + +static void setup(void **state) { + ssh_session session; + struct torture_sftp *t; + const char *host; + const char *user; + const char *password; + + host = getenv("TORTURE_HOST"); + if (host == NULL) { + host = "localhost"; + } + + user = getenv("TORTURE_USER"); + password = getenv("TORTURE_PASSWORD"); + + session = torture_ssh_session(host, user, password); + assert_false(session == NULL); + t = torture_sftp_session(session); + assert_false(t == NULL); + + *state = t; +} + +static void teardown(void **state) { + struct torture_sftp *t = *state; + + assert_false(t == NULL); + + torture_rmdirs(t->testdir); + torture_sftp_close(t); +} + +static void torture_sftp_read_blocking(void **state) { + struct torture_sftp *t = *state; + char libssh_tmp_file[] = "/tmp/libssh_sftp_test_XXXXXX"; + char buf[MAX_XFER_BUF_SIZE]; + ssize_t bytesread; + ssize_t byteswritten; + int fd; + sftp_file file; + + + file = sftp_open(t->sftp, "/usr/bin/ssh", O_RDONLY, 0); + assert_non_null(file); + + fd = mkstemp(libssh_tmp_file); + unlink(libssh_tmp_file); + + for (;;) { + bytesread = sftp_read(file, buf, MAX_XFER_BUF_SIZE); + if (bytesread == 0) { + break; /* EOF */ + } + assert_false(bytesread < 0); + + byteswritten = write(fd, buf, bytesread); + assert_int_equal(byteswritten, bytesread); + } + + close(fd); + sftp_close(file); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test_setup_teardown(torture_sftp_read_blocking, setup, teardown) + }; + + ssh_init(); + + rc = run_tests(tests); + ssh_finalize(); + + return rc; +} diff --git a/libssh/tests/unittests/torture_channel.c b/libssh/tests/unittests/torture_channel.c new file mode 100644 index 00000000..5bf34fd9 --- /dev/null +++ b/libssh/tests/unittests/torture_channel.c @@ -0,0 +1,48 @@ +#define LIBSSH_STATIC +#include + +#include +#include +#include + +#include "torture.h" +#include "channels.c" + +static void torture_channel_select(void **state) +{ + fd_set readfds; + int fd; + int rc; + int i; + + (void)state; /* unused */ + + fd = open("/dev/null", 0); + assert_true(fd > 2); + + FD_SET(fd, &readfds); + + for (i = 0; i < 10; i++) { + ssh_channel cin[1] = { NULL, }; + ssh_channel cout[1] = { NULL, }; + struct timeval tv = { .tv_sec = 0, .tv_usec = 1000 }; + + rc = ssh_select(cin, cout, fd + 1, &readfds, &tv); + assert_int_equal(rc, SSH_OK); + } + + close(fd); +} + +int torture_run_tests(void) { + int rc; + const UnitTest tests[] = { + unit_test(torture_channel_select), + }; + + ssh_init(); + rc = run_tests(tests); + ssh_finalize(); + + return rc; +} From 0783022d6d77ab3716e2a754856d40497b4cfd76 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 12 Sep 2014 16:06:06 -0400 Subject: [PATCH 058/703] Fix issue with OSX 10.10 --- libssh/ConfigureChecks.cmake | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libssh/ConfigureChecks.cmake b/libssh/ConfigureChecks.cmake index 472fe79e..29b70636 100644 --- a/libssh/ConfigureChecks.cmake +++ b/libssh/ConfigureChecks.cmake @@ -101,8 +101,17 @@ check_function_exists(snprintf HAVE_SNPRINTF) check_function_exists(poll HAVE_POLL) check_function_exists(select HAVE_SELECT) check_function_exists(getaddrinfo HAVE_GETADDRINFO) -check_function_exists(ntohll HAVE_NTOHLL) -check_function_exists(htonll HAVE_HTONLL) + +check_symbol_exists(ntohll sys/types.h HAVE_NTOHLL) +check_symbol_exists(htonll sys/types.h HAVE_HTONLL) + +if (NOT HAVE_NTOHLL) + check_function_exists(ntohll HAVE_NTOHLL) +endif (NOT HAVE_NTOHLL) +if (NOT HAVE_HTONLL) + check_function_exists(ntohll HAVE_HTONLL) +endif (NOT HAVE_HTONLL) + if (WIN32) check_function_exists(_strtoui64 HAVE__STRTOUI64) From 6e4adcc140102b80883bba85dff05003620b0029 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 31 Oct 2014 00:10:02 -0400 Subject: [PATCH 059/703] Update libssh --- libssh/AUTHORS | 2 +- libssh/CMakeLists.txt | 22 +- libssh/CPackConfig.cmake | 2 +- libssh/ConfigureChecks.cmake | 10 +- libssh/build/.gitkeep | 0 libssh/cmake/Modules/AddCMockaTest.cmake | 2 +- .../cmake/Modules/DefineCMakeDefaults.cmake | 3 + .../cmake/Modules/DefineCompilerFlags.cmake | 7 + libssh/cmake/Modules/FindArgp.cmake | 2 +- libssh/cmake/Modules/FindNaCl.cmake | 2 +- .../cmake/Modules/MacroAddCompileFlags.cmake | 21 - libssh/cmake/Modules/MacroAddLinkFlags.cmake | 20 - libssh/cmake/Modules/MacroAddPlugin.cmake | 30 - libssh/cmake/Modules/MacroCopyFile.cmake | 33 - libssh/cmake/Modules/UseDoxygen.cmake | 160 +- libssh/config.h.cmake | 6 + libssh/doc/{doxy.config.in => Doxyfile.in} | 2 +- libssh/doc/authentication.dox | 4 +- libssh/doc/doxy.trac.in | 1546 ----------------- libssh/doc/forwarding.dox | 11 +- libssh/doc/threading.dox | 1 + libssh/examples/CMakeLists.txt | 13 +- libssh/examples/authentication.c | 8 +- libssh/examples/samplesshd-tty.c | 466 ----- libssh/examples/samplesshd.c | 314 ---- libssh/examples/ssh_server_fork.c | 697 ++++++++ libssh/examples/sshnetcat.c | 45 +- libssh/include/libssh/bignum.h | 32 + libssh/include/libssh/buffer.h | 17 +- libssh/include/libssh/callbacks.h | 4 + libssh/include/libssh/channels.h | 2 + libssh/include/libssh/crypto.h | 10 +- libssh/include/libssh/curve25519.h | 5 +- libssh/include/libssh/dh.h | 4 - libssh/include/libssh/ecdh.h | 4 +- libssh/include/libssh/ed25519.h | 79 + libssh/include/libssh/fe25519.h | 68 + libssh/include/libssh/ge25519.h | 43 + libssh/include/libssh/knownhosts.h | 27 + libssh/include/libssh/legacy.h | 90 +- libssh/include/libssh/libcrypto.h | 13 + libssh/include/libssh/libgcrypt.h | 8 +- libssh/include/libssh/libssh.h | 56 +- libssh/include/libssh/libsshpp.hpp | 38 +- libssh/include/libssh/packet.h | 4 +- libssh/include/libssh/pki.h | 5 + libssh/include/libssh/pki_priv.h | 20 + libssh/include/libssh/priv.h | 25 +- libssh/include/libssh/sc25519.h | 74 + libssh/include/libssh/server.h | 66 +- libssh/include/libssh/session.h | 16 + libssh/include/libssh/sftp.h | 8 +- libssh/include/libssh/socket.h | 1 + libssh/include/libssh/wrapper.h | 29 + libssh/libssh-config.cmake.in | 6 +- libssh/{build => obj}/build_make.sh | 2 +- libssh/src/CMakeLists.txt | 16 +- libssh/src/agent.c | 8 +- libssh/src/auth.c | 565 ++---- libssh/src/base64.c | 13 +- libssh/src/bignum.c | 96 + libssh/src/bind.c | 77 +- libssh/src/buffer.c | 438 ++++- libssh/src/callbacks.c | 2 +- libssh/src/channels.c | 880 +++++----- libssh/src/channels1.c | 7 +- libssh/src/client.c | 100 +- libssh/src/config.c | 57 +- libssh/src/connect.c | 14 +- libssh/src/curve25519.c | 70 +- libssh/src/dh.c | 532 +++--- libssh/src/ecdh.c | 37 +- libssh/src/ed25519.c | 222 +++ libssh/src/error.c | 4 +- libssh/src/fe25519.c | 416 +++++ libssh/src/ge25519.c | 367 ++++ libssh/src/ge25519_base.data | 858 +++++++++ libssh/src/getpass.c | 2 +- libssh/src/gssapi.c | 455 ++--- libssh/src/gzip.c | 14 +- libssh/src/kex.c | 322 +++- libssh/src/kex1.c | 6 +- libssh/src/known_hosts.c | 104 +- libssh/src/legacy.c | 8 +- libssh/src/libcrypto.c | 84 +- libssh/src/libgcrypt.c | 127 +- libssh/src/log.c | 6 +- libssh/src/messages.c | 601 ++----- libssh/src/misc.c | 3 +- libssh/src/options.c | 288 ++- libssh/src/packet.c | 421 +++-- libssh/src/packet1.c | 27 +- libssh/src/packet_cb.c | 32 +- libssh/src/packet_crypt.c | 31 +- libssh/src/pcap.c | 149 +- libssh/src/pki.c | 249 ++- libssh/src/pki_crypto.c | 382 +++- libssh/src/pki_ed25519.c | 305 ++++ libssh/src/pki_gcrypt.c | 24 +- libssh/src/poll.c | 56 +- libssh/src/sc25519.c | 373 ++++ libssh/src/scp.c | 6 +- libssh/src/server.c | 304 ++-- libssh/src/session.c | 224 ++- libssh/src/sftp.c | 651 +++---- libssh/src/sftpserver.c | 122 +- libssh/src/socket.c | 260 +-- libssh/src/string.c | 9 +- libssh/src/threads.c | 25 +- libssh/src/threads/CMakeLists.txt | 134 +- libssh/src/wrapper.c | 117 +- libssh/tests/CMakeLists.txt | 8 +- libssh/tests/client/torture_algorithms.c | 136 +- libssh/tests/client/torture_auth.c | 116 +- libssh/tests/client/torture_connect.c | 40 +- libssh/tests/client/torture_forward.c | 10 +- libssh/tests/client/torture_knownhosts.c | 192 ++ libssh/tests/client/torture_request_env.c | 10 +- libssh/tests/client/torture_session.c | 5 +- libssh/tests/client/torture_sftp_read.c | 4 +- libssh/tests/pkd/CMakeLists.txt | 35 + libssh/tests/pkd/pkd_client.h | 69 + libssh/tests/pkd/pkd_daemon.c | 500 ++++++ libssh/tests/pkd/pkd_daemon.h | 40 + libssh/tests/pkd/pkd_hello.c | 534 ++++++ libssh/tests/pkd/pkd_keyutil.c | 138 ++ libssh/tests/pkd/pkd_keyutil.h | 40 + libssh/tests/pkd/pkd_util.c | 46 + libssh/tests/pkd/pkd_util.h | 16 + libssh/tests/test_ssh_bind_accept_fd.c | 139 ++ libssh/tests/torture.c | 242 ++- libssh/tests/torture.h | 10 +- libssh/tests/unittests/torture_buffer.c | 142 +- libssh/tests/unittests/torture_callbacks.c | 40 + libssh/tests/unittests/torture_channel.c | 1 + libssh/tests/unittests/torture_pki.c | 517 ++++-- 136 files changed, 11353 insertions(+), 6562 deletions(-) create mode 100644 libssh/build/.gitkeep delete mode 100644 libssh/cmake/Modules/MacroAddCompileFlags.cmake delete mode 100644 libssh/cmake/Modules/MacroAddLinkFlags.cmake delete mode 100644 libssh/cmake/Modules/MacroAddPlugin.cmake delete mode 100644 libssh/cmake/Modules/MacroCopyFile.cmake rename libssh/doc/{doxy.config.in => Doxyfile.in} (99%) delete mode 100644 libssh/doc/doxy.trac.in delete mode 100644 libssh/examples/samplesshd-tty.c delete mode 100644 libssh/examples/samplesshd.c create mode 100644 libssh/examples/ssh_server_fork.c create mode 100644 libssh/include/libssh/bignum.h create mode 100644 libssh/include/libssh/ed25519.h create mode 100644 libssh/include/libssh/fe25519.h create mode 100644 libssh/include/libssh/ge25519.h create mode 100644 libssh/include/libssh/knownhosts.h create mode 100644 libssh/include/libssh/sc25519.h rename libssh/{build => obj}/build_make.sh (98%) create mode 100644 libssh/src/bignum.c create mode 100644 libssh/src/ed25519.c create mode 100644 libssh/src/fe25519.c create mode 100644 libssh/src/ge25519.c create mode 100644 libssh/src/ge25519_base.data create mode 100644 libssh/src/pki_ed25519.c create mode 100644 libssh/src/sc25519.c create mode 100644 libssh/tests/pkd/CMakeLists.txt create mode 100644 libssh/tests/pkd/pkd_client.h create mode 100644 libssh/tests/pkd/pkd_daemon.c create mode 100644 libssh/tests/pkd/pkd_daemon.h create mode 100644 libssh/tests/pkd/pkd_hello.c create mode 100644 libssh/tests/pkd/pkd_keyutil.c create mode 100644 libssh/tests/pkd/pkd_keyutil.h create mode 100644 libssh/tests/pkd/pkd_util.c create mode 100644 libssh/tests/pkd/pkd_util.h create mode 100644 libssh/tests/test_ssh_bind_accept_fd.c diff --git a/libssh/AUTHORS b/libssh/AUTHORS index fd753860..51b3e46f 100644 --- a/libssh/AUTHORS +++ b/libssh/AUTHORS @@ -1,7 +1,7 @@ Author(s): Aris Adamantiadis (project initiator) -Andreas Schneider (developer) +Andreas Schneider (developer) Nick Zitzmann (mostly client SFTP stuff) diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt index 5998bc50..48559f37 100644 --- a/libssh/CMakeLists.txt +++ b/libssh/CMakeLists.txt @@ -7,8 +7,8 @@ cmake_minimum_required(VERSION 2.6.0) set(APPLICATION_NAME ${PROJECT_NAME}) set(APPLICATION_VERSION_MAJOR "0") -set(APPLICATION_VERSION_MINOR "5") -set(APPLICATION_VERSION_PATCH "90") +set(APPLICATION_VERSION_MINOR "7") +set(APPLICATION_VERSION_PATCH "0") set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}") @@ -39,10 +39,6 @@ include(CPackConfig.cmake) include(MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source build. Please create a separate build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there.") -# add macros -include(MacroAddPlugin) -include(MacroCopyFile) - # search for libraries if (WITH_ZLIB) find_package(ZLIB REQUIRED) @@ -101,18 +97,22 @@ install( ) # cmake config files -configure_file(libssh-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-config.cmake @ONLY) -configure_file(libssh-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-config-version.cmake @ONLY) +set(LIBSSH_LIBRARY_NAME @CMAKE_SHARED_LIBRARY_PREFIX@ssh@CMAKE_SHARED_LIBRARY_SUFFIX@) +set(LIBSSH_THREADS_LIBRARY_NAME @CMAKE_SHARED_LIBRARY_PREFIX@ssh@CMAKE_SHARED_LIBRARY_SUFFIX@) + +configure_file(${PROJECT_NAME}-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake @ONLY) +configure_file(${PROJECT_NAME}-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake @ONLY) install( FILES - ${CMAKE_CURRENT_BINARY_DIR}/libssh-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/libssh-config-version.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake DESTINATION - ${CMAKE_INSTALL_DIR} + ${CMAKE_INSTALL_DIR}/${PROJECT_NAME} COMPONENT devel ) + # in tree build settings configure_file(libssh-build-tree-settings.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-build-tree-settings.cmake @ONLY) diff --git a/libssh/CPackConfig.cmake b/libssh/CPackConfig.cmake index 2e4c9fee..2c4c3b5b 100644 --- a/libssh/CPackConfig.cmake +++ b/libssh/CPackConfig.cmake @@ -19,7 +19,7 @@ set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSIO ### source generator set(CPACK_SOURCE_GENERATOR "TGZ") -set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build/;tags;cscope.*") +set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build/;/obj/;tags;cscope.*") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") if (WIN32) diff --git a/libssh/ConfigureChecks.cmake b/libssh/ConfigureChecks.cmake index 29b70636..befb4290 100644 --- a/libssh/ConfigureChecks.cmake +++ b/libssh/ConfigureChecks.cmake @@ -48,9 +48,11 @@ endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) # HEADER FILES check_include_file(argp.h HAVE_ARGP_H) check_include_file(pty.h HAVE_PTY_H) +check_include_file(utmp.h HAVE_UTMP_H) check_include_file(termios.h HAVE_TERMIOS_H) check_include_file(unistd.h HAVE_UNISTD_H) check_include_file(util.h HAVE_UTIL_H) +check_include_file(libutil.h HAVE_LIBUTIL_H) if (WIN32) check_include_files("winsock2.h;ws2tcpip.h;wspiapi.h" HAVE_WSPIAPI_H) @@ -178,11 +180,9 @@ if (GCRYPT_FOUND) endif (GCRYPT_VERSION VERSION_GREATER "1.4.6") endif (GCRYPT_FOUND) -if (CMAKE_HAVE_THREADS_LIBRARY) - if (CMAKE_USE_PTHREADS_INIT) - set(HAVE_PTHREAD 1) - endif (CMAKE_USE_PTHREADS_INIT) -endif (CMAKE_HAVE_THREADS_LIBRARY) +if (CMAKE_USE_PTHREADS_INIT) + set(HAVE_PTHREAD 1) +endif (CMAKE_USE_PTHREADS_INIT) # OPTIONS check_c_source_compiles(" diff --git a/libssh/build/.gitkeep b/libssh/build/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/libssh/cmake/Modules/AddCMockaTest.cmake b/libssh/cmake/Modules/AddCMockaTest.cmake index b2d1ca8a..19eff622 100644 --- a/libssh/cmake/Modules/AddCMockaTest.cmake +++ b/libssh/cmake/Modules/AddCMockaTest.cmake @@ -1,7 +1,7 @@ # - ADD_CHECK_TEST(test_name test_source linklib1 ... linklibN) # Copyright (c) 2007 Daniel Gollub -# Copyright (c) 2007-2010 Andreas Schneider +# Copyright (c) 2007-2010 Andreas Schneider # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. diff --git a/libssh/cmake/Modules/DefineCMakeDefaults.cmake b/libssh/cmake/Modules/DefineCMakeDefaults.cmake index 72893c3c..22eda6fa 100644 --- a/libssh/cmake/Modules/DefineCMakeDefaults.cmake +++ b/libssh/cmake/Modules/DefineCMakeDefaults.cmake @@ -25,3 +25,6 @@ if (NOT CMAKE_BUILD_TYPE) "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." ) endif (NOT CMAKE_BUILD_TYPE) + +# Create the compile command database for clang by default +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/libssh/cmake/Modules/DefineCompilerFlags.cmake b/libssh/cmake/Modules/DefineCompilerFlags.cmake index 0ab8802c..bfbc38fc 100644 --- a/libssh/cmake/Modules/DefineCompilerFlags.cmake +++ b/libssh/cmake/Modules/DefineCompilerFlags.cmake @@ -75,3 +75,10 @@ if (MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") endif (MSVC) + +# This removes this annoying warning +# "warning: 'BN_CTX_free' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations]" +if (OSX) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") +endif (OSX) + diff --git a/libssh/cmake/Modules/FindArgp.cmake b/libssh/cmake/Modules/FindArgp.cmake index aa228557..8dedc855 100644 --- a/libssh/cmake/Modules/FindArgp.cmake +++ b/libssh/cmake/Modules/FindArgp.cmake @@ -6,7 +6,7 @@ # ARGP_LIBRARIES - Link these to use Argp # ARGP_DEFINITIONS - Compiler switches required for using Argp # -# Copyright (c) 2010 Andreas Schneider +# Copyright (c) 2010 Andreas Schneider # # Redistribution and use is allowed according to the terms of the New # BSD license. diff --git a/libssh/cmake/Modules/FindNaCl.cmake b/libssh/cmake/Modules/FindNaCl.cmake index fa9c4090..b1a8da45 100644 --- a/libssh/cmake/Modules/FindNaCl.cmake +++ b/libssh/cmake/Modules/FindNaCl.cmake @@ -6,7 +6,7 @@ # NACL_LIBRARIES - Link these to use NaCl # NACL_DEFINITIONS - Compiler switches required for using NaCl # -# Copyright (c) 2010 Andreas Schneider +# Copyright (c) 2010 Andreas Schneider # Copyright (c) 2013 Aris Adamantiadis # # Redistribution and use is allowed according to the terms of the New diff --git a/libssh/cmake/Modules/MacroAddCompileFlags.cmake b/libssh/cmake/Modules/MacroAddCompileFlags.cmake deleted file mode 100644 index a866689d..00000000 --- a/libssh/cmake/Modules/MacroAddCompileFlags.cmake +++ /dev/null @@ -1,21 +0,0 @@ -# - MACRO_ADD_COMPILE_FLAGS(target_name flag1 ... flagN) - -# Copyright (c) 2006, Oswald Buddenhagen, -# Copyright (c) 2006, Andreas Schneider, -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - - -macro (MACRO_ADD_COMPILE_FLAGS _target) - - get_target_property(_flags ${_target} COMPILE_FLAGS) - if (_flags) - set(_flags ${_flags} ${ARGN}) - else (_flags) - set(_flags ${ARGN}) - endif (_flags) - - set_target_properties(${_target} PROPERTIES COMPILE_FLAGS ${_flags}) - -endmacro (MACRO_ADD_COMPILE_FLAGS) diff --git a/libssh/cmake/Modules/MacroAddLinkFlags.cmake b/libssh/cmake/Modules/MacroAddLinkFlags.cmake deleted file mode 100644 index 91cad306..00000000 --- a/libssh/cmake/Modules/MacroAddLinkFlags.cmake +++ /dev/null @@ -1,20 +0,0 @@ -# - MACRO_ADD_LINK_FLAGS(target_name flag1 ... flagN) - -# Copyright (c) 2006, Oswald Buddenhagen, -# Copyright (c) 2006, Andreas Schneider, -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -macro (MACRO_ADD_LINK_FLAGS _target) - - get_target_property(_flags ${_target} LINK_FLAGS) - if (_flags) - set(_flags "${_flags} ${ARGN}") - else (_flags) - set(_flags "${ARGN}") - endif (_flags) - - set_target_properties(${_target} PROPERTIES LINK_FLAGS "${_flags}") - -endmacro (MACRO_ADD_LINK_FLAGS) diff --git a/libssh/cmake/Modules/MacroAddPlugin.cmake b/libssh/cmake/Modules/MacroAddPlugin.cmake deleted file mode 100644 index 36b5e57e..00000000 --- a/libssh/cmake/Modules/MacroAddPlugin.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# - MACRO_ADD_PLUGIN(name [WITH_PREFIX] file1 .. fileN) -# -# Create a plugin from the given source files. -# If WITH_PREFIX is given, the resulting plugin will have the -# prefix "lib", otherwise it won't. -# -# Copyright (c) 2006, Alexander Neundorf, -# Copyright (c) 2006, Laurent Montel, -# Copyright (c) 2006, Andreas Schneider, -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - - -macro (MACRO_ADD_PLUGIN _target_NAME _with_PREFIX) - - if (${_with_PREFIX} STREQUAL "WITH_PREFIX") - set(_first_SRC) - else (${_with_PREFIX} STREQUAL "WITH_PREFIX") - set(_first_SRC ${_with_PREFIX}) - endif (${_with_PREFIX} STREQUAL "WITH_PREFIX") - - add_library(${_target_NAME} MODULE ${_first_SRC} ${ARGN}) - - if (_first_SRC) - set_target_properties(${_target_NAME} PROPERTIES PREFIX "") - endif (_first_SRC) - -endmacro (MACRO_ADD_PLUGIN _name _sources) - diff --git a/libssh/cmake/Modules/MacroCopyFile.cmake b/libssh/cmake/Modules/MacroCopyFile.cmake deleted file mode 100644 index cee1cae3..00000000 --- a/libssh/cmake/Modules/MacroCopyFile.cmake +++ /dev/null @@ -1,33 +0,0 @@ -# - macro_copy_file(_src _dst) -# Copies a file to ${_dst} only if ${_src} is different (newer) than ${_dst} -# -# Example: -# macro_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/icon.png ${CMAKE_CURRENT_BINARY_DIR}/.) -# Copies file icon.png to ${CMAKE_CURRENT_BINARY_DIR} directory -# -# Copyright (c) 2006-2007 Wengo -# Copyright (c) 2006-2008 Andreas Schneider -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING file. - - -macro (macro_copy_file _src _dst) - # Removes all path containing .svn or CVS or CMakeLists.txt during the copy - if (NOT ${_src} MATCHES ".*\\.svn|CVS|CMakeLists\\.txt.*") - - if (CMAKE_VERBOSE_MAKEFILE) - message(STATUS "Copy file from ${_src} to ${_dst}") - endif (CMAKE_VERBOSE_MAKEFILE) - - # Creates directory if necessary - get_filename_component(_path ${_dst} PATH) - file(MAKE_DIRECTORY ${_path}) - - execute_process( - COMMAND - ${CMAKE_COMMAND} -E copy_if_different ${_src} ${_dst} - OUTPUT_QUIET - ) - endif (NOT ${_src} MATCHES ".*\\.svn|CVS|CMakeLists\\.txt.*") -endmacro (macro_copy_file) diff --git a/libssh/cmake/Modules/UseDoxygen.cmake b/libssh/cmake/Modules/UseDoxygen.cmake index 861afa51..72c384d2 100644 --- a/libssh/cmake/Modules/UseDoxygen.cmake +++ b/libssh/cmake/Modules/UseDoxygen.cmake @@ -2,10 +2,18 @@ # # Adds a doxygen target that runs doxygen to generate the html # and optionally the LaTeX API documentation. -# The doxygen target is added to the doc target as dependency. +# The doxygen target is added to the doc target as a dependency. # i.e.: the API documentation is built with: # make doc # +# USAGE: GLOBAL INSTALL +# +# Install it with: +# cmake ./ && sudo make install +# Add the following to the CMakeLists.txt of your project: +# include(UseDoxygen OPTIONAL) +# Optionally copy Doxyfile.in in the directory of CMakeLists.txt and edit it. +# # USAGE: INCLUDE IN PROJECT # # set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) @@ -13,88 +21,120 @@ # Add the Doxyfile.in and UseDoxygen.cmake files to the projects source directory. # # +# CONFIGURATION +# +# To configure Doxygen you can edit Doxyfile.in and set some variables in cmake. # Variables you may define are: -# DOXYFILE_OUTPUT_DIR - Path where the Doxygen output is stored. Defaults to "doc". -# -# DOXYFILE_LATEX_DIR - Directory where the Doxygen LaTeX output is stored. Defaults to "latex". -# -# DOXYFILE_HTML_DIR - Directory where the Doxygen html output is stored. Defaults to "html". +# DOXYFILE_SOURCE_DIR - Path where the Doxygen input files are. +# Defaults to the current source directory. +# DOXYFILE_EXTRA_SOURCES - Additional source diretories/files for Doxygen to scan. +# The Paths should be in double quotes and separated by space. e.g.: +# "${CMAKE_CURRENT_BINARY_DIR}/foo.c" "${CMAKE_CURRENT_BINARY_DIR}/bar/" +# +# DOXYFILE_OUTPUT_DIR - Path where the Doxygen output is stored. +# Defaults to "${CMAKE_CURRENT_BINARY_DIR}/doc". +# +# DOXYFILE_LATEX - ON/OFF; Set to "ON" if you want the LaTeX documentation +# to be built. +# DOXYFILE_LATEX_DIR - Directory relative to DOXYFILE_OUTPUT_DIR where +# the Doxygen LaTeX output is stored. Defaults to "latex". +# +# DOXYFILE_HTML_DIR - Directory relative to DOXYFILE_OUTPUT_DIR where +# the Doxygen html output is stored. Defaults to "html". # # -# Copyright (c) 2009-2010 Tobias Rautenkranz -# Copyright (c) 2010 Andreas Schneider +# Copyright (c) 2009, 2010, 2011 Tobias Rautenkranz # # Redistribution and use is allowed according to the terms of the New # BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # -macro(usedoxygen_set_default name value) - if(NOT DEFINED "${name}") - set("${name}" "${value}") - endif() +macro(usedoxygen_set_default name value type docstring) + if(NOT DEFINED "${name}") + set("${name}" "${value}" CACHE "${type}" "${docstring}") + endif() endmacro() find_package(Doxygen) if(DOXYGEN_FOUND) - find_file(DOXYFILE_IN - NAMES - doxy.config.in - PATHS - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_ROOT}/Modules/ - NO_DEFAULT_PATH) - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(DOXYFILE_IN DEFAULT_MSG "DOXYFILE_IN") + find_file(DOXYFILE_IN "Doxyfile.in" + PATHS "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_ROOT}/Modules/" + NO_DEFAULT_PATH + DOC "Path to the doxygen configuration template file") + set(DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(DOXYFILE_IN DEFAULT_MSG "DOXYFILE_IN") endif() if(DOXYGEN_FOUND AND DOXYFILE_IN_FOUND) - add_custom_target(doxygen ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.config) + usedoxygen_set_default(DOXYFILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc" + PATH "Doxygen output directory") + usedoxygen_set_default(DOXYFILE_HTML_DIR "html" + STRING "Doxygen HTML output directory") + usedoxygen_set_default(DOXYFILE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" + PATH "Input files source directory") + usedoxygen_set_default(DOXYFILE_EXTRA_SOURCE_DIRS "" + STRING "Additional source files/directories separated by space") + set(DOXYFILE_SOURCE_DIRS "\"${DOXYFILE_SOURCE_DIR}\" ${DOXYFILE_EXTRA_SOURCES}") - usedoxygen_set_default(DOXYFILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") - usedoxygen_set_default(DOXYFILE_HTML_DIR "html") + usedoxygen_set_default(DOXYFILE_LATEX YES BOOL "Generate LaTeX API documentation" OFF) + usedoxygen_set_default(DOXYFILE_LATEX_DIR "latex" STRING "LaTex output directory") - set_property(DIRECTORY APPEND PROPERTY - ADDITIONAL_MAKE_CLEAN_FILES "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_HTML_DIR}") + mark_as_advanced(DOXYFILE_OUTPUT_DIR DOXYFILE_HTML_DIR DOXYFILE_LATEX_DIR + DOXYFILE_SOURCE_DIR DOXYFILE_EXTRA_SOURCE_DIRS DOXYFILE_IN) - set(DOXYFILE_LATEX FALSE) - set(DOXYFILE_PDFLATEX FALSE) - set(DOXYFILE_DOT FALSE) - #find_package(LATEX) - #if(LATEX_COMPILER AND MAKEINDEX_COMPILER) - # set(DOXYFILE_LATEX TRUE) - # usedoxygen_set_default(DOXYFILE_LATEX_DIR "latex") - # - # set_property(DIRECTORY APPEND PROPERTY - # ADDITIONAL_MAKE_CLEAN_FILES - # "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") - # - # if(PDFLATEX_COMPILER) - # set(DOXYFILE_PDFLATEX TRUE) - # endif() - # if(DOXYGEN_DOT_EXECUTABLE) - # set(DOXYFILE_DOT TRUE) - # endif() - # - # add_custom_command(TARGET doxygen - # POST_BUILD - # COMMAND ${CMAKE_MAKE_PROGRAM} - # WORKING_DIRECTORY "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") - #endif() + set_property(DIRECTORY + APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES + "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_HTML_DIR}") - configure_file(${DOXYFILE_IN} ${CMAKE_CURRENT_BINARY_DIR}/doxy.config ESCAPE_QUOTES IMMEDIATE @ONLY) - if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/doxy.trac.in) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxy.trac.in ${CMAKE_CURRENT_BINARY_DIR}/doxy.trac ESCAPE_QUOTES IMMEDIATE @ONLY) - add_custom_target(doxygen-trac ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.trac) - endif() + add_custom_target(doxygen + COMMAND "${DOXYGEN_EXECUTABLE}" + "${DOXYFILE}" + COMMENT "Writing documentation to ${DOXYFILE_OUTPUT_DIR}..." + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") - get_target_property(DOC_TARGET doc TYPE) - if(NOT DOC_TARGET) - add_custom_target(doc) - endif() + set(DOXYFILE_DOT "NO") + if(DOXYGEN_DOT_EXECUTABLE) + set(DOXYFILE_DOT "YES") + endif() - add_dependencies(doc doxygen) + ## LaTeX + set(DOXYFILE_PDFLATEX "NO") + + set_property(DIRECTORY APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES + "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") + + if(DOXYFILE_LATEX STREQUAL "ON") + set(DOXYFILE_GENERATE_LATEX "YES") + find_package(LATEX) + find_program(DOXYFILE_MAKE make) + mark_as_advanced(DOXYFILE_MAKE) + if(LATEX_COMPILER AND MAKEINDEX_COMPILER AND DOXYFILE_MAKE) + if(PDFLATEX_COMPILER) + set(DOXYFILE_PDFLATEX "YES") + endif() + + add_custom_command(TARGET doxygen + POST_BUILD + COMMAND "${DOXYFILE_MAKE}" + COMMENT "Running LaTeX for Doxygen documentation in ${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}..." + WORKING_DIRECTORY "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") + else() + set(DOXYGEN_LATEX "NO") + endif() + else() + set(DOXYFILE_GENERATE_LATEX "NO") + endif() + + + configure_file("${DOXYFILE_IN}" "${DOXYFILE}" @ONLY) + + add_custom_target(doc) + add_dependencies(doc doxygen) endif() diff --git a/libssh/config.h.cmake b/libssh/config.h.cmake index 7e8cb6a8..55e37aca 100644 --- a/libssh/config.h.cmake +++ b/libssh/config.h.cmake @@ -20,9 +20,15 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PTY_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UTMP_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UTIL_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_LIBUTIL_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_TERMIOS_H 1 diff --git a/libssh/doc/doxy.config.in b/libssh/doc/Doxyfile.in similarity index 99% rename from libssh/doc/doxy.config.in rename to libssh/doc/Doxyfile.in index 4da9b2c9..a7a9ffbb 100644 --- a/libssh/doc/doxy.config.in +++ b/libssh/doc/Doxyfile.in @@ -721,7 +721,7 @@ EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */.git/* \ */.svn/* \ */cmake/* \ - */build/* + */obj/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/libssh/doc/authentication.dox b/libssh/doc/authentication.dox index fbc2103b..30690e8c 100644 --- a/libssh/doc/authentication.dox +++ b/libssh/doc/authentication.dox @@ -285,7 +285,7 @@ int authenticate_kbdint(ssh_session session) { int rc; - rc = ssh_userauth_none(session, NULL, NULL); + rc = ssh_userauth_none(session, NULL); return rc; } @endcode @@ -304,7 +304,7 @@ int test_several_auth_methods(ssh_session session) { int method, rc; - rc = ssh_userauth_none(session, NULL, NULL); + rc = ssh_userauth_none(session, NULL); if (rc != SSH_AUTH_SUCCESS) { return rc; } diff --git a/libssh/doc/doxy.trac.in b/libssh/doc/doxy.trac.in deleted file mode 100644 index dbd719aa..00000000 --- a/libssh/doc/doxy.trac.in +++ /dev/null @@ -1,1546 +0,0 @@ -# Doxyfile 1.6.1 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = @APPLICATION_NAME@ - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = @APPLICATION_VERSION@ - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = @CMAKE_SOURCE_DIR@ - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 2 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = YES - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = NO - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = YES - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = YES - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = @CMAKE_INTERNAL_DOC@ - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = YES - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = YES - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = @CMAKE_SOURCE_DIR@/include \ - @CMAKE_SOURCE_DIR@/libssh \ - @CMAKE_SOURCE_DIR@/doc - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.cpp \ - *.cc \ - *.c \ - *.h \ - *.hh \ - *.hpp \ - *.dox - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = */.git/* \ - */.svn/* \ - */cmake/* \ - */build/* - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/examples - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = *.c \ - *.h \ - INSTALL \ - DEPENDENCIES \ - CHANGELOG \ - LICENSE \ - LGPL - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = YES - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 2 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = trac - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = @CMAKE_CURRENT_SOURCE_DIR@/TracHeader.html - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = @CMAKE_CURRENT_SOURCE_DIR@/TracFooter.html - -# If the HTML_TIMESTAMP tag is set to YES then the generated HTML -# documentation will contain the timesstamp. - -HTML_TIMESTAMP = NO - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP) -# there is already a search function so this one should typically -# be disabled. - -SEARCHENGINE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = YES - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = YES - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = YES - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = @CMAKE_CURRENT_BINARY_DIR@/trac/@PROJECT_NAME@.TAGFILE - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = YES - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = NO - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = @DOXYGEN_DOT_FOUND@ - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = @DOXYGEN_DOT_EXECUTABLE_PATH@ - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = YES - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/libssh/doc/forwarding.dox b/libssh/doc/forwarding.dox index 9dc0df36..be4ab94e 100644 --- a/libssh/doc/forwarding.dox +++ b/libssh/doc/forwarding.dox @@ -144,10 +144,10 @@ or whatever use you have for it. @subsection libssh_reverse Doing reverse port forwarding with libssh -To do reverse port forwarding, call ssh_forward_listen(), -then ssh_forward_accept(). +To do reverse port forwarding, call ssh_channel_listen_forward(), +then ssh_channel_accept_forward(). -When you call ssh_forward_listen(), you can let the remote server +When you call ssh_channel_listen_forward(), you can let the remote server chose the non-priviledged port it should listen to. Otherwise, you can chose your own priviledged or non-priviledged port. Beware that you should have administrative priviledges on the remote server to open a priviledged port @@ -164,6 +164,7 @@ int web_server(ssh_session session) ssh_channel channel; char buffer[256]; int nbytes, nwritten; + int port = 0; char *helloworld = "" "HTTP/1.1 200 OK\n" "Content-Type: text/html\n" @@ -178,7 +179,7 @@ int web_server(ssh_session session) " \n" "\n"; - rc = ssh_forward_listen(session, NULL, 8080, NULL); + rc = ssh_channel_listen_forward(session, NULL, 8080, NULL); if (rc != SSH_OK) { fprintf(stderr, "Error opening remote port: %s\n", @@ -186,7 +187,7 @@ int web_server(ssh_session session) return rc; } - channel = ssh_forward_accept(session, 60000); + channel = ssh_channel_accept_forward(session, 60000, &port); if (channel == NULL) { fprintf(stderr, "Error waiting for incoming connection: %s\n", diff --git a/libssh/doc/threading.dox b/libssh/doc/threading.dox index a11c82f7..95eee6bb 100644 --- a/libssh/doc/threading.dox +++ b/libssh/doc/threading.dox @@ -61,5 +61,6 @@ implement the following methods : - mutex_destroy - thread_id +libgcrypt 1.6 and bigger backend does not support custom callback. Using anything else than pthreads (ssh_threads_get_pthread()) here will fail. Good luck ! */ diff --git a/libssh/examples/CMakeLists.txt b/libssh/examples/CMakeLists.txt index c155e097..4998f041 100644 --- a/libssh/examples/CMakeLists.txt +++ b/libssh/examples/CMakeLists.txt @@ -25,13 +25,6 @@ if (UNIX AND NOT WIN32) add_executable(sshnetcat sshnetcat.c ${examples_SRCS}) target_link_libraries(sshnetcat ${LIBSSH_SHARED_LIBRARY}) - if (WITH_SERVER) - if (HAVE_LIBUTIL) - add_executable(samplesshd-tty samplesshd-tty.c) - target_link_libraries(samplesshd-tty ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES} util) - endif (HAVE_LIBUTIL) - endif (WITH_SERVER) - if (WITH_SFTP) add_executable(samplesftp samplesftp.c ${examples_SRCS}) target_link_libraries(samplesftp ${LIBSSH_SHARED_LIBRARY}) @@ -41,8 +34,10 @@ if (UNIX AND NOT WIN32) target_link_libraries(samplessh ${LIBSSH_SHARED_LIBRARY}) if (WITH_SERVER) - add_executable(samplesshd samplesshd.c) - target_link_libraries(samplesshd ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES}) + if (HAVE_LIBUTIL) + add_executable(ssh_server_fork ssh_server_fork.c) + target_link_libraries(ssh_server_fork ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES} util) + endif (HAVE_LIBUTIL) if (WITH_GSSAPI AND GSSAPI_FOUND) add_executable(samplesshd-cb samplesshd-cb.c) diff --git a/libssh/examples/authentication.c b/libssh/examples/authentication.c index ab5e64d6..b9f70f5b 100644 --- a/libssh/examples/authentication.c +++ b/libssh/examples/authentication.c @@ -116,7 +116,7 @@ int authenticate_console(ssh_session session){ return rc; } - method = ssh_auth_list(session); + method = ssh_userauth_list(session, NULL); while (rc != SSH_AUTH_SUCCESS) { if (method & SSH_AUTH_METHOD_GSSAPI_MIC){ rc = ssh_userauth_gssapi(session); @@ -129,10 +129,10 @@ int authenticate_console(ssh_session session){ } // Try to authenticate with public key first if (method & SSH_AUTH_METHOD_PUBLICKEY) { - rc = ssh_userauth_autopubkey(session, NULL); + rc = ssh_userauth_publickey_auto(session, NULL, NULL); if (rc == SSH_AUTH_ERROR) { - error(session); - return rc; + error(session); + return rc; } else if (rc == SSH_AUTH_SUCCESS) { break; } diff --git a/libssh/examples/samplesshd-tty.c b/libssh/examples/samplesshd-tty.c deleted file mode 100644 index 83b75648..00000000 --- a/libssh/examples/samplesshd-tty.c +++ /dev/null @@ -1,466 +0,0 @@ -/* This is a sample implementation of a libssh based SSH server */ -/* -Copyright 2003-2011 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ - -#include "config.h" - -#include -#include -#include - -#ifdef HAVE_ARGP_H -#include -#endif -#include -#include -#include -#include -#ifdef HAVE_PTY_H -#include -#endif -#ifdef HAVE_UTIL_H -#include -#endif -#define SSHD_USER "libssh" -#define SSHD_PASSWORD "libssh" - -#ifndef KEYS_FOLDER -#ifdef _WIN32 -#define KEYS_FOLDER -#else -#define KEYS_FOLDER "/etc/ssh/" -#endif -#endif - -#ifdef WITH_PCAP -const char *pcap_file="debug.server.pcap"; -ssh_pcap_file pcap; - -static void set_pcap(ssh_session session){ - if(!pcap_file) - return; - pcap=ssh_pcap_file_new(); - if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ - printf("Error opening pcap file\n"); - ssh_pcap_file_free(pcap); - pcap=NULL; - return; - } - ssh_set_pcap_file(session,pcap); -} - -static void cleanup_pcap(){ - ssh_pcap_file_free(pcap); - pcap=NULL; -} -#endif - - -static int auth_password(const char *user, const char *password){ - if(strcmp(user, SSHD_USER)) - return 0; - if(strcmp(password, SSHD_PASSWORD)) - return 0; - return 1; // authenticated -} -#ifdef HAVE_ARGP_H -const char *argp_program_version = "libssh server example " - SSH_STRINGIFY(LIBSSH_VERSION); -const char *argp_program_bug_address = ""; - -/* Program documentation. */ -static char doc[] = "libssh -- a Secure Shell protocol implementation"; - -/* A description of the arguments we accept. */ -static char args_doc[] = "BINDADDR"; - -static int port = 22; - -/* The options we understand. */ -static struct argp_option options[] = { - { - .name = "port", - .key = 'p', - .arg = "PORT", - .flags = 0, - .doc = "Set the port to bind.", - .group = 0 - }, - { - .name = "hostkey", - .key = 'k', - .arg = "FILE", - .flags = 0, - .doc = "Set the host key.", - .group = 0 - }, - { - .name = "dsakey", - .key = 'd', - .arg = "FILE", - .flags = 0, - .doc = "Set the dsa key.", - .group = 0 - }, - { - .name = "rsakey", - .key = 'r', - .arg = "FILE", - .flags = 0, - .doc = "Set the rsa key.", - .group = 0 - }, - { - .name = "verbose", - .key = 'v', - .arg = NULL, - .flags = 0, - .doc = "Get verbose output.", - .group = 0 - }, - {NULL, 0, 0, 0, NULL, 0} -}; - -/* Parse a single option. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* Get the input argument from argp_parse, which we - * know is a pointer to our arguments structure. - */ - ssh_bind sshbind = state->input; - - switch (key) { - case 'p': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); - port = atoi(arg); - break; - case 'd': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); - break; - case 'k': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); - break; - case 'r': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); - break; - case 'v': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - /* Too many arguments. */ - argp_usage (state); - } - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - /* Not enough arguments. */ - argp_usage (state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; -#endif /* HAVE_ARGP_H */ - -static int authenticate(ssh_session session) { - ssh_message message; - - do { - message=ssh_message_get(session); - if(!message) - break; - switch(ssh_message_type(message)){ - case SSH_REQUEST_AUTH: - switch(ssh_message_subtype(message)){ - case SSH_AUTH_METHOD_PASSWORD: - printf("User %s wants to auth with pass %s\n", - ssh_message_auth_user(message), - ssh_message_auth_password(message)); - if(auth_password(ssh_message_auth_user(message), - ssh_message_auth_password(message))){ - ssh_message_auth_reply_success(message,0); - ssh_message_free(message); - return 1; - } - ssh_message_auth_set_methods(message, - SSH_AUTH_METHOD_PASSWORD | - SSH_AUTH_METHOD_INTERACTIVE); - // not authenticated, send default message - ssh_message_reply_default(message); - break; - - case SSH_AUTH_METHOD_NONE: - default: - printf("User %s wants to auth with unknown auth %d\n", - ssh_message_auth_user(message), - ssh_message_subtype(message)); - ssh_message_auth_set_methods(message, - SSH_AUTH_METHOD_PASSWORD | - SSH_AUTH_METHOD_INTERACTIVE); - ssh_message_reply_default(message); - break; - } - break; - default: - ssh_message_auth_set_methods(message, - SSH_AUTH_METHOD_PASSWORD | - SSH_AUTH_METHOD_INTERACTIVE); - ssh_message_reply_default(message); - } - ssh_message_free(message); - } while (1); - return 0; -} - -static int copy_fd_to_chan(socket_t fd, int revents, void *userdata) { - ssh_channel chan = (ssh_channel)userdata; - char buf[2048]; - int sz = 0; - - if(!chan) { - close(fd); - return -1; - } - if(revents & POLLIN) { - sz = read(fd, buf, 2048); - if(sz > 0) { - ssh_channel_write(chan, buf, sz); - } - } - if(revents & POLLHUP) { - ssh_channel_close(chan); - sz = -1; - } - return sz; -} - -static int copy_chan_to_fd(ssh_session session, - ssh_channel channel, - void *data, - uint32_t len, - int is_stderr, - void *userdata) { - int fd = *(int*)userdata; - int sz; - (void)session; - (void)channel; - (void)is_stderr; - - sz = write(fd, data, len); - return sz; -} - -static void chan_close(ssh_session session, ssh_channel channel, void *userdata) { - int fd = *(int*)userdata; - (void)session; - (void)channel; - - close(fd); -} - -struct ssh_channel_callbacks_struct cb = { - .channel_data_function = copy_chan_to_fd, - .channel_eof_function = chan_close, - .channel_close_function = chan_close, - .userdata = NULL -}; - -static int main_loop(ssh_channel chan) { - ssh_session session = ssh_channel_get_session(chan); - socket_t fd; - struct termios *term = NULL; - struct winsize *win = NULL; - pid_t childpid; - ssh_event event; - short events; - int rc; - - childpid = forkpty(&fd, NULL, term, win); - if(childpid == 0) { - execl("/bin/bash", "/bin/bash", (char *)NULL); - abort(); - } - - cb.userdata = &fd; - ssh_callbacks_init(&cb); - ssh_set_channel_callbacks(chan, &cb); - - events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; - - event = ssh_event_new(); - if(event == NULL) { - printf("Couldn't get a event\n"); - return -1; - } - if(ssh_event_add_fd(event, fd, events, copy_fd_to_chan, chan) != SSH_OK) { - printf("Couldn't add an fd to the event\n"); - ssh_event_free(event); - return -1; - } - if(ssh_event_add_session(event, session) != SSH_OK) { - printf("Couldn't add the session to the event\n"); - ssh_event_remove_fd(event, fd); - ssh_event_free(event); - return -1; - } - - do { - rc = ssh_event_dopoll(event, 1000); - if (rc == SSH_ERROR){ - fprintf(stderr, "Error : %s\n", ssh_get_error(session)); - ssh_event_free(event); - ssh_disconnect(session); - return -1; - } - } while(!ssh_channel_is_closed(chan)); - - ssh_event_remove_fd(event, fd); - - ssh_event_remove_session(event, session); - - ssh_event_free(event); - return 0; -} - - -int main(int argc, char **argv){ - ssh_session session; - ssh_bind sshbind; - ssh_message message; - ssh_channel chan=0; - int auth=0; - int shell=0; - int r; - - sshbind=ssh_bind_new(); - session=ssh_new(); - - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, - KEYS_FOLDER "ssh_host_dsa_key"); - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, - KEYS_FOLDER "ssh_host_rsa_key"); - -#ifdef HAVE_ARGP_H - /* - * Parse our arguments; every option seen by parse_opt will - * be reflected in arguments. - */ - argp_parse (&argp, argc, argv, 0, 0, sshbind); -#else - (void) argc; - (void) argv; -#endif -#ifdef WITH_PCAP - set_pcap(session); -#endif - - if(ssh_bind_listen(sshbind)<0){ - printf("Error listening to socket: %s\n", ssh_get_error(sshbind)); - return 1; - } - printf("Started sample libssh sshd on port %d\n", port); - printf("You can login as the user %s with the password %s\n", SSHD_USER, - SSHD_PASSWORD); - r = ssh_bind_accept(sshbind, session); - if(r==SSH_ERROR){ - printf("Error accepting a connection: %s\n", ssh_get_error(sshbind)); - return 1; - } - if (ssh_handle_key_exchange(session)) { - printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); - return 1; - } - - /* proceed to authentication */ - auth = authenticate(session); - if(!auth){ - printf("Authentication error: %s\n", ssh_get_error(session)); - ssh_disconnect(session); - return 1; - } - - - /* wait for a channel session */ - do { - message = ssh_message_get(session); - if(message){ - if(ssh_message_type(message) == SSH_REQUEST_CHANNEL_OPEN && - ssh_message_subtype(message) == SSH_CHANNEL_SESSION) { - chan = ssh_message_channel_request_open_reply_accept(message); - ssh_message_free(message); - break; - } else { - ssh_message_reply_default(message); - ssh_message_free(message); - } - } else { - break; - } - } while(!chan); - - if(!chan) { - printf("Error: cleint did not ask for a channel session (%s)\n", - ssh_get_error(session)); - ssh_finalize(); - return 1; - } - - - /* wait for a shell */ - do { - message = ssh_message_get(session); - if(message != NULL) { - if(ssh_message_type(message) == SSH_REQUEST_CHANNEL) { - if(ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_SHELL) { - shell = 1; - ssh_message_channel_request_reply_success(message); - ssh_message_free(message); - break; - } else if(ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_PTY) { - ssh_message_channel_request_reply_success(message); - ssh_message_free(message); - continue; - } - } - ssh_message_reply_default(message); - ssh_message_free(message); - } else { - break; - } - } while(!shell); - - if(!shell) { - printf("Error: No shell requested (%s)\n", ssh_get_error(session)); - return 1; - } - - printf("it works !\n"); - - main_loop(chan); - - ssh_disconnect(session); - ssh_bind_free(sshbind); -#ifdef WITH_PCAP - cleanup_pcap(); -#endif - ssh_finalize(); - return 0; -} - diff --git a/libssh/examples/samplesshd.c b/libssh/examples/samplesshd.c deleted file mode 100644 index f9e0dc8c..00000000 --- a/libssh/examples/samplesshd.c +++ /dev/null @@ -1,314 +0,0 @@ -/* This is a sample implementation of a libssh based SSH server */ -/* -Copyright 2003-2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ - -#include "config.h" - -#include -#include - -#ifdef HAVE_ARGP_H -#include -#endif -#include -#include -#include - -#ifndef KEYS_FOLDER -#ifdef _WIN32 -#define KEYS_FOLDER -#else -#define KEYS_FOLDER "/etc/ssh/" -#endif -#endif - -#ifdef WITH_PCAP -static const char *pcap_file="debug.server.pcap"; -static ssh_pcap_file pcap; - -static void set_pcap(ssh_session session) { - if(!pcap_file) - return; - pcap=ssh_pcap_file_new(); - if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ - printf("Error opening pcap file\n"); - ssh_pcap_file_free(pcap); - pcap=NULL; - return; - } - ssh_set_pcap_file(session,pcap); -} - -static void cleanup_pcap(void) { - ssh_pcap_file_free(pcap); - pcap=NULL; -} -#endif - - -static int auth_password(const char *user, const char *password){ - if(strcmp(user,"aris")) - return 0; - if(strcmp(password,"lala")) - return 0; - return 1; // authenticated -} -#ifdef HAVE_ARGP_H -const char *argp_program_version = "libssh server example " - SSH_STRINGIFY(LIBSSH_VERSION); -const char *argp_program_bug_address = ""; - -/* Program documentation. */ -static char doc[] = "libssh -- a Secure Shell protocol implementation"; - -/* A description of the arguments we accept. */ -static char args_doc[] = "BINDADDR"; - -/* The options we understand. */ -static struct argp_option options[] = { - { - .name = "port", - .key = 'p', - .arg = "PORT", - .flags = 0, - .doc = "Set the port to bind.", - .group = 0 - }, - { - .name = "hostkey", - .key = 'k', - .arg = "FILE", - .flags = 0, - .doc = "Set the host key.", - .group = 0 - }, - { - .name = "dsakey", - .key = 'd', - .arg = "FILE", - .flags = 0, - .doc = "Set the dsa key.", - .group = 0 - }, - { - .name = "rsakey", - .key = 'r', - .arg = "FILE", - .flags = 0, - .doc = "Set the rsa key.", - .group = 0 - }, - { - .name = "verbose", - .key = 'v', - .arg = NULL, - .flags = 0, - .doc = "Get verbose output.", - .group = 0 - }, - {NULL, 0, NULL, 0, NULL, 0} -}; - -/* Parse a single option. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* Get the input argument from argp_parse, which we - * know is a pointer to our arguments structure. - */ - ssh_bind sshbind = state->input; - - switch (key) { - case 'p': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); - break; - case 'd': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); - break; - case 'k': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); - break; - case 'r': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); - break; - case 'v': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - /* Too many arguments. */ - argp_usage (state); - } - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - /* Not enough arguments. */ - argp_usage (state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; -#endif /* HAVE_ARGP_H */ - -int main(int argc, char **argv){ - ssh_session session; - ssh_bind sshbind; - ssh_message message; - ssh_channel chan=0; - char buf[2048]; - int auth=0; - int sftp=0; - int i; - int r; - - sshbind=ssh_bind_new(); - session=ssh_new(); - - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key"); - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key"); - -#ifdef HAVE_ARGP_H - /* - * Parse our arguments; every option seen by parse_opt will - * be reflected in arguments. - */ - argp_parse (&argp, argc, argv, 0, 0, sshbind); -#else - (void) argc; - (void) argv; -#endif -#ifdef WITH_PCAP - set_pcap(session); -#endif - - if(ssh_bind_listen(sshbind)<0){ - printf("Error listening to socket: %s\n",ssh_get_error(sshbind)); - return 1; - } - r=ssh_bind_accept(sshbind,session); - if(r==SSH_ERROR){ - printf("error accepting a connection : %s\n",ssh_get_error(sshbind)); - return 1; - } - if (ssh_handle_key_exchange(session)) { - printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); - return 1; - } - do { - message=ssh_message_get(session); - if(!message) - break; - switch(ssh_message_type(message)){ - case SSH_REQUEST_AUTH: - switch(ssh_message_subtype(message)){ - case SSH_AUTH_METHOD_PASSWORD: - printf("User %s wants to auth with pass %s\n", - ssh_message_auth_user(message), - ssh_message_auth_password(message)); - if(auth_password(ssh_message_auth_user(message), - ssh_message_auth_password(message))){ - auth=1; - ssh_message_auth_reply_success(message,0); - break; - } - // not authenticated, send default message - case SSH_AUTH_METHOD_NONE: - default: - ssh_message_auth_set_methods(message,SSH_AUTH_METHOD_PASSWORD); - ssh_message_reply_default(message); - break; - } - break; - default: - ssh_message_reply_default(message); - } - ssh_message_free(message); - } while (!auth); - if(!auth){ - printf("auth error: %s\n",ssh_get_error(session)); - ssh_disconnect(session); - return 1; - } - do { - message=ssh_message_get(session); - if(message){ - switch(ssh_message_type(message)){ - case SSH_REQUEST_CHANNEL_OPEN: - if(ssh_message_subtype(message)==SSH_CHANNEL_SESSION){ - chan=ssh_message_channel_request_open_reply_accept(message); - break; - } - default: - ssh_message_reply_default(message); - } - ssh_message_free(message); - } - } while(message && !chan); - if(!chan){ - printf("error : %s\n",ssh_get_error(session)); - ssh_finalize(); - return 1; - } - do { - message=ssh_message_get(session); - if(message && ssh_message_type(message)==SSH_REQUEST_CHANNEL && - (ssh_message_subtype(message)==SSH_CHANNEL_REQUEST_SHELL || - ssh_message_subtype(message)==SSH_CHANNEL_REQUEST_PTY)) { -// if(!strcmp(ssh_message_channel_request_subsystem(message),"sftp")){ - sftp=1; - ssh_message_channel_request_reply_success(message); - break; - // } - } - if(!sftp){ - ssh_message_reply_default(message); - } - ssh_message_free(message); - } while (message && !sftp); - if(!sftp){ - printf("error : %s\n",ssh_get_error(session)); - return 1; - } - printf("it works !\n"); - do{ - i=ssh_channel_read(chan,buf, 2048, 0); - if(i>0) { - ssh_channel_write(chan, buf, i); - if (write(1,buf,i) < 0) { - printf("error writing to buffer\n"); - return 1; - } - if (buf[0] == '\x0d') { - if (write(1, "\n", 1) < 0) { - printf("error writing to buffer\n"); - return 1; - } - ssh_channel_write(chan, "\n", 1); - } - } - } while (i>0); - ssh_disconnect(session); - ssh_bind_free(sshbind); -#ifdef WITH_PCAP - cleanup_pcap(); -#endif - ssh_finalize(); - return 0; -} - diff --git a/libssh/examples/ssh_server_fork.c b/libssh/examples/ssh_server_fork.c new file mode 100644 index 00000000..837db6fe --- /dev/null +++ b/libssh/examples/ssh_server_fork.c @@ -0,0 +1,697 @@ +/* This is a sample implementation of a libssh based SSH server */ +/* +Copyright 2014 Audrius Butkevicius + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. +*/ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_ARGP_H +#include +#endif +#include +#ifdef HAVE_LIBUTIL_H +#include +#endif +#ifdef HAVE_PTY_H +#include +#endif +#include +#include +#ifdef HAVE_UTMP_H +#include +#endif +#ifdef HAVE_UTIL_H +#include +#endif +#include +#include +#include + +#ifndef KEYS_FOLDER +#ifdef _WIN32 +#define KEYS_FOLDER +#else +#define KEYS_FOLDER "/etc/ssh/" +#endif +#endif + +#define USER "myuser" +#define PASS "mypassword" +#define BUF_SIZE 1048576 +#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR) +#define SFTP_SERVER_PATH "/usr/lib/sftp-server" + +static void set_default_keys(ssh_bind sshbind, + int rsa_already_set, + int dsa_already_set, + int ecdsa_already_set) { + if (!rsa_already_set) { + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, + KEYS_FOLDER "ssh_host_rsa_key"); + } + if (!dsa_already_set) { + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, + KEYS_FOLDER "ssh_host_dsa_key"); + } + if (!ecdsa_already_set) { + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, + KEYS_FOLDER "ssh_host_ecdsa_key"); + } +} + +#ifdef HAVE_ARGP_H +const char *argp_program_version = "libssh server example " +SSH_STRINGIFY(LIBSSH_VERSION); +const char *argp_program_bug_address = ""; + +/* Program documentation. */ +static char doc[] = "libssh -- a Secure Shell protocol implementation"; + +/* A description of the arguments we accept. */ +static char args_doc[] = "BINDADDR"; + +/* The options we understand. */ +static struct argp_option options[] = { + { + .name = "port", + .key = 'p', + .arg = "PORT", + .flags = 0, + .doc = "Set the port to bind.", + .group = 0 + }, + { + .name = "hostkey", + .key = 'k', + .arg = "FILE", + .flags = 0, + .doc = "Set a host key. Can be used multiple times. " + "Implies no default keys.", + .group = 0 + }, + { + .name = "dsakey", + .key = 'd', + .arg = "FILE", + .flags = 0, + .doc = "Set the dsa key.", + .group = 0 + }, + { + .name = "rsakey", + .key = 'r', + .arg = "FILE", + .flags = 0, + .doc = "Set the rsa key.", + .group = 0 + }, + { + .name = "ecdsakey", + .key = 'e', + .arg = "FILE", + .flags = 0, + .doc = "Set the ecdsa key.", + .group = 0 + }, + { + .name = "no-default-keys", + .key = 'n', + .arg = NULL, + .flags = 0, + .doc = "Do not set default key locations.", + .group = 0 + }, + { + .name = "verbose", + .key = 'v', + .arg = NULL, + .flags = 0, + .doc = "Get verbose output.", + .group = 0 + }, + {NULL, 0, NULL, 0, NULL, 0} +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. */ + ssh_bind sshbind = state->input; + static int no_default_keys = 0; + static int rsa_already_set = 0, dsa_already_set = 0, ecdsa_already_set = 0; + + switch (key) { + case 'n': + no_default_keys = 1; + break; + case 'p': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); + break; + case 'd': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); + dsa_already_set = 1; + break; + case 'k': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); + /* We can't track the types of keys being added with this + option, so let's ensure we keep the keys we're adding + by just not setting the default keys */ + no_default_keys = 1; + break; + case 'r': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); + rsa_already_set = 1; + break; + case 'e': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, arg); + ecdsa_already_set = 1; + break; + case 'v': + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, + "3"); + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + /* Too many arguments. */ + argp_usage (state); + } + ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); + break; + case ARGP_KEY_END: + if (state->arg_num < 1) { + /* Not enough arguments. */ + argp_usage (state); + } + + if (!no_default_keys) { + set_default_keys(sshbind, + rsa_already_set, + dsa_already_set, + ecdsa_already_set); + } + + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +/* Our argp parser. */ +static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; +#endif /* HAVE_ARGP_H */ + +/* A userdata struct for channel. */ +struct channel_data_struct { + /* pid of the child process the channel will spawn. */ + pid_t pid; + /* For PTY allocation */ + socket_t pty_master; + socket_t pty_slave; + /* For communication with the child process. */ + socket_t stdin; + socket_t stdout; + /* Only used for subsystem and exec requests. */ + socket_t stderr; + /* Event which is used to poll the above descriptors. */ + ssh_event event; + /* Terminal size struct. */ + struct winsize *winsize; +}; + +/* A userdata struct for session. */ +struct session_data_struct { + /* Pointer to the channel the session will allocate. */ + ssh_channel channel; + int auth_attempts; + int authenticated; +}; + +static int data_function(ssh_session session, ssh_channel channel, void *data, + uint32_t len, int is_stderr, void *userdata) { + struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; + + (void) session; + (void) channel; + (void) is_stderr; + + if (len == 0 || cdata->pid < 1 || kill(cdata->pid, 0) < 0) { + return 0; + } + + return write(cdata->stdin, (char *) data, len); +} + +static int pty_request(ssh_session session, ssh_channel channel, + const char *term, int cols, int rows, int py, int px, + void *userdata) { + struct channel_data_struct *cdata = (struct channel_data_struct *)userdata; + + (void) session; + (void) channel; + (void) term; + + cdata->winsize->ws_row = rows; + cdata->winsize->ws_col = cols; + cdata->winsize->ws_xpixel = px; + cdata->winsize->ws_ypixel = py; + + if (openpty(&cdata->pty_master, &cdata->pty_slave, NULL, NULL, + cdata->winsize) != 0) { + fprintf(stderr, "Failed to open pty\n"); + return SSH_ERROR; + } + return SSH_OK; +} + +static int pty_resize(ssh_session session, ssh_channel channel, int cols, + int rows, int py, int px, void *userdata) { + struct channel_data_struct *cdata = (struct channel_data_struct *)userdata; + + (void) session; + (void) channel; + + cdata->winsize->ws_row = rows; + cdata->winsize->ws_col = cols; + cdata->winsize->ws_xpixel = px; + cdata->winsize->ws_ypixel = py; + + if (cdata->pty_master != -1) { + return ioctl(cdata->pty_master, TIOCSWINSZ, cdata->winsize); + } + + return SSH_ERROR; +} + +static int exec_pty(const char *mode, const char *command, + struct channel_data_struct *cdata) { + switch(cdata->pid = fork()) { + case -1: + close(cdata->pty_master); + close(cdata->pty_slave); + fprintf(stderr, "Failed to fork\n"); + return SSH_ERROR; + case 0: + close(cdata->pty_master); + if (login_tty(cdata->pty_slave) != 0) { + exit(1); + } + execl("/bin/sh", "sh", mode, command, NULL); + exit(0); + default: + close(cdata->pty_slave); + /* pty fd is bi-directional */ + cdata->stdout = cdata->stdin = cdata->pty_master; + } + return SSH_OK; +} + +static int exec_nopty(const char *command, struct channel_data_struct *cdata) { + int in[2], out[2], err[2]; + + /* Do the plumbing to be able to talk with the child process. */ + if (pipe(in) != 0) { + goto stdin_failed; + } + if (pipe(out) != 0) { + goto stdout_failed; + } + if (pipe(err) != 0) { + goto stderr_failed; + } + + switch(cdata->pid = fork()) { + case -1: + goto fork_failed; + case 0: + /* Finish the plumbing in the child process. */ + close(in[1]); + close(out[0]); + close(err[0]); + dup2(in[0], STDIN_FILENO); + dup2(out[1], STDOUT_FILENO); + dup2(err[1], STDERR_FILENO); + close(in[0]); + close(out[1]); + close(err[1]); + /* exec the requested command. */ + execl("/bin/sh", "sh", "-c", command, NULL); + exit(0); + } + + close(in[0]); + close(out[1]); + close(err[1]); + + cdata->stdin = in[1]; + cdata->stdout = out[0]; + cdata->stderr = err[0]; + + return SSH_OK; + +fork_failed: + close(err[0]); + close(err[1]); +stderr_failed: + close(out[0]); + close(out[1]); +stdout_failed: + close(in[0]); + close(in[1]); +stdin_failed: + return SSH_ERROR; +} + +static int exec_request(ssh_session session, ssh_channel channel, + const char *command, void *userdata) { + struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; + + + (void) session; + (void) channel; + + if(cdata->pid > 0) { + return SSH_ERROR; + } + + if (cdata->pty_master != -1 && cdata->pty_slave != -1) { + return exec_pty("-c", command, cdata); + } + return exec_nopty(command, cdata); +} + +static int shell_request(ssh_session session, ssh_channel channel, + void *userdata) { + struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; + + (void) session; + (void) channel; + + if(cdata->pid > 0) { + return SSH_ERROR; + } + + if (cdata->pty_master != -1 && cdata->pty_slave != -1) { + return exec_pty("-l", NULL, cdata); + } + /* Client requested a shell without a pty, let's pretend we allow that */ + return SSH_OK; +} + +static int subsystem_request(ssh_session session, ssh_channel channel, + const char *subsystem, void *userdata) { + /* subsystem requests behave simillarly to exec requests. */ + if (strcmp(subsystem, "sftp") == 0) { + return exec_request(session, channel, SFTP_SERVER_PATH, userdata); + } + return SSH_ERROR; +} + +static int auth_password(ssh_session session, const char *user, + const char *pass, void *userdata) { + struct session_data_struct *sdata = (struct session_data_struct *) userdata; + + (void) session; + + if (strcmp(user, USER) == 0 && strcmp(pass, PASS) == 0) { + sdata->authenticated = 1; + return SSH_AUTH_SUCCESS; + } + + sdata->auth_attempts++; + return SSH_AUTH_DENIED; +} + +static ssh_channel channel_open(ssh_session session, void *userdata) { + struct session_data_struct *sdata = (struct session_data_struct *) userdata; + + sdata->channel = ssh_channel_new(session); + return sdata->channel; +} + +static int process_stdout(socket_t fd, int revents, void *userdata) { + char buf[BUF_SIZE]; + int n = -1; + ssh_channel channel = (ssh_channel) userdata; + + if (channel != NULL && (revents & POLLIN) != 0) { + n = read(fd, buf, BUF_SIZE); + if (n > 0) { + ssh_channel_write(channel, buf, n); + } + } + + return n; +} + +static int process_stderr(socket_t fd, int revents, void *userdata) { + char buf[BUF_SIZE]; + int n = -1; + ssh_channel channel = (ssh_channel) userdata; + + if (channel != NULL && (revents & POLLIN) != 0) { + n = read(fd, buf, BUF_SIZE); + if (n > 0) { + ssh_channel_write_stderr(channel, buf, n); + } + } + + return n; +} + +static void handle_session(ssh_event event, ssh_session session) { + int n, rc; + + /* Structure for storing the pty size. */ + struct winsize wsize = { + .ws_row = 0, + .ws_col = 0, + .ws_xpixel = 0, + .ws_ypixel = 0 + }; + + /* Our struct holding information about the channel. */ + struct channel_data_struct cdata = { + .pid = 0, + .pty_master = -1, + .pty_slave = -1, + .stdin = -1, + .stdout = -1, + .stderr = -1, + .event = NULL, + .winsize = &wsize + }; + + /* Our struct holding information about the session. */ + struct session_data_struct sdata = { + .channel = NULL, + .auth_attempts = 0, + .authenticated = 0 + }; + + struct ssh_channel_callbacks_struct channel_cb = { + .userdata = &cdata, + .channel_pty_request_function = pty_request, + .channel_pty_window_change_function = pty_resize, + .channel_shell_request_function = shell_request, + .channel_exec_request_function = exec_request, + .channel_data_function = data_function, + .channel_subsystem_request_function = subsystem_request + }; + + struct ssh_server_callbacks_struct server_cb = { + .userdata = &sdata, + .auth_password_function = auth_password, + .channel_open_request_session_function = channel_open, + }; + + ssh_callbacks_init(&server_cb); + ssh_callbacks_init(&channel_cb); + + ssh_set_server_callbacks(session, &server_cb); + + if (ssh_handle_key_exchange(session) != SSH_OK) { + fprintf(stderr, "%s\n", ssh_get_error(session)); + return; + } + + ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD); + ssh_event_add_session(event, session); + + n = 0; + while (sdata.authenticated == 0 || sdata.channel == NULL) { + /* If the user has used up all attempts, or if he hasn't been able to + * authenticate in 10 seconds (n * 100ms), disconnect. */ + if (sdata.auth_attempts >= 3 || n >= 100) { + return; + } + + if (ssh_event_dopoll(event, 100) == SSH_ERROR) { + fprintf(stderr, "%s\n", ssh_get_error(session)); + return; + } + n++; + } + + ssh_set_channel_callbacks(sdata.channel, &channel_cb); + + do { + /* Poll the main event which takes care of the session, the channel and + * even our child process's stdout/stderr (once it's started). */ + if (ssh_event_dopoll(event, -1) == SSH_ERROR) { + ssh_channel_close(sdata.channel); + } + + /* If child process's stdout/stderr has been registered with the event, + * or the child process hasn't started yet, continue. */ + if (cdata.event != NULL || cdata.pid == 0) { + continue; + } + /* Executed only once, once the child process starts. */ + cdata.event = event; + /* If stdout valid, add stdout to be monitored by the poll event. */ + if (cdata.stdout != -1) { + if (ssh_event_add_fd(event, cdata.stdout, POLLIN, process_stdout, + sdata.channel) != SSH_OK) { + fprintf(stderr, "Failed to register stdout to poll context\n"); + ssh_channel_close(sdata.channel); + } + } + + /* If stderr valid, add stderr to be monitored by the poll event. */ + if (cdata.stderr != -1){ + if (ssh_event_add_fd(event, cdata.stderr, POLLIN, process_stderr, + sdata.channel) != SSH_OK) { + fprintf(stderr, "Failed to register stderr to poll context\n"); + ssh_channel_close(sdata.channel); + } + } + } while(ssh_channel_is_open(sdata.channel) && + (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0)); + + close(cdata.pty_master); + close(cdata.stdin); + close(cdata.stdout); + close(cdata.stderr); + + /* Remove the descriptors from the polling context, since they are now + * closed, they will always trigger during the poll calls. */ + ssh_event_remove_fd(event, cdata.stdout); + ssh_event_remove_fd(event, cdata.stderr); + + /* If the child process exited. */ + if (kill(cdata.pid, 0) < 0 && WIFEXITED(rc)) { + rc = WEXITSTATUS(rc); + ssh_channel_request_send_exit_status(sdata.channel, rc); + /* If client terminated the channel or the process did not exit nicely, + * but only if something has been forked. */ + } else if (cdata.pid > 0) { + kill(cdata.pid, SIGKILL); + } + + ssh_channel_send_eof(sdata.channel); + ssh_channel_close(sdata.channel); + + /* Wait up to 5 seconds for the client to terminate the session. */ + for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { + ssh_event_dopoll(event, 100); + } +} + +/* SIGCHLD handler for cleaning up dead children. */ +static void sigchld_handler(int signo) { + (void) signo; + while (waitpid(-1, NULL, WNOHANG) > 0); +} + +int main(int argc, char **argv) { + ssh_bind sshbind; + ssh_session session; + ssh_event event; + struct sigaction sa; + + /* Set up SIGCHLD handler. */ + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; + if (sigaction(SIGCHLD, &sa, NULL) != 0) { + fprintf(stderr, "Failed to register SIGCHLD handler\n"); + return 1; + } + + ssh_init(); + sshbind = ssh_bind_new(); + +#ifdef HAVE_ARGP_H + argp_parse(&argp, argc, argv, 0, 0, sshbind); +#else + (void) argc; + (void) argv; + + set_default_keys(sshbind, 0, 0, 0); +#endif /* HAVE_ARGP_H */ + + if(ssh_bind_listen(sshbind) < 0) { + fprintf(stderr, "%s\n", ssh_get_error(sshbind)); + return 1; + } + + while (1) { + session = ssh_new(); + if (session == NULL) { + fprintf(stderr, "Failed to allocate session\n"); + continue; + } + + /* Blocks until there is a new incoming connection. */ + if(ssh_bind_accept(sshbind, session) != SSH_ERROR) { + switch(fork()) { + case 0: + /* Remove the SIGCHLD handler inherited from parent. */ + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + /* Remove socket binding, which allows us to restart the + * parent process, without terminating existing sessions. */ + ssh_bind_free(sshbind); + + event = ssh_event_new(); + if (event != NULL) { + /* Blocks until the SSH session ends by either + * child process exiting, or client disconnecting. */ + handle_session(event, session); + ssh_event_free(event); + } else { + fprintf(stderr, "Could not create polling context\n"); + } + ssh_disconnect(session); + ssh_free(session); + + exit(0); + case -1: + fprintf(stderr, "Failed to fork\n"); + } + } else { + fprintf(stderr, "%s\n", ssh_get_error(sshbind)); + } + /* Since the session has been passed to a child fork, do some cleaning + * up at the parent process. */ + ssh_disconnect(session); + ssh_free(session); + } + + ssh_bind_free(sshbind); + ssh_finalize(); + return 0; +} diff --git a/libssh/examples/sshnetcat.c b/libssh/examples/sshnetcat.c index 052cabf4..8ac5a212 100644 --- a/libssh/examples/sshnetcat.c +++ b/libssh/examples/sshnetcat.c @@ -87,13 +87,23 @@ static void select_loop(ssh_session session,ssh_channel channel){ int ret; while(channel){ do{ + int fd; + FD_ZERO(&fds); if(!eof) FD_SET(0,&fds); timeout.tv_sec=30; timeout.tv_usec=0; - FD_SET(ssh_get_fd(session),&fds); - maxfd=ssh_get_fd(session)+1; + + fd = ssh_get_fd(session); + if (fd == -1) { + fprintf(stderr, "Error getting the session file descriptor: %s\n", + ssh_get_error(session)); + return; + } + FD_SET(fd, &fds); + maxfd = fd + 1; + channels[0]=channel; // set the first channel we want to read from channels[1]=NULL; ret=ssh_select(channels,outchannels,maxfd,&fds,&timeout); @@ -102,27 +112,27 @@ static void select_loop(ssh_session session,ssh_channel channel){ if(FD_ISSET(0,&fds)){ lus=read(0,buffer,sizeof(buffer)); if(lus) - channel_write(channel,buffer,lus); + ssh_channel_write(channel,buffer,lus); else { eof=1; - channel_send_eof(channel); + ssh_channel_send_eof(channel); } } - if(channel && channel_is_closed(channel)){ - channel_free(channel); + if(channel && ssh_channel_is_closed(channel)){ + ssh_channel_free(channel); channel=NULL; channels[0]=NULL; } if(outchannels[0]){ - while(channel && channel_is_open(channel) && channel_poll(channel,0)){ - lus=channel_read(channel,buffer,sizeof(buffer),0); + while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,0)){ + lus = ssh_channel_read(channel,buffer,sizeof(buffer),0); if(lus==-1){ fprintf(stderr, "Error reading channel: %s\n", ssh_get_error(session)); return; } if(lus==0){ - channel_free(channel); + ssh_channel_free(channel); channel=channels[0]=NULL; } else { ret = write(1, buffer, lus); @@ -133,27 +143,28 @@ static void select_loop(ssh_session session,ssh_channel channel){ } } } - while(channel && channel_is_open(channel) && channel_poll(channel,1)){ /* stderr */ - lus=channel_read(channel,buffer,sizeof(buffer),1); + while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,1)){ /* stderr */ + lus = ssh_channel_read(channel, buffer, sizeof(buffer), 1); if(lus==-1){ fprintf(stderr, "Error reading channel: %s\n", ssh_get_error(session)); return; } if(lus==0){ - channel_free(channel); + ssh_channel_free(channel); channel=channels[0]=NULL; - } else + } else { ret = write(2, buffer, lus); if (ret < 0) { fprintf(stderr, "Error writing to stderr: %s", strerror(errno)); return; } + } } } - if(channel && channel_is_closed(channel)){ - channel_free(channel); + if(channel && ssh_channel_is_closed(channel)){ + ssh_channel_free(channel); channel=NULL; } } while (ret==EINTR || ret==SSH_EINTR); @@ -164,8 +175,8 @@ static void select_loop(ssh_session session,ssh_channel channel){ static void forwarding(ssh_session session){ ssh_channel channel; int r; - channel=channel_new(session); - r=channel_open_forward(channel,desthost,atoi(port),"localhost",22); + channel = ssh_channel_new(session); + r = ssh_channel_open_forward(channel, desthost, atoi(port), "localhost", 22); if(r<0) { printf("error forwarding port : %s\n",ssh_get_error(session)); return; diff --git a/libssh/include/libssh/bignum.h b/libssh/include/libssh/bignum.h new file mode 100644 index 00000000..e5f2a472 --- /dev/null +++ b/libssh/include/libssh/bignum.h @@ -0,0 +1,32 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2014 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BIGNUM_H_ +#define BIGNUM_H_ + +#include "libssh/libcrypto.h" +#include "libssh/libgcrypt.h" + +bignum make_string_bn(ssh_string string); +ssh_string make_bignum_string(bignum num); +void ssh_print_bignum(const char *which,bignum num); + + +#endif /* BIGNUM_H_ */ diff --git a/libssh/include/libssh/buffer.h b/libssh/include/libssh/buffer.h index d7cdfbf4..2aebe7e7 100644 --- a/libssh/include/libssh/buffer.h +++ b/libssh/include/libssh/buffer.h @@ -21,6 +21,8 @@ #ifndef BUFFER_H_ #define BUFFER_H_ +#include + #include "libssh/libssh.h" /* * Describes a buffer state @@ -34,21 +36,32 @@ struct ssh_buffer_struct { uint32_t used; uint32_t allocated; uint32_t pos; + int secure; }; +#define SSH_BUFFER_PACK_END ((uint32_t) 0x4f65feb3) + LIBSSH_API void ssh_buffer_free(ssh_buffer buffer); LIBSSH_API void *ssh_buffer_get_begin(ssh_buffer buffer); LIBSSH_API uint32_t ssh_buffer_get_len(ssh_buffer buffer); LIBSSH_API ssh_buffer ssh_buffer_new(void); +void ssh_buffer_set_secure(ssh_buffer buffer); int buffer_add_ssh_string(ssh_buffer buffer, ssh_string string); int buffer_add_u8(ssh_buffer buffer, uint8_t data); int buffer_add_u16(ssh_buffer buffer, uint16_t data); int buffer_add_u32(ssh_buffer buffer, uint32_t data); int buffer_add_u64(ssh_buffer buffer, uint64_t data); -int buffer_add_data(ssh_buffer buffer, const void *data, uint32_t len); +int ssh_buffer_add_data(ssh_buffer buffer, const void *data, uint32_t len); +int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer, const char *format, va_list ap); +int _ssh_buffer_pack(struct ssh_buffer_struct *buffer, const char *format, ...); +#define ssh_buffer_pack(buffer, format, ...) _ssh_buffer_pack((buffer),(format), __VA_ARGS__, SSH_BUFFER_PACK_END) +int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer, const char *format, va_list ap); +int _ssh_buffer_unpack(struct ssh_buffer_struct *buffer, const char *format, ...); +#define ssh_buffer_unpack(buffer, format, ...) _ssh_buffer_unpack((buffer),(format), __VA_ARGS__, SSH_BUFFER_PACK_END) + int buffer_prepend_data(ssh_buffer buffer, const void *data, uint32_t len); int buffer_add_buffer(ssh_buffer buffer, ssh_buffer source); -int buffer_reinit(ssh_buffer buffer); +int ssh_buffer_reinit(ssh_buffer buffer); /* buffer_get_rest returns a pointer to the current position into the buffer */ void *buffer_get_rest(ssh_buffer buffer); diff --git a/libssh/include/libssh/callbacks.h b/libssh/include/libssh/callbacks.h index a841f2e5..6bd8c573 100644 --- a/libssh/include/libssh/callbacks.h +++ b/libssh/include/libssh/callbacks.h @@ -495,6 +495,8 @@ LIBSSH_API int ssh_set_callbacks(ssh_session session, ssh_callbacks cb); * @param len the length of the data * @param is_stderr is 0 for stdout or 1 for stderr * @param userdata Userdata to be passed to the callback function. + * @returns number of bytes processed by the callee. The remaining bytes will + * be sent in the next callback message, when more data is available. */ typedef int (*ssh_channel_data_callback) (ssh_session session, ssh_channel channel, @@ -801,6 +803,8 @@ struct ssh_threads_callbacks_struct { * * @see ssh_threads_callbacks_struct * @see SSH_THREADS_PTHREAD + * @bug libgcrypt 1.6 and bigger backend does not support custom callback. + * Using anything else than pthreads here will fail. */ LIBSSH_API int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb); diff --git a/libssh/include/libssh/channels.h b/libssh/include/libssh/channels.h index 4c726453..b05c4c02 100644 --- a/libssh/include/libssh/channels.h +++ b/libssh/include/libssh/channels.h @@ -75,6 +75,8 @@ struct ssh_channel_struct { int exit_status; enum ssh_channel_request_state_e request_state; ssh_channel_callbacks callbacks; + /* counters */ + ssh_counter counter; }; SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf); diff --git a/libssh/include/libssh/crypto.h b/libssh/include/libssh/crypto.h index eaff2ffd..61a2b27b 100644 --- a/libssh/include/libssh/crypto.h +++ b/libssh/include/libssh/crypto.h @@ -46,6 +46,8 @@ #include "libssh/kex.h" #include "libssh/curve25519.h" +#define DIGEST_MAX_LEN 64 + enum ssh_key_exchange_e { /* diffie-hellman-group1-sha1 */ SSH_KEX_DH_GROUP1_SHA1=1, @@ -79,8 +81,10 @@ struct ssh_crypto_struct { unsigned char *encryptkey; unsigned char *encryptMAC; unsigned char *decryptMAC; - unsigned char hmacbuf[EVP_MAX_MD_SIZE]; + unsigned char hmacbuf[DIGEST_MAX_LEN]; struct ssh_cipher_struct *in_cipher, *out_cipher; /* the cipher structures/objects */ + enum ssh_hmac_e in_hmac, out_hmac; /* the MAC algorithms used */ + ssh_string server_pubkey; const char *server_pubkey_type; int do_compress_out; /* idem */ @@ -111,9 +115,9 @@ struct ssh_cipher_struct { /* sets the new key for immediate use */ int (*set_encrypt_key)(struct ssh_cipher_struct *cipher, void *key, void *IV); int (*set_decrypt_key)(struct ssh_cipher_struct *cipher, void *key, void *IV); - void (*cbc_encrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, + void (*encrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, unsigned long len); - void (*cbc_decrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, + void (*decrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, unsigned long len); }; diff --git a/libssh/include/libssh/curve25519.h b/libssh/include/libssh/curve25519.h index 35e25be0..0406b9ee 100644 --- a/libssh/include/libssh/curve25519.h +++ b/libssh/include/libssh/curve25519.h @@ -39,7 +39,10 @@ int crypto_scalarmult_base(unsigned char *q, const unsigned char *n); int crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned char *p); #endif /* WITH_NACL */ -#define HAVE_CURVE25519 +#ifdef HAVE_ECC +#define HAVE_CURVE25519 1 +#endif + typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE]; typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE]; diff --git a/libssh/include/libssh/dh.h b/libssh/include/libssh/dh.h index e1039e24..95b76cdd 100644 --- a/libssh/include/libssh/dh.h +++ b/libssh/include/libssh/dh.h @@ -25,7 +25,6 @@ #include "libssh/crypto.h" -void ssh_print_bignum(const char *which,bignum num); int dh_generate_e(ssh_session session); int dh_generate_f(ssh_session session); int dh_generate_x(ssh_session session); @@ -48,8 +47,5 @@ int make_sessionid(ssh_session session); int hashbufin_add_cookie(ssh_session session, unsigned char *cookie); int hashbufout_add_cookie(ssh_session session); int generate_session_keys(ssh_session session); -bignum make_string_bn(ssh_string string); -ssh_string make_bignum_string(bignum num); - #endif /* DH_H_ */ diff --git a/libssh/include/libssh/ecdh.h b/libssh/include/libssh/ecdh.h index b35b2af7..8d1e7515 100644 --- a/libssh/include/libssh/ecdh.h +++ b/libssh/include/libssh/ecdh.h @@ -26,7 +26,9 @@ #ifdef HAVE_LIBCRYPTO #ifdef HAVE_OPENSSL_ECDH_H -#define HAVE_ECDH +#ifdef HAVE_ECC +#define HAVE_ECDH 1 +#endif #endif /* HAVE_OPENSSL_ECDH_H */ #endif /* HAVE_LIBCRYPTO */ diff --git a/libssh/include/libssh/ed25519.h b/libssh/include/libssh/ed25519.h new file mode 100644 index 00000000..7b48856c --- /dev/null +++ b/libssh/include/libssh/ed25519.h @@ -0,0 +1,79 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2014 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ED25519_H_ +#define ED25519_H_ +#include "libssh/priv.h" + +/** + * @defgroup ed25519 ed25519 API + * @internal + * @brief API for DJB's ed25519 + * + * @{ */ + +#define ED25519_PK_LEN 32 +#define ED25519_SK_LEN 64 +#define ED25519_SIG_LEN 64 + +typedef uint8_t ed25519_pubkey[ED25519_PK_LEN]; +typedef uint8_t ed25519_privkey[ED25519_SK_LEN]; +typedef uint8_t ed25519_signature[ED25519_SIG_LEN]; + +/** @internal + * @brief generate an ed25519 key pair + * @param[out] pk generated public key + * @param[out] sk generated secret key + * @return 0 on success, -1 on error. + * */ +int crypto_sign_ed25519_keypair(ed25519_pubkey pk, ed25519_privkey sk); + +/** @internal + * @brief sign a message with ed25519 + * @param[out] sm location to store the signed message. + * Its length should be mlen + 64. + * @param[out] smlen pointer to the size of the signed message + * @param[in] m message to be signed + * @param[in] mlen length of the message to be signed + * @param[in] sk secret key to sign the message with + * @return 0 on success. + */ +int crypto_sign_ed25519( + unsigned char *sm,unsigned long long *smlen, + const unsigned char *m,unsigned long long mlen, + const ed25519_privkey sk); + +/** @internal + * @brief "open" and verify the signature of a signed message + * @param[out] m location to store the verified message. + * Its length should be equal to smlen. + * @param[out] mlen pointer to the size of the verified message + * @param[in] sm signed message to verify + * @param[in] smlen length of the signed message to verify + * @param[in] pk public key used to sign the message + * @returns 0 on success (supposedly). + */ +int crypto_sign_ed25519_open( + unsigned char *m,unsigned long long *mlen, + const unsigned char *sm,unsigned long long smlen, + const ed25519_pubkey pk); + +/** @} */ +#endif /* ED25519_H_ */ diff --git a/libssh/include/libssh/fe25519.h b/libssh/include/libssh/fe25519.h new file mode 100644 index 00000000..e959912e --- /dev/null +++ b/libssh/include/libssh/fe25519.h @@ -0,0 +1,68 @@ +/* $OpenBSD: fe25519.h,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/fe25519.h + */ + +#ifndef FE25519_H +#define FE25519_H + +#include "libssh/priv.h" + +#define fe25519 crypto_sign_ed25519_ref_fe25519 +#define fe25519_freeze crypto_sign_ed25519_ref_fe25519_freeze +#define fe25519_unpack crypto_sign_ed25519_ref_fe25519_unpack +#define fe25519_pack crypto_sign_ed25519_ref_fe25519_pack +#define fe25519_iszero crypto_sign_ed25519_ref_fe25519_iszero +#define fe25519_iseq_vartime crypto_sign_ed25519_ref_fe25519_iseq_vartime +#define fe25519_cmov crypto_sign_ed25519_ref_fe25519_cmov +#define fe25519_setone crypto_sign_ed25519_ref_fe25519_setone +#define fe25519_setzero crypto_sign_ed25519_ref_fe25519_setzero +#define fe25519_neg crypto_sign_ed25519_ref_fe25519_neg +#define fe25519_getparity crypto_sign_ed25519_ref_fe25519_getparity +#define fe25519_add crypto_sign_ed25519_ref_fe25519_add +#define fe25519_sub crypto_sign_ed25519_ref_fe25519_sub +#define fe25519_mul crypto_sign_ed25519_ref_fe25519_mul +#define fe25519_square crypto_sign_ed25519_ref_fe25519_square +#define fe25519_invert crypto_sign_ed25519_ref_fe25519_invert +#define fe25519_pow2523 crypto_sign_ed25519_ref_fe25519_pow2523 + +typedef struct { + uint32_t v[32]; +} fe25519; + +void fe25519_freeze(fe25519 *r); + +void fe25519_unpack(fe25519 *r, const unsigned char x[32]); + +void fe25519_pack(unsigned char r[32], const fe25519 *x); + +int fe25519_iszero(const fe25519 *x); + +int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y); + +void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b); + +void fe25519_setone(fe25519 *r); + +void fe25519_setzero(fe25519 *r); + +void fe25519_neg(fe25519 *r, const fe25519 *x); + +unsigned char fe25519_getparity(const fe25519 *x); + +void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y); + +void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y); + +void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y); + +void fe25519_square(fe25519 *r, const fe25519 *x); + +void fe25519_invert(fe25519 *r, const fe25519 *x); + +void fe25519_pow2523(fe25519 *r, const fe25519 *x); + +#endif diff --git a/libssh/include/libssh/ge25519.h b/libssh/include/libssh/ge25519.h new file mode 100644 index 00000000..64f63c6f --- /dev/null +++ b/libssh/include/libssh/ge25519.h @@ -0,0 +1,43 @@ +/* $OpenBSD: ge25519.h,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519.h + */ + +#ifndef GE25519_H +#define GE25519_H + +#include "fe25519.h" +#include "sc25519.h" + +#define ge25519 crypto_sign_ed25519_ref_ge25519 +#define ge25519_base crypto_sign_ed25519_ref_ge25519_base +#define ge25519_unpackneg_vartime crypto_sign_ed25519_ref_unpackneg_vartime +#define ge25519_pack crypto_sign_ed25519_ref_pack +#define ge25519_isneutral_vartime crypto_sign_ed25519_ref_isneutral_vartime +#define ge25519_double_scalarmult_vartime crypto_sign_ed25519_ref_double_scalarmult_vartime +#define ge25519_scalarmult_base crypto_sign_ed25519_ref_scalarmult_base + +typedef struct +{ + fe25519 x; + fe25519 y; + fe25519 z; + fe25519 t; +} ge25519; + +const ge25519 ge25519_base; + +int ge25519_unpackneg_vartime(ge25519 *r, const unsigned char p[32]); + +void ge25519_pack(unsigned char r[32], const ge25519 *p); + +int ge25519_isneutral_vartime(const ge25519 *p); + +void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const sc25519 *s1, const ge25519 *p2, const sc25519 *s2); + +void ge25519_scalarmult_base(ge25519 *r, const sc25519 *s); + +#endif diff --git a/libssh/include/libssh/knownhosts.h b/libssh/include/libssh/knownhosts.h new file mode 100644 index 00000000..723c7ebc --- /dev/null +++ b/libssh/include/libssh/knownhosts.h @@ -0,0 +1,27 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 20014 by Aris Adamantiadis + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef KNOWNHOSTS_H_ +#define KNOWNHOSTS_H_ + +char **ssh_knownhosts_algorithms(ssh_session session); + +#endif /* KNOWNHOSTS_H_ */ diff --git a/libssh/include/libssh/legacy.h b/libssh/include/libssh/legacy.h index 771fe56f..911173ee 100644 --- a/libssh/include/libssh/legacy.h +++ b/libssh/include/libssh/legacy.h @@ -42,51 +42,51 @@ LIBSSH_API int ssh_userauth_autopubkey(ssh_session session, const char *passphra LIBSSH_API int ssh_userauth_privatekey_file(ssh_session session, const char *username, const char *filename, const char *passphrase); -LIBSSH_API void buffer_free(ssh_buffer buffer); -LIBSSH_API void *buffer_get(ssh_buffer buffer); -LIBSSH_API uint32_t buffer_get_len(ssh_buffer buffer); -LIBSSH_API ssh_buffer buffer_new(void); +SSH_DEPRECATED LIBSSH_API void buffer_free(ssh_buffer buffer); +SSH_DEPRECATED LIBSSH_API void *buffer_get(ssh_buffer buffer); +SSH_DEPRECATED LIBSSH_API uint32_t buffer_get_len(ssh_buffer buffer); +SSH_DEPRECATED LIBSSH_API ssh_buffer buffer_new(void); -LIBSSH_API ssh_channel channel_accept_x11(ssh_channel channel, int timeout_ms); -LIBSSH_API int channel_change_pty_size(ssh_channel channel,int cols,int rows); -LIBSSH_API ssh_channel channel_forward_accept(ssh_session session, int timeout_ms); -LIBSSH_API int channel_close(ssh_channel channel); -LIBSSH_API int channel_forward_cancel(ssh_session session, const char *address, int port); -LIBSSH_API int channel_forward_listen(ssh_session session, const char *address, int port, int *bound_port); -LIBSSH_API void channel_free(ssh_channel channel); -LIBSSH_API int channel_get_exit_status(ssh_channel channel); -LIBSSH_API ssh_session channel_get_session(ssh_channel channel); -LIBSSH_API int channel_is_closed(ssh_channel channel); -LIBSSH_API int channel_is_eof(ssh_channel channel); -LIBSSH_API int channel_is_open(ssh_channel channel); -LIBSSH_API ssh_channel channel_new(ssh_session session); -LIBSSH_API int channel_open_forward(ssh_channel channel, const char *remotehost, +SSH_DEPRECATED LIBSSH_API ssh_channel channel_accept_x11(ssh_channel channel, int timeout_ms); +SSH_DEPRECATED LIBSSH_API int channel_change_pty_size(ssh_channel channel,int cols,int rows); +SSH_DEPRECATED LIBSSH_API ssh_channel channel_forward_accept(ssh_session session, int timeout_ms); +SSH_DEPRECATED LIBSSH_API int channel_close(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_forward_cancel(ssh_session session, const char *address, int port); +SSH_DEPRECATED LIBSSH_API int channel_forward_listen(ssh_session session, const char *address, int port, int *bound_port); +SSH_DEPRECATED LIBSSH_API void channel_free(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_get_exit_status(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API ssh_session channel_get_session(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_is_closed(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_is_eof(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_is_open(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API ssh_channel channel_new(ssh_session session); +SSH_DEPRECATED LIBSSH_API int channel_open_forward(ssh_channel channel, const char *remotehost, int remoteport, const char *sourcehost, int localport); -LIBSSH_API int channel_open_session(ssh_channel channel); -LIBSSH_API int channel_poll(ssh_channel channel, int is_stderr); -LIBSSH_API int channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); +SSH_DEPRECATED LIBSSH_API int channel_open_session(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_poll(ssh_channel channel, int is_stderr); +SSH_DEPRECATED LIBSSH_API int channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); -LIBSSH_API int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, +SSH_DEPRECATED LIBSSH_API int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, int is_stderr); -LIBSSH_API int channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, +SSH_DEPRECATED LIBSSH_API int channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, int is_stderr); -LIBSSH_API int channel_request_env(ssh_channel channel, const char *name, const char *value); -LIBSSH_API int channel_request_exec(ssh_channel channel, const char *cmd); -LIBSSH_API int channel_request_pty(ssh_channel channel); -LIBSSH_API int channel_request_pty_size(ssh_channel channel, const char *term, +SSH_DEPRECATED LIBSSH_API int channel_request_env(ssh_channel channel, const char *name, const char *value); +SSH_DEPRECATED LIBSSH_API int channel_request_exec(ssh_channel channel, const char *cmd); +SSH_DEPRECATED LIBSSH_API int channel_request_pty(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_request_pty_size(ssh_channel channel, const char *term, int cols, int rows); -LIBSSH_API int channel_request_shell(ssh_channel channel); -LIBSSH_API int channel_request_send_signal(ssh_channel channel, const char *signum); -LIBSSH_API int channel_request_sftp(ssh_channel channel); -LIBSSH_API int channel_request_subsystem(ssh_channel channel, const char *subsystem); -LIBSSH_API int channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, +SSH_DEPRECATED LIBSSH_API int channel_request_shell(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_request_send_signal(ssh_channel channel, const char *signum); +SSH_DEPRECATED LIBSSH_API int channel_request_sftp(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_request_subsystem(ssh_channel channel, const char *subsystem); +SSH_DEPRECATED LIBSSH_API int channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, const char *cookie, int screen_number); -LIBSSH_API int channel_send_eof(ssh_channel channel); -LIBSSH_API int channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct +SSH_DEPRECATED LIBSSH_API int channel_send_eof(ssh_channel channel); +SSH_DEPRECATED LIBSSH_API int channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct timeval * timeout); -LIBSSH_API void channel_set_blocking(ssh_channel channel, int blocking); -LIBSSH_API int channel_write(ssh_channel channel, const void *data, uint32_t len); +SSH_DEPRECATED LIBSSH_API void channel_set_blocking(ssh_channel channel, int blocking); +SSH_DEPRECATED LIBSSH_API int channel_write(ssh_channel channel, const void *data, uint32_t len); LIBSSH_API void privatekey_free(ssh_private_key prv); LIBSSH_API ssh_private_key privatekey_from_file(ssh_session session, const char *filename, @@ -107,14 +107,14 @@ LIBSSH_API ssh_string ssh_get_pubkey(ssh_session session); LIBSSH_API ssh_message ssh_message_retrieve(ssh_session session, uint32_t packettype); LIBSSH_API ssh_public_key ssh_message_auth_publickey(ssh_message msg); -LIBSSH_API void string_burn(ssh_string str); -LIBSSH_API ssh_string string_copy(ssh_string str); -LIBSSH_API void *string_data(ssh_string str); -LIBSSH_API int string_fill(ssh_string str, const void *data, size_t len); -LIBSSH_API void string_free(ssh_string str); -LIBSSH_API ssh_string string_from_char(const char *what); -LIBSSH_API size_t string_len(ssh_string str); -LIBSSH_API ssh_string string_new(size_t size); -LIBSSH_API char *string_to_char(ssh_string str); +SSH_DEPRECATED LIBSSH_API void string_burn(ssh_string str); +SSH_DEPRECATED LIBSSH_API ssh_string string_copy(ssh_string str); +SSH_DEPRECATED LIBSSH_API void *string_data(ssh_string str); +SSH_DEPRECATED LIBSSH_API int string_fill(ssh_string str, const void *data, size_t len); +SSH_DEPRECATED LIBSSH_API void string_free(ssh_string str); +SSH_DEPRECATED LIBSSH_API ssh_string string_from_char(const char *what); +SSH_DEPRECATED LIBSSH_API size_t string_len(ssh_string str); +SSH_DEPRECATED LIBSSH_API ssh_string string_new(size_t size); +SSH_DEPRECATED LIBSSH_API char *string_to_char(ssh_string str); #endif /* LEGACY_H_ */ diff --git a/libssh/include/libssh/libcrypto.h b/libssh/include/libssh/libcrypto.h index 5cf2da28..c3783880 100644 --- a/libssh/include/libssh/libcrypto.h +++ b/libssh/include/libssh/libcrypto.h @@ -36,6 +36,8 @@ typedef SHA_CTX* SHACTX; typedef SHA256_CTX* SHA256CTX; +typedef SHA512_CTX* SHA384CTX; +typedef SHA512_CTX* SHA512CTX; typedef MD5_CTX* MD5CTX; typedef HMAC_CTX* HMACCTX; #ifdef HAVE_ECC @@ -45,6 +47,9 @@ typedef void *EVPCTX; #endif #define SHA_DIGEST_LEN SHA_DIGEST_LENGTH +#define SHA256_DIGEST_LEN SHA256_DIGEST_LENGTH +#define SHA384_DIGEST_LEN SHA384_DIGEST_LENGTH +#define SHA512_DIGEST_LEN SHA512_DIGEST_LENGTH #ifdef MD5_DIGEST_LEN #undef MD5_DIGEST_LEN #endif @@ -84,6 +89,14 @@ SHA256CTX sha256_init(void); void sha256_update(SHA256CTX c, const void *data, unsigned long len); void sha256_final(unsigned char *md, SHA256CTX c); +SHA384CTX sha384_init(void); +void sha384_update(SHA384CTX c, const void *data, unsigned long len); +void sha384_final(unsigned char *md, SHA384CTX c); + +SHA512CTX sha512_init(void); +void sha512_update(SHA512CTX c, const void *data, unsigned long len); +void sha512_final(unsigned char *md, SHA512CTX c); + struct ssh_cipher_struct *ssh_get_ciphertab(void); #endif /* HAVE_LIBCRYPTO */ diff --git a/libssh/include/libssh/libgcrypt.h b/libssh/include/libssh/libgcrypt.h index c1844a53..8e52a681 100644 --- a/libssh/include/libssh/libgcrypt.h +++ b/libssh/include/libssh/libgcrypt.h @@ -27,6 +27,9 @@ #include typedef gcry_md_hd_t SHACTX; +typedef gcry_md_hd_t SHA256CTX; +typedef gcry_md_hd_t SHA384CTX; +typedef gcry_md_hd_t SHA512CTX; typedef gcry_md_hd_t MD5CTX; typedef gcry_md_hd_t HMACCTX; typedef void *EVPCTX; @@ -34,11 +37,14 @@ typedef void *EVPCTX; #define SHA_DIGEST_LEN SHA_DIGEST_LENGTH #define MD5_DIGEST_LEN 16 #define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_LEN SHA256_DIGEST_LENGTH #define SHA384_DIGEST_LENGTH 48 +#define SHA384_DIGEST_LEN SHA384_DIGEST_LENGTH #define SHA512_DIGEST_LENGTH 64 +#define SHA512_DIGEST_LEN SHA512_DIGEST_LENGTH #ifndef EVP_MAX_MD_SIZE -#define EVP_MAX_MD_SIZE 36 +#define EVP_MAX_MD_SIZE 64 #endif #define EVP_DIGEST_LEN EVP_MAX_MD_SIZE diff --git a/libssh/include/libssh/libssh.h b/libssh/include/libssh/libssh.h index 3833adcd..ba4f5f44 100644 --- a/libssh/include/libssh/libssh.h +++ b/libssh/include/libssh/libssh.h @@ -104,6 +104,13 @@ extern "C" { #endif +struct ssh_counter_struct { + uint64_t in_bytes; + uint64_t out_bytes; + uint64_t in_packets; + uint64_t out_packets; +}; +typedef struct ssh_counter_struct *ssh_counter; typedef struct ssh_agent_struct* ssh_agent; typedef struct ssh_buffer_struct* ssh_buffer; @@ -245,7 +252,8 @@ enum ssh_keytypes_e{ SSH_KEYTYPE_DSS=1, SSH_KEYTYPE_RSA, SSH_KEYTYPE_RSA1, - SSH_KEYTYPE_ECDSA + SSH_KEYTYPE_ECDSA, + SSH_KEYTYPE_ED25519 }; enum ssh_keycmp_e { @@ -331,7 +339,12 @@ enum ssh_options_e { SSH_OPTIONS_COMPRESSION, SSH_OPTIONS_COMPRESSION_LEVEL, SSH_OPTIONS_KEY_EXCHANGE, - SSH_OPTIONS_HOSTKEYS + SSH_OPTIONS_HOSTKEYS, + SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, + SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, + SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, + SSH_OPTIONS_HMAC_C_S, + SSH_OPTIONS_HMAC_S_C, }; enum { @@ -374,6 +387,7 @@ LIBSSH_API int ssh_channel_open_x11(ssh_channel channel, const char *orig_addr, LIBSSH_API int ssh_channel_poll(ssh_channel channel, int is_stderr); LIBSSH_API int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr); LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); +LIBSSH_API int ssh_channel_read_timeout(ssh_channel channel, void *dest, uint32_t count, int is_stderr, int timeout_ms); LIBSSH_API int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, int is_stderr); LIBSSH_API int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value); @@ -391,6 +405,8 @@ LIBSSH_API int ssh_channel_send_eof(ssh_channel channel); LIBSSH_API int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct timeval * timeout); LIBSSH_API void ssh_channel_set_blocking(ssh_channel channel, int blocking); +LIBSSH_API void ssh_channel_set_counter(ssh_channel channel, + ssh_counter counter); LIBSSH_API int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len); LIBSSH_API uint32_t ssh_channel_window_size(ssh_channel channel); @@ -401,9 +417,19 @@ LIBSSH_API const char *ssh_copyright(void); LIBSSH_API void ssh_disconnect(ssh_session session); LIBSSH_API char *ssh_dirname (const char *path); LIBSSH_API int ssh_finalize(void); -LIBSSH_API ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms); -LIBSSH_API int ssh_forward_cancel(ssh_session session, const char *address, int port); -LIBSSH_API int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port); + +/* REVERSE PORT FORWARDING */ +LIBSSH_API ssh_channel ssh_channel_accept_forward(ssh_session session, + int timeout_ms, + int *destination_port); +LIBSSH_API int ssh_channel_cancel_forward(ssh_session session, + const char *address, + int port); +LIBSSH_API int ssh_channel_listen_forward(ssh_session session, + const char *address, + int port, + int *bound_port); + LIBSSH_API void ssh_free(ssh_session session); LIBSSH_API const char *ssh_get_disconnect_message(ssh_session session); LIBSSH_API const char *ssh_get_error(void *error); @@ -424,11 +450,17 @@ LIBSSH_API int ssh_get_publickey_hash(const ssh_key key, unsigned char **hash, size_t *hlen); +/* DEPRECATED FUNCTIONS */ SSH_DEPRECATED LIBSSH_API int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash); +SSH_DEPRECATED LIBSSH_API ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms); +SSH_DEPRECATED LIBSSH_API int ssh_forward_cancel(ssh_session session, const char *address, int port); +SSH_DEPRECATED LIBSSH_API int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port); + LIBSSH_API int ssh_get_random(void *where,int len,int strong); LIBSSH_API int ssh_get_version(ssh_session session); LIBSSH_API int ssh_get_status(ssh_session session); +LIBSSH_API int ssh_get_poll_flags(ssh_session session); LIBSSH_API int ssh_init(void); LIBSSH_API int ssh_is_blocking(ssh_session session); LIBSSH_API int ssh_is_connected(ssh_session session); @@ -509,6 +541,11 @@ LIBSSH_API int ssh_pki_import_privkey_file(const char *filename, ssh_auth_callback auth_fn, void *auth_data, ssh_key *pkey); +LIBSSH_API int ssh_pki_export_privkey_file(const ssh_key privkey, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + const char *filename); LIBSSH_API int ssh_pki_import_pubkey_base64(const char *b64_key, enum ssh_keytypes_e type, @@ -523,6 +560,8 @@ LIBSSH_API int ssh_pki_export_pubkey_base64(const ssh_key key, LIBSSH_API int ssh_pki_export_pubkey_file(const ssh_key key, const char *filename); +LIBSSH_API const char *ssh_pki_key_ecdsa_name(const ssh_key key); + LIBSSH_API void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len); LIBSSH_API int ssh_send_ignore (ssh_session session, const char *data); LIBSSH_API int ssh_send_debug (ssh_session session, const char *message, int always_display); @@ -550,6 +589,8 @@ LIBSSH_API int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socke LIBSSH_API int ssh_service_request(ssh_session session, const char *service); LIBSSH_API int ssh_set_agent_channel(ssh_session session, ssh_channel channel); LIBSSH_API void ssh_set_blocking(ssh_session session, int blocking); +LIBSSH_API void ssh_set_counters(ssh_session session, ssh_counter scounter, + ssh_counter rcounter); LIBSSH_API void ssh_set_fd_except(ssh_session session); LIBSSH_API void ssh_set_fd_toread(ssh_session session); LIBSSH_API void ssh_set_fd_towrite(ssh_session session); @@ -615,7 +656,12 @@ LIBSSH_API int ssh_event_dopoll(ssh_event event, int timeout); LIBSSH_API int ssh_event_remove_fd(ssh_event event, socket_t fd); LIBSSH_API int ssh_event_remove_session(ssh_event event, ssh_session session); LIBSSH_API void ssh_event_free(ssh_event event); +LIBSSH_API const char* ssh_get_clientbanner(ssh_session session); LIBSSH_API const char* ssh_get_serverbanner(ssh_session session); +LIBSSH_API const char* ssh_get_cipher_in(ssh_session session); +LIBSSH_API const char* ssh_get_cipher_out(ssh_session session); +LIBSSH_API const char* ssh_get_hmac_in(ssh_session session); +LIBSSH_API const char* ssh_get_hmac_out(ssh_session session); #ifndef LIBSSH_LEGACY_0_4 #include "libssh/legacy.h" diff --git a/libssh/include/libssh/libsshpp.hpp b/libssh/include/libssh/libsshpp.hpp index 1a5d80a9..af08a914 100644 --- a/libssh/include/libssh/libsshpp.hpp +++ b/libssh/include/libssh/libsshpp.hpp @@ -56,6 +56,7 @@ #include #include #include +#include namespace ssh { @@ -361,18 +362,18 @@ public: * @see ssh_channel_forward_accept * @see Session::listenForward */ - Channel *acceptForward(int timeout_ms); - /* acceptForward is implemented later in this file */ + inline Channel *acceptForward(int timeout_ms); + /* implemented outside the class due Channel references */ void_throwable cancelForward(const char *address, int port){ - int err=ssh_forward_cancel(c_session, address, port); + int err=ssh_channel_cancel_forward(c_session, address, port); ssh_throw(err); return_throwable; } void_throwable listenForward(const char *address, int port, int &boundport){ - int err=ssh_forward_listen(c_session, address, port, &boundport); + int err=ssh_channel_listen_forward(c_session, address, port, &boundport); ssh_throw(err); return_throwable; } @@ -480,12 +481,30 @@ public: ssh_throw(err); return err; } - int read(void *dest, size_t count, bool is_stderr=false){ + int read(void *dest, size_t count, bool is_stderr){ int err; /* handle int overflow */ if(count > 0x7fffffff) count = 0x7fffffff; - err=ssh_channel_read(channel,dest,count,is_stderr); + err=ssh_channel_read_timeout(channel,dest,count,is_stderr,-1); + ssh_throw(err); + return err; + } + int read(void *dest, size_t count, int timeout){ + int err; + /* handle int overflow */ + if(count > 0x7fffffff) + count = 0x7fffffff; + err=ssh_channel_read_timeout(channel,dest,count,false,timeout); + ssh_throw(err); + return err; + } + int read(void *dest, size_t count, bool is_stderr=false, int timeout=-1){ + int err; + /* handle int overflow */ + if(count > 0x7fffffff) + count = 0x7fffffff; + err=ssh_channel_read_timeout(channel,dest,count,is_stderr,timeout); ssh_throw(err); return err; } @@ -581,10 +600,9 @@ private: }; -/* This code cannot be put inline due to references to Channel */ -Channel *Session::acceptForward(int timeout_ms){ - ssh_channel forward = ssh_forward_accept(c_session, - timeout_ms); +inline Channel *Session::acceptForward(int timeout_ms){ + ssh_channel forward = + ssh_channel_accept_forward(c_session, timeout_ms, NULL); ssh_throw_null(c_session,forward); Channel *newchan = new Channel(*this,forward); return newchan; diff --git a/libssh/include/libssh/packet.h b/libssh/include/libssh/packet.h index 513eaa81..d8ef35bb 100644 --- a/libssh/include/libssh/packet.h +++ b/libssh/include/libssh/packet.h @@ -21,6 +21,8 @@ #ifndef PACKET_H_ #define PACKET_H_ +#include "libssh/wrapper.h" + struct ssh_socket_struct; /* this structure should go someday */ @@ -82,6 +84,6 @@ unsigned char *packet_encrypt(ssh_session session, void *packet, unsigned int len); int packet_hmac_verify(ssh_session session,ssh_buffer buffer, - unsigned char *mac); + unsigned char *mac, enum ssh_hmac_e type); #endif /* PACKET_H_ */ diff --git a/libssh/include/libssh/pki.h b/libssh/include/libssh/pki.h index 89a0f982..9f9ddf4a 100644 --- a/libssh/include/libssh/pki.h +++ b/libssh/include/libssh/pki.h @@ -21,6 +21,7 @@ #ifndef PKI_H_ #define PKI_H_ +#include "libssh/priv.h" #ifdef HAVE_OPENSSL_EC_H #include #endif @@ -29,6 +30,7 @@ #endif #include "libssh/crypto.h" +#include "libssh/ed25519.h" #define MAX_PUBKEY_SIZE 0x100000 /* 1M */ #define MAX_PRIVKEY_SIZE 0x400000 /* 4M */ @@ -55,6 +57,8 @@ struct ssh_key_struct { void *ecdsa; #endif /* HAVE_OPENSSL_EC_H */ #endif + ed25519_pubkey *ed25519_pubkey; + ed25519_privkey *ed25519_privkey; void *cert; }; @@ -74,6 +78,7 @@ struct ssh_signature_struct { void *ecdsa_sig; # endif #endif + ed25519_signature *ed25519_sig; }; typedef struct ssh_signature_struct *ssh_signature; diff --git a/libssh/include/libssh/pki_priv.h b/libssh/include/libssh/pki_priv.h index 2c361e43..0aaadb60 100644 --- a/libssh/include/libssh/pki_priv.h +++ b/libssh/include/libssh/pki_priv.h @@ -34,12 +34,15 @@ void _ssh_pki_log(const char *function, const char *format, ...) PRINTF_ATTRIBUTE(2, 3); int pki_key_ecdsa_nid_from_name(const char *name); +const char *pki_key_ecdsa_nid_to_name(int nid); /* SSH Key Functions */ ssh_key pki_key_dup(const ssh_key key, int demote); int pki_key_generate_rsa(ssh_key key, int parameter); int pki_key_generate_dss(ssh_key key, int parameter); int pki_key_generate_ecdsa(ssh_key key, int parameter); +int pki_key_generate_ed25519(ssh_key key); + int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what); @@ -51,6 +54,11 @@ ssh_key pki_private_key_from_base64(const char *b64_key, ssh_auth_callback auth_fn, void *auth_data); +ssh_string pki_private_key_to_pem(const ssh_key key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data); + /* SSH Public Key Functions */ int pki_pubkey_build_dss(ssh_key key, ssh_string p, @@ -85,4 +93,16 @@ ssh_signature pki_do_sign(const ssh_key privkey, ssh_signature pki_do_sign_sessionid(const ssh_key key, const unsigned char *hash, size_t hlen); +int pki_ed25519_sign(const ssh_key privkey, ssh_signature sig, + const unsigned char *hash, size_t hlen); +int pki_ed25519_verify(const ssh_key pubkey, ssh_signature sig, + const unsigned char *hash, size_t hlen); +int pki_ed25519_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what); +int pki_ed25519_key_dup(ssh_key new, const ssh_key key); +int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key); +ssh_string pki_ed25519_sig_to_blob(ssh_signature sig); +int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob); + #endif /* PKI_PRIV_H_ */ diff --git a/libssh/include/libssh/priv.h b/libssh/include/libssh/priv.h index d8176d90..0e3bab5b 100644 --- a/libssh/include/libssh/priv.h +++ b/libssh/include/libssh/priv.h @@ -120,11 +120,24 @@ int gettimeofday(struct timeval *__p, void *__t); #include "libssh/callbacks.h" /* some constants */ +#ifndef MAX_PACKAT_LEN #define MAX_PACKET_LEN 262144 +#endif +#ifndef ERROR_BUFFERLEN #define ERROR_BUFFERLEN 1024 +#endif +#ifndef CLIENTBANNER1 #define CLIENTBANNER1 "SSH-1.5-libssh-" SSH_STRINGIFY(LIBSSH_VERSION) +#endif +#ifndef CLIENTBANNER2 #define CLIENTBANNER2 "SSH-2.0-libssh-" SSH_STRINGIFY(LIBSSH_VERSION) +#endif +#ifndef KBDINT_MAX_PROMPT #define KBDINT_MAX_PROMPT 256 /* more than openssh's :) */ +#endif +#ifndef MAX_BUF_SIZE +#define MAX_BUF_SIZE 4096 +#endif #ifndef __FUNCTION__ #if defined(__SUNPRO_C) @@ -155,16 +168,6 @@ int gettimeofday(struct timeval *__p, void *__t); #include #endif -/* - * get rid of deprecacy warnings on OSX when using OpenSSL - */ -#if defined(__APPLE__) - #ifdef MAC_OS_X_VERSION_MIN_REQUIRED - #undef MAC_OS_X_VERSION_MIN_REQUIRED - #endif - #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6 -#endif - /* forward declarations */ struct ssh_common_struct; struct ssh_kex_struct; @@ -275,7 +278,7 @@ int match_hostname(const char *host, const char *pattern, unsigned int len); /** Overwrite the buffer with '\0' */ # define BURN_BUFFER(x, size) do { \ if ((x) != NULL) \ - memset((x), '\0', (size)); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ + memset((x), '\0', (size)); \ } while(0) #endif /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */ diff --git a/libssh/include/libssh/sc25519.h b/libssh/include/libssh/sc25519.h new file mode 100644 index 00000000..5a2c1b85 --- /dev/null +++ b/libssh/include/libssh/sc25519.h @@ -0,0 +1,74 @@ +/* $OpenBSD: sc25519.h,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/sc25519.h + */ + +#ifndef SC25519_H +#define SC25519_H + +#define sc25519 crypto_sign_ed25519_ref_sc25519 +#define shortsc25519 crypto_sign_ed25519_ref_shortsc25519 +#define sc25519_from32bytes crypto_sign_ed25519_ref_sc25519_from32bytes +#define shortsc25519_from16bytes crypto_sign_ed25519_ref_shortsc25519_from16bytes +#define sc25519_from64bytes crypto_sign_ed25519_ref_sc25519_from64bytes +#define sc25519_from_shortsc crypto_sign_ed25519_ref_sc25519_from_shortsc +#define sc25519_to32bytes crypto_sign_ed25519_ref_sc25519_to32bytes +#define sc25519_iszero_vartime crypto_sign_ed25519_ref_sc25519_iszero_vartime +#define sc25519_isshort_vartime crypto_sign_ed25519_ref_sc25519_isshort_vartime +#define sc25519_lt_vartime crypto_sign_ed25519_ref_sc25519_lt_vartime +#define sc25519_add crypto_sign_ed25519_ref_sc25519_add +#define sc25519_sub_nored crypto_sign_ed25519_ref_sc25519_sub_nored +#define sc25519_mul crypto_sign_ed25519_ref_sc25519_mul +#define sc25519_mul_shortsc crypto_sign_ed25519_ref_sc25519_mul_shortsc +#define sc25519_window3 crypto_sign_ed25519_ref_sc25519_window3 +#define sc25519_window5 crypto_sign_ed25519_ref_sc25519_window5 +#define sc25519_2interleave2 crypto_sign_ed25519_ref_sc25519_2interleave2 + +typedef struct { + uint32_t v[32]; +} sc25519; + +typedef struct { + uint32_t v[16]; +} shortsc25519; + +void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]); + +void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16]); + +void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]); + +void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x); + +void sc25519_to32bytes(unsigned char r[32], const sc25519 *x); + +int sc25519_iszero_vartime(const sc25519 *x); + +int sc25519_isshort_vartime(const sc25519 *x); + +int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y); + +void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y); + +void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y); + +void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y); + +void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y); + +/* Convert s into a representation of the form \sum_{i=0}^{84}r[i]2^3 + * with r[i] in {-4,...,3} + */ +void sc25519_window3(signed char r[85], const sc25519 *s); + +/* Convert s into a representation of the form \sum_{i=0}^{50}r[i]2^5 + * with r[i] in {-16,...,15} + */ +void sc25519_window5(signed char r[51], const sc25519 *s); + +void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2); + +#endif diff --git a/libssh/include/libssh/server.h b/libssh/include/libssh/server.h index 9d095feb..385a10a7 100644 --- a/libssh/include/libssh/server.h +++ b/libssh/include/libssh/server.h @@ -44,7 +44,8 @@ enum ssh_bind_options_e { SSH_BIND_OPTIONS_RSAKEY, SSH_BIND_OPTIONS_BANNER, SSH_BIND_OPTIONS_LOG_VERBOSITY, - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR + SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, + SSH_BIND_OPTIONS_ECDSAKEY }; typedef struct ssh_bind_struct* ssh_bind; @@ -80,69 +81,6 @@ typedef struct ssh_bind_callbacks_struct *ssh_bind_callbacks; */ LIBSSH_API ssh_bind ssh_bind_new(void); -/** - * @brief Set the options for the current SSH server bind. - * - * @param sshbind The ssh server bind to configure. - * - * @param type The option type to set. This could be one of the - * following: - * - * - SSH_BIND_OPTIONS_BINDADDR - * The ip address to bind (const char *). - * - * - SSH_BIND_OPTIONS_BINDPORT - * The port to bind (unsigned int). - * - * - SSH_BIND_OPTIONS_BINDPORT_STR - * The port to bind (const char *). - * - * - SSH_BIND_OPTIONS_HOSTKEY - * This specifies the file containing the private host key used - * by SSHv1. (const char *). - * - * - SSH_BIND_OPTIONS_DSAKEY - * This specifies the file containing the private host dsa key - * used by SSHv2. (const char *). - * - * - SSH_BIND_OPTIONS_RSAKEY - * This specifies the file containing the private host dsa key - * used by SSHv2. (const char *). - * - * - SSH_BIND_OPTIONS_BANNER - * That the server banner (version string) for SSH. - * (const char *). - * - * - SSH_BIND_OPTIONS_LOG_VERBOSITY - * Set the session logging verbosity (int).\n - * \n - * The verbosity of the messages. Every log smaller or - * equal to verbosity will be shown. - * - SSH_LOG_NOLOG: No logging - * - SSH_LOG_RARE: Rare conditions or warnings - * - SSH_LOG_ENTRY: API-accessible entrypoints - * - SSH_LOG_PACKET: Packet id and size - * - SSH_LOG_FUNCTIONS: Function entering and leaving - * - * - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR - * Set the session logging verbosity (const char *).\n - * \n - * The verbosity of the messages. Every log smaller or - * equal to verbosity will be shown. - * - SSH_LOG_NOLOG: No logging - * - SSH_LOG_RARE: Rare conditions or warnings - * - SSH_LOG_ENTRY: API-accessible entrypoints - * - SSH_LOG_PACKET: Packet id and size - * - SSH_LOG_FUNCTIONS: Function entering and leaving - * \n - * See the corresponding numbers in libssh.h. - * - * @param value The value to set. This is a generic pointer and the - * datatype which is used should be set according to the - * type set. - * - * @returns SSH_OK on success, SSH_ERROR on invalid option or parameter. - */ LIBSSH_API int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, const void *value); diff --git a/libssh/include/libssh/session.h b/libssh/include/libssh/session.h index 281c7c66..29bdd60b 100644 --- a/libssh/include/libssh/session.h +++ b/libssh/include/libssh/session.h @@ -127,6 +127,15 @@ struct ssh_session_struct { struct ssh_agent_state_struct *agent_state; struct ssh_auth_auto_state_struct *auth_auto_state; + /* + * RFC 4253, 7.1: if the first_kex_packet_follows flag was set in + * the received SSH_MSG_KEXINIT, but the guess was wrong, this + * field will be set such that the following guessed packet will + * be ignored. Once that packet has been received and ignored, + * this field is cleared. + */ + int first_kex_follows_guess_wrong; + ssh_buffer in_hashbuf; ssh_buffer out_hashbuf; struct ssh_crypto_struct *current_crypto; @@ -175,6 +184,7 @@ struct ssh_session_struct { char *knownhosts; char *wanted_methods[10]; char *ProxyCommand; + char *custombanner; unsigned long timeout; /* seconds */ unsigned long timeout_usec; unsigned int port; @@ -183,7 +193,13 @@ struct ssh_session_struct { int ssh2; int ssh1; char compressionlevel; + char *gss_server_identity; + char *gss_client_identity; + int gss_delegate_creds; } opts; + /* counters */ + ssh_counter socket_counter; + ssh_counter raw_counter; }; /** @internal diff --git a/libssh/include/libssh/sftp.h b/libssh/include/libssh/sftp.h index d370f0ec..8fb8f116 100644 --- a/libssh/include/libssh/sftp.h +++ b/libssh/include/libssh/sftp.h @@ -143,10 +143,10 @@ struct sftp_request_queue_struct { /* SSH_FXP_MESSAGE described into .7 page 26 */ struct sftp_status_message_struct { - uint32_t id; - uint32_t status; - ssh_string error; - ssh_string lang; + uint32_t id; + uint32_t status; + ssh_string error_unused; /* not used anymore */ + ssh_string lang_unused; /* not used anymore */ char *errormsg; char *langmsg; }; diff --git a/libssh/include/libssh/socket.h b/libssh/include/libssh/socket.h index 285a02e3..8e1eac21 100644 --- a/libssh/include/libssh/socket.h +++ b/libssh/include/libssh/socket.h @@ -52,6 +52,7 @@ void ssh_socket_set_write_wontblock(ssh_socket s); void ssh_socket_set_read_wontblock(ssh_socket s); void ssh_socket_set_except(ssh_socket s); int ssh_socket_get_status(ssh_socket s); +int ssh_socket_get_poll_flags(ssh_socket s); int ssh_socket_buffered_write_bytes(ssh_socket s); int ssh_socket_data_available(ssh_socket s); int ssh_socket_data_writable(ssh_socket s); diff --git a/libssh/include/libssh/wrapper.h b/libssh/include/libssh/wrapper.h index 7374a88a..a327e188 100644 --- a/libssh/include/libssh/wrapper.h +++ b/libssh/include/libssh/wrapper.h @@ -22,6 +22,7 @@ #define WRAPPER_H_ #include "config.h" +#include "libssh/libssh.h" #include "libssh/libcrypto.h" #include "libssh/libgcrypt.h" @@ -34,6 +35,9 @@ enum ssh_mac_e { enum ssh_hmac_e { SSH_HMAC_SHA1 = 1, + SSH_HMAC_SHA256, + SSH_HMAC_SHA384, + SSH_HMAC_SHA512, SSH_HMAC_MD5 }; @@ -42,16 +46,36 @@ enum ssh_des_e { SSH_DES }; +struct ssh_hmac_struct { + const char* name; + enum ssh_hmac_e hmac_type; +}; + typedef struct ssh_mac_ctx_struct *ssh_mac_ctx; MD5CTX md5_init(void); void md5_update(MD5CTX c, const void *data, unsigned long len); void md5_final(unsigned char *md,MD5CTX c); + SHACTX sha1_init(void); void sha1_update(SHACTX c, const void *data, unsigned long len); void sha1_final(unsigned char *md,SHACTX c); void sha1(unsigned char *digest,int len,unsigned char *hash); + +SHA256CTX sha256_init(void); +void sha256_update(SHA256CTX c, const void *data, unsigned long len); +void sha256_final(unsigned char *md,SHA256CTX c); void sha256(unsigned char *digest, int len, unsigned char *hash); +SHA384CTX sha384_init(void); +void sha384_update(SHA384CTX c, const void *data, unsigned long len); +void sha384_final(unsigned char *md,SHA384CTX c); +void sha384(unsigned char *digest, int len, unsigned char *hash); + +SHA512CTX sha512_init(void); +void sha512_update(SHA512CTX c, const void *data, unsigned long len); +void sha512_final(unsigned char *md,SHA512CTX c); +void sha512(unsigned char *digest, int len, unsigned char *hash); + void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen); EVPCTX evp_init(int nid); void evp_update(EVPCTX ctx, const void *data, unsigned long len); @@ -64,11 +88,16 @@ void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx); HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type); void hmac_update(HMACCTX c, const void *data, unsigned long len); void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len); +size_t hmac_digest_len(enum ssh_hmac_e type); int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type); int crypt_set_algorithms_server(ssh_session session); struct ssh_crypto_struct *crypto_new(void); void crypto_free(struct ssh_crypto_struct *crypto); +void ssh_reseed(void); + +struct ssh_hmac_struct *ssh_get_hmactab(void); +const char *ssh_hmac_type_to_string(enum ssh_hmac_e hmac_type); #endif /* WRAPPER_H_ */ diff --git a/libssh/libssh-config.cmake.in b/libssh/libssh-config.cmake.in index 1e287fae..fa9cecf8 100644 --- a/libssh/libssh-config.cmake.in +++ b/libssh/libssh-config.cmake.in @@ -7,5 +7,7 @@ else() set(LIBSSH_INCLUDE_DIR @INCLUDE_INSTALL_DIR@) endif() -set(LIBSSH_LIRBARY @LIB_INSTALL_DIR@/libssh.so) -set(LIBSSH_LIRBARIES @LIB_INSTALL_DIR@/libssh.so) +set(LIBSSH_LIBRARY @LIB_INSTALL_DIR@/@LIBSSH_LIBRARY_NAME@) +set(LIBSSH_LIBRARIES @LIB_INSTALL_DIR@/@LIBSSH_LIBRARY_NAME@) + +set(LIBSSH_THREADS_LIBRARY @LIB_INSTALL_DIR@/@LIBSSH_THREADS_LIBRARY_NAME@) diff --git a/libssh/build/build_make.sh b/libssh/obj/build_make.sh similarity index 98% rename from libssh/build/build_make.sh rename to libssh/obj/build_make.sh index ba03c99c..2f2e4a6c 100755 --- a/libssh/build/build_make.sh +++ b/libssh/obj/build_make.sh @@ -4,7 +4,7 @@ # # Script to build libssh on UNIX. # -# Copyright (c) 2006-2007 Andreas Schneider +# Copyright (c) 2006-2007 Andreas Schneider # SOURCE_DIR=".." diff --git a/libssh/src/CMakeLists.txt b/libssh/src/CMakeLists.txt index 83435d0c..a4bc8595 100644 --- a/libssh/src/CMakeLists.txt +++ b/libssh/src/CMakeLists.txt @@ -28,7 +28,7 @@ if (HAVE_LIBSOCKET) ) endif (HAVE_LIBSOCKET) -if (OPENSSL_LIBRARIES) +if (OPENSSL_CRYPTO_LIBRARIES) set(LIBSSH_PRIVATE_INCLUDE_DIRS ${LIBSSH_PRIVATE_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} @@ -36,9 +36,9 @@ if (OPENSSL_LIBRARIES) set(LIBSSH_LINK_LIBRARIES ${LIBSSH_LINK_LIBRARIES} - ${OPENSSL_LIBRARIES} + ${OPENSSL_CRYPTO_LIBRARIES} ) -endif (OPENSSL_LIBRARIES) +endif (OPENSSL_CRYPTO_LIBRARIES) if (GCRYPT_LIBRARY) set(LIBSSH_PRIVATE_INCLUDE_DIRS @@ -109,6 +109,7 @@ set(libssh_SRCS agent.c auth.c base64.c + bignum.c buffer.c callbacks.c channels.c @@ -118,7 +119,10 @@ set(libssh_SRCS curve25519.c dh.c ecdh.c + ed25519.c error.c + fe25519.c + ge25519.c getpass.c init.c kex.c @@ -135,8 +139,10 @@ set(libssh_SRCS packet_crypt.c pcap.c pki.c + pki_ed25519.c poll.c session.c + sc25519.c scp.c socket.c string.c @@ -288,6 +294,6 @@ if (WITH_STATIC_LIB) ) endif (WITH_STATIC_LIB) -if (CMAKE_HAVE_THREADS_LIBRARY) +if (Threads_FOUND) add_subdirectory(threads) -endif (CMAKE_HAVE_THREADS_LIBRARY) +endif (Threads_FOUND) diff --git a/libssh/src/agent.c b/libssh/src/agent.c index 1b094ed4..d5257604 100644 --- a/libssh/src/agent.c +++ b/libssh/src/agent.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2008-2009 by Andreas Schneider + * Copyright (c) 2008-2013 by Andreas Schneider * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -300,7 +300,7 @@ static int agent_talk(struct ssh_session_struct *session, "Error reading response from authentication socket."); return -1; } - if (buffer_add_data(reply, payload, n) < 0) { + if (ssh_buffer_add_data(reply, payload, n) < 0) { SSH_LOG(SSH_LOG_WARN, "Not enough space"); return -1; } @@ -393,7 +393,7 @@ int ssh_agent_get_ident_count(struct ssh_session_struct *session) { } if (session->agent->ident) { - buffer_reinit(session->agent->ident); + ssh_buffer_reinit(session->agent->ident); } session->agent->ident = reply; @@ -526,7 +526,7 @@ ssh_string ssh_agent_sign_data(ssh_session session, ssh_buffer_free(request); return NULL; } - if (buffer_add_data(request, buffer_get_rest(data), dlen) < 0) { + if (ssh_buffer_add_data(request, buffer_get_rest(data), dlen) < 0) { ssh_buffer_free(request); return NULL; } diff --git a/libssh/src/auth.c b/libssh/src/auth.c index 6664c203..d56111d2 100644 --- a/libssh/src/auth.c +++ b/libssh/src/auth.c @@ -3,8 +3,8 @@ * * This file is part of the SSH Library * - * Copyright (c) 2003-2011 by Aris Adamantiadis - * Copyright (c) 2008-2011 Andreas Schneider + * Copyright (c) 2003-2013 by Aris Adamantiadis + * Copyright (c) 2008-2013 Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -182,25 +182,19 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_banner){ */ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure){ char *auth_methods = NULL; - ssh_string auth; uint8_t partial = 0; + int rc; (void) type; (void) user; - auth = buffer_get_ssh_string(packet); - if (auth == NULL || buffer_get_u8(packet, &partial) != 1) { + rc = ssh_buffer_unpack(packet, "sb", &auth_methods, &partial); + if (rc != SSH_OK) { ssh_set_error(session, SSH_FATAL, "Invalid SSH_MSG_USERAUTH_FAILURE message"); session->auth_state=SSH_AUTH_STATE_ERROR; goto end; } - auth_methods = ssh_string_to_char(auth); - if (auth_methods == NULL) { - ssh_set_error_oom(session); - goto end; - } - if (partial) { session->auth_state=SSH_AUTH_STATE_PARTIAL; SSH_LOG(SSH_LOG_INFO, @@ -234,7 +228,6 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure){ } end: - ssh_string_free(auth); SAFE_FREE(auth_methods); return SSH_PACKET_USED; @@ -359,7 +352,6 @@ int ssh_userauth_list(ssh_session session, const char *username) * before you connect to the server. */ int ssh_userauth_none(ssh_session session, const char *username) { - ssh_string str; int rc; #ifdef WITH_SSH1 @@ -387,47 +379,12 @@ int ssh_userauth_none(ssh_session session, const char *username) { } /* request */ - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); - if (rc < 0) { - goto fail; - } - - /* username */ - if (username) { - str = ssh_string_from_char(username); - } else { - str = ssh_string_from_char(session->opts.username); - } - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* service */ - str = ssh_string_from_char("ssh-connection"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* method */ - str = ssh_string_from_char("none"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); + rc = ssh_buffer_pack(session->out_buffer, "bsss", + SSH2_MSG_USERAUTH_REQUEST, + username ? username : session->opts.username, + "ssh-connection", + "none" + ); if (rc < 0) { goto fail; } @@ -448,7 +405,7 @@ pending: return rc; fail: ssh_set_error_oom(session); - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_AUTH_ERROR; } @@ -485,7 +442,7 @@ int ssh_userauth_try_publickey(ssh_session session, const char *username, const ssh_key pubkey) { - ssh_string str; + ssh_string pubkey_s = NULL; int rc; if (session == NULL) { @@ -522,82 +479,28 @@ int ssh_userauth_try_publickey(ssh_session session, return SSH_AUTH_ERROR; } - /* request */ - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); - if (rc < 0) { - goto fail; - } - - /* username */ - if (username) { - str = ssh_string_from_char(username); - } else { - str = ssh_string_from_char(session->opts.username); - } - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* service */ - str = ssh_string_from_char("ssh-connection"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* method */ - str = ssh_string_from_char("publickey"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* private key? */ - rc = buffer_add_u8(session->out_buffer, 0); - if (rc < 0) { - goto fail; - } - - /* algo */ - str = ssh_string_from_char(pubkey->type_c); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - /* public key */ - rc = ssh_pki_export_pubkey_blob(pubkey, &str); + rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_s); if (rc < 0) { goto fail; } - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); + /* request */ + rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", + SSH2_MSG_USERAUTH_REQUEST, + username ? username : session->opts.username, + "ssh-connection", + "publickey", + 0, /* private key ? */ + pubkey->type_c, /* algo */ + pubkey_s /* public key */ + ); if (rc < 0) { goto fail; } + ssh_string_free(pubkey_s); + session->auth_state = SSH_AUTH_STATE_NONE; session->pending_call_state = SSH_PENDING_CALL_AUTH_OFFER_PUBKEY; rc = packet_send(session); @@ -613,8 +516,9 @@ pending: return rc; fail: + ssh_string_free(pubkey_s); ssh_set_error_oom(session); - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_AUTH_ERROR; } @@ -647,7 +551,7 @@ int ssh_userauth_publickey(ssh_session session, const char *username, const ssh_key privkey) { - ssh_string str; + ssh_string str = NULL; int rc; if (session == NULL) { @@ -684,81 +588,26 @@ int ssh_userauth_publickey(ssh_session session, return SSH_AUTH_ERROR; } - /* request */ - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); - if (rc < 0) { - goto fail; - } - - /* username */ - if (username) { - str = ssh_string_from_char(username); - } else { - str = ssh_string_from_char(session->opts.username); - } - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* service */ - str = ssh_string_from_char("ssh-connection"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* method */ - str = ssh_string_from_char("publickey"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* private key? */ - rc = buffer_add_u8(session->out_buffer, 1); - if (rc < 0) { - goto fail; - } - - /* algo */ - str = ssh_string_from_char(privkey->type_c); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - /* public key */ rc = ssh_pki_export_pubkey_blob(privkey, &str); if (rc < 0) { goto fail; } - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); + /* request */ + rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", + SSH2_MSG_USERAUTH_REQUEST, + username ? username : session->opts.username, + "ssh-connection", + "publickey", + 1, /* private key */ + privkey->type_c, /* algo */ + str /* public key */ + ); if (rc < 0) { goto fail; } + ssh_string_free(str); /* sign the buffer with the private key */ str = ssh_pki_do_sign(session, session->out_buffer, privkey); @@ -768,6 +617,7 @@ int ssh_userauth_publickey(ssh_session session, rc = buffer_add_ssh_string(session->out_buffer, str); ssh_string_free(str); + str = NULL; if (rc < 0) { goto fail; } @@ -787,8 +637,9 @@ pending: return rc; fail: + ssh_string_free(str); ssh_set_error_oom(session); - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_AUTH_ERROR; } @@ -798,7 +649,7 @@ static int ssh_userauth_agent_publickey(ssh_session session, const char *username, ssh_key pubkey) { - ssh_string str; + ssh_string str = NULL; int rc; switch(session->pending_call_state) { @@ -820,69 +671,6 @@ static int ssh_userauth_agent_publickey(ssh_session session, return SSH_AUTH_ERROR; } - /* request */ - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); - if (rc < 0) { - goto fail; - } - - /* username */ - if (username) { - str = ssh_string_from_char(username); - } else { - str = ssh_string_from_char(session->opts.username); - } - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* service */ - str = ssh_string_from_char("ssh-connection"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* method */ - str = ssh_string_from_char("publickey"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* private key? */ - rc = buffer_add_u8(session->out_buffer, 1); - if (rc < 0) { - goto fail; - } - - /* algo */ - str = ssh_string_from_char(pubkey->type_c); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } /* public key */ rc = ssh_pki_export_pubkey_blob(pubkey, &str); @@ -890,12 +678,22 @@ static int ssh_userauth_agent_publickey(ssh_session session, goto fail; } - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); + /* request */ + rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", + SSH2_MSG_USERAUTH_REQUEST, + username ? username : session->opts.username, + "ssh-connection", + "publickey", + 1, /* private key */ + pubkey->type_c, /* algo */ + str /* public key */ + ); if (rc < 0) { goto fail; } + ssh_string_free(str); + /* sign the buffer with the private key */ str = ssh_pki_do_sign_agent(session, session->out_buffer, pubkey); if (str == NULL) { @@ -924,7 +722,8 @@ pending: return rc; fail: ssh_set_error_oom(session); - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); + ssh_string_free(str); return SSH_AUTH_ERROR; } @@ -1306,7 +1105,6 @@ int ssh_userauth_publickey_auto(ssh_session session, int ssh_userauth_password(ssh_session session, const char *username, const char *password) { - ssh_string str; int rc; #ifdef WITH_SSH1 @@ -1336,65 +1134,14 @@ int ssh_userauth_password(ssh_session session, } /* request */ - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); - if (rc < 0) { - goto fail; - } - - /* username */ - if (username) { - str = ssh_string_from_char(username); - } else { - str = ssh_string_from_char(session->opts.username); - } - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* service */ - str = ssh_string_from_char("ssh-connection"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* method */ - str = ssh_string_from_char("password"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* FALSE */ - rc = buffer_add_u8(session->out_buffer, 0); - if (rc < 0) { - goto fail; - } - - /* password */ - str = ssh_string_from_char(password); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); + rc = ssh_buffer_pack(session->out_buffer, "bsssbs", + SSH2_MSG_USERAUTH_REQUEST, + username ? username : session->opts.username, + "ssh-connection", + "password", + 0, /* false */ + password + ); if (rc < 0) { goto fail; } @@ -1415,7 +1162,7 @@ pending: return rc; fail: ssh_set_error_oom(session); - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_AUTH_ERROR; } @@ -1536,7 +1283,6 @@ static int ssh_userauth_kbdint_init(ssh_session session, const char *username, const char *submethods) { - ssh_string str; int rc; if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_INIT) goto pending; @@ -1552,78 +1298,18 @@ static int ssh_userauth_kbdint_init(ssh_session session, } /* request */ - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); + rc = ssh_buffer_pack(session->out_buffer, "bsssss", + SSH2_MSG_USERAUTH_REQUEST, + username ? username : session->opts.username, + "ssh-connection", + "keyboard-interactive", + "", /* lang (ignore it) */ + submethods ? submethods : "" + ); if (rc < 0) { goto fail; } - /* username */ - if (username) { - str = ssh_string_from_char(username); - } else { - str = ssh_string_from_char(session->opts.username); - } - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* service */ - str = ssh_string_from_char("ssh-connection"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* method */ - str = ssh_string_from_char("keyboard-interactive"); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* lang string (ignore it) */ - str = ssh_string_from_char(""); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - /* submethods */ - if (submethods == NULL) { - submethods = ""; - } - - str = ssh_string_from_char(submethods); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } session->auth_state = SSH_AUTH_STATE_KBDINT_SENT; session->pending_call_state = SSH_PENDING_CALL_AUTH_KBDINT_INIT; @@ -1642,7 +1328,7 @@ pending: return rc; fail: ssh_set_error_oom(session); - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_AUTH_ERROR; } @@ -1660,7 +1346,6 @@ fail: */ static int ssh_userauth_kbdint_send(ssh_session session) { - ssh_string answer; uint32_t i; int rc; if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_SEND) @@ -1669,29 +1354,17 @@ static int ssh_userauth_kbdint_send(ssh_session session) ssh_set_error_invalid(session); return SSH_ERROR; } - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_INFO_RESPONSE); - if (rc < 0) { - goto fail; - } - - rc = buffer_add_u32(session->out_buffer, htonl(session->kbdint->nprompts)); + rc = ssh_buffer_pack(session->out_buffer, "bd", + SSH2_MSG_USERAUTH_INFO_RESPONSE, + session->kbdint->nprompts); if (rc < 0) { goto fail; } for (i = 0; i < session->kbdint->nprompts; i++) { - if (session->kbdint->answers && session->kbdint->answers[i]) { - answer = ssh_string_from_char(session->kbdint->answers[i]); - } else { - answer = ssh_string_from_char(""); - } - if (answer == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, answer); - string_burn(answer); - string_free(answer); + rc = ssh_buffer_pack(session->out_buffer, "s", + session->kbdint->answers && session->kbdint->answers[i] ? + session->kbdint->answers[i]:""); if (rc < 0) { goto fail; } @@ -1716,7 +1389,7 @@ pending: return rc; fail: ssh_set_error_oom(session); - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_AUTH_ERROR; } @@ -1728,64 +1401,41 @@ fail: * authentication state. */ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { - ssh_string name; /* name of the "asking" window showed to client */ - ssh_string instruction; - ssh_string tmp; + ssh_string tmp = NULL; uint32_t nprompts; uint32_t i; + int rc; (void)user; (void)type; - name = buffer_get_ssh_string(packet); - instruction = buffer_get_ssh_string(packet); - tmp = buffer_get_ssh_string(packet); - buffer_get_u32(packet, &nprompts); - - /* We don't care about tmp */ - ssh_string_free(tmp); - - if (name == NULL || instruction == NULL) { - ssh_string_free(name); - ssh_string_free(instruction); - ssh_set_error(session, SSH_FATAL, "Invalid USERAUTH_INFO_REQUEST msg"); - - return SSH_PACKET_USED; - } if (session->kbdint == NULL) { session->kbdint = ssh_kbdint_new(); if (session->kbdint == NULL) { ssh_set_error_oom(session); - ssh_string_free(name); - ssh_string_free(instruction); - return SSH_PACKET_USED; } } else { ssh_kbdint_clean(session->kbdint); } - session->kbdint->name = ssh_string_to_char(name); - ssh_string_free(name); - if (session->kbdint->name == NULL) { - ssh_set_error_oom(session); - ssh_kbdint_free(session->kbdint); - ssh_string_free(instruction); + rc = ssh_buffer_unpack(packet, "ssSd", + &session->kbdint->name, /* name of the "asking" window shown to client */ + &session->kbdint->instruction, + &tmp, /* to ignore */ + &nprompts + ); - return SSH_PACKET_USED; + /* We don't care about tmp */ + ssh_string_free(tmp); + + if (rc != SSH_OK) { + ssh_set_error(session, SSH_FATAL, "Invalid USERAUTH_INFO_REQUEST msg"); + ssh_kbdint_free(session->kbdint); + session->kbdint = NULL; + return SSH_PACKET_USED; } - session->kbdint->instruction = ssh_string_to_char(instruction); - ssh_string_free(instruction); - if (session->kbdint->instruction == NULL) { - ssh_set_error_oom(session); - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - return SSH_PACKET_USED; - } - - nprompts = ntohl(nprompts); SSH_LOG(SSH_LOG_DEBUG, "%d keyboard-interactive prompts", nprompts); if (nprompts > KBDINT_MAX_PROMPT) { @@ -1823,23 +1473,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { memset(session->kbdint->echo, 0, nprompts); for (i = 0; i < nprompts; i++) { - tmp = buffer_get_ssh_string(packet); - buffer_get_u8(packet, &session->kbdint->echo[i]); - if (tmp == NULL) { + rc = ssh_buffer_unpack(packet, "sb", + &session->kbdint->prompts[i], + &session->kbdint->echo[i]); + if (rc == SSH_ERROR) { ssh_set_error(session, SSH_FATAL, "Short INFO_REQUEST packet"); ssh_kbdint_free(session->kbdint); session->kbdint = NULL; - return SSH_PACKET_USED; - } - session->kbdint->prompts[i] = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (session->kbdint->prompts[i] == NULL) { - ssh_set_error_oom(session); - session->kbdint->nprompts = i; - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - return SSH_PACKET_USED; } } diff --git a/libssh/src/base64.c b/libssh/src/base64.c index ff7671c1..2a162d0b 100644 --- a/libssh/src/base64.c +++ b/libssh/src/base64.c @@ -82,13 +82,18 @@ ssh_buffer base64_to_bin(const char *source) { SAFE_FREE(base64); return NULL; } + /* + * The base64 buffer often contains sensitive data. Make sure we don't leak + * sensitive data + */ + ssh_buffer_set_secure(buffer); len = strlen(ptr); while (len > 4) { if (_base64_to_bin(block, ptr, 3) < 0) { goto error; } - if (buffer_add_data(buffer, block, 3) < 0) { + if (ssh_buffer_add_data(buffer, block, 3) < 0) { goto error; } len -= 4; @@ -112,7 +117,7 @@ ssh_buffer base64_to_bin(const char *source) { if (_base64_to_bin(block, ptr, 3) < 0) { goto error; } - if (buffer_add_data(buffer, block, 3) < 0) { + if (ssh_buffer_add_data(buffer, block, 3) < 0) { goto error; } SAFE_FREE(base64); @@ -131,7 +136,7 @@ ssh_buffer base64_to_bin(const char *source) { if (_base64_to_bin(block, ptr, 1) < 0) { goto error; } - if (buffer_add_data(buffer, block, 1) < 0) { + if (ssh_buffer_add_data(buffer, block, 1) < 0) { goto error; } SAFE_FREE(base64); @@ -149,7 +154,7 @@ ssh_buffer base64_to_bin(const char *source) { if (_base64_to_bin(block, ptr, 2) < 0) { goto error; } - if (buffer_add_data(buffer,block,2) < 0) { + if (ssh_buffer_add_data(buffer,block,2) < 0) { goto error; } SAFE_FREE(base64); diff --git a/libssh/src/bignum.c b/libssh/src/bignum.c new file mode 100644 index 00000000..14b5aa54 --- /dev/null +++ b/libssh/src/bignum.c @@ -0,0 +1,96 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2014 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include + +#include "libssh/priv.h" +#include "libssh/bignum.h" +#include "libssh/string.h" + +ssh_string make_bignum_string(bignum num) { + ssh_string ptr = NULL; + int pad = 0; + unsigned int len = bignum_num_bytes(num); + unsigned int bits = bignum_num_bits(num); + + if (len == 0) { + return NULL; + } + + /* If the first bit is set we have a negative number */ + if (!(bits % 8) && bignum_is_bit_set(num, bits - 1)) { + pad++; + } + +#ifdef DEBUG_CRYPTO + fprintf(stderr, "%d bits, %d bytes, %d padding\n", bits, len, pad); +#endif /* DEBUG_CRYPTO */ + + ptr = ssh_string_new(len + pad); + if (ptr == NULL) { + return NULL; + } + + /* We have a negative number so we need a leading zero */ + if (pad) { + ptr->data[0] = 0; + } + +#ifdef HAVE_LIBGCRYPT + bignum_bn2bin(num, len, ptr->data + pad); +#elif HAVE_LIBCRYPTO + bignum_bn2bin(num, ptr->data + pad); +#endif + + return ptr; +} + +bignum make_string_bn(ssh_string string){ + bignum bn = NULL; + unsigned int len = ssh_string_len(string); + +#ifdef DEBUG_CRYPTO + fprintf(stderr, "Importing a %d bits, %d bytes object ...\n", + len * 8, len); +#endif /* DEBUG_CRYPTO */ + +#ifdef HAVE_LIBGCRYPT + bignum_bin2bn(string->data, len, &bn); +#elif defined HAVE_LIBCRYPTO + bn = bignum_bin2bn(string->data, len, NULL); +#endif + + return bn; +} + +/* prints the bignum on stderr */ +void ssh_print_bignum(const char *which, bignum num) { +#ifdef HAVE_LIBGCRYPT + unsigned char *hex = NULL; + bignum_bn2hex(num, &hex); +#elif defined HAVE_LIBCRYPTO + char *hex = NULL; + hex = bignum_bn2hex(num); +#endif + fprintf(stderr, "%s value: ", which); + fprintf(stderr, "%s\n", (hex == NULL) ? "(null)" : (char *) hex); + SAFE_FREE(hex); +} diff --git a/libssh/src/bind.c b/libssh/src/bind.c index 8132e3e9..b3239462 100644 --- a/libssh/src/bind.c +++ b/libssh/src/bind.c @@ -144,26 +144,19 @@ ssh_bind ssh_bind_new(void) { return ptr; } -int ssh_bind_listen(ssh_bind sshbind) { - const char *host; - socket_t fd; +static int ssh_bind_import_keys(ssh_bind sshbind) { int rc; - if (ssh_init() < 0) { - ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed"); - return -1; - } - if (sshbind->ecdsakey == NULL && sshbind->dsakey == NULL && sshbind->rsakey == NULL) { ssh_set_error(sshbind, SSH_FATAL, - "DSA or RSA host key file must be set before listen()"); + "ECDSA, DSA, or RSA host key file must be set"); return SSH_ERROR; } #ifdef HAVE_ECC - if (sshbind->ecdsakey) { + if (sshbind->ecdsa == NULL && sshbind->ecdsakey != NULL) { rc = ssh_pki_import_privkey_file(sshbind->ecdsakey, NULL, NULL, @@ -179,12 +172,13 @@ int ssh_bind_listen(ssh_bind sshbind) { ssh_set_error(sshbind, SSH_FATAL, "The ECDSA host key has the wrong type"); ssh_key_free(sshbind->ecdsa); + sshbind->ecdsa = NULL; return SSH_ERROR; } } #endif - if (sshbind->dsakey) { + if (sshbind->dsa == NULL && sshbind->dsakey != NULL) { rc = ssh_pki_import_privkey_file(sshbind->dsakey, NULL, NULL, @@ -201,11 +195,12 @@ int ssh_bind_listen(ssh_bind sshbind) { "The DSA host key has the wrong type: %d", ssh_key_type(sshbind->dsa)); ssh_key_free(sshbind->dsa); + sshbind->dsa = NULL; return SSH_ERROR; } } - if (sshbind->rsakey) { + if (sshbind->rsa == NULL && sshbind->rsakey != NULL) { rc = ssh_pki_import_privkey_file(sshbind->rsakey, NULL, NULL, @@ -222,10 +217,29 @@ int ssh_bind_listen(ssh_bind sshbind) { ssh_set_error(sshbind, SSH_FATAL, "The RSA host key has the wrong type"); ssh_key_free(sshbind->rsa); + sshbind->rsa = NULL; return SSH_ERROR; } } + return SSH_OK; +} + +int ssh_bind_listen(ssh_bind sshbind) { + const char *host; + socket_t fd; + int rc; + + if (ssh_init() < 0) { + ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed"); + return -1; + } + + rc = ssh_bind_import_keys(sshbind); + if (rc != SSH_OK) { + return SSH_ERROR; + } + if (sshbind->bindfd == SSH_INVALID_SOCKET) { host = sshbind->bindaddr; if (host == NULL) { @@ -235,10 +249,11 @@ int ssh_bind_listen(ssh_bind sshbind) { fd = bind_socket(sshbind, host, sshbind->bindport); if (fd == SSH_INVALID_SOCKET) { ssh_key_free(sshbind->dsa); + sshbind->dsa = NULL; ssh_key_free(sshbind->rsa); + sshbind->rsa = NULL; return -1; } - sshbind->bindfd = fd; if (listen(fd, 10) < 0) { ssh_set_error(sshbind, SSH_FATAL, @@ -246,9 +261,13 @@ int ssh_bind_listen(ssh_bind sshbind) { fd, strerror(errno)); close(fd); ssh_key_free(sshbind->dsa); + sshbind->dsa = NULL; ssh_key_free(sshbind->rsa); + sshbind->rsa = NULL; return -1; } + + sshbind->bindfd = fd; } else { SSH_LOG(SSH_LOG_INFO, "Using app-provided bind socket"); } @@ -341,11 +360,18 @@ void ssh_bind_free(ssh_bind sshbind){ /* options */ SAFE_FREE(sshbind->banner); + SAFE_FREE(sshbind->bindaddr); + SAFE_FREE(sshbind->dsakey); SAFE_FREE(sshbind->rsakey); - SAFE_FREE(sshbind->dsa); - SAFE_FREE(sshbind->rsa); - SAFE_FREE(sshbind->bindaddr); + SAFE_FREE(sshbind->ecdsakey); + + ssh_key_free(sshbind->dsa); + sshbind->dsa = NULL; + ssh_key_free(sshbind->rsa); + sshbind->rsa = NULL; + ssh_key_free(sshbind->ecdsa); + sshbind->ecdsa = NULL; for (i = 0; i < 10; i++) { if (sshbind->wanted_methods[i]) { @@ -357,7 +383,7 @@ void ssh_bind_free(ssh_bind sshbind){ } int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ - int i; + int i, rc; if (session == NULL){ ssh_set_error(sshbind, SSH_FATAL,"session is null"); @@ -368,7 +394,7 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ session->version = 2; /* copy options */ - for (i = 0; i < 10; ++i) { + for (i = 0; i < 10; i++) { if (sshbind->wanted_methods[i]) { session->opts.wanted_methods[i] = strdup(sshbind->wanted_methods[i]); if (session->opts.wanted_methods[i] == NULL) { @@ -388,7 +414,8 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ } session->common.log_verbosity = sshbind->common.log_verbosity; - + if(sshbind->banner != NULL) + session->opts.custombanner = strdup(sshbind->banner); ssh_socket_free(session->socket); session->socket = ssh_socket_new(session); if (session->socket == NULL) { @@ -399,6 +426,16 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ ssh_socket_set_fd(session->socket, fd); ssh_socket_get_poll_handle_out(session->socket); + /* We must try to import any keys that could be imported in case + * we are not using ssh_bind_listen (which is the other place + * where keys can be imported) on this ssh_bind and are instead + * only using ssh_bind_accept_fd to manage sockets ourselves. + */ + rc = ssh_bind_import_keys(sshbind); + if (rc != SSH_OK) { + return SSH_ERROR; + } + #ifdef HAVE_ECC if (sshbind->ecdsa) { session->srv.ecdsa_key = ssh_key_dup(sshbind->ecdsa); @@ -422,6 +459,8 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ return SSH_ERROR; } } + /* force PRNG to change state in case we fork after ssh_bind_accept */ + ssh_reseed(); return SSH_OK; } diff --git a/libssh/src/buffer.c b/libssh/src/buffer.c index ca120868..be25a32f 100644 --- a/libssh/src/buffer.c +++ b/libssh/src/buffer.c @@ -24,6 +24,7 @@ #include #include #include +#include #ifndef _WIN32 #include @@ -32,6 +33,8 @@ #include "libssh/priv.h" #include "libssh/buffer.h" +#include "libssh/misc.h" +#include "libssh/bignum.h" /** * @defgroup libssh_buffer The SSH buffer functions. @@ -104,13 +107,25 @@ void ssh_buffer_free(struct ssh_buffer_struct *buffer) { if (buffer->data) { /* burn the data */ - memset(buffer->data, 0, buffer->allocated); + BURN_BUFFER(buffer->data, buffer->allocated); SAFE_FREE(buffer->data); } - memset(buffer, 'X', sizeof(*buffer)); + BURN_BUFFER(buffer, sizeof(struct ssh_buffer_struct)); SAFE_FREE(buffer); } +/** + * @brief Sets the buffer as secure. + * + * A secure buffer will never leave cleartext data in the heap + * after being reallocated or freed. + * + * @param[in] buffer buffer to set secure. + */ +void ssh_buffer_set_secure(ssh_buffer buffer){ + buffer->secure = 1; +} + static int realloc_buffer(struct ssh_buffer_struct *buffer, size_t needed) { size_t smallest = 1; char *new; @@ -125,9 +140,20 @@ static int realloc_buffer(struct ssh_buffer_struct *buffer, size_t needed) { smallest <<= 1; } needed = smallest; - new = realloc(buffer->data, needed); - if (new == NULL) { - return -1; + if (buffer->secure){ + new = malloc(needed); + if (new == NULL) { + return -1; + } + memcpy(new, buffer->data,buffer->used); + BURN_BUFFER(buffer->data, buffer->used); + SAFE_FREE(buffer->data); + } else { + new = realloc(buffer->data, needed); + if (new == NULL) { + buffer->data = NULL; + return -1; + } } buffer->data = new; buffer->allocated = needed; @@ -140,12 +166,20 @@ static int realloc_buffer(struct ssh_buffer_struct *buffer, size_t needed) { * @param buffer SSH buffer */ static void buffer_shift(ssh_buffer buffer){ + uint32_t burn_pos = buffer->pos; + buffer_verify(buffer); if(buffer->pos==0) return; memmove(buffer->data, buffer->data + buffer->pos, buffer->used - buffer->pos); buffer->used -= buffer->pos; buffer->pos=0; + + if (buffer->secure){ + void *ptr = buffer->data + buffer->used; + BURN_BUFFER(ptr, burn_pos); + } + buffer_verify(buffer); } @@ -158,9 +192,10 @@ static void buffer_shift(ssh_buffer buffer){ * * @return 0 on success, < 0 on error. */ -int buffer_reinit(struct ssh_buffer_struct *buffer) { +int ssh_buffer_reinit(struct ssh_buffer_struct *buffer) +{ buffer_verify(buffer); - memset(buffer->data, 0, buffer->used); + BURN_BUFFER(buffer->data, buffer->used); buffer->used = 0; buffer->pos = 0; if(buffer->allocated > 127) { @@ -185,7 +220,8 @@ int buffer_reinit(struct ssh_buffer_struct *buffer) { * * @return 0 on success, < 0 on error. */ -int buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint32_t len) { +int ssh_buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint32_t len) +{ buffer_verify(buffer); if (buffer->used + len < len) { @@ -222,7 +258,7 @@ int buffer_add_ssh_string(struct ssh_buffer_struct *buffer, uint32_t len = 0; len = ssh_string_len(string); - if (buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) { + if (ssh_buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) { return -1; } @@ -240,12 +276,16 @@ int buffer_add_ssh_string(struct ssh_buffer_struct *buffer, * * @return 0 on success, -1 on error. */ -int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data){ - if (buffer_add_data(buffer, &data, sizeof(data)) < 0) { - return -1; - } +int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data) +{ + int rc; - return 0; + rc = ssh_buffer_add_data(buffer, &data, sizeof(data)); + if (rc < 0) { + return -1; + } + + return 0; } /** @@ -259,12 +299,16 @@ int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data){ * * @return 0 on success, -1 on error. */ -int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data){ - if (buffer_add_data(buffer, &data, sizeof(data)) < 0) { - return -1; - } +int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data) +{ + int rc; - return 0; + rc = ssh_buffer_add_data(buffer, &data, sizeof(data)); + if (rc < 0) { + return -1; + } + + return 0; } /** @@ -278,12 +322,16 @@ int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data){ * * @return 0 on success, -1 on error. */ -int buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data){ - if (buffer_add_data(buffer, &data, sizeof(data)) < 0) { - return -1; - } +int buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data) +{ + int rc; - return 0; + rc = ssh_buffer_add_data(buffer, &data, sizeof(data)); + if (rc < 0) { + return -1; + } + + return 0; } /** @@ -297,12 +345,16 @@ int buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data){ * * @return 0 on success, -1 on error. */ -int buffer_add_u8(struct ssh_buffer_struct *buffer,uint8_t data){ - if (buffer_add_data(buffer, &data, sizeof(uint8_t)) < 0) { - return -1; - } +int buffer_add_u8(struct ssh_buffer_struct *buffer,uint8_t data) +{ + int rc; - return 0; + rc = ssh_buffer_add_data(buffer, &data, sizeof(uint8_t)); + if (rc < 0) { + return -1; + } + + return 0; } /** @@ -360,12 +412,18 @@ int buffer_prepend_data(struct ssh_buffer_struct *buffer, const void *data, * @return 0 on success, -1 on error. */ int buffer_add_buffer(struct ssh_buffer_struct *buffer, - struct ssh_buffer_struct *source) { - if (buffer_add_data(buffer, buffer_get_rest(source), buffer_get_rest_len(source)) < 0) { - return -1; - } + struct ssh_buffer_struct *source) +{ + int rc; - return 0; + rc = ssh_buffer_add_data(buffer, + buffer_get_rest(source), + buffer_get_rest_len(source)); + if (rc < 0) { + return -1; + } + + return 0; } /** @@ -621,6 +679,318 @@ struct ssh_string_struct *buffer_get_mpint(struct ssh_buffer_struct *buffer) { return str; } +/** @internal + * @brief Add multiple values in a buffer on a single function call + * @param[in] buffer The buffer to add to + * @param[in] format A format string of arguments. + * @param[in] ap A va_list of arguments. + * @returns SSH_OK on success + * SSH_ERROR on error + * @see ssh_buffer_add_format() for format list values. + */ +int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer, const char *format, va_list ap){ + int rc = SSH_ERROR; + const char *p; + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + ssh_string string; + void *data; + } o; + char *cstring; + bignum b; + size_t len; + + for (p = format; *p != '\0'; p++) { + switch(*p) { + case 'b': + o.byte = (uint8_t)va_arg(ap, unsigned int); + rc = buffer_add_u8(buffer, o.byte); + break; + case 'w': + o.word = (uint16_t)va_arg(ap, unsigned int); + o.word = htons(o.word); + rc = buffer_add_u16(buffer, o.word); + break; + case 'd': + o.dword = va_arg(ap, uint32_t); + o.dword = htonl(o.dword); + rc = buffer_add_u32(buffer, o.dword); + break; + case 'q': + o.qword = va_arg(ap, uint64_t); + o.qword = htonll(o.qword); + rc = buffer_add_u64(buffer, o.qword); + break; + case 'S': + o.string = va_arg(ap, ssh_string); + rc = buffer_add_ssh_string(buffer, o.string); + o.string = NULL; + break; + case 's': + cstring = va_arg(ap, char *); + len = strlen(cstring); + rc = buffer_add_u32(buffer, htonl(len)); + if (rc == SSH_OK){ + rc = ssh_buffer_add_data(buffer, cstring, len); + } + cstring = NULL; + break; + case 'P': + len = va_arg(ap, size_t); + o.data = va_arg(ap, void *); + rc = ssh_buffer_add_data(buffer, o.data, len); + o.data = NULL; + break; + case 'B': + b = va_arg(ap, bignum); + o.string = make_bignum_string(b); + if(o.string == NULL){ + rc = SSH_ERROR; + break; + } + rc = buffer_add_ssh_string(buffer, o.string); + SAFE_FREE(o.string); + break; + case 't': + cstring = va_arg(ap, char *); + len = strlen(cstring); + rc = ssh_buffer_add_data(buffer, cstring, len); + cstring = NULL; + break; + default: + SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p); + rc = SSH_ERROR; + } + if (rc != SSH_OK){ + break; + } + } + + if (rc != SSH_ERROR){ + /* verify that the last hidden argument is correct */ + o.dword = va_arg(ap, uint32_t); + if (o.dword != SSH_BUFFER_PACK_END){ + rc = SSH_ERROR; + } + } + return rc; +} + +/** @internal + * @brief Add multiple values in a buffer on a single function call + * @param[in] buffer The buffer to add to + * @param[in] format A format string of arguments. This string contains single + * letters describing the order and type of arguments: + * 'b': uint8_t (pushed in network byte order) + * 'w': uint16_t (pushed in network byte order) + * 'd': uint32_t (pushed in network byte order) + * 'q': uint64_t (pushed in network byte order) + * 'S': ssh_string + * 's': char * (C string, pushed as SSH string) + * 't': char * (C string, pushed as free text) + * 'P': size_t, void * (len of data, pointer to data) + * only pushes data. + * 'B': bignum (pushed as SSH string) + * @returns SSH_OK on success + * SSH_ERROR on error + * @warning when using 'P' with a constant size (e.g. 8), do not + * forget to cast to (size_t). + */ +int _ssh_buffer_pack(struct ssh_buffer_struct *buffer, const char *format, ...){ + va_list ap; + int rc; + + va_start(ap, format); + rc = ssh_buffer_pack_va(buffer, format, ap); + va_end(ap); + return rc; +} + +/** @internal + * @brief Get multiple values from a buffer on a single function call + * @param[in] buffer The buffer to get from + * @param[in] format A format string of arguments. + * @param[in] ap A va_list of arguments. + * @returns SSH_OK on success + * SSH_ERROR on error + * @see ssh_buffer_get_format() for format list values. + */ +int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer, const char *format, va_list ap){ + int rc = SSH_ERROR; + const char *p, *last; + union { + uint8_t *byte; + uint16_t *word; + uint32_t *dword; + uint64_t *qword; + ssh_string *string; + char **cstring; + void **data; + } o; + size_t len, rlen; + uint32_t u32len; + va_list ap_copy; + + /* copy the argument list in case a rollback is needed */ + va_copy(ap_copy, ap); + + for (p = format; *p != '\0'; p++) { + switch (*p) { + case 'b': + o.byte = va_arg(ap, uint8_t *); + rlen = buffer_get_u8(buffer, o.byte); + rc = rlen==1 ? SSH_OK : SSH_ERROR; + break; + case 'w': + o.word = va_arg(ap, uint16_t *); + rlen = buffer_get_data(buffer, o.word, sizeof(uint16_t)); + *o.word = ntohs(*o.word); + rc = rlen==2 ? SSH_OK : SSH_ERROR; + break; + case 'd': + o.dword = va_arg(ap, uint32_t *); + rlen = buffer_get_u32(buffer, o.dword); + *o.dword = ntohl(*o.dword); + rc = rlen==4 ? SSH_OK : SSH_ERROR; + break; + case 'q': + o.qword = va_arg(ap, uint64_t*); + rlen = buffer_get_u64(buffer, o.qword); + *o.qword = ntohll(*o.qword); + rc = rlen==8 ? SSH_OK : SSH_ERROR; + break; + case 'S': + o.string = va_arg(ap, ssh_string *); + *o.string = buffer_get_ssh_string(buffer); + rc = *o.string != NULL ? SSH_OK : SSH_ERROR; + o.string = NULL; + break; + case 's': + o.cstring = va_arg(ap, char **); + *o.cstring = NULL; + rc = buffer_get_u32(buffer, &u32len); + if (rc != 4){ + rc = SSH_ERROR; + break; + } + len = ntohl(u32len); + if (len > UINT_MAX - 1){ + rc = SSH_ERROR; + break; + } + *o.cstring = malloc(len + 1); + if (*o.cstring == NULL){ + rc = SSH_ERROR; + break; + } + rlen = buffer_get_data(buffer, *o.cstring, len); + if (rlen != len){ + SAFE_FREE(*o.cstring); + rc = SSH_ERROR; + break; + } + (*o.cstring)[len] = '\0'; + o.cstring = NULL; + rc = SSH_OK; + break; + case 'P': + len = va_arg(ap, size_t); + o.data = va_arg(ap, void **); + *o.data = malloc(len); + if(*o.data == NULL){ + rc = SSH_ERROR; + break; + } + rlen = buffer_get_data(buffer, *o.data, len); + if (rlen != len){ + SAFE_FREE(*o.data); + rc = SSH_ERROR; + break; + } + o.data = NULL; + rc = SSH_OK; + break; + default: + SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p); + rc = SSH_ERROR; + } + if (rc != SSH_OK) { + break; + } + } + if (rc != SSH_ERROR){ + /* verify that the last hidden argument is correct */ + uint32_t canary = va_arg(ap, uint32_t); + if (canary != SSH_BUFFER_PACK_END){ + rc = SSH_ERROR; + } + } + if (rc != SSH_OK){ + /* Reset the format string and erase everything that was allocated */ + last = p; + for(p=format;p + * Copyright (c) 2009-2013 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/libssh/src/channels.c b/libssh/src/channels.c index f7bcded2..7a4e71fe 100644 --- a/libssh/src/channels.c +++ b/libssh/src/channels.c @@ -3,8 +3,8 @@ * * This file is part of the SSH Library * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider + * Copyright (c) 2003-2013 by Aris Adamantiadis + * Copyright (c) 2009-2013 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -139,15 +139,16 @@ uint32_t ssh_channel_new_id(ssh_session session) { */ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ uint32_t channelid=0; - uint32_t tmp; ssh_channel channel; + int rc; (void)type; (void)user; SSH_LOG(SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"); - buffer_get_u32(packet, &channelid); - channelid=ntohl(channelid); + rc = ssh_buffer_unpack(packet, "d", &channelid); + if (rc != SSH_OK) + goto error; channel=ssh_channel_from_local(session,channelid); if(channel==NULL){ ssh_set_error(session, SSH_FATAL, @@ -158,14 +159,12 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ return SSH_PACKET_USED; } - buffer_get_u32(packet, &tmp); - channel->remote_channel = ntohl(tmp); - - buffer_get_u32(packet, &tmp); - channel->remote_window = ntohl(tmp); - - buffer_get_u32(packet,&tmp); - channel->remote_maxpacket=ntohl(tmp); + rc = ssh_buffer_unpack(packet, "ddd", + &channel->remote_channel, + &channel->remote_window, + &channel->remote_maxpacket); + if (rc != SSH_OK) + goto error; SSH_LOG(SSH_LOG_PROTOCOL, "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d", @@ -177,8 +176,11 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ (long unsigned int) channel->remote_maxpacket); channel->state = SSH_CHANNEL_STATE_OPEN; - channel->flags = channel->flags & ~SSH_CHANNEL_FLAG_NOT_BOUND; + channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND; + return SSH_PACKET_USED; +error: + ssh_set_error(session, SSH_FATAL, "Invalid packet"); return SSH_PACKET_USED; } @@ -190,31 +192,28 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){ ssh_channel channel; - ssh_string error_s; char *error = NULL; uint32_t code; + int rc; (void)user; (void)type; + channel=channel_from_msg(session,packet); if(channel==NULL){ SSH_LOG(SSH_LOG_RARE,"Invalid channel in packet"); return SSH_PACKET_USED; } - buffer_get_u32(packet, &code); - error_s = buffer_get_ssh_string(packet); - if(error_s != NULL) - error = ssh_string_to_char(error_s); - ssh_string_free(error_s); - if (error == NULL) { - ssh_set_error_oom(session); - return SSH_PACKET_USED; + rc = ssh_buffer_unpack(packet, "ds", &code, &error); + if (rc != SSH_OK){ + ssh_set_error(session, SSH_FATAL, "Invalid packet"); + return SSH_PACKET_USED; } ssh_set_error(session, SSH_REQUEST_DENIED, "Channel opening failure: channel %u error (%lu) %s", channel->local_channel, - (long unsigned int) ntohl(code), + (long unsigned int) code, error); SAFE_FREE(error); channel->state=SSH_CHANNEL_STATE_OPEN_DENIED; @@ -238,7 +237,7 @@ static int ssh_channel_open_termination(void *c){ * * @param[in] channel The current channel. * - * @param[in] type_c A C string describing the kind of channel (e.g. "exec"). + * @param[in] type A C string describing the kind of channel (e.g. "exec"). * * @param[in] window The receiving window of the channel. The window is the * maximum size of data that can stay in buffers and @@ -248,11 +247,11 @@ static int ssh_channel_open_termination(void *c){ * * @param[in] payload The buffer containing additional payload for the query. */ -static int channel_open(ssh_channel channel, const char *type_c, int window, +static int channel_open(ssh_channel channel, const char *type, int window, int maxpacket, ssh_buffer payload) { ssh_session session = channel->session; - ssh_string type = NULL; int err=SSH_ERROR; + int rc; switch(channel->state){ case SSH_CHANNEL_STATE_NOT_OPEN: @@ -274,26 +273,18 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, "Creating a channel %d with %d window and %d max packet", channel->local_channel, window, maxpacket); - type = ssh_string_from_char(type_c); - if (type == NULL) { + rc = ssh_buffer_pack(session->out_buffer, + "bsddd", + SSH2_MSG_CHANNEL_OPEN, + type, + channel->local_channel, + channel->local_window, + channel->local_maxpacket); + if (rc != SSH_OK){ ssh_set_error_oom(session); - return err; } - if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN) < 0 || - buffer_add_ssh_string(session->out_buffer,type) < 0 || - buffer_add_u32(session->out_buffer, htonl(channel->local_channel)) < 0 || - buffer_add_u32(session->out_buffer, htonl(channel->local_window)) < 0 || - buffer_add_u32(session->out_buffer, htonl(channel->local_maxpacket)) < 0) { - ssh_set_error_oom(session); - ssh_string_free(type); - - return err; - } - - ssh_string_free(type); - if (payload != NULL) { if (buffer_add_buffer(session->out_buffer, payload) < 0) { ssh_set_error_oom(session); @@ -309,7 +300,7 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, SSH_LOG(SSH_LOG_PACKET, "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d", - type_c, channel->local_channel); + type, channel->local_channel); pending: /* wait until channel is opened by server */ err = ssh_handle_packets_termination(session, @@ -353,6 +344,7 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) { */ static int grow_window(ssh_session session, ssh_channel channel, int minimumsize) { uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE; + int rc; #ifdef WITH_SSH1 if (session->version == 1){ @@ -372,9 +364,12 @@ static int grow_window(ssh_session session, ssh_channel channel, int minimumsize /* WINDOW_ADJUST packet needs a relative increment rather than an absolute * value, so we give here the missing bytes needed to reach new_window */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_WINDOW_ADJUST) < 0 || - buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 || - buffer_add_u32(session->out_buffer, htonl(new_window - channel->local_window)) < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bdd", + SSH2_MSG_CHANNEL_WINDOW_ADJUST, + channel->remote_channel, + new_window - channel->local_window); + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } @@ -393,7 +388,7 @@ static int grow_window(ssh_session session, ssh_channel channel, int minimumsize return SSH_OK; error: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_ERROR; } @@ -416,22 +411,24 @@ error: static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) { ssh_channel channel; uint32_t chan; + int rc; #ifdef WITH_SSH1 /* With SSH1, the channel is always the first one */ if(session->version==1) return ssh_get_channel1(session); #endif - if (buffer_get_u32(packet, &chan) != sizeof(uint32_t)) { + rc = ssh_buffer_unpack(packet,"d",&chan); + if (rc != SSH_OK) { ssh_set_error(session, SSH_FATAL, "Getting channel from message: short read"); return NULL; } - channel = ssh_channel_from_local(session, ntohl(chan)); + channel = ssh_channel_from_local(session, chan); if (channel == NULL) { ssh_set_error(session, SSH_FATAL, "Server specified invalid channel %lu", - (long unsigned int) ntohl(chan)); + (long unsigned int) chan); } return channel; @@ -449,15 +446,14 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) { SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); } - rc = buffer_get_u32(packet, &bytes); - if (channel == NULL || rc != sizeof(uint32_t)) { + rc = ssh_buffer_unpack(packet, "d", &bytes); + if (channel == NULL || rc != SSH_OK) { SSH_LOG(SSH_LOG_PACKET, "Error getting a window adjust message: invalid packet"); return SSH_PACKET_USED; } - bytes = ntohl(bytes); SSH_LOG(SSH_LOG_PROTOCOL, "Adding %d bytes to channel (%d:%d) (from %d bytes)", bytes, @@ -507,7 +503,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ } len = ssh_string_len(str); - SSH_LOG(SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PACKET, "Channel receiving %" PRIdS " bytes data in %d (local win=%d remote win=%d)", len, is_stderr, @@ -535,7 +531,7 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ channel->local_window = 0; /* buggy remote */ } - SSH_LOG(SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PACKET, "Channel windows are now (local win=%d remote win=%d)", channel->local_window, channel->remote_window); @@ -555,6 +551,9 @@ SSH_PACKET_CALLBACK(channel_rcv_data){ is_stderr, channel->callbacks->userdata); if(rest > 0) { + if (channel->counter != NULL) { + channel->counter->in_bytes += rest; + } buffer_pass_bytes(buf, rest); } if (channel->local_window + buffer_get_rest_len(buf) < WINDOWLIMIT) { @@ -635,7 +634,7 @@ SSH_PACKET_CALLBACK(channel_rcv_close) { channel, channel->callbacks->userdata); } - channel->flags &= SSH_CHANNEL_FLAG_CLOSED_REMOTE; + channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE; if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL) ssh_channel_do_free(channel); @@ -644,8 +643,7 @@ SSH_PACKET_CALLBACK(channel_rcv_close) { SSH_PACKET_CALLBACK(channel_rcv_request) { ssh_channel channel; - ssh_string request_s; - char *request; + char *request=NULL; uint8_t status; int rc; (void)user; @@ -654,32 +652,22 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { channel = channel_from_msg(session,packet); if (channel == NULL) { SSH_LOG(SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session)); - return SSH_PACKET_USED; } - request_s = buffer_get_ssh_string(packet); - if (request_s == NULL) { + rc = ssh_buffer_unpack(packet, "sb", + &request, + &status); + if (rc != SSH_OK) { SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - return SSH_PACKET_USED; } - request = ssh_string_to_char(request_s); - ssh_string_free(request_s); - if (request == NULL) { - - return SSH_PACKET_USED; - } - - buffer_get_u8(packet, (uint8_t *) &status); - if (strcmp(request,"exit-status") == 0) { uint32_t exit_status = 0; SAFE_FREE(request); - buffer_get_u32(packet, &exit_status); - channel->exit_status = ntohl(exit_status); + rc = ssh_buffer_unpack(packet, "d", &exit_status); SSH_LOG(SSH_LOG_PACKET, "received exit-status %d", channel->exit_status); if(ssh_callbacks_exists(channel->callbacks, channel_exit_status_function)) { @@ -693,27 +681,17 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { } if (strcmp(request,"signal") == 0) { - ssh_string signal_str; - char *sig; + char *sig = NULL; SAFE_FREE(request); SSH_LOG(SSH_LOG_PACKET, "received signal"); - signal_str = buffer_get_ssh_string(packet); - if (signal_str == NULL) { + rc = ssh_buffer_unpack(packet, "s", &sig); + if (rc != SSH_OK) { SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - return SSH_PACKET_USED; } - sig = ssh_string_to_char(signal_str); - ssh_string_free(signal_str); - if (sig == NULL) { - - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_PACKET, "Remote connection sent a signal SIG %s", sig); if(ssh_callbacks_exists(channel->callbacks, channel_signal_function)) { @@ -729,73 +707,33 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { if (strcmp(request, "exit-signal") == 0) { const char *core = "(core dumped)"; - ssh_string tmp; - char *sig; + char *sig = NULL; char *errmsg = NULL; char *lang = NULL; - uint8_t i; + uint8_t core_dumped; SAFE_FREE(request); - tmp = buffer_get_ssh_string(packet); - if (tmp == NULL) { + rc = ssh_buffer_unpack(packet, "sbs", + &sig, /* signal name */ + &core_dumped, /* core dumped */ + &errmsg, /* error message */ + &lang); + if (rc != SSH_OK) { SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - return SSH_PACKET_USED; } - sig = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (sig == NULL) { - - return SSH_PACKET_USED; - } - - buffer_get_u8(packet, &i); - if (i == 0) { + if (core_dumped == 0) { core = ""; } - tmp = buffer_get_ssh_string(packet); - if (tmp == NULL) { - SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - SAFE_FREE(sig); - - return SSH_PACKET_USED; - } - - errmsg = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (errmsg == NULL) { - SAFE_FREE(sig); - - return SSH_PACKET_USED; - } - - tmp = buffer_get_ssh_string(packet); - if (tmp == NULL) { - SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - SAFE_FREE(errmsg); - SAFE_FREE(sig); - - return SSH_PACKET_USED; - } - - lang = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (lang == NULL) { - SAFE_FREE(errmsg); - SAFE_FREE(sig); - - return SSH_PACKET_USED; - } - SSH_LOG(SSH_LOG_PACKET, "Remote connection closed by signal SIG %s %s", sig, core); if(ssh_callbacks_exists(channel->callbacks, channel_exit_signal_function)) { channel->callbacks->channel_exit_signal_function(channel->session, channel, - sig, i, errmsg, lang, + sig, core_dumped, errmsg, lang, channel->callbacks->userdata); } @@ -808,12 +746,12 @@ SSH_PACKET_CALLBACK(channel_rcv_request) { if(strcmp(request,"keepalive@openssh.com")==0){ SAFE_FREE(request); SSH_LOG(SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive"); - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_FAILURE); - if (rc < 0) { - return SSH_PACKET_USED; - } - rc = buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)); - if (rc < 0) { + + rc = ssh_buffer_pack(session->out_buffer, + "bd", + SSH2_MSG_CHANNEL_FAILURE, + channel->remote_channel); + if (rc != SSH_OK) { return SSH_PACKET_USED; } packet_send(session); @@ -867,7 +805,7 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, return -1; } - SSH_LOG(SSH_LOG_RARE, + SSH_LOG(SSH_LOG_PACKET, "placing %d bytes into channel buffer (stderr=%d)", len, is_stderr); if (is_stderr == 0) { /* stdout */ @@ -879,7 +817,7 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, } } - if (buffer_add_data(channel->stdout_buffer, data, len) < 0) { + if (ssh_buffer_add_data(channel->stdout_buffer, data, len) < 0) { ssh_set_error_oom(session); ssh_buffer_free(channel->stdout_buffer); channel->stdout_buffer = NULL; @@ -895,7 +833,7 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, } } - if (buffer_add_data(channel->stderr_buffer, data, len) < 0) { + if (ssh_buffer_add_data(channel->stderr_buffer, data, len) < 0) { ssh_set_error_oom(session); ssh_buffer_free(channel->stderr_buffer); channel->stderr_buffer = NULL; @@ -916,10 +854,10 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len, * SSH_AGAIN if in nonblocking mode and call has * to be done again. * - * @see channel_open_forward() - * @see channel_request_env() - * @see channel_request_shell() - * @see channel_request_exec() + * @see ssh_channel_open_forward() + * @see ssh_channel_request_env() + * @see ssh_channel_request_shell() + * @see ssh_channel_request_exec() */ int ssh_channel_open_session(ssh_channel channel) { if(channel == NULL) { @@ -952,7 +890,7 @@ int ssh_channel_open_session(ssh_channel channel) { * SSH_AGAIN if in nonblocking mode and call has * to be done again. * - * @see channel_open_forward() + * @see ssh_channel_open_forward() */ int ssh_channel_open_auth_agent(ssh_channel channel){ if(channel == NULL) { @@ -1021,27 +959,14 @@ int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, ssh_set_error_oom(session); goto error; } - str = ssh_string_from_char(remotehost); - if (str == NULL) { - ssh_set_error_oom(session); - goto error; - } - if (buffer_add_ssh_string(payload, str) < 0 || - buffer_add_u32(payload,htonl(remoteport)) < 0) { - ssh_set_error_oom(session); - goto error; - } - - ssh_string_free(str); - str = ssh_string_from_char(sourcehost); - if (str == NULL) { - ssh_set_error_oom(session); - goto error; - } - - if (buffer_add_ssh_string(payload, str) < 0 || - buffer_add_u32(payload,htonl(localport)) < 0) { + rc = ssh_buffer_pack(payload, + "sdsd", + remotehost, + remoteport, + sourcehost, + localport); + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } @@ -1078,7 +1003,7 @@ void ssh_channel_free(ssh_channel channel) { if (session->alive && channel->state == SSH_CHANNEL_STATE_OPEN) { ssh_channel_close(channel); } - channel->flags &= SSH_CHANNEL_FLAG_FREED_LOCAL; + channel->flags |= SSH_CHANNEL_FLAG_FREED_LOCAL; /* The idea behind the flags is the following : it is well possible * that a client closes a channel that stills exists on the server side. @@ -1120,12 +1045,29 @@ void ssh_channel_do_free(ssh_channel channel){ * * @return SSH_OK on success, SSH_ERROR if an error occurred. * - * @see channel_close() - * @see channel_free() + * Example: +@code + rc = ssh_channel_send_eof(channel); + if (rc == SSH_ERROR) { + return -1; + } + while(!ssh_channel_is_eof(channel)) { + rc = ssh_channel_read(channel, buf, sizeof(buf), 0); + if (rc == SSH_ERROR) { + return -1; + } + } + ssh_channel_close(channel); +@endcode + * + * @see ssh_channel_close() + * @see ssh_channel_free() + * @see ssh_channel_is_eof() */ int ssh_channel_send_eof(ssh_channel channel){ ssh_session session; int rc = SSH_ERROR; + int err; if(channel == NULL) { return rc; @@ -1133,25 +1075,30 @@ int ssh_channel_send_eof(ssh_channel channel){ session = channel->session; - if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_EOF) < 0) { - ssh_set_error_oom(session); - goto error; - } - if (buffer_add_u32(session->out_buffer,htonl(channel->remote_channel)) < 0) { + err = ssh_buffer_pack(session->out_buffer, + "bd", + SSH2_MSG_CHANNEL_EOF, + channel->remote_channel); + if (err != SSH_OK) { ssh_set_error_oom(session); goto error; } + rc = packet_send(session); SSH_LOG(SSH_LOG_PACKET, "Sent a EOF on client channel (%d:%d)", channel->local_channel, channel->remote_channel); + rc = ssh_channel_flush(channel); + if(rc == SSH_ERROR) + goto error; + channel->local_eof = 1; return rc; error: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return rc; } @@ -1166,8 +1113,8 @@ error: * * @return SSH_OK on success, SSH_ERROR if an error occurred. * - * @see channel_free() - * @see channel_eof() + * @see ssh_channel_free() + * @see ssh_channel_is_eof() */ int ssh_channel_close(ssh_channel channel){ ssh_session session; @@ -1187,8 +1134,11 @@ int ssh_channel_close(ssh_channel channel){ return rc; } - if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_CLOSE) < 0 || - buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bd", + SSH2_MSG_CHANNEL_CLOSE, + channel->remote_channel); + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } @@ -1203,9 +1153,13 @@ int ssh_channel_close(ssh_channel channel){ channel->state=SSH_CHANNEL_STATE_CLOSED; } + rc = ssh_channel_flush(channel); + if(rc == SSH_ERROR) + goto error; + return rc; error: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return rc; } @@ -1214,7 +1168,8 @@ error: static int ssh_channel_waitwindow_termination(void *c){ ssh_channel channel = (ssh_channel) c; if (channel->remote_window > 0 || - channel->session->session_state == SSH_SESSION_STATE_ERROR) + channel->session->session_state == SSH_SESSION_STATE_ERROR || + channel->state == SSH_CHANNEL_STATE_CLOSED) return 1; else return 0; @@ -1321,7 +1276,10 @@ static int channel_write_common(ssh_channel channel, "Wait for a growing window message..."); rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT, ssh_channel_waitwindow_termination,channel); - if (rc == SSH_ERROR || !ssh_channel_waitwindow_termination(channel)) + if (rc == SSH_ERROR || + !ssh_channel_waitwindow_termination(channel) || + channel->session->session_state == SSH_SESSION_STATE_ERROR || + channel->state == SSH_CHANNEL_STATE_CLOSED) goto out; continue; } @@ -1332,39 +1290,32 @@ static int channel_write_common(ssh_channel channel, effectivelen = MIN(effectivelen, maxpacketlen);; - rc = buffer_add_u8(session->out_buffer, - is_stderr ? SSH2_MSG_CHANNEL_EXTENDED_DATA - : SSH2_MSG_CHANNEL_DATA); - if (rc < 0) { - ssh_set_error_oom(session); - goto error; - } - - rc = buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bd", + is_stderr ? SSH2_MSG_CHANNEL_EXTENDED_DATA : SSH2_MSG_CHANNEL_DATA, + channel->remote_channel); + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } /* stderr message has an extra field */ if (is_stderr) { - rc = buffer_add_u32(session->out_buffer, - htonl(SSH2_EXTENDED_DATA_STDERR)); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "d", + SSH2_EXTENDED_DATA_STDERR); + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } } /* append payload data */ - rc = buffer_add_u32(session->out_buffer, htonl(effectivelen)); - if (rc < 0) { - ssh_set_error_oom(session); - goto error; - } - - rc = buffer_add_data(session->out_buffer, data, effectivelen); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "dP", + effectivelen, + (size_t)effectivelen, data); + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } @@ -1374,12 +1325,15 @@ static int channel_write_common(ssh_channel channel, return SSH_ERROR; } - SSH_LOG(SSH_LOG_RARE, + SSH_LOG(SSH_LOG_PACKET, "channel_write wrote %ld bytes", (long int) effectivelen); channel->remote_window -= effectivelen; len -= effectivelen; data = ((uint8_t*)data + effectivelen); + if (channel->counter != NULL) { + channel->counter->out_bytes += effectivelen; + } } /* it's a good idea to flush the socket now */ @@ -1392,7 +1346,7 @@ out: return (int)(origlen - len); error: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_ERROR; } @@ -1412,7 +1366,7 @@ uint32_t ssh_channel_window_size(ssh_channel channel) { * * @return The number of bytes written, SSH_ERROR on error. * - * @see channel_read() + * @see ssh_channel_read() */ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) { return channel_write_common(channel, data, len, 0); @@ -1425,7 +1379,7 @@ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) { * * @return 0 if channel is closed, nonzero otherwise. * - * @see channel_is_closed() + * @see ssh_channel_is_closed() */ int ssh_channel_is_open(ssh_channel channel) { if(channel == NULL) { @@ -1441,7 +1395,7 @@ int ssh_channel_is_open(ssh_channel channel) { * * @return 0 if channel is opened, nonzero otherwise. * - * @see channel_is_open() + * @see ssh_channel_is_open() */ int ssh_channel_is_closed(ssh_channel channel) { if(channel == NULL) { @@ -1566,8 +1520,8 @@ static int ssh_channel_request_termination(void *c){ static int channel_request(ssh_channel channel, const char *request, ssh_buffer buffer, int reply) { ssh_session session = channel->session; - ssh_string req = NULL; int rc = SSH_ERROR; + int ret; switch(channel->request_state){ case SSH_CHANNEL_REQ_STATE_NONE: @@ -1576,24 +1530,19 @@ static int channel_request(ssh_channel channel, const char *request, goto pending; } - req = ssh_string_from_char(request); - if (req == NULL) { + ret = ssh_buffer_pack(session->out_buffer, + "bdsb", + SSH2_MSG_CHANNEL_REQUEST, + channel->remote_channel, + request, + reply == 0 ? 0 : 1); + if (ret != SSH_OK) { ssh_set_error_oom(session); goto error; } - if (buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_REQUEST) < 0 || - buffer_add_u32(session->out_buffer, htonl(channel->remote_channel)) < 0 || - buffer_add_ssh_string(session->out_buffer, req) < 0 || - buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1) < 0) { - ssh_set_error_oom(session); - ssh_string_free(req); - goto error; - } - ssh_string_free(req); - if (buffer != NULL) { - if (buffer_add_data(session->out_buffer, buffer_get_rest(buffer), + if (ssh_buffer_add_data(session->out_buffer, buffer_get_rest(buffer), buffer_get_rest_len(buffer)) < 0) { ssh_set_error_oom(session); goto error; @@ -1647,7 +1596,7 @@ pending: return rc; error: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return rc; } @@ -1671,7 +1620,6 @@ error: int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, int col, int row) { ssh_session session; - ssh_string term = NULL; ssh_buffer buffer = NULL; int rc = SSH_ERROR; @@ -1705,19 +1653,17 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, goto error; } - term = ssh_string_from_char(terminal); - if (term == NULL) { - ssh_set_error_oom(session); - goto error; - } + rc = ssh_buffer_pack(buffer, + "sdddddb", + terminal, + col, + row, + 0, /* pix */ + 0, /* pix */ + 1, /* add a 0byte string */ + 0); - if (buffer_add_ssh_string(buffer, term) < 0 || - buffer_add_u32(buffer, htonl(col)) < 0 || - buffer_add_u32(buffer, htonl(row)) < 0 || - buffer_add_u32(buffer, 0) < 0 || - buffer_add_u32(buffer, 0) < 0 || - buffer_add_u32(buffer, htonl(1)) < 0 || /* Add a 0byte string */ - buffer_add_u8(buffer, 0) < 0) { + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } @@ -1725,7 +1671,6 @@ pending: rc = channel_request(channel, "pty-req", buffer, 1); error: ssh_buffer_free(buffer); - ssh_string_free(term); return rc; } @@ -1740,7 +1685,7 @@ error: * SSH_AGAIN if in nonblocking mode and call has * to be done again. * - * @see channel_request_pty_size() + * @see ssh_channel_request_pty_size() */ int ssh_channel_request_pty(ssh_channel channel) { return ssh_channel_request_pty_size(channel, "xterm", 80, 24); @@ -1780,10 +1725,13 @@ int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) { goto error; } - if (buffer_add_u32(buffer, htonl(cols)) < 0 || - buffer_add_u32(buffer, htonl(rows)) < 0 || - buffer_add_u32(buffer, 0) < 0 || - buffer_add_u32(buffer, 0) < 0) { + rc = ssh_buffer_pack(buffer, + "dddd", + cols, + rows, + 0, /* pix */ + 0 /* pix */); + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } @@ -1833,7 +1781,6 @@ int ssh_channel_request_shell(ssh_channel channel) { */ int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) { ssh_buffer buffer = NULL; - ssh_string subsystem = NULL; int rc = SSH_ERROR; if(channel == NULL) { @@ -1856,13 +1803,8 @@ int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) { goto error; } - subsystem = ssh_string_from_char(subsys); - if (subsystem == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - if (buffer_add_ssh_string(buffer, subsystem) < 0) { + rc = ssh_buffer_pack(buffer, "s", subsys); + if (rc != SSH_OK) { ssh_set_error_oom(channel->session); goto error; } @@ -1870,7 +1812,6 @@ pending: rc = channel_request(channel, "subsystem", buffer, 1); error: ssh_buffer_free(buffer); - ssh_string_free(subsystem); return rc; } @@ -1882,7 +1823,7 @@ int ssh_channel_request_sftp( ssh_channel channel){ return ssh_channel_request_subsystem(channel, "sftp"); } -static ssh_string generate_cookie(void) { +static char *generate_cookie(void) { static const char *hex = "0123456789abcdef"; char s[36]; unsigned char rnd[16]; @@ -1894,7 +1835,7 @@ static ssh_string generate_cookie(void) { s[i*2+1] = hex[rnd[i] >> 4]; } s[32] = '\0'; - return ssh_string_from_char(s); + return strdup(s); } /** @@ -1925,8 +1866,7 @@ static ssh_string generate_cookie(void) { int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, const char *cookie, int screen_number) { ssh_buffer buffer = NULL; - ssh_string p = NULL; - ssh_string c = NULL; + char *c = NULL; int rc = SSH_ERROR; if(channel == NULL) { @@ -1945,26 +1885,24 @@ int ssh_channel_request_x11(ssh_channel channel, int single_connection, const ch goto error; } - p = ssh_string_from_char(protocol ? protocol : "MIT-MAGIC-COOKIE-1"); - if (p == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - if (cookie) { - c = ssh_string_from_char(cookie); - } else { + if (cookie == NULL) { c = generate_cookie(); - } - if (c == NULL) { - ssh_set_error_oom(channel->session); - goto error; + if (c == NULL) { + ssh_set_error_oom(channel->session); + goto error; + } } - if (buffer_add_u8(buffer, single_connection == 0 ? 0 : 1) < 0 || - buffer_add_ssh_string(buffer, p) < 0 || - buffer_add_ssh_string(buffer, c) < 0 || - buffer_add_u32(buffer, htonl(screen_number)) < 0) { + rc = ssh_buffer_pack(buffer, + "bssd", + single_connection == 0 ? 0 : 1, + protocol ? protocol : "MIT-MAGIC-COOKIE-1", + cookie ? cookie : c, + screen_number); + if (c != NULL){ + SAFE_FREE(c); + } + if (rc != SSH_OK) { ssh_set_error_oom(channel->session); goto error; } @@ -1973,13 +1911,11 @@ pending: error: ssh_buffer_free(buffer); - ssh_string_free(p); - ssh_string_free(c); return rc; } static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, - int timeout_ms) { + int timeout_ms, int *destination_port) { #ifndef _WIN32 static const struct timespec ts = { .tv_sec = 0, @@ -1996,7 +1932,11 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, * 50 ms. So we need to decrement by 100 ms. */ for (t = timeout_ms; t >= 0; t -= 100) { - ssh_handle_packets(session, 50); + if (timeout_ms == 0) { + ssh_handle_packets(session, 0); + } else { + ssh_handle_packets(session, 50); + } if (session->ssh_message_list) { iterator = ssh_list_get_iterator(session->ssh_message_list); @@ -2006,6 +1946,10 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, ssh_message_subtype(msg) == channeltype) { ssh_list_remove(session->ssh_message_list, iterator); channel = ssh_message_channel_request_open_reply_accept(msg); + if(destination_port) { + *destination_port=msg->channel_request_open.destination_port; + } + ssh_message_free(msg); return channel; } @@ -2036,7 +1980,7 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, * the server. */ ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) { - return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms); + return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL); } /** @@ -2116,7 +2060,6 @@ static int ssh_global_request_termination(void *s){ */ static int global_request(ssh_session session, const char *request, ssh_buffer buffer, int reply) { - ssh_string req = NULL; int rc; switch (session->global_req_state) { @@ -2126,35 +2069,19 @@ static int global_request(ssh_session session, const char *request, goto pending; } - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST); - if (rc < 0) { - goto error; - } - - req = ssh_string_from_char(request); - if (req == NULL) { - ssh_set_error_oom(session); - rc = SSH_ERROR; - goto error; - } - - rc = buffer_add_ssh_string(session->out_buffer, req); - ssh_string_free(req); - if (rc < 0) { - ssh_set_error_oom(session); - rc = SSH_ERROR; - goto error; - } - - rc = buffer_add_u8(session->out_buffer, reply == 0 ? 0 : 1); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bsb", + SSH2_MSG_GLOBAL_REQUEST, + request, + reply == 0 ? 0 : 1); + if (rc != SSH_OK){ ssh_set_error_oom(session); rc = SSH_ERROR; goto error; } if (buffer != NULL) { - rc = buffer_add_data(session->out_buffer, + rc = ssh_buffer_add_data(session->out_buffer, buffer_get_rest(buffer), buffer_get_rest_len(buffer)); if (rc < 0) { @@ -2210,7 +2137,7 @@ pending: return rc; error: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return rc; } @@ -2237,11 +2164,13 @@ error: * SSH_AGAIN if in nonblocking mode and call has * to be done again. **/ -int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port) { +int ssh_channel_listen_forward(ssh_session session, + const char *address, + int port, + int *bound_port) +{ ssh_buffer buffer = NULL; - ssh_string addr = NULL; int rc = SSH_ERROR; - uint32_t tmp; if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) goto pending; @@ -2252,14 +2181,11 @@ int ssh_forward_listen(ssh_session session, const char *address, int port, int * goto error; } - addr = ssh_string_from_char(address ? address : ""); - if (addr == NULL) { - ssh_set_error_oom(session); - goto error; - } - - if (buffer_add_ssh_string(buffer, addr) < 0 || - buffer_add_u32(buffer, htonl(port)) < 0) { + rc = ssh_buffer_pack(buffer, + "sd", + address ? address : "", + port); + if (rc != SSH_OK){ ssh_set_error_oom(session); goto error; } @@ -2268,29 +2194,36 @@ pending: /* TODO: FIXME no guarantee the last packet we received contains * that info */ - if (rc == SSH_OK && port == 0 && bound_port) { - buffer_get_u32(session->in_buffer, &tmp); - *bound_port = ntohl(tmp); + if (rc == SSH_OK && port == 0 && bound_port != NULL) { + rc = ssh_buffer_unpack(session->in_buffer, "d", bound_port); + if (rc != SSH_OK) + *bound_port = 0; } error: ssh_buffer_free(buffer); - ssh_string_free(addr); return rc; } +/* DEPRECATED */ +ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) { + return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL); +} + /** - * @brief Accept an incoming TCP/IP forwarding channel. - * + * @brief Accept an incoming TCP/IP forwarding channel and get information + * about incomming connection * @param[in] session The ssh session to use. * * @param[in] timeout_ms A timeout in milliseconds. * + * @param[in] destination_port A pointer to destination port or NULL. + * * @return Newly created channel, or NULL if no incoming channel request from * the server */ -ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) { - return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms); +ssh_channel ssh_channel_accept_forward(ssh_session session, int timeout_ms, int* destination_port) { + return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port); } /** @@ -2308,9 +2241,11 @@ ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) { * SSH_AGAIN if in nonblocking mode and call has * to be done again. */ -int ssh_forward_cancel(ssh_session session, const char *address, int port) { +int ssh_channel_cancel_forward(ssh_session session, + const char *address, + int port) +{ ssh_buffer buffer = NULL; - ssh_string addr = NULL; int rc = SSH_ERROR; if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) @@ -2322,26 +2257,25 @@ int ssh_forward_cancel(ssh_session session, const char *address, int port) { goto error; } - addr = ssh_string_from_char(address ? address : ""); - if (addr == NULL) { - ssh_set_error_oom(session); - goto error; - } - - if (buffer_add_ssh_string(buffer, addr) < 0 || - buffer_add_u32(buffer, htonl(port)) < 0) { - ssh_set_error_oom(session); - goto error; + rc = ssh_buffer_pack(buffer, "sd", + address ? address : "", + port); + if (rc != SSH_OK){ + ssh_set_error_oom(session); + goto error; } pending: rc = global_request(session, "cancel-tcpip-forward", buffer, 1); error: ssh_buffer_free(buffer); - ssh_string_free(addr); return rc; } +int ssh_forward_cancel(ssh_session session, const char *address, int port) { + return ssh_channel_cancel_forward(session, address, port); +} + /** * @brief Set environment variables. * @@ -2359,7 +2293,6 @@ error: */ int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value) { ssh_buffer buffer = NULL; - ssh_string str = NULL; int rc = SSH_ERROR; if(channel == NULL) { @@ -2381,25 +2314,11 @@ int ssh_channel_request_env(ssh_channel channel, const char *name, const char *v goto error; } - str = ssh_string_from_char(name); - if (str == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - if (buffer_add_ssh_string(buffer, str) < 0) { - ssh_set_error_oom(channel->session); - goto error; - } - - ssh_string_free(str); - str = ssh_string_from_char(value); - if (str == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - if (buffer_add_ssh_string(buffer, str) < 0) { + rc = ssh_buffer_pack(buffer, + "ss", + name, + value); + if (rc != SSH_OK){ ssh_set_error_oom(channel->session); goto error; } @@ -2407,7 +2326,6 @@ pending: rc = channel_request(channel, "env", buffer,1); error: ssh_buffer_free(buffer); - ssh_string_free(str); return rc; } @@ -2426,24 +2344,25 @@ error: * SSH_ERROR if an error occurred, * SSH_AGAIN if in nonblocking mode and call has * to be done again. - * @code - * rc = channel_request_exec(channel, "ps aux"); - * if (rc > 0) { - * return -1; - * } * - * while ((rc = channel_read(channel, buffer, sizeof(buffer), 0)) > 0) { - * if (fwrite(buffer, 1, rc, stdout) != (unsigned int) rc) { - * return -1; - * } - * } - * @endcode + * Example: +@code + rc = channel_request_exec(channel, "ps aux"); + if (rc > 0) { + return -1; + } + + while ((rc = channel_read(channel, buffer, sizeof(buffer), 0)) > 0) { + if (fwrite(buffer, 1, rc, stdout) != (unsigned int) rc) { + return -1; + } + } +@endcode * - * @see channel_request_shell() + * @see ssh_channel_request_shell() */ int ssh_channel_request_exec(ssh_channel channel, const char *cmd) { ssh_buffer buffer = NULL; - ssh_string command = NULL; int rc = SSH_ERROR; if(channel == NULL) { @@ -2471,13 +2390,9 @@ int ssh_channel_request_exec(ssh_channel channel, const char *cmd) { goto error; } - command = ssh_string_from_char(cmd); - if (command == NULL) { - goto error; - ssh_set_error_oom(channel->session); - } + rc = ssh_buffer_pack(buffer, "s", cmd); - if (buffer_add_ssh_string(buffer, command) < 0) { + if (rc != SSH_OK) { ssh_set_error_oom(channel->session); goto error; } @@ -2485,7 +2400,6 @@ pending: rc = channel_request(channel, "exec", buffer, 1); error: ssh_buffer_free(buffer); - ssh_string_free(command); return rc; } @@ -2524,7 +2438,6 @@ error: */ int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) { ssh_buffer buffer = NULL; - ssh_string encoded_signal = NULL; int rc = SSH_ERROR; if(channel == NULL) { @@ -2547,13 +2460,8 @@ int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) { goto error; } - encoded_signal = ssh_string_from_char(sig); - if (encoded_signal == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - if (buffer_add_ssh_string(buffer, encoded_signal) < 0) { + rc = ssh_buffer_pack(buffer, "s", sig); + if (rc != SSH_OK) { ssh_set_error_oom(channel->session); goto error; } @@ -2561,7 +2469,6 @@ int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) { rc = channel_request(channel, "signal", buffer, 0); error: ssh_buffer_free(buffer); - ssh_string_free(encoded_signal); return rc; } @@ -2602,7 +2509,7 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, return SSH_ERROR; } - buffer_reinit(buffer); + ssh_buffer_reinit(buffer); if(count==0){ do { r=ssh_channel_poll(channel, is_stderr); @@ -2614,7 +2521,7 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, if(r < 0){ return r; } - if(buffer_add_data(buffer,buffer_tmp,r) < 0){ + if(ssh_buffer_add_data(buffer,buffer_tmp,r) < 0){ ssh_set_error_oom(session); r = SSH_ERROR; } @@ -2635,7 +2542,7 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, if(r==0){ return total; } - if(buffer_add_data(buffer,buffer_tmp,r) < 0){ + if (ssh_buffer_add_data(buffer,buffer_tmp,r) < 0) { ssh_set_error_oom(session); return SSH_ERROR; @@ -2685,7 +2592,40 @@ static int ssh_channel_read_termination(void *s){ * @warning The read function using a buffer has been renamed to * channel_read_buffer(). */ -int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr) { +int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr) +{ + return ssh_channel_read_timeout(channel, dest, count, is_stderr, -1); +} + +/** + * @brief Reads data from a channel. + * + * @param[in] channel The channel to read from. + * + * @param[in] dest The destination buffer which will get the data. + * + * @param[in] count The count of bytes to be read. + * + * @param[in] is_stderr A boolean value to mark reading from the stderr flow. + * + * @param[in] timeout_ms A timeout in milliseconds. A value of -1 means + * infinite timeout. + * + * @return The number of bytes read, 0 on end of file or SSH_ERROR + * on error. In nonblocking mode it Can return 0 if no data + * is available or SSH_AGAIN. + * + * @warning This function may return less than count bytes of data, and won't + * block until count bytes have been read. + * @warning The read function using a buffer has been renamed to + * channel_read_buffer(). + */ +int ssh_channel_read_timeout(ssh_channel channel, + void *dest, + uint32_t count, + int is_stderr, + int timeout) +{ ssh_session session; ssh_buffer stdbuf; uint32_t len; @@ -2715,7 +2655,7 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std * We may have problem if the window is too small to accept as much data * as asked */ - SSH_LOG(SSH_LOG_PROTOCOL, + SSH_LOG(SSH_LOG_PACKET, "Read (%d) buffered : %d bytes. Window: %d", count, buffer_get_rest_len(stdbuf), @@ -2733,8 +2673,15 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std ctx.channel = channel; ctx.buffer = stdbuf; ctx.count = 1; - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT, - ssh_channel_read_termination, &ctx); + + if (timeout < 0) { + timeout = SSH_TIMEOUT_DEFAULT; + } + + rc = ssh_handle_packets_termination(session, + timeout, + ssh_channel_read_termination, + &ctx); if (rc == SSH_ERROR){ return rc; } @@ -2749,6 +2696,9 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std len = (len > count ? count : len); memcpy(dest, buffer_get_rest(stdbuf), len); buffer_pass_bytes(stdbuf,len); + if (channel->counter != NULL) { + channel->counter->in_bytes += len; + } /* Authorize some buffering while userapp is busy */ if (channel->local_window < WINDOWLIMIT) { if (grow_window(session, channel, 0) < 0) { @@ -2778,7 +2728,7 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std * * @warning Don't forget to check for EOF as it would return 0 here. * - * @see channel_is_eof() + * @see ssh_channel_is_eof() */ int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, int is_stderr) { @@ -2830,7 +2780,7 @@ int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count * * @warning When the channel is in EOF state, the function returns SSH_EOF. * - * @see channel_is_eof() + * @see ssh_channel_is_eof() */ int ssh_channel_poll(ssh_channel channel, int is_stderr){ ssh_buffer stdbuf; @@ -2882,7 +2832,7 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr){ * * @warning When the channel is in EOF state, the function returns SSH_EOF. * - * @see channel_is_eof() + * @see ssh_channel_is_eof() */ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){ ssh_session session; @@ -2955,6 +2905,11 @@ static int ssh_channel_exit_status_termination(void *c){ * (yet). * @warning This function may block until a timeout (or never) * if the other side is not willing to close the channel. + * + * If you're looking for an async handling of this register a callback for the + * exit status. + * + * @see ssh_channel_exit_status_callback */ int ssh_channel_get_exit_status(ssh_channel channel) { int rc; @@ -3175,6 +3130,31 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, return 0; } +/** + * @brief Set the channel data counter. + * + * @code + * struct ssh_counter_struct counter = { + * .in_bytes = 0, + * .out_bytes = 0, + * .in_packets = 0, + * .out_packets = 0 + * }; + * + * ssh_channel_set_counter(channel, &counter); + * @endcode + * + * @param[in] channel The SSH channel. + * + * @param[in] counter Counter for bytes handled by the channel. + */ +void ssh_channel_set_counter(ssh_channel channel, + ssh_counter counter) { + if (channel != NULL) { + channel->counter = counter; + } +} + #if WITH_SERVER /** * @brief Blocking write on a channel stderr. @@ -3187,7 +3167,7 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, * * @return The number of bytes written, SSH_ERROR on error. * - * @see channel_read() + * @see ssh_channel_read() */ int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) { return channel_write_common(channel, data, len, 1); @@ -3221,7 +3201,6 @@ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost int remoteport, const char *sourcehost, int localport) { ssh_session session; ssh_buffer payload = NULL; - ssh_string str = NULL; int rc = SSH_ERROR; if(channel == NULL) { @@ -3232,7 +3211,6 @@ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost return rc; } - session = channel->session; if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN) @@ -3242,27 +3220,13 @@ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost ssh_set_error_oom(session); goto error; } - str = ssh_string_from_char(remotehost); - if (str == NULL) { - ssh_set_error_oom(session); - goto error; - } - - if (buffer_add_ssh_string(payload, str) < 0 || - buffer_add_u32(payload,htonl(remoteport)) < 0) { - ssh_set_error_oom(session); - goto error; - } - - ssh_string_free(str); - str = ssh_string_from_char(sourcehost); - if (str == NULL) { - ssh_set_error_oom(session); - goto error; - } - - if (buffer_add_ssh_string(payload, str) < 0 || - buffer_add_u32(payload,htonl(localport)) < 0) { + rc = ssh_buffer_pack(payload, + "sdsd", + remotehost, + remoteport, + sourcehost, + localport); + if (rc != SSH_OK){ ssh_set_error_oom(session); goto error; } @@ -3275,7 +3239,6 @@ pending: error: ssh_buffer_free(payload); - ssh_string_free(str); return rc; } @@ -3298,10 +3261,9 @@ error: * use channel_read and channel_write for this. */ int ssh_channel_open_x11(ssh_channel channel, - const char *orig_addr, int orig_port) { + const char *orig_addr, int orig_port) { ssh_session session; ssh_buffer payload = NULL; - ssh_string str = NULL; int rc = SSH_ERROR; if(channel == NULL) { @@ -3322,14 +3284,11 @@ int ssh_channel_open_x11(ssh_channel channel, goto error; } - str = ssh_string_from_char(orig_addr); - if (str == NULL) { - ssh_set_error_oom(session); - goto error; - } - - if (buffer_add_ssh_string(payload, str) < 0 || - buffer_add_u32(payload,htonl(orig_port)) < 0) { + rc = ssh_buffer_pack(payload, + "sd", + orig_addr, + orig_port); + if (rc != SSH_OK) { ssh_set_error_oom(session); goto error; } @@ -3342,7 +3301,6 @@ pending: error: ssh_buffer_free(payload); - ssh_string_free(str); return rc; } @@ -3381,7 +3339,8 @@ int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) { goto error; } - if (buffer_add_u32(buffer, ntohl(exit_status)) < 0) { + rc = ssh_buffer_pack(buffer, "d", exit_status); + if (rc != SSH_OK) { ssh_set_error_oom(channel->session); goto error; } @@ -3393,9 +3352,9 @@ error: } /** - * @brief Send an exit signal to remote process (as described in RFC 4254, section 6.10). + * @brief Send an exit signal to remote process (RFC 4254, section 6.10). * - * Sends a signal 'sig' to the remote process. + * This sends the exit status of the remote process. * Note, that remote system may not support signals concept. * In such a case this request will be silently ignored. * Only SSH-v2 is supported (I'm not sure about SSH-v1). @@ -3414,7 +3373,6 @@ error: int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig, int core, const char *errmsg, const char *lang) { ssh_buffer buffer = NULL; - ssh_string tmp = NULL; int rc = SSH_ERROR; if(channel == NULL) { @@ -3436,48 +3394,20 @@ int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig, goto error; } - tmp = ssh_string_from_char(sig); - if (tmp == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - if (buffer_add_ssh_string(buffer, tmp) < 0) { + rc = ssh_buffer_pack(buffer, + "sbss", + sig, + core ? 1 : 0, + errmsg, + lang); + if (rc != SSH_OK) { ssh_set_error_oom(channel->session); goto error; } - if (buffer_add_u8(buffer, core?1:0) < 0) { - ssh_set_error_oom(channel->session); - goto error; - } - - ssh_string_free(tmp); - tmp = ssh_string_from_char(errmsg); - if (tmp == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - if (buffer_add_ssh_string(buffer, tmp) < 0) { - ssh_set_error_oom(channel->session); - goto error; - } - - ssh_string_free(tmp); - tmp = ssh_string_from_char(lang); - if (tmp == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - if (buffer_add_ssh_string(buffer, tmp) < 0) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = channel_request(channel, "signal", buffer, 0); + rc = channel_request(channel, "exit-signal", buffer, 0); error: ssh_buffer_free(buffer); - if(tmp) - ssh_string_free(tmp); return rc; } diff --git a/libssh/src/channels1.c b/libssh/src/channels1.c index bc66488d..4d82c636 100644 --- a/libssh/src/channels1.c +++ b/libssh/src/channels1.c @@ -4,7 +4,7 @@ * This file is part of the SSH Library * * Copyright (c) 2003-2008 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider + * Copyright (c) 2009 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -356,7 +356,7 @@ int channel_write1(ssh_channel channel, const void *data, int len) { effectivelen = len > 32000 ? 32000 : len; if (buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 || - buffer_add_data(session->out_buffer, ptr, effectivelen) < 0) { + ssh_buffer_add_data(session->out_buffer, ptr, effectivelen) < 0) { return -1; } @@ -367,6 +367,9 @@ int channel_write1(ssh_channel channel, const void *data, int len) { return -1; } ssh_handle_packets(session, SSH_TIMEOUT_NONBLOCKING); + if (channel->counter != NULL) { + channel->counter->out_bytes += effectivelen; + } } if (ssh_blocking_flush(session,SSH_TIMEOUT_USER) == SSH_ERROR) return -1; diff --git a/libssh/src/client.c b/libssh/src/client.c index 1fb963d7..0a45944c 100644 --- a/libssh/src/client.c +++ b/libssh/src/client.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2003-2008 by Aris Adamantiadis + * Copyright (c) 2003-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -60,12 +60,15 @@ static void socket_callback_connected(int code, int errno_code, void *user){ ssh_session session=(ssh_session)user; - if(session->session_state != SSH_SESSION_STATE_CONNECTING){ + if (session->session_state != SSH_SESSION_STATE_CONNECTING && + session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED) + { ssh_set_error(session,SSH_FATAL, "Wrong state in socket_callback_connected : %d", session->session_state); return; } + SSH_LOG(SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code); if(code == SSH_SOCKET_CONNECTED_OK) session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED; @@ -152,19 +155,27 @@ int ssh_send_banner(ssh_session session, int server) { banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2; if (server) { - session->serverbanner = strdup(banner); + if(session->opts.custombanner == NULL){ + session->serverbanner = strdup(banner); + } else { + session->serverbanner = malloc(strlen(session->opts.custombanner) + 9); + if(!session->serverbanner) + goto end; + strcpy(session->serverbanner, "SSH-2.0-"); + strcat(session->serverbanner, session->opts.custombanner); + } if (session->serverbanner == NULL) { goto end; } + snprintf(buffer, 128, "%s\n", session->serverbanner); } else { session->clientbanner = strdup(banner); if (session->clientbanner == NULL) { goto end; } + snprintf(buffer, 128, "%s\n", session->clientbanner); } - snprintf(buffer, 128, "%s\n", banner); - if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERROR) { goto end; } @@ -258,24 +269,19 @@ static int ssh_service_request_termination(void *s){ * @bug actually only works with ssh-userauth */ int ssh_service_request(ssh_session session, const char *service) { - ssh_string service_s = NULL; int rc=SSH_ERROR; if(session->auth_service_state != SSH_AUTH_SERVICE_NONE) goto pending; - if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_REQUEST) < 0) { - return SSH_ERROR; - } - service_s = ssh_string_from_char(service); - if (service_s == NULL) { - return SSH_ERROR; - } - if (buffer_add_ssh_string(session->out_buffer,service_s) < 0) { - ssh_string_free(service_s); + rc = ssh_buffer_pack(session->out_buffer, + "bs", + SSH2_MSG_SERVICE_REQUEST, + service); + if (rc != SSH_OK){ + ssh_set_error_oom(session); return SSH_ERROR; } - ssh_string_free(service_s); session->auth_service_state=SSH_AUTH_SERVICE_SENT; if (packet_send(session) == SSH_ERROR) { ssh_set_error(session, SSH_FATAL, @@ -495,7 +501,12 @@ int ssh_connect(ssh_session session) { ssh_set_error(session, SSH_FATAL, "Couldn't apply options"); return SSH_ERROR; } - SSH_LOG(SSH_LOG_RARE,"libssh %s, using threading %s", ssh_copyright(), ssh_threads_get_type()); + + SSH_LOG(SSH_LOG_PROTOCOL, + "libssh %s, using threading %s", + ssh_copyright(), + ssh_threads_get_type()); + session->ssh_connection_callback = ssh_client_connection_callback; session->session_state=SSH_SESSION_STATE_CONNECTING; ssh_socket_set_callbacks(session->socket,&session->socket_callbacks); @@ -504,6 +515,7 @@ int ssh_connect(ssh_session session) { session->socket_callbacks.exception=ssh_socket_exception_callback; session->socket_callbacks.userdata=session; if (session->opts.fd != SSH_INVALID_SOCKET) { + session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED; ssh_socket_set_fd(session->socket, session->opts.fd); ret=SSH_OK; #ifndef _WIN32 @@ -535,7 +547,8 @@ pending: } SSH_LOG(SSH_LOG_PACKET,"Actual timeout : %d", timeout); ret = ssh_handle_packets_termination(session, timeout, ssh_connect_termination, session); - if (ret == SSH_ERROR || !ssh_connect_termination(session)) { + if (session->session_state != SSH_SESSION_STATE_ERROR && + (ret == SSH_ERROR || !ssh_connect_termination(session))) { ssh_set_error(session, SSH_FATAL, "Timeout connecting to %s", session->opts.host); session->session_state = SSH_SESSION_STATE_ERROR; @@ -612,32 +625,23 @@ int ssh_get_openssh_version(ssh_session session) { * @param[in] session The SSH session to use. */ void ssh_disconnect(ssh_session session) { - ssh_string str = NULL; struct ssh_iterator *it; + int rc; if (session == NULL) { return; } if (session->socket != NULL && ssh_socket_is_open(session->socket)) { - if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bds", + SSH2_MSG_DISCONNECT, + SSH2_DISCONNECT_BY_APPLICATION, + "Bye Bye"); + if (rc != SSH_OK){ + ssh_set_error_oom(session); goto error; } - if (buffer_add_u32(session->out_buffer, - htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) { - goto error; - } - - str = ssh_string_from_char("Bye Bye"); - if (str == NULL) { - goto error; - } - - if (buffer_add_ssh_string(session->out_buffer,str) < 0) { - ssh_string_free(str); - goto error; - } - ssh_string_free(str); packet_send(session); ssh_socket_close(session->socket); @@ -651,21 +655,25 @@ error: session->session_state=SSH_SESSION_STATE_DISCONNECTED; while ((it=ssh_list_get_iterator(session->channels)) != NULL) { - ssh_channel_free(ssh_iterator_value(ssh_channel,it)); + ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); ssh_list_remove(session->channels, it); } if(session->current_crypto){ crypto_free(session->current_crypto); session->current_crypto=NULL; } - if(session->in_buffer) - buffer_reinit(session->in_buffer); - if(session->out_buffer) - buffer_reinit(session->out_buffer); - if(session->in_hashbuf) - buffer_reinit(session->in_hashbuf); - if(session->out_hashbuf) - buffer_reinit(session->out_hashbuf); + if (session->in_buffer) { + ssh_buffer_reinit(session->in_buffer); + } + if (session->out_buffer) { + ssh_buffer_reinit(session->out_buffer); + } + if (session->in_hashbuf) { + ssh_buffer_reinit(session->in_hashbuf); + } + if (session->out_hashbuf) { + ssh_buffer_reinit(session->out_hashbuf); + } session->auth_methods = 0; SAFE_FREE(session->serverbanner); SAFE_FREE(session->clientbanner); @@ -687,8 +695,8 @@ error: } const char *ssh_copyright(void) { - return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2010 Aris Adamantiadis " - "(aris@0xbadc0de.be) Distributed under the LGPL, please refer to COPYING " + return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2014 Aris Adamantiadis, Andreas Schneider, " + "and libssh contributors. Distributed under the LGPL, please refer to COPYING " "file for information about your rights"; } /** @} */ diff --git a/libssh/src/config.c b/libssh/src/config.c index 7935e884..4c966ed3 100644 --- a/libssh/src/config.c +++ b/libssh/src/config.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2009 by Andreas Schneider + * Copyright (c) 2009-2013 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -46,7 +46,10 @@ enum ssh_config_opcode_e { SOC_PROTOCOL, SOC_STRICTHOSTKEYCHECK, SOC_KNOWNHOSTS, - SOC_PROXYCOMMAND + SOC_PROXYCOMMAND, + SOC_GSSAPISERVERIDENTITY, + SOC_GSSAPICLIENTIDENTITY, + SOC_GSSAPIDELEGATECREDENTIALS, }; struct ssh_config_keyword_table_s { @@ -67,6 +70,9 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = { { "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK }, { "userknownhostsfile", SOC_KNOWNHOSTS }, { "proxycommand", SOC_PROXYCOMMAND }, + { "gssapiserveridentity", SOC_GSSAPISERVERIDENTITY }, + { "gssapiserveridentity", SOC_GSSAPICLIENTIDENTITY }, + { "gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS }, { NULL, SOC_UNSUPPORTED } }; @@ -213,16 +219,25 @@ static int ssh_config_parse_line(ssh_session session, const char *line, switch (opcode) { case SOC_HOST: - *parsing = 0; - lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL; - for (p = ssh_config_get_str_tok(&s, NULL); p && *p; - p = ssh_config_get_str_tok(&s, NULL)) { - if (match_hostname(lowerhost, p, strlen(p))) { - *parsing = 1; + *parsing = 0; + lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL; + for (p = ssh_config_get_str_tok(&s, NULL); + p != NULL && p[0] != '\0'; + p = ssh_config_get_str_tok(&s, NULL)) { + char *z = ssh_path_expand_escape(session, p); + int ok; + + if (z == NULL) { + z = strdup(p); + } + ok = match_hostname(lowerhost, z, strlen(z)); + if (ok) { + *parsing = 1; + } + free(z); } - } - SAFE_FREE(lowerhost); - break; + SAFE_FREE(lowerhost); + break; case SOC_HOSTNAME: p = ssh_config_get_str_tok(&s, NULL); if (p && *parsing) { @@ -323,6 +338,24 @@ static int ssh_config_parse_line(ssh_session session, const char *line, ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p); } break; + case SOC_GSSAPISERVERIDENTITY: + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p); + } + break; + case SOC_GSSAPICLIENTIDENTITY: + p = ssh_config_get_str_tok(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p); + } + break; + case SOC_GSSAPIDELEGATECREDENTIALS: + i = ssh_config_get_yesno(&s, -1); + if (i >=0 && *parsing) { + ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i); + } + break; case SOC_UNSUPPORTED: SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d\n", keyword, count); @@ -350,7 +383,7 @@ int ssh_config_parse_file(ssh_session session, const char *filename) { return 0; } - SSH_LOG(SSH_LOG_RARE, "Reading configuration data from %s", filename); + SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename); parsing = 1; while (fgets(line, sizeof(line), f)) { diff --git a/libssh/src/connect.c b/libssh/src/connect.c index b299d41e..4ef85bc4 100644 --- a/libssh/src/connect.c +++ b/libssh/src/connect.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2003-2009 by Aris Adamantiadis + * Copyright (c) 2003-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -382,7 +382,15 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, continue; } - connect(s, itr->ai_addr, itr->ai_addrlen); + rc = connect(s, itr->ai_addr, itr->ai_addrlen); + if (rc == -1 && (errno != EINPROGRESS)) { + ssh_set_error(session, SSH_FATAL, + "Failed to connect: %s", strerror(errno)); + ssh_connect_socket_close(s); + s = -1; + continue; + } + break; } @@ -421,7 +429,7 @@ static int ssh_select_cb (socket_t fd, int revents, void *userdata){ * @param[in] readfds A fd_set of file descriptors to be select'ed for * reading. * - * @param[in] timeout A timeout for the select. + * @param[in] timeout The timeout in milliseconds. * * @return SSH_OK on success, * SSH_ERROR on error, diff --git a/libssh/src/curve25519.c b/libssh/src/curve25519.c index 153fbcd9..99d7145b 100644 --- a/libssh/src/curve25519.c +++ b/libssh/src/curve25519.c @@ -37,19 +37,14 @@ #include "libssh/crypto.h" #include "libssh/dh.h" #include "libssh/pki.h" +#include "libssh/bignum.h" /** @internal * @brief Starts curve25519-sha256@libssh.org key exchange */ int ssh_client_curve25519_init(ssh_session session){ - ssh_string client_pubkey; int rc; - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT); - if (rc < 0) { - return SSH_ERROR; - } - rc = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); if (rc == 0){ ssh_set_error(session, SSH_FATAL, "PRNG error"); @@ -58,15 +53,14 @@ int ssh_client_curve25519_init(ssh_session session){ crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey, session->next_crypto->curve25519_privkey); - client_pubkey = ssh_string_new(CURVE25519_PUBKEY_SIZE); - if (client_pubkey == NULL) { - return SSH_ERROR; - } - ssh_string_fill(client_pubkey, session->next_crypto->curve25519_client_pubkey, - CURVE25519_PUBKEY_SIZE); - rc = buffer_add_ssh_string(session->out_buffer,client_pubkey); - if (rc < 0) { - ssh_string_free(client_pubkey); + + rc = ssh_buffer_pack(session->out_buffer, + "bdP", + SSH2_MSG_KEX_ECDH_INIT, + CURVE25519_PUBKEY_SIZE, + (size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_client_pubkey); + if (rc != SSH_OK) { + ssh_set_error_oom(session); return SSH_ERROR; } @@ -90,7 +84,7 @@ static int ssh_curve25519_build_k(ssh_session session) { crypto_scalarmult(k, session->next_crypto->curve25519_privkey, session->next_crypto->curve25519_server_pubkey); - BN_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k); + bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k); #ifdef DEBUG_CRYPTO ssh_print_hexa("Session server cookie", @@ -133,6 +127,7 @@ int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet){ goto error; } memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE); + ssh_string_free(q_s_string); signature = buffer_get_ssh_string(packet); if (signature == NULL) { @@ -194,44 +189,36 @@ int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ rc = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); if (rc == 0){ - ssh_set_error(session, SSH_FATAL, "PRNG error"); - return SSH_ERROR; + ssh_set_error(session, SSH_FATAL, "PRNG error"); + return SSH_ERROR; } crypto_scalarmult_base(session->next_crypto->curve25519_server_pubkey, session->next_crypto->curve25519_privkey); - q_s_string = ssh_string_new(CURVE25519_PUBKEY_SIZE); - if (q_s_string == NULL) { - return SSH_ERROR; - } - - ssh_string_fill(q_s_string, session->next_crypto->curve25519_server_pubkey, - CURVE25519_PUBKEY_SIZE); - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_REPLY); if (rc < 0) { ssh_set_error_oom(session); - return SSH_ERROR; + goto error; } /* build k and session_id */ rc = ssh_curve25519_build_k(session); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - return SSH_ERROR; + goto error; } /* privkey is not allocated */ rc = ssh_get_key_params(session, &privkey); if (rc == SSH_ERROR) { - return SSH_ERROR; + goto error; } rc = make_sessionid(session); if (rc != SSH_OK) { ssh_set_error(session, SSH_FATAL, "Could not create a session id"); - return SSH_ERROR; + goto error; } /* add host's public key */ @@ -239,29 +226,37 @@ int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ session->next_crypto->server_pubkey); if (rc < 0) { ssh_set_error_oom(session); - return SSH_ERROR; + goto error; } /* add ecdh public key */ + q_s_string = ssh_string_new(CURVE25519_PUBKEY_SIZE); + if (q_s_string == NULL) { + goto error; + } + + ssh_string_fill(q_s_string, + session->next_crypto->curve25519_server_pubkey, + CURVE25519_PUBKEY_SIZE); + rc = buffer_add_ssh_string(session->out_buffer, q_s_string); ssh_string_free(q_s_string); - if (rc < 0) { ssh_set_error_oom(session); - return SSH_ERROR; + goto error; } /* add signature blob */ sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); - return SSH_ERROR; + goto error; } rc = buffer_add_ssh_string(session->out_buffer, sig_blob); ssh_string_free(sig_blob); if (rc < 0) { ssh_set_error_oom(session); - return SSH_ERROR; + goto error; } SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_ECDH_REPLY sent"); @@ -273,7 +268,7 @@ int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ /* Send the MSG_NEWKEYS */ rc = buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); if (rc < 0) { - return SSH_ERROR;; + goto error; } session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; @@ -281,6 +276,9 @@ int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); return rc; +error: + ssh_buffer_reinit(session->out_buffer); + return SSH_ERROR; } #endif /* WITH_SERVER */ diff --git a/libssh/src/dh.c b/libssh/src/dh.c index 5ebbc91e..e489a1d5 100644 --- a/libssh/src/dh.c +++ b/libssh/src/dh.c @@ -3,8 +3,8 @@ * * This file is part of the SSH Library * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider + * Copyright (c) 2003-2013 by Aris Adamantiadis + * Copyright (c) 2009-2013 by Andreas Schneider * Copyright (c) 2012 by Dmitriy Kuznetsov * * The SSH Library is free software; you can redistribute it and/or modify @@ -60,6 +60,7 @@ #include "libssh/dh.h" #include "libssh/ssh2.h" #include "libssh/pki.h" +#include "libssh/bignum.h" /* todo: remove it */ #include "libssh/string.h" @@ -170,7 +171,7 @@ int ssh_crypto_init(void) { return -1; } bignum_bin2bn(p_group14_value, P_GROUP14_LEN, &p_group14); - if (p_group1 == NULL) { + if (p_group14 == NULL) { bignum_free(g); bignum_free(p_group1); g = NULL; @@ -225,20 +226,6 @@ void ssh_crypto_finalize(void) { } } -/* prints the bignum on stderr */ -void ssh_print_bignum(const char *which, bignum num) { -#ifdef HAVE_LIBGCRYPT - unsigned char *hex = NULL; - bignum_bn2hex(num, &hex); -#elif defined HAVE_LIBCRYPTO - char *hex = NULL; - hex = bignum_bn2hex(num); -#endif - fprintf(stderr, "%s value: ", which); - fprintf(stderr, "%s\n", (hex == NULL) ? "(null)" : (char *) hex); - SAFE_FREE(hex); -} - int dh_generate_x(ssh_session session) { session->next_crypto->x = bignum_new(); if (session->next_crypto->x == NULL) { @@ -351,62 +338,6 @@ int dh_generate_f(ssh_session session) { return 0; } -ssh_string make_bignum_string(bignum num) { - ssh_string ptr = NULL; - int pad = 0; - unsigned int len = bignum_num_bytes(num); - unsigned int bits = bignum_num_bits(num); - - if (len == 0) { - return NULL; - } - - /* If the first bit is set we have a negative number */ - if (!(bits % 8) && bignum_is_bit_set(num, bits - 1)) { - pad++; - } - -#ifdef DEBUG_CRYPTO - fprintf(stderr, "%d bits, %d bytes, %d padding\n", bits, len, pad); -#endif /* DEBUG_CRYPTO */ - - ptr = ssh_string_new(len + pad); - if (ptr == NULL) { - return NULL; - } - - /* We have a negative number so we need a leading zero */ - if (pad) { - ptr->data[0] = 0; - } - -#ifdef HAVE_LIBGCRYPT - bignum_bn2bin(num, len, ptr->data + pad); -#elif HAVE_LIBCRYPTO - bignum_bn2bin(num, ptr->data + pad); -#endif - - return ptr; -} - -bignum make_string_bn(ssh_string string){ - bignum bn = NULL; - unsigned int len = ssh_string_len(string); - -#ifdef DEBUG_CRYPTO - fprintf(stderr, "Importing a %d bits, %d bytes object ...\n", - len * 8, len); -#endif /* DEBUG_CRYPTO */ - -#ifdef HAVE_LIBGCRYPT - bignum_bin2bn(string->data, len, &bn); -#elif defined HAVE_LIBCRYPTO - bn = bignum_bin2bn(string->data, len, NULL); -#endif - - return bn; -} - ssh_string dh_get_e(ssh_session session) { return make_bignum_string(session->next_crypto->e); } @@ -504,10 +435,6 @@ int ssh_client_dh_init(ssh_session session){ ssh_string e = NULL; int rc; - if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_INIT) < 0) { - goto error; - } - if (dh_generate_x(session) < 0) { goto error; } @@ -520,9 +447,11 @@ int ssh_client_dh_init(ssh_session session){ goto error; } - if (buffer_add_ssh_string(session->out_buffer, e) < 0) { + rc = ssh_buffer_pack(session->out_buffer, "bS", SSH2_MSG_KEXDH_INIT, e); + if (rc != SSH_OK) { goto error; } + ssh_string_burn(e); ssh_string_free(e); e=NULL; @@ -587,218 +516,180 @@ error: return SSH_ERROR; } - -/* -static void sha_add(ssh_string str,SHACTX ctx){ - sha1_update(ctx,str,string_len(str)+4); -#ifdef DEBUG_CRYPTO - ssh_print_hexa("partial hashed sessionid",str,string_len(str)+4); -#endif -} -*/ - int make_sessionid(ssh_session session) { - ssh_string num = NULL; - ssh_string str = NULL; - ssh_buffer server_hash = NULL; - ssh_buffer client_hash = NULL; - ssh_buffer buf = NULL; - uint32_t len; - int rc = SSH_ERROR; + ssh_string num = NULL; + ssh_buffer server_hash = NULL; + ssh_buffer client_hash = NULL; + ssh_buffer buf = NULL; + int rc = SSH_ERROR; - buf = ssh_buffer_new(); - if (buf == NULL) { - return rc; - } - - str = ssh_string_from_char(session->clientbanner); - if (str == NULL) { - goto error; - } - - if (buffer_add_ssh_string(buf, str) < 0) { - goto error; - } - ssh_string_free(str); - - str = ssh_string_from_char(session->serverbanner); - if (str == NULL) { - goto error; - } - - if (buffer_add_ssh_string(buf, str) < 0) { - goto error; - } - - if (session->client) { - server_hash = session->in_hashbuf; - client_hash = session->out_hashbuf; - } else { - server_hash = session->out_hashbuf; - client_hash = session->in_hashbuf; - } - - if (buffer_add_u32(server_hash, 0) < 0) { - goto error; - } - if (buffer_add_u8(server_hash, 0) < 0) { - goto error; - } - if (buffer_add_u32(client_hash, 0) < 0) { - goto error; - } - if (buffer_add_u8(client_hash, 0) < 0) { - goto error; - } - - len = ntohl(buffer_get_rest_len(client_hash)); - if (buffer_add_u32(buf,len) < 0) { - goto error; - } - if (buffer_add_data(buf, buffer_get_rest(client_hash), - buffer_get_rest_len(client_hash)) < 0) { - goto error; - } - - len = ntohl(buffer_get_rest_len(server_hash)); - if (buffer_add_u32(buf, len) < 0) { - goto error; - } - if (buffer_add_data(buf, buffer_get_rest(server_hash), - buffer_get_rest_len(server_hash)) < 0) { - goto error; - } - - len = ssh_string_len(session->next_crypto->server_pubkey) + 4; - if (buffer_add_data(buf, session->next_crypto->server_pubkey, len) < 0) { - goto error; - } - if(session->next_crypto->kex_type == SSH_KEX_DH_GROUP1_SHA1 || - session->next_crypto->kex_type == SSH_KEX_DH_GROUP14_SHA1) { - - num = make_bignum_string(session->next_crypto->e); - if (num == NULL) { - goto error; + buf = ssh_buffer_new(); + if (buf == NULL) { + return rc; } - len = ssh_string_len(num) + 4; - if (buffer_add_data(buf, num, len) < 0) { - goto error; + rc = ssh_buffer_pack(buf, + "ss", + session->clientbanner, + session->serverbanner); + if (rc == SSH_ERROR) { + goto error; } - ssh_string_free(num); - num = make_bignum_string(session->next_crypto->f); - if (num == NULL) { - goto error; + if (session->client) { + server_hash = session->in_hashbuf; + client_hash = session->out_hashbuf; + } else { + server_hash = session->out_hashbuf; + client_hash = session->in_hashbuf; } - len = ssh_string_len(num) + 4; - if (buffer_add_data(buf, num, len) < 0) { - goto error; + /* + * Handle the two final fields for the KEXINIT message (RFC 4253 7.1): + * + * boolean first_kex_packet_follows + * uint32 0 (reserved for future extension) + */ + rc = buffer_add_u8(server_hash, 0); + if (rc < 0) { + goto error; + } + rc = buffer_add_u32(server_hash, 0); + if (rc < 0) { + goto error; } - ssh_string_free(num); + /* These fields are handled for the server case in ssh_packet_kexinit. */ + if (session->client) { + rc = buffer_add_u8(client_hash, 0); + if (rc < 0) { + goto error; + } + rc = buffer_add_u32(client_hash, 0); + if (rc < 0) { + goto error; + } + } + + rc = ssh_buffer_pack(buf, + "dPdPS", + buffer_get_rest_len(client_hash), + buffer_get_rest_len(client_hash), + buffer_get_rest(client_hash), + buffer_get_rest_len(server_hash), + buffer_get_rest_len(server_hash), + buffer_get_rest(server_hash), + session->next_crypto->server_pubkey); + + if(rc != SSH_OK){ + goto error; + } + + if (session->next_crypto->kex_type == SSH_KEX_DH_GROUP1_SHA1 || + session->next_crypto->kex_type == SSH_KEX_DH_GROUP14_SHA1) { + rc = ssh_buffer_pack(buf, + "BB", + session->next_crypto->e, + session->next_crypto->f); + if (rc != SSH_OK) { + goto error; + } + #ifdef HAVE_ECDH - } else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256){ - if(session->next_crypto->ecdh_client_pubkey == NULL || - session->next_crypto->ecdh_server_pubkey == NULL){ - SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing"); - goto error; - } - rc = buffer_add_ssh_string(buf,session->next_crypto->ecdh_client_pubkey); - if (rc < 0) { - goto error; - } - rc = buffer_add_ssh_string(buf,session->next_crypto->ecdh_server_pubkey); - if (rc < 0) { - goto error; - } + } else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256) { + if (session->next_crypto->ecdh_client_pubkey == NULL || + session->next_crypto->ecdh_server_pubkey == NULL) { + SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing"); + goto error; + } + rc = ssh_buffer_pack(buf, + "SS", + session->next_crypto->ecdh_client_pubkey, + session->next_crypto->ecdh_server_pubkey); + if (rc != SSH_OK) { + goto error; + } #endif #ifdef HAVE_CURVE25519 - } else if(session->next_crypto->kex_type == SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG){ - rc = buffer_add_u32(buf, htonl(CURVE25519_PUBKEY_SIZE)); - rc += buffer_add_data(buf, session->next_crypto->curve25519_client_pubkey, - CURVE25519_PUBKEY_SIZE); - rc += buffer_add_u32(buf, htonl(CURVE25519_PUBKEY_SIZE)); - rc += buffer_add_data(buf, session->next_crypto->curve25519_server_pubkey, - CURVE25519_PUBKEY_SIZE); - if (rc != SSH_OK) { - goto error; - } -#endif - } - num = make_bignum_string(session->next_crypto->k); - if (num == NULL) { - goto error; - } + } else if (session->next_crypto->kex_type == SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG) { + rc = ssh_buffer_pack(buf, + "dPdP", + CURVE25519_PUBKEY_SIZE, + (size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_client_pubkey, + CURVE25519_PUBKEY_SIZE, + (size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_server_pubkey); - len = ssh_string_len(num) + 4; - if (buffer_add_data(buf, num, len) < 0) { - goto error; - } + if (rc != SSH_OK) { + goto error; + } +#endif + } + rc = ssh_buffer_pack(buf, "B", session->next_crypto->k); + if (rc != SSH_OK) { + goto error; + } #ifdef DEBUG_CRYPTO - ssh_print_hexa("hash buffer", ssh_buffer_get_begin(buf), ssh_buffer_get_len(buf)); + ssh_print_hexa("hash buffer", ssh_buffer_get_begin(buf), ssh_buffer_get_len(buf)); #endif - switch(session->next_crypto->kex_type){ + switch (session->next_crypto->kex_type) { case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP14_SHA1: - session->next_crypto->digest_len = SHA_DIGEST_LENGTH; - session->next_crypto->mac_type = SSH_MAC_SHA1; - session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); - if(session->next_crypto->secret_hash == NULL){ - ssh_set_error_oom(session); - goto error; - } - sha1(buffer_get_rest(buf), buffer_get_rest_len(buf), - session->next_crypto->secret_hash); - break; + session->next_crypto->digest_len = SHA_DIGEST_LENGTH; + session->next_crypto->mac_type = SSH_MAC_SHA1; + session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); + if (session->next_crypto->secret_hash == NULL) { + ssh_set_error_oom(session); + goto error; + } + sha1(buffer_get_rest(buf), buffer_get_rest_len(buf), + session->next_crypto->secret_hash); + break; case SSH_KEX_ECDH_SHA2_NISTP256: case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: - session->next_crypto->digest_len = SHA256_DIGEST_LENGTH; - session->next_crypto->mac_type = SSH_MAC_SHA256; - session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); - if(session->next_crypto->secret_hash == NULL){ - ssh_set_error_oom(session); - goto error; - } - sha256(buffer_get_rest(buf), buffer_get_rest_len(buf), - session->next_crypto->secret_hash); - break; - } - /* During the first kex, secret hash and session ID are equal. However, after - * a key re-exchange, a new secret hash is calculated. This hash will not replace - * but complement existing session id. - */ - if (!session->next_crypto->session_id){ - session->next_crypto->session_id = malloc(session->next_crypto->digest_len); - if (session->next_crypto->session_id == NULL){ - ssh_set_error_oom(session); - goto error; - } - memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash, - session->next_crypto->digest_len); - } + session->next_crypto->digest_len = SHA256_DIGEST_LENGTH; + session->next_crypto->mac_type = SSH_MAC_SHA256; + session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); + if (session->next_crypto->secret_hash == NULL) { + ssh_set_error_oom(session); + goto error; + } + sha256(buffer_get_rest(buf), buffer_get_rest_len(buf), + session->next_crypto->secret_hash); + break; + } + /* During the first kex, secret hash and session ID are equal. However, after + * a key re-exchange, a new secret hash is calculated. This hash will not replace + * but complement existing session id. + */ + if (!session->next_crypto->session_id) { + session->next_crypto->session_id = malloc(session->next_crypto->digest_len); + if (session->next_crypto->session_id == NULL) { + ssh_set_error_oom(session); + goto error; + } + memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash, + session->next_crypto->digest_len); + } #ifdef DEBUG_CRYPTO - printf("Session hash: \n"); - ssh_print_hexa("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len); - ssh_print_hexa("session id", session->next_crypto->session_id, session->next_crypto->digest_len); + printf("Session hash: \n"); + ssh_print_hexa("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len); + ssh_print_hexa("session id", session->next_crypto->session_id, session->next_crypto->digest_len); #endif - rc = SSH_OK; + rc = SSH_OK; error: - ssh_buffer_free(buf); - ssh_buffer_free(client_hash); - ssh_buffer_free(server_hash); + ssh_buffer_free(buf); + ssh_buffer_free(client_hash); + ssh_buffer_free(server_hash); - session->in_hashbuf = NULL; - session->out_hashbuf = NULL; + session->in_hashbuf = NULL; + session->out_hashbuf = NULL; - ssh_string_free(str); - ssh_string_free(num); + ssh_string_free(num); - return rc; + return rc; } int hashbufout_add_cookie(ssh_session session) { @@ -808,20 +699,20 @@ int hashbufout_add_cookie(ssh_session session) { } if (buffer_add_u8(session->out_hashbuf, 20) < 0) { - buffer_reinit(session->out_hashbuf); + ssh_buffer_reinit(session->out_hashbuf); return -1; } if (session->server) { - if (buffer_add_data(session->out_hashbuf, + if (ssh_buffer_add_data(session->out_hashbuf, session->next_crypto->server_kex.cookie, 16) < 0) { - buffer_reinit(session->out_hashbuf); + ssh_buffer_reinit(session->out_hashbuf); return -1; } } else { - if (buffer_add_data(session->out_hashbuf, + if (ssh_buffer_add_data(session->out_hashbuf, session->next_crypto->client_kex.cookie, 16) < 0) { - buffer_reinit(session->out_hashbuf); + ssh_buffer_reinit(session->out_hashbuf); return -1; } } @@ -836,11 +727,11 @@ int hashbufin_add_cookie(ssh_session session, unsigned char *cookie) { } if (buffer_add_u8(session->in_hashbuf, 20) < 0) { - buffer_reinit(session->in_hashbuf); + ssh_buffer_reinit(session->in_hashbuf); return -1; } - if (buffer_add_data(session->in_hashbuf,cookie, 16) < 0) { - buffer_reinit(session->in_hashbuf); + if (ssh_buffer_add_data(session->in_hashbuf,cookie, 16) < 0) { + ssh_buffer_reinit(session->in_hashbuf); return -1; } @@ -848,8 +739,10 @@ int hashbufin_add_cookie(ssh_session session, unsigned char *cookie) { } static int generate_one_key(ssh_string k, - struct ssh_crypto_struct *crypto, unsigned char *output, char letter) { + struct ssh_crypto_struct *crypto, unsigned char **output, char letter, size_t requested_size) { ssh_mac_ctx ctx; + unsigned char *tmp; + size_t size = crypto->digest_len; ctx=ssh_mac_ctx_init(crypto->mac_type); if (ctx == NULL) { @@ -860,14 +753,32 @@ static int generate_one_key(ssh_string k, ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); ssh_mac_update(ctx, &letter, 1); ssh_mac_update(ctx, crypto->session_id, crypto->digest_len); - ssh_mac_final(output, ctx); + ssh_mac_final(*output, ctx); + + while(requested_size > size) { + tmp = realloc(*output, size + crypto->digest_len); + if (tmp == NULL) { + return -1; + } + *output = tmp; + + ctx = ssh_mac_ctx_init(crypto->mac_type); + if (ctx == NULL) { + return -1; + } + ssh_mac_update(ctx, k, ssh_string_len(k) + 4); + ssh_mac_update(ctx, crypto->secret_hash, + crypto->digest_len); + ssh_mac_update(ctx, tmp, size); + ssh_mac_final(tmp + size, ctx); + size += crypto->digest_len; + } return 0; } int generate_session_keys(ssh_session session) { ssh_string k_string = NULL; - ssh_mac_ctx ctx = NULL; struct ssh_crypto_struct *crypto = session->next_crypto; int rc = -1; @@ -892,88 +803,71 @@ int generate_session_keys(ssh_session session) { /* IV */ if (session->client) { - if (generate_one_key(k_string, crypto, crypto->encryptIV, 'A') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'A', crypto->digest_len); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->decryptIV, 'B') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'B', crypto->digest_len); + if (rc < 0) { goto error; } } else { - if (generate_one_key(k_string, crypto, crypto->decryptIV, 'A') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'A', crypto->digest_len); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->encryptIV, 'B') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'B', crypto->digest_len); + if (rc < 0) { goto error; } } if (session->client) { - if (generate_one_key(k_string, crypto, crypto->encryptkey, 'C') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'C', crypto->out_cipher->keysize / 8); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->decryptkey, 'D') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'D', crypto->in_cipher->keysize / 8); + if (rc < 0) { goto error; } } else { - if (generate_one_key(k_string, crypto, crypto->decryptkey, 'C') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'C', crypto->in_cipher->keysize / 8); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->encryptkey, 'D') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'D', crypto->out_cipher->keysize / 8); + if (rc < 0) { goto error; } } - /* some ciphers need more than DIGEST_LEN bytes of input key */ - if (crypto->out_cipher->keysize > crypto->digest_len * 8) { - crypto->encryptkey = realloc(crypto->encryptkey, crypto->digest_len * 2); - if(crypto->encryptkey == NULL) - goto error; - ctx = ssh_mac_ctx_init(crypto->mac_type); - if (ctx == NULL) { - goto error; - } - ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); - ssh_mac_update(ctx, crypto->secret_hash, - crypto->digest_len); - ssh_mac_update(ctx, crypto->encryptkey, crypto->digest_len); - ssh_mac_final(crypto->encryptkey + crypto->digest_len, ctx); - } - - if (crypto->in_cipher->keysize > crypto->digest_len * 8) { - crypto->decryptkey = realloc(crypto->decryptkey, crypto->digest_len *2); - if(crypto->decryptkey == NULL) - goto error; - ctx = ssh_mac_ctx_init(crypto->mac_type); - ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); - ssh_mac_update(ctx, crypto->secret_hash, - crypto->digest_len); - ssh_mac_update(ctx, crypto->decryptkey, crypto->digest_len); - ssh_mac_final(crypto->decryptkey + crypto->digest_len, ctx); - } if(session->client) { - if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'E') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'E', hmac_digest_len(crypto->out_hmac)); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'F') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'F', hmac_digest_len(crypto->in_hmac)); + if (rc < 0) { goto error; } } else { - if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'E') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'E', hmac_digest_len(crypto->in_hmac)); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'F') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'F', hmac_digest_len(crypto->out_hmac)); + if (rc < 0) { goto error; } } #ifdef DEBUG_CRYPTO - ssh_print_hexa("Encrypt IV", crypto->encryptIV, SHA_DIGEST_LEN); - ssh_print_hexa("Decrypt IV", crypto->decryptIV, SHA_DIGEST_LEN); - ssh_print_hexa("Encryption key", crypto->encryptkey, - crypto->out_cipher->keysize); - ssh_print_hexa("Decryption key", crypto->decryptkey, - crypto->in_cipher->keysize); - ssh_print_hexa("Encryption MAC", crypto->encryptMAC, SHA_DIGEST_LEN); - ssh_print_hexa("Decryption MAC", crypto->decryptMAC, 20); + ssh_print_hexa("Encrypt IV", crypto->encryptIV, crypto->digest_len); + ssh_print_hexa("Decrypt IV", crypto->decryptIV, crypto->digest_len); + ssh_print_hexa("Encryption key", crypto->encryptkey, crypto->out_cipher->keysize / 8); + ssh_print_hexa("Decryption key", crypto->decryptkey, crypto->in_cipher->keysize / 8); + ssh_print_hexa("Encryption MAC", crypto->encryptMAC, hmac_digest_len(crypto->out_hmac)); + ssh_print_hexa("Decryption MAC", crypto->decryptMAC, hmac_digest_len(crypto->in_hmac)); #endif rc = 0; diff --git a/libssh/src/ecdh.c b/libssh/src/ecdh.c index 3f065e7e..e6310b4c 100644 --- a/libssh/src/ecdh.c +++ b/libssh/src/ecdh.c @@ -1,7 +1,7 @@ /* * This file is part of the SSH Library * - * Copyright (c) 2011 by Aris Adamantiadis + * Copyright (c) 2011-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -26,6 +26,7 @@ #include "libssh/buffer.h" #include "libssh/ssh2.h" #include "libssh/pki.h" +#include "libssh/bignum.h" #ifdef HAVE_ECDH #include @@ -154,7 +155,7 @@ static int ecdh_build_k(ssh_session session) { return -1; } - BN_bin2bn(buffer, len, session->next_crypto->k); + bignum_bin2bn(buffer, len, session->next_crypto->k); free(buffer); EC_KEY_free(session->next_crypto->ecdh_privkey); @@ -285,12 +286,6 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ session->next_crypto->ecdh_privkey = ecdh_key; session->next_crypto->ecdh_server_pubkey = q_s_string; - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY); - if (rc < 0) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - /* build k and session_id */ rc = ecdh_build_k(session); if (rc < 0) { @@ -310,30 +305,22 @@ int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ return SSH_ERROR; } - /* add host's public key */ - rc = buffer_add_ssh_string(session->out_buffer, - session->next_crypto->server_pubkey); - if (rc < 0) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - - /* add ecdh public key */ - rc = buffer_add_ssh_string(session->out_buffer, q_s_string); - if (rc < 0) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - /* add signature blob */ sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); if (sig_blob == NULL) { ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); return SSH_ERROR; } - rc = buffer_add_ssh_string(session->out_buffer, sig_blob); + rc = ssh_buffer_pack(session->out_buffer, + "bSSS", + SSH2_MSG_KEXDH_REPLY, + session->next_crypto->server_pubkey, /* host's pubkey */ + q_s_string, /* ecdh public key */ + sig_blob); /* signature blob */ + ssh_string_free(sig_blob); - if (rc < 0) { + + if (rc != SSH_OK) { ssh_set_error_oom(session); return SSH_ERROR; } diff --git a/libssh/src/ed25519.c b/libssh/src/ed25519.c new file mode 100644 index 00000000..2ae0ef4e --- /dev/null +++ b/libssh/src/ed25519.c @@ -0,0 +1,222 @@ +/* $OpenBSD: ed25519.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/ed25519.c + */ + +#include "config.h" + +#include "libssh/libcrypto.h" +#include "libssh/wrapper.h" +#include "libssh/ge25519.h" +#include "libssh/sc25519.h" +#include "libssh/ed25519.h" + +/* + * Public Domain, Author: Daniel J. Bernstein + * Copied from nacl-20110221/crypto_verify/32/ref/verify.c + */ + +static int crypto_verify_32(const unsigned char *x,const unsigned char *y) +{ + unsigned int differentbits = 0; +#define F(i) differentbits |= x[i] ^ y[i]; + F(0) + F(1) + F(2) + F(3) + F(4) + F(5) + F(6) + F(7) + F(8) + F(9) + F(10) + F(11) + F(12) + F(13) + F(14) + F(15) + F(16) + F(17) + F(18) + F(19) + F(20) + F(21) + F(22) + F(23) + F(24) + F(25) + F(26) + F(27) + F(28) + F(29) + F(30) + F(31) + + return (1 & ((differentbits - 1) >> 8)) - 1; +} + +static void get_hram(unsigned char *hram, + const unsigned char *sm, + const unsigned char *pk, + unsigned char *playground, + unsigned long long smlen) +{ + unsigned long long i; + SHA512CTX ctx; + for (i = 0;i < 32;++i) playground[i] = sm[i]; + for (i = 32;i < 64;++i) playground[i] = pk[i-32]; + for (i = 64;i < smlen;++i) playground[i] = sm[i]; + + ctx = sha512_init(); + sha512_update(ctx, playground, smlen); + sha512_final(hram, ctx); +} + + +int crypto_sign_ed25519_keypair(unsigned char *pk, + unsigned char *sk) +{ + sc25519 scsk; + ge25519 gepk; + SHA512CTX ctx; + unsigned char extsk[64]; + int i; + int rc; + + rc = ssh_get_random(sk, 32, 0); + if (rc < 0){ + return -1; + } + + ctx = sha512_init(); + sha512_update(ctx, sk, 32); + sha512_final(extsk, ctx); + extsk[0] &= 248; + extsk[31] &= 127; + extsk[31] |= 64; + + sc25519_from32bytes(&scsk,extsk); + + ge25519_scalarmult_base(&gepk, &scsk); + ge25519_pack(pk, &gepk); + for(i=0;i<32;i++) { + sk[32 + i] = pk[i]; + } + + return 0; +} + +int crypto_sign_ed25519(unsigned char *sm, + unsigned long long *smlen, + const unsigned char *m, + unsigned long long mlen, + const unsigned char *sk) +{ + sc25519 sck, scs, scsk; + ge25519 ger; + SHA512CTX ctx; + unsigned char r[32]; + unsigned char s[32]; + unsigned char extsk[64]; + unsigned long long i; + unsigned char hmg[SHA512_DIGEST_LEN]; + unsigned char hram[SHA512_DIGEST_LEN]; + + ctx = sha512_init(); + sha512_update(ctx, sk, 32); + sha512_final(extsk, ctx); + + extsk[0] &= 248; + extsk[31] &= 127; + extsk[31] |= 64; + + *smlen = mlen + 64; + for (i = 0;i < mlen; i++) { + sm[64 + i] = m[i]; + } + for (i = 0;i < 32; i++) { + sm[32 + i] = extsk[32+i]; + } + + /* Generate k as h(extsk[32],...,extsk[63],m) */ + ctx = sha512_init(); + sha512_update(ctx, sm + 32, mlen + 32); + sha512_final(hmg, ctx); + + /* Computation of R */ + sc25519_from64bytes(&sck, hmg); + ge25519_scalarmult_base(&ger, &sck); + ge25519_pack(r, &ger); + + /* Computation of s */ + for (i = 0; i < 32; i++) { + sm[i] = r[i]; + } + + get_hram(hram, sm, sk+32, sm, mlen+64); + + sc25519_from64bytes(&scs, hram); + sc25519_from32bytes(&scsk, extsk); + sc25519_mul(&scs, &scs, &scsk); + + sc25519_add(&scs, &scs, &sck); + + sc25519_to32bytes(s,&scs); /* cat s */ + for (i = 0;i < 32; i++) { + sm[32 + i] = s[i]; + } + + return 0; +} + +int crypto_sign_ed25519_open(unsigned char *m, + unsigned long long *mlen, + const unsigned char *sm, + unsigned long long smlen, + const unsigned char *pk) +{ + unsigned int i; + int ret; + unsigned char t2[32]; + ge25519 get1, get2; + sc25519 schram, scs; + unsigned char hram[SHA512_DIGEST_LEN]; + + *mlen = (unsigned long long) -1; + if (smlen < 64) return -1; + + if (ge25519_unpackneg_vartime(&get1, pk)) { + return -1; + } + + get_hram(hram,sm,pk,m,smlen); + + sc25519_from64bytes(&schram, hram); + + sc25519_from32bytes(&scs, sm+32); + + ge25519_double_scalarmult_vartime(&get2, + &get1, + &schram, + &ge25519_base, + &scs); + ge25519_pack(t2, &get2); + + ret = crypto_verify_32(sm, t2); + if (ret != 0) { + for (i = 0; i < smlen - 64; i++) { + m[i] = sm[i + 64]; + } + *mlen = smlen-64; + } else { + for (i = 0; i < smlen - 64; i++) { + m[i] = 0; + } + } + + return ret; +} diff --git a/libssh/src/error.c b/libssh/src/error.c index fbe0e787..bd755c4f 100644 --- a/libssh/src/error.c +++ b/libssh/src/error.c @@ -104,7 +104,7 @@ void _ssh_set_error_invalid(void *error, const char *function) /** * @brief Retrieve the error text message from the last error. * - * @param error The SSH session pointer. + * @param error An ssh_session or ssh_bind. * * @return A static string describing the error. */ @@ -117,7 +117,7 @@ const char *ssh_get_error(void *error) { /** * @brief Retrieve the error code from the last error. * - * @param error The SSH session pointer. + * @param error An ssh_session or ssh_bind. * * \return SSH_NO_ERROR No error occurred\n * SSH_REQUEST_DENIED The last request was denied but situation is diff --git a/libssh/src/fe25519.c b/libssh/src/fe25519.c new file mode 100644 index 00000000..0cedd89d --- /dev/null +++ b/libssh/src/fe25519.c @@ -0,0 +1,416 @@ +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/fe25519.c + */ + +#define WINDOWSIZE 1 /* Should be 1,2, or 4 */ +#define WINDOWMASK ((1<>= 31; /* 1: yes; 0: no */ + return x; +} + +static uint32_t ge(uint32_t a,uint32_t b) /* 16-bit inputs */ +{ + unsigned int x = a; + + x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */ + x >>= 31; /* 0: yes; 1: no */ + x ^= 1; /* 1: yes; 0: no */ + + return x; +} + +static uint32_t times19(uint32_t a) +{ + return (a << 4) + (a << 1) + a; +} + +static uint32_t times38(uint32_t a) +{ + return (a << 5) + (a << 2) + (a << 1); +} + +static void reduce_add_sub(fe25519 *r) +{ + uint32_t t; + int i,rep; + + for(rep = 0; rep < 4; rep++) { + t = r->v[31] >> 7; + r->v[31] &= 127; + t = times19(t); + r->v[0] += t; + for(i = 0; i < 31; i++) { + t = r->v[i] >> 8; + r->v[i+1] += t; + r->v[i] &= 255; + } + } +} + +static void reduce_mul(fe25519 *r) +{ + uint32_t t; + int i,rep; + + for(rep = 0; rep < 2; rep++) { + t = r->v[31] >> 7; + r->v[31] &= 127; + t = times19(t); + r->v[0] += t; + for(i = 0; i < 31; i++) { + t = r->v[i] >> 8; + r->v[i+1] += t; + r->v[i] &= 255; + } + } +} + +/* reduction modulo 2^255-19 */ +void fe25519_freeze(fe25519 *r) +{ + int i; + uint32_t m = equal(r->v[31],127); + + for (i = 30; i > 0; i--) { + m &= equal(r->v[i],255); + } + m &= ge(r->v[0],237); + + m = -m; + + r->v[31] -= m&127; + for (i = 30; i > 0; i--) { + r->v[i] -= m&255; + } + r->v[0] -= m&237; +} + +void fe25519_unpack(fe25519 *r, const unsigned char x[32]) +{ + int i; + + for (i = 0;i < 32; i++) { + r->v[i] = x[i]; + } + + r->v[31] &= 127; +} + +/* Assumes input x being reduced below 2^255 */ +void fe25519_pack(unsigned char r[32], const fe25519 *x) +{ + int i; + + fe25519 y = *x; + fe25519_freeze(&y); + + for (i = 0; i < 32; i++) { + r[i] = y.v[i]; + } +} + +int fe25519_iszero(const fe25519 *x) +{ + int i; + int r; + + fe25519 t = *x; + fe25519_freeze(&t); + + r = equal(t.v[0],0); + for (i = 1; i < 32; i++) { + r &= equal(t.v[i],0); + } + + return r; +} + +int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y) +{ + int i; + + fe25519 t1 = *x; + fe25519 t2 = *y; + fe25519_freeze(&t1); + fe25519_freeze(&t2); + + for (i = 0; i < 32; i++) { + if(t1.v[i] != t2.v[i]) { + return 0; + } + } + + return 1; +} + +void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b) +{ + int i; + uint32_t mask = b; + + mask = -mask; + + for (i = 0; i < 32; i++) { + r->v[i] ^= mask & (x->v[i] ^ r->v[i]); + } +} + +unsigned char fe25519_getparity(const fe25519 *x) +{ + fe25519 t = *x; + fe25519_freeze(&t); + + return t.v[0] & 1; +} + +void fe25519_setone(fe25519 *r) +{ + int i; + + r->v[0] = 1; + for (i = 1; i < 32; i++) { + r->v[i]=0; + } +} + +void fe25519_setzero(fe25519 *r) +{ + int i; + + for (i = 0; i < 32; i++) { + r->v[i]=0; + } +} + +void fe25519_neg(fe25519 *r, const fe25519 *x) +{ + fe25519 t; + int i; + + for (i = 0; i < 32; i++) { + t.v[i]=x->v[i]; + } + + fe25519_setzero(r); + fe25519_sub(r, r, &t); +} + +void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y) +{ + int i; + + for (i = 0; i < 32; i++) { + r->v[i] = x->v[i] + y->v[i]; + } + + reduce_add_sub(r); +} + +void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y) +{ + int i; + uint32_t t[32]; + + t[0] = x->v[0] + 0x1da; + t[31] = x->v[31] + 0xfe; + + for (i = 1; i < 31; i++) { + t[i] = x->v[i] + 0x1fe; + } + + for (i = 0; i < 32; i++) { + r->v[i] = t[i] - y->v[i]; + } + + reduce_add_sub(r); +} + +void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y) +{ + int i,j; + uint32_t t[63]; + + for (i = 0; i < 63; i++) { + t[i] = 0; + } + + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + t[i+j] += x->v[i] * y->v[j]; + } + } + + for (i = 32; i < 63; i++) { + r->v[i-32] = t[i-32] + times38(t[i]); + } + r->v[31] = t[31]; /* result now in r[0]...r[31] */ + + reduce_mul(r); +} + +void fe25519_square(fe25519 *r, const fe25519 *x) +{ + fe25519_mul(r, x, x); +} + +void fe25519_invert(fe25519 *r, const fe25519 *x) +{ + fe25519 z2; + fe25519 z9; + fe25519 z11; + fe25519 z2_5_0; + fe25519 z2_10_0; + fe25519 z2_20_0; + fe25519 z2_50_0; + fe25519 z2_100_0; + fe25519 t0; + fe25519 t1; + int i; + + /* 2 */ fe25519_square(&z2, x); + /* 4 */ fe25519_square(&t1, &z2); + /* 8 */ fe25519_square(&t0, &t1); + /* 9 */ fe25519_mul(&z9, &t0, x); + /* 11 */ fe25519_mul(&z11, &z9, &z2); + /* 22 */ fe25519_square(&t0, &z11); + /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0, &t0, &z9); + + /* 2^6 - 2^1 */ fe25519_square(&t0, &z2_5_0); + /* 2^7 - 2^2 */ fe25519_square(&t1, &t0); + /* 2^8 - 2^3 */ fe25519_square(&t0, &t1); + /* 2^9 - 2^4 */ fe25519_square(&t1, &t0); + /* 2^10 - 2^5 */ fe25519_square(&t0, &t1); + /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0, &t0, &z2_5_0); + + /* 2^11 - 2^1 */ fe25519_square(&t0, &z2_10_0); + /* 2^12 - 2^2 */ fe25519_square(&t1, &t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { + fe25519_square(&t0, &t1); + fe25519_square(&t1, &t0); + } + /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0, &t1, &z2_10_0); + + /* 2^21 - 2^1 */ fe25519_square(&t0, &z2_20_0); + /* 2^22 - 2^2 */ fe25519_square(&t1, &t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { + fe25519_square(&t0, &t1); + fe25519_square(&t1,&t0); + } + /* 2^40 - 2^0 */ fe25519_mul(&t0, &t1, &z2_20_0); + + /* 2^41 - 2^1 */ fe25519_square(&t1, &t0); + /* 2^42 - 2^2 */ fe25519_square(&t0, &t1); + /* 2^50 - 2^10 */ for (i = 2; i < 10;i += 2) { + fe25519_square(&t1, &t0); + fe25519_square(&t0, &t1); + } + /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0); + + /* 2^51 - 2^1 */ fe25519_square(&t0, &z2_50_0); + /* 2^52 - 2^2 */ fe25519_square(&t1, &t0); + /* 2^100 - 2^50 */ for (i = 2; i < 50; i += 2) { + fe25519_square(&t0, &t1); + fe25519_square(&t1,&t0); + } + /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0, &t1, &z2_50_0); + + /* 2^101 - 2^1 */ fe25519_square(&t1, &z2_100_0); + /* 2^102 - 2^2 */ fe25519_square(&t0, &t1); + /* 2^200 - 2^100 */ for (i = 2; i < 100; i += 2) { + fe25519_square(&t1, &t0); + fe25519_square(&t0,&t1); + } + /* 2^200 - 2^0 */ fe25519_mul(&t1, &t0, &z2_100_0); + + /* 2^201 - 2^1 */ fe25519_square(&t0, &t1); + /* 2^202 - 2^2 */ fe25519_square(&t1, &t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { + fe25519_square(&t0, &t1); + fe25519_square(&t1,&t0); + } + /* 2^250 - 2^0 */ fe25519_mul(&t0, &t1, &z2_50_0); + + /* 2^251 - 2^1 */ fe25519_square(&t1, &t0); + /* 2^252 - 2^2 */ fe25519_square(&t0, &t1); + /* 2^253 - 2^3 */ fe25519_square(&t1, &t0); + /* 2^254 - 2^4 */ fe25519_square(&t0, &t1); + /* 2^255 - 2^5 */ fe25519_square(&t1, &t0); + /* 2^255 - 21 */ fe25519_mul(r, &t1, &z11); +} + +void fe25519_pow2523(fe25519 *r, const fe25519 *x) +{ + fe25519 z2; + fe25519 z9; + fe25519 z11; + fe25519 z2_5_0; + fe25519 z2_10_0; + fe25519 z2_20_0; + fe25519 z2_50_0; + fe25519 z2_100_0; + fe25519 t; + int i; + + /* 2 */ fe25519_square(&z2, x); + /* 4 */ fe25519_square(&t, &z2); + /* 8 */ fe25519_square(&t, &t); + /* 9 */ fe25519_mul(&z9, &t, x); + /* 11 */ fe25519_mul(&z11, &z9, &z2); + /* 22 */ fe25519_square(&t, &z11); + /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0, &t, &z9); + + /* 2^6 - 2^1 */ fe25519_square(&t, &z2_5_0); + /* 2^10 - 2^5 */ for (i = 1; i < 5; i++) { + fe25519_square(&t,&t); + } + /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0, &t, &z2_5_0); + + /* 2^11 - 2^1 */ fe25519_square(&t, &z2_10_0); + /* 2^20 - 2^10 */ for (i = 1; i < 10; i++) { + fe25519_square(&t, &t); + } + /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0, &t, &z2_10_0); + + /* 2^21 - 2^1 */ fe25519_square(&t, &z2_20_0); + /* 2^40 - 2^20 */ for (i = 1; i < 20; i++) { + fe25519_square(&t,&t); + } + /* 2^40 - 2^0 */ fe25519_mul(&t, &t, &z2_20_0); + + /* 2^41 - 2^1 */ fe25519_square(&t, &t); + /* 2^50 - 2^10 */ for (i = 1; i < 10; i++) { + fe25519_square(&t,&t); + } + /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0, &t, &z2_10_0); + + /* 2^51 - 2^1 */ fe25519_square(&t, &z2_50_0); + /* 2^100 - 2^50 */ for (i = 1; i < 50; i++) { + fe25519_square(&t, &t); + } + /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0, &t, &z2_50_0); + + /* 2^101 - 2^1 */ fe25519_square(&t, &z2_100_0); + /* 2^200 - 2^100 */ for (i = 1; i < 100; i++) { + fe25519_square(&t, &t); + } + /* 2^200 - 2^0 */ fe25519_mul(&t, &t, &z2_100_0); + + /* 2^201 - 2^1 */ fe25519_square(&t, &t); + /* 2^250 - 2^50 */ for (i = 1; i < 50; i++) { + fe25519_square(&t, &t); + } + /* 2^250 - 2^0 */ fe25519_mul(&t, &t, &z2_50_0); + + /* 2^251 - 2^1 */ fe25519_square(&t, &t); + /* 2^252 - 2^2 */ fe25519_square(&t, &t); + /* 2^252 - 3 */ fe25519_mul(r, &t, x); +} diff --git a/libssh/src/ge25519.c b/libssh/src/ge25519.c new file mode 100644 index 00000000..20a33d1e --- /dev/null +++ b/libssh/src/ge25519.c @@ -0,0 +1,367 @@ +/* $OpenBSD: ge25519.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519.c + */ + +#include "libssh/fe25519.h" +#include "libssh/sc25519.h" +#include "libssh/ge25519.h" + +/* + * Arithmetic on the twisted Edwards curve -x^2 + y^2 = 1 + dx^2y^2 + * with d = -(121665/121666) = 37095705934669439343138083508754565189542113879843219016388785533085940283555 + * Base point: (15112221349535400772501151409588531511454012693041857206046113283949847762202,46316835694926478169428394003475163141307993866256225615783033603165251855960); + */ + +/* d */ +static const fe25519 ge25519_ecd = { + {0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, + 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00, + 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, + 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52} +}; + +/* 2*d */ +static const fe25519 ge25519_ec2d = { + {0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, + 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00, + 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, + 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24} +}; + +/* sqrt(-1) */ +static const fe25519 ge25519_sqrtm1 = { + {0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, + 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F, + 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, + 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B} +}; + +#define ge25519_p3 ge25519 + +typedef struct { + fe25519 x; + fe25519 z; + fe25519 y; + fe25519 t; +} ge25519_p1p1; + +typedef struct { + fe25519 x; + fe25519 y; + fe25519 z; +} ge25519_p2; + +typedef struct { + fe25519 x; + fe25519 y; +} ge25519_aff; + + +/* Packed coordinates of the base point */ +const ge25519 ge25519_base = { + {{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, + 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69, + 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, + 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}}, + {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, + 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20, + 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, + 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}} +}; + +/* Multiples of the base point in affine representation */ +static const ge25519_aff ge25519_base_multiples_affine[425] = { +#include "ge25519_base.data" +}; + +static void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p) +{ + fe25519_mul(&r->x, &p->x, &p->t); + fe25519_mul(&r->y, &p->y, &p->z); + fe25519_mul(&r->z, &p->z, &p->t); +} + +static void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p) +{ + p1p1_to_p2((ge25519_p2 *)r, p); + fe25519_mul(&r->t, &p->x, &p->y); +} + +static void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q) +{ + fe25519 a,b,t1,t2,c,d,e,f,g,h,qt; + fe25519_mul(&qt, &q->x, &q->y); + fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */ + fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */ + fe25519_sub(&t1, &q->y, &q->x); + fe25519_add(&t2, &q->y, &q->x); + fe25519_mul(&a, &a, &t1); + fe25519_mul(&b, &b, &t2); + fe25519_sub(&e, &b, &a); /* E = B-A */ + fe25519_add(&h, &b, &a); /* H = B+A */ + fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */ + fe25519_mul(&c, &c, &ge25519_ec2d); + fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */ + fe25519_sub(&f, &d, &c); /* F = D-C */ + fe25519_add(&g, &d, &c); /* G = D+C */ + fe25519_mul(&r->x, &e, &f); + fe25519_mul(&r->y, &h, &g); + fe25519_mul(&r->z, &g, &f); + fe25519_mul(&r->t, &e, &h); +} + +static void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q) +{ + fe25519 a, b, c, d, t; + + fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */ + fe25519_sub(&t, &q->y, &q->x); + fe25519_mul(&a, &a, &t); + fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */ + fe25519_add(&t, &q->x, &q->y); + fe25519_mul(&b, &b, &t); + fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */ + fe25519_mul(&c, &c, &ge25519_ec2d); + fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */ + fe25519_add(&d, &d, &d); + fe25519_sub(&r->x, &b, &a); /* E = B-A */ + fe25519_sub(&r->t, &d, &c); /* F = D-C */ + fe25519_add(&r->z, &d, &c); /* G = D+C */ + fe25519_add(&r->y, &b, &a); /* H = B+A */ +} + +/* See http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd */ +static void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p) +{ + fe25519 a,b,c,d; + fe25519_square(&a, &p->x); + fe25519_square(&b, &p->y); + fe25519_square(&c, &p->z); + fe25519_add(&c, &c, &c); + fe25519_neg(&d, &a); + + fe25519_add(&r->x, &p->x, &p->y); + fe25519_square(&r->x, &r->x); + fe25519_sub(&r->x, &r->x, &a); + fe25519_sub(&r->x, &r->x, &b); + fe25519_add(&r->z, &d, &b); + fe25519_sub(&r->t, &r->z, &c); + fe25519_sub(&r->y, &d, &b); +} + +/* Constant-time version of: if(b) r = p */ +static void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b) +{ + fe25519_cmov(&r->x, &p->x, b); + fe25519_cmov(&r->y, &p->y, b); +} + +static unsigned char equal(signed char b,signed char c) +{ + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + uint32_t y = x; /* 0: yes; 1..255: no */ + + y -= 1; /* 4294967295: yes; 0..254: no */ + y >>= 31; /* 1: yes; 0: no */ + + return y; +} + +static unsigned char negative(signed char b) +{ + unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + + x >>= 63; /* 1: yes; 0: no */ + + return x; +} + +static void choose_t(ge25519_aff *t, unsigned long long pos, signed char b) +{ + /* constant time */ + fe25519 v; + + *t = ge25519_base_multiples_affine[5 * pos + 0]; + + cmov_aff(t, &ge25519_base_multiples_affine[5 * pos + 1], + equal(b,1) | equal(b,-1)); + cmov_aff(t, &ge25519_base_multiples_affine[5 * pos + 2], + equal(b,2) | equal(b,-2)); + cmov_aff(t, &ge25519_base_multiples_affine[5 * pos + 3], + equal(b,3) | equal(b,-3)); + cmov_aff(t, &ge25519_base_multiples_affine[5 * pos + 4], + equal(b,-4)); + + fe25519_neg(&v, &t->x); + fe25519_cmov(&t->x, &v, negative(b)); +} + +static void setneutral(ge25519 *r) +{ + fe25519_setzero(&r->x); + fe25519_setone(&r->y); + fe25519_setone(&r->z); + fe25519_setzero(&r->t); +} + +/* ******************************************************************** + * EXPORTED FUNCTIONS + ******************************************************************** */ + +/* return 0 on success, -1 otherwise */ +int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32]) +{ + unsigned char par; + + fe25519 t, chk, num, den, den2, den4, den6; + fe25519_setone(&r->z); + par = p[31] >> 7; + fe25519_unpack(&r->y, p); + fe25519_square(&num, &r->y); /* x = y^2 */ + fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */ + fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */ + fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */ + + /* Computation of sqrt(num/den) */ + /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */ + fe25519_square(&den2, &den); + fe25519_square(&den4, &den2); + fe25519_mul(&den6, &den4, &den2); + fe25519_mul(&t, &den6, &num); + fe25519_mul(&t, &t, &den); + + fe25519_pow2523(&t, &t); + /* 2. computation of r->x = t * num * den^3 */ + fe25519_mul(&t, &t, &num); + fe25519_mul(&t, &t, &den); + fe25519_mul(&t, &t, &den); + fe25519_mul(&r->x, &t, &den); + + /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */ + fe25519_square(&chk, &r->x); + fe25519_mul(&chk, &chk, &den); + if (!fe25519_iseq_vartime(&chk, &num)) { + fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1); + } + + /* 4. Now we have one of the two square roots, except if input was not a square */ + fe25519_square(&chk, &r->x); + fe25519_mul(&chk, &chk, &den); + if (!fe25519_iseq_vartime(&chk, &num)) { + return -1; + } + + /* 5. Choose the desired square root according to parity: */ + if(fe25519_getparity(&r->x) != (1-par)) { + fe25519_neg(&r->x, &r->x); + } + + fe25519_mul(&r->t, &r->x, &r->y); + + return 0; +} + +void ge25519_pack(unsigned char r[32], const ge25519_p3 *p) +{ + fe25519 tx, ty, zi; + + fe25519_invert(&zi, &p->z); + fe25519_mul(&tx, &p->x, &zi); + fe25519_mul(&ty, &p->y, &zi); + fe25519_pack(r, &ty); + + r[31] ^= fe25519_getparity(&tx) << 7; +} + +int ge25519_isneutral_vartime(const ge25519_p3 *p) +{ + int ret = 1; + + if (!fe25519_iszero(&p->x)) { + ret = 0; + } + + if (!fe25519_iseq_vartime(&p->y, &p->z)) { + ret = 0; + } + + return ret; +} + +/* computes [s1]p1 + [s2]p2 */ +void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2) +{ + ge25519_p1p1 tp1p1; + ge25519_p3 pre[16]; + unsigned char b[127]; + int i; + + /* precomputation s2 s1 */ + setneutral(pre); /* 00 00 */ + pre[1] = *p1; /* 00 01 */ + dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */ + add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */ + pre[4] = *p2; /* 01 00 */ + add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */ + add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */ + add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */ + dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */ + add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */ + dbl_p1p1(&tp1p1,(ge25519_p2 *)&pre[5]); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */ + add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */ + add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */ + add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */ + add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */ + add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */ + + sc25519_2interleave2(b,s1,s2); + + /* scalar multiplication */ + *r = pre[b[126]]; + + for (i = 125; i >= 0; i--) { + dbl_p1p1(&tp1p1, (ge25519_p2 *)r); + p1p1_to_p2((ge25519_p2 *) r, &tp1p1); + dbl_p1p1(&tp1p1, (ge25519_p2 *)r); + if(b[i] != 0) { + p1p1_to_p3(r, &tp1p1); + add_p1p1(&tp1p1, r, &pre[b[i]]); + } + if (i != 0) { + p1p1_to_p2((ge25519_p2 *)r, &tp1p1); + } else { + p1p1_to_p3(r, &tp1p1); + } + } +} + +void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s) +{ + signed char b[85]; + int i; + ge25519_aff t; + + sc25519_window3(b,s); + + choose_t((ge25519_aff *)r, 0, b[0]); + fe25519_setone(&r->z); + fe25519_mul(&r->t, &r->x, &r->y); + for (i = 1; i < 85; i++) { + choose_t(&t, (unsigned long long) i, b[i]); + ge25519_mixadd2(r, &t); + } +} diff --git a/libssh/src/ge25519_base.data b/libssh/src/ge25519_base.data new file mode 100644 index 00000000..66fb1b61 --- /dev/null +++ b/libssh/src/ge25519_base.data @@ -0,0 +1,858 @@ +/* $OpenBSD: ge25519_base.data,v 1.3 2013/12/09 11:03:45 markus Exp $ */ + +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519_base.data + */ + +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21}} , + {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}}, +{{{0x0e, 0xce, 0x43, 0x28, 0x4e, 0xa1, 0xc5, 0x83, 0x5f, 0xa4, 0xd7, 0x15, 0x45, 0x8e, 0x0d, 0x08, 0xac, 0xe7, 0x33, 0x18, 0x7d, 0x3b, 0x04, 0x3d, 0x6c, 0x04, 0x5a, 0x9f, 0x4c, 0x38, 0xab, 0x36}} , + {{0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0x0e, 0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39, 0x97, 0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d, 0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd, 0x60, 0x22}}}, +{{{0x5c, 0xe2, 0xf8, 0xd3, 0x5f, 0x48, 0x62, 0xac, 0x86, 0x48, 0x62, 0x81, 0x19, 0x98, 0x43, 0x63, 0x3a, 0xc8, 0xda, 0x3e, 0x74, 0xae, 0xf4, 0x1f, 0x49, 0x8f, 0x92, 0x22, 0x4a, 0x9c, 0xae, 0x67}} , + {{0xd4, 0xb4, 0xf5, 0x78, 0x48, 0x68, 0xc3, 0x02, 0x04, 0x03, 0x24, 0x67, 0x17, 0xec, 0x16, 0x9f, 0xf7, 0x9e, 0x26, 0x60, 0x8e, 0xa1, 0x26, 0xa1, 0xab, 0x69, 0xee, 0x77, 0xd1, 0xb1, 0x67, 0x12}}}, +{{{0x70, 0xf8, 0xc9, 0xc4, 0x57, 0xa6, 0x3a, 0x49, 0x47, 0x15, 0xce, 0x93, 0xc1, 0x9e, 0x73, 0x1a, 0xf9, 0x20, 0x35, 0x7a, 0xb8, 0xd4, 0x25, 0x83, 0x46, 0xf1, 0xcf, 0x56, 0xdb, 0xa8, 0x3d, 0x20}} , + {{0x2f, 0x11, 0x32, 0xca, 0x61, 0xab, 0x38, 0xdf, 0xf0, 0x0f, 0x2f, 0xea, 0x32, 0x28, 0xf2, 0x4c, 0x6c, 0x71, 0xd5, 0x80, 0x85, 0xb8, 0x0e, 0x47, 0xe1, 0x95, 0x15, 0xcb, 0x27, 0xe8, 0xd0, 0x47}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xc8, 0x84, 0xa5, 0x08, 0xbc, 0xfd, 0x87, 0x3b, 0x99, 0x8b, 0x69, 0x80, 0x7b, 0xc6, 0x3a, 0xeb, 0x93, 0xcf, 0x4e, 0xf8, 0x5c, 0x2d, 0x86, 0x42, 0xb6, 0x71, 0xd7, 0x97, 0x5f, 0xe1, 0x42, 0x67}} , + {{0xb4, 0xb9, 0x37, 0xfc, 0xa9, 0x5b, 0x2f, 0x1e, 0x93, 0xe4, 0x1e, 0x62, 0xfc, 0x3c, 0x78, 0x81, 0x8f, 0xf3, 0x8a, 0x66, 0x09, 0x6f, 0xad, 0x6e, 0x79, 0x73, 0xe5, 0xc9, 0x00, 0x06, 0xd3, 0x21}}}, +{{{0xf8, 0xf9, 0x28, 0x6c, 0x6d, 0x59, 0xb2, 0x59, 0x74, 0x23, 0xbf, 0xe7, 0x33, 0x8d, 0x57, 0x09, 0x91, 0x9c, 0x24, 0x08, 0x15, 0x2b, 0xe2, 0xb8, 0xee, 0x3a, 0xe5, 0x27, 0x06, 0x86, 0xa4, 0x23}} , + {{0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8, 0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a, 0xb0, 0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f, 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96, 0x96, 0x70}}}, +{{{0x81, 0x6b, 0x88, 0xe8, 0x1e, 0xc7, 0x77, 0x96, 0x0e, 0xa1, 0xa9, 0x52, 0xe0, 0xd8, 0x0e, 0x61, 0x9e, 0x79, 0x2d, 0x95, 0x9c, 0x8d, 0x96, 0xe0, 0x06, 0x40, 0x5d, 0x87, 0x28, 0x5f, 0x98, 0x70}} , + {{0xf1, 0x79, 0x7b, 0xed, 0x4f, 0x44, 0xb2, 0xe7, 0x08, 0x0d, 0xc2, 0x08, 0x12, 0xd2, 0x9f, 0xdf, 0xcd, 0x93, 0x20, 0x8a, 0xcf, 0x33, 0xca, 0x6d, 0x89, 0xb9, 0x77, 0xc8, 0x93, 0x1b, 0x4e, 0x60}}}, +{{{0x26, 0x4f, 0x7e, 0x97, 0xf6, 0x40, 0xdd, 0x4f, 0xfc, 0x52, 0x78, 0xf9, 0x90, 0x31, 0x03, 0xe6, 0x7d, 0x56, 0x39, 0x0b, 0x1d, 0x56, 0x82, 0x85, 0xf9, 0x1a, 0x42, 0x17, 0x69, 0x6c, 0xcf, 0x39}} , + {{0x69, 0xd2, 0x06, 0x3a, 0x4f, 0x39, 0x2d, 0xf9, 0x38, 0x40, 0x8c, 0x4c, 0xe7, 0x05, 0x12, 0xb4, 0x78, 0x8b, 0xf8, 0xc0, 0xec, 0x93, 0xde, 0x7a, 0x6b, 0xce, 0x2c, 0xe1, 0x0e, 0xa9, 0x34, 0x44}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x0b, 0xa4, 0x3c, 0xb0, 0x0f, 0x7a, 0x51, 0xf1, 0x78, 0xd6, 0xd9, 0x6a, 0xfd, 0x46, 0xe8, 0xb8, 0xa8, 0x79, 0x1d, 0x87, 0xf9, 0x90, 0xf2, 0x9c, 0x13, 0x29, 0xf8, 0x0b, 0x20, 0x64, 0xfa, 0x05}} , + {{0x26, 0x09, 0xda, 0x17, 0xaf, 0x95, 0xd6, 0xfb, 0x6a, 0x19, 0x0d, 0x6e, 0x5e, 0x12, 0xf1, 0x99, 0x4c, 0xaa, 0xa8, 0x6f, 0x79, 0x86, 0xf4, 0x72, 0x28, 0x00, 0x26, 0xf9, 0xea, 0x9e, 0x19, 0x3d}}}, +{{{0x87, 0xdd, 0xcf, 0xf0, 0x5b, 0x49, 0xa2, 0x5d, 0x40, 0x7a, 0x23, 0x26, 0xa4, 0x7a, 0x83, 0x8a, 0xb7, 0x8b, 0xd2, 0x1a, 0xbf, 0xea, 0x02, 0x24, 0x08, 0x5f, 0x7b, 0xa9, 0xb1, 0xbe, 0x9d, 0x37}} , + {{0xfc, 0x86, 0x4b, 0x08, 0xee, 0xe7, 0xa0, 0xfd, 0x21, 0x45, 0x09, 0x34, 0xc1, 0x61, 0x32, 0x23, 0xfc, 0x9b, 0x55, 0x48, 0x53, 0x99, 0xf7, 0x63, 0xd0, 0x99, 0xce, 0x01, 0xe0, 0x9f, 0xeb, 0x28}}}, +{{{0x47, 0xfc, 0xab, 0x5a, 0x17, 0xf0, 0x85, 0x56, 0x3a, 0x30, 0x86, 0x20, 0x28, 0x4b, 0x8e, 0x44, 0x74, 0x3a, 0x6e, 0x02, 0xf1, 0x32, 0x8f, 0x9f, 0x3f, 0x08, 0x35, 0xe9, 0xca, 0x16, 0x5f, 0x6e}} , + {{0x1c, 0x59, 0x1c, 0x65, 0x5d, 0x34, 0xa4, 0x09, 0xcd, 0x13, 0x9c, 0x70, 0x7d, 0xb1, 0x2a, 0xc5, 0x88, 0xaf, 0x0b, 0x60, 0xc7, 0x9f, 0x34, 0x8d, 0xd6, 0xb7, 0x7f, 0xea, 0x78, 0x65, 0x8d, 0x77}}}, +{{{0x56, 0xa5, 0xc2, 0x0c, 0xdd, 0xbc, 0xb8, 0x20, 0x6d, 0x57, 0x61, 0xb5, 0xfb, 0x78, 0xb5, 0xd4, 0x49, 0x54, 0x90, 0x26, 0xc1, 0xcb, 0xe9, 0xe6, 0xbf, 0xec, 0x1d, 0x4e, 0xed, 0x07, 0x7e, 0x5e}} , + {{0xc7, 0xf6, 0x6c, 0x56, 0x31, 0x20, 0x14, 0x0e, 0xa8, 0xd9, 0x27, 0xc1, 0x9a, 0x3d, 0x1b, 0x7d, 0x0e, 0x26, 0xd3, 0x81, 0xaa, 0xeb, 0xf5, 0x6b, 0x79, 0x02, 0xf1, 0x51, 0x5c, 0x75, 0x55, 0x0f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x0a, 0x34, 0xcd, 0x82, 0x3c, 0x33, 0x09, 0x54, 0xd2, 0x61, 0x39, 0x30, 0x9b, 0xfd, 0xef, 0x21, 0x26, 0xd4, 0x70, 0xfa, 0xee, 0xf9, 0x31, 0x33, 0x73, 0x84, 0xd0, 0xb3, 0x81, 0xbf, 0xec, 0x2e}} , + {{0xe8, 0x93, 0x8b, 0x00, 0x64, 0xf7, 0x9c, 0xb8, 0x74, 0xe0, 0xe6, 0x49, 0x48, 0x4d, 0x4d, 0x48, 0xb6, 0x19, 0xa1, 0x40, 0xb7, 0xd9, 0x32, 0x41, 0x7c, 0x82, 0x37, 0xa1, 0x2d, 0xdc, 0xd2, 0x54}}}, +{{{0x68, 0x2b, 0x4a, 0x5b, 0xd5, 0xc7, 0x51, 0x91, 0x1d, 0xe1, 0x2a, 0x4b, 0xc4, 0x47, 0xf1, 0xbc, 0x7a, 0xb3, 0xcb, 0xc8, 0xb6, 0x7c, 0xac, 0x90, 0x05, 0xfd, 0xf3, 0xf9, 0x52, 0x3a, 0x11, 0x6b}} , + {{0x3d, 0xc1, 0x27, 0xf3, 0x59, 0x43, 0x95, 0x90, 0xc5, 0x96, 0x79, 0xf5, 0xf4, 0x95, 0x65, 0x29, 0x06, 0x9c, 0x51, 0x05, 0x18, 0xda, 0xb8, 0x2e, 0x79, 0x7e, 0x69, 0x59, 0x71, 0x01, 0xeb, 0x1a}}}, +{{{0x15, 0x06, 0x49, 0xb6, 0x8a, 0x3c, 0xea, 0x2f, 0x34, 0x20, 0x14, 0xc3, 0xaa, 0xd6, 0xaf, 0x2c, 0x3e, 0xbd, 0x65, 0x20, 0xe2, 0x4d, 0x4b, 0x3b, 0xeb, 0x9f, 0x4a, 0xc3, 0xad, 0xa4, 0x3b, 0x60}} , + {{0xbc, 0x58, 0xe6, 0xc0, 0x95, 0x2a, 0x2a, 0x81, 0x9a, 0x7a, 0xf3, 0xd2, 0x06, 0xbe, 0x48, 0xbc, 0x0c, 0xc5, 0x46, 0xe0, 0x6a, 0xd4, 0xac, 0x0f, 0xd9, 0xcc, 0x82, 0x34, 0x2c, 0xaf, 0xdb, 0x1f}}}, +{{{0xf7, 0x17, 0x13, 0xbd, 0xfb, 0xbc, 0xd2, 0xec, 0x45, 0xb3, 0x15, 0x31, 0xe9, 0xaf, 0x82, 0x84, 0x3d, 0x28, 0xc6, 0xfc, 0x11, 0xf5, 0x41, 0xb5, 0x8b, 0xd3, 0x12, 0x76, 0x52, 0xe7, 0x1a, 0x3c}} , + {{0x4e, 0x36, 0x11, 0x07, 0xa2, 0x15, 0x20, 0x51, 0xc4, 0x2a, 0xc3, 0x62, 0x8b, 0x5e, 0x7f, 0xa6, 0x0f, 0xf9, 0x45, 0x85, 0x6c, 0x11, 0x86, 0xb7, 0x7e, 0xe5, 0xd7, 0xf9, 0xc3, 0x91, 0x1c, 0x05}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xea, 0xd6, 0xde, 0x29, 0x3a, 0x00, 0xb9, 0x02, 0x59, 0xcb, 0x26, 0xc4, 0xba, 0x99, 0xb1, 0x97, 0x2f, 0x8e, 0x00, 0x92, 0x26, 0x4f, 0x52, 0xeb, 0x47, 0x1b, 0x89, 0x8b, 0x24, 0xc0, 0x13, 0x7d}} , + {{0xd5, 0x20, 0x5b, 0x80, 0xa6, 0x80, 0x20, 0x95, 0xc3, 0xe9, 0x9f, 0x8e, 0x87, 0x9e, 0x1e, 0x9e, 0x7a, 0xc7, 0xcc, 0x75, 0x6c, 0xa5, 0xf1, 0x91, 0x1a, 0xa8, 0x01, 0x2c, 0xab, 0x76, 0xa9, 0x59}}}, +{{{0xde, 0xc9, 0xb1, 0x31, 0x10, 0x16, 0xaa, 0x35, 0x14, 0x6a, 0xd4, 0xb5, 0x34, 0x82, 0x71, 0xd2, 0x4a, 0x5d, 0x9a, 0x1f, 0x53, 0x26, 0x3c, 0xe5, 0x8e, 0x8d, 0x33, 0x7f, 0xff, 0xa9, 0xd5, 0x17}} , + {{0x89, 0xaf, 0xf6, 0xa4, 0x64, 0xd5, 0x10, 0xe0, 0x1d, 0xad, 0xef, 0x44, 0xbd, 0xda, 0x83, 0xac, 0x7a, 0xa8, 0xf0, 0x1c, 0x07, 0xf9, 0xc3, 0x43, 0x6c, 0x3f, 0xb7, 0xd3, 0x87, 0x22, 0x02, 0x73}}}, +{{{0x64, 0x1d, 0x49, 0x13, 0x2f, 0x71, 0xec, 0x69, 0x87, 0xd0, 0x42, 0xee, 0x13, 0xec, 0xe3, 0xed, 0x56, 0x7b, 0xbf, 0xbd, 0x8c, 0x2f, 0x7d, 0x7b, 0x9d, 0x28, 0xec, 0x8e, 0x76, 0x2f, 0x6f, 0x08}} , + {{0x22, 0xf5, 0x5f, 0x4d, 0x15, 0xef, 0xfc, 0x4e, 0x57, 0x03, 0x36, 0x89, 0xf0, 0xeb, 0x5b, 0x91, 0xd6, 0xe2, 0xca, 0x01, 0xa5, 0xee, 0x52, 0xec, 0xa0, 0x3c, 0x8f, 0x33, 0x90, 0x5a, 0x94, 0x72}}}, +{{{0x8a, 0x4b, 0xe7, 0x38, 0xbc, 0xda, 0xc2, 0xb0, 0x85, 0xe1, 0x4a, 0xfe, 0x2d, 0x44, 0x84, 0xcb, 0x20, 0x6b, 0x2d, 0xbf, 0x11, 0x9c, 0xd7, 0xbe, 0xd3, 0x3e, 0x5f, 0xbf, 0x68, 0xbc, 0xa8, 0x07}} , + {{0x01, 0x89, 0x28, 0x22, 0x6a, 0x78, 0xaa, 0x29, 0x03, 0xc8, 0x74, 0x95, 0x03, 0x3e, 0xdc, 0xbd, 0x07, 0x13, 0xa8, 0xa2, 0x20, 0x2d, 0xb3, 0x18, 0x70, 0x42, 0xfd, 0x7a, 0xc4, 0xd7, 0x49, 0x72}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x02, 0xff, 0x32, 0x2b, 0x5c, 0x93, 0x54, 0x32, 0xe8, 0x57, 0x54, 0x1a, 0x8b, 0x33, 0x60, 0x65, 0xd3, 0x67, 0xa4, 0xc1, 0x26, 0xc4, 0xa4, 0x34, 0x1f, 0x9b, 0xa7, 0xa9, 0xf4, 0xd9, 0x4f, 0x5b}} , + {{0x46, 0x8d, 0xb0, 0x33, 0x54, 0x26, 0x5b, 0x68, 0xdf, 0xbb, 0xc5, 0xec, 0xc2, 0xf9, 0x3c, 0x5a, 0x37, 0xc1, 0x8e, 0x27, 0x47, 0xaa, 0x49, 0x5a, 0xf8, 0xfb, 0x68, 0x04, 0x23, 0xd1, 0xeb, 0x40}}}, +{{{0x65, 0xa5, 0x11, 0x84, 0x8a, 0x67, 0x9d, 0x9e, 0xd1, 0x44, 0x68, 0x7a, 0x34, 0xe1, 0x9f, 0xa3, 0x54, 0xcd, 0x07, 0xca, 0x79, 0x1f, 0x54, 0x2f, 0x13, 0x70, 0x4e, 0xee, 0xa2, 0xfa, 0xe7, 0x5d}} , + {{0x36, 0xec, 0x54, 0xf8, 0xce, 0xe4, 0x85, 0xdf, 0xf6, 0x6f, 0x1d, 0x90, 0x08, 0xbc, 0xe8, 0xc0, 0x92, 0x2d, 0x43, 0x6b, 0x92, 0xa9, 0x8e, 0xab, 0x0a, 0x2e, 0x1c, 0x1e, 0x64, 0x23, 0x9f, 0x2c}}}, +{{{0xa7, 0xd6, 0x2e, 0xd5, 0xcc, 0xd4, 0xcb, 0x5a, 0x3b, 0xa7, 0xf9, 0x46, 0x03, 0x1d, 0xad, 0x2b, 0x34, 0x31, 0x90, 0x00, 0x46, 0x08, 0x82, 0x14, 0xc4, 0xe0, 0x9c, 0xf0, 0xe3, 0x55, 0x43, 0x31}} , + {{0x60, 0xd6, 0xdd, 0x78, 0xe6, 0xd4, 0x22, 0x42, 0x1f, 0x00, 0xf9, 0xb1, 0x6a, 0x63, 0xe2, 0x92, 0x59, 0xd1, 0x1a, 0xb7, 0x00, 0x54, 0x29, 0xc9, 0xc1, 0xf6, 0x6f, 0x7a, 0xc5, 0x3c, 0x5f, 0x65}}}, +{{{0x27, 0x4f, 0xd0, 0x72, 0xb1, 0x11, 0x14, 0x27, 0x15, 0x94, 0x48, 0x81, 0x7e, 0x74, 0xd8, 0x32, 0xd5, 0xd1, 0x11, 0x28, 0x60, 0x63, 0x36, 0x32, 0x37, 0xb5, 0x13, 0x1c, 0xa0, 0x37, 0xe3, 0x74}} , + {{0xf1, 0x25, 0x4e, 0x11, 0x96, 0x67, 0xe6, 0x1c, 0xc2, 0xb2, 0x53, 0xe2, 0xda, 0x85, 0xee, 0xb2, 0x9f, 0x59, 0xf3, 0xba, 0xbd, 0xfa, 0xcf, 0x6e, 0xf9, 0xda, 0xa4, 0xb3, 0x02, 0x8f, 0x64, 0x08}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x34, 0x94, 0xf2, 0x64, 0x54, 0x47, 0x37, 0x07, 0x40, 0x8a, 0x20, 0xba, 0x4a, 0x55, 0xd7, 0x3f, 0x47, 0xba, 0x25, 0x23, 0x14, 0xb0, 0x2c, 0xe8, 0x55, 0xa8, 0xa6, 0xef, 0x51, 0xbd, 0x6f, 0x6a}} , + {{0x71, 0xd6, 0x16, 0x76, 0xb2, 0x06, 0xea, 0x79, 0xf5, 0xc4, 0xc3, 0x52, 0x7e, 0x61, 0xd1, 0xe1, 0xad, 0x70, 0x78, 0x1d, 0x16, 0x11, 0xf8, 0x7c, 0x2b, 0xfc, 0x55, 0x9f, 0x52, 0xf8, 0xf5, 0x16}}}, +{{{0x34, 0x96, 0x9a, 0xf6, 0xc5, 0xe0, 0x14, 0x03, 0x24, 0x0e, 0x4c, 0xad, 0x9e, 0x9a, 0x70, 0x23, 0x96, 0xb2, 0xf1, 0x2e, 0x9d, 0xc3, 0x32, 0x9b, 0x54, 0xa5, 0x73, 0xde, 0x88, 0xb1, 0x3e, 0x24}} , + {{0xf6, 0xe2, 0x4c, 0x1f, 0x5b, 0xb2, 0xaf, 0x82, 0xa5, 0xcf, 0x81, 0x10, 0x04, 0xef, 0xdb, 0xa2, 0xcc, 0x24, 0xb2, 0x7e, 0x0b, 0x7a, 0xeb, 0x01, 0xd8, 0x52, 0xf4, 0x51, 0x89, 0x29, 0x79, 0x37}}}, +{{{0x74, 0xde, 0x12, 0xf3, 0x68, 0xb7, 0x66, 0xc3, 0xee, 0x68, 0xdc, 0x81, 0xb5, 0x55, 0x99, 0xab, 0xd9, 0x28, 0x63, 0x6d, 0x8b, 0x40, 0x69, 0x75, 0x6c, 0xcd, 0x5c, 0x2a, 0x7e, 0x32, 0x7b, 0x29}} , + {{0x02, 0xcc, 0x22, 0x74, 0x4d, 0x19, 0x07, 0xc0, 0xda, 0xb5, 0x76, 0x51, 0x2a, 0xaa, 0xa6, 0x0a, 0x5f, 0x26, 0xd4, 0xbc, 0xaf, 0x48, 0x88, 0x7f, 0x02, 0xbc, 0xf2, 0xe1, 0xcf, 0xe9, 0xdd, 0x15}}}, +{{{0xed, 0xb5, 0x9a, 0x8c, 0x9a, 0xdd, 0x27, 0xf4, 0x7f, 0x47, 0xd9, 0x52, 0xa7, 0xcd, 0x65, 0xa5, 0x31, 0x22, 0xed, 0xa6, 0x63, 0x5b, 0x80, 0x4a, 0xad, 0x4d, 0xed, 0xbf, 0xee, 0x49, 0xb3, 0x06}} , + {{0xf8, 0x64, 0x8b, 0x60, 0x90, 0xe9, 0xde, 0x44, 0x77, 0xb9, 0x07, 0x36, 0x32, 0xc2, 0x50, 0xf5, 0x65, 0xdf, 0x48, 0x4c, 0x37, 0xaa, 0x68, 0xab, 0x9a, 0x1f, 0x3e, 0xff, 0x89, 0x92, 0xa0, 0x07}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x7d, 0x4f, 0x9c, 0x19, 0xc0, 0x4a, 0x31, 0xec, 0xf9, 0xaa, 0xeb, 0xb2, 0x16, 0x9c, 0xa3, 0x66, 0x5f, 0xd1, 0xd4, 0xed, 0xb8, 0x92, 0x1c, 0xab, 0xda, 0xea, 0xd9, 0x57, 0xdf, 0x4c, 0x2a, 0x48}} , + {{0x4b, 0xb0, 0x4e, 0x6e, 0x11, 0x3b, 0x51, 0xbd, 0x6a, 0xfd, 0xe4, 0x25, 0xa5, 0x5f, 0x11, 0x3f, 0x98, 0x92, 0x51, 0x14, 0xc6, 0x5f, 0x3c, 0x0b, 0xa8, 0xf7, 0xc2, 0x81, 0x43, 0xde, 0x91, 0x73}}}, +{{{0x3c, 0x8f, 0x9f, 0x33, 0x2a, 0x1f, 0x43, 0x33, 0x8f, 0x68, 0xff, 0x1f, 0x3d, 0x73, 0x6b, 0xbf, 0x68, 0xcc, 0x7d, 0x13, 0x6c, 0x24, 0x4b, 0xcc, 0x4d, 0x24, 0x0d, 0xfe, 0xde, 0x86, 0xad, 0x3b}} , + {{0x79, 0x51, 0x81, 0x01, 0xdc, 0x73, 0x53, 0xe0, 0x6e, 0x9b, 0xea, 0x68, 0x3f, 0x5c, 0x14, 0x84, 0x53, 0x8d, 0x4b, 0xc0, 0x9f, 0x9f, 0x89, 0x2b, 0x8c, 0xba, 0x86, 0xfa, 0xf2, 0xcd, 0xe3, 0x2d}}}, +{{{0x06, 0xf9, 0x29, 0x5a, 0xdb, 0x3d, 0x84, 0x52, 0xab, 0xcc, 0x6b, 0x60, 0x9d, 0xb7, 0x4a, 0x0e, 0x36, 0x63, 0x91, 0xad, 0xa0, 0x95, 0xb0, 0x97, 0x89, 0x4e, 0xcf, 0x7d, 0x3c, 0xe5, 0x7c, 0x28}} , + {{0x2e, 0x69, 0x98, 0xfd, 0xc6, 0xbd, 0xcc, 0xca, 0xdf, 0x9a, 0x44, 0x7e, 0x9d, 0xca, 0x89, 0x6d, 0xbf, 0x27, 0xc2, 0xf8, 0xcd, 0x46, 0x00, 0x2b, 0xb5, 0x58, 0x4e, 0xb7, 0x89, 0x09, 0xe9, 0x2d}}}, +{{{0x54, 0xbe, 0x75, 0xcb, 0x05, 0xb0, 0x54, 0xb7, 0xe7, 0x26, 0x86, 0x4a, 0xfc, 0x19, 0xcf, 0x27, 0x46, 0xd4, 0x22, 0x96, 0x5a, 0x11, 0xe8, 0xd5, 0x1b, 0xed, 0x71, 0xc5, 0x5d, 0xc8, 0xaf, 0x45}} , + {{0x40, 0x7b, 0x77, 0x57, 0x49, 0x9e, 0x80, 0x39, 0x23, 0xee, 0x81, 0x0b, 0x22, 0xcf, 0xdb, 0x7a, 0x2f, 0x14, 0xb8, 0x57, 0x8f, 0xa1, 0x39, 0x1e, 0x77, 0xfc, 0x0b, 0xa6, 0xbf, 0x8a, 0x0c, 0x6c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x77, 0x3a, 0xd4, 0xd8, 0x27, 0xcf, 0xe8, 0xa1, 0x72, 0x9d, 0xca, 0xdd, 0x0d, 0x96, 0xda, 0x79, 0xed, 0x56, 0x42, 0x15, 0x60, 0xc7, 0x1c, 0x6b, 0x26, 0x30, 0xf6, 0x6a, 0x95, 0x67, 0xf3, 0x0a}} , + {{0xc5, 0x08, 0xa4, 0x2b, 0x2f, 0xbd, 0x31, 0x81, 0x2a, 0xa6, 0xb6, 0xe4, 0x00, 0x91, 0xda, 0x3d, 0xb2, 0xb0, 0x96, 0xce, 0x8a, 0xd2, 0x8d, 0x70, 0xb3, 0xd3, 0x34, 0x01, 0x90, 0x8d, 0x10, 0x21}}}, +{{{0x33, 0x0d, 0xe7, 0xba, 0x4f, 0x07, 0xdf, 0x8d, 0xea, 0x7d, 0xa0, 0xc5, 0xd6, 0xb1, 0xb0, 0xe5, 0x57, 0x1b, 0x5b, 0xf5, 0x45, 0x13, 0x14, 0x64, 0x5a, 0xeb, 0x5c, 0xfc, 0x54, 0x01, 0x76, 0x2b}} , + {{0x02, 0x0c, 0xc2, 0xaf, 0x96, 0x36, 0xfe, 0x4a, 0xe2, 0x54, 0x20, 0x6a, 0xeb, 0xb2, 0x9f, 0x62, 0xd7, 0xce, 0xa2, 0x3f, 0x20, 0x11, 0x34, 0x37, 0xe0, 0x42, 0xed, 0x6f, 0xf9, 0x1a, 0xc8, 0x7d}}}, +{{{0xd8, 0xb9, 0x11, 0xe8, 0x36, 0x3f, 0x42, 0xc1, 0xca, 0xdc, 0xd3, 0xf1, 0xc8, 0x23, 0x3d, 0x4f, 0x51, 0x7b, 0x9d, 0x8d, 0xd8, 0xe4, 0xa0, 0xaa, 0xf3, 0x04, 0xd6, 0x11, 0x93, 0xc8, 0x35, 0x45}} , + {{0x61, 0x36, 0xd6, 0x08, 0x90, 0xbf, 0xa7, 0x7a, 0x97, 0x6c, 0x0f, 0x84, 0xd5, 0x33, 0x2d, 0x37, 0xc9, 0x6a, 0x80, 0x90, 0x3d, 0x0a, 0xa2, 0xaa, 0xe1, 0xb8, 0x84, 0xba, 0x61, 0x36, 0xdd, 0x69}}}, +{{{0x6b, 0xdb, 0x5b, 0x9c, 0xc6, 0x92, 0xbc, 0x23, 0xaf, 0xc5, 0xb8, 0x75, 0xf8, 0x42, 0xfa, 0xd6, 0xb6, 0x84, 0x94, 0x63, 0x98, 0x93, 0x48, 0x78, 0x38, 0xcd, 0xbb, 0x18, 0x34, 0xc3, 0xdb, 0x67}} , + {{0x96, 0xf3, 0x3a, 0x09, 0x56, 0xb0, 0x6f, 0x7c, 0x51, 0x1e, 0x1b, 0x39, 0x48, 0xea, 0xc9, 0x0c, 0x25, 0xa2, 0x7a, 0xca, 0xe7, 0x92, 0xfc, 0x59, 0x30, 0xa3, 0x89, 0x85, 0xdf, 0x6f, 0x43, 0x38}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x79, 0x84, 0x44, 0x19, 0xbd, 0xe9, 0x54, 0xc4, 0xc0, 0x6e, 0x2a, 0xa8, 0xa8, 0x9b, 0x43, 0xd5, 0x71, 0x22, 0x5f, 0xdc, 0x01, 0xfa, 0xdf, 0xb3, 0xb8, 0x47, 0x4b, 0x0a, 0xa5, 0x44, 0xea, 0x29}} , + {{0x05, 0x90, 0x50, 0xaf, 0x63, 0x5f, 0x9d, 0x9e, 0xe1, 0x9d, 0x38, 0x97, 0x1f, 0x6c, 0xac, 0x30, 0x46, 0xb2, 0x6a, 0x19, 0xd1, 0x4b, 0xdb, 0xbb, 0x8c, 0xda, 0x2e, 0xab, 0xc8, 0x5a, 0x77, 0x6c}}}, +{{{0x2b, 0xbe, 0xaf, 0xa1, 0x6d, 0x2f, 0x0b, 0xb1, 0x8f, 0xe3, 0xe0, 0x38, 0xcd, 0x0b, 0x41, 0x1b, 0x4a, 0x15, 0x07, 0xf3, 0x6f, 0xdc, 0xb8, 0xe9, 0xde, 0xb2, 0xa3, 0x40, 0x01, 0xa6, 0x45, 0x1e}} , + {{0x76, 0x0a, 0xda, 0x8d, 0x2c, 0x07, 0x3f, 0x89, 0x7d, 0x04, 0xad, 0x43, 0x50, 0x6e, 0xd2, 0x47, 0xcb, 0x8a, 0xe6, 0x85, 0x1a, 0x24, 0xf3, 0xd2, 0x60, 0xfd, 0xdf, 0x73, 0xa4, 0x0d, 0x73, 0x0e}}}, +{{{0xfd, 0x67, 0x6b, 0x71, 0x9b, 0x81, 0x53, 0x39, 0x39, 0xf4, 0xb8, 0xd5, 0xc3, 0x30, 0x9b, 0x3b, 0x7c, 0xa3, 0xf0, 0xd0, 0x84, 0x21, 0xd6, 0xbf, 0xb7, 0x4c, 0x87, 0x13, 0x45, 0x2d, 0xa7, 0x55}} , + {{0x5d, 0x04, 0xb3, 0x40, 0x28, 0x95, 0x2d, 0x30, 0x83, 0xec, 0x5e, 0xe4, 0xff, 0x75, 0xfe, 0x79, 0x26, 0x9d, 0x1d, 0x36, 0xcd, 0x0a, 0x15, 0xd2, 0x24, 0x14, 0x77, 0x71, 0xd7, 0x8a, 0x1b, 0x04}}}, +{{{0x5d, 0x93, 0xc9, 0xbe, 0xaa, 0x90, 0xcd, 0x9b, 0xfb, 0x73, 0x7e, 0xb0, 0x64, 0x98, 0x57, 0x44, 0x42, 0x41, 0xb1, 0xaf, 0xea, 0xc1, 0xc3, 0x22, 0xff, 0x60, 0x46, 0xcb, 0x61, 0x81, 0x70, 0x61}} , + {{0x0d, 0x82, 0xb9, 0xfe, 0x21, 0xcd, 0xc4, 0xf5, 0x98, 0x0c, 0x4e, 0x72, 0xee, 0x87, 0x49, 0xf8, 0xa1, 0x95, 0xdf, 0x8f, 0x2d, 0xbd, 0x21, 0x06, 0x7c, 0x15, 0xe8, 0x12, 0x6d, 0x93, 0xd6, 0x38}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x91, 0xf7, 0x51, 0xd9, 0xef, 0x7d, 0x42, 0x01, 0x13, 0xe9, 0xb8, 0x7f, 0xa6, 0x49, 0x17, 0x64, 0x21, 0x80, 0x83, 0x2c, 0x63, 0x4c, 0x60, 0x09, 0x59, 0x91, 0x92, 0x77, 0x39, 0x51, 0xf4, 0x48}} , + {{0x60, 0xd5, 0x22, 0x83, 0x08, 0x2f, 0xff, 0x99, 0x3e, 0x69, 0x6d, 0x88, 0xda, 0xe7, 0x5b, 0x52, 0x26, 0x31, 0x2a, 0xe5, 0x89, 0xde, 0x68, 0x90, 0xb6, 0x22, 0x5a, 0xbd, 0xd3, 0x85, 0x53, 0x31}}}, +{{{0xd8, 0xce, 0xdc, 0xf9, 0x3c, 0x4b, 0xa2, 0x1d, 0x2c, 0x2f, 0x36, 0xbe, 0x7a, 0xfc, 0xcd, 0xbc, 0xdc, 0xf9, 0x30, 0xbd, 0xff, 0x05, 0xc7, 0xe4, 0x8e, 0x17, 0x62, 0xf8, 0x4d, 0xa0, 0x56, 0x79}} , + {{0x82, 0xe7, 0xf6, 0xba, 0x53, 0x84, 0x0a, 0xa3, 0x34, 0xff, 0x3c, 0xa3, 0x6a, 0xa1, 0x37, 0xea, 0xdd, 0xb6, 0x95, 0xb3, 0x78, 0x19, 0x76, 0x1e, 0x55, 0x2f, 0x77, 0x2e, 0x7f, 0xc1, 0xea, 0x5e}}}, +{{{0x83, 0xe1, 0x6e, 0xa9, 0x07, 0x33, 0x3e, 0x83, 0xff, 0xcb, 0x1c, 0x9f, 0xb1, 0xa3, 0xb4, 0xc9, 0xe1, 0x07, 0x97, 0xff, 0xf8, 0x23, 0x8f, 0xce, 0x40, 0xfd, 0x2e, 0x5e, 0xdb, 0x16, 0x43, 0x2d}} , + {{0xba, 0x38, 0x02, 0xf7, 0x81, 0x43, 0x83, 0xa3, 0x20, 0x4f, 0x01, 0x3b, 0x8a, 0x04, 0x38, 0x31, 0xc6, 0x0f, 0xc8, 0xdf, 0xd7, 0xfa, 0x2f, 0x88, 0x3f, 0xfc, 0x0c, 0x76, 0xc4, 0xa6, 0x45, 0x72}}}, +{{{0xbb, 0x0c, 0xbc, 0x6a, 0xa4, 0x97, 0x17, 0x93, 0x2d, 0x6f, 0xde, 0x72, 0x10, 0x1c, 0x08, 0x2c, 0x0f, 0x80, 0x32, 0x68, 0x27, 0xd4, 0xab, 0xdd, 0xc5, 0x58, 0x61, 0x13, 0x6d, 0x11, 0x1e, 0x4d}} , + {{0x1a, 0xb9, 0xc9, 0x10, 0xfb, 0x1e, 0x4e, 0xf4, 0x84, 0x4b, 0x8a, 0x5e, 0x7b, 0x4b, 0xe8, 0x43, 0x8c, 0x8f, 0x00, 0xb5, 0x54, 0x13, 0xc5, 0x5c, 0xb6, 0x35, 0x4e, 0x9d, 0xe4, 0x5b, 0x41, 0x6d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x15, 0x7d, 0x12, 0x48, 0x82, 0x14, 0x42, 0xcd, 0x32, 0xd4, 0x4b, 0xc1, 0x72, 0x61, 0x2a, 0x8c, 0xec, 0xe2, 0xf8, 0x24, 0x45, 0x94, 0xe3, 0xbe, 0xdd, 0x67, 0xa8, 0x77, 0x5a, 0xae, 0x5b, 0x4b}} , + {{0xcb, 0x77, 0x9a, 0x20, 0xde, 0xb8, 0x23, 0xd9, 0xa0, 0x0f, 0x8c, 0x7b, 0xa5, 0xcb, 0xae, 0xb6, 0xec, 0x42, 0x67, 0x0e, 0x58, 0xa4, 0x75, 0x98, 0x21, 0x71, 0x84, 0xb3, 0xe0, 0x76, 0x94, 0x73}}}, +{{{0xdf, 0xfc, 0x69, 0x28, 0x23, 0x3f, 0x5b, 0xf8, 0x3b, 0x24, 0x37, 0xf3, 0x1d, 0xd5, 0x22, 0x6b, 0xd0, 0x98, 0xa8, 0x6c, 0xcf, 0xff, 0x06, 0xe1, 0x13, 0xdf, 0xb9, 0xc1, 0x0c, 0xa9, 0xbf, 0x33}} , + {{0xd9, 0x81, 0xda, 0xb2, 0x4f, 0x82, 0x9d, 0x43, 0x81, 0x09, 0xf1, 0xd2, 0x01, 0xef, 0xac, 0xf4, 0x2d, 0x7d, 0x01, 0x09, 0xf1, 0xff, 0xa5, 0x9f, 0xe5, 0xca, 0x27, 0x63, 0xdb, 0x20, 0xb1, 0x53}}}, +{{{0x67, 0x02, 0xe8, 0xad, 0xa9, 0x34, 0xd4, 0xf0, 0x15, 0x81, 0xaa, 0xc7, 0x4d, 0x87, 0x94, 0xea, 0x75, 0xe7, 0x4c, 0x94, 0x04, 0x0e, 0x69, 0x87, 0xe7, 0x51, 0x91, 0x10, 0x03, 0xc7, 0xbe, 0x56}} , + {{0x32, 0xfb, 0x86, 0xec, 0x33, 0x6b, 0x2e, 0x51, 0x2b, 0xc8, 0xfa, 0x6c, 0x70, 0x47, 0x7e, 0xce, 0x05, 0x0c, 0x71, 0xf3, 0xb4, 0x56, 0xa6, 0xdc, 0xcc, 0x78, 0x07, 0x75, 0xd0, 0xdd, 0xb2, 0x6a}}}, +{{{0xc6, 0xef, 0xb9, 0xc0, 0x2b, 0x22, 0x08, 0x1e, 0x71, 0x70, 0xb3, 0x35, 0x9c, 0x7a, 0x01, 0x92, 0x44, 0x9a, 0xf6, 0xb0, 0x58, 0x95, 0xc1, 0x9b, 0x02, 0xed, 0x2d, 0x7c, 0x34, 0x29, 0x49, 0x44}} , + {{0x45, 0x62, 0x1d, 0x2e, 0xff, 0x2a, 0x1c, 0x21, 0xa4, 0x25, 0x7b, 0x0d, 0x8c, 0x15, 0x39, 0xfc, 0x8f, 0x7c, 0xa5, 0x7d, 0x1e, 0x25, 0xa3, 0x45, 0xd6, 0xab, 0xbd, 0xcb, 0xc5, 0x5e, 0x78, 0x77}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xd0, 0xd3, 0x42, 0xed, 0x1d, 0x00, 0x3c, 0x15, 0x2c, 0x9c, 0x77, 0x81, 0xd2, 0x73, 0xd1, 0x06, 0xd5, 0xc4, 0x7f, 0x94, 0xbb, 0x92, 0x2d, 0x2c, 0x4b, 0x45, 0x4b, 0xe9, 0x2a, 0x89, 0x6b, 0x2b}} , + {{0xd2, 0x0c, 0x88, 0xc5, 0x48, 0x4d, 0xea, 0x0d, 0x4a, 0xc9, 0x52, 0x6a, 0x61, 0x79, 0xe9, 0x76, 0xf3, 0x85, 0x52, 0x5c, 0x1b, 0x2c, 0xe1, 0xd6, 0xc4, 0x0f, 0x18, 0x0e, 0x4e, 0xf6, 0x1c, 0x7f}}}, +{{{0xb4, 0x04, 0x2e, 0x42, 0xcb, 0x1f, 0x2b, 0x11, 0x51, 0x7b, 0x08, 0xac, 0xaa, 0x3e, 0x9e, 0x52, 0x60, 0xb7, 0xc2, 0x61, 0x57, 0x8c, 0x84, 0xd5, 0x18, 0xa6, 0x19, 0xfc, 0xb7, 0x75, 0x91, 0x1b}} , + {{0xe8, 0x68, 0xca, 0x44, 0xc8, 0x38, 0x38, 0xcc, 0x53, 0x0a, 0x32, 0x35, 0xcc, 0x52, 0xcb, 0x0e, 0xf7, 0xc5, 0xe7, 0xec, 0x3d, 0x85, 0xcc, 0x58, 0xe2, 0x17, 0x47, 0xff, 0x9f, 0xa5, 0x30, 0x17}}}, +{{{0xe3, 0xae, 0xc8, 0xc1, 0x71, 0x75, 0x31, 0x00, 0x37, 0x41, 0x5c, 0x0e, 0x39, 0xda, 0x73, 0xa0, 0xc7, 0x97, 0x36, 0x6c, 0x5b, 0xf2, 0xee, 0x64, 0x0a, 0x3d, 0x89, 0x1e, 0x1d, 0x49, 0x8c, 0x37}} , + {{0x4c, 0xe6, 0xb0, 0xc1, 0xa5, 0x2a, 0x82, 0x09, 0x08, 0xad, 0x79, 0x9c, 0x56, 0xf6, 0xf9, 0xc1, 0xd7, 0x7c, 0x39, 0x7f, 0x93, 0xca, 0x11, 0x55, 0xbf, 0x07, 0x1b, 0x82, 0x29, 0x69, 0x95, 0x5c}}}, +{{{0x87, 0xee, 0xa6, 0x56, 0x9e, 0xc2, 0x9a, 0x56, 0x24, 0x42, 0x85, 0x4d, 0x98, 0x31, 0x1e, 0x60, 0x4d, 0x87, 0x85, 0x04, 0xae, 0x46, 0x12, 0xf9, 0x8e, 0x7f, 0xe4, 0x7f, 0xf6, 0x1c, 0x37, 0x01}} , + {{0x73, 0x4c, 0xb6, 0xc5, 0xc4, 0xe9, 0x6c, 0x85, 0x48, 0x4a, 0x5a, 0xac, 0xd9, 0x1f, 0x43, 0xf8, 0x62, 0x5b, 0xee, 0x98, 0x2a, 0x33, 0x8e, 0x79, 0xce, 0x61, 0x06, 0x35, 0xd8, 0xd7, 0xca, 0x71}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x72, 0xd3, 0xae, 0xa6, 0xca, 0x8f, 0xcd, 0xcc, 0x78, 0x8e, 0x19, 0x4d, 0xa7, 0xd2, 0x27, 0xe9, 0xa4, 0x3c, 0x16, 0x5b, 0x84, 0x80, 0xf9, 0xd0, 0xcc, 0x6a, 0x1e, 0xca, 0x1e, 0x67, 0xbd, 0x63}} , + {{0x7b, 0x6e, 0x2a, 0xd2, 0x87, 0x48, 0xff, 0xa1, 0xca, 0xe9, 0x15, 0x85, 0xdc, 0xdb, 0x2c, 0x39, 0x12, 0x91, 0xa9, 0x20, 0xaa, 0x4f, 0x29, 0xf4, 0x15, 0x7a, 0xd2, 0xf5, 0x32, 0xcc, 0x60, 0x04}}}, +{{{0xe5, 0x10, 0x47, 0x3b, 0xfa, 0x90, 0xfc, 0x30, 0xb5, 0xea, 0x6f, 0x56, 0x8f, 0xfb, 0x0e, 0xa7, 0x3b, 0xc8, 0xb2, 0xff, 0x02, 0x7a, 0x33, 0x94, 0x93, 0x2a, 0x03, 0xe0, 0x96, 0x3a, 0x6c, 0x0f}} , + {{0x5a, 0x63, 0x67, 0xe1, 0x9b, 0x47, 0x78, 0x9f, 0x38, 0x79, 0xac, 0x97, 0x66, 0x1d, 0x5e, 0x51, 0xee, 0x24, 0x42, 0xe8, 0x58, 0x4b, 0x8a, 0x03, 0x75, 0x86, 0x37, 0x86, 0xe2, 0x97, 0x4e, 0x3d}}}, +{{{0x3f, 0x75, 0x8e, 0xb4, 0xff, 0xd8, 0xdd, 0xd6, 0x37, 0x57, 0x9d, 0x6d, 0x3b, 0xbd, 0xd5, 0x60, 0x88, 0x65, 0x9a, 0xb9, 0x4a, 0x68, 0x84, 0xa2, 0x67, 0xdd, 0x17, 0x25, 0x97, 0x04, 0x8b, 0x5e}} , + {{0xbb, 0x40, 0x5e, 0xbc, 0x16, 0x92, 0x05, 0xc4, 0xc0, 0x4e, 0x72, 0x90, 0x0e, 0xab, 0xcf, 0x8a, 0xed, 0xef, 0xb9, 0x2d, 0x3b, 0xf8, 0x43, 0x5b, 0xba, 0x2d, 0xeb, 0x2f, 0x52, 0xd2, 0xd1, 0x5a}}}, +{{{0x40, 0xb4, 0xab, 0xe6, 0xad, 0x9f, 0x46, 0x69, 0x4a, 0xb3, 0x8e, 0xaa, 0xea, 0x9c, 0x8a, 0x20, 0x16, 0x5d, 0x8c, 0x13, 0xbd, 0xf6, 0x1d, 0xc5, 0x24, 0xbd, 0x90, 0x2a, 0x1c, 0xc7, 0x13, 0x3b}} , + {{0x54, 0xdc, 0x16, 0x0d, 0x18, 0xbe, 0x35, 0x64, 0x61, 0x52, 0x02, 0x80, 0xaf, 0x05, 0xf7, 0xa6, 0x42, 0xd3, 0x8f, 0x2e, 0x79, 0x26, 0xa8, 0xbb, 0xb2, 0x17, 0x48, 0xb2, 0x7a, 0x0a, 0x89, 0x14}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x20, 0xa8, 0x88, 0xe3, 0x91, 0xc0, 0x6e, 0xbb, 0x8a, 0x27, 0x82, 0x51, 0x83, 0xb2, 0x28, 0xa9, 0x83, 0xeb, 0xa6, 0xa9, 0x4d, 0x17, 0x59, 0x22, 0x54, 0x00, 0x50, 0x45, 0xcb, 0x48, 0x4b, 0x18}} , + {{0x33, 0x7c, 0xe7, 0x26, 0xba, 0x4d, 0x32, 0xfe, 0x53, 0xf4, 0xfa, 0x83, 0xe3, 0xa5, 0x79, 0x66, 0x73, 0xef, 0x80, 0x23, 0x68, 0xc2, 0x60, 0xdd, 0xa9, 0x33, 0xdc, 0x03, 0x7a, 0xe0, 0xe0, 0x3e}}}, +{{{0x34, 0x5c, 0x13, 0xfb, 0xc0, 0xe3, 0x78, 0x2b, 0x54, 0x58, 0x22, 0x9b, 0x76, 0x81, 0x7f, 0x93, 0x9c, 0x25, 0x3c, 0xd2, 0xe9, 0x96, 0x21, 0x26, 0x08, 0xf5, 0xed, 0x95, 0x11, 0xae, 0x04, 0x5a}} , + {{0xb9, 0xe8, 0xc5, 0x12, 0x97, 0x1f, 0x83, 0xfe, 0x3e, 0x94, 0x99, 0xd4, 0x2d, 0xf9, 0x52, 0x59, 0x5c, 0x82, 0xa6, 0xf0, 0x75, 0x7e, 0xe8, 0xec, 0xcc, 0xac, 0x18, 0x21, 0x09, 0x67, 0x66, 0x67}}}, +{{{0xb3, 0x40, 0x29, 0xd1, 0xcb, 0x1b, 0x08, 0x9e, 0x9c, 0xb7, 0x53, 0xb9, 0x3b, 0x71, 0x08, 0x95, 0x12, 0x1a, 0x58, 0xaf, 0x7e, 0x82, 0x52, 0x43, 0x4f, 0x11, 0x39, 0xf4, 0x93, 0x1a, 0x26, 0x05}} , + {{0x6e, 0x44, 0xa3, 0xf9, 0x64, 0xaf, 0xe7, 0x6d, 0x7d, 0xdf, 0x1e, 0xac, 0x04, 0xea, 0x3b, 0x5f, 0x9b, 0xe8, 0x24, 0x9d, 0x0e, 0xe5, 0x2e, 0x3e, 0xdf, 0xa9, 0xf7, 0xd4, 0x50, 0x71, 0xf0, 0x78}}}, +{{{0x3e, 0xa8, 0x38, 0xc2, 0x57, 0x56, 0x42, 0x9a, 0xb1, 0xe2, 0xf8, 0x45, 0xaa, 0x11, 0x48, 0x5f, 0x17, 0xc4, 0x54, 0x27, 0xdc, 0x5d, 0xaa, 0xdd, 0x41, 0xbc, 0xdf, 0x81, 0xb9, 0x53, 0xee, 0x52}} , + {{0xc3, 0xf1, 0xa7, 0x6d, 0xb3, 0x5f, 0x92, 0x6f, 0xcc, 0x91, 0xb8, 0x95, 0x05, 0xdf, 0x3c, 0x64, 0x57, 0x39, 0x61, 0x51, 0xad, 0x8c, 0x38, 0x7b, 0xc8, 0xde, 0x00, 0x34, 0xbe, 0xa1, 0xb0, 0x7e}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x25, 0x24, 0x1d, 0x8a, 0x67, 0x20, 0xee, 0x42, 0xeb, 0x38, 0xed, 0x0b, 0x8b, 0xcd, 0x46, 0x9d, 0x5e, 0x6b, 0x1e, 0x24, 0x9d, 0x12, 0x05, 0x1a, 0xcc, 0x05, 0x4e, 0x92, 0x38, 0xe1, 0x1f, 0x50}} , + {{0x4e, 0xee, 0x1c, 0x91, 0xe6, 0x11, 0xbd, 0x8e, 0x55, 0x1a, 0x18, 0x75, 0x66, 0xaf, 0x4d, 0x7b, 0x0f, 0xae, 0x6d, 0x85, 0xca, 0x82, 0x58, 0x21, 0x9c, 0x18, 0xe0, 0xed, 0xec, 0x22, 0x80, 0x2f}}}, +{{{0x68, 0x3b, 0x0a, 0x39, 0x1d, 0x6a, 0x15, 0x57, 0xfc, 0xf0, 0x63, 0x54, 0xdb, 0x39, 0xdb, 0xe8, 0x5c, 0x64, 0xff, 0xa0, 0x09, 0x4f, 0x3b, 0xb7, 0x32, 0x60, 0x99, 0x94, 0xfd, 0x94, 0x82, 0x2d}} , + {{0x24, 0xf6, 0x5a, 0x44, 0xf1, 0x55, 0x2c, 0xdb, 0xea, 0x7c, 0x84, 0x7c, 0x01, 0xac, 0xe3, 0xfd, 0xc9, 0x27, 0xc1, 0x5a, 0xb9, 0xde, 0x4f, 0x5a, 0x90, 0xdd, 0xc6, 0x67, 0xaa, 0x6f, 0x8a, 0x3a}}}, +{{{0x78, 0x52, 0x87, 0xc9, 0x97, 0x63, 0xb1, 0xdd, 0x54, 0x5f, 0xc1, 0xf8, 0xf1, 0x06, 0xa6, 0xa8, 0xa3, 0x88, 0x82, 0xd4, 0xcb, 0xa6, 0x19, 0xdd, 0xd1, 0x11, 0x87, 0x08, 0x17, 0x4c, 0x37, 0x2a}} , + {{0xa1, 0x0c, 0xf3, 0x08, 0x43, 0xd9, 0x24, 0x1e, 0x83, 0xa7, 0xdf, 0x91, 0xca, 0xbd, 0x69, 0x47, 0x8d, 0x1b, 0xe2, 0xb9, 0x4e, 0xb5, 0xe1, 0x76, 0xb3, 0x1c, 0x93, 0x03, 0xce, 0x5f, 0xb3, 0x5a}}}, +{{{0x1d, 0xda, 0xe4, 0x61, 0x03, 0x50, 0xa9, 0x8b, 0x68, 0x18, 0xef, 0xb2, 0x1c, 0x84, 0x3b, 0xa2, 0x44, 0x95, 0xa3, 0x04, 0x3b, 0xd6, 0x99, 0x00, 0xaf, 0x76, 0x42, 0x67, 0x02, 0x7d, 0x85, 0x56}} , + {{0xce, 0x72, 0x0e, 0x29, 0x84, 0xb2, 0x7d, 0xd2, 0x45, 0xbe, 0x57, 0x06, 0xed, 0x7f, 0xcf, 0xed, 0xcd, 0xef, 0x19, 0xd6, 0xbc, 0x15, 0x79, 0x64, 0xd2, 0x18, 0xe3, 0x20, 0x67, 0x3a, 0x54, 0x0b}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x52, 0xfd, 0x04, 0xc5, 0xfb, 0x99, 0xe7, 0xe8, 0xfb, 0x8c, 0xe1, 0x42, 0x03, 0xef, 0x9d, 0xd9, 0x9e, 0x4d, 0xf7, 0x80, 0xcf, 0x2e, 0xcc, 0x9b, 0x45, 0xc9, 0x7b, 0x7a, 0xbc, 0x37, 0xa8, 0x52}} , + {{0x96, 0x11, 0x41, 0x8a, 0x47, 0x91, 0xfe, 0xb6, 0xda, 0x7a, 0x54, 0x63, 0xd1, 0x14, 0x35, 0x05, 0x86, 0x8c, 0xa9, 0x36, 0x3f, 0xf2, 0x85, 0x54, 0x4e, 0x92, 0xd8, 0x85, 0x01, 0x46, 0xd6, 0x50}}}, +{{{0x53, 0xcd, 0xf3, 0x86, 0x40, 0xe6, 0x39, 0x42, 0x95, 0xd6, 0xcb, 0x45, 0x1a, 0x20, 0xc8, 0x45, 0x4b, 0x32, 0x69, 0x04, 0xb1, 0xaf, 0x20, 0x46, 0xc7, 0x6b, 0x23, 0x5b, 0x69, 0xee, 0x30, 0x3f}} , + {{0x70, 0x83, 0x47, 0xc0, 0xdb, 0x55, 0x08, 0xa8, 0x7b, 0x18, 0x6d, 0xf5, 0x04, 0x5a, 0x20, 0x0c, 0x4a, 0x8c, 0x60, 0xae, 0xae, 0x0f, 0x64, 0x55, 0x55, 0x2e, 0xd5, 0x1d, 0x53, 0x31, 0x42, 0x41}}}, +{{{0xca, 0xfc, 0x88, 0x6b, 0x96, 0x78, 0x0a, 0x8b, 0x83, 0xdc, 0xbc, 0xaf, 0x40, 0xb6, 0x8d, 0x7f, 0xef, 0xb4, 0xd1, 0x3f, 0xcc, 0xa2, 0x74, 0xc9, 0xc2, 0x92, 0x55, 0x00, 0xab, 0xdb, 0xbf, 0x4f}} , + {{0x93, 0x1c, 0x06, 0x2d, 0x66, 0x65, 0x02, 0xa4, 0x97, 0x18, 0xfd, 0x00, 0xe7, 0xab, 0x03, 0xec, 0xce, 0xc1, 0xbf, 0x37, 0xf8, 0x13, 0x53, 0xa5, 0xe5, 0x0c, 0x3a, 0xa8, 0x55, 0xb9, 0xff, 0x68}}}, +{{{0xe4, 0xe6, 0x6d, 0x30, 0x7d, 0x30, 0x35, 0xc2, 0x78, 0x87, 0xf9, 0xfc, 0x6b, 0x5a, 0xc3, 0xb7, 0x65, 0xd8, 0x2e, 0xc7, 0xa5, 0x0c, 0xc6, 0xdc, 0x12, 0xaa, 0xd6, 0x4f, 0xc5, 0x38, 0xbc, 0x0e}} , + {{0xe2, 0x3c, 0x76, 0x86, 0x38, 0xf2, 0x7b, 0x2c, 0x16, 0x78, 0x8d, 0xf5, 0xa4, 0x15, 0xda, 0xdb, 0x26, 0x85, 0xa0, 0x56, 0xdd, 0x1d, 0xe3, 0xb3, 0xfd, 0x40, 0xef, 0xf2, 0xd9, 0xa1, 0xb3, 0x04}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xdb, 0x49, 0x0e, 0xe6, 0x58, 0x10, 0x7a, 0x52, 0xda, 0xb5, 0x7d, 0x37, 0x6a, 0x3e, 0xa1, 0x78, 0xce, 0xc7, 0x1c, 0x24, 0x23, 0xdb, 0x7d, 0xfb, 0x8c, 0x8d, 0xdc, 0x30, 0x67, 0x69, 0x75, 0x3b}} , + {{0xa9, 0xea, 0x6d, 0x16, 0x16, 0x60, 0xf4, 0x60, 0x87, 0x19, 0x44, 0x8c, 0x4a, 0x8b, 0x3e, 0xfb, 0x16, 0x00, 0x00, 0x54, 0xa6, 0x9e, 0x9f, 0xef, 0xcf, 0xd9, 0xd2, 0x4c, 0x74, 0x31, 0xd0, 0x34}}}, +{{{0xa4, 0xeb, 0x04, 0xa4, 0x8c, 0x8f, 0x71, 0x27, 0x95, 0x85, 0x5d, 0x55, 0x4b, 0xb1, 0x26, 0x26, 0xc8, 0xae, 0x6a, 0x7d, 0xa2, 0x21, 0xca, 0xce, 0x38, 0xab, 0x0f, 0xd0, 0xd5, 0x2b, 0x6b, 0x00}} , + {{0xe5, 0x67, 0x0c, 0xf1, 0x3a, 0x9a, 0xea, 0x09, 0x39, 0xef, 0xd1, 0x30, 0xbc, 0x33, 0xba, 0xb1, 0x6a, 0xc5, 0x27, 0x08, 0x7f, 0x54, 0x80, 0x3d, 0xab, 0xf6, 0x15, 0x7a, 0xc2, 0x40, 0x73, 0x72}}}, +{{{0x84, 0x56, 0x82, 0xb6, 0x12, 0x70, 0x7f, 0xf7, 0xf0, 0xbd, 0x5b, 0xa9, 0xd5, 0xc5, 0x5f, 0x59, 0xbf, 0x7f, 0xb3, 0x55, 0x22, 0x02, 0xc9, 0x44, 0x55, 0x87, 0x8f, 0x96, 0x98, 0x64, 0x6d, 0x15}} , + {{0xb0, 0x8b, 0xaa, 0x1e, 0xec, 0xc7, 0xa5, 0x8f, 0x1f, 0x92, 0x04, 0xc6, 0x05, 0xf6, 0xdf, 0xa1, 0xcc, 0x1f, 0x81, 0xf5, 0x0e, 0x9c, 0x57, 0xdc, 0xe3, 0xbb, 0x06, 0x87, 0x1e, 0xfe, 0x23, 0x6c}}}, +{{{0xd8, 0x2b, 0x5b, 0x16, 0xea, 0x20, 0xf1, 0xd3, 0x68, 0x8f, 0xae, 0x5b, 0xd0, 0xa9, 0x1a, 0x19, 0xa8, 0x36, 0xfb, 0x2b, 0x57, 0x88, 0x7d, 0x90, 0xd5, 0xa6, 0xf3, 0xdc, 0x38, 0x89, 0x4e, 0x1f}} , + {{0xcc, 0x19, 0xda, 0x9b, 0x3b, 0x43, 0x48, 0x21, 0x2e, 0x23, 0x4d, 0x3d, 0xae, 0xf8, 0x8c, 0xfc, 0xdd, 0xa6, 0x74, 0x37, 0x65, 0xca, 0xee, 0x1a, 0x19, 0x8e, 0x9f, 0x64, 0x6f, 0x0c, 0x8b, 0x5a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x25, 0xb9, 0xc2, 0xf0, 0x72, 0xb8, 0x15, 0x16, 0xcc, 0x8d, 0x3c, 0x6f, 0x25, 0xed, 0xf4, 0x46, 0x2e, 0x0c, 0x60, 0x0f, 0xe2, 0x84, 0x34, 0x55, 0x89, 0x59, 0x34, 0x1b, 0xf5, 0x8d, 0xfe, 0x08}} , + {{0xf8, 0xab, 0x93, 0xbc, 0x44, 0xba, 0x1b, 0x75, 0x4b, 0x49, 0x6f, 0xd0, 0x54, 0x2e, 0x63, 0xba, 0xb5, 0xea, 0xed, 0x32, 0x14, 0xc9, 0x94, 0xd8, 0xc5, 0xce, 0xf4, 0x10, 0x68, 0xe0, 0x38, 0x27}}}, +{{{0x74, 0x1c, 0x14, 0x9b, 0xd4, 0x64, 0x61, 0x71, 0x5a, 0xb6, 0x21, 0x33, 0x4f, 0xf7, 0x8e, 0xba, 0xa5, 0x48, 0x9a, 0xc7, 0xfa, 0x9a, 0xf0, 0xb4, 0x62, 0xad, 0xf2, 0x5e, 0xcc, 0x03, 0x24, 0x1a}} , + {{0xf5, 0x76, 0xfd, 0xe4, 0xaf, 0xb9, 0x03, 0x59, 0xce, 0x63, 0xd2, 0x3b, 0x1f, 0xcd, 0x21, 0x0c, 0xad, 0x44, 0xa5, 0x97, 0xac, 0x80, 0x11, 0x02, 0x9b, 0x0c, 0xe5, 0x8b, 0xcd, 0xfb, 0x79, 0x77}}}, +{{{0x15, 0xbe, 0x9a, 0x0d, 0xba, 0x38, 0x72, 0x20, 0x8a, 0xf5, 0xbe, 0x59, 0x93, 0x79, 0xb7, 0xf6, 0x6a, 0x0c, 0x38, 0x27, 0x1a, 0x60, 0xf4, 0x86, 0x3b, 0xab, 0x5a, 0x00, 0xa0, 0xce, 0x21, 0x7d}} , + {{0x6c, 0xba, 0x14, 0xc5, 0xea, 0x12, 0x9e, 0x2e, 0x82, 0x63, 0xce, 0x9b, 0x4a, 0xe7, 0x1d, 0xec, 0xf1, 0x2e, 0x51, 0x1c, 0xf4, 0xd0, 0x69, 0x15, 0x42, 0x9d, 0xa3, 0x3f, 0x0e, 0xbf, 0xe9, 0x5c}}}, +{{{0xe4, 0x0d, 0xf4, 0xbd, 0xee, 0x31, 0x10, 0xed, 0xcb, 0x12, 0x86, 0xad, 0xd4, 0x2f, 0x90, 0x37, 0x32, 0xc3, 0x0b, 0x73, 0xec, 0x97, 0x85, 0xa4, 0x01, 0x1c, 0x76, 0x35, 0xfe, 0x75, 0xdd, 0x71}} , + {{0x11, 0xa4, 0x88, 0x9f, 0x3e, 0x53, 0x69, 0x3b, 0x1b, 0xe0, 0xf7, 0xba, 0x9b, 0xad, 0x4e, 0x81, 0x5f, 0xb5, 0x5c, 0xae, 0xbe, 0x67, 0x86, 0x37, 0x34, 0x8e, 0x07, 0x32, 0x45, 0x4a, 0x67, 0x39}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x90, 0x70, 0x58, 0x20, 0x03, 0x1e, 0x67, 0xb2, 0xc8, 0x9b, 0x58, 0xc5, 0xb1, 0xeb, 0x2d, 0x4a, 0xde, 0x82, 0x8c, 0xf2, 0xd2, 0x14, 0xb8, 0x70, 0x61, 0x4e, 0x73, 0xd6, 0x0b, 0x6b, 0x0d, 0x30}} , + {{0x81, 0xfc, 0x55, 0x5c, 0xbf, 0xa7, 0xc4, 0xbd, 0xe2, 0xf0, 0x4b, 0x8f, 0xe9, 0x7d, 0x99, 0xfa, 0xd3, 0xab, 0xbc, 0xc7, 0x83, 0x2b, 0x04, 0x7f, 0x0c, 0x19, 0x43, 0x03, 0x3d, 0x07, 0xca, 0x40}}}, +{{{0xf9, 0xc8, 0xbe, 0x8c, 0x16, 0x81, 0x39, 0x96, 0xf6, 0x17, 0x58, 0xc8, 0x30, 0x58, 0xfb, 0xc2, 0x03, 0x45, 0xd2, 0x52, 0x76, 0xe0, 0x6a, 0x26, 0x28, 0x5c, 0x88, 0x59, 0x6a, 0x5a, 0x54, 0x42}} , + {{0x07, 0xb5, 0x2e, 0x2c, 0x67, 0x15, 0x9b, 0xfb, 0x83, 0x69, 0x1e, 0x0f, 0xda, 0xd6, 0x29, 0xb1, 0x60, 0xe0, 0xb2, 0xba, 0x69, 0xa2, 0x9e, 0xbd, 0xbd, 0xe0, 0x1c, 0xbd, 0xcd, 0x06, 0x64, 0x70}}}, +{{{0x41, 0xfa, 0x8c, 0xe1, 0x89, 0x8f, 0x27, 0xc8, 0x25, 0x8f, 0x6f, 0x5f, 0x55, 0xf8, 0xde, 0x95, 0x6d, 0x2f, 0x75, 0x16, 0x2b, 0x4e, 0x44, 0xfd, 0x86, 0x6e, 0xe9, 0x70, 0x39, 0x76, 0x97, 0x7e}} , + {{0x17, 0x62, 0x6b, 0x14, 0xa1, 0x7c, 0xd0, 0x79, 0x6e, 0xd8, 0x8a, 0xa5, 0x6d, 0x8c, 0x93, 0xd2, 0x3f, 0xec, 0x44, 0x8d, 0x6e, 0x91, 0x01, 0x8c, 0x8f, 0xee, 0x01, 0x8f, 0xc0, 0xb4, 0x85, 0x0e}}}, +{{{0x02, 0x3a, 0x70, 0x41, 0xe4, 0x11, 0x57, 0x23, 0xac, 0xe6, 0xfc, 0x54, 0x7e, 0xcd, 0xd7, 0x22, 0xcb, 0x76, 0x9f, 0x20, 0xce, 0xa0, 0x73, 0x76, 0x51, 0x3b, 0xa4, 0xf8, 0xe3, 0x62, 0x12, 0x6c}} , + {{0x7f, 0x00, 0x9c, 0x26, 0x0d, 0x6f, 0x48, 0x7f, 0x3a, 0x01, 0xed, 0xc5, 0x96, 0xb0, 0x1f, 0x4f, 0xa8, 0x02, 0x62, 0x27, 0x8a, 0x50, 0x8d, 0x9a, 0x8b, 0x52, 0x0f, 0x1e, 0xcf, 0x41, 0x38, 0x19}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xf5, 0x6c, 0xd4, 0x2f, 0x0f, 0x69, 0x0f, 0x87, 0x3f, 0x61, 0x65, 0x1e, 0x35, 0x34, 0x85, 0xba, 0x02, 0x30, 0xac, 0x25, 0x3d, 0xe2, 0x62, 0xf1, 0xcc, 0xe9, 0x1b, 0xc2, 0xef, 0x6a, 0x42, 0x57}} , + {{0x34, 0x1f, 0x2e, 0xac, 0xd1, 0xc7, 0x04, 0x52, 0x32, 0x66, 0xb2, 0x33, 0x73, 0x21, 0x34, 0x54, 0xf7, 0x71, 0xed, 0x06, 0xb0, 0xff, 0xa6, 0x59, 0x6f, 0x8a, 0x4e, 0xfb, 0x02, 0xb0, 0x45, 0x6b}}}, +{{{0xf5, 0x48, 0x0b, 0x03, 0xc5, 0x22, 0x7d, 0x80, 0x08, 0x53, 0xfe, 0x32, 0xb1, 0xa1, 0x8a, 0x74, 0x6f, 0xbd, 0x3f, 0x85, 0xf4, 0xcf, 0xf5, 0x60, 0xaf, 0x41, 0x7e, 0x3e, 0x46, 0xa3, 0x5a, 0x20}} , + {{0xaa, 0x35, 0x87, 0x44, 0x63, 0x66, 0x97, 0xf8, 0x6e, 0x55, 0x0c, 0x04, 0x3e, 0x35, 0x50, 0xbf, 0x93, 0x69, 0xd2, 0x8b, 0x05, 0x55, 0x99, 0xbe, 0xe2, 0x53, 0x61, 0xec, 0xe8, 0x08, 0x0b, 0x32}}}, +{{{0xb3, 0x10, 0x45, 0x02, 0x69, 0x59, 0x2e, 0x97, 0xd9, 0x64, 0xf8, 0xdb, 0x25, 0x80, 0xdc, 0xc4, 0xd5, 0x62, 0x3c, 0xed, 0x65, 0x91, 0xad, 0xd1, 0x57, 0x81, 0x94, 0xaa, 0xa1, 0x29, 0xfc, 0x68}} , + {{0xdd, 0xb5, 0x7d, 0xab, 0x5a, 0x21, 0x41, 0x53, 0xbb, 0x17, 0x79, 0x0d, 0xd1, 0xa8, 0x0c, 0x0c, 0x20, 0x88, 0x09, 0xe9, 0x84, 0xe8, 0x25, 0x11, 0x67, 0x7a, 0x8b, 0x1a, 0xe4, 0x5d, 0xe1, 0x5d}}}, +{{{0x37, 0xea, 0xfe, 0x65, 0x3b, 0x25, 0xe8, 0xe1, 0xc2, 0xc5, 0x02, 0xa4, 0xbe, 0x98, 0x0a, 0x2b, 0x61, 0xc1, 0x9b, 0xe2, 0xd5, 0x92, 0xe6, 0x9e, 0x7d, 0x1f, 0xca, 0x43, 0x88, 0x8b, 0x2c, 0x59}} , + {{0xe0, 0xb5, 0x00, 0x1d, 0x2a, 0x6f, 0xaf, 0x79, 0x86, 0x2f, 0xa6, 0x5a, 0x93, 0xd1, 0xfe, 0xae, 0x3a, 0xee, 0xdb, 0x7c, 0x61, 0xbe, 0x7c, 0x01, 0xf9, 0xfe, 0x52, 0xdc, 0xd8, 0x52, 0xa3, 0x42}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x22, 0xaf, 0x13, 0x37, 0xbd, 0x37, 0x71, 0xac, 0x04, 0x46, 0x63, 0xac, 0xa4, 0x77, 0xed, 0x25, 0x38, 0xe0, 0x15, 0xa8, 0x64, 0x00, 0x0d, 0xce, 0x51, 0x01, 0xa9, 0xbc, 0x0f, 0x03, 0x1c, 0x04}} , + {{0x89, 0xf9, 0x80, 0x07, 0xcf, 0x3f, 0xb3, 0xe9, 0xe7, 0x45, 0x44, 0x3d, 0x2a, 0x7c, 0xe9, 0xe4, 0x16, 0x5c, 0x5e, 0x65, 0x1c, 0xc7, 0x7d, 0xc6, 0x7a, 0xfb, 0x43, 0xee, 0x25, 0x76, 0x46, 0x72}}}, +{{{0x02, 0xa2, 0xed, 0xf4, 0x8f, 0x6b, 0x0b, 0x3e, 0xeb, 0x35, 0x1a, 0xd5, 0x7e, 0xdb, 0x78, 0x00, 0x96, 0x8a, 0xa0, 0xb4, 0xcf, 0x60, 0x4b, 0xd4, 0xd5, 0xf9, 0x2d, 0xbf, 0x88, 0xbd, 0x22, 0x62}} , + {{0x13, 0x53, 0xe4, 0x82, 0x57, 0xfa, 0x1e, 0x8f, 0x06, 0x2b, 0x90, 0xba, 0x08, 0xb6, 0x10, 0x54, 0x4f, 0x7c, 0x1b, 0x26, 0xed, 0xda, 0x6b, 0xdd, 0x25, 0xd0, 0x4e, 0xea, 0x42, 0xbb, 0x25, 0x03}}}, +{{{0x51, 0x16, 0x50, 0x7c, 0xd5, 0x5d, 0xf6, 0x99, 0xe8, 0x77, 0x72, 0x4e, 0xfa, 0x62, 0xcb, 0x76, 0x75, 0x0c, 0xe2, 0x71, 0x98, 0x92, 0xd5, 0xfa, 0x45, 0xdf, 0x5c, 0x6f, 0x1e, 0x9e, 0x28, 0x69}} , + {{0x0d, 0xac, 0x66, 0x6d, 0xc3, 0x8b, 0xba, 0x16, 0xb5, 0xe2, 0xa0, 0x0d, 0x0c, 0xbd, 0xa4, 0x8e, 0x18, 0x6c, 0xf2, 0xdc, 0xf9, 0xdc, 0x4a, 0x86, 0x25, 0x95, 0x14, 0xcb, 0xd8, 0x1a, 0x04, 0x0f}}}, +{{{0x97, 0xa5, 0xdb, 0x8b, 0x2d, 0xaa, 0x42, 0x11, 0x09, 0xf2, 0x93, 0xbb, 0xd9, 0x06, 0x84, 0x4e, 0x11, 0xa8, 0xa0, 0x25, 0x2b, 0xa6, 0x5f, 0xae, 0xc4, 0xb4, 0x4c, 0xc8, 0xab, 0xc7, 0x3b, 0x02}} , + {{0xee, 0xc9, 0x29, 0x0f, 0xdf, 0x11, 0x85, 0xed, 0xce, 0x0d, 0x62, 0x2c, 0x8f, 0x4b, 0xf9, 0x04, 0xe9, 0x06, 0x72, 0x1d, 0x37, 0x20, 0x50, 0xc9, 0x14, 0xeb, 0xec, 0x39, 0xa7, 0x97, 0x2b, 0x4d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x69, 0xd1, 0x39, 0xbd, 0xfb, 0x33, 0xbe, 0xc4, 0xf0, 0x5c, 0xef, 0xf0, 0x56, 0x68, 0xfc, 0x97, 0x47, 0xc8, 0x72, 0xb6, 0x53, 0xa4, 0x0a, 0x98, 0xa5, 0xb4, 0x37, 0x71, 0xcf, 0x66, 0x50, 0x6d}} , + {{0x17, 0xa4, 0x19, 0x52, 0x11, 0x47, 0xb3, 0x5c, 0x5b, 0xa9, 0x2e, 0x22, 0xb4, 0x00, 0x52, 0xf9, 0x57, 0x18, 0xb8, 0xbe, 0x5a, 0xe3, 0xab, 0x83, 0xc8, 0x87, 0x0a, 0x2a, 0xd8, 0x8c, 0xbb, 0x54}}}, +{{{0xa9, 0x62, 0x93, 0x85, 0xbe, 0xe8, 0x73, 0x4a, 0x0e, 0xb0, 0xb5, 0x2d, 0x94, 0x50, 0xaa, 0xd3, 0xb2, 0xea, 0x9d, 0x62, 0x76, 0x3b, 0x07, 0x34, 0x4e, 0x2d, 0x70, 0xc8, 0x9a, 0x15, 0x66, 0x6b}} , + {{0xc5, 0x96, 0xca, 0xc8, 0x22, 0x1a, 0xee, 0x5f, 0xe7, 0x31, 0x60, 0x22, 0x83, 0x08, 0x63, 0xce, 0xb9, 0x32, 0x44, 0x58, 0x5d, 0x3a, 0x9b, 0xe4, 0x04, 0xd5, 0xef, 0x38, 0xef, 0x4b, 0xdd, 0x19}}}, +{{{0x4d, 0xc2, 0x17, 0x75, 0xa1, 0x68, 0xcd, 0xc3, 0xc6, 0x03, 0x44, 0xe3, 0x78, 0x09, 0x91, 0x47, 0x3f, 0x0f, 0xe4, 0x92, 0x58, 0xfa, 0x7d, 0x1f, 0x20, 0x94, 0x58, 0x5e, 0xbc, 0x19, 0x02, 0x6f}} , + {{0x20, 0xd6, 0xd8, 0x91, 0x54, 0xa7, 0xf3, 0x20, 0x4b, 0x34, 0x06, 0xfa, 0x30, 0xc8, 0x6f, 0x14, 0x10, 0x65, 0x74, 0x13, 0x4e, 0xf0, 0x69, 0x26, 0xce, 0xcf, 0x90, 0xf4, 0xd0, 0xc5, 0xc8, 0x64}}}, +{{{0x26, 0xa2, 0x50, 0x02, 0x24, 0x72, 0xf1, 0xf0, 0x4e, 0x2d, 0x93, 0xd5, 0x08, 0xe7, 0xae, 0x38, 0xf7, 0x18, 0xa5, 0x32, 0x34, 0xc2, 0xf0, 0xa6, 0xec, 0xb9, 0x61, 0x7b, 0x64, 0x99, 0xac, 0x71}} , + {{0x25, 0xcf, 0x74, 0x55, 0x1b, 0xaa, 0xa9, 0x38, 0x41, 0x40, 0xd5, 0x95, 0x95, 0xab, 0x1c, 0x5e, 0xbc, 0x41, 0x7e, 0x14, 0x30, 0xbe, 0x13, 0x89, 0xf4, 0xe5, 0xeb, 0x28, 0xc0, 0xc2, 0x96, 0x3a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x2b, 0x77, 0x45, 0xec, 0x67, 0x76, 0x32, 0x4c, 0xb9, 0xdf, 0x25, 0x32, 0x6b, 0xcb, 0xe7, 0x14, 0x61, 0x43, 0xee, 0xba, 0x9b, 0x71, 0xef, 0xd2, 0x48, 0x65, 0xbb, 0x1b, 0x8a, 0x13, 0x1b, 0x22}} , + {{0x84, 0xad, 0x0c, 0x18, 0x38, 0x5a, 0xba, 0xd0, 0x98, 0x59, 0xbf, 0x37, 0xb0, 0x4f, 0x97, 0x60, 0x20, 0xb3, 0x9b, 0x97, 0xf6, 0x08, 0x6c, 0xa4, 0xff, 0xfb, 0xb7, 0xfa, 0x95, 0xb2, 0x51, 0x79}}}, +{{{0x28, 0x5c, 0x3f, 0xdb, 0x6b, 0x18, 0x3b, 0x5c, 0xd1, 0x04, 0x28, 0xde, 0x85, 0x52, 0x31, 0xb5, 0xbb, 0xf6, 0xa9, 0xed, 0xbe, 0x28, 0x4f, 0xb3, 0x7e, 0x05, 0x6a, 0xdb, 0x95, 0x0d, 0x1b, 0x1c}} , + {{0xd5, 0xc5, 0xc3, 0x9a, 0x0a, 0xd0, 0x31, 0x3e, 0x07, 0x36, 0x8e, 0xc0, 0x8a, 0x62, 0xb1, 0xca, 0xd6, 0x0e, 0x1e, 0x9d, 0xef, 0xab, 0x98, 0x4d, 0xbb, 0x6c, 0x05, 0xe0, 0xe4, 0x5d, 0xbd, 0x57}}}, +{{{0xcc, 0x21, 0x27, 0xce, 0xfd, 0xa9, 0x94, 0x8e, 0xe1, 0xab, 0x49, 0xe0, 0x46, 0x26, 0xa1, 0xa8, 0x8c, 0xa1, 0x99, 0x1d, 0xb4, 0x27, 0x6d, 0x2d, 0xc8, 0x39, 0x30, 0x5e, 0x37, 0x52, 0xc4, 0x6e}} , + {{0xa9, 0x85, 0xf4, 0xe7, 0xb0, 0x15, 0x33, 0x84, 0x1b, 0x14, 0x1a, 0x02, 0xd9, 0x3b, 0xad, 0x0f, 0x43, 0x6c, 0xea, 0x3e, 0x0f, 0x7e, 0xda, 0xdd, 0x6b, 0x4c, 0x7f, 0x6e, 0xd4, 0x6b, 0xbf, 0x0f}}}, +{{{0x47, 0x9f, 0x7c, 0x56, 0x7c, 0x43, 0x91, 0x1c, 0xbb, 0x4e, 0x72, 0x3e, 0x64, 0xab, 0xa0, 0xa0, 0xdf, 0xb4, 0xd8, 0x87, 0x3a, 0xbd, 0xa8, 0x48, 0xc9, 0xb8, 0xef, 0x2e, 0xad, 0x6f, 0x84, 0x4f}} , + {{0x2d, 0x2d, 0xf0, 0x1b, 0x7e, 0x2a, 0x6c, 0xf8, 0xa9, 0x6a, 0xe1, 0xf0, 0x99, 0xa1, 0x67, 0x9a, 0xd4, 0x13, 0xca, 0xca, 0xba, 0x27, 0x92, 0xaa, 0xa1, 0x5d, 0x50, 0xde, 0xcc, 0x40, 0x26, 0x0a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x9f, 0x3e, 0xf2, 0xb2, 0x90, 0xce, 0xdb, 0x64, 0x3e, 0x03, 0xdd, 0x37, 0x36, 0x54, 0x70, 0x76, 0x24, 0xb5, 0x69, 0x03, 0xfc, 0xa0, 0x2b, 0x74, 0xb2, 0x05, 0x0e, 0xcc, 0xd8, 0x1f, 0x6a, 0x1f}} , + {{0x19, 0x5e, 0x60, 0x69, 0x58, 0x86, 0xa0, 0x31, 0xbd, 0x32, 0xe9, 0x2c, 0x5c, 0xd2, 0x85, 0xba, 0x40, 0x64, 0xa8, 0x74, 0xf8, 0x0e, 0x1c, 0xb3, 0xa9, 0x69, 0xe8, 0x1e, 0x40, 0x64, 0x99, 0x77}}}, +{{{0x6c, 0x32, 0x4f, 0xfd, 0xbb, 0x5c, 0xbb, 0x8d, 0x64, 0x66, 0x4a, 0x71, 0x1f, 0x79, 0xa3, 0xad, 0x8d, 0xf9, 0xd4, 0xec, 0xcf, 0x67, 0x70, 0xfa, 0x05, 0x4a, 0x0f, 0x6e, 0xaf, 0x87, 0x0a, 0x6f}} , + {{0xc6, 0x36, 0x6e, 0x6c, 0x8c, 0x24, 0x09, 0x60, 0xbe, 0x26, 0xd2, 0x4c, 0x5e, 0x17, 0xca, 0x5f, 0x1d, 0xcc, 0x87, 0xe8, 0x42, 0x6a, 0xcb, 0xcb, 0x7d, 0x92, 0x05, 0x35, 0x81, 0x13, 0x60, 0x6b}}}, +{{{0xf4, 0x15, 0xcd, 0x0f, 0x0a, 0xaf, 0x4e, 0x6b, 0x51, 0xfd, 0x14, 0xc4, 0x2e, 0x13, 0x86, 0x74, 0x44, 0xcb, 0x66, 0x6b, 0xb6, 0x9d, 0x74, 0x56, 0x32, 0xac, 0x8d, 0x8e, 0x8c, 0x8c, 0x8c, 0x39}} , + {{0xca, 0x59, 0x74, 0x1a, 0x11, 0xef, 0x6d, 0xf7, 0x39, 0x5c, 0x3b, 0x1f, 0xfa, 0xe3, 0x40, 0x41, 0x23, 0x9e, 0xf6, 0xd1, 0x21, 0xa2, 0xbf, 0xad, 0x65, 0x42, 0x6b, 0x59, 0x8a, 0xe8, 0xc5, 0x7f}}}, +{{{0x64, 0x05, 0x7a, 0x84, 0x4a, 0x13, 0xc3, 0xf6, 0xb0, 0x6e, 0x9a, 0x6b, 0x53, 0x6b, 0x32, 0xda, 0xd9, 0x74, 0x75, 0xc4, 0xba, 0x64, 0x3d, 0x3b, 0x08, 0xdd, 0x10, 0x46, 0xef, 0xc7, 0x90, 0x1f}} , + {{0x7b, 0x2f, 0x3a, 0xce, 0xc8, 0xa1, 0x79, 0x3c, 0x30, 0x12, 0x44, 0x28, 0xf6, 0xbc, 0xff, 0xfd, 0xf4, 0xc0, 0x97, 0xb0, 0xcc, 0xc3, 0x13, 0x7a, 0xb9, 0x9a, 0x16, 0xe4, 0xcb, 0x4c, 0x34, 0x63}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x07, 0x4e, 0xd3, 0x2d, 0x09, 0x33, 0x0e, 0xd2, 0x0d, 0xbe, 0x3e, 0xe7, 0xe4, 0xaa, 0xb7, 0x00, 0x8b, 0xe8, 0xad, 0xaa, 0x7a, 0x8d, 0x34, 0x28, 0xa9, 0x81, 0x94, 0xc5, 0xe7, 0x42, 0xac, 0x47}} , + {{0x24, 0x89, 0x7a, 0x8f, 0xb5, 0x9b, 0xf0, 0xc2, 0x03, 0x64, 0xd0, 0x1e, 0xf5, 0xa4, 0xb2, 0xf3, 0x74, 0xe9, 0x1a, 0x16, 0xfd, 0xcb, 0x15, 0xea, 0xeb, 0x10, 0x6c, 0x35, 0xd1, 0xc1, 0xa6, 0x28}}}, +{{{0xcc, 0xd5, 0x39, 0xfc, 0xa5, 0xa4, 0xad, 0x32, 0x15, 0xce, 0x19, 0xe8, 0x34, 0x2b, 0x1c, 0x60, 0x91, 0xfc, 0x05, 0xa9, 0xb3, 0xdc, 0x80, 0x29, 0xc4, 0x20, 0x79, 0x06, 0x39, 0xc0, 0xe2, 0x22}} , + {{0xbb, 0xa8, 0xe1, 0x89, 0x70, 0x57, 0x18, 0x54, 0x3c, 0xf6, 0x0d, 0x82, 0x12, 0x05, 0x87, 0x96, 0x06, 0x39, 0xe3, 0xf8, 0xb3, 0x95, 0xe5, 0xd7, 0x26, 0xbf, 0x09, 0x5a, 0x94, 0xf9, 0x1c, 0x63}}}, +{{{0x2b, 0x8c, 0x2d, 0x9a, 0x8b, 0x84, 0xf2, 0x56, 0xfb, 0xad, 0x2e, 0x7f, 0xb7, 0xfc, 0x30, 0xe1, 0x35, 0x89, 0xba, 0x4d, 0xa8, 0x6d, 0xce, 0x8c, 0x8b, 0x30, 0xe0, 0xda, 0x29, 0x18, 0x11, 0x17}} , + {{0x19, 0xa6, 0x5a, 0x65, 0x93, 0xc3, 0xb5, 0x31, 0x22, 0x4f, 0xf3, 0xf6, 0x0f, 0xeb, 0x28, 0xc3, 0x7c, 0xeb, 0xce, 0x86, 0xec, 0x67, 0x76, 0x6e, 0x35, 0x45, 0x7b, 0xd8, 0x6b, 0x92, 0x01, 0x65}}}, +{{{0x3d, 0xd5, 0x9a, 0x64, 0x73, 0x36, 0xb1, 0xd6, 0x86, 0x98, 0x42, 0x3f, 0x8a, 0xf1, 0xc7, 0xf5, 0x42, 0xa8, 0x9c, 0x52, 0xa8, 0xdc, 0xf9, 0x24, 0x3f, 0x4a, 0xa1, 0xa4, 0x5b, 0xe8, 0x62, 0x1a}} , + {{0xc5, 0xbd, 0xc8, 0x14, 0xd5, 0x0d, 0xeb, 0xe1, 0xa5, 0xe6, 0x83, 0x11, 0x09, 0x00, 0x1d, 0x55, 0x83, 0x51, 0x7e, 0x75, 0x00, 0x81, 0xb9, 0xcb, 0xd8, 0xc5, 0xe5, 0xa1, 0xd9, 0x17, 0x6d, 0x1f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xea, 0xf9, 0xe4, 0xe9, 0xe1, 0x52, 0x3f, 0x51, 0x19, 0x0d, 0xdd, 0xd9, 0x9d, 0x93, 0x31, 0x87, 0x23, 0x09, 0xd5, 0x83, 0xeb, 0x92, 0x09, 0x76, 0x6e, 0xe3, 0xf8, 0xc0, 0xa2, 0x66, 0xb5, 0x36}} , + {{0x3a, 0xbb, 0x39, 0xed, 0x32, 0x02, 0xe7, 0x43, 0x7a, 0x38, 0x14, 0x84, 0xe3, 0x44, 0xd2, 0x5e, 0x94, 0xdd, 0x78, 0x89, 0x55, 0x4c, 0x73, 0x9e, 0xe1, 0xe4, 0x3e, 0x43, 0xd0, 0x4a, 0xde, 0x1b}}}, +{{{0xb2, 0xe7, 0x8f, 0xe3, 0xa3, 0xc5, 0xcb, 0x72, 0xee, 0x79, 0x41, 0xf8, 0xdf, 0xee, 0x65, 0xc5, 0x45, 0x77, 0x27, 0x3c, 0xbd, 0x58, 0xd3, 0x75, 0xe2, 0x04, 0x4b, 0xbb, 0x65, 0xf3, 0xc8, 0x0f}} , + {{0x24, 0x7b, 0x93, 0x34, 0xb5, 0xe2, 0x74, 0x48, 0xcd, 0xa0, 0x0b, 0x92, 0x97, 0x66, 0x39, 0xf4, 0xb0, 0xe2, 0x5d, 0x39, 0x6a, 0x5b, 0x45, 0x17, 0x78, 0x1e, 0xdb, 0x91, 0x81, 0x1c, 0xf9, 0x16}}}, +{{{0x16, 0xdf, 0xd1, 0x5a, 0xd5, 0xe9, 0x4e, 0x58, 0x95, 0x93, 0x5f, 0x51, 0x09, 0xc3, 0x2a, 0xc9, 0xd4, 0x55, 0x48, 0x79, 0xa4, 0xa3, 0xb2, 0xc3, 0x62, 0xaa, 0x8c, 0xe8, 0xad, 0x47, 0x39, 0x1b}} , + {{0x46, 0xda, 0x9e, 0x51, 0x3a, 0xe6, 0xd1, 0xa6, 0xbb, 0x4d, 0x7b, 0x08, 0xbe, 0x8c, 0xd5, 0xf3, 0x3f, 0xfd, 0xf7, 0x44, 0x80, 0x2d, 0x53, 0x4b, 0xd0, 0x87, 0x68, 0xc1, 0xb5, 0xd8, 0xf7, 0x07}}}, +{{{0xf4, 0x10, 0x46, 0xbe, 0xb7, 0xd2, 0xd1, 0xce, 0x5e, 0x76, 0xa2, 0xd7, 0x03, 0xdc, 0xe4, 0x81, 0x5a, 0xf6, 0x3c, 0xde, 0xae, 0x7a, 0x9d, 0x21, 0x34, 0xa5, 0xf6, 0xa9, 0x73, 0xe2, 0x8d, 0x60}} , + {{0xfa, 0x44, 0x71, 0xf6, 0x41, 0xd8, 0xc6, 0x58, 0x13, 0x37, 0xeb, 0x84, 0x0f, 0x96, 0xc7, 0xdc, 0xc8, 0xa9, 0x7a, 0x83, 0xb2, 0x2f, 0x31, 0xb1, 0x1a, 0xd8, 0x98, 0x3f, 0x11, 0xd0, 0x31, 0x3b}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x81, 0xd5, 0x34, 0x16, 0x01, 0xa3, 0x93, 0xea, 0x52, 0x94, 0xec, 0x93, 0xb7, 0x81, 0x11, 0x2d, 0x58, 0xf9, 0xb5, 0x0a, 0xaa, 0x4f, 0xf6, 0x2e, 0x3f, 0x36, 0xbf, 0x33, 0x5a, 0xe7, 0xd1, 0x08}} , + {{0x1a, 0xcf, 0x42, 0xae, 0xcc, 0xb5, 0x77, 0x39, 0xc4, 0x5b, 0x5b, 0xd0, 0x26, 0x59, 0x27, 0xd0, 0x55, 0x71, 0x12, 0x9d, 0x88, 0x3d, 0x9c, 0xea, 0x41, 0x6a, 0xf0, 0x50, 0x93, 0x93, 0xdd, 0x47}}}, +{{{0x6f, 0xc9, 0x51, 0x6d, 0x1c, 0xaa, 0xf5, 0xa5, 0x90, 0x3f, 0x14, 0xe2, 0x6e, 0x8e, 0x64, 0xfd, 0xac, 0xe0, 0x4e, 0x22, 0xe5, 0xc1, 0xbc, 0x29, 0x0a, 0x6a, 0x9e, 0xa1, 0x60, 0xcb, 0x2f, 0x0b}} , + {{0xdc, 0x39, 0x32, 0xf3, 0xa1, 0x44, 0xe9, 0xc5, 0xc3, 0x78, 0xfb, 0x95, 0x47, 0x34, 0x35, 0x34, 0xe8, 0x25, 0xde, 0x93, 0xc6, 0xb4, 0x76, 0x6d, 0x86, 0x13, 0xc6, 0xe9, 0x68, 0xb5, 0x01, 0x63}}}, +{{{0x1f, 0x9a, 0x52, 0x64, 0x97, 0xd9, 0x1c, 0x08, 0x51, 0x6f, 0x26, 0x9d, 0xaa, 0x93, 0x33, 0x43, 0xfa, 0x77, 0xe9, 0x62, 0x9b, 0x5d, 0x18, 0x75, 0xeb, 0x78, 0xf7, 0x87, 0x8f, 0x41, 0xb4, 0x4d}} , + {{0x13, 0xa8, 0x82, 0x3e, 0xe9, 0x13, 0xad, 0xeb, 0x01, 0xca, 0xcf, 0xda, 0xcd, 0xf7, 0x6c, 0xc7, 0x7a, 0xdc, 0x1e, 0x6e, 0xc8, 0x4e, 0x55, 0x62, 0x80, 0xea, 0x78, 0x0c, 0x86, 0xb9, 0x40, 0x51}}}, +{{{0x27, 0xae, 0xd3, 0x0d, 0x4c, 0x8f, 0x34, 0xea, 0x7d, 0x3c, 0xe5, 0x8a, 0xcf, 0x5b, 0x92, 0xd8, 0x30, 0x16, 0xb4, 0xa3, 0x75, 0xff, 0xeb, 0x27, 0xc8, 0x5c, 0x6c, 0xc2, 0xee, 0x6c, 0x21, 0x0b}} , + {{0xc3, 0xba, 0x12, 0x53, 0x2a, 0xaa, 0x77, 0xad, 0x19, 0x78, 0x55, 0x8a, 0x2e, 0x60, 0x87, 0xc2, 0x6e, 0x91, 0x38, 0x91, 0x3f, 0x7a, 0xc5, 0x24, 0x8f, 0x51, 0xc5, 0xde, 0xb0, 0x53, 0x30, 0x56}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x02, 0xfe, 0x54, 0x12, 0x18, 0xca, 0x7d, 0xa5, 0x68, 0x43, 0xa3, 0x6d, 0x14, 0x2a, 0x6a, 0xa5, 0x8e, 0x32, 0xe7, 0x63, 0x4f, 0xe3, 0xc6, 0x44, 0x3e, 0xab, 0x63, 0xca, 0x17, 0x86, 0x74, 0x3f}} , + {{0x1e, 0x64, 0xc1, 0x7d, 0x52, 0xdc, 0x13, 0x5a, 0xa1, 0x9c, 0x4e, 0xee, 0x99, 0x28, 0xbb, 0x4c, 0xee, 0xac, 0xa9, 0x1b, 0x89, 0xa2, 0x38, 0x39, 0x7b, 0xc4, 0x0f, 0x42, 0xe6, 0x89, 0xed, 0x0f}}}, +{{{0xf3, 0x3c, 0x8c, 0x80, 0x83, 0x10, 0x8a, 0x37, 0x50, 0x9c, 0xb4, 0xdf, 0x3f, 0x8c, 0xf7, 0x23, 0x07, 0xd6, 0xff, 0xa0, 0x82, 0x6c, 0x75, 0x3b, 0xe4, 0xb5, 0xbb, 0xe4, 0xe6, 0x50, 0xf0, 0x08}} , + {{0x62, 0xee, 0x75, 0x48, 0x92, 0x33, 0xf2, 0xf4, 0xad, 0x15, 0x7a, 0xa1, 0x01, 0x46, 0xa9, 0x32, 0x06, 0x88, 0xb6, 0x36, 0x47, 0x35, 0xb9, 0xb4, 0x42, 0x85, 0x76, 0xf0, 0x48, 0x00, 0x90, 0x38}}}, +{{{0x51, 0x15, 0x9d, 0xc3, 0x95, 0xd1, 0x39, 0xbb, 0x64, 0x9d, 0x15, 0x81, 0xc1, 0x68, 0xd0, 0xb6, 0xa4, 0x2c, 0x7d, 0x5e, 0x02, 0x39, 0x00, 0xe0, 0x3b, 0xa4, 0xcc, 0xca, 0x1d, 0x81, 0x24, 0x10}} , + {{0xe7, 0x29, 0xf9, 0x37, 0xd9, 0x46, 0x5a, 0xcd, 0x70, 0xfe, 0x4d, 0x5b, 0xbf, 0xa5, 0xcf, 0x91, 0xf4, 0xef, 0xee, 0x8a, 0x29, 0xd0, 0xe7, 0xc4, 0x25, 0x92, 0x8a, 0xff, 0x36, 0xfc, 0xe4, 0x49}}}, +{{{0xbd, 0x00, 0xb9, 0x04, 0x7d, 0x35, 0xfc, 0xeb, 0xd0, 0x0b, 0x05, 0x32, 0x52, 0x7a, 0x89, 0x24, 0x75, 0x50, 0xe1, 0x63, 0x02, 0x82, 0x8e, 0xe7, 0x85, 0x0c, 0xf2, 0x56, 0x44, 0x37, 0x83, 0x25}} , + {{0x8f, 0xa1, 0xce, 0xcb, 0x60, 0xda, 0x12, 0x02, 0x1e, 0x29, 0x39, 0x2a, 0x03, 0xb7, 0xeb, 0x77, 0x40, 0xea, 0xc9, 0x2b, 0x2c, 0xd5, 0x7d, 0x7e, 0x2c, 0xc7, 0x5a, 0xfd, 0xff, 0xc4, 0xd1, 0x62}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x1d, 0x88, 0x98, 0x5b, 0x4e, 0xfc, 0x41, 0x24, 0x05, 0xe6, 0x50, 0x2b, 0xae, 0x96, 0x51, 0xd9, 0x6b, 0x72, 0xb2, 0x33, 0x42, 0x98, 0x68, 0xbb, 0x10, 0x5a, 0x7a, 0x8c, 0x9d, 0x07, 0xb4, 0x05}} , + {{0x2f, 0x61, 0x9f, 0xd7, 0xa8, 0x3f, 0x83, 0x8c, 0x10, 0x69, 0x90, 0xe6, 0xcf, 0xd2, 0x63, 0xa3, 0xe4, 0x54, 0x7e, 0xe5, 0x69, 0x13, 0x1c, 0x90, 0x57, 0xaa, 0xe9, 0x53, 0x22, 0x43, 0x29, 0x23}}}, +{{{0xe5, 0x1c, 0xf8, 0x0a, 0xfd, 0x2d, 0x7e, 0xf5, 0xf5, 0x70, 0x7d, 0x41, 0x6b, 0x11, 0xfe, 0xbe, 0x99, 0xd1, 0x55, 0x29, 0x31, 0xbf, 0xc0, 0x97, 0x6c, 0xd5, 0x35, 0xcc, 0x5e, 0x8b, 0xd9, 0x69}} , + {{0x8e, 0x4e, 0x9f, 0x25, 0xf8, 0x81, 0x54, 0x2d, 0x0e, 0xd5, 0x54, 0x81, 0x9b, 0xa6, 0x92, 0xce, 0x4b, 0xe9, 0x8f, 0x24, 0x3b, 0xca, 0xe0, 0x44, 0xab, 0x36, 0xfe, 0xfb, 0x87, 0xd4, 0x26, 0x3e}}}, +{{{0x0f, 0x93, 0x9c, 0x11, 0xe7, 0xdb, 0xf1, 0xf0, 0x85, 0x43, 0x28, 0x15, 0x37, 0xdd, 0xde, 0x27, 0xdf, 0xad, 0x3e, 0x49, 0x4f, 0xe0, 0x5b, 0xf6, 0x80, 0x59, 0x15, 0x3c, 0x85, 0xb7, 0x3e, 0x12}} , + {{0xf5, 0xff, 0xcc, 0xf0, 0xb4, 0x12, 0x03, 0x5f, 0xc9, 0x84, 0xcb, 0x1d, 0x17, 0xe0, 0xbc, 0xcc, 0x03, 0x62, 0xa9, 0x8b, 0x94, 0xa6, 0xaa, 0x18, 0xcb, 0x27, 0x8d, 0x49, 0xa6, 0x17, 0x15, 0x07}}}, +{{{0xd9, 0xb6, 0xd4, 0x9d, 0xd4, 0x6a, 0xaf, 0x70, 0x07, 0x2c, 0x10, 0x9e, 0xbd, 0x11, 0xad, 0xe4, 0x26, 0x33, 0x70, 0x92, 0x78, 0x1c, 0x74, 0x9f, 0x75, 0x60, 0x56, 0xf4, 0x39, 0xa8, 0xa8, 0x62}} , + {{0x3b, 0xbf, 0x55, 0x35, 0x61, 0x8b, 0x44, 0x97, 0xe8, 0x3a, 0x55, 0xc1, 0xc8, 0x3b, 0xfd, 0x95, 0x29, 0x11, 0x60, 0x96, 0x1e, 0xcb, 0x11, 0x9d, 0xc2, 0x03, 0x8a, 0x1b, 0xc6, 0xd6, 0x45, 0x3d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x7e, 0x0e, 0x50, 0xb2, 0xcc, 0x0d, 0x6b, 0xa6, 0x71, 0x5b, 0x42, 0xed, 0xbd, 0xaf, 0xac, 0xf0, 0xfc, 0x12, 0xa2, 0x3f, 0x4e, 0xda, 0xe8, 0x11, 0xf3, 0x23, 0xe1, 0x04, 0x62, 0x03, 0x1c, 0x4e}} , + {{0xc8, 0xb1, 0x1b, 0x6f, 0x73, 0x61, 0x3d, 0x27, 0x0d, 0x7d, 0x7a, 0x25, 0x5f, 0x73, 0x0e, 0x2f, 0x93, 0xf6, 0x24, 0xd8, 0x4f, 0x90, 0xac, 0xa2, 0x62, 0x0a, 0xf0, 0x61, 0xd9, 0x08, 0x59, 0x6a}}}, +{{{0x6f, 0x2d, 0x55, 0xf8, 0x2f, 0x8e, 0xf0, 0x18, 0x3b, 0xea, 0xdd, 0x26, 0x72, 0xd1, 0xf5, 0xfe, 0xe5, 0xb8, 0xe6, 0xd3, 0x10, 0x48, 0x46, 0x49, 0x3a, 0x9f, 0x5e, 0x45, 0x6b, 0x90, 0xe8, 0x7f}} , + {{0xd3, 0x76, 0x69, 0x33, 0x7b, 0xb9, 0x40, 0x70, 0xee, 0xa6, 0x29, 0x6b, 0xdd, 0xd0, 0x5d, 0x8d, 0xc1, 0x3e, 0x4a, 0xea, 0x37, 0xb1, 0x03, 0x02, 0x03, 0x35, 0xf1, 0x28, 0x9d, 0xff, 0x00, 0x13}}}, +{{{0x7a, 0xdb, 0x12, 0xd2, 0x8a, 0x82, 0x03, 0x1b, 0x1e, 0xaf, 0xf9, 0x4b, 0x9c, 0xbe, 0xae, 0x7c, 0xe4, 0x94, 0x2a, 0x23, 0xb3, 0x62, 0x86, 0xe7, 0xfd, 0x23, 0xaa, 0x99, 0xbd, 0x2b, 0x11, 0x6c}} , + {{0x8d, 0xa6, 0xd5, 0xac, 0x9d, 0xcc, 0x68, 0x75, 0x7f, 0xc3, 0x4d, 0x4b, 0xdd, 0x6c, 0xbb, 0x11, 0x5a, 0x60, 0xe5, 0xbd, 0x7d, 0x27, 0x8b, 0xda, 0xb4, 0x95, 0xf6, 0x03, 0x27, 0xa4, 0x92, 0x3f}}}, +{{{0x22, 0xd6, 0xb5, 0x17, 0x84, 0xbf, 0x12, 0xcc, 0x23, 0x14, 0x4a, 0xdf, 0x14, 0x31, 0xbc, 0xa1, 0xac, 0x6e, 0xab, 0xfa, 0x57, 0x11, 0x53, 0xb3, 0x27, 0xe6, 0xf9, 0x47, 0x33, 0x44, 0x34, 0x1e}} , + {{0x79, 0xfc, 0xa6, 0xb4, 0x0b, 0x35, 0x20, 0xc9, 0x4d, 0x22, 0x84, 0xc4, 0xa9, 0x20, 0xec, 0x89, 0x94, 0xba, 0x66, 0x56, 0x48, 0xb9, 0x87, 0x7f, 0xca, 0x1e, 0x06, 0xed, 0xa5, 0x55, 0x59, 0x29}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x56, 0xe1, 0xf5, 0xf1, 0xd5, 0xab, 0xa8, 0x2b, 0xae, 0x89, 0xf3, 0xcf, 0x56, 0x9f, 0xf2, 0x4b, 0x31, 0xbc, 0x18, 0xa9, 0x06, 0x5b, 0xbe, 0xb4, 0x61, 0xf8, 0xb2, 0x06, 0x9c, 0x81, 0xab, 0x4c}} , + {{0x1f, 0x68, 0x76, 0x01, 0x16, 0x38, 0x2b, 0x0f, 0x77, 0x97, 0x92, 0x67, 0x4e, 0x86, 0x6a, 0x8b, 0xe5, 0xe8, 0x0c, 0xf7, 0x36, 0x39, 0xb5, 0x33, 0xe6, 0xcf, 0x5e, 0xbd, 0x18, 0xfb, 0x10, 0x1f}}}, +{{{0x83, 0xf0, 0x0d, 0x63, 0xef, 0x53, 0x6b, 0xb5, 0x6b, 0xf9, 0x83, 0xcf, 0xde, 0x04, 0x22, 0x9b, 0x2c, 0x0a, 0xe0, 0xa5, 0xd8, 0xc7, 0x9c, 0xa5, 0xa3, 0xf6, 0x6f, 0xcf, 0x90, 0x6b, 0x68, 0x7c}} , + {{0x33, 0x15, 0xd7, 0x7f, 0x1a, 0xd5, 0x21, 0x58, 0xc4, 0x18, 0xa5, 0xf0, 0xcc, 0x73, 0xa8, 0xfd, 0xfa, 0x18, 0xd1, 0x03, 0x91, 0x8d, 0x52, 0xd2, 0xa3, 0xa4, 0xd3, 0xb1, 0xea, 0x1d, 0x0f, 0x00}}}, +{{{0xcc, 0x48, 0x83, 0x90, 0xe5, 0xfd, 0x3f, 0x84, 0xaa, 0xf9, 0x8b, 0x82, 0x59, 0x24, 0x34, 0x68, 0x4f, 0x1c, 0x23, 0xd9, 0xcc, 0x71, 0xe1, 0x7f, 0x8c, 0xaf, 0xf1, 0xee, 0x00, 0xb6, 0xa0, 0x77}} , + {{0xf5, 0x1a, 0x61, 0xf7, 0x37, 0x9d, 0x00, 0xf4, 0xf2, 0x69, 0x6f, 0x4b, 0x01, 0x85, 0x19, 0x45, 0x4d, 0x7f, 0x02, 0x7c, 0x6a, 0x05, 0x47, 0x6c, 0x1f, 0x81, 0x20, 0xd4, 0xe8, 0x50, 0x27, 0x72}}}, +{{{0x2c, 0x3a, 0xe5, 0xad, 0xf4, 0xdd, 0x2d, 0xf7, 0x5c, 0x44, 0xb5, 0x5b, 0x21, 0xa3, 0x89, 0x5f, 0x96, 0x45, 0xca, 0x4d, 0xa4, 0x21, 0x99, 0x70, 0xda, 0xc4, 0xc4, 0xa0, 0xe5, 0xf4, 0xec, 0x0a}} , + {{0x07, 0x68, 0x21, 0x65, 0xe9, 0x08, 0xa0, 0x0b, 0x6a, 0x4a, 0xba, 0xb5, 0x80, 0xaf, 0xd0, 0x1b, 0xc5, 0xf5, 0x4b, 0x73, 0x50, 0x60, 0x2d, 0x71, 0x69, 0x61, 0x0e, 0xc0, 0x20, 0x40, 0x30, 0x19}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xd0, 0x75, 0x57, 0x3b, 0xeb, 0x5c, 0x14, 0x56, 0x50, 0xc9, 0x4f, 0xb8, 0xb8, 0x1e, 0xa3, 0xf4, 0xab, 0xf5, 0xa9, 0x20, 0x15, 0x94, 0x82, 0xda, 0x96, 0x1c, 0x9b, 0x59, 0x8c, 0xff, 0xf4, 0x51}} , + {{0xc1, 0x3a, 0x86, 0xd7, 0xb0, 0x06, 0x84, 0x7f, 0x1b, 0xbd, 0xd4, 0x07, 0x78, 0x80, 0x2e, 0xb1, 0xb4, 0xee, 0x52, 0x38, 0xee, 0x9a, 0xf9, 0xf6, 0xf3, 0x41, 0x6e, 0xd4, 0x88, 0x95, 0xac, 0x35}}}, +{{{0x41, 0x97, 0xbf, 0x71, 0x6a, 0x9b, 0x72, 0xec, 0xf3, 0xf8, 0x6b, 0xe6, 0x0e, 0x6c, 0x69, 0xa5, 0x2f, 0x68, 0x52, 0xd8, 0x61, 0x81, 0xc0, 0x63, 0x3f, 0xa6, 0x3c, 0x13, 0x90, 0xe6, 0x8d, 0x56}} , + {{0xe8, 0x39, 0x30, 0x77, 0x23, 0xb1, 0xfd, 0x1b, 0x3d, 0x3e, 0x74, 0x4d, 0x7f, 0xae, 0x5b, 0x3a, 0xb4, 0x65, 0x0e, 0x3a, 0x43, 0xdc, 0xdc, 0x41, 0x47, 0xe6, 0xe8, 0x92, 0x09, 0x22, 0x48, 0x4c}}}, +{{{0x85, 0x57, 0x9f, 0xb5, 0xc8, 0x06, 0xb2, 0x9f, 0x47, 0x3f, 0xf0, 0xfa, 0xe6, 0xa9, 0xb1, 0x9b, 0x6f, 0x96, 0x7d, 0xf9, 0xa4, 0x65, 0x09, 0x75, 0x32, 0xa6, 0x6c, 0x7f, 0x47, 0x4b, 0x2f, 0x4f}} , + {{0x34, 0xe9, 0x59, 0x93, 0x9d, 0x26, 0x80, 0x54, 0xf2, 0xcc, 0x3c, 0xc2, 0x25, 0x85, 0xe3, 0x6a, 0xc1, 0x62, 0x04, 0xa7, 0x08, 0x32, 0x6d, 0xa1, 0x39, 0x84, 0x8a, 0x3b, 0x87, 0x5f, 0x11, 0x13}}}, +{{{0xda, 0x03, 0x34, 0x66, 0xc4, 0x0c, 0x73, 0x6e, 0xbc, 0x24, 0xb5, 0xf9, 0x70, 0x81, 0x52, 0xe9, 0xf4, 0x7c, 0x23, 0xdd, 0x9f, 0xb8, 0x46, 0xef, 0x1d, 0x22, 0x55, 0x7d, 0x71, 0xc4, 0x42, 0x33}} , + {{0xc5, 0x37, 0x69, 0x5b, 0xa8, 0xc6, 0x9d, 0xa4, 0xfc, 0x61, 0x6e, 0x68, 0x46, 0xea, 0xd7, 0x1c, 0x67, 0xd2, 0x7d, 0xfa, 0xf1, 0xcc, 0x54, 0x8d, 0x36, 0x35, 0xc9, 0x00, 0xdf, 0x6c, 0x67, 0x50}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x9a, 0x4d, 0x42, 0x29, 0x5d, 0xa4, 0x6b, 0x6f, 0xa8, 0x8a, 0x4d, 0x91, 0x7b, 0xd2, 0xdf, 0x36, 0xef, 0x01, 0x22, 0xc5, 0xcc, 0x8d, 0xeb, 0x58, 0x3d, 0xb3, 0x50, 0xfc, 0x8b, 0x97, 0x96, 0x33}} , + {{0x93, 0x33, 0x07, 0xc8, 0x4a, 0xca, 0xd0, 0xb1, 0xab, 0xbd, 0xdd, 0xa7, 0x7c, 0xac, 0x3e, 0x45, 0xcb, 0xcc, 0x07, 0x91, 0xbf, 0x35, 0x9d, 0xcb, 0x7d, 0x12, 0x3c, 0x11, 0x59, 0x13, 0xcf, 0x5c}}}, +{{{0x45, 0xb8, 0x41, 0xd7, 0xab, 0x07, 0x15, 0x00, 0x8e, 0xce, 0xdf, 0xb2, 0x43, 0x5c, 0x01, 0xdc, 0xf4, 0x01, 0x51, 0x95, 0x10, 0x5a, 0xf6, 0x24, 0x24, 0xa0, 0x19, 0x3a, 0x09, 0x2a, 0xaa, 0x3f}} , + {{0xdc, 0x8e, 0xeb, 0xc6, 0xbf, 0xdd, 0x11, 0x7b, 0xe7, 0x47, 0xe6, 0xce, 0xe7, 0xb6, 0xc5, 0xe8, 0x8a, 0xdc, 0x4b, 0x57, 0x15, 0x3b, 0x66, 0xca, 0x89, 0xa3, 0xfd, 0xac, 0x0d, 0xe1, 0x1d, 0x7a}}}, +{{{0x89, 0xef, 0xbf, 0x03, 0x75, 0xd0, 0x29, 0x50, 0xcb, 0x7d, 0xd6, 0xbe, 0xad, 0x5f, 0x7b, 0x00, 0x32, 0xaa, 0x98, 0xed, 0x3f, 0x8f, 0x92, 0xcb, 0x81, 0x56, 0x01, 0x63, 0x64, 0xa3, 0x38, 0x39}} , + {{0x8b, 0xa4, 0xd6, 0x50, 0xb4, 0xaa, 0x5d, 0x64, 0x64, 0x76, 0x2e, 0xa1, 0xa6, 0xb3, 0xb8, 0x7c, 0x7a, 0x56, 0xf5, 0x5c, 0x4e, 0x84, 0x5c, 0xfb, 0xdd, 0xca, 0x48, 0x8b, 0x48, 0xb9, 0xba, 0x34}}}, +{{{0xc5, 0xe3, 0xe8, 0xae, 0x17, 0x27, 0xe3, 0x64, 0x60, 0x71, 0x47, 0x29, 0x02, 0x0f, 0x92, 0x5d, 0x10, 0x93, 0xc8, 0x0e, 0xa1, 0xed, 0xba, 0xa9, 0x96, 0x1c, 0xc5, 0x76, 0x30, 0xcd, 0xf9, 0x30}} , + {{0x95, 0xb0, 0xbd, 0x8c, 0xbc, 0xa7, 0x4f, 0x7e, 0xfd, 0x4e, 0x3a, 0xbf, 0x5f, 0x04, 0x79, 0x80, 0x2b, 0x5a, 0x9f, 0x4f, 0x68, 0x21, 0x19, 0x71, 0xc6, 0x20, 0x01, 0x42, 0xaa, 0xdf, 0xae, 0x2c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x90, 0x6e, 0x7e, 0x4b, 0x71, 0x93, 0xc0, 0x72, 0xed, 0xeb, 0x71, 0x24, 0x97, 0x26, 0x9c, 0xfe, 0xcb, 0x3e, 0x59, 0x19, 0xa8, 0x0f, 0x75, 0x7d, 0xbe, 0x18, 0xe6, 0x96, 0x1e, 0x95, 0x70, 0x60}} , + {{0x89, 0x66, 0x3e, 0x1d, 0x4c, 0x5f, 0xfe, 0xc0, 0x04, 0x43, 0xd6, 0x44, 0x19, 0xb5, 0xad, 0xc7, 0x22, 0xdc, 0x71, 0x28, 0x64, 0xde, 0x41, 0x38, 0x27, 0x8f, 0x2c, 0x6b, 0x08, 0xb8, 0xb8, 0x7b}}}, +{{{0x3d, 0x70, 0x27, 0x9d, 0xd9, 0xaf, 0xb1, 0x27, 0xaf, 0xe3, 0x5d, 0x1e, 0x3a, 0x30, 0x54, 0x61, 0x60, 0xe8, 0xc3, 0x26, 0x3a, 0xbc, 0x7e, 0xf5, 0x81, 0xdd, 0x64, 0x01, 0x04, 0xeb, 0xc0, 0x1e}} , + {{0xda, 0x2c, 0xa4, 0xd1, 0xa1, 0xc3, 0x5c, 0x6e, 0x32, 0x07, 0x1f, 0xb8, 0x0e, 0x19, 0x9e, 0x99, 0x29, 0x33, 0x9a, 0xae, 0x7a, 0xed, 0x68, 0x42, 0x69, 0x7c, 0x07, 0xb3, 0x38, 0x2c, 0xf6, 0x3d}}}, +{{{0x64, 0xaa, 0xb5, 0x88, 0x79, 0x65, 0x38, 0x8c, 0x94, 0xd6, 0x62, 0x37, 0x7d, 0x64, 0xcd, 0x3a, 0xeb, 0xff, 0xe8, 0x81, 0x09, 0xc7, 0x6a, 0x50, 0x09, 0x0d, 0x28, 0x03, 0x0d, 0x9a, 0x93, 0x0a}} , + {{0x42, 0xa3, 0xf1, 0xc5, 0xb4, 0x0f, 0xd8, 0xc8, 0x8d, 0x15, 0x31, 0xbd, 0xf8, 0x07, 0x8b, 0xcd, 0x08, 0x8a, 0xfb, 0x18, 0x07, 0xfe, 0x8e, 0x52, 0x86, 0xef, 0xbe, 0xec, 0x49, 0x52, 0x99, 0x08}}}, +{{{0x0f, 0xa9, 0xd5, 0x01, 0xaa, 0x48, 0x4f, 0x28, 0x66, 0x32, 0x1a, 0xba, 0x7c, 0xea, 0x11, 0x80, 0x17, 0x18, 0x9b, 0x56, 0x88, 0x25, 0x06, 0x69, 0x12, 0x2c, 0xea, 0x56, 0x69, 0x41, 0x24, 0x19}} , + {{0xde, 0x21, 0xf0, 0xda, 0x8a, 0xfb, 0xb1, 0xb8, 0xcd, 0xc8, 0x6a, 0x82, 0x19, 0x73, 0xdb, 0xc7, 0xcf, 0x88, 0xeb, 0x96, 0xee, 0x6f, 0xfb, 0x06, 0xd2, 0xcd, 0x7d, 0x7b, 0x12, 0x28, 0x8e, 0x0c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x93, 0x44, 0x97, 0xce, 0x28, 0xff, 0x3a, 0x40, 0xc4, 0xf5, 0xf6, 0x9b, 0xf4, 0x6b, 0x07, 0x84, 0xfb, 0x98, 0xd8, 0xec, 0x8c, 0x03, 0x57, 0xec, 0x49, 0xed, 0x63, 0xb6, 0xaa, 0xff, 0x98, 0x28}} , + {{0x3d, 0x16, 0x35, 0xf3, 0x46, 0xbc, 0xb3, 0xf4, 0xc6, 0xb6, 0x4f, 0xfa, 0xf4, 0xa0, 0x13, 0xe6, 0x57, 0x45, 0x93, 0xb9, 0xbc, 0xd6, 0x59, 0xe7, 0x77, 0x94, 0x6c, 0xab, 0x96, 0x3b, 0x4f, 0x09}}}, +{{{0x5a, 0xf7, 0x6b, 0x01, 0x12, 0x4f, 0x51, 0xc1, 0x70, 0x84, 0x94, 0x47, 0xb2, 0x01, 0x6c, 0x71, 0xd7, 0xcc, 0x17, 0x66, 0x0f, 0x59, 0x5d, 0x5d, 0x10, 0x01, 0x57, 0x11, 0xf5, 0xdd, 0xe2, 0x34}} , + {{0x26, 0xd9, 0x1f, 0x5c, 0x58, 0xac, 0x8b, 0x03, 0xd2, 0xc3, 0x85, 0x0f, 0x3a, 0xc3, 0x7f, 0x6d, 0x8e, 0x86, 0xcd, 0x52, 0x74, 0x8f, 0x55, 0x77, 0x17, 0xb7, 0x8e, 0xb7, 0x88, 0xea, 0xda, 0x1b}}}, +{{{0xb6, 0xea, 0x0e, 0x40, 0x93, 0x20, 0x79, 0x35, 0x6a, 0x61, 0x84, 0x5a, 0x07, 0x6d, 0xf9, 0x77, 0x6f, 0xed, 0x69, 0x1c, 0x0d, 0x25, 0x76, 0xcc, 0xf0, 0xdb, 0xbb, 0xc5, 0xad, 0xe2, 0x26, 0x57}} , + {{0xcf, 0xe8, 0x0e, 0x6b, 0x96, 0x7d, 0xed, 0x27, 0xd1, 0x3c, 0xa9, 0xd9, 0x50, 0xa9, 0x98, 0x84, 0x5e, 0x86, 0xef, 0xd6, 0xf0, 0xf8, 0x0e, 0x89, 0x05, 0x2f, 0xd9, 0x5f, 0x15, 0x5f, 0x73, 0x79}}}, +{{{0xc8, 0x5c, 0x16, 0xfe, 0xed, 0x9f, 0x26, 0x56, 0xf6, 0x4b, 0x9f, 0xa7, 0x0a, 0x85, 0xfe, 0xa5, 0x8c, 0x87, 0xdd, 0x98, 0xce, 0x4e, 0xc3, 0x58, 0x55, 0xb2, 0x7b, 0x3d, 0xd8, 0x6b, 0xb5, 0x4c}} , + {{0x65, 0x38, 0xa0, 0x15, 0xfa, 0xa7, 0xb4, 0x8f, 0xeb, 0xc4, 0x86, 0x9b, 0x30, 0xa5, 0x5e, 0x4d, 0xea, 0x8a, 0x9a, 0x9f, 0x1a, 0xd8, 0x5b, 0x53, 0x14, 0x19, 0x25, 0x63, 0xb4, 0x6f, 0x1f, 0x5d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xac, 0x8f, 0xbc, 0x1e, 0x7d, 0x8b, 0x5a, 0x0b, 0x8d, 0xaf, 0x76, 0x2e, 0x71, 0xe3, 0x3b, 0x6f, 0x53, 0x2f, 0x3e, 0x90, 0x95, 0xd4, 0x35, 0x14, 0x4f, 0x8c, 0x3c, 0xce, 0x57, 0x1c, 0x76, 0x49}} , + {{0xa8, 0x50, 0xe1, 0x61, 0x6b, 0x57, 0x35, 0xeb, 0x44, 0x0b, 0x0c, 0x6e, 0xf9, 0x25, 0x80, 0x74, 0xf2, 0x8f, 0x6f, 0x7a, 0x3e, 0x7f, 0x2d, 0xf3, 0x4e, 0x09, 0x65, 0x10, 0x5e, 0x03, 0x25, 0x32}}}, +{{{0xa9, 0x60, 0xdc, 0x0f, 0x64, 0xe5, 0x1d, 0xe2, 0x8d, 0x4f, 0x79, 0x2f, 0x0e, 0x24, 0x02, 0x00, 0x05, 0x77, 0x43, 0x25, 0x3d, 0x6a, 0xc7, 0xb7, 0xbf, 0x04, 0x08, 0x65, 0xf4, 0x39, 0x4b, 0x65}} , + {{0x96, 0x19, 0x12, 0x6b, 0x6a, 0xb7, 0xe3, 0xdc, 0x45, 0x9b, 0xdb, 0xb4, 0xa8, 0xae, 0xdc, 0xa8, 0x14, 0x44, 0x65, 0x62, 0xce, 0x34, 0x9a, 0x84, 0x18, 0x12, 0x01, 0xf1, 0xe2, 0x7b, 0xce, 0x50}}}, +{{{0x41, 0x21, 0x30, 0x53, 0x1b, 0x47, 0x01, 0xb7, 0x18, 0xd8, 0x82, 0x57, 0xbd, 0xa3, 0x60, 0xf0, 0x32, 0xf6, 0x5b, 0xf0, 0x30, 0x88, 0x91, 0x59, 0xfd, 0x90, 0xa2, 0xb9, 0x55, 0x93, 0x21, 0x34}} , + {{0x97, 0x67, 0x9e, 0xeb, 0x6a, 0xf9, 0x6e, 0xd6, 0x73, 0xe8, 0x6b, 0x29, 0xec, 0x63, 0x82, 0x00, 0xa8, 0x99, 0x1c, 0x1d, 0x30, 0xc8, 0x90, 0x52, 0x90, 0xb6, 0x6a, 0x80, 0x4e, 0xff, 0x4b, 0x51}}}, +{{{0x0f, 0x7d, 0x63, 0x8c, 0x6e, 0x5c, 0xde, 0x30, 0xdf, 0x65, 0xfa, 0x2e, 0xb0, 0xa3, 0x25, 0x05, 0x54, 0xbd, 0x25, 0xba, 0x06, 0xae, 0xdf, 0x8b, 0xd9, 0x1b, 0xea, 0x38, 0xb3, 0x05, 0x16, 0x09}} , + {{0xc7, 0x8c, 0xbf, 0x64, 0x28, 0xad, 0xf8, 0xa5, 0x5a, 0x6f, 0xc9, 0xba, 0xd5, 0x7f, 0xd5, 0xd6, 0xbd, 0x66, 0x2f, 0x3d, 0xaa, 0x54, 0xf6, 0xba, 0x32, 0x22, 0x9a, 0x1e, 0x52, 0x05, 0xf4, 0x1d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xaa, 0x1f, 0xbb, 0xeb, 0xfe, 0xe4, 0x87, 0xfc, 0xb1, 0x2c, 0xb7, 0x88, 0xf4, 0xc6, 0xb9, 0xf5, 0x24, 0x46, 0xf2, 0xa5, 0x9f, 0x8f, 0x8a, 0x93, 0x70, 0x69, 0xd4, 0x56, 0xec, 0xfd, 0x06, 0x46}} , + {{0x4e, 0x66, 0xcf, 0x4e, 0x34, 0xce, 0x0c, 0xd9, 0xa6, 0x50, 0xd6, 0x5e, 0x95, 0xaf, 0xe9, 0x58, 0xfa, 0xee, 0x9b, 0xb8, 0xa5, 0x0f, 0x35, 0xe0, 0x43, 0x82, 0x6d, 0x65, 0xe6, 0xd9, 0x00, 0x0f}}}, +{{{0x7b, 0x75, 0x3a, 0xfc, 0x64, 0xd3, 0x29, 0x7e, 0xdd, 0x49, 0x9a, 0x59, 0x53, 0xbf, 0xb4, 0xa7, 0x52, 0xb3, 0x05, 0xab, 0xc3, 0xaf, 0x16, 0x1a, 0x85, 0x42, 0x32, 0xa2, 0x86, 0xfa, 0x39, 0x43}} , + {{0x0e, 0x4b, 0xa3, 0x63, 0x8a, 0xfe, 0xa5, 0x58, 0xf1, 0x13, 0xbd, 0x9d, 0xaa, 0x7f, 0x76, 0x40, 0x70, 0x81, 0x10, 0x75, 0x99, 0xbb, 0xbe, 0x0b, 0x16, 0xe9, 0xba, 0x62, 0x34, 0xcc, 0x07, 0x6d}}}, +{{{0xc3, 0xf1, 0xc6, 0x93, 0x65, 0xee, 0x0b, 0xbc, 0xea, 0x14, 0xf0, 0xc1, 0xf8, 0x84, 0x89, 0xc2, 0xc9, 0xd7, 0xea, 0x34, 0xca, 0xa7, 0xc4, 0x99, 0xd5, 0x50, 0x69, 0xcb, 0xd6, 0x21, 0x63, 0x7c}} , + {{0x99, 0xeb, 0x7c, 0x31, 0x73, 0x64, 0x67, 0x7f, 0x0c, 0x66, 0xaa, 0x8c, 0x69, 0x91, 0xe2, 0x26, 0xd3, 0x23, 0xe2, 0x76, 0x5d, 0x32, 0x52, 0xdf, 0x5d, 0xc5, 0x8f, 0xb7, 0x7c, 0x84, 0xb3, 0x70}}}, +{{{0xeb, 0x01, 0xc7, 0x36, 0x97, 0x4e, 0xb6, 0xab, 0x5f, 0x0d, 0x2c, 0xba, 0x67, 0x64, 0x55, 0xde, 0xbc, 0xff, 0xa6, 0xec, 0x04, 0xd3, 0x8d, 0x39, 0x56, 0x5e, 0xee, 0xf8, 0xe4, 0x2e, 0x33, 0x62}} , + {{0x65, 0xef, 0xb8, 0x9f, 0xc8, 0x4b, 0xa7, 0xfd, 0x21, 0x49, 0x9b, 0x92, 0x35, 0x82, 0xd6, 0x0a, 0x9b, 0xf2, 0x79, 0xf1, 0x47, 0x2f, 0x6a, 0x7e, 0x9f, 0xcf, 0x18, 0x02, 0x3c, 0xfb, 0x1b, 0x3e}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x2f, 0x8b, 0xc8, 0x40, 0x51, 0xd1, 0xac, 0x1a, 0x0b, 0xe4, 0xa9, 0xa2, 0x42, 0x21, 0x19, 0x2f, 0x7b, 0x97, 0xbf, 0xf7, 0x57, 0x6d, 0x3f, 0x3d, 0x4f, 0x0f, 0xe2, 0xb2, 0x81, 0x00, 0x9e, 0x7b}} , + {{0x8c, 0x85, 0x2b, 0xc4, 0xfc, 0xf1, 0xab, 0xe8, 0x79, 0x22, 0xc4, 0x84, 0x17, 0x3a, 0xfa, 0x86, 0xa6, 0x7d, 0xf9, 0xf3, 0x6f, 0x03, 0x57, 0x20, 0x4d, 0x79, 0xf9, 0x6e, 0x71, 0x54, 0x38, 0x09}}}, +{{{0x40, 0x29, 0x74, 0xa8, 0x2f, 0x5e, 0xf9, 0x79, 0xa4, 0xf3, 0x3e, 0xb9, 0xfd, 0x33, 0x31, 0xac, 0x9a, 0x69, 0x88, 0x1e, 0x77, 0x21, 0x2d, 0xf3, 0x91, 0x52, 0x26, 0x15, 0xb2, 0xa6, 0xcf, 0x7e}} , + {{0xc6, 0x20, 0x47, 0x6c, 0xa4, 0x7d, 0xcb, 0x63, 0xea, 0x5b, 0x03, 0xdf, 0x3e, 0x88, 0x81, 0x6d, 0xce, 0x07, 0x42, 0x18, 0x60, 0x7e, 0x7b, 0x55, 0xfe, 0x6a, 0xf3, 0xda, 0x5c, 0x8b, 0x95, 0x10}}}, +{{{0x62, 0xe4, 0x0d, 0x03, 0xb4, 0xd7, 0xcd, 0xfa, 0xbd, 0x46, 0xdf, 0x93, 0x71, 0x10, 0x2c, 0xa8, 0x3b, 0xb6, 0x09, 0x05, 0x70, 0x84, 0x43, 0x29, 0xa8, 0x59, 0xf5, 0x8e, 0x10, 0xe4, 0xd7, 0x20}} , + {{0x57, 0x82, 0x1c, 0xab, 0xbf, 0x62, 0x70, 0xe8, 0xc4, 0xcf, 0xf0, 0x28, 0x6e, 0x16, 0x3c, 0x08, 0x78, 0x89, 0x85, 0x46, 0x0f, 0xf6, 0x7f, 0xcf, 0xcb, 0x7e, 0xb8, 0x25, 0xe9, 0x5a, 0xfa, 0x03}}}, +{{{0xfb, 0x95, 0x92, 0x63, 0x50, 0xfc, 0x62, 0xf0, 0xa4, 0x5e, 0x8c, 0x18, 0xc2, 0x17, 0x24, 0xb7, 0x78, 0xc2, 0xa9, 0xe7, 0x6a, 0x32, 0xd6, 0x29, 0x85, 0xaf, 0xcb, 0x8d, 0x91, 0x13, 0xda, 0x6b}} , + {{0x36, 0x0a, 0xc2, 0xb6, 0x4b, 0xa5, 0x5d, 0x07, 0x17, 0x41, 0x31, 0x5f, 0x62, 0x46, 0xf8, 0x92, 0xf9, 0x66, 0x48, 0x73, 0xa6, 0x97, 0x0d, 0x7d, 0x88, 0xee, 0x62, 0xb1, 0x03, 0xa8, 0x3f, 0x2c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x4a, 0xb1, 0x70, 0x8a, 0xa9, 0xe8, 0x63, 0x79, 0x00, 0xe2, 0x25, 0x16, 0xca, 0x4b, 0x0f, 0xa4, 0x66, 0xad, 0x19, 0x9f, 0x88, 0x67, 0x0c, 0x8b, 0xc2, 0x4a, 0x5b, 0x2b, 0x6d, 0x95, 0xaf, 0x19}} , + {{0x8b, 0x9d, 0xb6, 0xcc, 0x60, 0xb4, 0x72, 0x4f, 0x17, 0x69, 0x5a, 0x4a, 0x68, 0x34, 0xab, 0xa1, 0x45, 0x32, 0x3c, 0x83, 0x87, 0x72, 0x30, 0x54, 0x77, 0x68, 0xae, 0xfb, 0xb5, 0x8b, 0x22, 0x5e}}}, +{{{0xf1, 0xb9, 0x87, 0x35, 0xc5, 0xbb, 0xb9, 0xcf, 0xf5, 0xd6, 0xcd, 0xd5, 0x0c, 0x7c, 0x0e, 0xe6, 0x90, 0x34, 0xfb, 0x51, 0x42, 0x1e, 0x6d, 0xac, 0x9a, 0x46, 0xc4, 0x97, 0x29, 0x32, 0xbf, 0x45}} , + {{0x66, 0x9e, 0xc6, 0x24, 0xc0, 0xed, 0xa5, 0x5d, 0x88, 0xd4, 0xf0, 0x73, 0x97, 0x7b, 0xea, 0x7f, 0x42, 0xff, 0x21, 0xa0, 0x9b, 0x2f, 0x9a, 0xfd, 0x53, 0x57, 0x07, 0x84, 0x48, 0x88, 0x9d, 0x52}}}, +{{{0xc6, 0x96, 0x48, 0x34, 0x2a, 0x06, 0xaf, 0x94, 0x3d, 0xf4, 0x1a, 0xcf, 0xf2, 0xc0, 0x21, 0xc2, 0x42, 0x5e, 0xc8, 0x2f, 0x35, 0xa2, 0x3e, 0x29, 0xfa, 0x0c, 0x84, 0xe5, 0x89, 0x72, 0x7c, 0x06}} , + {{0x32, 0x65, 0x03, 0xe5, 0x89, 0xa6, 0x6e, 0xb3, 0x5b, 0x8e, 0xca, 0xeb, 0xfe, 0x22, 0x56, 0x8b, 0x5d, 0x14, 0x4b, 0x4d, 0xf9, 0xbe, 0xb5, 0xf5, 0xe6, 0x5c, 0x7b, 0x8b, 0xf4, 0x13, 0x11, 0x34}}}, +{{{0x07, 0xc6, 0x22, 0x15, 0xe2, 0x9c, 0x60, 0xa2, 0x19, 0xd9, 0x27, 0xae, 0x37, 0x4e, 0xa6, 0xc9, 0x80, 0xa6, 0x91, 0x8f, 0x12, 0x49, 0xe5, 0x00, 0x18, 0x47, 0xd1, 0xd7, 0x28, 0x22, 0x63, 0x39}} , + {{0xe8, 0xe2, 0x00, 0x7e, 0xf2, 0x9e, 0x1e, 0x99, 0x39, 0x95, 0x04, 0xbd, 0x1e, 0x67, 0x7b, 0xb2, 0x26, 0xac, 0xe6, 0xaa, 0xe2, 0x46, 0xd5, 0xe4, 0xe8, 0x86, 0xbd, 0xab, 0x7c, 0x55, 0x59, 0x6f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x24, 0x64, 0x6e, 0x9b, 0x35, 0x71, 0x78, 0xce, 0x33, 0x03, 0x21, 0x33, 0x36, 0xf1, 0x73, 0x9b, 0xb9, 0x15, 0x8b, 0x2c, 0x69, 0xcf, 0x4d, 0xed, 0x4f, 0x4d, 0x57, 0x14, 0x13, 0x82, 0xa4, 0x4d}} , + {{0x65, 0x6e, 0x0a, 0xa4, 0x59, 0x07, 0x17, 0xf2, 0x6b, 0x4a, 0x1f, 0x6e, 0xf6, 0xb5, 0xbc, 0x62, 0xe4, 0xb6, 0xda, 0xa2, 0x93, 0xbc, 0x29, 0x05, 0xd2, 0xd2, 0x73, 0x46, 0x03, 0x16, 0x40, 0x31}}}, +{{{0x4c, 0x73, 0x6d, 0x15, 0xbd, 0xa1, 0x4d, 0x5c, 0x13, 0x0b, 0x24, 0x06, 0x98, 0x78, 0x1c, 0x5b, 0xeb, 0x1f, 0x18, 0x54, 0x43, 0xd9, 0x55, 0x66, 0xda, 0x29, 0x21, 0xe8, 0xb8, 0x3c, 0x42, 0x22}} , + {{0xb4, 0xcd, 0x08, 0x6f, 0x15, 0x23, 0x1a, 0x0b, 0x22, 0xed, 0xd1, 0xf1, 0xa7, 0xc7, 0x73, 0x45, 0xf3, 0x9e, 0xce, 0x76, 0xb7, 0xf6, 0x39, 0xb6, 0x8e, 0x79, 0xbe, 0xe9, 0x9b, 0xcf, 0x7d, 0x62}}}, +{{{0x92, 0x5b, 0xfc, 0x72, 0xfd, 0xba, 0xf1, 0xfd, 0xa6, 0x7c, 0x95, 0xe3, 0x61, 0x3f, 0xe9, 0x03, 0xd4, 0x2b, 0xd4, 0x20, 0xd9, 0xdb, 0x4d, 0x32, 0x3e, 0xf5, 0x11, 0x64, 0xe3, 0xb4, 0xbe, 0x32}} , + {{0x86, 0x17, 0x90, 0xe7, 0xc9, 0x1f, 0x10, 0xa5, 0x6a, 0x2d, 0x39, 0xd0, 0x3b, 0xc4, 0xa6, 0xe9, 0x59, 0x13, 0xda, 0x1a, 0xe6, 0xa0, 0xb9, 0x3c, 0x50, 0xb8, 0x40, 0x7c, 0x15, 0x36, 0x5a, 0x42}}}, +{{{0xb4, 0x0b, 0x32, 0xab, 0xdc, 0x04, 0x51, 0x55, 0x21, 0x1e, 0x0b, 0x75, 0x99, 0x89, 0x73, 0x35, 0x3a, 0x91, 0x2b, 0xfe, 0xe7, 0x49, 0xea, 0x76, 0xc1, 0xf9, 0x46, 0xb9, 0x53, 0x02, 0x23, 0x04}} , + {{0xfc, 0x5a, 0x1e, 0x1d, 0x74, 0x58, 0x95, 0xa6, 0x8f, 0x7b, 0x97, 0x3e, 0x17, 0x3b, 0x79, 0x2d, 0xa6, 0x57, 0xef, 0x45, 0x02, 0x0b, 0x4d, 0x6e, 0x9e, 0x93, 0x8d, 0x2f, 0xd9, 0x9d, 0xdb, 0x04}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xc0, 0xd7, 0x56, 0x97, 0x58, 0x91, 0xde, 0x09, 0x4f, 0x9f, 0xbe, 0x63, 0xb0, 0x83, 0x86, 0x43, 0x5d, 0xbc, 0xe0, 0xf3, 0xc0, 0x75, 0xbf, 0x8b, 0x8e, 0xaa, 0xf7, 0x8b, 0x64, 0x6e, 0xb0, 0x63}} , + {{0x16, 0xae, 0x8b, 0xe0, 0x9b, 0x24, 0x68, 0x5c, 0x44, 0xc2, 0xd0, 0x08, 0xb7, 0x7b, 0x62, 0xfd, 0x7f, 0xd8, 0xd4, 0xb7, 0x50, 0xfd, 0x2c, 0x1b, 0xbf, 0x41, 0x95, 0xd9, 0x8e, 0xd8, 0x17, 0x1b}}}, +{{{0x86, 0x55, 0x37, 0x8e, 0xc3, 0x38, 0x48, 0x14, 0xb5, 0x97, 0xd2, 0xa7, 0x54, 0x45, 0xf1, 0x35, 0x44, 0x38, 0x9e, 0xf1, 0x1b, 0xb6, 0x34, 0x00, 0x3c, 0x96, 0xee, 0x29, 0x00, 0xea, 0x2c, 0x0b}} , + {{0xea, 0xda, 0x99, 0x9e, 0x19, 0x83, 0x66, 0x6d, 0xe9, 0x76, 0x87, 0x50, 0xd1, 0xfd, 0x3c, 0x60, 0x87, 0xc6, 0x41, 0xd9, 0x8e, 0xdb, 0x5e, 0xde, 0xaa, 0x9a, 0xd3, 0x28, 0xda, 0x95, 0xea, 0x47}}}, +{{{0xd0, 0x80, 0xba, 0x19, 0xae, 0x1d, 0xa9, 0x79, 0xf6, 0x3f, 0xac, 0x5d, 0x6f, 0x96, 0x1f, 0x2a, 0xce, 0x29, 0xb2, 0xff, 0x37, 0xf1, 0x94, 0x8f, 0x0c, 0xb5, 0x28, 0xba, 0x9a, 0x21, 0xf6, 0x66}} , + {{0x02, 0xfb, 0x54, 0xb8, 0x05, 0xf3, 0x81, 0x52, 0x69, 0x34, 0x46, 0x9d, 0x86, 0x76, 0x8f, 0xd7, 0xf8, 0x6a, 0x66, 0xff, 0xe6, 0xa7, 0x90, 0xf7, 0x5e, 0xcd, 0x6a, 0x9b, 0x55, 0xfc, 0x9d, 0x48}}}, +{{{0xbd, 0xaa, 0x13, 0xe6, 0xcd, 0x45, 0x4a, 0xa4, 0x59, 0x0a, 0x64, 0xb1, 0x98, 0xd6, 0x34, 0x13, 0x04, 0xe6, 0x97, 0x94, 0x06, 0xcb, 0xd4, 0x4e, 0xbb, 0x96, 0xcd, 0xd1, 0x57, 0xd1, 0xe3, 0x06}} , + {{0x7a, 0x6c, 0x45, 0x27, 0xc4, 0x93, 0x7f, 0x7d, 0x7c, 0x62, 0x50, 0x38, 0x3a, 0x6b, 0xb5, 0x88, 0xc6, 0xd9, 0xf1, 0x78, 0x19, 0xb9, 0x39, 0x93, 0x3d, 0xc9, 0xe0, 0x9c, 0x3c, 0xce, 0xf5, 0x72}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x24, 0xea, 0x23, 0x7d, 0x56, 0x2c, 0xe2, 0x59, 0x0e, 0x85, 0x60, 0x04, 0x88, 0x5a, 0x74, 0x1e, 0x4b, 0xef, 0x13, 0xda, 0x4c, 0xff, 0x83, 0x45, 0x85, 0x3f, 0x08, 0x95, 0x2c, 0x20, 0x13, 0x1f}} , + {{0x48, 0x5f, 0x27, 0x90, 0x5c, 0x02, 0x42, 0xad, 0x78, 0x47, 0x5c, 0xb5, 0x7e, 0x08, 0x85, 0x00, 0xfa, 0x7f, 0xfd, 0xfd, 0xe7, 0x09, 0x11, 0xf2, 0x7e, 0x1b, 0x38, 0x6c, 0x35, 0x6d, 0x33, 0x66}}}, +{{{0x93, 0x03, 0x36, 0x81, 0xac, 0xe4, 0x20, 0x09, 0x35, 0x4c, 0x45, 0xb2, 0x1e, 0x4c, 0x14, 0x21, 0xe6, 0xe9, 0x8a, 0x7b, 0x8d, 0xfe, 0x1e, 0xc6, 0x3e, 0xc1, 0x35, 0xfa, 0xe7, 0x70, 0x4e, 0x1d}} , + {{0x61, 0x2e, 0xc2, 0xdd, 0x95, 0x57, 0xd1, 0xab, 0x80, 0xe8, 0x63, 0x17, 0xb5, 0x48, 0xe4, 0x8a, 0x11, 0x9e, 0x72, 0xbe, 0x85, 0x8d, 0x51, 0x0a, 0xf2, 0x9f, 0xe0, 0x1c, 0xa9, 0x07, 0x28, 0x7b}}}, +{{{0xbb, 0x71, 0x14, 0x5e, 0x26, 0x8c, 0x3d, 0xc8, 0xe9, 0x7c, 0xd3, 0xd6, 0xd1, 0x2f, 0x07, 0x6d, 0xe6, 0xdf, 0xfb, 0x79, 0xd6, 0x99, 0x59, 0x96, 0x48, 0x40, 0x0f, 0x3a, 0x7b, 0xb2, 0xa0, 0x72}} , + {{0x4e, 0x3b, 0x69, 0xc8, 0x43, 0x75, 0x51, 0x6c, 0x79, 0x56, 0xe4, 0xcb, 0xf7, 0xa6, 0x51, 0xc2, 0x2c, 0x42, 0x0b, 0xd4, 0x82, 0x20, 0x1c, 0x01, 0x08, 0x66, 0xd7, 0xbf, 0x04, 0x56, 0xfc, 0x02}}}, +{{{0x24, 0xe8, 0xb7, 0x60, 0xae, 0x47, 0x80, 0xfc, 0xe5, 0x23, 0xe7, 0xc2, 0xc9, 0x85, 0xe6, 0x98, 0xa0, 0x29, 0x4e, 0xe1, 0x84, 0x39, 0x2d, 0x95, 0x2c, 0xf3, 0x45, 0x3c, 0xff, 0xaf, 0x27, 0x4c}} , + {{0x6b, 0xa6, 0xf5, 0x4b, 0x11, 0xbd, 0xba, 0x5b, 0x9e, 0xc4, 0xa4, 0x51, 0x1e, 0xbe, 0xd0, 0x90, 0x3a, 0x9c, 0xc2, 0x26, 0xb6, 0x1e, 0xf1, 0x95, 0x7d, 0xc8, 0x6d, 0x52, 0xe6, 0x99, 0x2c, 0x5f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x85, 0xe0, 0x24, 0x32, 0xb4, 0xd1, 0xef, 0xfc, 0x69, 0xa2, 0xbf, 0x8f, 0x72, 0x2c, 0x95, 0xf6, 0xe4, 0x6e, 0x7d, 0x90, 0xf7, 0x57, 0x81, 0xa0, 0xf7, 0xda, 0xef, 0x33, 0x07, 0xe3, 0x6b, 0x78}} , + {{0x36, 0x27, 0x3e, 0xc6, 0x12, 0x07, 0xab, 0x4e, 0xbe, 0x69, 0x9d, 0xb3, 0xbe, 0x08, 0x7c, 0x2a, 0x47, 0x08, 0xfd, 0xd4, 0xcd, 0x0e, 0x27, 0x34, 0x5b, 0x98, 0x34, 0x2f, 0x77, 0x5f, 0x3a, 0x65}}}, +{{{0x13, 0xaa, 0x2e, 0x4c, 0xf0, 0x22, 0xb8, 0x6c, 0xb3, 0x19, 0x4d, 0xeb, 0x6b, 0xd0, 0xa4, 0xc6, 0x9c, 0xdd, 0xc8, 0x5b, 0x81, 0x57, 0x89, 0xdf, 0x33, 0xa9, 0x68, 0x49, 0x80, 0xe4, 0xfe, 0x21}} , + {{0x00, 0x17, 0x90, 0x30, 0xe9, 0xd3, 0x60, 0x30, 0x31, 0xc2, 0x72, 0x89, 0x7a, 0x36, 0xa5, 0xbd, 0x39, 0x83, 0x85, 0x50, 0xa1, 0x5d, 0x6c, 0x41, 0x1d, 0xb5, 0x2c, 0x07, 0x40, 0x77, 0x0b, 0x50}}}, +{{{0x64, 0x34, 0xec, 0xc0, 0x9e, 0x44, 0x41, 0xaf, 0xa0, 0x36, 0x05, 0x6d, 0xea, 0x30, 0x25, 0x46, 0x35, 0x24, 0x9d, 0x86, 0xbd, 0x95, 0xf1, 0x6a, 0x46, 0xd7, 0x94, 0x54, 0xf9, 0x3b, 0xbd, 0x5d}} , + {{0x77, 0x5b, 0xe2, 0x37, 0xc7, 0xe1, 0x7c, 0x13, 0x8c, 0x9f, 0x7b, 0x7b, 0x2a, 0xce, 0x42, 0xa3, 0xb9, 0x2a, 0x99, 0xa8, 0xc0, 0xd8, 0x3c, 0x86, 0xb0, 0xfb, 0xe9, 0x76, 0x77, 0xf7, 0xf5, 0x56}}}, +{{{0xdf, 0xb3, 0x46, 0x11, 0x6e, 0x13, 0xb7, 0x28, 0x4e, 0x56, 0xdd, 0xf1, 0xac, 0xad, 0x58, 0xc3, 0xf8, 0x88, 0x94, 0x5e, 0x06, 0x98, 0xa1, 0xe4, 0x6a, 0xfb, 0x0a, 0x49, 0x5d, 0x8a, 0xfe, 0x77}} , + {{0x46, 0x02, 0xf5, 0xa5, 0xaf, 0xc5, 0x75, 0x6d, 0xba, 0x45, 0x35, 0x0a, 0xfe, 0xc9, 0xac, 0x22, 0x91, 0x8d, 0x21, 0x95, 0x33, 0x03, 0xc0, 0x8a, 0x16, 0xf3, 0x39, 0xe0, 0x01, 0x0f, 0x53, 0x3c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x34, 0x75, 0x37, 0x1f, 0x34, 0x4e, 0xa9, 0x1d, 0x68, 0x67, 0xf8, 0x49, 0x98, 0x96, 0xfc, 0x4c, 0x65, 0x97, 0xf7, 0x02, 0x4a, 0x52, 0x6c, 0x01, 0xbd, 0x48, 0xbb, 0x1b, 0xed, 0xa4, 0xe2, 0x53}} , + {{0x59, 0xd5, 0x9b, 0x5a, 0xa2, 0x90, 0xd3, 0xb8, 0x37, 0x4c, 0x55, 0x82, 0x28, 0x08, 0x0f, 0x7f, 0xaa, 0x81, 0x65, 0xe0, 0x0c, 0x52, 0xc9, 0xa3, 0x32, 0x27, 0x64, 0xda, 0xfd, 0x34, 0x23, 0x5a}}}, +{{{0xb5, 0xb0, 0x0c, 0x4d, 0xb3, 0x7b, 0x23, 0xc8, 0x1f, 0x8a, 0x39, 0x66, 0xe6, 0xba, 0x4c, 0x10, 0x37, 0xca, 0x9c, 0x7c, 0x05, 0x9e, 0xff, 0xc0, 0xf8, 0x8e, 0xb1, 0x8f, 0x6f, 0x67, 0x18, 0x26}} , + {{0x4b, 0x41, 0x13, 0x54, 0x23, 0x1a, 0xa4, 0x4e, 0xa9, 0x8b, 0x1e, 0x4b, 0xfc, 0x15, 0x24, 0xbb, 0x7e, 0xcb, 0xb6, 0x1e, 0x1b, 0xf5, 0xf2, 0xc8, 0x56, 0xec, 0x32, 0xa2, 0x60, 0x5b, 0xa0, 0x2a}}}, +{{{0xa4, 0x29, 0x47, 0x86, 0x2e, 0x92, 0x4f, 0x11, 0x4f, 0xf3, 0xb2, 0x5c, 0xd5, 0x3e, 0xa6, 0xb9, 0xc8, 0xe2, 0x33, 0x11, 0x1f, 0x01, 0x8f, 0xb0, 0x9b, 0xc7, 0xa5, 0xff, 0x83, 0x0f, 0x1e, 0x28}} , + {{0x1d, 0x29, 0x7a, 0xa1, 0xec, 0x8e, 0xb5, 0xad, 0xea, 0x02, 0x68, 0x60, 0x74, 0x29, 0x1c, 0xa5, 0xcf, 0xc8, 0x3b, 0x7d, 0x8b, 0x2b, 0x7c, 0xad, 0xa4, 0x40, 0x17, 0x51, 0x59, 0x7c, 0x2e, 0x5d}}}, +{{{0x0a, 0x6c, 0x4f, 0xbc, 0x3e, 0x32, 0xe7, 0x4a, 0x1a, 0x13, 0xc1, 0x49, 0x38, 0xbf, 0xf7, 0xc2, 0xd3, 0x8f, 0x6b, 0xad, 0x52, 0xf7, 0xcf, 0xbc, 0x27, 0xcb, 0x40, 0x67, 0x76, 0xcd, 0x6d, 0x56}} , + {{0xe5, 0xb0, 0x27, 0xad, 0xbe, 0x9b, 0xf2, 0xb5, 0x63, 0xde, 0x3a, 0x23, 0x95, 0xb7, 0x0a, 0x7e, 0xf3, 0x9e, 0x45, 0x6f, 0x19, 0x39, 0x75, 0x8f, 0x39, 0x3d, 0x0f, 0xc0, 0x9f, 0xf1, 0xe9, 0x51}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x88, 0xaa, 0x14, 0x24, 0x86, 0x94, 0x11, 0x12, 0x3e, 0x1a, 0xb5, 0xcc, 0xbb, 0xe0, 0x9c, 0xd5, 0x9c, 0x6d, 0xba, 0x58, 0x72, 0x8d, 0xfb, 0x22, 0x7b, 0x9f, 0x7c, 0x94, 0x30, 0xb3, 0x51, 0x21}} , + {{0xf6, 0x74, 0x3d, 0xf2, 0xaf, 0xd0, 0x1e, 0x03, 0x7c, 0x23, 0x6b, 0xc9, 0xfc, 0x25, 0x70, 0x90, 0xdc, 0x9a, 0xa4, 0xfb, 0x49, 0xfc, 0x3d, 0x0a, 0x35, 0x38, 0x6f, 0xe4, 0x7e, 0x50, 0x01, 0x2a}}}, +{{{0xd6, 0xe3, 0x96, 0x61, 0x3a, 0xfd, 0xef, 0x9b, 0x1f, 0x90, 0xa4, 0x24, 0x14, 0x5b, 0xc8, 0xde, 0x50, 0xb1, 0x1d, 0xaf, 0xe8, 0x55, 0x8a, 0x87, 0x0d, 0xfe, 0xaa, 0x3b, 0x82, 0x2c, 0x8d, 0x7b}} , + {{0x85, 0x0c, 0xaf, 0xf8, 0x83, 0x44, 0x49, 0xd9, 0x45, 0xcf, 0xf7, 0x48, 0xd9, 0x53, 0xb4, 0xf1, 0x65, 0xa0, 0xe1, 0xc3, 0xb3, 0x15, 0xed, 0x89, 0x9b, 0x4f, 0x62, 0xb3, 0x57, 0xa5, 0x45, 0x1c}}}, +{{{0x8f, 0x12, 0xea, 0xaf, 0xd1, 0x1f, 0x79, 0x10, 0x0b, 0xf6, 0xa3, 0x7b, 0xea, 0xac, 0x8b, 0x57, 0x32, 0x62, 0xe7, 0x06, 0x12, 0x51, 0xa0, 0x3b, 0x43, 0x5e, 0xa4, 0x20, 0x78, 0x31, 0xce, 0x0d}} , + {{0x84, 0x7c, 0xc2, 0xa6, 0x91, 0x23, 0xce, 0xbd, 0xdc, 0xf9, 0xce, 0xd5, 0x75, 0x30, 0x22, 0xe6, 0xf9, 0x43, 0x62, 0x0d, 0xf7, 0x75, 0x9d, 0x7f, 0x8c, 0xff, 0x7d, 0xe4, 0x72, 0xac, 0x9f, 0x1c}}}, +{{{0x88, 0xc1, 0x99, 0xd0, 0x3c, 0x1c, 0x5d, 0xb4, 0xef, 0x13, 0x0f, 0x90, 0xb9, 0x36, 0x2f, 0x95, 0x95, 0xc6, 0xdc, 0xde, 0x0a, 0x51, 0xe2, 0x8d, 0xf3, 0xbc, 0x51, 0xec, 0xdf, 0xb1, 0xa2, 0x5f}} , + {{0x2e, 0x68, 0xa1, 0x23, 0x7d, 0x9b, 0x40, 0x69, 0x85, 0x7b, 0x42, 0xbf, 0x90, 0x4b, 0xd6, 0x40, 0x2f, 0xd7, 0x52, 0x52, 0xb2, 0x21, 0xde, 0x64, 0xbd, 0x88, 0xc3, 0x6d, 0xa5, 0xfa, 0x81, 0x3f}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xfb, 0xfd, 0x47, 0x7b, 0x8a, 0x66, 0x9e, 0x79, 0x2e, 0x64, 0x82, 0xef, 0xf7, 0x21, 0xec, 0xf6, 0xd8, 0x86, 0x09, 0x31, 0x7c, 0xdd, 0x03, 0x6a, 0x58, 0xa0, 0x77, 0xb7, 0x9b, 0x8c, 0x87, 0x1f}} , + {{0x55, 0x47, 0xe4, 0xa8, 0x3d, 0x55, 0x21, 0x34, 0xab, 0x1d, 0xae, 0xe0, 0xf4, 0xea, 0xdb, 0xc5, 0xb9, 0x58, 0xbf, 0xc4, 0x2a, 0x89, 0x31, 0x1a, 0xf4, 0x2d, 0xe1, 0xca, 0x37, 0x99, 0x47, 0x59}}}, +{{{0xc7, 0xca, 0x63, 0xc1, 0x49, 0xa9, 0x35, 0x45, 0x55, 0x7e, 0xda, 0x64, 0x32, 0x07, 0x50, 0xf7, 0x32, 0xac, 0xde, 0x75, 0x58, 0x9b, 0x11, 0xb2, 0x3a, 0x1f, 0xf5, 0xf7, 0x79, 0x04, 0xe6, 0x08}} , + {{0x46, 0xfa, 0x22, 0x4b, 0xfa, 0xe1, 0xfe, 0x96, 0xfc, 0x67, 0xba, 0x67, 0x97, 0xc4, 0xe7, 0x1b, 0x86, 0x90, 0x5f, 0xee, 0xf4, 0x5b, 0x11, 0xb2, 0xcd, 0xad, 0xee, 0xc2, 0x48, 0x6c, 0x2b, 0x1b}}}, +{{{0xe3, 0x39, 0x62, 0xb4, 0x4f, 0x31, 0x04, 0xc9, 0xda, 0xd5, 0x73, 0x51, 0x57, 0xc5, 0xb8, 0xf3, 0xa3, 0x43, 0x70, 0xe4, 0x61, 0x81, 0x84, 0xe2, 0xbb, 0xbf, 0x4f, 0x9e, 0xa4, 0x5e, 0x74, 0x06}} , + {{0x29, 0xac, 0xff, 0x27, 0xe0, 0x59, 0xbe, 0x39, 0x9c, 0x0d, 0x83, 0xd7, 0x10, 0x0b, 0x15, 0xb7, 0xe1, 0xc2, 0x2c, 0x30, 0x73, 0x80, 0x3a, 0x7d, 0x5d, 0xab, 0x58, 0x6b, 0xc1, 0xf0, 0xf4, 0x22}}}, +{{{0xfe, 0x7f, 0xfb, 0x35, 0x7d, 0xc6, 0x01, 0x23, 0x28, 0xc4, 0x02, 0xac, 0x1f, 0x42, 0xb4, 0x9d, 0xfc, 0x00, 0x94, 0xa5, 0xee, 0xca, 0xda, 0x97, 0x09, 0x41, 0x77, 0x87, 0x5d, 0x7b, 0x87, 0x78}} , + {{0xf5, 0xfb, 0x90, 0x2d, 0x81, 0x19, 0x9e, 0x2f, 0x6d, 0x85, 0x88, 0x8c, 0x40, 0x5c, 0x77, 0x41, 0x4d, 0x01, 0x19, 0x76, 0x60, 0xe8, 0x4c, 0x48, 0xe4, 0x33, 0x83, 0x32, 0x6c, 0xb4, 0x41, 0x03}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xff, 0x10, 0xc2, 0x09, 0x4f, 0x6e, 0xf4, 0xd2, 0xdf, 0x7e, 0xca, 0x7b, 0x1c, 0x1d, 0xba, 0xa3, 0xb6, 0xda, 0x67, 0x33, 0xd4, 0x87, 0x36, 0x4b, 0x11, 0x20, 0x05, 0xa6, 0x29, 0xc1, 0x87, 0x17}} , + {{0xf6, 0x96, 0xca, 0x2f, 0xda, 0x38, 0xa7, 0x1b, 0xfc, 0xca, 0x7d, 0xfe, 0x08, 0x89, 0xe2, 0x47, 0x2b, 0x6a, 0x5d, 0x4b, 0xfa, 0xa1, 0xb4, 0xde, 0xb6, 0xc2, 0x31, 0x51, 0xf5, 0xe0, 0xa4, 0x0b}}}, +{{{0x5c, 0xe5, 0xc6, 0x04, 0x8e, 0x2b, 0x57, 0xbe, 0x38, 0x85, 0x23, 0xcb, 0xb7, 0xbe, 0x4f, 0xa9, 0xd3, 0x6e, 0x12, 0xaa, 0xd5, 0xb2, 0x2e, 0x93, 0x29, 0x9a, 0x4a, 0x88, 0x18, 0x43, 0xf5, 0x01}} , + {{0x50, 0xfc, 0xdb, 0xa2, 0x59, 0x21, 0x8d, 0xbd, 0x7e, 0x33, 0xae, 0x2f, 0x87, 0x1a, 0xd0, 0x97, 0xc7, 0x0d, 0x4d, 0x63, 0x01, 0xef, 0x05, 0x84, 0xec, 0x40, 0xdd, 0xa8, 0x0a, 0x4f, 0x70, 0x0b}}}, +{{{0x41, 0x69, 0x01, 0x67, 0x5c, 0xd3, 0x8a, 0xc5, 0xcf, 0x3f, 0xd1, 0x57, 0xd1, 0x67, 0x3e, 0x01, 0x39, 0xb5, 0xcb, 0x81, 0x56, 0x96, 0x26, 0xb6, 0xc2, 0xe7, 0x5c, 0xfb, 0x63, 0x97, 0x58, 0x06}} , + {{0x0c, 0x0e, 0xf3, 0xba, 0xf0, 0xe5, 0xba, 0xb2, 0x57, 0x77, 0xc6, 0x20, 0x9b, 0x89, 0x24, 0xbe, 0xf2, 0x9c, 0x8a, 0xba, 0x69, 0xc1, 0xf1, 0xb0, 0x4f, 0x2a, 0x05, 0x9a, 0xee, 0x10, 0x7e, 0x36}}}, +{{{0x3f, 0x26, 0xe9, 0x40, 0xe9, 0x03, 0xad, 0x06, 0x69, 0x91, 0xe0, 0xd1, 0x89, 0x60, 0x84, 0x79, 0xde, 0x27, 0x6d, 0xe6, 0x76, 0xbd, 0xea, 0xe6, 0xae, 0x48, 0xc3, 0x67, 0xc0, 0x57, 0xcd, 0x2f}} , + {{0x7f, 0xc1, 0xdc, 0xb9, 0xc7, 0xbc, 0x86, 0x3d, 0x55, 0x4b, 0x28, 0x7a, 0xfb, 0x4d, 0xc7, 0xf8, 0xbc, 0x67, 0x2a, 0x60, 0x4d, 0x8f, 0x07, 0x0b, 0x1a, 0x17, 0xbf, 0xfa, 0xac, 0xa7, 0x3d, 0x1a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x91, 0x3f, 0xed, 0x5e, 0x18, 0x78, 0x3f, 0x23, 0x2c, 0x0d, 0x8c, 0x44, 0x00, 0xe8, 0xfb, 0xe9, 0x8e, 0xd6, 0xd1, 0x36, 0x58, 0x57, 0x9e, 0xae, 0x4b, 0x5c, 0x0b, 0x07, 0xbc, 0x6b, 0x55, 0x2b}} , + {{0x6f, 0x4d, 0x17, 0xd7, 0xe1, 0x84, 0xd9, 0x78, 0xb1, 0x90, 0xfd, 0x2e, 0xb3, 0xb5, 0x19, 0x3f, 0x1b, 0xfa, 0xc0, 0x68, 0xb3, 0xdd, 0x00, 0x2e, 0x89, 0xbd, 0x7e, 0x80, 0x32, 0x13, 0xa0, 0x7b}}}, +{{{0x1a, 0x6f, 0x40, 0xaf, 0x44, 0x44, 0xb0, 0x43, 0x8f, 0x0d, 0xd0, 0x1e, 0xc4, 0x0b, 0x19, 0x5d, 0x8e, 0xfe, 0xc1, 0xf3, 0xc5, 0x5c, 0x91, 0xf8, 0x04, 0x4e, 0xbe, 0x90, 0xb4, 0x47, 0x5c, 0x3f}} , + {{0xb0, 0x3b, 0x2c, 0xf3, 0xfe, 0x32, 0x71, 0x07, 0x3f, 0xaa, 0xba, 0x45, 0x60, 0xa8, 0x8d, 0xea, 0x54, 0xcb, 0x39, 0x10, 0xb4, 0xf2, 0x8b, 0xd2, 0x14, 0x82, 0x42, 0x07, 0x8e, 0xe9, 0x7c, 0x53}}}, +{{{0xb0, 0xae, 0xc1, 0x8d, 0xc9, 0x8f, 0xb9, 0x7a, 0x77, 0xef, 0xba, 0x79, 0xa0, 0x3c, 0xa8, 0xf5, 0x6a, 0xe2, 0x3f, 0x5d, 0x00, 0xe3, 0x4b, 0x45, 0x24, 0x7b, 0x43, 0x78, 0x55, 0x1d, 0x2b, 0x1e}} , + {{0x01, 0xb8, 0xd6, 0x16, 0x67, 0xa0, 0x15, 0xb9, 0xe1, 0x58, 0xa4, 0xa7, 0x31, 0x37, 0x77, 0x2f, 0x8b, 0x12, 0x9f, 0xf4, 0x3f, 0xc7, 0x36, 0x66, 0xd2, 0xa8, 0x56, 0xf7, 0x7f, 0x74, 0xc6, 0x41}}}, +{{{0x5d, 0xf8, 0xb4, 0xa8, 0x30, 0xdd, 0xcc, 0x38, 0xa5, 0xd3, 0xca, 0xd8, 0xd1, 0xf8, 0xb2, 0x31, 0x91, 0xd4, 0x72, 0x05, 0x57, 0x4a, 0x3b, 0x82, 0x4a, 0xc6, 0x68, 0x20, 0xe2, 0x18, 0x41, 0x61}} , + {{0x19, 0xd4, 0x8d, 0x47, 0x29, 0x12, 0x65, 0xb0, 0x11, 0x78, 0x47, 0xb5, 0xcb, 0xa3, 0xa5, 0xfa, 0x05, 0x85, 0x54, 0xa9, 0x33, 0x97, 0x8d, 0x2b, 0xc2, 0xfe, 0x99, 0x35, 0x28, 0xe5, 0xeb, 0x63}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xb1, 0x3f, 0x3f, 0xef, 0xd8, 0xf4, 0xfc, 0xb3, 0xa0, 0x60, 0x50, 0x06, 0x2b, 0x29, 0x52, 0x70, 0x15, 0x0b, 0x24, 0x24, 0xf8, 0x5f, 0x79, 0x18, 0xcc, 0xff, 0x89, 0x99, 0x84, 0xa1, 0xae, 0x13}} , + {{0x44, 0x1f, 0xb8, 0xc2, 0x01, 0xc1, 0x30, 0x19, 0x55, 0x05, 0x60, 0x10, 0xa4, 0x6c, 0x2d, 0x67, 0x70, 0xe5, 0x25, 0x1b, 0xf2, 0xbf, 0xdd, 0xfb, 0x70, 0x2b, 0xa1, 0x8c, 0x9c, 0x94, 0x84, 0x08}}}, +{{{0xe7, 0xc4, 0x43, 0x4d, 0xc9, 0x2b, 0x69, 0x5d, 0x1d, 0x3c, 0xaf, 0xbb, 0x43, 0x38, 0x4e, 0x98, 0x3d, 0xed, 0x0d, 0x21, 0x03, 0xfd, 0xf0, 0x99, 0x47, 0x04, 0xb0, 0x98, 0x69, 0x55, 0x72, 0x0f}} , + {{0x5e, 0xdf, 0x15, 0x53, 0x3b, 0x86, 0x80, 0xb0, 0xf1, 0x70, 0x68, 0x8f, 0x66, 0x7c, 0x0e, 0x49, 0x1a, 0xd8, 0x6b, 0xfe, 0x4e, 0xef, 0xca, 0x47, 0xd4, 0x03, 0xc1, 0x37, 0x50, 0x9c, 0xc1, 0x16}}}, +{{{0xcd, 0x24, 0xc6, 0x3e, 0x0c, 0x82, 0x9b, 0x91, 0x2b, 0x61, 0x4a, 0xb2, 0x0f, 0x88, 0x55, 0x5f, 0x5a, 0x57, 0xff, 0xe5, 0x74, 0x0b, 0x13, 0x43, 0x00, 0xd8, 0x6b, 0xcf, 0xd2, 0x15, 0x03, 0x2c}} , + {{0xdc, 0xff, 0x15, 0x61, 0x2f, 0x4a, 0x2f, 0x62, 0xf2, 0x04, 0x2f, 0xb5, 0x0c, 0xb7, 0x1e, 0x3f, 0x74, 0x1a, 0x0f, 0xd7, 0xea, 0xcd, 0xd9, 0x7d, 0xf6, 0x12, 0x0e, 0x2f, 0xdb, 0x5a, 0x3b, 0x16}}}, +{{{0x1b, 0x37, 0x47, 0xe3, 0xf5, 0x9e, 0xea, 0x2c, 0x2a, 0xe7, 0x82, 0x36, 0xf4, 0x1f, 0x81, 0x47, 0x92, 0x4b, 0x69, 0x0e, 0x11, 0x8c, 0x5d, 0x53, 0x5b, 0x81, 0x27, 0x08, 0xbc, 0xa0, 0xae, 0x25}} , + {{0x69, 0x32, 0xa1, 0x05, 0x11, 0x42, 0x00, 0xd2, 0x59, 0xac, 0x4d, 0x62, 0x8b, 0x13, 0xe2, 0x50, 0x5d, 0xa0, 0x9d, 0x9b, 0xfd, 0xbb, 0x12, 0x41, 0x75, 0x41, 0x9e, 0xcc, 0xdc, 0xc7, 0xdc, 0x5d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xd9, 0xe3, 0x38, 0x06, 0x46, 0x70, 0x82, 0x5e, 0x28, 0x49, 0x79, 0xff, 0x25, 0xd2, 0x4e, 0x29, 0x8d, 0x06, 0xb0, 0x23, 0xae, 0x9b, 0x66, 0xe4, 0x7d, 0xc0, 0x70, 0x91, 0xa3, 0xfc, 0xec, 0x4e}} , + {{0x62, 0x12, 0x37, 0x6a, 0x30, 0xf6, 0x1e, 0xfb, 0x14, 0x5c, 0x0d, 0x0e, 0xb7, 0x81, 0x6a, 0xe7, 0x08, 0x05, 0xac, 0xaa, 0x38, 0x46, 0xe2, 0x73, 0xea, 0x4b, 0x07, 0x81, 0x43, 0x7c, 0x9e, 0x5e}}}, +{{{0xfc, 0xf9, 0x21, 0x4f, 0x2e, 0x76, 0x9b, 0x1f, 0x28, 0x60, 0x77, 0x43, 0x32, 0x9d, 0xbe, 0x17, 0x30, 0x2a, 0xc6, 0x18, 0x92, 0x66, 0x62, 0x30, 0x98, 0x40, 0x11, 0xa6, 0x7f, 0x18, 0x84, 0x28}} , + {{0x3f, 0xab, 0xd3, 0xf4, 0x8a, 0x76, 0xa1, 0x3c, 0xca, 0x2d, 0x49, 0xc3, 0xea, 0x08, 0x0b, 0x85, 0x17, 0x2a, 0xc3, 0x6c, 0x08, 0xfd, 0x57, 0x9f, 0x3d, 0x5f, 0xdf, 0x67, 0x68, 0x42, 0x00, 0x32}}}, +{{{0x51, 0x60, 0x1b, 0x06, 0x4f, 0x8a, 0x21, 0xba, 0x38, 0xa8, 0xba, 0xd6, 0x40, 0xf6, 0xe9, 0x9b, 0x76, 0x4d, 0x56, 0x21, 0x5b, 0x0a, 0x9b, 0x2e, 0x4f, 0x3d, 0x81, 0x32, 0x08, 0x9f, 0x97, 0x5b}} , + {{0xe5, 0x44, 0xec, 0x06, 0x9d, 0x90, 0x79, 0x9f, 0xd3, 0xe0, 0x79, 0xaf, 0x8f, 0x10, 0xfd, 0xdd, 0x04, 0xae, 0x27, 0x97, 0x46, 0x33, 0x79, 0xea, 0xb8, 0x4e, 0xca, 0x5a, 0x59, 0x57, 0xe1, 0x0e}}}, +{{{0x1a, 0xda, 0xf3, 0xa5, 0x41, 0x43, 0x28, 0xfc, 0x7e, 0xe7, 0x71, 0xea, 0xc6, 0x3b, 0x59, 0xcc, 0x2e, 0xd3, 0x40, 0xec, 0xb3, 0x13, 0x6f, 0x44, 0xcd, 0x13, 0xb2, 0x37, 0xf2, 0x6e, 0xd9, 0x1c}} , + {{0xe3, 0xdb, 0x60, 0xcd, 0x5c, 0x4a, 0x18, 0x0f, 0xef, 0x73, 0x36, 0x71, 0x8c, 0xf6, 0x11, 0xb4, 0xd8, 0xce, 0x17, 0x5e, 0x4f, 0x26, 0x77, 0x97, 0x5f, 0xcb, 0xef, 0x91, 0xeb, 0x6a, 0x62, 0x7a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x18, 0x4a, 0xa2, 0x97, 0x08, 0x81, 0x2d, 0x83, 0xc4, 0xcc, 0xf0, 0x83, 0x7e, 0xec, 0x0d, 0x95, 0x4c, 0x5b, 0xfb, 0xfa, 0x98, 0x80, 0x4a, 0x66, 0x56, 0x0c, 0x51, 0xb3, 0xf2, 0x04, 0x5d, 0x27}} , + {{0x3b, 0xb9, 0xb8, 0x06, 0x5a, 0x2e, 0xfe, 0xc3, 0x82, 0x37, 0x9c, 0xa3, 0x11, 0x1f, 0x9c, 0xa6, 0xda, 0x63, 0x48, 0x9b, 0xad, 0xde, 0x2d, 0xa6, 0xbc, 0x6e, 0x32, 0xda, 0x27, 0x65, 0xdd, 0x57}}}, +{{{0x84, 0x4f, 0x37, 0x31, 0x7d, 0x2e, 0xbc, 0xad, 0x87, 0x07, 0x2a, 0x6b, 0x37, 0xfc, 0x5f, 0xeb, 0x4e, 0x75, 0x35, 0xa6, 0xde, 0xab, 0x0a, 0x19, 0x3a, 0xb7, 0xb1, 0xef, 0x92, 0x6a, 0x3b, 0x3c}} , + {{0x3b, 0xb2, 0x94, 0x6d, 0x39, 0x60, 0xac, 0xee, 0xe7, 0x81, 0x1a, 0x3b, 0x76, 0x87, 0x5c, 0x05, 0x94, 0x2a, 0x45, 0xb9, 0x80, 0xe9, 0x22, 0xb1, 0x07, 0xcb, 0x40, 0x9e, 0x70, 0x49, 0x6d, 0x12}}}, +{{{0xfd, 0x18, 0x78, 0x84, 0xa8, 0x4c, 0x7d, 0x6e, 0x59, 0xa6, 0xe5, 0x74, 0xf1, 0x19, 0xa6, 0x84, 0x2e, 0x51, 0xc1, 0x29, 0x13, 0xf2, 0x14, 0x6b, 0x5d, 0x53, 0x51, 0xf7, 0xef, 0xbf, 0x01, 0x22}} , + {{0xa4, 0x4b, 0x62, 0x4c, 0xe6, 0xfd, 0x72, 0x07, 0xf2, 0x81, 0xfc, 0xf2, 0xbd, 0x12, 0x7c, 0x68, 0x76, 0x2a, 0xba, 0xf5, 0x65, 0xb1, 0x1f, 0x17, 0x0a, 0x38, 0xb0, 0xbf, 0xc0, 0xf8, 0xf4, 0x2a}}}, +{{{0x55, 0x60, 0x55, 0x5b, 0xe4, 0x1d, 0x71, 0x4c, 0x9d, 0x5b, 0x9f, 0x70, 0xa6, 0x85, 0x9a, 0x2c, 0xa0, 0xe2, 0x32, 0x48, 0xce, 0x9e, 0x2a, 0xa5, 0x07, 0x3b, 0xc7, 0x6c, 0x86, 0x77, 0xde, 0x3c}} , + {{0xf7, 0x18, 0x7a, 0x96, 0x7e, 0x43, 0x57, 0xa9, 0x55, 0xfc, 0x4e, 0xb6, 0x72, 0x00, 0xf2, 0xe4, 0xd7, 0x52, 0xd3, 0xd3, 0xb6, 0x85, 0xf6, 0x71, 0xc7, 0x44, 0x3f, 0x7f, 0xd7, 0xb3, 0xf2, 0x79}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x46, 0xca, 0xa7, 0x55, 0x7b, 0x79, 0xf3, 0xca, 0x5a, 0x65, 0xf6, 0xed, 0x50, 0x14, 0x7b, 0xe4, 0xc4, 0x2a, 0x65, 0x9e, 0xe2, 0xf9, 0xca, 0xa7, 0x22, 0x26, 0x53, 0xcb, 0x21, 0x5b, 0xa7, 0x31}} , + {{0x90, 0xd7, 0xc5, 0x26, 0x08, 0xbd, 0xb0, 0x53, 0x63, 0x58, 0xc3, 0x31, 0x5e, 0x75, 0x46, 0x15, 0x91, 0xa6, 0xf8, 0x2f, 0x1a, 0x08, 0x65, 0x88, 0x2f, 0x98, 0x04, 0xf1, 0x7c, 0x6e, 0x00, 0x77}}}, +{{{0x81, 0x21, 0x61, 0x09, 0xf6, 0x4e, 0xf1, 0x92, 0xee, 0x63, 0x61, 0x73, 0x87, 0xc7, 0x54, 0x0e, 0x42, 0x4b, 0xc9, 0x47, 0xd1, 0xb8, 0x7e, 0x91, 0x75, 0x37, 0x99, 0x28, 0xb8, 0xdd, 0x7f, 0x50}} , + {{0x89, 0x8f, 0xc0, 0xbe, 0x5d, 0xd6, 0x9f, 0xa0, 0xf0, 0x9d, 0x81, 0xce, 0x3a, 0x7b, 0x98, 0x58, 0xbb, 0xd7, 0x78, 0xc8, 0x3f, 0x13, 0xf1, 0x74, 0x19, 0xdf, 0xf8, 0x98, 0x89, 0x5d, 0xfa, 0x5f}}}, +{{{0x9e, 0x35, 0x85, 0x94, 0x47, 0x1f, 0x90, 0x15, 0x26, 0xd0, 0x84, 0xed, 0x8a, 0x80, 0xf7, 0x63, 0x42, 0x86, 0x27, 0xd7, 0xf4, 0x75, 0x58, 0xdc, 0x9c, 0xc0, 0x22, 0x7e, 0x20, 0x35, 0xfd, 0x1f}} , + {{0x68, 0x0e, 0x6f, 0x97, 0xba, 0x70, 0xbb, 0xa3, 0x0e, 0xe5, 0x0b, 0x12, 0xf4, 0xa2, 0xdc, 0x47, 0xf8, 0xe6, 0xd0, 0x23, 0x6c, 0x33, 0xa8, 0x99, 0x46, 0x6e, 0x0f, 0x44, 0xba, 0x76, 0x48, 0x0f}}}, +{{{0xa3, 0x2a, 0x61, 0x37, 0xe2, 0x59, 0x12, 0x0e, 0x27, 0xba, 0x64, 0x43, 0xae, 0xc0, 0x42, 0x69, 0x79, 0xa4, 0x1e, 0x29, 0x8b, 0x15, 0xeb, 0xf8, 0xaf, 0xd4, 0xa2, 0x68, 0x33, 0xb5, 0x7a, 0x24}} , + {{0x2c, 0x19, 0x33, 0xdd, 0x1b, 0xab, 0xec, 0x01, 0xb0, 0x23, 0xf8, 0x42, 0x2b, 0x06, 0x88, 0xea, 0x3d, 0x2d, 0x00, 0x2a, 0x78, 0x45, 0x4d, 0x38, 0xed, 0x2e, 0x2e, 0x44, 0x49, 0xed, 0xcb, 0x33}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xa0, 0x68, 0xe8, 0x41, 0x8f, 0x91, 0xf8, 0x11, 0x13, 0x90, 0x2e, 0xa7, 0xab, 0x30, 0xef, 0xad, 0xa0, 0x61, 0x00, 0x88, 0xef, 0xdb, 0xce, 0x5b, 0x5c, 0xbb, 0x62, 0xc8, 0x56, 0xf9, 0x00, 0x73}} , + {{0x3f, 0x60, 0xc1, 0x82, 0x2d, 0xa3, 0x28, 0x58, 0x24, 0x9e, 0x9f, 0xe3, 0x70, 0xcc, 0x09, 0x4e, 0x1a, 0x3f, 0x11, 0x11, 0x15, 0x07, 0x3c, 0xa4, 0x41, 0xe0, 0x65, 0xa3, 0x0a, 0x41, 0x6d, 0x11}}}, +{{{0x31, 0x40, 0x01, 0x52, 0x56, 0x94, 0x5b, 0x28, 0x8a, 0xaa, 0x52, 0xee, 0xd8, 0x0a, 0x05, 0x8d, 0xcd, 0xb5, 0xaa, 0x2e, 0x38, 0xaa, 0xb7, 0x87, 0xf7, 0x2b, 0xfb, 0x04, 0xcb, 0x84, 0x3d, 0x54}} , + {{0x20, 0xef, 0x59, 0xde, 0xa4, 0x2b, 0x93, 0x6e, 0x2e, 0xec, 0x42, 0x9a, 0xd4, 0x2d, 0xf4, 0x46, 0x58, 0x27, 0x2b, 0x18, 0x8f, 0x83, 0x3d, 0x69, 0x9e, 0xd4, 0x3e, 0xb6, 0xc5, 0xfd, 0x58, 0x03}}}, +{{{0x33, 0x89, 0xc9, 0x63, 0x62, 0x1c, 0x17, 0xb4, 0x60, 0xc4, 0x26, 0x68, 0x09, 0xc3, 0x2e, 0x37, 0x0f, 0x7b, 0xb4, 0x9c, 0xb6, 0xf9, 0xfb, 0xd4, 0x51, 0x78, 0xc8, 0x63, 0xea, 0x77, 0x47, 0x07}} , + {{0x32, 0xb4, 0x18, 0x47, 0x79, 0xcb, 0xd4, 0x5a, 0x07, 0x14, 0x0f, 0xa0, 0xd5, 0xac, 0xd0, 0x41, 0x40, 0xab, 0x61, 0x23, 0xe5, 0x2a, 0x2a, 0x6f, 0xf7, 0xa8, 0xd4, 0x76, 0xef, 0xe7, 0x45, 0x6c}}}, +{{{0xa1, 0x5e, 0x60, 0x4f, 0xfb, 0xe1, 0x70, 0x6a, 0x1f, 0x55, 0x4f, 0x09, 0xb4, 0x95, 0x33, 0x36, 0xc6, 0x81, 0x01, 0x18, 0x06, 0x25, 0x27, 0xa4, 0xb4, 0x24, 0xa4, 0x86, 0x03, 0x4c, 0xac, 0x02}} , + {{0x77, 0x38, 0xde, 0xd7, 0x60, 0x48, 0x07, 0xf0, 0x74, 0xa8, 0xff, 0x54, 0xe5, 0x30, 0x43, 0xff, 0x77, 0xfb, 0x21, 0x07, 0xff, 0xb2, 0x07, 0x6b, 0xe4, 0xe5, 0x30, 0xfc, 0x19, 0x6c, 0xa3, 0x01}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x13, 0xc5, 0x2c, 0xac, 0xd3, 0x83, 0x82, 0x7c, 0x29, 0xf7, 0x05, 0xa5, 0x00, 0xb6, 0x1f, 0x86, 0x55, 0xf4, 0xd6, 0x2f, 0x0c, 0x99, 0xd0, 0x65, 0x9b, 0x6b, 0x46, 0x0d, 0x43, 0xf8, 0x16, 0x28}} , + {{0x1e, 0x7f, 0xb4, 0x74, 0x7e, 0xb1, 0x89, 0x4f, 0x18, 0x5a, 0xab, 0x64, 0x06, 0xdf, 0x45, 0x87, 0xe0, 0x6a, 0xc6, 0xf0, 0x0e, 0xc9, 0x24, 0x35, 0x38, 0xea, 0x30, 0x54, 0xb4, 0xc4, 0x52, 0x54}}}, +{{{0xe9, 0x9f, 0xdc, 0x3f, 0xc1, 0x89, 0x44, 0x74, 0x27, 0xe4, 0xc1, 0x90, 0xff, 0x4a, 0xa7, 0x3c, 0xee, 0xcd, 0xf4, 0x1d, 0x25, 0x94, 0x7f, 0x63, 0x16, 0x48, 0xbc, 0x64, 0xfe, 0x95, 0xc4, 0x0c}} , + {{0x8b, 0x19, 0x75, 0x6e, 0x03, 0x06, 0x5e, 0x6a, 0x6f, 0x1a, 0x8c, 0xe3, 0xd3, 0x28, 0xf2, 0xe0, 0xb9, 0x7a, 0x43, 0x69, 0xe6, 0xd3, 0xc0, 0xfe, 0x7e, 0x97, 0xab, 0x6c, 0x7b, 0x8e, 0x13, 0x42}}}, +{{{0xd4, 0xca, 0x70, 0x3d, 0xab, 0xfb, 0x5f, 0x5e, 0x00, 0x0c, 0xcc, 0x77, 0x22, 0xf8, 0x78, 0x55, 0xae, 0x62, 0x35, 0xfb, 0x9a, 0xc6, 0x03, 0xe4, 0x0c, 0xee, 0xab, 0xc7, 0xc0, 0x89, 0x87, 0x54}} , + {{0x32, 0xad, 0xae, 0x85, 0x58, 0x43, 0xb8, 0xb1, 0xe6, 0x3e, 0x00, 0x9c, 0x78, 0x88, 0x56, 0xdb, 0x9c, 0xfc, 0x79, 0xf6, 0xf9, 0x41, 0x5f, 0xb7, 0xbc, 0x11, 0xf9, 0x20, 0x36, 0x1c, 0x53, 0x2b}}}, +{{{0x5a, 0x20, 0x5b, 0xa1, 0xa5, 0x44, 0x91, 0x24, 0x02, 0x63, 0x12, 0x64, 0xb8, 0x55, 0xf6, 0xde, 0x2c, 0xdb, 0x47, 0xb8, 0xc6, 0x0a, 0xc3, 0x00, 0x78, 0x93, 0xd8, 0xf5, 0xf5, 0x18, 0x28, 0x0a}} , + {{0xd6, 0x1b, 0x9a, 0x6c, 0xe5, 0x46, 0xea, 0x70, 0x96, 0x8d, 0x4e, 0x2a, 0x52, 0x21, 0x26, 0x4b, 0xb1, 0xbb, 0x0f, 0x7c, 0xa9, 0x9b, 0x04, 0xbb, 0x51, 0x08, 0xf1, 0x9a, 0xa4, 0x76, 0x7c, 0x18}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xfa, 0x94, 0xf7, 0x40, 0xd0, 0xd7, 0xeb, 0xa9, 0x82, 0x36, 0xd5, 0x15, 0xb9, 0x33, 0x7a, 0xbf, 0x8a, 0xf2, 0x63, 0xaa, 0x37, 0xf5, 0x59, 0xac, 0xbd, 0xbb, 0x32, 0x36, 0xbe, 0x73, 0x99, 0x38}} , + {{0x2c, 0xb3, 0xda, 0x7a, 0xd8, 0x3d, 0x99, 0xca, 0xd2, 0xf4, 0xda, 0x99, 0x8e, 0x4f, 0x98, 0xb7, 0xf4, 0xae, 0x3e, 0x9f, 0x8e, 0x35, 0x60, 0xa4, 0x33, 0x75, 0xa4, 0x04, 0x93, 0xb1, 0x6b, 0x4d}}}, +{{{0x97, 0x9d, 0xa8, 0xcd, 0x97, 0x7b, 0x9d, 0xb9, 0xe7, 0xa5, 0xef, 0xfd, 0xa8, 0x42, 0x6b, 0xc3, 0x62, 0x64, 0x7d, 0xa5, 0x1b, 0xc9, 0x9e, 0xd2, 0x45, 0xb9, 0xee, 0x03, 0xb0, 0xbf, 0xc0, 0x68}} , + {{0xed, 0xb7, 0x84, 0x2c, 0xf6, 0xd3, 0xa1, 0x6b, 0x24, 0x6d, 0x87, 0x56, 0x97, 0x59, 0x79, 0x62, 0x9f, 0xac, 0xed, 0xf3, 0xc9, 0x89, 0x21, 0x2e, 0x04, 0xb3, 0xcc, 0x2f, 0xbe, 0xd6, 0x0a, 0x4b}}}, +{{{0x39, 0x61, 0x05, 0xed, 0x25, 0x89, 0x8b, 0x5d, 0x1b, 0xcb, 0x0c, 0x55, 0xf4, 0x6a, 0x00, 0x8a, 0x46, 0xe8, 0x1e, 0xc6, 0x83, 0xc8, 0x5a, 0x76, 0xdb, 0xcc, 0x19, 0x7a, 0xcc, 0x67, 0x46, 0x0b}} , + {{0x53, 0xcf, 0xc2, 0xa1, 0xad, 0x6a, 0xf3, 0xcd, 0x8f, 0xc9, 0xde, 0x1c, 0xf8, 0x6c, 0x8f, 0xf8, 0x76, 0x42, 0xe7, 0xfe, 0xb2, 0x72, 0x21, 0x0a, 0x66, 0x74, 0x8f, 0xb7, 0xeb, 0xe4, 0x6f, 0x01}}}, +{{{0x22, 0x8c, 0x6b, 0xbe, 0xfc, 0x4d, 0x70, 0x62, 0x6e, 0x52, 0x77, 0x99, 0x88, 0x7e, 0x7b, 0x57, 0x7a, 0x0d, 0xfe, 0xdc, 0x72, 0x92, 0xf1, 0x68, 0x1d, 0x97, 0xd7, 0x7c, 0x8d, 0x53, 0x10, 0x37}} , + {{0x53, 0x88, 0x77, 0x02, 0xca, 0x27, 0xa8, 0xe5, 0x45, 0xe2, 0xa8, 0x48, 0x2a, 0xab, 0x18, 0xca, 0xea, 0x2d, 0x2a, 0x54, 0x17, 0x37, 0x32, 0x09, 0xdc, 0xe0, 0x4a, 0xb7, 0x7d, 0x82, 0x10, 0x7d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x8a, 0x64, 0x1e, 0x14, 0x0a, 0x57, 0xd4, 0xda, 0x5c, 0x96, 0x9b, 0x01, 0x4c, 0x67, 0xbf, 0x8b, 0x30, 0xfe, 0x08, 0xdb, 0x0d, 0xd5, 0xa8, 0xd7, 0x09, 0x11, 0x85, 0xa2, 0xd3, 0x45, 0xfb, 0x7e}} , + {{0xda, 0x8c, 0xc2, 0xd0, 0xac, 0x18, 0xe8, 0x52, 0x36, 0xd4, 0x21, 0xa3, 0xdd, 0x57, 0x22, 0x79, 0xb7, 0xf8, 0x71, 0x9d, 0xc6, 0x91, 0x70, 0x86, 0x56, 0xbf, 0xa1, 0x11, 0x8b, 0x19, 0xe1, 0x0f}}}, +{{{0x18, 0x32, 0x98, 0x2c, 0x8f, 0x91, 0xae, 0x12, 0xf0, 0x8c, 0xea, 0xf3, 0x3c, 0xb9, 0x5d, 0xe4, 0x69, 0xed, 0xb2, 0x47, 0x18, 0xbd, 0xce, 0x16, 0x52, 0x5c, 0x23, 0xe2, 0xa5, 0x25, 0x52, 0x5d}} , + {{0xb9, 0xb1, 0xe7, 0x5d, 0x4e, 0xbc, 0xee, 0xbb, 0x40, 0x81, 0x77, 0x82, 0x19, 0xab, 0xb5, 0xc6, 0xee, 0xab, 0x5b, 0x6b, 0x63, 0x92, 0x8a, 0x34, 0x8d, 0xcd, 0xee, 0x4f, 0x49, 0xe5, 0xc9, 0x7e}}}, +{{{0x21, 0xac, 0x8b, 0x22, 0xcd, 0xc3, 0x9a, 0xe9, 0x5e, 0x78, 0xbd, 0xde, 0xba, 0xad, 0xab, 0xbf, 0x75, 0x41, 0x09, 0xc5, 0x58, 0xa4, 0x7d, 0x92, 0xb0, 0x7f, 0xf2, 0xa1, 0xd1, 0xc0, 0xb3, 0x6d}} , + {{0x62, 0x4f, 0xd0, 0x75, 0x77, 0xba, 0x76, 0x77, 0xd7, 0xb8, 0xd8, 0x92, 0x6f, 0x98, 0x34, 0x3d, 0xd6, 0x4e, 0x1c, 0x0f, 0xf0, 0x8f, 0x2e, 0xf1, 0xb3, 0xbd, 0xb1, 0xb9, 0xec, 0x99, 0xb4, 0x07}}}, +{{{0x60, 0x57, 0x2e, 0x9a, 0x72, 0x1d, 0x6b, 0x6e, 0x58, 0x33, 0x24, 0x8c, 0x48, 0x39, 0x46, 0x8e, 0x89, 0x6a, 0x88, 0x51, 0x23, 0x62, 0xb5, 0x32, 0x09, 0x36, 0xe3, 0x57, 0xf5, 0x98, 0xde, 0x6f}} , + {{0x8b, 0x2c, 0x00, 0x48, 0x4a, 0xf9, 0x5b, 0x87, 0x69, 0x52, 0xe5, 0x5b, 0xd1, 0xb1, 0xe5, 0x25, 0x25, 0xe0, 0x9c, 0xc2, 0x13, 0x44, 0xe8, 0xb9, 0x0a, 0x70, 0xad, 0xbd, 0x0f, 0x51, 0x94, 0x69}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xa2, 0xdc, 0xab, 0xa9, 0x25, 0x2d, 0xac, 0x5f, 0x03, 0x33, 0x08, 0xe7, 0x7e, 0xfe, 0x95, 0x36, 0x3c, 0x5b, 0x3a, 0xd3, 0x05, 0x82, 0x1c, 0x95, 0x2d, 0xd8, 0x77, 0x7e, 0x02, 0xd9, 0x5b, 0x70}} , + {{0xc2, 0xfe, 0x1b, 0x0c, 0x67, 0xcd, 0xd6, 0xe0, 0x51, 0x8e, 0x2c, 0xe0, 0x79, 0x88, 0xf0, 0xcf, 0x41, 0x4a, 0xad, 0x23, 0xd4, 0x46, 0xca, 0x94, 0xa1, 0xc3, 0xeb, 0x28, 0x06, 0xfa, 0x17, 0x14}}}, +{{{0x7b, 0xaa, 0x70, 0x0a, 0x4b, 0xfb, 0xf5, 0xbf, 0x80, 0xc5, 0xcf, 0x08, 0x7a, 0xdd, 0xa1, 0xf4, 0x9d, 0x54, 0x50, 0x53, 0x23, 0x77, 0x23, 0xf5, 0x34, 0xa5, 0x22, 0xd1, 0x0d, 0x96, 0x2e, 0x47}} , + {{0xcc, 0xb7, 0x32, 0x89, 0x57, 0xd0, 0x98, 0x75, 0xe4, 0x37, 0x99, 0xa9, 0xe8, 0xba, 0xed, 0xba, 0xeb, 0xc7, 0x4f, 0x15, 0x76, 0x07, 0x0c, 0x4c, 0xef, 0x9f, 0x52, 0xfc, 0x04, 0x5d, 0x58, 0x10}}}, +{{{0xce, 0x82, 0xf0, 0x8f, 0x79, 0x02, 0xa8, 0xd1, 0xda, 0x14, 0x09, 0x48, 0xee, 0x8a, 0x40, 0x98, 0x76, 0x60, 0x54, 0x5a, 0xde, 0x03, 0x24, 0xf5, 0xe6, 0x2f, 0xe1, 0x03, 0xbf, 0x68, 0x82, 0x7f}} , + {{0x64, 0xe9, 0x28, 0xc7, 0xa4, 0xcf, 0x2a, 0xf9, 0x90, 0x64, 0x72, 0x2c, 0x8b, 0xeb, 0xec, 0xa0, 0xf2, 0x7d, 0x35, 0xb5, 0x90, 0x4d, 0x7f, 0x5b, 0x4a, 0x49, 0xe4, 0xb8, 0x3b, 0xc8, 0xa1, 0x2f}}}, +{{{0x8b, 0xc5, 0xcc, 0x3d, 0x69, 0xa6, 0xa1, 0x18, 0x44, 0xbc, 0x4d, 0x77, 0x37, 0xc7, 0x86, 0xec, 0x0c, 0xc9, 0xd6, 0x44, 0xa9, 0x23, 0x27, 0xb9, 0x03, 0x34, 0xa7, 0x0a, 0xd5, 0xc7, 0x34, 0x37}} , + {{0xf9, 0x7e, 0x3e, 0x66, 0xee, 0xf9, 0x99, 0x28, 0xff, 0xad, 0x11, 0xd8, 0xe2, 0x66, 0xc5, 0xcd, 0x0f, 0x0d, 0x0b, 0x6a, 0xfc, 0x7c, 0x24, 0xa8, 0x4f, 0xa8, 0x5e, 0x80, 0x45, 0x8b, 0x6c, 0x41}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xef, 0x1e, 0xec, 0xf7, 0x8d, 0x77, 0xf2, 0xea, 0xdb, 0x60, 0x03, 0x21, 0xc0, 0xff, 0x5e, 0x67, 0xc3, 0x71, 0x0b, 0x21, 0xb4, 0x41, 0xa0, 0x68, 0x38, 0xc6, 0x01, 0xa3, 0xd3, 0x51, 0x3c, 0x3c}} , + {{0x92, 0xf8, 0xd6, 0x4b, 0xef, 0x42, 0x13, 0xb2, 0x4a, 0xc4, 0x2e, 0x72, 0x3f, 0xc9, 0x11, 0xbd, 0x74, 0x02, 0x0e, 0xf5, 0x13, 0x9d, 0x83, 0x1a, 0x1b, 0xd5, 0x54, 0xde, 0xc4, 0x1e, 0x16, 0x6c}}}, +{{{0x27, 0x52, 0xe4, 0x63, 0xaa, 0x94, 0xe6, 0xc3, 0x28, 0x9c, 0xc6, 0x56, 0xac, 0xfa, 0xb6, 0xbd, 0xe2, 0xcc, 0x76, 0xc6, 0x27, 0x27, 0xa2, 0x8e, 0x78, 0x2b, 0x84, 0x72, 0x10, 0xbd, 0x4e, 0x2a}} , + {{0xea, 0xa7, 0x23, 0xef, 0x04, 0x61, 0x80, 0x50, 0xc9, 0x6e, 0xa5, 0x96, 0xd1, 0xd1, 0xc8, 0xc3, 0x18, 0xd7, 0x2d, 0xfd, 0x26, 0xbd, 0xcb, 0x7b, 0x92, 0x51, 0x0e, 0x4a, 0x65, 0x57, 0xb8, 0x49}}}, +{{{0xab, 0x55, 0x36, 0xc3, 0xec, 0x63, 0x55, 0x11, 0x55, 0xf6, 0xa5, 0xc7, 0x01, 0x5f, 0xfe, 0x79, 0xd8, 0x0a, 0xf7, 0x03, 0xd8, 0x98, 0x99, 0xf5, 0xd0, 0x00, 0x54, 0x6b, 0x66, 0x28, 0xf5, 0x25}} , + {{0x7a, 0x8d, 0xa1, 0x5d, 0x70, 0x5d, 0x51, 0x27, 0xee, 0x30, 0x65, 0x56, 0x95, 0x46, 0xde, 0xbd, 0x03, 0x75, 0xb4, 0x57, 0x59, 0x89, 0xeb, 0x02, 0x9e, 0xcc, 0x89, 0x19, 0xa7, 0xcb, 0x17, 0x67}}}, +{{{0x6a, 0xeb, 0xfc, 0x9a, 0x9a, 0x10, 0xce, 0xdb, 0x3a, 0x1c, 0x3c, 0x6a, 0x9d, 0xea, 0x46, 0xbc, 0x45, 0x49, 0xac, 0xe3, 0x41, 0x12, 0x7c, 0xf0, 0xf7, 0x4f, 0xf9, 0xf7, 0xff, 0x2c, 0x89, 0x04}} , + {{0x30, 0x31, 0x54, 0x1a, 0x46, 0xca, 0xe6, 0xc6, 0xcb, 0xe2, 0xc3, 0xc1, 0x8b, 0x75, 0x81, 0xbe, 0xee, 0xf8, 0xa3, 0x11, 0x1c, 0x25, 0xa3, 0xa7, 0x35, 0x51, 0x55, 0xe2, 0x25, 0xaa, 0xe2, 0x3a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xb4, 0x48, 0x10, 0x9f, 0x8a, 0x09, 0x76, 0xfa, 0xf0, 0x7a, 0xb0, 0x70, 0xf7, 0x83, 0x80, 0x52, 0x84, 0x2b, 0x26, 0xa2, 0xc4, 0x5d, 0x4f, 0xba, 0xb1, 0xc8, 0x40, 0x0d, 0x78, 0x97, 0xc4, 0x60}} , + {{0xd4, 0xb1, 0x6c, 0x08, 0xc7, 0x40, 0x38, 0x73, 0x5f, 0x0b, 0xf3, 0x76, 0x5d, 0xb2, 0xa5, 0x2f, 0x57, 0x57, 0x07, 0xed, 0x08, 0xa2, 0x6c, 0x4f, 0x08, 0x02, 0xb5, 0x0e, 0xee, 0x44, 0xfa, 0x22}}}, +{{{0x0f, 0x00, 0x3f, 0xa6, 0x04, 0x19, 0x56, 0x65, 0x31, 0x7f, 0x8b, 0xeb, 0x0d, 0xe1, 0x47, 0x89, 0x97, 0x16, 0x53, 0xfa, 0x81, 0xa7, 0xaa, 0xb2, 0xbf, 0x67, 0xeb, 0x72, 0x60, 0x81, 0x0d, 0x48}} , + {{0x7e, 0x13, 0x33, 0xcd, 0xa8, 0x84, 0x56, 0x1e, 0x67, 0xaf, 0x6b, 0x43, 0xac, 0x17, 0xaf, 0x16, 0xc0, 0x52, 0x99, 0x49, 0x5b, 0x87, 0x73, 0x7e, 0xb5, 0x43, 0xda, 0x6b, 0x1d, 0x0f, 0x2d, 0x55}}}, +{{{0xe9, 0x58, 0x1f, 0xff, 0x84, 0x3f, 0x93, 0x1c, 0xcb, 0xe1, 0x30, 0x69, 0xa5, 0x75, 0x19, 0x7e, 0x14, 0x5f, 0xf8, 0xfc, 0x09, 0xdd, 0xa8, 0x78, 0x9d, 0xca, 0x59, 0x8b, 0xd1, 0x30, 0x01, 0x13}} , + {{0xff, 0x76, 0x03, 0xc5, 0x4b, 0x89, 0x99, 0x70, 0x00, 0x59, 0x70, 0x9c, 0xd5, 0xd9, 0x11, 0x89, 0x5a, 0x46, 0xfe, 0xef, 0xdc, 0xd9, 0x55, 0x2b, 0x45, 0xa7, 0xb0, 0x2d, 0xfb, 0x24, 0xc2, 0x29}}}, +{{{0x38, 0x06, 0xf8, 0x0b, 0xac, 0x82, 0xc4, 0x97, 0x2b, 0x90, 0xe0, 0xf7, 0xa8, 0xab, 0x6c, 0x08, 0x80, 0x66, 0x90, 0x46, 0xf7, 0x26, 0x2d, 0xf8, 0xf1, 0xc4, 0x6b, 0x4a, 0x82, 0x98, 0x8e, 0x37}} , + {{0x8e, 0xb4, 0xee, 0xb8, 0xd4, 0x3f, 0xb2, 0x1b, 0xe0, 0x0a, 0x3d, 0x75, 0x34, 0x28, 0xa2, 0x8e, 0xc4, 0x92, 0x7b, 0xfe, 0x60, 0x6e, 0x6d, 0xb8, 0x31, 0x1d, 0x62, 0x0d, 0x78, 0x14, 0x42, 0x11}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x5e, 0xa8, 0xd8, 0x04, 0x9b, 0x73, 0xc9, 0xc9, 0xdc, 0x0d, 0x73, 0xbf, 0x0a, 0x0a, 0x73, 0xff, 0x18, 0x1f, 0x9c, 0x51, 0xaa, 0xc6, 0xf1, 0x83, 0x25, 0xfd, 0xab, 0xa3, 0x11, 0xd3, 0x01, 0x24}} , + {{0x4d, 0xe3, 0x7e, 0x38, 0x62, 0x5e, 0x64, 0xbb, 0x2b, 0x53, 0xb5, 0x03, 0x68, 0xc4, 0xf2, 0x2b, 0x5a, 0x03, 0x32, 0x99, 0x4a, 0x41, 0x9a, 0xe1, 0x1a, 0xae, 0x8c, 0x48, 0xf3, 0x24, 0x32, 0x65}}}, +{{{0xe8, 0xdd, 0xad, 0x3a, 0x8c, 0xea, 0xf4, 0xb3, 0xb2, 0xe5, 0x73, 0xf2, 0xed, 0x8b, 0xbf, 0xed, 0xb1, 0x0c, 0x0c, 0xfb, 0x2b, 0xf1, 0x01, 0x48, 0xe8, 0x26, 0x03, 0x8e, 0x27, 0x4d, 0x96, 0x72}} , + {{0xc8, 0x09, 0x3b, 0x60, 0xc9, 0x26, 0x4d, 0x7c, 0xf2, 0x9c, 0xd4, 0xa1, 0x3b, 0x26, 0xc2, 0x04, 0x33, 0x44, 0x76, 0x3c, 0x02, 0xbb, 0x11, 0x42, 0x0c, 0x22, 0xb7, 0xc6, 0xe1, 0xac, 0xb4, 0x0e}}}, +{{{0x6f, 0x85, 0xe7, 0xef, 0xde, 0x67, 0x30, 0xfc, 0xbf, 0x5a, 0xe0, 0x7b, 0x7a, 0x2a, 0x54, 0x6b, 0x5d, 0x62, 0x85, 0xa1, 0xf8, 0x16, 0x88, 0xec, 0x61, 0xb9, 0x96, 0xb5, 0xef, 0x2d, 0x43, 0x4d}} , + {{0x7c, 0x31, 0x33, 0xcc, 0xe4, 0xcf, 0x6c, 0xff, 0x80, 0x47, 0x77, 0xd1, 0xd8, 0xe9, 0x69, 0x97, 0x98, 0x7f, 0x20, 0x57, 0x1d, 0x1d, 0x4f, 0x08, 0x27, 0xc8, 0x35, 0x57, 0x40, 0xc6, 0x21, 0x0c}}}, +{{{0xd2, 0x8e, 0x9b, 0xfa, 0x42, 0x8e, 0xdf, 0x8f, 0xc7, 0x86, 0xf9, 0xa4, 0xca, 0x70, 0x00, 0x9d, 0x21, 0xbf, 0xec, 0x57, 0x62, 0x30, 0x58, 0x8c, 0x0d, 0x35, 0xdb, 0x5d, 0x8b, 0x6a, 0xa0, 0x5a}} , + {{0xc1, 0x58, 0x7c, 0x0d, 0x20, 0xdd, 0x11, 0x26, 0x5f, 0x89, 0x3b, 0x97, 0x58, 0xf8, 0x8b, 0xe3, 0xdf, 0x32, 0xe2, 0xfc, 0xd8, 0x67, 0xf2, 0xa5, 0x37, 0x1e, 0x6d, 0xec, 0x7c, 0x27, 0x20, 0x79}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xd0, 0xe9, 0xc0, 0xfa, 0x95, 0x45, 0x23, 0x96, 0xf1, 0x2c, 0x79, 0x25, 0x14, 0xce, 0x40, 0x14, 0x44, 0x2c, 0x36, 0x50, 0xd9, 0x63, 0x56, 0xb7, 0x56, 0x3b, 0x9e, 0xa7, 0xef, 0x89, 0xbb, 0x0e}} , + {{0xce, 0x7f, 0xdc, 0x0a, 0xcc, 0x82, 0x1c, 0x0a, 0x78, 0x71, 0xe8, 0x74, 0x8d, 0x01, 0x30, 0x0f, 0xa7, 0x11, 0x4c, 0xdf, 0x38, 0xd7, 0xa7, 0x0d, 0xf8, 0x48, 0x52, 0x00, 0x80, 0x7b, 0x5f, 0x0e}}}, +{{{0x25, 0x83, 0xe6, 0x94, 0x7b, 0x81, 0xb2, 0x91, 0xae, 0x0e, 0x05, 0xc9, 0xa3, 0x68, 0x2d, 0xd9, 0x88, 0x25, 0x19, 0x2a, 0x61, 0x61, 0x21, 0x97, 0x15, 0xa1, 0x35, 0xa5, 0x46, 0xc8, 0xa2, 0x0e}} , + {{0x1b, 0x03, 0x0d, 0x8b, 0x5a, 0x1b, 0x97, 0x4b, 0xf2, 0x16, 0x31, 0x3d, 0x1f, 0x33, 0xa0, 0x50, 0x3a, 0x18, 0xbe, 0x13, 0xa1, 0x76, 0xc1, 0xba, 0x1b, 0xf1, 0x05, 0x7b, 0x33, 0xa8, 0x82, 0x3b}}}, +{{{0xba, 0x36, 0x7b, 0x6d, 0xa9, 0xea, 0x14, 0x12, 0xc5, 0xfa, 0x91, 0x00, 0xba, 0x9b, 0x99, 0xcc, 0x56, 0x02, 0xe9, 0xa0, 0x26, 0x40, 0x66, 0x8c, 0xc4, 0xf8, 0x85, 0x33, 0x68, 0xe7, 0x03, 0x20}} , + {{0x50, 0x5b, 0xff, 0xa9, 0xb2, 0xf1, 0xf1, 0x78, 0xcf, 0x14, 0xa4, 0xa9, 0xfc, 0x09, 0x46, 0x94, 0x54, 0x65, 0x0d, 0x9c, 0x5f, 0x72, 0x21, 0xe2, 0x97, 0xa5, 0x2d, 0x81, 0xce, 0x4a, 0x5f, 0x79}}}, +{{{0x3d, 0x5f, 0x5c, 0xd2, 0xbc, 0x7d, 0x77, 0x0e, 0x2a, 0x6d, 0x22, 0x45, 0x84, 0x06, 0xc4, 0xdd, 0xc6, 0xa6, 0xc6, 0xd7, 0x49, 0xad, 0x6d, 0x87, 0x91, 0x0e, 0x3a, 0x67, 0x1d, 0x2c, 0x1d, 0x56}} , + {{0xfe, 0x7a, 0x74, 0xcf, 0xd4, 0xd2, 0xe5, 0x19, 0xde, 0xd0, 0xdb, 0x70, 0x23, 0x69, 0xe6, 0x6d, 0xec, 0xec, 0xcc, 0x09, 0x33, 0x6a, 0x77, 0xdc, 0x6b, 0x22, 0x76, 0x5d, 0x92, 0x09, 0xac, 0x2d}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x23, 0x15, 0x17, 0xeb, 0xd3, 0xdb, 0x12, 0x5e, 0x01, 0xf0, 0x91, 0xab, 0x2c, 0x41, 0xce, 0xac, 0xed, 0x1b, 0x4b, 0x2d, 0xbc, 0xdb, 0x17, 0x66, 0x89, 0x46, 0xad, 0x4b, 0x1e, 0x6f, 0x0b, 0x14}} , + {{0x11, 0xce, 0xbf, 0xb6, 0x77, 0x2d, 0x48, 0x22, 0x18, 0x4f, 0xa3, 0x5d, 0x4a, 0xb0, 0x70, 0x12, 0x3e, 0x54, 0xd7, 0xd8, 0x0e, 0x2b, 0x27, 0xdc, 0x53, 0xff, 0xca, 0x8c, 0x59, 0xb3, 0x4e, 0x44}}}, +{{{0x07, 0x76, 0x61, 0x0f, 0x66, 0xb2, 0x21, 0x39, 0x7e, 0xc0, 0xec, 0x45, 0x28, 0x82, 0xa1, 0x29, 0x32, 0x44, 0x35, 0x13, 0x5e, 0x61, 0x5e, 0x54, 0xcb, 0x7c, 0xef, 0xf6, 0x41, 0xcf, 0x9f, 0x0a}} , + {{0xdd, 0xf9, 0xda, 0x84, 0xc3, 0xe6, 0x8a, 0x9f, 0x24, 0xd2, 0x96, 0x5d, 0x39, 0x6f, 0x58, 0x8c, 0xc1, 0x56, 0x93, 0xab, 0xb5, 0x79, 0x3b, 0xd2, 0xa8, 0x73, 0x16, 0xed, 0xfa, 0xb4, 0x2f, 0x73}}}, +{{{0x8b, 0xb1, 0x95, 0xe5, 0x92, 0x50, 0x35, 0x11, 0x76, 0xac, 0xf4, 0x4d, 0x24, 0xc3, 0x32, 0xe6, 0xeb, 0xfe, 0x2c, 0x87, 0xc4, 0xf1, 0x56, 0xc4, 0x75, 0x24, 0x7a, 0x56, 0x85, 0x5a, 0x3a, 0x13}} , + {{0x0d, 0x16, 0xac, 0x3c, 0x4a, 0x58, 0x86, 0x3a, 0x46, 0x7f, 0x6c, 0xa3, 0x52, 0x6e, 0x37, 0xe4, 0x96, 0x9c, 0xe9, 0x5c, 0x66, 0x41, 0x67, 0xe4, 0xfb, 0x79, 0x0c, 0x05, 0xf6, 0x64, 0xd5, 0x7c}}}, +{{{0x28, 0xc1, 0xe1, 0x54, 0x73, 0xf2, 0xbf, 0x76, 0x74, 0x19, 0x19, 0x1b, 0xe4, 0xb9, 0xa8, 0x46, 0x65, 0x73, 0xf3, 0x77, 0x9b, 0x29, 0x74, 0x5b, 0xc6, 0x89, 0x6c, 0x2c, 0x7c, 0xf8, 0xb3, 0x0f}} , + {{0xf7, 0xd5, 0xe9, 0x74, 0x5d, 0xb8, 0x25, 0x16, 0xb5, 0x30, 0xbc, 0x84, 0xc5, 0xf0, 0xad, 0xca, 0x12, 0x28, 0xbc, 0x9d, 0xd4, 0xfa, 0x82, 0xe6, 0xe3, 0xbf, 0xa2, 0x15, 0x2c, 0xd4, 0x34, 0x10}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x61, 0xb1, 0x46, 0xba, 0x0e, 0x31, 0xa5, 0x67, 0x6c, 0x7f, 0xd6, 0xd9, 0x27, 0x85, 0x0f, 0x79, 0x14, 0xc8, 0x6c, 0x2f, 0x5f, 0x5b, 0x9c, 0x35, 0x3d, 0x38, 0x86, 0x77, 0x65, 0x55, 0x6a, 0x7b}} , + {{0xd3, 0xb0, 0x3a, 0x66, 0x60, 0x1b, 0x43, 0xf1, 0x26, 0x58, 0x99, 0x09, 0x8f, 0x2d, 0xa3, 0x14, 0x71, 0x85, 0xdb, 0xed, 0xf6, 0x26, 0xd5, 0x61, 0x9a, 0x73, 0xac, 0x0e, 0xea, 0xac, 0xb7, 0x0c}}}, +{{{0x5e, 0xf4, 0xe5, 0x17, 0x0e, 0x10, 0x9f, 0xe7, 0x43, 0x5f, 0x67, 0x5c, 0xac, 0x4b, 0xe5, 0x14, 0x41, 0xd2, 0xbf, 0x48, 0xf5, 0x14, 0xb0, 0x71, 0xc6, 0x61, 0xc1, 0xb2, 0x70, 0x58, 0xd2, 0x5a}} , + {{0x2d, 0xba, 0x16, 0x07, 0x92, 0x94, 0xdc, 0xbd, 0x50, 0x2b, 0xc9, 0x7f, 0x42, 0x00, 0xba, 0x61, 0xed, 0xf8, 0x43, 0xed, 0xf5, 0xf9, 0x40, 0x60, 0xb2, 0xb0, 0x82, 0xcb, 0xed, 0x75, 0xc7, 0x65}}}, +{{{0x80, 0xba, 0x0d, 0x09, 0x40, 0xa7, 0x39, 0xa6, 0x67, 0x34, 0x7e, 0x66, 0xbe, 0x56, 0xfb, 0x53, 0x78, 0xc4, 0x46, 0xe8, 0xed, 0x68, 0x6c, 0x7f, 0xce, 0xe8, 0x9f, 0xce, 0xa2, 0x64, 0x58, 0x53}} , + {{0xe8, 0xc1, 0xa9, 0xc2, 0x7b, 0x59, 0x21, 0x33, 0xe2, 0x43, 0x73, 0x2b, 0xac, 0x2d, 0xc1, 0x89, 0x3b, 0x15, 0xe2, 0xd5, 0xc0, 0x97, 0x8a, 0xfd, 0x6f, 0x36, 0x33, 0xb7, 0xb9, 0xc3, 0x88, 0x09}}}, +{{{0xd0, 0xb6, 0x56, 0x30, 0x5c, 0xae, 0xb3, 0x75, 0x44, 0xa4, 0x83, 0x51, 0x6e, 0x01, 0x65, 0xef, 0x45, 0x76, 0xe6, 0xf5, 0xa2, 0x0d, 0xd4, 0x16, 0x3b, 0x58, 0x2f, 0xf2, 0x2f, 0x36, 0x18, 0x3f}} , + {{0xfd, 0x2f, 0xe0, 0x9b, 0x1e, 0x8c, 0xc5, 0x18, 0xa9, 0xca, 0xd4, 0x2b, 0x35, 0xb6, 0x95, 0x0a, 0x9f, 0x7e, 0xfb, 0xc4, 0xef, 0x88, 0x7b, 0x23, 0x43, 0xec, 0x2f, 0x0d, 0x0f, 0x7a, 0xfc, 0x5c}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x8d, 0xd2, 0xda, 0xc7, 0x44, 0xd6, 0x7a, 0xdb, 0x26, 0x7d, 0x1d, 0xb8, 0xe1, 0xde, 0x9d, 0x7a, 0x7d, 0x17, 0x7e, 0x1c, 0x37, 0x04, 0x8d, 0x2d, 0x7c, 0x5e, 0x18, 0x38, 0x1e, 0xaf, 0xc7, 0x1b}} , + {{0x33, 0x48, 0x31, 0x00, 0x59, 0xf6, 0xf2, 0xca, 0x0f, 0x27, 0x1b, 0x63, 0x12, 0x7e, 0x02, 0x1d, 0x49, 0xc0, 0x5d, 0x79, 0x87, 0xef, 0x5e, 0x7a, 0x2f, 0x1f, 0x66, 0x55, 0xd8, 0x09, 0xd9, 0x61}}}, +{{{0x54, 0x83, 0x02, 0x18, 0x82, 0x93, 0x99, 0x07, 0xd0, 0xa7, 0xda, 0xd8, 0x75, 0x89, 0xfa, 0xf2, 0xd9, 0xa3, 0xb8, 0x6b, 0x5a, 0x35, 0x28, 0xd2, 0x6b, 0x59, 0xc2, 0xf8, 0x45, 0xe2, 0xbc, 0x06}} , + {{0x65, 0xc0, 0xa3, 0x88, 0x51, 0x95, 0xfc, 0x96, 0x94, 0x78, 0xe8, 0x0d, 0x8b, 0x41, 0xc9, 0xc2, 0x58, 0x48, 0x75, 0x10, 0x2f, 0xcd, 0x2a, 0xc9, 0xa0, 0x6d, 0x0f, 0xdd, 0x9c, 0x98, 0x26, 0x3d}}}, +{{{0x2f, 0x66, 0x29, 0x1b, 0x04, 0x89, 0xbd, 0x7e, 0xee, 0x6e, 0xdd, 0xb7, 0x0e, 0xef, 0xb0, 0x0c, 0xb4, 0xfc, 0x7f, 0xc2, 0xc9, 0x3a, 0x3c, 0x64, 0xef, 0x45, 0x44, 0xaf, 0x8a, 0x90, 0x65, 0x76}} , + {{0xa1, 0x4c, 0x70, 0x4b, 0x0e, 0xa0, 0x83, 0x70, 0x13, 0xa4, 0xaf, 0xb8, 0x38, 0x19, 0x22, 0x65, 0x09, 0xb4, 0x02, 0x4f, 0x06, 0xf8, 0x17, 0xce, 0x46, 0x45, 0xda, 0x50, 0x7c, 0x8a, 0xd1, 0x4e}}}, +{{{0xf7, 0xd4, 0x16, 0x6c, 0x4e, 0x95, 0x9d, 0x5d, 0x0f, 0x91, 0x2b, 0x52, 0xfe, 0x5c, 0x34, 0xe5, 0x30, 0xe6, 0xa4, 0x3b, 0xf3, 0xf3, 0x34, 0x08, 0xa9, 0x4a, 0xa0, 0xb5, 0x6e, 0xb3, 0x09, 0x0a}} , + {{0x26, 0xd9, 0x5e, 0xa3, 0x0f, 0xeb, 0xa2, 0xf3, 0x20, 0x3b, 0x37, 0xd4, 0xe4, 0x9e, 0xce, 0x06, 0x3d, 0x53, 0xed, 0xae, 0x2b, 0xeb, 0xb6, 0x24, 0x0a, 0x11, 0xa3, 0x0f, 0xd6, 0x7f, 0xa4, 0x3a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xdb, 0x9f, 0x2c, 0xfc, 0xd6, 0xb2, 0x1e, 0x2e, 0x52, 0x7a, 0x06, 0x87, 0x2d, 0x86, 0x72, 0x2b, 0x6d, 0x90, 0x77, 0x46, 0x43, 0xb5, 0x7a, 0xf8, 0x60, 0x7d, 0x91, 0x60, 0x5b, 0x9d, 0x9e, 0x07}} , + {{0x97, 0x87, 0xc7, 0x04, 0x1c, 0x38, 0x01, 0x39, 0x58, 0xc7, 0x85, 0xa3, 0xfc, 0x64, 0x00, 0x64, 0x25, 0xa2, 0xbf, 0x50, 0x94, 0xca, 0x26, 0x31, 0x45, 0x0a, 0x24, 0xd2, 0x51, 0x29, 0x51, 0x16}}}, +{{{0x4d, 0x4a, 0xd7, 0x98, 0x71, 0x57, 0xac, 0x7d, 0x8b, 0x37, 0xbd, 0x63, 0xff, 0x87, 0xb1, 0x49, 0x95, 0x20, 0x7c, 0xcf, 0x7c, 0x59, 0xc4, 0x91, 0x9c, 0xef, 0xd0, 0xdb, 0x60, 0x09, 0x9d, 0x46}} , + {{0xcb, 0x78, 0x94, 0x90, 0xe4, 0x45, 0xb3, 0xf6, 0xd9, 0xf6, 0x57, 0x74, 0xd5, 0xf8, 0x83, 0x4f, 0x39, 0xc9, 0xbd, 0x88, 0xc2, 0x57, 0x21, 0x1f, 0x24, 0x32, 0x68, 0xf8, 0xc7, 0x21, 0x5f, 0x0b}}}, +{{{0x2a, 0x36, 0x68, 0xfc, 0x5f, 0xb6, 0x4f, 0xa5, 0xe3, 0x9d, 0x24, 0x2f, 0xc0, 0x93, 0x61, 0xcf, 0xf8, 0x0a, 0xed, 0xe1, 0xdb, 0x27, 0xec, 0x0e, 0x14, 0x32, 0x5f, 0x8e, 0xa1, 0x62, 0x41, 0x16}} , + {{0x95, 0x21, 0x01, 0xce, 0x95, 0x5b, 0x0e, 0x57, 0xc7, 0xb9, 0x62, 0xb5, 0x28, 0xca, 0x11, 0xec, 0xb4, 0x46, 0x06, 0x73, 0x26, 0xff, 0xfb, 0x66, 0x7d, 0xee, 0x5f, 0xb2, 0x56, 0xfd, 0x2a, 0x08}}}, +{{{0x92, 0x67, 0x77, 0x56, 0xa1, 0xff, 0xc4, 0xc5, 0x95, 0xf0, 0xe3, 0x3a, 0x0a, 0xca, 0x94, 0x4d, 0x9e, 0x7e, 0x3d, 0xb9, 0x6e, 0xb6, 0xb0, 0xce, 0xa4, 0x30, 0x89, 0x99, 0xe9, 0xad, 0x11, 0x59}} , + {{0xf6, 0x48, 0x95, 0xa1, 0x6f, 0x5f, 0xb7, 0xa5, 0xbb, 0x30, 0x00, 0x1c, 0xd2, 0x8a, 0xd6, 0x25, 0x26, 0x1b, 0xb2, 0x0d, 0x37, 0x6a, 0x05, 0xf4, 0x9d, 0x3e, 0x17, 0x2a, 0x43, 0xd2, 0x3a, 0x06}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x32, 0x99, 0x93, 0xd1, 0x9a, 0x72, 0xf3, 0xa9, 0x16, 0xbd, 0xb4, 0x4c, 0xdd, 0xf9, 0xd4, 0xb2, 0x64, 0x9a, 0xd3, 0x05, 0xe4, 0xa3, 0x73, 0x1c, 0xcb, 0x7e, 0x57, 0x67, 0xff, 0x04, 0xb3, 0x10}} , + {{0xb9, 0x4b, 0xa4, 0xad, 0xd0, 0x6d, 0x61, 0x23, 0xb4, 0xaf, 0x34, 0xa9, 0xaa, 0x65, 0xec, 0xd9, 0x69, 0xe3, 0x85, 0xcd, 0xcc, 0xe7, 0xb0, 0x9b, 0x41, 0xc1, 0x1c, 0xf9, 0xa0, 0xfa, 0xb7, 0x13}}}, +{{{0x04, 0xfd, 0x88, 0x3c, 0x0c, 0xd0, 0x09, 0x52, 0x51, 0x4f, 0x06, 0x19, 0xcc, 0xc3, 0xbb, 0xde, 0x80, 0xc5, 0x33, 0xbc, 0xf9, 0xf3, 0x17, 0x36, 0xdd, 0xc6, 0xde, 0xe8, 0x9b, 0x5d, 0x79, 0x1b}} , + {{0x65, 0x0a, 0xbe, 0x51, 0x57, 0xad, 0x50, 0x79, 0x08, 0x71, 0x9b, 0x07, 0x95, 0x8f, 0xfb, 0xae, 0x4b, 0x38, 0xba, 0xcf, 0x53, 0x2a, 0x86, 0x1e, 0xc0, 0x50, 0x5c, 0x67, 0x1b, 0xf6, 0x87, 0x6c}}}, +{{{0x4f, 0x00, 0xb2, 0x66, 0x55, 0xed, 0x4a, 0xed, 0x8d, 0xe1, 0x66, 0x18, 0xb2, 0x14, 0x74, 0x8d, 0xfd, 0x1a, 0x36, 0x0f, 0x26, 0x5c, 0x8b, 0x89, 0xf3, 0xab, 0xf2, 0xf3, 0x24, 0x67, 0xfd, 0x70}} , + {{0xfd, 0x4e, 0x2a, 0xc1, 0x3a, 0xca, 0x8f, 0x00, 0xd8, 0xec, 0x74, 0x67, 0xef, 0x61, 0xe0, 0x28, 0xd0, 0x96, 0xf4, 0x48, 0xde, 0x81, 0xe3, 0xef, 0xdc, 0xaa, 0x7d, 0xf3, 0xb6, 0x55, 0xa6, 0x65}}}, +{{{0xeb, 0xcb, 0xc5, 0x70, 0x91, 0x31, 0x10, 0x93, 0x0d, 0xc8, 0xd0, 0xef, 0x62, 0xe8, 0x6f, 0x82, 0xe3, 0x69, 0x3d, 0x91, 0x7f, 0x31, 0xe1, 0x26, 0x35, 0x3c, 0x4a, 0x2f, 0xab, 0xc4, 0x9a, 0x5e}} , + {{0xab, 0x1b, 0xb5, 0xe5, 0x2b, 0xc3, 0x0e, 0x29, 0xb0, 0xd0, 0x73, 0xe6, 0x4f, 0x64, 0xf2, 0xbc, 0xe4, 0xe4, 0xe1, 0x9a, 0x52, 0x33, 0x2f, 0xbd, 0xcc, 0x03, 0xee, 0x8a, 0xfa, 0x00, 0x5f, 0x50}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xf6, 0xdb, 0x0d, 0x22, 0x3d, 0xb5, 0x14, 0x75, 0x31, 0xf0, 0x81, 0xe2, 0xb9, 0x37, 0xa2, 0xa9, 0x84, 0x11, 0x9a, 0x07, 0xb5, 0x53, 0x89, 0x78, 0xa9, 0x30, 0x27, 0xa1, 0xf1, 0x4e, 0x5c, 0x2e}} , + {{0x8b, 0x00, 0x54, 0xfb, 0x4d, 0xdc, 0xcb, 0x17, 0x35, 0x40, 0xff, 0xb7, 0x8c, 0xfe, 0x4a, 0xe4, 0x4e, 0x99, 0x4e, 0xa8, 0x74, 0x54, 0x5d, 0x5c, 0x96, 0xa3, 0x12, 0x55, 0x36, 0x31, 0x17, 0x5c}}}, +{{{0xce, 0x24, 0xef, 0x7b, 0x86, 0xf2, 0x0f, 0x77, 0xe8, 0x5c, 0x7d, 0x87, 0x38, 0x2d, 0xef, 0xaf, 0xf2, 0x8c, 0x72, 0x2e, 0xeb, 0xb6, 0x55, 0x4b, 0x6e, 0xf1, 0x4e, 0x8a, 0x0e, 0x9a, 0x6c, 0x4c}} , + {{0x25, 0xea, 0x86, 0xc2, 0xd1, 0x4f, 0xb7, 0x3e, 0xa8, 0x5c, 0x8d, 0x66, 0x81, 0x25, 0xed, 0xc5, 0x4c, 0x05, 0xb9, 0xd8, 0xd6, 0x70, 0xbe, 0x73, 0x82, 0xe8, 0xa1, 0xe5, 0x1e, 0x71, 0xd5, 0x26}}}, +{{{0x4e, 0x6d, 0xc3, 0xa7, 0x4f, 0x22, 0x45, 0x26, 0xa2, 0x7e, 0x16, 0xf7, 0xf7, 0x63, 0xdc, 0x86, 0x01, 0x2a, 0x71, 0x38, 0x5c, 0x33, 0xc3, 0xce, 0x30, 0xff, 0xf9, 0x2c, 0x91, 0x71, 0x8a, 0x72}} , + {{0x8c, 0x44, 0x09, 0x28, 0xd5, 0x23, 0xc9, 0x8f, 0xf3, 0x84, 0x45, 0xc6, 0x9a, 0x5e, 0xff, 0xd2, 0xc7, 0x57, 0x93, 0xa3, 0xc1, 0x69, 0xdd, 0x62, 0x0f, 0xda, 0x5c, 0x30, 0x59, 0x5d, 0xe9, 0x4c}}}, +{{{0x92, 0x7e, 0x50, 0x27, 0x72, 0xd7, 0x0c, 0xd6, 0x69, 0x96, 0x81, 0x35, 0x84, 0x94, 0x35, 0x8b, 0x6c, 0xaa, 0x62, 0x86, 0x6e, 0x1c, 0x15, 0xf3, 0x6c, 0xb3, 0xff, 0x65, 0x1b, 0xa2, 0x9b, 0x59}} , + {{0xe2, 0xa9, 0x65, 0x88, 0xc4, 0x50, 0xfa, 0xbb, 0x3b, 0x6e, 0x5f, 0x44, 0x01, 0xca, 0x97, 0xd4, 0xdd, 0xf6, 0xcd, 0x3f, 0x3f, 0xe5, 0x97, 0x67, 0x2b, 0x8c, 0x66, 0x0f, 0x35, 0x9b, 0xf5, 0x07}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xf1, 0x59, 0x27, 0xd8, 0xdb, 0x5a, 0x11, 0x5e, 0x82, 0xf3, 0x38, 0xff, 0x1c, 0xed, 0xfe, 0x3f, 0x64, 0x54, 0x3f, 0x7f, 0xd1, 0x81, 0xed, 0xef, 0x65, 0xc5, 0xcb, 0xfd, 0xe1, 0x80, 0xcd, 0x11}} , + {{0xe0, 0xdb, 0x22, 0x28, 0xe6, 0xff, 0x61, 0x9d, 0x41, 0x14, 0x2d, 0x3b, 0x26, 0x22, 0xdf, 0xf1, 0x34, 0x81, 0xe9, 0x45, 0xee, 0x0f, 0x98, 0x8b, 0xa6, 0x3f, 0xef, 0xf7, 0x43, 0x19, 0xf1, 0x43}}}, +{{{0xee, 0xf3, 0x00, 0xa1, 0x50, 0xde, 0xc0, 0xb6, 0x01, 0xe3, 0x8c, 0x3c, 0x4d, 0x31, 0xd2, 0xb0, 0x58, 0xcd, 0xed, 0x10, 0x4a, 0x7a, 0xef, 0x80, 0xa9, 0x19, 0x32, 0xf3, 0xd8, 0x33, 0x8c, 0x06}} , + {{0xcb, 0x7d, 0x4f, 0xff, 0x30, 0xd8, 0x12, 0x3b, 0x39, 0x1c, 0x06, 0xf9, 0x4c, 0x34, 0x35, 0x71, 0xb5, 0x16, 0x94, 0x67, 0xdf, 0xee, 0x11, 0xde, 0xa4, 0x1d, 0x88, 0x93, 0x35, 0xa9, 0x32, 0x10}}}, +{{{0xe9, 0xc3, 0xbc, 0x7b, 0x5c, 0xfc, 0xb2, 0xf9, 0xc9, 0x2f, 0xe5, 0xba, 0x3a, 0x0b, 0xab, 0x64, 0x38, 0x6f, 0x5b, 0x4b, 0x93, 0xda, 0x64, 0xec, 0x4d, 0x3d, 0xa0, 0xf5, 0xbb, 0xba, 0x47, 0x48}} , + {{0x60, 0xbc, 0x45, 0x1f, 0x23, 0xa2, 0x3b, 0x70, 0x76, 0xe6, 0x97, 0x99, 0x4f, 0x77, 0x54, 0x67, 0x30, 0x9a, 0xe7, 0x66, 0xd6, 0xcd, 0x2e, 0x51, 0x24, 0x2c, 0x42, 0x4a, 0x11, 0xfe, 0x6f, 0x7e}}}, +{{{0x87, 0xc0, 0xb1, 0xf0, 0xa3, 0x6f, 0x0c, 0x93, 0xa9, 0x0a, 0x72, 0xef, 0x5c, 0xbe, 0x65, 0x35, 0xa7, 0x6a, 0x4e, 0x2c, 0xbf, 0x21, 0x23, 0xe8, 0x2f, 0x97, 0xc7, 0x3e, 0xc8, 0x17, 0xac, 0x1e}} , + {{0x7b, 0xef, 0x21, 0xe5, 0x40, 0xcc, 0x1e, 0xdc, 0xd6, 0xbd, 0x97, 0x7a, 0x7c, 0x75, 0x86, 0x7a, 0x25, 0x5a, 0x6e, 0x7c, 0xe5, 0x51, 0x3c, 0x1b, 0x5b, 0x82, 0x9a, 0x07, 0x60, 0xa1, 0x19, 0x04}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x96, 0x88, 0xa6, 0xab, 0x8f, 0xe3, 0x3a, 0x49, 0xf8, 0xfe, 0x34, 0xe7, 0x6a, 0xb2, 0xfe, 0x40, 0x26, 0x74, 0x57, 0x4c, 0xf6, 0xd4, 0x99, 0xce, 0x5d, 0x7b, 0x2f, 0x67, 0xd6, 0x5a, 0xe4, 0x4e}} , + {{0x5c, 0x82, 0xb3, 0xbd, 0x55, 0x25, 0xf6, 0x6a, 0x93, 0xa4, 0x02, 0xc6, 0x7d, 0x5c, 0xb1, 0x2b, 0x5b, 0xff, 0xfb, 0x56, 0xf8, 0x01, 0x41, 0x90, 0xc6, 0xb6, 0xac, 0x4f, 0xfe, 0xa7, 0x41, 0x70}}}, +{{{0xdb, 0xfa, 0x9b, 0x2c, 0xd4, 0x23, 0x67, 0x2c, 0x8a, 0x63, 0x6c, 0x07, 0x26, 0x48, 0x4f, 0xc2, 0x03, 0xd2, 0x53, 0x20, 0x28, 0xed, 0x65, 0x71, 0x47, 0xa9, 0x16, 0x16, 0x12, 0xbc, 0x28, 0x33}} , + {{0x39, 0xc0, 0xfa, 0xfa, 0xcd, 0x33, 0x43, 0xc7, 0x97, 0x76, 0x9b, 0x93, 0x91, 0x72, 0xeb, 0xc5, 0x18, 0x67, 0x4c, 0x11, 0xf0, 0xf4, 0xe5, 0x73, 0xb2, 0x5c, 0x1b, 0xc2, 0x26, 0x3f, 0xbf, 0x2b}}}, +{{{0x86, 0xe6, 0x8c, 0x1d, 0xdf, 0xca, 0xfc, 0xd5, 0xf8, 0x3a, 0xc3, 0x44, 0x72, 0xe6, 0x78, 0x9d, 0x2b, 0x97, 0xf8, 0x28, 0x45, 0xb4, 0x20, 0xc9, 0x2a, 0x8c, 0x67, 0xaa, 0x11, 0xc5, 0x5b, 0x2f}} , + {{0x17, 0x0f, 0x86, 0x52, 0xd7, 0x9d, 0xc3, 0x44, 0x51, 0x76, 0x32, 0x65, 0xb4, 0x37, 0x81, 0x99, 0x46, 0x37, 0x62, 0xed, 0xcf, 0x64, 0x9d, 0x72, 0x40, 0x7a, 0x4c, 0x0b, 0x76, 0x2a, 0xfb, 0x56}}}, +{{{0x33, 0xa7, 0x90, 0x7c, 0xc3, 0x6f, 0x17, 0xa5, 0xa0, 0x67, 0x72, 0x17, 0xea, 0x7e, 0x63, 0x14, 0x83, 0xde, 0xc1, 0x71, 0x2d, 0x41, 0x32, 0x7a, 0xf3, 0xd1, 0x2b, 0xd8, 0x2a, 0xa6, 0x46, 0x36}} , + {{0xac, 0xcc, 0x6b, 0x7c, 0xf9, 0xb8, 0x8b, 0x08, 0x5c, 0xd0, 0x7d, 0x8f, 0x73, 0xea, 0x20, 0xda, 0x86, 0xca, 0x00, 0xc7, 0xad, 0x73, 0x4d, 0xe9, 0xe8, 0xa9, 0xda, 0x1f, 0x03, 0x06, 0xdd, 0x24}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x9c, 0xb2, 0x61, 0x0a, 0x98, 0x2a, 0xa5, 0xd7, 0xee, 0xa9, 0xac, 0x65, 0xcb, 0x0a, 0x1e, 0xe2, 0xbe, 0xdc, 0x85, 0x59, 0x0f, 0x9c, 0xa6, 0x57, 0x34, 0xa5, 0x87, 0xeb, 0x7b, 0x1e, 0x0c, 0x3c}} , + {{0x2f, 0xbd, 0x84, 0x63, 0x0d, 0xb5, 0xa0, 0xf0, 0x4b, 0x9e, 0x93, 0xc6, 0x34, 0x9a, 0x34, 0xff, 0x73, 0x19, 0x2f, 0x6e, 0x54, 0x45, 0x2c, 0x92, 0x31, 0x76, 0x34, 0xf1, 0xb2, 0x26, 0xe8, 0x74}}}, +{{{0x0a, 0x67, 0x90, 0x6d, 0x0c, 0x4c, 0xcc, 0xc0, 0xe6, 0xbd, 0xa7, 0x5e, 0x55, 0x8c, 0xcd, 0x58, 0x9b, 0x11, 0xa2, 0xbb, 0x4b, 0xb1, 0x43, 0x04, 0x3c, 0x55, 0xed, 0x23, 0xfe, 0xcd, 0xb1, 0x53}} , + {{0x05, 0xfb, 0x75, 0xf5, 0x01, 0xaf, 0x38, 0x72, 0x58, 0xfc, 0x04, 0x29, 0x34, 0x7a, 0x67, 0xa2, 0x08, 0x50, 0x6e, 0xd0, 0x2b, 0x73, 0xd5, 0xb8, 0xe4, 0x30, 0x96, 0xad, 0x45, 0xdf, 0xa6, 0x5c}}}, +{{{0x0d, 0x88, 0x1a, 0x90, 0x7e, 0xdc, 0xd8, 0xfe, 0xc1, 0x2f, 0x5d, 0x67, 0xee, 0x67, 0x2f, 0xed, 0x6f, 0x55, 0x43, 0x5f, 0x87, 0x14, 0x35, 0x42, 0xd3, 0x75, 0xae, 0xd5, 0xd3, 0x85, 0x1a, 0x76}} , + {{0x87, 0xc8, 0xa0, 0x6e, 0xe1, 0xb0, 0xad, 0x6a, 0x4a, 0x34, 0x71, 0xed, 0x7c, 0xd6, 0x44, 0x03, 0x65, 0x4a, 0x5c, 0x5c, 0x04, 0xf5, 0x24, 0x3f, 0xb0, 0x16, 0x5e, 0x8c, 0xb2, 0xd2, 0xc5, 0x20}}}, +{{{0x98, 0x83, 0xc2, 0x37, 0xa0, 0x41, 0xa8, 0x48, 0x5c, 0x5f, 0xbf, 0xc8, 0xfa, 0x24, 0xe0, 0x59, 0x2c, 0xbd, 0xf6, 0x81, 0x7e, 0x88, 0xe6, 0xca, 0x04, 0xd8, 0x5d, 0x60, 0xbb, 0x74, 0xa7, 0x0b}} , + {{0x21, 0x13, 0x91, 0xbf, 0x77, 0x7a, 0x33, 0xbc, 0xe9, 0x07, 0x39, 0x0a, 0xdd, 0x7d, 0x06, 0x10, 0x9a, 0xee, 0x47, 0x73, 0x1b, 0x15, 0x5a, 0xfb, 0xcd, 0x4d, 0xd0, 0xd2, 0x3a, 0x01, 0xba, 0x54}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x48, 0xd5, 0x39, 0x4a, 0x0b, 0x20, 0x6a, 0x43, 0xa0, 0x07, 0x82, 0x5e, 0x49, 0x7c, 0xc9, 0x47, 0xf1, 0x7c, 0x37, 0xb9, 0x23, 0xef, 0x6b, 0x46, 0x45, 0x8c, 0x45, 0x76, 0xdf, 0x14, 0x6b, 0x6e}} , + {{0x42, 0xc9, 0xca, 0x29, 0x4c, 0x76, 0x37, 0xda, 0x8a, 0x2d, 0x7c, 0x3a, 0x58, 0xf2, 0x03, 0xb4, 0xb5, 0xb9, 0x1a, 0x13, 0x2d, 0xde, 0x5f, 0x6b, 0x9d, 0xba, 0x52, 0xc9, 0x5d, 0xb3, 0xf3, 0x30}}}, +{{{0x4c, 0x6f, 0xfe, 0x6b, 0x0c, 0x62, 0xd7, 0x48, 0x71, 0xef, 0xb1, 0x85, 0x79, 0xc0, 0xed, 0x24, 0xb1, 0x08, 0x93, 0x76, 0x8e, 0xf7, 0x38, 0x8e, 0xeb, 0xfe, 0x80, 0x40, 0xaf, 0x90, 0x64, 0x49}} , + {{0x4a, 0x88, 0xda, 0xc1, 0x98, 0x44, 0x3c, 0x53, 0x4e, 0xdb, 0x4b, 0xb9, 0x12, 0x5f, 0xcd, 0x08, 0x04, 0xef, 0x75, 0xe7, 0xb1, 0x3a, 0xe5, 0x07, 0xfa, 0xca, 0x65, 0x7b, 0x72, 0x10, 0x64, 0x7f}}}, +{{{0x3d, 0x81, 0xf0, 0xeb, 0x16, 0xfd, 0x58, 0x33, 0x8d, 0x7c, 0x1a, 0xfb, 0x20, 0x2c, 0x8a, 0xee, 0x90, 0xbb, 0x33, 0x6d, 0x45, 0xe9, 0x8e, 0x99, 0x85, 0xe1, 0x08, 0x1f, 0xc5, 0xf1, 0xb5, 0x46}} , + {{0xe4, 0xe7, 0x43, 0x4b, 0xa0, 0x3f, 0x2b, 0x06, 0xba, 0x17, 0xae, 0x3d, 0xe6, 0xce, 0xbd, 0xb8, 0xed, 0x74, 0x11, 0x35, 0xec, 0x96, 0xfe, 0x31, 0xe3, 0x0e, 0x7a, 0x4e, 0xc9, 0x1d, 0xcb, 0x20}}}, +{{{0xe0, 0x67, 0xe9, 0x7b, 0xdb, 0x96, 0x5c, 0xb0, 0x32, 0xd0, 0x59, 0x31, 0x90, 0xdc, 0x92, 0x97, 0xac, 0x09, 0x38, 0x31, 0x0f, 0x7e, 0xd6, 0x5d, 0xd0, 0x06, 0xb6, 0x1f, 0xea, 0xf0, 0x5b, 0x07}} , + {{0x81, 0x9f, 0xc7, 0xde, 0x6b, 0x41, 0x22, 0x35, 0x14, 0x67, 0x77, 0x3e, 0x90, 0x81, 0xb0, 0xd9, 0x85, 0x4c, 0xca, 0x9b, 0x3f, 0x04, 0x59, 0xd6, 0xaa, 0x17, 0xc3, 0x88, 0x34, 0x37, 0xba, 0x43}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x4c, 0xb6, 0x69, 0xc8, 0x81, 0x95, 0x94, 0x33, 0x92, 0x34, 0xe9, 0x3c, 0x84, 0x0d, 0x3d, 0x5a, 0x37, 0x9c, 0x22, 0xa0, 0xaa, 0x65, 0xce, 0xb4, 0xc2, 0x2d, 0x66, 0x67, 0x02, 0xff, 0x74, 0x10}} , + {{0x22, 0xb0, 0xd5, 0xe6, 0xc7, 0xef, 0xb1, 0xa7, 0x13, 0xda, 0x60, 0xb4, 0x80, 0xc1, 0x42, 0x7d, 0x10, 0x70, 0x97, 0x04, 0x4d, 0xda, 0x23, 0x89, 0xc2, 0x0e, 0x68, 0xcb, 0xde, 0xe0, 0x9b, 0x29}}}, +{{{0x33, 0xfe, 0x42, 0x2a, 0x36, 0x2b, 0x2e, 0x36, 0x64, 0x5c, 0x8b, 0xcc, 0x81, 0x6a, 0x15, 0x08, 0xa1, 0x27, 0xe8, 0x57, 0xe5, 0x78, 0x8e, 0xf2, 0x58, 0x19, 0x12, 0x42, 0xae, 0xc4, 0x63, 0x3e}} , + {{0x78, 0x96, 0x9c, 0xa7, 0xca, 0x80, 0xae, 0x02, 0x85, 0xb1, 0x7c, 0x04, 0x5c, 0xc1, 0x5b, 0x26, 0xc1, 0xba, 0xed, 0xa5, 0x59, 0x70, 0x85, 0x8c, 0x8c, 0xe8, 0x87, 0xac, 0x6a, 0x28, 0x99, 0x35}}}, +{{{0x9f, 0x04, 0x08, 0x28, 0xbe, 0x87, 0xda, 0x80, 0x28, 0x38, 0xde, 0x9f, 0xcd, 0xe4, 0xe3, 0x62, 0xfb, 0x2e, 0x46, 0x8d, 0x01, 0xb3, 0x06, 0x51, 0xd4, 0x19, 0x3b, 0x11, 0xfa, 0xe2, 0xad, 0x1e}} , + {{0xa0, 0x20, 0x99, 0x69, 0x0a, 0xae, 0xa3, 0x70, 0x4e, 0x64, 0x80, 0xb7, 0x85, 0x9c, 0x87, 0x54, 0x43, 0x43, 0x55, 0x80, 0x6d, 0x8d, 0x7c, 0xa9, 0x64, 0xca, 0x6c, 0x2e, 0x21, 0xd8, 0xc8, 0x6c}}}, +{{{0x91, 0x4a, 0x07, 0xad, 0x08, 0x75, 0xc1, 0x4f, 0xa4, 0xb2, 0xc3, 0x6f, 0x46, 0x3e, 0xb1, 0xce, 0x52, 0xab, 0x67, 0x09, 0x54, 0x48, 0x6b, 0x6c, 0xd7, 0x1d, 0x71, 0x76, 0xcb, 0xff, 0xdd, 0x31}} , + {{0x36, 0x88, 0xfa, 0xfd, 0xf0, 0x36, 0x6f, 0x07, 0x74, 0x88, 0x50, 0xd0, 0x95, 0x38, 0x4a, 0x48, 0x2e, 0x07, 0x64, 0x97, 0x11, 0x76, 0x01, 0x1a, 0x27, 0x4d, 0x8e, 0x25, 0x9a, 0x9b, 0x1c, 0x22}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xbe, 0x57, 0xbd, 0x0e, 0x0f, 0xac, 0x5e, 0x76, 0xa3, 0x71, 0xad, 0x2b, 0x10, 0x45, 0x02, 0xec, 0x59, 0xd5, 0x5d, 0xa9, 0x44, 0xcc, 0x25, 0x4c, 0xb3, 0x3c, 0x5b, 0x69, 0x07, 0x55, 0x26, 0x6b}} , + {{0x30, 0x6b, 0xd4, 0xa7, 0x51, 0x29, 0xe3, 0xf9, 0x7a, 0x75, 0x2a, 0x82, 0x2f, 0xd6, 0x1d, 0x99, 0x2b, 0x80, 0xd5, 0x67, 0x1e, 0x15, 0x9d, 0xca, 0xfd, 0xeb, 0xac, 0x97, 0x35, 0x09, 0x7f, 0x3f}}}, +{{{0x35, 0x0d, 0x34, 0x0a, 0xb8, 0x67, 0x56, 0x29, 0x20, 0xf3, 0x19, 0x5f, 0xe2, 0x83, 0x42, 0x73, 0x53, 0xa8, 0xc5, 0x02, 0x19, 0x33, 0xb4, 0x64, 0xbd, 0xc3, 0x87, 0x8c, 0xd7, 0x76, 0xed, 0x25}} , + {{0x47, 0x39, 0x37, 0x76, 0x0d, 0x1d, 0x0c, 0xf5, 0x5a, 0x6d, 0x43, 0x88, 0x99, 0x15, 0xb4, 0x52, 0x0f, 0x2a, 0xb3, 0xb0, 0x3f, 0xa6, 0xb3, 0x26, 0xb3, 0xc7, 0x45, 0xf5, 0x92, 0x5f, 0x9b, 0x17}}}, +{{{0x9d, 0x23, 0xbd, 0x15, 0xfe, 0x52, 0x52, 0x15, 0x26, 0x79, 0x86, 0xba, 0x06, 0x56, 0x66, 0xbb, 0x8c, 0x2e, 0x10, 0x11, 0xd5, 0x4a, 0x18, 0x52, 0xda, 0x84, 0x44, 0xf0, 0x3e, 0xe9, 0x8c, 0x35}} , + {{0xad, 0xa0, 0x41, 0xec, 0xc8, 0x4d, 0xb9, 0xd2, 0x6e, 0x96, 0x4e, 0x5b, 0xc5, 0xc2, 0xa0, 0x1b, 0xcf, 0x0c, 0xbf, 0x17, 0x66, 0x57, 0xc1, 0x17, 0x90, 0x45, 0x71, 0xc2, 0xe1, 0x24, 0xeb, 0x27}}}, +{{{0x2c, 0xb9, 0x42, 0xa4, 0xaf, 0x3b, 0x42, 0x0e, 0xc2, 0x0f, 0xf2, 0xea, 0x83, 0xaf, 0x9a, 0x13, 0x17, 0xb0, 0xbd, 0x89, 0x17, 0xe3, 0x72, 0xcb, 0x0e, 0x76, 0x7e, 0x41, 0x63, 0x04, 0x88, 0x71}} , + {{0x75, 0x78, 0x38, 0x86, 0x57, 0xdd, 0x9f, 0xee, 0x54, 0x70, 0x65, 0xbf, 0xf1, 0x2c, 0xe0, 0x39, 0x0d, 0xe3, 0x89, 0xfd, 0x8e, 0x93, 0x4f, 0x43, 0xdc, 0xd5, 0x5b, 0xde, 0xf9, 0x98, 0xe5, 0x7b}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xe7, 0x3b, 0x65, 0x11, 0xdf, 0xb2, 0xf2, 0x63, 0x94, 0x12, 0x6f, 0x5c, 0x9e, 0x77, 0xc1, 0xb6, 0xd8, 0xab, 0x58, 0x7a, 0x1d, 0x95, 0x73, 0xdd, 0xe7, 0xe3, 0x6f, 0xf2, 0x03, 0x1d, 0xdb, 0x76}} , + {{0xae, 0x06, 0x4e, 0x2c, 0x52, 0x1b, 0xbc, 0x5a, 0x5a, 0xa5, 0xbe, 0x27, 0xbd, 0xeb, 0xe1, 0x14, 0x17, 0x68, 0x26, 0x07, 0x03, 0xd1, 0x18, 0x0b, 0xdf, 0xf1, 0x06, 0x5c, 0xa6, 0x1b, 0xb9, 0x24}}}, +{{{0xc5, 0x66, 0x80, 0x13, 0x0e, 0x48, 0x8c, 0x87, 0x31, 0x84, 0xb4, 0x60, 0xed, 0xc5, 0xec, 0xb6, 0xc5, 0x05, 0x33, 0x5f, 0x2f, 0x7d, 0x40, 0xb6, 0x32, 0x1d, 0x38, 0x74, 0x1b, 0xf1, 0x09, 0x3d}} , + {{0xd4, 0x69, 0x82, 0xbc, 0x8d, 0xf8, 0x34, 0x36, 0x75, 0x55, 0x18, 0x55, 0x58, 0x3c, 0x79, 0xaf, 0x26, 0x80, 0xab, 0x9b, 0x95, 0x00, 0xf1, 0xcb, 0xda, 0xc1, 0x9f, 0xf6, 0x2f, 0xa2, 0xf4, 0x45}}}, +{{{0x17, 0xbe, 0xeb, 0x85, 0xed, 0x9e, 0xcd, 0x56, 0xf5, 0x17, 0x45, 0x42, 0xb4, 0x1f, 0x44, 0x4c, 0x05, 0x74, 0x15, 0x47, 0x00, 0xc6, 0x6a, 0x3d, 0x24, 0x09, 0x0d, 0x58, 0xb1, 0x42, 0xd7, 0x04}} , + {{0x8d, 0xbd, 0xa3, 0xc4, 0x06, 0x9b, 0x1f, 0x90, 0x58, 0x60, 0x74, 0xb2, 0x00, 0x3b, 0x3c, 0xd2, 0xda, 0x82, 0xbb, 0x10, 0x90, 0x69, 0x92, 0xa9, 0xb4, 0x30, 0x81, 0xe3, 0x7c, 0xa8, 0x89, 0x45}}}, +{{{0x3f, 0xdc, 0x05, 0xcb, 0x41, 0x3c, 0xc8, 0x23, 0x04, 0x2c, 0x38, 0x99, 0xe3, 0x68, 0x55, 0xf9, 0xd3, 0x32, 0xc7, 0xbf, 0xfa, 0xd4, 0x1b, 0x5d, 0xde, 0xdc, 0x10, 0x42, 0xc0, 0x42, 0xd9, 0x75}} , + {{0x2d, 0xab, 0x35, 0x4e, 0x87, 0xc4, 0x65, 0x97, 0x67, 0x24, 0xa4, 0x47, 0xad, 0x3f, 0x8e, 0xf3, 0xcb, 0x31, 0x17, 0x77, 0xc5, 0xe2, 0xd7, 0x8f, 0x3c, 0xc1, 0xcd, 0x56, 0x48, 0xc1, 0x6c, 0x69}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x14, 0xae, 0x5f, 0x88, 0x7b, 0xa5, 0x90, 0xdf, 0x10, 0xb2, 0x8b, 0x5e, 0x24, 0x17, 0xc3, 0xa3, 0xd4, 0x0f, 0x92, 0x61, 0x1a, 0x19, 0x5a, 0xad, 0x76, 0xbd, 0xd8, 0x1c, 0xdd, 0xe0, 0x12, 0x6d}} , + {{0x8e, 0xbd, 0x70, 0x8f, 0x02, 0xa3, 0x24, 0x4d, 0x5a, 0x67, 0xc4, 0xda, 0xf7, 0x20, 0x0f, 0x81, 0x5b, 0x7a, 0x05, 0x24, 0x67, 0x83, 0x0b, 0x2a, 0x80, 0xe7, 0xfd, 0x74, 0x4b, 0x9e, 0x5c, 0x0d}}}, +{{{0x94, 0xd5, 0x5f, 0x1f, 0xa2, 0xfb, 0xeb, 0xe1, 0x07, 0x34, 0xf8, 0x20, 0xad, 0x81, 0x30, 0x06, 0x2d, 0xa1, 0x81, 0x95, 0x36, 0xcf, 0x11, 0x0b, 0xaf, 0xc1, 0x2b, 0x9a, 0x6c, 0x55, 0xc1, 0x16}} , + {{0x36, 0x4f, 0xf1, 0x5e, 0x74, 0x35, 0x13, 0x28, 0xd7, 0x11, 0xcf, 0xb8, 0xde, 0x93, 0xb3, 0x05, 0xb8, 0xb5, 0x73, 0xe9, 0xeb, 0xad, 0x19, 0x1e, 0x89, 0x0f, 0x8b, 0x15, 0xd5, 0x8c, 0xe3, 0x23}}}, +{{{0x33, 0x79, 0xe7, 0x18, 0xe6, 0x0f, 0x57, 0x93, 0x15, 0xa0, 0xa7, 0xaa, 0xc4, 0xbf, 0x4f, 0x30, 0x74, 0x95, 0x5e, 0x69, 0x4a, 0x5b, 0x45, 0xe4, 0x00, 0xeb, 0x23, 0x74, 0x4c, 0xdf, 0x6b, 0x45}} , + {{0x97, 0x29, 0x6c, 0xc4, 0x42, 0x0b, 0xdd, 0xc0, 0x29, 0x5c, 0x9b, 0x34, 0x97, 0xd0, 0xc7, 0x79, 0x80, 0x63, 0x74, 0xe4, 0x8e, 0x37, 0xb0, 0x2b, 0x7c, 0xe8, 0x68, 0x6c, 0xc3, 0x82, 0x97, 0x57}}}, +{{{0x22, 0xbe, 0x83, 0xb6, 0x4b, 0x80, 0x6b, 0x43, 0x24, 0x5e, 0xef, 0x99, 0x9b, 0xa8, 0xfc, 0x25, 0x8d, 0x3b, 0x03, 0x94, 0x2b, 0x3e, 0xe7, 0x95, 0x76, 0x9b, 0xcc, 0x15, 0xdb, 0x32, 0xe6, 0x66}} , + {{0x84, 0xf0, 0x4a, 0x13, 0xa6, 0xd6, 0xfa, 0x93, 0x46, 0x07, 0xf6, 0x7e, 0x5c, 0x6d, 0x5e, 0xf6, 0xa6, 0xe7, 0x48, 0xf0, 0x06, 0xea, 0xff, 0x90, 0xc1, 0xcc, 0x4c, 0x19, 0x9c, 0x3c, 0x4e, 0x53}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x2a, 0x50, 0xe3, 0x07, 0x15, 0x59, 0xf2, 0x8b, 0x81, 0xf2, 0xf3, 0xd3, 0x6c, 0x99, 0x8c, 0x70, 0x67, 0xec, 0xcc, 0xee, 0x9e, 0x59, 0x45, 0x59, 0x7d, 0x47, 0x75, 0x69, 0xf5, 0x24, 0x93, 0x5d}} , + {{0x6a, 0x4f, 0x1b, 0xbe, 0x6b, 0x30, 0xcf, 0x75, 0x46, 0xe3, 0x7b, 0x9d, 0xfc, 0xcd, 0xd8, 0x5c, 0x1f, 0xb4, 0xc8, 0xe2, 0x24, 0xec, 0x1a, 0x28, 0x05, 0x32, 0x57, 0xfd, 0x3c, 0x5a, 0x98, 0x10}}}, +{{{0xa3, 0xdb, 0xf7, 0x30, 0xd8, 0xc2, 0x9a, 0xe1, 0xd3, 0xce, 0x22, 0xe5, 0x80, 0x1e, 0xd9, 0xe4, 0x1f, 0xab, 0xc0, 0x71, 0x1a, 0x86, 0x0e, 0x27, 0x99, 0x5b, 0xfa, 0x76, 0x99, 0xb0, 0x08, 0x3c}} , + {{0x2a, 0x93, 0xd2, 0x85, 0x1b, 0x6a, 0x5d, 0xa6, 0xee, 0xd1, 0xd1, 0x33, 0xbd, 0x6a, 0x36, 0x73, 0x37, 0x3a, 0x44, 0xb4, 0xec, 0xa9, 0x7a, 0xde, 0x83, 0x40, 0xd7, 0xdf, 0x28, 0xba, 0xa2, 0x30}}}, +{{{0xd3, 0xb5, 0x6d, 0x05, 0x3f, 0x9f, 0xf3, 0x15, 0x8d, 0x7c, 0xca, 0xc9, 0xfc, 0x8a, 0x7c, 0x94, 0xb0, 0x63, 0x36, 0x9b, 0x78, 0xd1, 0x91, 0x1f, 0x93, 0xd8, 0x57, 0x43, 0xde, 0x76, 0xa3, 0x43}} , + {{0x9b, 0x35, 0xe2, 0xa9, 0x3d, 0x32, 0x1e, 0xbb, 0x16, 0x28, 0x70, 0xe9, 0x45, 0x2f, 0x8f, 0x70, 0x7f, 0x08, 0x7e, 0x53, 0xc4, 0x7a, 0xbf, 0xf7, 0xe1, 0xa4, 0x6a, 0xd8, 0xac, 0x64, 0x1b, 0x11}}}, +{{{0xb2, 0xeb, 0x47, 0x46, 0x18, 0x3e, 0x1f, 0x99, 0x0c, 0xcc, 0xf1, 0x2c, 0xe0, 0xe7, 0x8f, 0xe0, 0x01, 0x7e, 0x65, 0xb8, 0x0c, 0xd0, 0xfb, 0xc8, 0xb9, 0x90, 0x98, 0x33, 0x61, 0x3b, 0xd8, 0x27}} , + {{0xa0, 0xbe, 0x72, 0x3a, 0x50, 0x4b, 0x74, 0xab, 0x01, 0xc8, 0x93, 0xc5, 0xe4, 0xc7, 0x08, 0x6c, 0xb4, 0xca, 0xee, 0xeb, 0x8e, 0xd7, 0x4e, 0x26, 0xc6, 0x1d, 0xe2, 0x71, 0xaf, 0x89, 0xa0, 0x2a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x98, 0x0b, 0xe4, 0xde, 0xdb, 0xa8, 0xfa, 0x82, 0x74, 0x06, 0x52, 0x6d, 0x08, 0x52, 0x8a, 0xff, 0x62, 0xc5, 0x6a, 0x44, 0x0f, 0x51, 0x8c, 0x1f, 0x6e, 0xb6, 0xc6, 0x2c, 0x81, 0xd3, 0x76, 0x46}} , + {{0xf4, 0x29, 0x74, 0x2e, 0x80, 0xa7, 0x1a, 0x8f, 0xf6, 0xbd, 0xd6, 0x8e, 0xbf, 0xc1, 0x95, 0x2a, 0xeb, 0xa0, 0x7f, 0x45, 0xa0, 0x50, 0x14, 0x05, 0xb1, 0x57, 0x4c, 0x74, 0xb7, 0xe2, 0x89, 0x7d}}}, +{{{0x07, 0xee, 0xa7, 0xad, 0xb7, 0x09, 0x0b, 0x49, 0x4e, 0xbf, 0xca, 0xe5, 0x21, 0xe6, 0xe6, 0xaf, 0xd5, 0x67, 0xf3, 0xce, 0x7e, 0x7c, 0x93, 0x7b, 0x5a, 0x10, 0x12, 0x0e, 0x6c, 0x06, 0x11, 0x75}} , + {{0xd5, 0xfc, 0x86, 0xa3, 0x3b, 0xa3, 0x3e, 0x0a, 0xfb, 0x0b, 0xf7, 0x36, 0xb1, 0x5b, 0xda, 0x70, 0xb7, 0x00, 0xa7, 0xda, 0x88, 0x8f, 0x84, 0xa8, 0xbc, 0x1c, 0x39, 0xb8, 0x65, 0xf3, 0x4d, 0x60}}}, +{{{0x96, 0x9d, 0x31, 0xf4, 0xa2, 0xbe, 0x81, 0xb9, 0xa5, 0x59, 0x9e, 0xba, 0x07, 0xbe, 0x74, 0x58, 0xd8, 0xeb, 0xc5, 0x9f, 0x3d, 0xd1, 0xf4, 0xae, 0xce, 0x53, 0xdf, 0x4f, 0xc7, 0x2a, 0x89, 0x4d}} , + {{0x29, 0xd8, 0xf2, 0xaa, 0xe9, 0x0e, 0xf7, 0x2e, 0x5f, 0x9d, 0x8a, 0x5b, 0x09, 0xed, 0xc9, 0x24, 0x22, 0xf4, 0x0f, 0x25, 0x8f, 0x1c, 0x84, 0x6e, 0x34, 0x14, 0x6c, 0xea, 0xb3, 0x86, 0x5d, 0x04}}}, +{{{0x07, 0x98, 0x61, 0xe8, 0x6a, 0xd2, 0x81, 0x49, 0x25, 0xd5, 0x5b, 0x18, 0xc7, 0x35, 0x52, 0x51, 0xa4, 0x46, 0xad, 0x18, 0x0d, 0xc9, 0x5f, 0x18, 0x91, 0x3b, 0xb4, 0xc0, 0x60, 0x59, 0x8d, 0x66}} , + {{0x03, 0x1b, 0x79, 0x53, 0x6e, 0x24, 0xae, 0x57, 0xd9, 0x58, 0x09, 0x85, 0x48, 0xa2, 0xd3, 0xb5, 0xe2, 0x4d, 0x11, 0x82, 0xe6, 0x86, 0x3c, 0xe9, 0xb1, 0x00, 0x19, 0xc2, 0x57, 0xf7, 0x66, 0x7a}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x0f, 0xe3, 0x89, 0x03, 0xd7, 0x22, 0x95, 0x9f, 0xca, 0xb4, 0x8d, 0x9e, 0x6d, 0x97, 0xff, 0x8d, 0x21, 0x59, 0x07, 0xef, 0x03, 0x2d, 0x5e, 0xf8, 0x44, 0x46, 0xe7, 0x85, 0x80, 0xc5, 0x89, 0x50}} , + {{0x8b, 0xd8, 0x53, 0x86, 0x24, 0x86, 0x29, 0x52, 0x01, 0xfa, 0x20, 0xc3, 0x4e, 0x95, 0xcb, 0xad, 0x7b, 0x34, 0x94, 0x30, 0xb7, 0x7a, 0xfa, 0x96, 0x41, 0x60, 0x2b, 0xcb, 0x59, 0xb9, 0xca, 0x50}}}, +{{{0xc2, 0x5b, 0x9b, 0x78, 0x23, 0x1b, 0x3a, 0x88, 0x94, 0x5f, 0x0a, 0x9b, 0x98, 0x2b, 0x6e, 0x53, 0x11, 0xf6, 0xff, 0xc6, 0x7d, 0x42, 0xcc, 0x02, 0x80, 0x40, 0x0d, 0x1e, 0xfb, 0xaf, 0x61, 0x07}} , + {{0xb0, 0xe6, 0x2f, 0x81, 0x70, 0xa1, 0x2e, 0x39, 0x04, 0x7c, 0xc4, 0x2c, 0x87, 0x45, 0x4a, 0x5b, 0x69, 0x97, 0xac, 0x6d, 0x2c, 0x10, 0x42, 0x7c, 0x3b, 0x15, 0x70, 0x60, 0x0e, 0x11, 0x6d, 0x3a}}}, +{{{0x9b, 0x18, 0x80, 0x5e, 0xdb, 0x05, 0xbd, 0xc6, 0xb7, 0x3c, 0xc2, 0x40, 0x4d, 0x5d, 0xce, 0x97, 0x8a, 0x34, 0x15, 0xab, 0x28, 0x5d, 0x10, 0xf0, 0x37, 0x0c, 0xcc, 0x16, 0xfa, 0x1f, 0x33, 0x0d}} , + {{0x19, 0xf9, 0x35, 0xaa, 0x59, 0x1a, 0x0c, 0x5c, 0x06, 0xfc, 0x6a, 0x0b, 0x97, 0x53, 0x36, 0xfc, 0x2a, 0xa5, 0x5a, 0x9b, 0x30, 0xef, 0x23, 0xaf, 0x39, 0x5d, 0x9a, 0x6b, 0x75, 0x57, 0x48, 0x0b}}}, +{{{0x26, 0xdc, 0x76, 0x3b, 0xfc, 0xf9, 0x9c, 0x3f, 0x89, 0x0b, 0x62, 0x53, 0xaf, 0x83, 0x01, 0x2e, 0xbc, 0x6a, 0xc6, 0x03, 0x0d, 0x75, 0x2a, 0x0d, 0xe6, 0x94, 0x54, 0xcf, 0xb3, 0xe5, 0x96, 0x25}} , + {{0xfe, 0x82, 0xb1, 0x74, 0x31, 0x8a, 0xa7, 0x6f, 0x56, 0xbd, 0x8d, 0xf4, 0xe0, 0x94, 0x51, 0x59, 0xde, 0x2c, 0x5a, 0xf4, 0x84, 0x6b, 0x4a, 0x88, 0x93, 0xc0, 0x0c, 0x9a, 0xac, 0xa7, 0xa0, 0x68}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x25, 0x0d, 0xd6, 0xc7, 0x23, 0x47, 0x10, 0xad, 0xc7, 0x08, 0x5c, 0x87, 0x87, 0x93, 0x98, 0x18, 0xb8, 0xd3, 0x9c, 0xac, 0x5a, 0x3d, 0xc5, 0x75, 0xf8, 0x49, 0x32, 0x14, 0xcc, 0x51, 0x96, 0x24}} , + {{0x65, 0x9c, 0x5d, 0xf0, 0x37, 0x04, 0xf0, 0x34, 0x69, 0x2a, 0xf0, 0xa5, 0x64, 0xca, 0xde, 0x2b, 0x5b, 0x15, 0x10, 0xd2, 0xab, 0x06, 0xdd, 0xc4, 0xb0, 0xb6, 0x5b, 0xc1, 0x17, 0xdf, 0x8f, 0x02}}}, +{{{0xbd, 0x59, 0x3d, 0xbf, 0x5c, 0x31, 0x44, 0x2c, 0x32, 0x94, 0x04, 0x60, 0x84, 0x0f, 0xad, 0x00, 0xb6, 0x8f, 0xc9, 0x1d, 0xcc, 0x5c, 0xa2, 0x49, 0x0e, 0x50, 0x91, 0x08, 0x9a, 0x43, 0x55, 0x05}} , + {{0x5d, 0x93, 0x55, 0xdf, 0x9b, 0x12, 0x19, 0xec, 0x93, 0x85, 0x42, 0x9e, 0x66, 0x0f, 0x9d, 0xaf, 0x99, 0xaf, 0x26, 0x89, 0xbc, 0x61, 0xfd, 0xff, 0xce, 0x4b, 0xf4, 0x33, 0x95, 0xc9, 0x35, 0x58}}}, +{{{0x12, 0x55, 0xf9, 0xda, 0xcb, 0x44, 0xa7, 0xdc, 0x57, 0xe2, 0xf9, 0x9a, 0xe6, 0x07, 0x23, 0x60, 0x54, 0xa7, 0x39, 0xa5, 0x9b, 0x84, 0x56, 0x6e, 0xaa, 0x8b, 0x8f, 0xb0, 0x2c, 0x87, 0xaf, 0x67}} , + {{0x00, 0xa9, 0x4c, 0xb2, 0x12, 0xf8, 0x32, 0xa8, 0x7a, 0x00, 0x4b, 0x49, 0x32, 0xba, 0x1f, 0x5d, 0x44, 0x8e, 0x44, 0x7a, 0xdc, 0x11, 0xfb, 0x39, 0x08, 0x57, 0x87, 0xa5, 0x12, 0x42, 0x93, 0x0e}}}, +{{{0x17, 0xb4, 0xae, 0x72, 0x59, 0xd0, 0xaa, 0xa8, 0x16, 0x8b, 0x63, 0x11, 0xb3, 0x43, 0x04, 0xda, 0x0c, 0xa8, 0xb7, 0x68, 0xdd, 0x4e, 0x54, 0xe7, 0xaf, 0x5d, 0x5d, 0x05, 0x76, 0x36, 0xec, 0x0d}} , + {{0x6d, 0x7c, 0x82, 0x32, 0x38, 0x55, 0x57, 0x74, 0x5b, 0x7d, 0xc3, 0xc4, 0xfb, 0x06, 0x29, 0xf0, 0x13, 0x55, 0x54, 0xc6, 0xa7, 0xdc, 0x4c, 0x9f, 0x98, 0x49, 0x20, 0xa8, 0xc3, 0x8d, 0xfa, 0x48}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x87, 0x47, 0x9d, 0xe9, 0x25, 0xd5, 0xe3, 0x47, 0x78, 0xdf, 0x85, 0xa7, 0x85, 0x5e, 0x7a, 0x4c, 0x5f, 0x79, 0x1a, 0xf3, 0xa2, 0xb2, 0x28, 0xa0, 0x9c, 0xdd, 0x30, 0x40, 0xd4, 0x38, 0xbd, 0x28}} , + {{0xfc, 0xbb, 0xd5, 0x78, 0x6d, 0x1d, 0xd4, 0x99, 0xb4, 0xaa, 0x44, 0x44, 0x7a, 0x1b, 0xd8, 0xfe, 0xb4, 0x99, 0xb9, 0xcc, 0xe7, 0xc4, 0xd3, 0x3a, 0x73, 0x83, 0x41, 0x5c, 0x40, 0xd7, 0x2d, 0x55}}}, +{{{0x26, 0xe1, 0x7b, 0x5f, 0xe5, 0xdc, 0x3f, 0x7d, 0xa1, 0xa7, 0x26, 0x44, 0x22, 0x23, 0xc0, 0x8f, 0x7d, 0xf1, 0xb5, 0x11, 0x47, 0x7b, 0x19, 0xd4, 0x75, 0x6f, 0x1e, 0xa5, 0x27, 0xfe, 0xc8, 0x0e}} , + {{0xd3, 0x11, 0x3d, 0xab, 0xef, 0x2c, 0xed, 0xb1, 0x3d, 0x7c, 0x32, 0x81, 0x6b, 0xfe, 0xf8, 0x1c, 0x3c, 0x7b, 0xc0, 0x61, 0xdf, 0xb8, 0x75, 0x76, 0x7f, 0xaa, 0xd8, 0x93, 0xaf, 0x3d, 0xe8, 0x3d}}}, +{{{0xfd, 0x5b, 0x4e, 0x8d, 0xb6, 0x7e, 0x82, 0x9b, 0xef, 0xce, 0x04, 0x69, 0x51, 0x52, 0xff, 0xef, 0xa0, 0x52, 0xb5, 0x79, 0x17, 0x5e, 0x2f, 0xde, 0xd6, 0x3c, 0x2d, 0xa0, 0x43, 0xb4, 0x0b, 0x19}} , + {{0xc0, 0x61, 0x48, 0x48, 0x17, 0xf4, 0x9e, 0x18, 0x51, 0x2d, 0xea, 0x2f, 0xf2, 0xf2, 0xe0, 0xa3, 0x14, 0xb7, 0x8b, 0x3a, 0x30, 0xf5, 0x81, 0xc1, 0x5d, 0x71, 0x39, 0x62, 0x55, 0x1f, 0x60, 0x5a}}}, +{{{0xe5, 0x89, 0x8a, 0x76, 0x6c, 0xdb, 0x4d, 0x0a, 0x5b, 0x72, 0x9d, 0x59, 0x6e, 0x63, 0x63, 0x18, 0x7c, 0xe3, 0xfa, 0xe2, 0xdb, 0xa1, 0x8d, 0xf4, 0xa5, 0xd7, 0x16, 0xb2, 0xd0, 0xb3, 0x3f, 0x39}} , + {{0xce, 0x60, 0x09, 0x6c, 0xf5, 0x76, 0x17, 0x24, 0x80, 0x3a, 0x96, 0xc7, 0x94, 0x2e, 0xf7, 0x6b, 0xef, 0xb5, 0x05, 0x96, 0xef, 0xd3, 0x7b, 0x51, 0xda, 0x05, 0x44, 0x67, 0xbc, 0x07, 0x21, 0x4e}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xe9, 0x73, 0x6f, 0x21, 0xb9, 0xde, 0x22, 0x7d, 0xeb, 0x97, 0x31, 0x10, 0xa3, 0xea, 0xe1, 0xc6, 0x37, 0xeb, 0x8f, 0x43, 0x58, 0xde, 0x41, 0x64, 0x0e, 0x3e, 0x07, 0x99, 0x3d, 0xf1, 0xdf, 0x1e}} , + {{0xf8, 0xad, 0x43, 0xc2, 0x17, 0x06, 0xe2, 0xe4, 0xa9, 0x86, 0xcd, 0x18, 0xd7, 0x78, 0xc8, 0x74, 0x66, 0xd2, 0x09, 0x18, 0xa5, 0xf1, 0xca, 0xa6, 0x62, 0x92, 0xc1, 0xcb, 0x00, 0xeb, 0x42, 0x2e}}}, +{{{0x7b, 0x34, 0x24, 0x4c, 0xcf, 0x38, 0xe5, 0x6c, 0x0a, 0x01, 0x2c, 0x22, 0x0b, 0x24, 0x38, 0xad, 0x24, 0x7e, 0x19, 0xf0, 0x6c, 0xf9, 0x31, 0xf4, 0x35, 0x11, 0xf6, 0x46, 0x33, 0x3a, 0x23, 0x59}} , + {{0x20, 0x0b, 0xa1, 0x08, 0x19, 0xad, 0x39, 0x54, 0xea, 0x3e, 0x23, 0x09, 0xb6, 0xe2, 0xd2, 0xbc, 0x4d, 0xfc, 0x9c, 0xf0, 0x13, 0x16, 0x22, 0x3f, 0xb9, 0xd2, 0x11, 0x86, 0x90, 0x55, 0xce, 0x3c}}}, +{{{0xc4, 0x0b, 0x4b, 0x62, 0x99, 0x37, 0x84, 0x3f, 0x74, 0xa2, 0xf9, 0xce, 0xe2, 0x0b, 0x0f, 0x2a, 0x3d, 0xa3, 0xe3, 0xdb, 0x5a, 0x9d, 0x93, 0xcc, 0xa5, 0xef, 0x82, 0x91, 0x1d, 0xe6, 0x6c, 0x68}} , + {{0xa3, 0x64, 0x17, 0x9b, 0x8b, 0xc8, 0x3a, 0x61, 0xe6, 0x9d, 0xc6, 0xed, 0x7b, 0x03, 0x52, 0x26, 0x9d, 0x3a, 0xb3, 0x13, 0xcc, 0x8a, 0xfd, 0x2c, 0x1a, 0x1d, 0xed, 0x13, 0xd0, 0x55, 0x57, 0x0e}}}, +{{{0x1a, 0xea, 0xbf, 0xfd, 0x4a, 0x3c, 0x8e, 0xec, 0x29, 0x7e, 0x77, 0x77, 0x12, 0x99, 0xd7, 0x84, 0xf9, 0x55, 0x7f, 0xf1, 0x8b, 0xb4, 0xd2, 0x95, 0xa3, 0x8d, 0xf0, 0x8a, 0xa7, 0xeb, 0x82, 0x4b}} , + {{0x2c, 0x28, 0xf4, 0x3a, 0xf6, 0xde, 0x0a, 0xe0, 0x41, 0x44, 0x23, 0xf8, 0x3f, 0x03, 0x64, 0x9f, 0xc3, 0x55, 0x4c, 0xc6, 0xc1, 0x94, 0x1c, 0x24, 0x5d, 0x5f, 0x92, 0x45, 0x96, 0x57, 0x37, 0x14}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xc1, 0xcd, 0x90, 0x66, 0xb9, 0x76, 0xa0, 0x5b, 0xa5, 0x85, 0x75, 0x23, 0xf9, 0x89, 0xa5, 0x82, 0xb2, 0x6f, 0xb1, 0xeb, 0xc4, 0x69, 0x6f, 0x18, 0x5a, 0xed, 0x94, 0x3d, 0x9d, 0xd9, 0x2c, 0x1a}} , + {{0x35, 0xb0, 0xe6, 0x73, 0x06, 0xb7, 0x37, 0xe0, 0xf8, 0xb0, 0x22, 0xe8, 0xd2, 0xed, 0x0b, 0xef, 0xe6, 0xc6, 0x5a, 0x99, 0x9e, 0x1a, 0x9f, 0x04, 0x97, 0xe4, 0x4d, 0x0b, 0xbe, 0xba, 0x44, 0x40}}}, +{{{0xc1, 0x56, 0x96, 0x91, 0x5f, 0x1f, 0xbb, 0x54, 0x6f, 0x88, 0x89, 0x0a, 0xb2, 0xd6, 0x41, 0x42, 0x6a, 0x82, 0xee, 0x14, 0xaa, 0x76, 0x30, 0x65, 0x0f, 0x67, 0x39, 0xa6, 0x51, 0x7c, 0x49, 0x24}} , + {{0x35, 0xa3, 0x78, 0xd1, 0x11, 0x0f, 0x75, 0xd3, 0x70, 0x46, 0xdb, 0x20, 0x51, 0xcb, 0x92, 0x80, 0x54, 0x10, 0x74, 0x36, 0x86, 0xa9, 0xd7, 0xa3, 0x08, 0x78, 0xf1, 0x01, 0x29, 0xf8, 0x80, 0x3b}}}, +{{{0xdb, 0xa7, 0x9d, 0x9d, 0xbf, 0xa0, 0xcc, 0xed, 0x53, 0xa2, 0xa2, 0x19, 0x39, 0x48, 0x83, 0x19, 0x37, 0x58, 0xd1, 0x04, 0x28, 0x40, 0xf7, 0x8a, 0xc2, 0x08, 0xb7, 0xa5, 0x42, 0xcf, 0x53, 0x4c}} , + {{0xa7, 0xbb, 0xf6, 0x8e, 0xad, 0xdd, 0xf7, 0x90, 0xdd, 0x5f, 0x93, 0x89, 0xae, 0x04, 0x37, 0xe6, 0x9a, 0xb7, 0xe8, 0xc0, 0xdf, 0x16, 0x2a, 0xbf, 0xc4, 0x3a, 0x3c, 0x41, 0xd5, 0x89, 0x72, 0x5a}}}, +{{{0x1f, 0x96, 0xff, 0x34, 0x2c, 0x13, 0x21, 0xcb, 0x0a, 0x89, 0x85, 0xbe, 0xb3, 0x70, 0x9e, 0x1e, 0xde, 0x97, 0xaf, 0x96, 0x30, 0xf7, 0x48, 0x89, 0x40, 0x8d, 0x07, 0xf1, 0x25, 0xf0, 0x30, 0x58}} , + {{0x1e, 0xd4, 0x93, 0x57, 0xe2, 0x17, 0xe7, 0x9d, 0xab, 0x3c, 0x55, 0x03, 0x82, 0x2f, 0x2b, 0xdb, 0x56, 0x1e, 0x30, 0x2e, 0x24, 0x47, 0x6e, 0xe6, 0xff, 0x33, 0x24, 0x2c, 0x75, 0x51, 0xd4, 0x67}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0x2b, 0x06, 0xd9, 0xa1, 0x5d, 0xe1, 0xf4, 0xd1, 0x1e, 0x3c, 0x9a, 0xc6, 0x29, 0x2b, 0x13, 0x13, 0x78, 0xc0, 0xd8, 0x16, 0x17, 0x2d, 0x9e, 0xa9, 0xc9, 0x79, 0x57, 0xab, 0x24, 0x91, 0x92, 0x19}} , + {{0x69, 0xfb, 0xa1, 0x9c, 0xa6, 0x75, 0x49, 0x7d, 0x60, 0x73, 0x40, 0x42, 0xc4, 0x13, 0x0a, 0x95, 0x79, 0x1e, 0x04, 0x83, 0x94, 0x99, 0x9b, 0x1e, 0x0c, 0xe8, 0x1f, 0x54, 0xef, 0xcb, 0xc0, 0x52}}}, +{{{0x14, 0x89, 0x73, 0xa1, 0x37, 0x87, 0x6a, 0x7a, 0xcf, 0x1d, 0xd9, 0x2e, 0x1a, 0x67, 0xed, 0x74, 0xc0, 0xf0, 0x9c, 0x33, 0xdd, 0xdf, 0x08, 0xbf, 0x7b, 0xd1, 0x66, 0xda, 0xe6, 0xc9, 0x49, 0x08}} , + {{0xe9, 0xdd, 0x5e, 0x55, 0xb0, 0x0a, 0xde, 0x21, 0x4c, 0x5a, 0x2e, 0xd4, 0x80, 0x3a, 0x57, 0x92, 0x7a, 0xf1, 0xc4, 0x2c, 0x40, 0xaf, 0x2f, 0xc9, 0x92, 0x03, 0xe5, 0x5a, 0xbc, 0xdc, 0xf4, 0x09}}}, +{{{0xf3, 0xe1, 0x2b, 0x7c, 0x05, 0x86, 0x80, 0x93, 0x4a, 0xad, 0xb4, 0x8f, 0x7e, 0x99, 0x0c, 0xfd, 0xcd, 0xef, 0xd1, 0xff, 0x2c, 0x69, 0x34, 0x13, 0x41, 0x64, 0xcf, 0x3b, 0xd0, 0x90, 0x09, 0x1e}} , + {{0x9d, 0x45, 0xd6, 0x80, 0xe6, 0x45, 0xaa, 0xf4, 0x15, 0xaa, 0x5c, 0x34, 0x87, 0x99, 0xa2, 0x8c, 0x26, 0x84, 0x62, 0x7d, 0xb6, 0x29, 0xc0, 0x52, 0xea, 0xf5, 0x81, 0x18, 0x0f, 0x35, 0xa9, 0x0e}}}, +{{{0xe7, 0x20, 0x72, 0x7c, 0x6d, 0x94, 0x5f, 0x52, 0x44, 0x54, 0xe3, 0xf1, 0xb2, 0xb0, 0x36, 0x46, 0x0f, 0xae, 0x92, 0xe8, 0x70, 0x9d, 0x6e, 0x79, 0xb1, 0xad, 0x37, 0xa9, 0x5f, 0xc0, 0xde, 0x03}} , + {{0x15, 0x55, 0x37, 0xc6, 0x1c, 0x27, 0x1c, 0x6d, 0x14, 0x4f, 0xca, 0xa4, 0xc4, 0x88, 0x25, 0x46, 0x39, 0xfc, 0x5a, 0xe5, 0xfe, 0x29, 0x11, 0x69, 0xf5, 0x72, 0x84, 0x4d, 0x78, 0x9f, 0x94, 0x15}}}, +{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, +{{{0xec, 0xd3, 0xff, 0x57, 0x0b, 0xb0, 0xb2, 0xdc, 0xf8, 0x4f, 0xe2, 0x12, 0xd5, 0x36, 0xbe, 0x6b, 0x09, 0x43, 0x6d, 0xa3, 0x4d, 0x90, 0x2d, 0xb8, 0x74, 0xe8, 0x71, 0x45, 0x19, 0x8b, 0x0c, 0x6a}} , + {{0xb8, 0x42, 0x1c, 0x03, 0xad, 0x2c, 0x03, 0x8e, 0xac, 0xd7, 0x98, 0x29, 0x13, 0xc6, 0x02, 0x29, 0xb5, 0xd4, 0xe7, 0xcf, 0xcc, 0x8b, 0x83, 0xec, 0x35, 0xc7, 0x9c, 0x74, 0xb7, 0xad, 0x85, 0x5f}}}, +{{{0x78, 0x84, 0xe1, 0x56, 0x45, 0x69, 0x68, 0x5a, 0x4f, 0xb8, 0xb1, 0x29, 0xff, 0x33, 0x03, 0x31, 0xb7, 0xcb, 0x96, 0x25, 0xe6, 0xe6, 0x41, 0x98, 0x1a, 0xbb, 0x03, 0x56, 0xf2, 0xb2, 0x91, 0x34}} , + {{0x2c, 0x6c, 0xf7, 0x66, 0xa4, 0x62, 0x6b, 0x39, 0xb3, 0xba, 0x65, 0xd3, 0x1c, 0xf8, 0x11, 0xaa, 0xbe, 0xdc, 0x80, 0x59, 0x87, 0xf5, 0x7b, 0xe5, 0xe3, 0xb3, 0x3e, 0x39, 0xda, 0xbe, 0x88, 0x09}}}, +{{{0x8b, 0xf1, 0xa0, 0xf5, 0xdc, 0x29, 0xb4, 0xe2, 0x07, 0xc6, 0x7a, 0x00, 0xd0, 0x89, 0x17, 0x51, 0xd4, 0xbb, 0xd4, 0x22, 0xea, 0x7e, 0x7d, 0x7c, 0x24, 0xea, 0xf2, 0xe8, 0x22, 0x12, 0x95, 0x06}} , + {{0xda, 0x7c, 0xa4, 0x0c, 0xf4, 0xba, 0x6e, 0xe1, 0x89, 0xb5, 0x59, 0xca, 0xf1, 0xc0, 0x29, 0x36, 0x09, 0x44, 0xe2, 0x7f, 0xd1, 0x63, 0x15, 0x99, 0xea, 0x25, 0xcf, 0x0c, 0x9d, 0xc0, 0x44, 0x6f}}}, +{{{0x1d, 0x86, 0x4e, 0xcf, 0xf7, 0x37, 0x10, 0x25, 0x8f, 0x12, 0xfb, 0x19, 0xfb, 0xe0, 0xed, 0x10, 0xc8, 0xe2, 0xf5, 0x75, 0xb1, 0x33, 0xc0, 0x96, 0x0d, 0xfb, 0x15, 0x6c, 0x0d, 0x07, 0x5f, 0x05}} , + {{0x69, 0x3e, 0x47, 0x97, 0x2c, 0xaf, 0x52, 0x7c, 0x78, 0x83, 0xad, 0x1b, 0x39, 0x82, 0x2f, 0x02, 0x6f, 0x47, 0xdb, 0x2a, 0xb0, 0xe1, 0x91, 0x99, 0x55, 0xb8, 0x99, 0x3a, 0xa0, 0x44, 0x11, 0x51}}} diff --git a/libssh/src/getpass.c b/libssh/src/getpass.c index 0ffb955d..c36f263b 100644 --- a/libssh/src/getpass.c +++ b/libssh/src/getpass.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2011 by Andreas Schneider + * Copyright (c) 2011-2013 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/libssh/src/gssapi.c b/libssh/src/gssapi.c index c92c66cb..099294ee 100644 --- a/libssh/src/gssapi.c +++ b/libssh/src/gssapi.c @@ -49,7 +49,7 @@ struct ssh_gssapi_struct{ enum ssh_gssapi_state_e state; /* current state */ struct gss_OID_desc_struct mech; /* mechanism being elected for auth */ gss_cred_id_t server_creds; /* credentials of server */ - gss_cred_id_t client_creds; /* creds of the client */ + gss_cred_id_t client_creds; /* creds delegated by the client */ gss_ctx_id_t ctx; /* the authentication context */ gss_name_t client_name; /* Identity of the client */ char *user; /* username of client */ @@ -57,7 +57,9 @@ struct ssh_gssapi_struct{ char *service; /* name of the service */ struct { gss_name_t server_name; /* identity of server */ + OM_uint32 flags; /* flags used for init context */ gss_OID oid; /* mech being used for authentication */ + gss_cred_id_t creds; /* creds used to initialize context */ gss_cred_id_t client_deleg_creds; /* delegated creds (const, not freeable) */ } client; }; @@ -89,12 +91,13 @@ static void ssh_gssapi_free(ssh_session session){ OM_uint32 min; if (session->gssapi == NULL) return; - if (session->gssapi->mech.elements) - SAFE_FREE(session->gssapi->mech.elements); - if (session->gssapi->user) - SAFE_FREE(session->gssapi->user); - if (session->gssapi->server_creds) - gss_release_cred(&min,&session->gssapi->server_creds); + SAFE_FREE(session->gssapi->user); + SAFE_FREE(session->gssapi->mech.elements); + gss_release_cred(&min,&session->gssapi->server_creds); + if (session->gssapi->client.creds != + session->gssapi->client.client_deleg_creds) { + gss_release_cred(&min, &session->gssapi->client.creds); + } SAFE_FREE(session->gssapi); } @@ -208,8 +211,8 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n maj_stat = gss_import_name(&min_stat, &name_buf, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name); if (maj_stat != GSS_S_COMPLETE) { - SSH_LOG(0, "importing name %d, %d", maj_stat, min_stat); - ssh_gssapi_log_error(0, "importing name", maj_stat); + SSH_LOG(SSH_LOG_WARNING, "importing name %d, %d", maj_stat, min_stat); + ssh_gssapi_log_error(SSH_LOG_WARNING, "importing name", maj_stat); return -1; } @@ -220,13 +223,13 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n gss_release_oid_set(&min_stat, &both_supported); if (maj_stat != GSS_S_COMPLETE) { - SSH_LOG(0, "error acquiring credentials %d, %d", maj_stat, min_stat); - ssh_gssapi_log_error(0, "acquiring creds", maj_stat); + SSH_LOG(SSH_LOG_WARNING, "error acquiring credentials %d, %d", maj_stat, min_stat); + ssh_gssapi_log_error(SSH_LOG_WARNING, "acquiring creds", maj_stat); ssh_auth_reply_default(session,0); return SSH_ERROR; } - SSH_LOG(0, "acquiring credentials %d, %d", maj_stat, min_stat); + SSH_LOG(SSH_LOG_PROTOCOL, "acquiring credentials %d, %d", maj_stat, min_stat); /* finding which OID from client we selected */ for (i=0 ; i< n_oid ; ++i){ @@ -263,7 +266,7 @@ static char *ssh_gssapi_name_to_char(gss_name_t name){ OM_uint32 maj_stat, min_stat; char *ptr; maj_stat = gss_display_name(&min_stat, name, &buffer, NULL); - ssh_gssapi_log_error(0, "converting name", maj_stat); + ssh_gssapi_log_error(SSH_LOG_WARNING, "converting name", maj_stat); ptr=malloc(buffer.length + 1); memcpy(ptr, buffer.value, buffer.length); ptr[buffer.length] = '\0'; @@ -308,14 +311,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ return SSH_PACKET_USED; } if (ssh_string_len(out_token) != 0){ - rc = buffer_add_u8(session->out_buffer, - SSH2_MSG_USERAUTH_GSSAPI_TOKEN); - if (rc < 0) { - ssh_set_error_oom(session); - return SSH_PACKET_USED; - } - rc = buffer_add_ssh_string(session->out_buffer, out_token); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bS", + SSH2_MSG_USERAUTH_GSSAPI_TOKEN, + out_token); + if (rc != SSH_OK) { ssh_set_error_oom(session); return SSH_PACKET_USED; } @@ -335,14 +335,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ maj_stat = gss_accept_sec_context(&min_stat, &session->gssapi->ctx, session->gssapi->server_creds, &input_token, input_bindings, &client_name, NULL /*mech_oid*/, &output_token, &ret_flags, NULL /*time*/, &session->gssapi->client_creds); - ssh_gssapi_log_error(0, "accepting token", maj_stat); + ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "accepting token", maj_stat); ssh_string_free(token); if (client_name != GSS_C_NO_NAME){ session->gssapi->client_name = client_name; session->gssapi->canonic_user = ssh_gssapi_name_to_char(client_name); } if (GSS_ERROR(maj_stat)){ - ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "Gssapi error", maj_stat); + ssh_gssapi_log_error(SSH_LOG_WARNING, "Gssapi error", maj_stat); ssh_auth_reply_default(session,0); ssh_gssapi_free(session); session->gssapi=NULL; @@ -353,12 +353,12 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ hexa = ssh_get_hexa(output_token.value, output_token.length); SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); SAFE_FREE(hexa); - token = ssh_string_new(output_token.length); - ssh_string_fill(token, output_token.value, output_token.length); - buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_TOKEN); - buffer_add_ssh_string(session->out_buffer,token); + ssh_buffer_pack(session->out_buffer, + "bdP", + SSH2_MSG_USERAUTH_GSSAPI_TOKEN, + output_token.length, + (size_t)output_token.length, output_token.value); packet_send(session); - ssh_string_free(token); } if(maj_stat == GSS_S_COMPLETE){ session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC; @@ -370,69 +370,24 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ static ssh_buffer ssh_gssapi_build_mic(ssh_session session){ ssh_buffer mic_buffer; - ssh_string str; int rc; - str = ssh_string_new(session->current_crypto->digest_len); - if (str == NULL) { - return NULL; - } - ssh_string_fill(str, session->current_crypto->session_id, - session->current_crypto->digest_len); - mic_buffer = ssh_buffer_new(); if (mic_buffer == NULL) { - ssh_string_free(str); + ssh_set_error_oom(session); return NULL; } - rc = buffer_add_ssh_string(mic_buffer, str); - ssh_string_free(str); - if (rc < 0) { - ssh_buffer_free(mic_buffer); - return NULL; - } - - rc = buffer_add_u8(mic_buffer, SSH2_MSG_USERAUTH_REQUEST); - if (rc < 0) { - ssh_buffer_free(mic_buffer); - return NULL; - } - - str = ssh_string_from_char(session->gssapi->user); - if (str == NULL) { - ssh_buffer_free(mic_buffer); - return NULL; - } - - rc = buffer_add_ssh_string(mic_buffer, str); - ssh_string_free(str); - if (rc < 0) { - ssh_buffer_free(mic_buffer); - return NULL; - } - - str = ssh_string_from_char("ssh-connection"); - if (str == NULL) { - ssh_buffer_free(mic_buffer); - return NULL; - } - rc = buffer_add_ssh_string(mic_buffer, str); - ssh_string_free(str); - if (rc < 0) { - ssh_buffer_free(mic_buffer); - return NULL; - } - - str = ssh_string_from_char("gssapi-with-mic"); - if (str == NULL) { - ssh_buffer_free(mic_buffer); - return NULL; - } - - rc = buffer_add_ssh_string(mic_buffer, str); - ssh_string_free(str); - if (rc < 0) { + rc = ssh_buffer_pack(mic_buffer, + "dPbsss", + session->current_crypto->digest_len, + (size_t)session->current_crypto->digest_len, session->current_crypto->session_id, + SSH2_MSG_USERAUTH_REQUEST, + session->gssapi->user, + "ssh-connection", + "gssapi-with-mic"); + if (rc != SSH_OK) { + ssh_set_error_oom(session); ssh_buffer_free(mic_buffer); return NULL; } @@ -484,8 +439,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic) mic_token_buf.value = ssh_string_data(mic_token); maj_stat = gss_verify_mic(&min_stat, session->gssapi->ctx, &mic_buf, &mic_token_buf, NULL); - ssh_gssapi_log_error(0, "verifying MIC", maj_stat); - ssh_gssapi_log_error(0, "verifying MIC (min stat)", min_stat); + ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "verifying MIC", maj_stat); + ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "verifying MIC (min stat)", min_stat); if (maj_stat == GSS_S_DEFECTIVE_TOKEN || GSS_ERROR(maj_stat)) { goto error; } @@ -536,8 +491,12 @@ ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session){ return (ssh_gssapi_creds)session->gssapi->client_creds; } +#endif /* SERVER */ + /** * @brief Set the forwadable ticket to be given to the server for authentication. + * Unlike ssh_gssapi_get_creds() this is called on the client side of an ssh + * connection. * * @param[in] creds gssapi credentials handle. */ @@ -556,49 +515,20 @@ void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds) session->gssapi->client.client_deleg_creds = (gss_cred_id_t)creds; } -#endif /* SERVER */ - static int ssh_gssapi_send_auth_mic(ssh_session session, ssh_string *oid_set, int n_oid){ - ssh_string str; int rc; int i; - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_REQUEST); - if (rc < 0) { - goto fail; - } - /* username */ - str = ssh_string_from_char(session->opts.username); - if (str == NULL) { - goto fail; - } - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - /* service */ - str = ssh_string_from_char("ssh-connection"); - if (str == NULL) { - goto fail; - } - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - /* method */ - str = ssh_string_from_char("gssapi-with-mic"); - if (str == NULL) { - goto fail; - } - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - rc = buffer_add_u32(session->out_buffer, htonl(n_oid)); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bsssd", + SSH2_MSG_USERAUTH_REQUEST, + session->opts.username, + "ssh-connection", + "gssapi-with-mic", + n_oid); + + if (rc != SSH_OK) { + ssh_set_error_oom(session); goto fail; } @@ -612,92 +542,79 @@ static int ssh_gssapi_send_auth_mic(ssh_session session, ssh_string *oid_set, in session->auth_state = SSH_AUTH_STATE_GSSAPI_REQUEST_SENT; return packet_send(session); fail: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_ERROR; } -/** @brief returns the OIDs of the mechs that work with both - * hostname and username +/** @brief returns the OIDs of the mechs that have usable credentials */ -static int ssh_gssapi_match(ssh_session session, char *hostname, char *username, gss_OID_set *valid_oids, int deleg){ - gss_buffer_desc host_namebuf, user_namebuf; - gss_name_t host_name, user_name; - OM_uint32 maj_stat, min_stat; - gss_OID_set supported; +static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids) +{ + OM_uint32 maj_stat, min_stat, lifetime; + gss_OID_set actual_mechs; + gss_buffer_desc namebuf; + gss_name_t client_id = GSS_C_NO_NAME; gss_OID oid; - gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - gss_cred_id_t client_creds = GSS_C_NO_CREDENTIAL; unsigned int i; char *ptr; - char hostname_buf[256]; + int ret; + if (session->gssapi->client.client_deleg_creds == NULL) { + if (session->opts.gss_client_identity != NULL) { + namebuf.value = (void *)session->opts.gss_client_identity; + namebuf.length = strlen(session->opts.gss_client_identity); - gss_create_empty_oid_set(&min_stat, valid_oids); - maj_stat = gss_indicate_mechs(&min_stat, &supported); - for (i=0; i < supported->count; ++i){ - ptr=ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length); - SSH_LOG(SSH_LOG_DEBUG, "GSSAPI oid supported %d : %s\n",i, ptr); - SAFE_FREE(ptr); - } - - user_namebuf.value = username; - user_namebuf.length = strlen(username) + 1; - maj_stat = gss_import_name(&min_stat, &user_namebuf, - (gss_OID) GSS_C_NT_USER_NAME, &user_name); - if (maj_stat != GSS_S_COMPLETE) { - SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat); - ssh_gssapi_log_error(SSH_LOG_DEBUG, "importing name", maj_stat); - return -1; - } - - snprintf(hostname_buf, sizeof(hostname_buf),"host@%s", hostname); - host_namebuf.value = hostname_buf; - host_namebuf.length = strlen(hostname_buf) + 1; - maj_stat = gss_import_name(&min_stat, &host_namebuf, - (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &host_name); - if (maj_stat != GSS_S_COMPLETE) { - SSH_LOG(0, "importing name %d, %d", maj_stat, min_stat); - ssh_gssapi_log_error(0, "importing name", maj_stat); - return -1; - } - - ssh_gssapi_init(session); - session->gssapi->client_name = user_name; - session->gssapi->client.server_name = host_name; - session->gssapi->user = strdup(username); - for (i=0; icount; ++i){ - oid = &supported->elements[i]; - maj_stat = gss_init_sec_context(&min_stat, - session->gssapi->client.client_deleg_creds, &ctx, host_name, oid, - GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | (deleg ? GSS_C_DELEG_FLAG : 0), - 0, NULL, &input_token, NULL, &output_token, NULL, NULL); - if (!GSS_ERROR(maj_stat)){ - gss_OID_set tmp; - if (session->gssapi->client.client_deleg_creds != GSS_C_NO_CREDENTIAL){ - /* we know the oid is ok since init_sec_context worked */ - gss_add_oid_set_member(&min_stat, oid, valid_oids); - SSH_LOG(SSH_LOG_PROTOCOL, "Matched oid %u for server (with forwarding)", i); - } else { - gss_create_empty_oid_set(&min_stat, &tmp); - gss_add_oid_set_member(&min_stat, oid, &tmp); - maj_stat = gss_acquire_cred(&min_stat, user_name, 0, - tmp, GSS_C_INITIATE, - &client_creds, NULL, NULL); - gss_release_oid_set(&min_stat, &tmp); - if (!GSS_ERROR(maj_stat)){ - gss_release_cred(&min_stat, &client_creds); - gss_add_oid_set_member(&min_stat,oid,valid_oids); - SSH_LOG(SSH_LOG_PROTOCOL, "Matched oid %u for server", i); - } + maj_stat = gss_import_name(&min_stat, &namebuf, + GSS_C_NT_USER_NAME, &client_id); + if (GSS_ERROR(maj_stat)) { + ret = SSH_ERROR; + goto end; } } - gss_delete_sec_context(&min_stat,&ctx, &output_token); - ctx = GSS_C_NO_CONTEXT; + + maj_stat = gss_acquire_cred(&min_stat, client_id, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, GSS_C_INITIATE, + &session->gssapi->client.creds, + &actual_mechs, NULL); + if (GSS_ERROR(maj_stat)) { + ret = SSH_ERROR; + goto end; + } + } else { + session->gssapi->client.creds = + session->gssapi->client.client_deleg_creds; + + maj_stat = gss_inquire_cred(&min_stat, session->gssapi->client.creds, + &client_id, NULL, NULL, &actual_mechs); + if (GSS_ERROR(maj_stat)) { + ret = SSH_ERROR; + goto end; + } } - return SSH_OK; + gss_create_empty_oid_set(&min_stat, valid_oids); + + /* double check each single cred */ + for (i = 0; i < actual_mechs->count; i++) { + /* check lifetime is not 0 or skip */ + lifetime = 0; + oid = &actual_mechs->elements[i]; + maj_stat = gss_inquire_cred_by_mech(&min_stat, + session->gssapi->client.creds, + oid, NULL, &lifetime, NULL, NULL); + if (maj_stat == GSS_S_COMPLETE && lifetime > 0) { + gss_add_oid_set_member(&min_stat, oid, valid_oids); + ptr = ssh_get_hexa(oid->elements, oid->length); + SSH_LOG(SSH_LOG_DEBUG, "GSSAPI valid oid %d : %s\n", i, ptr); + SAFE_FREE(ptr); + } + } + + ret = SSH_OK; + +end: + gss_release_name(&min_stat, &client_id); + return ret; } /** @@ -713,21 +630,55 @@ int ssh_gssapi_auth_mic(ssh_session session){ ssh_string *oids; int rc; int n_oids = 0; + OM_uint32 maj_stat, min_stat; + char name_buf[256]; + gss_buffer_desc hostname; + const char *gss_host = session->opts.host; - if (ssh_gssapi_init(session) == SSH_ERROR) + rc = ssh_gssapi_init(session); + if (rc == SSH_ERROR) { return SSH_AUTH_ERROR; + } + if (session->opts.gss_server_identity != NULL) { + gss_host = session->opts.gss_server_identity; + } + /* import target host name */ + snprintf(name_buf, sizeof(name_buf), "host@%s", gss_host); + + hostname.value = name_buf; + hostname.length = strlen(name_buf) + 1; + maj_stat = gss_import_name(&min_stat, &hostname, + (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, + &session->gssapi->client.server_name); + if (maj_stat != GSS_S_COMPLETE) { + SSH_LOG(SSH_LOG_WARNING, "importing name %d, %d", maj_stat, min_stat); + ssh_gssapi_log_error(SSH_LOG_WARNING, "importing name", maj_stat); + return SSH_PACKET_USED; + } + + /* copy username */ + session->gssapi->user = strdup(session->opts.username); + if (session->gssapi->user == NULL) { + ssh_set_error_oom(session); + return SSH_AUTH_ERROR; + } SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi to host %s with user %s", - session->opts.host, session->opts.username); - rc = ssh_gssapi_match(session,session->opts.host, session->opts.username, &selected, 0); - if (rc == SSH_ERROR) + session->opts.host, session->gssapi->user); + rc = ssh_gssapi_match(session, &selected); + if (rc == SSH_ERROR) { return SSH_AUTH_DENIED; + } n_oids = selected->count; SSH_LOG(SSH_LOG_PROTOCOL, "Sending %d oids", n_oids); oids = calloc(n_oids, sizeof(ssh_string)); + if (oids == NULL) { + ssh_set_error_oom(session); + return SSH_AUTH_ERROR; + } for (i=0; ielements[i].length + 2); @@ -769,15 +720,10 @@ static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s){ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){ ssh_string oid_s; - gss_OID oid; gss_uint32 maj_stat, min_stat; - int deleg = 0; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - gss_OID_set tmp; char *hexa; - ssh_string token; - gss_cred_id_t creds = GSS_C_NO_CREDENTIAL; (void)type; (void)user; @@ -791,31 +737,27 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){ ssh_set_error(session, SSH_FATAL, "Missing OID"); return SSH_PACKET_USED; } - oid = ssh_gssapi_oid_from_string(oid_s); + session->gssapi->client.oid = ssh_gssapi_oid_from_string(oid_s); ssh_string_free(oid_s); - if (!oid) { + if (!session->gssapi->client.oid) { ssh_set_error(session, SSH_FATAL, "Invalid OID"); return SSH_PACKET_USED; } - if (session->gssapi->client.client_deleg_creds != GSS_C_NO_CREDENTIAL) - creds = session->gssapi->client.client_deleg_creds; - if (creds == GSS_C_NO_CREDENTIAL){ - gss_create_empty_oid_set(&min_stat, &tmp); - gss_add_oid_set_member(&min_stat, oid, &tmp); - maj_stat = gss_acquire_cred(&min_stat, session->gssapi->client_name, 0, - tmp, GSS_C_INITIATE, - &session->gssapi->client_creds, NULL, NULL); - gss_release_oid_set(&min_stat, &tmp); - if (GSS_ERROR(maj_stat)){ - ssh_gssapi_log_error(SSH_LOG_WARNING,"Error acquiring credentials",maj_stat); - return SSH_PACKET_USED; - } + + session->gssapi->client.flags = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; + if (session->opts.gss_delegate_creds) { + session->gssapi->client.flags |= GSS_C_DELEG_FLAG; } + /* prepare the first TOKEN response */ maj_stat = gss_init_sec_context(&min_stat, - creds, &session->gssapi->ctx, session->gssapi->client.server_name, oid, - GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | (deleg ? GSS_C_DELEG_FLAG : 0), - 0, NULL, &input_token, NULL, &output_token, NULL, NULL); + session->gssapi->client.creds, + &session->gssapi->ctx, + session->gssapi->client.server_name, + session->gssapi->client.oid, + session->gssapi->client.flags, + 0, NULL, &input_token, NULL, + &output_token, NULL, NULL); if(GSS_ERROR(maj_stat)){ ssh_gssapi_log_error(SSH_LOG_WARNING, "Initializing gssapi context", maj_stat); return SSH_PACKET_USED; @@ -824,15 +766,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){ hexa = ssh_get_hexa(output_token.value, output_token.length); SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); SAFE_FREE(hexa); - token = ssh_string_new(output_token.length); - ssh_string_fill(token, output_token.value, output_token.length); - buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_TOKEN); - buffer_add_ssh_string(session->out_buffer,token); + ssh_buffer_pack(session->out_buffer, + "bdP", + SSH2_MSG_USERAUTH_GSSAPI_TOKEN, + output_token.length, + (size_t)output_token.length, output_token.value); packet_send(session); - ssh_string_free(token); session->auth_state = SSH_AUTH_STATE_GSSAPI_TOKEN; } - session->gssapi->client.oid = oid; return SSH_PACKET_USED; } @@ -856,31 +797,21 @@ static int ssh_gssapi_send_mic(ssh_session session){ maj_stat = gss_get_mic(&min_stat,session->gssapi->ctx, GSS_C_QOP_DEFAULT, &mic_buf, &mic_token_buf); if (GSS_ERROR(maj_stat)){ ssh_buffer_free(mic_buffer); - ssh_gssapi_log_error(0, "generating MIC", maj_stat); + ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "generating MIC", maj_stat); return SSH_ERROR; } - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_MIC); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bdP", + SSH2_MSG_USERAUTH_GSSAPI_MIC, + mic_token_buf.length, + (size_t)mic_token_buf.length, mic_token_buf.value); + if (rc != SSH_OK) { ssh_buffer_free(mic_buffer); ssh_set_error_oom(session); return SSH_ERROR; } - rc = buffer_add_u32(session->out_buffer, htonl(mic_token_buf.length)); - if (rc < 0) { - ssh_buffer_free(mic_buffer); - ssh_set_error_oom(session); - return SSH_ERROR; - } - - rc = buffer_add_data(session->out_buffer, mic_token_buf.value, mic_token_buf.length); - ssh_buffer_free(mic_buffer); - if (rc < 0) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - return packet_send(session); } @@ -889,8 +820,6 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){ char *hexa; OM_uint32 maj_stat, min_stat; gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER; - gss_cred_id_t creds = GSS_C_NO_CREDENTIAL; - int deleg = 0; (void)user; (void)type; @@ -910,16 +839,16 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){ SAFE_FREE(hexa); input_token.length = ssh_string_len(token); input_token.value = ssh_string_data(token); - if (session->gssapi->client.client_deleg_creds != GSS_C_NO_CREDENTIAL) - creds = session->gssapi->client.client_deleg_creds; - else - creds = session->gssapi->client_creds; maj_stat = gss_init_sec_context(&min_stat, - creds, &session->gssapi->ctx, session->gssapi->client.server_name, session->gssapi->client.oid, - GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | (deleg ? GSS_C_DELEG_FLAG : 0), - 0, NULL, &input_token, NULL, &output_token, NULL, NULL); + session->gssapi->client.creds, + &session->gssapi->ctx, + session->gssapi->client.server_name, + session->gssapi->client.oid, + session->gssapi->client.flags, + 0, NULL, &input_token, NULL, + &output_token, NULL, NULL); - ssh_gssapi_log_error(0, "accepting token", maj_stat); + ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "accepting token", maj_stat); ssh_string_free(token); if (GSS_ERROR(maj_stat)){ ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "Gssapi error", maj_stat); @@ -932,12 +861,12 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){ hexa = ssh_get_hexa(output_token.value, output_token.length); SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); SAFE_FREE(hexa); - token = ssh_string_new(output_token.length); - ssh_string_fill(token, output_token.value, output_token.length); - buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_TOKEN); - buffer_add_ssh_string(session->out_buffer,token); + ssh_buffer_pack(session->out_buffer, + "bdP", + SSH2_MSG_USERAUTH_GSSAPI_TOKEN, + output_token.length, + (size_t)output_token.length, output_token.value); packet_send(session); - ssh_string_free(token); } if(maj_stat == GSS_S_COMPLETE){ session->auth_state = SSH_AUTH_STATE_NONE; diff --git a/libssh/src/gzip.c b/libssh/src/gzip.c index 339217d4..ca190bc2 100644 --- a/libssh/src/gzip.c +++ b/libssh/src/gzip.c @@ -4,7 +4,7 @@ * This file is part of the SSH Library * * Copyright (c) 2003 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider + * Copyright (c) 2009 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -90,7 +90,7 @@ static ssh_buffer gzip_compress(ssh_session session,ssh_buffer source,int level) return NULL; } len = BLOCKSIZE - zout->avail_out; - if (buffer_add_data(dest, out_buf, len) < 0) { + if (ssh_buffer_add_data(dest, out_buf, len) < 0) { ssh_buffer_free(dest); return NULL; } @@ -108,12 +108,12 @@ int compress_buffer(ssh_session session, ssh_buffer buf) { return -1; } - if (buffer_reinit(buf) < 0) { + if (ssh_buffer_reinit(buf) < 0) { ssh_buffer_free(dest); return -1; } - if (buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) { + if (ssh_buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) { ssh_buffer_free(dest); return -1; } @@ -181,7 +181,7 @@ static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t } len = BLOCKSIZE - zin->avail_out; - if (buffer_add_data(dest,out_buf,len) < 0) { + if (ssh_buffer_add_data(dest,out_buf,len) < 0) { ssh_buffer_free(dest); return NULL; } @@ -204,12 +204,12 @@ int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen){ return -1; } - if (buffer_reinit(buf) < 0) { + if (ssh_buffer_reinit(buf) < 0) { ssh_buffer_free(dest); return -1; } - if (buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) { + if (ssh_buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) { ssh_buffer_free(dest); return -1; } diff --git a/libssh/src/kex.c b/libssh/src/kex.c index 7cd404f7..f1a1b566 100644 --- a/libssh/src/kex.c +++ b/libssh/src/kex.c @@ -35,6 +35,7 @@ #include "libssh/ssh2.h" #include "libssh/string.h" #include "libssh/curve25519.h" +#include "libssh/knownhosts.h" #ifdef HAVE_LIBGCRYPT # define BLOWFISH "blowfish-cbc," @@ -72,7 +73,7 @@ #ifdef HAVE_ECDH #define ECDH "ecdh-sha2-nistp256," -#define HOSTKEYS "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss" +#define HOSTKEYS "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss" #else #define HOSTKEYS "ssh-rsa,ssh-dss" #define ECDH "" @@ -87,8 +88,8 @@ static const char *default_methods[] = { HOSTKEYS, AES BLOWFISH DES, AES BLOWFISH DES, - "hmac-sha1", - "hmac-sha1", + "hmac-sha1,hmac-sha2-256,hmac-sha2-512", + "hmac-sha1,hmac-sha2-256,hmac-sha2-512", "none", "none", "", @@ -102,8 +103,8 @@ static const char *supported_methods[] = { HOSTKEYS, AES BLOWFISH DES, AES BLOWFISH DES, - "hmac-sha1", - "hmac-sha1", + "hmac-sha1,hmac-sha2-256,hmac-sha2-512", + "hmac-sha1,hmac-sha2-256,hmac-sha2-512", ZLIB, ZLIB, "", @@ -274,87 +275,180 @@ char *ssh_find_matching(const char *available_d, const char *preferred_d){ return NULL; } +/** + * @internal + * @brief returns whether the first client key exchange algorithm matches + * the first server key exchange algorithm + * @returns whether the first client key exchange algorithm matches + * the first server key exchange algorithm + */ +static int is_first_kex_packet_follows_guess_wrong(const char *client_kex, + const char *server_kex) { + int is_wrong = 1; + char **server_kex_tokens = NULL; + char **client_kex_tokens = NULL; + + if ((client_kex == NULL) || (server_kex == NULL)) { + goto out; + } + + client_kex_tokens = tokenize(client_kex); + + if (client_kex_tokens == NULL) { + goto out; + } + + if (client_kex_tokens[0] == NULL) { + goto freeout; + } + + server_kex_tokens = tokenize(server_kex); + if (server_kex_tokens == NULL) { + goto freeout; + } + + is_wrong = (strcmp(client_kex_tokens[0], server_kex_tokens[0]) != 0); + + SAFE_FREE(server_kex_tokens[0]); + SAFE_FREE(server_kex_tokens); +freeout: + SAFE_FREE(client_kex_tokens[0]); + SAFE_FREE(client_kex_tokens); +out: + return is_wrong; +} + SSH_PACKET_CALLBACK(ssh_packet_kexinit){ - int server_kex=session->server; - ssh_string str = NULL; - char *strings[KEX_METHODS_SIZE]; - int i; + int i; + int server_kex=session->server; + ssh_string str = NULL; + char *strings[KEX_METHODS_SIZE]; + int rc = SSH_ERROR; - (void)type; - (void)user; - memset(strings, 0, sizeof(strings)); - if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED){ - SSH_LOG(SSH_LOG_WARNING, "Other side initiating key re-exchange"); - } else if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){ - ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); - goto error; - } - if (server_kex) { - if (buffer_get_data(packet,session->next_crypto->client_kex.cookie,16) != 16) { - ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); + uint8_t first_kex_packet_follows = 0; + uint32_t kexinit_reserved = 0; + + (void)type; + (void)user; + + memset(strings, 0, sizeof(strings)); + if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED){ + SSH_LOG(SSH_LOG_WARNING, "Other side initiating key re-exchange"); + } else if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){ + ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); goto error; - } - - if (hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie) < 0) { - ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); - goto error; - } - } else { - if (buffer_get_data(packet,session->next_crypto->server_kex.cookie,16) != 16) { - ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); - goto error; - } - - if (hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie) < 0) { - ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); - goto error; - } - } - - for (i = 0; i < KEX_METHODS_SIZE; i++) { - str = buffer_get_ssh_string(packet); - if (str == NULL) { - break; } - if (buffer_add_ssh_string(session->in_hashbuf, str) < 0) { - ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer"); - goto error; + if (server_kex) { + rc = buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16); + if (rc != 16) { + ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); + goto error; + } + + rc = hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); + goto error; + } + } else { + rc = buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16); + if (rc != 16) { + ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); + goto error; + } + + rc = hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); + goto error; + } } - strings[i] = ssh_string_to_char(str); - if (strings[i] == NULL) { - ssh_set_error_oom(session); - goto error; - } - ssh_string_free(str); - str = NULL; - } + for (i = 0; i < KEX_METHODS_SIZE; i++) { + str = buffer_get_ssh_string(packet); + if (str == NULL) { + break; + } - /* copy the server kex info into an array of strings */ - if (server_kex) { - for (i = 0; i < SSH_KEX_METHODS; i++) { - session->next_crypto->client_kex.methods[i] = strings[i]; - } - } else { /* client */ - for (i = 0; i < SSH_KEX_METHODS; i++) { - session->next_crypto->server_kex.methods[i] = strings[i]; - } - } + rc = buffer_add_ssh_string(session->in_hashbuf, str); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer"); + goto error; + } + + strings[i] = ssh_string_to_char(str); + if (strings[i] == NULL) { + ssh_set_error_oom(session); + goto error; + } + ssh_string_free(str); + str = NULL; + } + + /* copy the server kex info into an array of strings */ + if (server_kex) { + for (i = 0; i < SSH_KEX_METHODS; i++) { + session->next_crypto->client_kex.methods[i] = strings[i]; + } + } else { /* client */ + for (i = 0; i < SSH_KEX_METHODS; i++) { + session->next_crypto->server_kex.methods[i] = strings[i]; + } + } + + /* + * Handle the two final fields for the KEXINIT message (RFC 4253 7.1): + * + * boolean first_kex_packet_follows + * uint32 0 (reserved for future extension) + * + * Notably if clients set 'first_kex_packet_follows', it is expected + * that its value is included when computing the session ID (see + * 'make_sessionid'). + */ + if (server_kex) { + rc = buffer_get_u8(packet, &first_kex_packet_follows); + if (rc != 1) { + goto error; + } + + rc = buffer_add_u8(session->in_hashbuf, first_kex_packet_follows); + if (rc < 0) { + goto error; + } + + rc = buffer_add_u32(session->in_hashbuf, kexinit_reserved); + if (rc < 0) { + goto error; + } + + /* + * Remember whether 'first_kex_packet_follows' was set and the client + * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message + * must be ignored. + */ + if (first_kex_packet_follows) { + session->first_kex_follows_guess_wrong = + is_first_kex_packet_follows_guess_wrong(session->next_crypto->client_kex.methods[SSH_KEX], + session->next_crypto->server_kex.methods[SSH_KEX]); + } + } + + session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED; + session->dh_handshake_state = DH_STATE_INIT; + session->ssh_connection_callback(session); + return SSH_PACKET_USED; - session->session_state=SSH_SESSION_STATE_KEXINIT_RECEIVED; - session->dh_handshake_state=DH_STATE_INIT; - session->ssh_connection_callback(session); - return SSH_PACKET_USED; error: - ssh_string_free(str); - for (i = 0; i < SSH_KEX_METHODS; i++) { - SAFE_FREE(strings[i]); - } + ssh_string_free(str); + for (i = 0; i < SSH_KEX_METHODS; i++) { + SAFE_FREE(strings[i]); + } - session->session_state = SSH_SESSION_STATE_ERROR; + session->session_state = SSH_SESSION_STATE_ERROR; - return SSH_PACKET_USED; + return SSH_PACKET_USED; } void ssh_list_kex(struct ssh_kex_struct *kex) { @@ -373,6 +467,53 @@ void ssh_list_kex(struct ssh_kex_struct *kex) { } } +/** + * @internal + * @brief selects the hostkey mechanisms to be chosen for the key exchange, + * as some hostkey mechanisms may be present in known_hosts file and preferred + * @returns a cstring containing a comma-separated list of hostkey methods. + * NULL if no method matches + */ +static char *ssh_client_select_hostkeys(ssh_session session){ + char methods_buffer[128]={0}; + static const char *preferred_hostkeys[]={"ecdsa-sha2-nistp521","ecdsa-sha2-nistp384", + "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss", "ssh-rsa1", NULL}; + char **methods; + int i,j; + int needcoma=0; + + methods = ssh_knownhosts_algorithms(session); + if (methods == NULL || methods[0] == NULL){ + SAFE_FREE(methods); + return NULL; + } + + for (i=0;preferred_hostkeys[i] != NULL; ++i){ + for (j=0; methods[j] != NULL; ++j){ + if(strcmp(preferred_hostkeys[i], methods[j]) == 0){ + if (verify_existing_algo(SSH_HOSTKEYS, methods[j])){ + if(needcoma) + strncat(methods_buffer,",",sizeof(methods_buffer)-strlen(methods_buffer)-1); + strncat(methods_buffer, methods[j], sizeof(methods_buffer)-strlen(methods_buffer)-1); + needcoma = 1; + } + } + } + } + for(i=0;methods[i]!= NULL; ++i){ + SAFE_FREE(methods[i]); + } + SAFE_FREE(methods); + + if(strlen(methods_buffer) > 0){ + SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", methods_buffer); + return strdup(methods_buffer); + } else { + SSH_LOG(SSH_LOG_DEBUG, "No supported kex method for existing key in known_hosts file"); + return NULL; + } + +} /** * @brief sets the key exchange parameters to be sent to the server, * in function of the options and available methods. @@ -385,6 +526,13 @@ int set_client_kex(ssh_session session){ ssh_get_random(client->cookie, 16, 0); memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **)); + /* first check if we have specific host key methods */ + if(session->opts.wanted_methods[SSH_HOSTKEYS] == NULL){ + /* Only if no override */ + session->opts.wanted_methods[SSH_HOSTKEYS] = + ssh_client_select_hostkeys(session); + } + for (i = 0; i < KEX_METHODS_SIZE; i++) { wanted = session->opts.wanted_methods[i]; if (wanted == NULL) @@ -434,14 +582,15 @@ int ssh_send_kex(ssh_session session, int server_kex) { &session->next_crypto->client_kex); ssh_string str = NULL; int i; + int rc; - if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXINIT) < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bP", + SSH2_MSG_KEXINIT, + 16, + kex->cookie); /* cookie */ + if (rc != SSH_OK) goto error; - } - if (buffer_add_data(session->out_buffer, kex->cookie, 16) < 0) { - goto error; - } - if (hashbufout_add_cookie(session) < 0) { goto error; } @@ -464,10 +613,11 @@ int ssh_send_kex(ssh_session session, int server_kex) { str = NULL; } - if (buffer_add_u8(session->out_buffer, 0) < 0) { - goto error; - } - if (buffer_add_u32(session->out_buffer, 0) < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bd", + 0, + 0); + if (rc != SSH_OK) { goto error; } @@ -477,8 +627,8 @@ int ssh_send_kex(ssh_session session, int server_kex) { return 0; error: - buffer_reinit(session->out_buffer); - buffer_reinit(session->out_hashbuf); + ssh_buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_hashbuf); ssh_string_free(str); return -1; diff --git a/libssh/src/kex1.c b/libssh/src/kex1.c index 22899429..758054f8 100644 --- a/libssh/src/kex1.c +++ b/libssh/src/kex1.c @@ -417,7 +417,7 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1){ if (buffer_add_u8(session->out_buffer, support_3DES ? SSH_CIPHER_3DES : SSH_CIPHER_DES) < 0) { goto error; } - if (buffer_add_data(session->out_buffer, session->next_crypto->server_kex.cookie, 8) < 0) { + if (ssh_buffer_add_data(session->out_buffer, session->next_crypto->server_kex.cookie, 8) < 0) { goto error; } @@ -431,10 +431,10 @@ SSH_PACKET_CALLBACK(ssh_packet_publickey1){ bits, ssh_string_len(enc_session)); bits = htons(bits); /* the encrypted mpint */ - if (buffer_add_data(session->out_buffer, &bits, sizeof(uint16_t)) < 0) { + if (ssh_buffer_add_data(session->out_buffer, &bits, sizeof(uint16_t)) < 0) { goto error; } - if (buffer_add_data(session->out_buffer, ssh_string_data(enc_session), + if (ssh_buffer_add_data(session->out_buffer, ssh_string_data(enc_session), ssh_string_len(enc_session)) < 0) { goto error; } diff --git a/libssh/src/known_hosts.c b/libssh/src/known_hosts.c index 6e9eb915..8c05c8de 100644 --- a/libssh/src/known_hosts.c +++ b/libssh/src/known_hosts.c @@ -4,7 +4,7 @@ * This file is part of the SSH Library * * Copyright (c) 2003-2009 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider + * Copyright (c) 2009 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -34,7 +34,7 @@ #include "libssh/misc.h" #include "libssh/pki.h" #include "libssh/options.h" - +#include "libssh/knownhosts.h" /*todo: remove this include */ #include "libssh/string.h" @@ -239,9 +239,9 @@ static int check_public_key(ssh_session session, char **tokens) { /* TODO: fix the hardcoding */ tmpstring->size = htonl(len); #ifdef HAVE_LIBGCRYPT - bignum_bn2bin(tmpbn, len, string_data(tmpstring)); + bignum_bn2bin(tmpbn, len, ssh_string_data(tmpstring)); #elif defined HAVE_LIBCRYPTO - bignum_bn2bin(tmpbn, string_data(tmpstring)); + bignum_bn2bin(tmpbn, ssh_string_data(tmpstring)); #endif bignum_free(tmpbn); if (buffer_add_ssh_string(pubkey_buffer, tmpstring) < 0) { @@ -647,6 +647,102 @@ int ssh_write_knownhost(ssh_session session) { return 0; } +#define KNOWNHOSTS_MAXTYPES 10 + +/** + * @internal + * @brief Check which kind of host keys should be preferred for connection + * by reading the known_hosts file. + * + * @param[in] session The SSH session to use. + * + * @returns array of supported key types + * NULL on error + */ +char **ssh_knownhosts_algorithms(ssh_session session) { + FILE *file = NULL; + char **tokens; + char *host; + char *hostport; + const char *type; + int match; + char **array; + int i=0, j; + + if (session->opts.knownhosts == NULL) { + if (ssh_options_apply(session) < 0) { + ssh_set_error(session, SSH_REQUEST_DENIED, + "Can't find a known_hosts file"); + return NULL; + } + } + + if (session->opts.host == NULL) { + return NULL; + } + + host = ssh_lowercase(session->opts.host); + hostport = ssh_hostport(host, session->opts.port); + array = malloc(sizeof(char *) * KNOWNHOSTS_MAXTYPES); + + if (host == NULL || hostport == NULL || array == NULL) { + ssh_set_error_oom(session); + SAFE_FREE(host); + SAFE_FREE(hostport); + SAFE_FREE(array); + return NULL; + } + + do { + tokens = ssh_get_knownhost_line(&file, + session->opts.knownhosts, &type); + + /* End of file, return the current state */ + if (tokens == NULL) { + break; + } + match = match_hashed_host(host, tokens[0]); + if (match == 0){ + match = match_hostname(hostport, tokens[0], strlen(tokens[0])); + } + if (match == 0) { + match = match_hostname(host, tokens[0], strlen(tokens[0])); + } + if (match == 0) { + match = match_hashed_host(hostport, tokens[0]); + } + if (match) { + /* We got a match. Now check the key type */ + SSH_LOG(SSH_LOG_DEBUG, "server %s:%d has %s in known_hosts", + host, session->opts.port, type); + /* don't copy more than once */ + for(j=0;j= KNOWNHOSTS_MAXTYPES-1){ + tokens_free(tokens); + break; + } + } + } + tokens_free(tokens); + } while (1); + + array[i]=NULL; + SAFE_FREE(host); + SAFE_FREE(hostport); + if (file != NULL) { + fclose(file); + } + + /* Return the current state at end of file */ + return array; +} + /** @} */ /* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/legacy.c b/libssh/src/legacy.c index 6e7bfffc..15f287a6 100644 --- a/libssh/src/legacy.c +++ b/libssh/src/legacy.c @@ -163,7 +163,7 @@ int channel_change_pty_size(ssh_channel channel,int cols,int rows){ } ssh_channel channel_forward_accept(ssh_session session, int timeout_ms){ - return ssh_forward_accept(session,timeout_ms); + return ssh_channel_accept_forward(session, timeout_ms, NULL); } int channel_close(ssh_channel channel){ @@ -171,12 +171,12 @@ int channel_close(ssh_channel channel){ } int channel_forward_cancel(ssh_session session, const char *address, int port){ - return ssh_forward_cancel(session, address, port); + return ssh_channel_cancel_forward(session, address, port); } int channel_forward_listen(ssh_session session, const char *address, int port, int *bound_port){ - return ssh_forward_listen(session, address, port, bound_port); + return ssh_channel_listen_forward(session, address, port, bound_port); } void channel_free(ssh_channel channel){ @@ -580,7 +580,7 @@ int ssh_publickey_to_file(ssh_session session, ssh_set_error(session, SSH_FATAL, "Invalid parameters"); return SSH_ERROR; } - pubkey_64 = bin_to_base64(string_data(pubkey), ssh_string_len(pubkey)); + pubkey_64 = bin_to_base64(ssh_string_data(pubkey), ssh_string_len(pubkey)); if (pubkey_64 == NULL) { return SSH_ERROR; } diff --git a/libssh/src/libcrypto.c b/libssh/src/libcrypto.c index bb1d96ad..479c8c18 100644 --- a/libssh/src/libcrypto.c +++ b/libssh/src/libcrypto.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "libssh/priv.h" #include "libssh/session.h" @@ -38,6 +39,8 @@ #include #include #include +#include + #ifdef HAVE_OPENSSL_AES_H #define HAS_AES #include @@ -62,6 +65,8 @@ struct ssh_mac_ctx_struct { union { SHACTX sha1_ctx; SHA256CTX sha256_ctx; + SHA384CTX sha384_ctx; + SHA512CTX sha512_ctx; } ctx; }; @@ -74,6 +79,12 @@ static int alloc_key(struct ssh_cipher_struct *cipher) { return 0; } +void ssh_reseed(void){ + struct timeval tv; + gettimeofday(&tv, NULL); + RAND_add(&tv, sizeof(tv), 0.0); +} + SHACTX sha1_init(void) { SHACTX c = malloc(sizeof(*c)); if (c == NULL) { @@ -172,6 +183,52 @@ void sha256(unsigned char *digest, int len, unsigned char *hash) { SHA256(digest, len, hash); } +SHA384CTX sha384_init(void){ + SHA384CTX c = malloc(sizeof(*c)); + if (c == NULL) { + return NULL; + } + SHA384_Init(c); + + return c; +} + +void sha384_update(SHA384CTX c, const void *data, unsigned long len){ + SHA384_Update(c,data,len); +} + +void sha384_final(unsigned char *md, SHA384CTX c) { + SHA384_Final(md, c); + SAFE_FREE(c); +} + +void sha384(unsigned char *digest, int len, unsigned char *hash) { + SHA384(digest, len, hash); +} + +SHA512CTX sha512_init(void){ + SHA512CTX c = malloc(sizeof(*c)); + if (c == NULL) { + return NULL; + } + SHA512_Init(c); + + return c; +} + +void sha512_update(SHA512CTX c, const void *data, unsigned long len){ + SHA512_Update(c,data,len); +} + +void sha512_final(unsigned char *md, SHA512CTX c) { + SHA512_Final(md, c); + SAFE_FREE(c); +} + +void sha512(unsigned char *digest, int len, unsigned char *hash) { + SHA512(digest, len, hash); +} + MD5CTX md5_init(void) { MD5CTX c = malloc(sizeof(*c)); if (c == NULL) { @@ -193,7 +250,11 @@ void md5_final(unsigned char *md, MD5CTX c) { } ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ - ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct)); + ssh_mac_ctx ctx = malloc(sizeof(struct ssh_mac_ctx_struct)); + if (ctx == NULL) { + return NULL; + } + ctx->mac_type=type; switch(type){ case SSH_MAC_SHA1: @@ -203,7 +264,11 @@ ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ ctx->ctx.sha256_ctx = sha256_init(); return ctx; case SSH_MAC_SHA384: + ctx->ctx.sha384_ctx = sha384_init(); + return ctx; case SSH_MAC_SHA512: + ctx->ctx.sha512_ctx = sha512_init(); + return ctx; default: SAFE_FREE(ctx); return NULL; @@ -219,7 +284,11 @@ void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { sha256_update(ctx->ctx.sha256_ctx, data, len); break; case SSH_MAC_SHA384: + sha384_update(ctx->ctx.sha384_ctx, data, len); + break; case SSH_MAC_SHA512: + sha512_update(ctx->ctx.sha512_ctx, data, len); + break; default: break; } @@ -234,7 +303,11 @@ void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { sha256_final(md,ctx->ctx.sha256_ctx); break; case SSH_MAC_SHA384: + sha384_final(md,ctx->ctx.sha384_ctx); + break; case SSH_MAC_SHA512: + sha512_final(md,ctx->ctx.sha512_ctx); + break; default: break; } @@ -257,6 +330,15 @@ HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { case SSH_HMAC_SHA1: HMAC_Init(ctx, key, len, EVP_sha1()); break; + case SSH_HMAC_SHA256: + HMAC_Init(ctx, key, len, EVP_sha256()); + break; + case SSH_HMAC_SHA384: + HMAC_Init(ctx, key, len, EVP_sha384()); + break; + case SSH_HMAC_SHA512: + HMAC_Init(ctx, key, len, EVP_sha512()); + break; case SSH_HMAC_MD5: HMAC_Init(ctx, key, len, EVP_md5()); break; diff --git a/libssh/src/libgcrypt.c b/libssh/src/libgcrypt.c index 899bccdf..24d4a3c5 100644 --- a/libssh/src/libgcrypt.c +++ b/libssh/src/libgcrypt.c @@ -45,6 +45,9 @@ static int alloc_key(struct ssh_cipher_struct *cipher) { return 0; } +void ssh_reseed(void){ + } + SHACTX sha1_init(void) { SHACTX ctx = NULL; gcry_md_open(&ctx, GCRY_MD_SHA1, 0); @@ -66,10 +69,69 @@ void sha1(unsigned char *digest, int len, unsigned char *hash) { gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len); } +SHA256CTX sha256_init(void) { + SHA256CTX ctx = NULL; + gcry_md_open(&ctx, GCRY_MD_SHA256, 0); + + return ctx; +} + +void sha256_update(SHACTX c, const void *data, unsigned long len) { + gcry_md_write(c, data, len); +} + +void sha256_final(unsigned char *md, SHACTX c) { + gcry_md_final(c); + memcpy(md, gcry_md_read(c, 0), SHA256_DIGEST_LEN); + gcry_md_close(c); +} + void sha256(unsigned char *digest, int len, unsigned char *hash){ gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len); } +SHA384CTX sha384_init(void) { + SHA384CTX ctx = NULL; + gcry_md_open(&ctx, GCRY_MD_SHA384, 0); + + return ctx; +} + +void sha384_update(SHACTX c, const void *data, unsigned long len) { + gcry_md_write(c, data, len); +} + +void sha384_final(unsigned char *md, SHACTX c) { + gcry_md_final(c); + memcpy(md, gcry_md_read(c, 0), SHA384_DIGEST_LEN); + gcry_md_close(c); +} + +void sha384(unsigned char *digest, int len, unsigned char *hash) { + gcry_md_hash_buffer(GCRY_MD_SHA384, hash, digest, len); +} + +SHA512CTX sha512_init(void) { + SHA512CTX ctx = NULL; + gcry_md_open(&ctx, GCRY_MD_SHA512, 0); + + return ctx; +} + +void sha512_update(SHACTX c, const void *data, unsigned long len) { + gcry_md_write(c, data, len); +} + +void sha512_final(unsigned char *md, SHACTX c) { + gcry_md_final(c); + memcpy(md, gcry_md_read(c, 0), SHA512_DIGEST_LEN); + gcry_md_close(c); +} + +void sha512(unsigned char *digest, int len, unsigned char *hash) { + gcry_md_hash_buffer(GCRY_MD_SHA512, hash, digest, len); +} + MD5CTX md5_init(void) { MD5CTX c = NULL; gcry_md_open(&c, GCRY_MD_MD5, 0); @@ -88,7 +150,11 @@ void md5_final(unsigned char *md, MD5CTX c) { } ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ - ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct)); + ssh_mac_ctx ctx = malloc(sizeof(struct ssh_mac_ctx_struct)); + if (ctx == NULL) { + return NULL; + } + ctx->mac_type=type; switch(type){ case SSH_MAC_SHA1: @@ -121,13 +187,13 @@ void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { len=SHA_DIGEST_LEN; break; case SSH_MAC_SHA256: - len=SHA256_DIGEST_LENGTH; + len=SHA256_DIGEST_LEN; break; case SSH_MAC_SHA384: - len=SHA384_DIGEST_LENGTH; + len=SHA384_DIGEST_LEN; break; case SSH_MAC_SHA512: - len=SHA512_DIGEST_LENGTH; + len=SHA512_DIGEST_LEN; break; } gcry_md_final(ctx->ctx); @@ -143,6 +209,15 @@ HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { case SSH_HMAC_SHA1: gcry_md_open(&c, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); break; + case SSH_HMAC_SHA256: + gcry_md_open(&c, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); + break; + case SSH_HMAC_SHA384: + gcry_md_open(&c, GCRY_MD_SHA384, GCRY_MD_FLAG_HMAC); + break; + case SSH_HMAC_SHA512: + gcry_md_open(&c, GCRY_MD_SHA512, GCRY_MD_FLAG_HMAC); + break; case SSH_HMAC_MD5: gcry_md_open(&c, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC); break; @@ -403,8 +478,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 128, .set_encrypt_key = blowfish_set_key, .set_decrypt_key = blowfish_set_key, - .cbc_encrypt = blowfish_encrypt, - .cbc_decrypt = blowfish_decrypt + .encrypt = blowfish_encrypt, + .decrypt = blowfish_decrypt }, { .name = "aes128-ctr", @@ -414,8 +489,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 128, .set_encrypt_key = aes_set_key, .set_decrypt_key = aes_set_key, - .cbc_encrypt = aes_encrypt, - .cbc_decrypt = aes_encrypt + .encrypt = aes_encrypt, + .decrypt = aes_encrypt }, { .name = "aes192-ctr", @@ -425,8 +500,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 192, .set_encrypt_key = aes_set_key, .set_decrypt_key = aes_set_key, - .cbc_encrypt = aes_encrypt, - .cbc_decrypt = aes_encrypt + .encrypt = aes_encrypt, + .decrypt = aes_encrypt }, { .name = "aes256-ctr", @@ -436,8 +511,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 256, .set_encrypt_key = aes_set_key, .set_decrypt_key = aes_set_key, - .cbc_encrypt = aes_encrypt, - .cbc_decrypt = aes_encrypt + .encrypt = aes_encrypt, + .decrypt = aes_encrypt }, { .name = "aes128-cbc", @@ -447,8 +522,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 128, .set_encrypt_key = aes_set_key, .set_decrypt_key = aes_set_key, - .cbc_encrypt = aes_encrypt, - .cbc_decrypt = aes_decrypt + .encrypt = aes_encrypt, + .decrypt = aes_decrypt }, { .name = "aes192-cbc", @@ -458,8 +533,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 192, .set_encrypt_key = aes_set_key, .set_decrypt_key = aes_set_key, - .cbc_encrypt = aes_encrypt, - .cbc_decrypt = aes_decrypt + .encrypt = aes_encrypt, + .decrypt = aes_decrypt }, { .name = "aes256-cbc", @@ -469,8 +544,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 256, .set_encrypt_key = aes_set_key, .set_decrypt_key = aes_set_key, - .cbc_encrypt = aes_encrypt, - .cbc_decrypt = aes_decrypt + .encrypt = aes_encrypt, + .decrypt = aes_decrypt }, { .name = "3des-cbc", @@ -480,8 +555,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 192, .set_encrypt_key = des3_set_key, .set_decrypt_key = des3_set_key, - .cbc_encrypt = des3_encrypt, - .cbc_decrypt = des3_decrypt + .encrypt = des3_encrypt, + .decrypt = des3_decrypt }, { .name = "3des-cbc-ssh1", @@ -491,8 +566,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 192, .set_encrypt_key = des3_1_set_key, .set_decrypt_key = des3_1_set_key, - .cbc_encrypt = des3_1_encrypt, - .cbc_decrypt = des3_1_decrypt + .encrypt = des3_1_encrypt, + .decrypt = des3_1_decrypt }, { .name = "des-cbc-ssh1", @@ -502,8 +577,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 64, .set_encrypt_key = des1_set_key, .set_decrypt_key = des1_set_key, - .cbc_encrypt = des1_1_encrypt, - .cbc_decrypt = des1_1_decrypt + .encrypt = des1_1_encrypt, + .decrypt = des1_1_decrypt }, { .name = NULL, @@ -513,8 +588,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .keysize = 0, .set_encrypt_key = NULL, .set_decrypt_key = NULL, - .cbc_encrypt = NULL, - .cbc_decrypt = NULL + .encrypt = NULL, + .decrypt = NULL } }; diff --git a/libssh/src/log.c b/libssh/src/log.c index 4552b969..a72dd412 100644 --- a/libssh/src/log.c +++ b/libssh/src/log.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2008 by Aris Adamantiadis + * Copyright (c) 2008-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -215,6 +215,10 @@ ssh_logging_callback ssh_get_log_callback(void) { */ void *ssh_get_log_userdata(void) { + if (ssh_log_userdata == NULL) { + return NULL; + } + return ssh_log_userdata; } diff --git a/libssh/src/messages.c b/libssh/src/messages.c index 2c99311d..5a6963e8 100644 --- a/libssh/src/messages.c +++ b/libssh/src/messages.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2003-2009 by Aris Adamantiadis + * Copyright (c) 2003-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -208,8 +208,8 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg) ssh_callbacks_exists(channel->callbacks, channel_pty_window_change_function)) { rc = channel->callbacks->channel_pty_window_change_function(session, channel, - msg->channel_request.height, msg->channel_request.width, - msg->channel_request.pxheight, msg->channel_request.pxwidth, + msg->channel_request.width, msg->channel_request.height, + msg->channel_request.pxwidth, msg->channel_request.pxheight, channel->callbacks->userdata); } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_EXEC && ssh_callbacks_exists(channel->callbacks, channel_exec_request_function)) { @@ -508,8 +508,7 @@ void ssh_message_free(ssh_message msg){ case SSH_REQUEST_AUTH: SAFE_FREE(msg->auth_request.username); if (msg->auth_request.password) { - memset(msg->auth_request.password, 0, - strlen(msg->auth_request.password)); + BURN_STRING(msg->auth_request.password); SAFE_FREE(msg->auth_request.password); } ssh_key_free(msg->auth_request.pubkey); @@ -586,104 +585,34 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session, session->current_crypto ? session->current_crypto : session->next_crypto; ssh_buffer buffer; - ssh_string str; + ssh_string str=NULL; int rc; buffer = ssh_buffer_new(); if (buffer == NULL) { return NULL; } - - /* Add session id */ - str = ssh_string_new(crypto->digest_len); - if (str == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - ssh_string_fill(str, crypto->session_id, crypto->digest_len); - - rc = buffer_add_ssh_string(buffer, str); - string_free(str); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - /* Add the type */ - rc = buffer_add_u8(buffer, SSH2_MSG_USERAUTH_REQUEST); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - /* Add the username */ - str = ssh_string_from_char(msg->auth_request.username); - if (str == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - rc = buffer_add_ssh_string(buffer, str); - string_free(str); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - /* Add the service name */ - str = ssh_string_from_char(service); - if (str == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - rc = buffer_add_ssh_string(buffer, str); - string_free(str); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - /* Add the method (publickey) */ - str = ssh_string_from_char("publickey"); - if (str == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - rc = buffer_add_ssh_string(buffer, str); - string_free(str); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - /* Has been signed (TRUE) */ - rc = buffer_add_u8(buffer, 1); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - /* Add the public key algorithm */ - str = ssh_string_from_char(msg->auth_request.pubkey->type_c); - if (str == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - rc = buffer_add_ssh_string(buffer, str); - string_free(str); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - /* Add the publickey as blob */ rc = ssh_pki_export_pubkey_blob(msg->auth_request.pubkey, &str); if (rc < 0) { ssh_buffer_free(buffer); return NULL; } - rc = buffer_add_ssh_string(buffer, str); - string_free(str); - if (rc < 0) { + + rc = ssh_buffer_pack(buffer, + "dPbsssbsS", + crypto->digest_len, /* session ID string */ + (size_t)crypto->digest_len, crypto->session_id, + SSH2_MSG_USERAUTH_REQUEST, /* type */ + msg->auth_request.username, + service, + "publickey", /* method */ + 1, /* has to be signed (true) */ + msg->auth_request.pubkey->type_c, /* pubkey algorithm */ + str); /* public key as a blob */ + + ssh_string_free(str); + if (rc != SSH_OK) { + ssh_set_error_oom(session); ssh_buffer_free(buffer); return NULL; } @@ -698,11 +627,10 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session, * SSH Message */ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ - ssh_string str; ssh_message msg = NULL; char *service = NULL; char *method = NULL; - uint32_t method_size = 0; + int rc; (void)user; (void)type; @@ -713,35 +641,13 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ goto error; } msg->type = SSH_REQUEST_AUTH; + rc = ssh_buffer_unpack(packet, + "sss", + &msg->auth_request.username, + &service, + &method); - str = buffer_get_ssh_string(packet); - if (str == NULL) { - goto error; - } - msg->auth_request.username = ssh_string_to_char(str); - ssh_string_free(str); - if (msg->auth_request.username == NULL) { - goto error; - } - - str = buffer_get_ssh_string(packet); - if (str == NULL) { - goto error; - } - service = ssh_string_to_char(str); - ssh_string_free(str); - if (service == NULL) { - goto error; - } - - str = buffer_get_ssh_string(packet); - if (str == NULL) { - goto error; - } - method = ssh_string_to_char(str); - method_size = ssh_string_len(str); - ssh_string_free(str); - if (method == NULL) { + if (rc != SSH_OK) { goto error; } @@ -751,32 +657,23 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ msg->auth_request.username); - if (strncmp(method, "none", method_size) == 0) { + if (strcmp(method, "none") == 0) { msg->auth_request.method = SSH_AUTH_METHOD_NONE; goto end; } - if (strncmp(method, "password", method_size) == 0) { - ssh_string pass = NULL; + if (strcmp(method, "password") == 0) { uint8_t tmp; msg->auth_request.method = SSH_AUTH_METHOD_PASSWORD; - buffer_get_u8(packet, &tmp); - pass = buffer_get_ssh_string(packet); - if (pass == NULL) { - goto error; - } - msg->auth_request.password = ssh_string_to_char(pass); - ssh_string_burn(pass); - ssh_string_free(pass); - pass = NULL; - if (msg->auth_request.password == NULL) { + rc = ssh_buffer_unpack(packet, "bs", &tmp, &msg->auth_request.password); + if (rc != SSH_OK) { goto error; } goto end; } - if (strncmp(method, "keyboard-interactive", method_size) == 0) { + if (strcmp(method, "keyboard-interactive") == 0) { ssh_string lang = NULL; ssh_string submethods = NULL; @@ -806,23 +703,20 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ goto end; } - if (strncmp(method, "publickey", method_size) == 0) { + if (strcmp(method, "publickey") == 0) { ssh_string algo = NULL; ssh_string pubkey_blob = NULL; uint8_t has_sign; - int rc; msg->auth_request.method = SSH_AUTH_METHOD_PUBLICKEY; SAFE_FREE(method); - buffer_get_u8(packet, &has_sign); - algo = buffer_get_ssh_string(packet); - if (algo == NULL) { - goto error; - } - pubkey_blob = buffer_get_ssh_string(packet); - if (pubkey_blob == NULL) { - ssh_string_free(algo); - algo = NULL; + rc = ssh_buffer_unpack(packet, "bSS", + &has_sign, + &algo, + &pubkey_blob + ); + + if (rc != SSH_OK) { goto error; } ssh_string_free(algo); @@ -877,7 +771,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ goto end; } #ifdef WITH_GSSAPI - if (strncmp(method, "gssapi-with-mic", method_size) == 0) { + if (strcmp(method, "gssapi-with-mic") == 0) { uint32_t n_oid; ssh_string *oids; ssh_string oid; @@ -966,6 +860,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ uint32_t nanswers; uint32_t i; ssh_string tmp; + int rc; ssh_message msg = NULL; @@ -993,7 +888,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ msg->auth_request.username = NULL; #endif - buffer_get_u32(packet, &nanswers); + rc = ssh_buffer_unpack(packet, "d", &nanswers); + if (rc != SSH_OK) { + ssh_set_error_invalid(session); + goto error; + } if (session->kbdint == NULL) { SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a keyboard-interactive " @@ -1007,7 +906,6 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ } } - nanswers = ntohl(nanswers); SSH_LOG(SSH_LOG_PACKET,"kbdint: %d answers",nanswers); if (nanswers > KBDINT_MAX_PROMPT) { ssh_set_error(session, SSH_FATAL, @@ -1071,9 +969,9 @@ error: SSH_PACKET_CALLBACK(ssh_packet_channel_open){ ssh_message msg = NULL; - ssh_string type_s = NULL, originator = NULL, destination = NULL; char *type_c = NULL; - uint32_t sender, window, packet_size, originator_port, destination_port; + uint32_t originator_port, destination_port; + int rc; (void)type; (void)user; @@ -1084,30 +982,18 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){ } msg->type = SSH_REQUEST_CHANNEL_OPEN; - - type_s = buffer_get_ssh_string(packet); - if (type_s == NULL) { - ssh_set_error_oom(session); - goto error; - } - type_c = ssh_string_to_char(type_s); - if (type_c == NULL) { - ssh_set_error_oom(session); - goto error; + rc = ssh_buffer_unpack(packet, "s", &type_c); + if (rc != SSH_OK){ + goto error; } SSH_LOG(SSH_LOG_PACKET, "Clients wants to open a %s channel", type_c); - ssh_string_free(type_s); - type_s=NULL; - buffer_get_u32(packet, &sender); - buffer_get_u32(packet, &window); - buffer_get_u32(packet, &packet_size); - - msg->channel_request_open.sender = ntohl(sender); - msg->channel_request_open.window = ntohl(window); - msg->channel_request_open.packet_size = ntohl(packet_size); + ssh_buffer_unpack(packet,"ddd", + &msg->channel_request_open.sender, + &msg->channel_request_open.window, + &msg->channel_request_open.packet_size); if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED){ ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)"); @@ -1121,96 +1007,46 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){ } if (strcmp(type_c,"direct-tcpip") == 0) { - destination = buffer_get_ssh_string(packet); - if (destination == NULL) { - ssh_set_error_oom(session); + rc = ssh_buffer_unpack(packet, + "sdsd", + &msg->channel_request_open.destination, + &destination_port, + &msg->channel_request_open.originator, + &originator_port); + if (rc != SSH_OK) { goto error; } - msg->channel_request_open.destination = ssh_string_to_char(destination); - if (msg->channel_request_open.destination == NULL) { - ssh_set_error_oom(session); - ssh_string_free(destination); - goto error; - } - ssh_string_free(destination); - - buffer_get_u32(packet, &destination_port); - msg->channel_request_open.destination_port = (uint16_t) ntohl(destination_port); - - originator = buffer_get_ssh_string(packet); - if (originator == NULL) { - ssh_set_error_oom(session); - goto error; - } - msg->channel_request_open.originator = ssh_string_to_char(originator); - if (msg->channel_request_open.originator == NULL) { - ssh_set_error_oom(session); - ssh_string_free(originator); - goto error; - } - ssh_string_free(originator); - - buffer_get_u32(packet, &originator_port); - msg->channel_request_open.originator_port = (uint16_t) ntohl(originator_port); + msg->channel_request_open.destination_port = (uint16_t) destination_port; + msg->channel_request_open.originator_port = (uint16_t) originator_port; msg->channel_request_open.type = SSH_CHANNEL_DIRECT_TCPIP; goto end; } if (strcmp(type_c,"forwarded-tcpip") == 0) { - destination = buffer_get_ssh_string(packet); - if (destination == NULL) { - ssh_set_error_oom(session); - goto error; - } - msg->channel_request_open.destination = ssh_string_to_char(destination); - if (msg->channel_request_open.destination == NULL) { - ssh_set_error_oom(session); - ssh_string_free(destination); - goto error; - } - ssh_string_free(destination); - - buffer_get_u32(packet, &destination_port); - msg->channel_request_open.destination_port = (uint16_t) ntohl(destination_port); - - originator = buffer_get_ssh_string(packet); - if (originator == NULL) { - ssh_set_error_oom(session); - goto error; - } - msg->channel_request_open.originator = ssh_string_to_char(originator); - if (msg->channel_request_open.originator == NULL) { - ssh_set_error_oom(session); - ssh_string_free(originator); - goto error; - } - ssh_string_free(originator); - - buffer_get_u32(packet, &originator_port); - msg->channel_request_open.originator_port = (uint16_t) ntohl(originator_port); - + rc = ssh_buffer_unpack(packet, "sdsd", + &msg->channel_request_open.destination, + &destination_port, + &msg->channel_request_open.originator, + &originator_port + ); + if (rc != SSH_OK){ + goto error; + } + msg->channel_request_open.destination_port = (uint16_t) destination_port; + msg->channel_request_open.originator_port = (uint16_t) originator_port; msg->channel_request_open.type = SSH_CHANNEL_FORWARDED_TCPIP; goto end; } if (strcmp(type_c,"x11") == 0) { - originator = buffer_get_ssh_string(packet); - if (originator == NULL) { - ssh_set_error_oom(session); - goto error; - } - msg->channel_request_open.originator = ssh_string_to_char(originator); - if (msg->channel_request_open.originator == NULL) { - ssh_set_error_oom(session); - ssh_string_free(originator); - goto error; - } - ssh_string_free(originator); - - buffer_get_u32(packet, &originator_port); - msg->channel_request_open.originator_port = (uint16_t) ntohl(originator_port); - + rc = ssh_buffer_unpack(packet, "sd", + &msg->channel_request_open.originator, + &originator_port); + if (rc != SSH_OK){ + goto error; + } + msg->channel_request_open.originator_port = (uint16_t) originator_port; msg->channel_request_open.type = SSH_CHANNEL_X11; goto end; } @@ -1222,8 +1058,6 @@ error: ssh_message_free(msg); msg=NULL; end: - if(type_s != NULL) - ssh_string_free(type_s); SAFE_FREE(type_c); if(msg != NULL) ssh_message_queue(session,msg); @@ -1249,28 +1083,15 @@ int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_c chan->remote_window = msg->channel_request_open.window; chan->state = SSH_CHANNEL_STATE_OPEN; - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); - if (rc < 0) { - return SSH_ERROR; - } - - rc = buffer_add_u32(session->out_buffer, htonl(chan->remote_channel)); - if (rc < 0) { - return SSH_ERROR; - } - - rc =buffer_add_u32(session->out_buffer, htonl(chan->local_channel)); - if (rc < 0) { - return SSH_ERROR; - } - - rc = buffer_add_u32(session->out_buffer, htonl(chan->local_window)); - if (rc < 0) { - return SSH_ERROR; - } - - rc = buffer_add_u32(session->out_buffer, htonl(chan->local_maxpacket)); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bdddd", + SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, + chan->remote_channel, + chan->local_channel, + chan->local_window, + chan->local_maxpacket); + if (rc != SSH_OK) { + ssh_set_error_oom(session); return SSH_ERROR; } @@ -1327,6 +1148,7 @@ ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg) { int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet, const char *request, uint8_t want_reply) { ssh_message msg = NULL; + int rc; msg = ssh_message_new(session); if (msg == NULL) { @@ -1343,36 +1165,18 @@ int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, msg->channel_request.want_reply = want_reply; if (strcmp(request, "pty-req") == 0) { - ssh_string term = NULL; - char *term_c = NULL; - term = buffer_get_ssh_string(packet); - if (term == NULL) { - ssh_set_error_oom(session); - goto error; - } - term_c = ssh_string_to_char(term); - if (term_c == NULL) { - ssh_set_error_oom(session); - ssh_string_free(term); - goto error; - } - ssh_string_free(term); + rc = ssh_buffer_unpack(packet, "sddddS", + &msg->channel_request.TERM, + &msg->channel_request.width, + &msg->channel_request.height, + &msg->channel_request.pxwidth, + &msg->channel_request.pxheight, + &msg->channel_request.modes + ); msg->channel_request.type = SSH_CHANNEL_REQUEST_PTY; - msg->channel_request.TERM = term_c; - buffer_get_u32(packet, &msg->channel_request.width); - buffer_get_u32(packet, &msg->channel_request.height); - buffer_get_u32(packet, &msg->channel_request.pxwidth); - buffer_get_u32(packet, &msg->channel_request.pxheight); - - msg->channel_request.width = ntohl(msg->channel_request.width); - msg->channel_request.height = ntohl(msg->channel_request.height); - msg->channel_request.pxwidth = ntohl(msg->channel_request.pxwidth); - msg->channel_request.pxheight = ntohl(msg->channel_request.pxheight); - msg->channel_request.modes = buffer_get_ssh_string(packet); - if (msg->channel_request.modes == NULL) { - SAFE_FREE(term_c); + if (rc != SSH_OK) { goto error; } goto end; @@ -1380,39 +1184,24 @@ int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, if (strcmp(request, "window-change") == 0) { msg->channel_request.type = SSH_CHANNEL_REQUEST_WINDOW_CHANGE; - - buffer_get_u32(packet, &msg->channel_request.width); - buffer_get_u32(packet, &msg->channel_request.height); - buffer_get_u32(packet, &msg->channel_request.pxwidth); - buffer_get_u32(packet, &msg->channel_request.pxheight); - - msg->channel_request.width = ntohl(msg->channel_request.width); - msg->channel_request.height = ntohl(msg->channel_request.height); - msg->channel_request.pxwidth = ntohl(msg->channel_request.pxwidth); - msg->channel_request.pxheight = ntohl(msg->channel_request.pxheight); - + rc = ssh_buffer_unpack(packet, "dddd", + &msg->channel_request.width, + &msg->channel_request.height, + &msg->channel_request.pxwidth, + &msg->channel_request.pxheight); + if (rc != SSH_OK){ + goto error; + } goto end; } if (strcmp(request, "subsystem") == 0) { - ssh_string subsys = NULL; - char *subsys_c = NULL; - subsys = buffer_get_ssh_string(packet); - if (subsys == NULL) { - ssh_set_error_oom(session); - goto error; - } - subsys_c = ssh_string_to_char(subsys); - if (subsys_c == NULL) { - ssh_set_error_oom(session); - ssh_string_free(subsys); - goto error; - } - ssh_string_free(subsys); - + rc = ssh_buffer_unpack(packet, "s", + &msg->channel_request.subsystem); msg->channel_request.type = SSH_CHANNEL_REQUEST_SUBSYSTEM; - msg->channel_request.subsystem = subsys_c; - + if (rc != SSH_OK){ + goto error; + } goto end; } @@ -1422,82 +1211,37 @@ int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, } if (strcmp(request, "exec") == 0) { - ssh_string cmd = NULL; - cmd = buffer_get_ssh_string(packet); - if (cmd == NULL) { - ssh_set_error_oom(session); - goto error; - } + rc = ssh_buffer_unpack(packet, "s", + &msg->channel_request.command); msg->channel_request.type = SSH_CHANNEL_REQUEST_EXEC; - msg->channel_request.command = ssh_string_to_char(cmd); - ssh_string_free(cmd); - if (msg->channel_request.command == NULL) { + if (rc != SSH_OK) { goto error; } goto end; } if (strcmp(request, "env") == 0) { - ssh_string name = NULL; - ssh_string value = NULL; - name = buffer_get_ssh_string(packet); - if (name == NULL) { - ssh_set_error_oom(session); - goto error; - } - value = buffer_get_ssh_string(packet); - if (value == NULL) { - ssh_set_error_oom(session); - ssh_string_free(name); - goto error; - } - + rc = ssh_buffer_unpack(packet, "ss", + &msg->channel_request.var_name, + &msg->channel_request.var_value); msg->channel_request.type = SSH_CHANNEL_REQUEST_ENV; - msg->channel_request.var_name = ssh_string_to_char(name); - msg->channel_request.var_value = ssh_string_to_char(value); - if (msg->channel_request.var_name == NULL || - msg->channel_request.var_value == NULL) { - ssh_string_free(name); - ssh_string_free(value); + if (rc != SSH_OK) { goto error; } - ssh_string_free(name); - ssh_string_free(value); - goto end; } if (strcmp(request, "x11-req") == 0) { - ssh_string auth_protocol = NULL; - ssh_string auth_cookie = NULL; - - buffer_get_u8(packet, &msg->channel_request.x11_single_connection); - - auth_protocol = buffer_get_ssh_string(packet); - if (auth_protocol == NULL) { - ssh_set_error_oom(session); - goto error; - } - auth_cookie = buffer_get_ssh_string(packet); - if (auth_cookie == NULL) { - ssh_set_error_oom(session); - ssh_string_free(auth_protocol); - goto error; - } + rc = ssh_buffer_unpack(packet, "bssd", + &msg->channel_request.x11_single_connection, + &msg->channel_request.x11_auth_protocol, + &msg->channel_request.x11_auth_cookie, + &msg->channel_request.x11_screen_number); msg->channel_request.type = SSH_CHANNEL_REQUEST_X11; - msg->channel_request.x11_auth_protocol = ssh_string_to_char(auth_protocol); - msg->channel_request.x11_auth_cookie = ssh_string_to_char(auth_cookie); - if (msg->channel_request.x11_auth_protocol == NULL || - msg->channel_request.x11_auth_cookie == NULL) { - ssh_string_free(auth_protocol); - ssh_string_free(auth_cookie); + if (rc != SSH_OK) { goto error; } - ssh_string_free(auth_protocol); - ssh_string_free(auth_cookie); - - buffer_get_u32(packet, &msg->channel_request.x11_screen_number); goto end; } @@ -1515,6 +1259,7 @@ error: int ssh_message_channel_request_reply_success(ssh_message msg) { uint32_t channel; + int rc; if (msg == NULL) { return SSH_ERROR; @@ -1526,10 +1271,12 @@ int ssh_message_channel_request_reply_success(ssh_message msg) { SSH_LOG(SSH_LOG_PACKET, "Sending a channel_request success to channel %d", channel); - if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_CHANNEL_SUCCESS) < 0) { - return SSH_ERROR; - } - if (buffer_add_u32(msg->session->out_buffer, htonl(channel)) < 0) { + rc = ssh_buffer_pack(msg->session->out_buffer, + "bd", + SSH2_MSG_CHANNEL_SUCCESS, + channel); + if (rc != SSH_OK){ + ssh_set_error_oom(msg->session); return SSH_ERROR; } @@ -1545,72 +1292,65 @@ int ssh_message_channel_request_reply_success(ssh_message msg) { #ifdef WITH_SERVER SSH_PACKET_CALLBACK(ssh_packet_global_request){ ssh_message msg = NULL; - ssh_string request_s=NULL; char *request=NULL; - ssh_string bind_addr_s=NULL; - char *bind_addr=NULL; - uint32_t bind_port; uint8_t want_reply; int rc = SSH_PACKET_USED; + int r; (void)user; (void)type; (void)packet; - request_s = buffer_get_ssh_string(packet); - if (request_s != NULL) { - request = ssh_string_to_char(request_s); - ssh_string_free(request_s); - } - - buffer_get_u8(packet, &want_reply); - SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH_MSG_GLOBAL_REQUEST packet"); + r = ssh_buffer_unpack(packet, "sb", + &request, + &want_reply); + if (r != SSH_OK){ + goto error; + } msg = ssh_message_new(session); if (msg == NULL) { - ssh_string_free_char(request); - return SSH_PACKET_NOT_USED; + ssh_set_error_oom(session); + goto error; } msg->type = SSH_REQUEST_GLOBAL; - if (request && strcmp(request, "tcpip-forward") == 0) { - bind_addr_s = buffer_get_ssh_string(packet); - if (bind_addr_s != NULL) { - bind_addr = ssh_string_to_char(bind_addr_s); - ssh_string_free(bind_addr_s); + if (strcmp(request, "tcpip-forward") == 0) { + r = ssh_buffer_unpack(packet, "sd", + &msg->global_request.bind_address, + &msg->global_request.bind_port + ); + if (r != SSH_OK){ + goto error; } - - buffer_get_u32(packet, &bind_port); - bind_port = ntohl(bind_port); - msg->global_request.type = SSH_GLOBAL_REQUEST_TCPIP_FORWARD; msg->global_request.want_reply = want_reply; - msg->global_request.bind_address = bind_addr; - msg->global_request.bind_port = bind_port; - SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, + msg->global_request.bind_address, + msg->global_request.bind_port); if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) { - SSH_LOG(SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + SSH_LOG(SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, + want_reply, msg->global_request.bind_address, + msg->global_request.bind_port); session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata); } else { ssh_message_reply_default(msg); } - } else if (request && strcmp(request, "cancel-tcpip-forward") == 0) { - bind_addr_s = buffer_get_ssh_string(packet); - if (bind_addr_s != NULL) { - bind_addr = ssh_string_to_char(bind_addr_s); - ssh_string_free(bind_addr_s); + } else if (strcmp(request, "cancel-tcpip-forward") == 0) { + r = ssh_buffer_unpack(packet, "sd", + &msg->global_request.bind_address, + &msg->global_request.bind_port); + if (r != SSH_OK){ + goto error; } - buffer_get_u32(packet, &bind_port); - bind_port = ntohl(bind_port); - msg->global_request.type = SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD; msg->global_request.want_reply = want_reply; - msg->global_request.bind_address = bind_addr; - msg->global_request.bind_port = bind_port; - SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, + msg->global_request.bind_address, + msg->global_request.bind_port); if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) { session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata); @@ -1624,9 +1364,12 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){ SAFE_FREE(msg); SAFE_FREE(request); - SAFE_FREE(bind_addr); - return rc; +error: + SAFE_FREE(msg); + SAFE_FREE(request); + SSH_LOG(SSH_LOG_WARNING, "Invalid SSH_MSG_GLOBAL_REQUEST packet"); + return SSH_PACKET_NOT_USED; } #endif /* WITH_SERVER */ diff --git a/libssh/src/misc.c b/libssh/src/misc.c index 18a59d60..6daf60ab 100644 --- a/libssh/src/misc.c +++ b/libssh/src/misc.c @@ -4,7 +4,7 @@ * This file is part of the SSH Library * * Copyright (c) 2003-2009 by Aris Adamantiadis - * Copyright (c) 2008-2009 by Andreas Schneider + * Copyright (c) 2008-2009 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -697,7 +697,6 @@ char *ssh_path_expand_tilde(const char *d) { } char *ssh_path_expand_escape(ssh_session session, const char *s) { -#define MAX_BUF_SIZE 4096 char host[NI_MAXHOST]; char buf[MAX_BUF_SIZE]; char *r, *x = NULL; diff --git a/libssh/src/options.c b/libssh/src/options.c index e02ad4df..f7f24553 100644 --- a/libssh/src/options.c +++ b/libssh/src/options.c @@ -4,7 +4,7 @@ * This file is part of the SSH Library * * Copyright (c) 2003-2008 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider + * Copyright (c) 2009-2013 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -136,7 +136,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest) { } } - for (i = 0; i < 10; ++i) { + for (i = 0; i < 10; i++) { if (src->opts.wanted_methods[i]) { new->opts.wanted_methods[i] = strdup(src->opts.wanted_methods[i]); if (new->opts.wanted_methods[i] == NULL) { @@ -367,6 +367,18 @@ int ssh_options_set_algo(ssh_session session, int algo, * Set the command to be executed in order to connect to * server (const char *). * + * - SSH_OPTIONS_GSSAPI_SERVER_IDENTITY + * Set it to specify the GSSAPI server identity that libssh + * should expect when connecting to the server (const char *). + * + * - SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY + * Set it to specify the GSSAPI client identity that libssh + * should expect when connecting to the server (const char *). + * + * - SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS + * Set it to specify that GSSAPI should delegate credentials + * to the server (int, 0 = false). + * * @param value The value to set. This is a generic pointer and the * datatype which is used should be set according to the * type set. @@ -661,6 +673,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, } session->common.log_verbosity = i & 0xffff; + ssh_set_log_level(i & 0xffff); } break; case SSH_OPTIONS_CIPHERS_C_S: @@ -703,6 +716,26 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } break; + case SSH_OPTIONS_HMAC_C_S: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + if (ssh_options_set_algo(session, SSH_MAC_C_S, v) < 0) + return -1; + } + break; + case SSH_OPTIONS_HMAC_S_C: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + if (ssh_options_set_algo(session, SSH_MAC_S_C, v) < 0) + return -1; + } + break; case SSH_OPTIONS_COMPRESSION_C_S: v = value; if (v == NULL || v[0] == '\0') { @@ -792,6 +825,45 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, } } break; + case SSH_OPTIONS_GSSAPI_SERVER_IDENTITY: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + SAFE_FREE(session->opts.gss_server_identity); + session->opts.gss_server_identity = strdup(v); + if (session->opts.gss_server_identity == NULL) { + ssh_set_error_oom(session); + return -1; + } + } + break; + case SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + SAFE_FREE(session->opts.gss_client_identity); + session->opts.gss_client_identity = strdup(v); + if (session->opts.gss_client_identity == NULL) { + ssh_set_error_oom(session); + return -1; + } + } + break; + case SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS: + if (value == NULL) { + ssh_set_error_invalid(session); + return -1; + } else { + int x = *(int *)value; + + session->opts.gss_delegate_creds = (x & 0xff); + } + break; + default: ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); return -1; @@ -1232,84 +1304,93 @@ int ssh_options_apply(ssh_session session) { * @addtogroup libssh_server * @{ */ -static int ssh_bind_options_set_algo(ssh_bind sshbind, int algo, - const char *list) { - if (!verify_existing_algo(algo, list)) { - ssh_set_error(sshbind, SSH_REQUEST_DENIED, - "Setting method: no algorithm for method \"%s\" (%s)\n", - ssh_kex_get_description(algo), list); - return -1; - } - - SAFE_FREE(sshbind->wanted_methods[algo]); - sshbind->wanted_methods[algo] = strdup(list); - if (sshbind->wanted_methods[algo] == NULL) { - ssh_set_error_oom(sshbind); - return -1; - } - - return 0; +static int ssh_bind_set_key(ssh_bind sshbind, char **key_loc, + const void *value) { + if (value == NULL) { + ssh_set_error_invalid(sshbind); + return -1; + } else { + SAFE_FREE(*key_loc); + *key_loc = strdup(value); + if (*key_loc == NULL) { + ssh_set_error_oom(sshbind); + return -1; + } + } + return 0; } /** - * @brief This function can set all possible ssh bind options. + * @brief Set options for an SSH server bind. * - * @param sshbind An allocated ssh bind structure. + * @param sshbind The ssh server bind to configure. * - * @param type The option type to set. This could be one of the + * @param type The option type to set. This should be one of the * following: * - * SSH_BIND_OPTIONS_LOG_VERBOSITY: - * Set the session logging verbosity (integer). + * - SSH_BIND_OPTIONS_HOSTKEY: + * Set the path to an ssh host key, regardless + * of type. Only one key from per key type + * (RSA, DSA, ECDSA) is allowed in an ssh_bind + * at a time, and later calls to this function + * with this option for the same key type will + * override prior calls (const char *). * - * The verbosity of the messages. Every log smaller or - * equal to verbosity will be shown. - * SSH_LOG_NOLOG: No logging - * SSH_LOG_RARE: Rare conditions or warnings - * SSH_LOG_ENTRY: API-accessible entrypoints - * SSH_LOG_PACKET: Packet id and size - * SSH_LOG_FUNCTIONS: Function entering and leaving + * - SSH_BIND_OPTIONS_BINDADDR: + * Set the IP address to bind (const char *). * - * SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: - * Set the session logging verbosity (integer). + * - SSH_BIND_OPTIONS_BINDPORT: + * Set the port to bind (unsigned int *). * - * The verbosity of the messages. Every log smaller or - * equal to verbosity will be shown. - * SSH_LOG_NOLOG: No logging - * SSH_LOG_RARE: Rare conditions or warnings - * SSH_LOG_ENTRY: API-accessible entrypoints - * SSH_LOG_PACKET: Packet id and size - * SSH_LOG_FUNCTIONS: Function entering and leaving + * - SSH_BIND_OPTIONS_BINDPORT_STR: + * Set the port to bind (const char *). * - * SSH_BIND_OPTIONS_BINDADDR: - * Set the bind address. + * - SSH_BIND_OPTIONS_LOG_VERBOSITY: + * Set the session logging verbosity (int *). + * The logging verbosity should have one of the + * following values, which are listed in order + * of increasing verbosity. Every log message + * with verbosity less than or equal to the + * logging verbosity will be shown. + * - SSH_LOG_NOLOG: No logging + * - SSH_LOG_RARE: Rare conditions or warnings + * - SSH_LOG_ENTRY: API-accessible entrypoints + * - SSH_LOG_PACKET: Packet id and size + * - SSH_LOG_FUNCTIONS: Function entering and leaving * - * SSH_BIND_OPTIONS_BINDPORT: - * Set the bind port, default is 22. + * - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: + * Set the session logging verbosity via a + * string that will be converted to a numerical + * value (e.g. "3") and interpreted according + * to the values of + * SSH_BIND_OPTIONS_LOG_VERBOSITY above (const + * char *). * - * SSH_BIND_OPTIONS_HOSTKEY: - * Set the server public key type: ssh-rsa or ssh-dss - * (string). + * - SSH_BIND_OPTIONS_DSAKEY: + * Set the path to the ssh host dsa key, SSHv2 + * only (const char *). * - * SSH_BIND_OPTIONS_DSAKEY: - * Set the path to the dsa ssh host key (string). + * - SSH_BIND_OPTIONS_RSAKEY: + * Set the path to the ssh host rsa key, SSHv2 + * only (const char *). * - * SSH_BIND_OPTIONS_RSAKEY: - * Set the path to the ssh host rsa key (string). + * - SSH_BIND_OPTIONS_ECDSAKEY: + * Set the path to the ssh host ecdsa key, + * SSHv2 only (const char *). * - * SSH_BIND_OPTIONS_BANNER: - * Set the server banner sent to clients (string). + * - SSH_BIND_OPTIONS_BANNER: + * Set the server banner sent to clients (const char *). * * @param value The value to set. This is a generic pointer and the - * datatype which is used should be set according to the - * type set. + * datatype which should be used is described at the + * corresponding value of type above. * - * @return 0 on success, < 0 on error. + * @return 0 on success, < 0 on error, invalid option, or parameter. */ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, const void *value) { char *p, *q; - int i; + int i, rc; if (sshbind == NULL) { return -1; @@ -1321,8 +1402,57 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, ssh_set_error_invalid(sshbind); return -1; } else { - if (ssh_bind_options_set_algo(sshbind, SSH_HOSTKEYS, value) < 0) - return -1; + int key_type; + ssh_key key; + ssh_key *bind_key_loc = NULL; + char **bind_key_path_loc; + + rc = ssh_pki_import_privkey_file(value, NULL, NULL, NULL, &key); + if (rc != SSH_OK) { + return -1; + } + key_type = ssh_key_type(key); + switch (key_type) { + case SSH_KEYTYPE_DSS: + bind_key_loc = &sshbind->dsa; + bind_key_path_loc = &sshbind->dsakey; + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_ECC + bind_key_loc = &sshbind->ecdsa; + bind_key_path_loc = &sshbind->ecdsakey; +#else + ssh_set_error(sshbind, + SSH_FATAL, + "ECDSA key used and libssh compiled " + "without ECDSA support"); +#endif + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + bind_key_loc = &sshbind->rsa; + bind_key_path_loc = &sshbind->rsakey; + break; + default: + ssh_set_error(sshbind, + SSH_FATAL, + "Unsupported key type %d", key_type); + } + + if (bind_key_loc == NULL) { + ssh_key_free(key); + return -1; + } + + /* Set the location of the key on disk even though we don't + need it in case some other function wants it */ + rc = ssh_bind_set_key(sshbind, bind_key_path_loc, value); + if (rc < 0) { + ssh_key_free(key); + return -1; + } + ssh_key_free(*bind_key_loc); + *bind_key_loc = key; } break; case SSH_BIND_OPTIONS_BINDADDR: @@ -1376,7 +1506,7 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, break; case SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: if (value == NULL) { - sshbind->common.log_verbosity = 0; + ssh_set_log_level(0); } else { q = strdup(value); if (q == NULL) { @@ -1389,35 +1519,27 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, } SAFE_FREE(q); - sshbind->common.log_verbosity = i & 0xffff; + ssh_set_log_level(i & 0xffff); } break; case SSH_BIND_OPTIONS_DSAKEY: - if (value == NULL) { - ssh_set_error_invalid(sshbind); - return -1; - } else { - SAFE_FREE(sshbind->dsakey); - sshbind->dsakey = strdup(value); - if (sshbind->dsakey == NULL) { - ssh_set_error_oom(sshbind); - return -1; + rc = ssh_bind_set_key(sshbind, &sshbind->dsakey, value); + if (rc < 0) { + return -1; } - } - break; + break; case SSH_BIND_OPTIONS_RSAKEY: - if (value == NULL) { - ssh_set_error_invalid(sshbind); - return -1; - } else { - SAFE_FREE(sshbind->rsakey); - sshbind->rsakey = strdup(value); - if (sshbind->rsakey == NULL) { - ssh_set_error_oom(sshbind); - return -1; + rc = ssh_bind_set_key(sshbind, &sshbind->rsakey, value); + if (rc < 0) { + return -1; } - } - break; + break; + case SSH_BIND_OPTIONS_ECDSAKEY: + rc = ssh_bind_set_key(sshbind, &sshbind->ecdsakey, value); + if (rc < 0) { + return -1; + } + break; case SSH_BIND_OPTIONS_BANNER: if (value == NULL) { ssh_set_error_invalid(sshbind); diff --git a/libssh/src/packet.c b/libssh/src/packet.c index 3302847a..d16cd165 100644 --- a/libssh/src/packet.c +++ b/libssh/src/packet.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2003-2008 by Aris Adamantiadis + * Copyright (c) 2003-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -48,8 +48,6 @@ #include "libssh/auth.h" #include "libssh/gssapi.h" -#define MACSIZE SHA_DIGEST_LEN - static ssh_packet_callback default_packet_handlers[]= { ssh_packet_disconnect_callback, // SSH2_MSG_DISCONNECT 1 ssh_packet_ignore_callback, // SSH2_MSG_IGNORE 2 @@ -141,174 +139,225 @@ static ssh_packet_callback default_packet_handlers[]= { * @len length of data received. It might not be enough for a complete packet * @returns number of bytes read and processed. */ -int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user){ - ssh_session session=(ssh_session) user; - unsigned int blocksize = (session->current_crypto ? - session->current_crypto->in_cipher->blocksize : 8); - int current_macsize = session->current_crypto ? MACSIZE : 0; - unsigned char mac[30] = {0}; - char buffer[16] = {0}; - const void *packet = NULL; - int to_be_read; - int rc; - uint32_t len, compsize, payloadsize; - uint8_t padding; - size_t processed=0; /* number of byte processed from the callback */ +int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) +{ + ssh_session session= (ssh_session) user; + unsigned int blocksize = (session->current_crypto ? + session->current_crypto->in_cipher->blocksize : 8); + unsigned char mac[DIGEST_MAX_LEN] = {0}; + char buffer[16] = {0}; + size_t current_macsize = 0; + const uint8_t *packet; + int to_be_read; + int rc; + uint32_t len, compsize, payloadsize; + uint8_t padding; + size_t processed = 0; /* number of byte processed from the callback */ - if (data == NULL) { - goto error; - } + if(session->current_crypto != NULL) { + current_macsize = hmac_digest_len(session->current_crypto->in_hmac); + } - if (session->session_state == SSH_SESSION_STATE_ERROR) - goto error; - switch(session->packet_state) { - case PACKET_STATE_INIT: - if(receivedlen < blocksize){ - /* We didn't receive enough data to read at least one block size, give up */ - return 0; - } - memset(&session->in_packet, 0, sizeof(PACKET)); - - if (session->in_buffer) { - if (buffer_reinit(session->in_buffer) < 0) { - goto error; - } - } else { - session->in_buffer = ssh_buffer_new(); - if (session->in_buffer == NULL) { - goto error; - } - } - - memcpy(buffer,data,blocksize); - processed += blocksize; - len = packet_decrypt_len(session, buffer); - - if (buffer_add_data(session->in_buffer, buffer, blocksize) < 0) { + if (data == NULL) { goto error; - } + } - if(len > MAX_PACKET_LEN) { - ssh_set_error(session, SSH_FATAL, - "read_packet(): Packet len too high(%u %.4x)", len, len); + if (session->session_state == SSH_SESSION_STATE_ERROR) { goto error; - } + } - to_be_read = len - blocksize + sizeof(uint32_t); - if (to_be_read < 0) { - /* remote sshd sends invalid sizes? */ - ssh_set_error(session, SSH_FATAL, - "given numbers of bytes left to be read < 0 (%d)!", to_be_read); - goto error; - } + switch(session->packet_state) { + case PACKET_STATE_INIT: + if (receivedlen < blocksize) { + /* + * We didn't receive enough data to read at least one + * block size, give up + */ + return 0; + } - /* saves the status of the current operations */ - session->in_packet.len = len; - session->packet_state = PACKET_STATE_SIZEREAD; - /* FALL TROUGH */ - case PACKET_STATE_SIZEREAD: - len = session->in_packet.len; - to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize; - /* if to_be_read is zero, the whole packet was blocksize bytes. */ - if (to_be_read != 0) { - if(receivedlen - processed < (unsigned int)to_be_read){ - /* give up, not enough data in buffer */ - SSH_LOG(SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len); - return processed; - } + memset(&session->in_packet, 0, sizeof(PACKET)); - packet = ((unsigned char *)data) + processed; -// ssh_socket_read(session->socket,packet,to_be_read-current_macsize); + if (session->in_buffer) { + rc = ssh_buffer_reinit(session->in_buffer); + if (rc < 0) { + goto error; + } + } else { + session->in_buffer = ssh_buffer_new(); + if (session->in_buffer == NULL) { + goto error; + } + } - if (buffer_add_data(session->in_buffer, packet, - to_be_read - current_macsize) < 0) { - goto error; - } - processed += to_be_read - current_macsize; - } + memcpy(buffer, data, blocksize); + processed += blocksize; + len = packet_decrypt_len(session, buffer); - if (session->current_crypto) { - /* - * decrypt the rest of the packet (blocksize bytes already - * have been decrypted) - */ - if (packet_decrypt(session, - ((uint8_t*)buffer_get_rest(session->in_buffer) + blocksize), - buffer_get_rest_len(session->in_buffer) - blocksize) < 0) { - ssh_set_error(session, SSH_FATAL, "Decrypt error"); - goto error; - } - /* copy the last part from the incoming buffer */ - memcpy(mac,(unsigned char *)packet + to_be_read - current_macsize, MACSIZE); + rc = ssh_buffer_add_data(session->in_buffer, buffer, blocksize); + if (rc < 0) { + goto error; + } - if (packet_hmac_verify(session, session->in_buffer, mac) < 0) { - ssh_set_error(session, SSH_FATAL, "HMAC error"); - goto error; - } - processed += current_macsize; - } + if (len > MAX_PACKET_LEN) { + ssh_set_error(session, + SSH_FATAL, + "read_packet(): Packet len too high(%u %.4x)", + len, len); + goto error; + } - /* skip the size field which has been processed before */ - buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); + to_be_read = len - blocksize + sizeof(uint32_t); + if (to_be_read < 0) { + /* remote sshd sends invalid sizes? */ + ssh_set_error(session, + SSH_FATAL, + "Given numbers of bytes left to be read < 0 (%d)!", + to_be_read); + goto error; + } - if (buffer_get_u8(session->in_buffer, &padding) == 0) { - ssh_set_error(session, SSH_FATAL, "Packet too short to read padding"); - goto error; - } + /* Saves the status of the current operations */ + session->in_packet.len = len; + session->packet_state = PACKET_STATE_SIZEREAD; + /* FALL TROUGH */ + case PACKET_STATE_SIZEREAD: + len = session->in_packet.len; + to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize; + /* if to_be_read is zero, the whole packet was blocksize bytes. */ + if (to_be_read != 0) { + if (receivedlen - processed < (unsigned int)to_be_read) { + /* give up, not enough data in buffer */ + SSH_LOG(SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len); + return processed; + } - if (padding > buffer_get_rest_len(session->in_buffer)) { - ssh_set_error(session, SSH_FATAL, - "Invalid padding: %d (%d left)", - padding, - buffer_get_rest_len(session->in_buffer)); - goto error; - } - buffer_pass_bytes_end(session->in_buffer, padding); - compsize = buffer_get_rest_len(session->in_buffer); + packet = ((uint8_t*)data) + processed; +#if 0 + ssh_socket_read(session->socket, + packet, + to_be_read - current_macsize); +#endif + + rc = ssh_buffer_add_data(session->in_buffer, + packet, + to_be_read - current_macsize); + if (rc < 0) { + goto error; + } + processed += to_be_read - current_macsize; + } + + if (session->current_crypto) { + /* + * Decrypt the rest of the packet (blocksize bytes already + * have been decrypted) + */ + uint32_t buffer_len = buffer_get_rest_len(session->in_buffer); + + /* The following check avoids decrypting zero bytes */ + if (buffer_len > blocksize) { + uint8_t *payload = ((uint8_t*)buffer_get_rest(session->in_buffer) + blocksize); + uint32_t plen = buffer_len - blocksize; + + rc = packet_decrypt(session, payload, plen); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "Decrypt error"); + goto error; + } + } + + /* copy the last part from the incoming buffer */ + packet = ((uint8_t *)data) + processed; + memcpy(mac, packet, current_macsize); + + rc = packet_hmac_verify(session, session->in_buffer, mac, session->current_crypto->in_hmac); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "HMAC error"); + goto error; + } + processed += current_macsize; + } + + /* skip the size field which has been processed before */ + buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); + + rc = buffer_get_u8(session->in_buffer, &padding); + if (rc == 0) { + ssh_set_error(session, + SSH_FATAL, + "Packet too short to read padding"); + goto error; + } + + if (padding > buffer_get_rest_len(session->in_buffer)) { + ssh_set_error(session, + SSH_FATAL, + "Invalid padding: %d (%d left)", + padding, + buffer_get_rest_len(session->in_buffer)); + goto error; + } + buffer_pass_bytes_end(session->in_buffer, padding); + compsize = buffer_get_rest_len(session->in_buffer); #ifdef WITH_ZLIB - if (session->current_crypto - && session->current_crypto->do_compress_in - && buffer_get_rest_len(session->in_buffer)) { - if (decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN) < 0) { - goto error; - } - } + if (session->current_crypto + && session->current_crypto->do_compress_in + && buffer_get_rest_len(session->in_buffer) > 0) { + rc = decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN); + if (rc < 0) { + goto error; + } + } #endif /* WITH_ZLIB */ - payloadsize=buffer_get_rest_len(session->in_buffer); - session->recv_seq++; - /* We don't want to rewrite a new packet while still executing the packet callbacks */ - session->packet_state = PACKET_STATE_PROCESSING; - ssh_packet_parse_type(session); - SSH_LOG(SSH_LOG_PACKET, - "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", - session->in_packet.type, len, padding, compsize, payloadsize); - /* execute callbacks */ - ssh_packet_process(session, session->in_packet.type); - session->packet_state = PACKET_STATE_INIT; - if(processed < receivedlen){ - /* Handle a potential packet left in socket buffer */ - SSH_LOG(SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", - receivedlen-processed); - rc = ssh_packet_socket_callback(((unsigned char *)data) + processed, - receivedlen - processed,user); - processed += rc; - } + payloadsize = buffer_get_rest_len(session->in_buffer); + session->recv_seq++; + if (session->raw_counter != NULL) { + session->raw_counter->in_bytes += payloadsize; + session->raw_counter->in_packets++; + } - return processed; - case PACKET_STATE_PROCESSING: - SSH_LOG(SSH_LOG_RARE, "Nested packet processing. Delaying."); - return 0; - } + /* + * We don't want to rewrite a new packet while still executing the + * packet callbacks + */ + session->packet_state = PACKET_STATE_PROCESSING; + ssh_packet_parse_type(session); + SSH_LOG(SSH_LOG_PACKET, + "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", + session->in_packet.type, len, padding, compsize, payloadsize); - ssh_set_error(session, SSH_FATAL, - "Invalid state into packet_read2(): %d", - session->packet_state); + /* Execute callbacks */ + ssh_packet_process(session, session->in_packet.type); + session->packet_state = PACKET_STATE_INIT; + if (processed < receivedlen) { + /* Handle a potential packet left in socket buffer */ + SSH_LOG(SSH_LOG_PACKET, + "Processing %" PRIdS " bytes left in socket buffer", + receivedlen-processed); + + packet = ((uint8_t*)data) + processed; + + rc = ssh_packet_socket_callback(packet, receivedlen - processed,user); + processed += rc; + } + + return processed; + case PACKET_STATE_PROCESSING: + SSH_LOG(SSH_LOG_PACKET, "Nested packet processing. Delaying."); + return 0; + } + + ssh_set_error(session, + SSH_FATAL, + "Invalid state into packet_read2(): %d", + session->packet_state); error: - session->session_state= SSH_SESSION_STATE_ERROR; + session->session_state= SSH_SESSION_STATE_ERROR; - return processed; + return processed; } void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){ @@ -393,34 +442,42 @@ void ssh_packet_process(ssh_session session, uint8_t type){ * @return SSH_ERROR on error, else SSH_OK */ int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum){ - int r; + int rc; - r = buffer_add_u8(session->out_buffer, SSH2_MSG_UNIMPLEMENTED); - if (r < 0) { - return SSH_ERROR; - } - r = buffer_add_u32(session->out_buffer, htonl(seqnum)); - if (r < 0) { - return SSH_ERROR; - } - r = packet_send(session); + rc = ssh_buffer_pack(session->out_buffer, + "bd", + SSH2_MSG_UNIMPLEMENTED, + seqnum); + if (rc != SSH_OK) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + rc = packet_send(session); - return r; + return rc; } /** @internal * @brief handles a SSH_MSG_UNIMPLEMENTED packet */ SSH_PACKET_CALLBACK(ssh_packet_unimplemented){ - uint32_t seq; + uint32_t seq; + int rc; + (void)session; /* unused */ - (void)type; - (void)user; - buffer_get_u32(packet,&seq); - seq=ntohl(seq); - SSH_LOG(SSH_LOG_RARE, - "Received SSH_MSG_UNIMPLEMENTED (sequence number %d)",seq); - return SSH_PACKET_USED; + (void)type; + (void)user; + + rc = ssh_buffer_unpack(packet, "d", &seq); + if (rc != SSH_OK) { + SSH_LOG(SSH_LOG_WARNING, + "Could not unpack SSH_MSG_UNIMPLEMENTED packet"); + } + + SSH_LOG(SSH_LOG_RARE, + "Received SSH_MSG_UNIMPLEMENTED (sequence number %d)",seq); + + return SSH_PACKET_USED; } /** @internal @@ -459,13 +516,17 @@ static int ssh_packet_write(ssh_session session) { static int packet_send2(ssh_session session) { unsigned int blocksize = (session->current_crypto ? session->current_crypto->out_cipher->blocksize : 8); + enum ssh_hmac_e hmac_type = (session->current_crypto ? + session->current_crypto->out_hmac : session->next_crypto->out_hmac); uint32_t currentlen = buffer_get_rest_len(session->out_buffer); unsigned char *hmac = NULL; - char padstring[32] = {0}; + char padstring[32] = { 0 }; int rc = SSH_ERROR; uint32_t finallen,payloadsize,compsize; uint8_t padding; + uint8_t header[sizeof(padding) + sizeof(finallen)] = { 0 }; + payloadsize = currentlen; #ifdef WITH_ZLIB if (session->current_crypto @@ -485,19 +546,18 @@ static int packet_send2(ssh_session session) { if (session->current_crypto) { ssh_get_random(padstring, padding, 0); - } else { - memset(padstring,0,padding); } finallen = htonl(currentlen + padding + 1); - if (buffer_prepend_data(session->out_buffer, &padding, sizeof(uint8_t)) < 0) { + memcpy(&header[0], &finallen, sizeof(finallen)); + header[sizeof(finallen)] = padding; + rc = buffer_prepend_data(session->out_buffer, &header, sizeof(header)); + if (rc < 0) { goto error; } - if (buffer_prepend_data(session->out_buffer, &finallen, sizeof(uint32_t)) < 0) { - goto error; - } - if (buffer_add_data(session->out_buffer, padstring, padding) < 0) { + rc = ssh_buffer_add_data(session->out_buffer, padstring, padding); + if (rc < 0) { goto error; } #ifdef WITH_PCAP @@ -510,18 +570,23 @@ static int packet_send2(ssh_session session) { hmac = packet_encrypt(session, buffer_get_rest(session->out_buffer), buffer_get_rest_len(session->out_buffer)); if (hmac) { - if (buffer_add_data(session->out_buffer, hmac, 20) < 0) { + rc = ssh_buffer_add_data(session->out_buffer, hmac, hmac_digest_len(hmac_type)); + if (rc < 0) { goto error; } } rc = ssh_packet_write(session); session->send_seq++; + if (session->raw_counter != NULL) { + session->raw_counter->out_bytes += payloadsize; + session->raw_counter->out_packets++; + } SSH_LOG(SSH_LOG_PACKET, "packet: wrote [len=%d,padding=%hhd,comp=%d,payload=%d]", ntohl(finallen), padding, compsize, payloadsize); - if (buffer_reinit(session->out_buffer) < 0) { + if (ssh_buffer_reinit(session->out_buffer) < 0) { rc = SSH_ERROR; } error: diff --git a/libssh/src/packet1.c b/libssh/src/packet1.c index 7ac8318d..eac70084 100644 --- a/libssh/src/packet1.c +++ b/libssh/src/packet1.c @@ -106,7 +106,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user size_t processed=0; uint32_t padding; uint32_t crc; - uint32_t len; + uint32_t len, buffer_len; ssh_session session=(ssh_session)user; switch (session->packet_state){ @@ -114,7 +114,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user memset(&session->in_packet, 0, sizeof(PACKET)); if (session->in_buffer) { - if (buffer_reinit(session->in_buffer) < 0) { + if (ssh_buffer_reinit(session->in_buffer) < 0) { goto error; } } else { @@ -155,7 +155,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user /* it is _not_ possible that to_be_read be < 8. */ packet = (char *)data + processed; - if (buffer_add_data(session->in_buffer,packet,to_be_read) < 0) { + if (ssh_buffer_add_data(session->in_buffer,packet,to_be_read) < 0) { goto error; } processed += to_be_read; @@ -168,11 +168,16 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user * We decrypt everything, missing the lenght part (which was * previously read, unencrypted, and is not part of the buffer */ - if (packet_decrypt(session, - ssh_buffer_get_begin(session->in_buffer), - ssh_buffer_get_len(session->in_buffer)) < 0) { - ssh_set_error(session, SSH_FATAL, "Packet decrypt error"); - goto error; + buffer_len = ssh_buffer_get_len(session->in_buffer); + if (buffer_len > 0) { + int rc; + rc = packet_decrypt(session, + ssh_buffer_get_begin(session->in_buffer), + buffer_len); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "Packet decrypt error"); + goto error; + } } } #ifdef DEBUG_CRYPTO @@ -236,7 +241,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user return processed; case PACKET_STATE_PROCESSING: - SSH_LOG(SSH_LOG_RARE, "Nested packet processing. Delaying."); + SSH_LOG(SSH_LOG_PACKET, "Nested packet processing. Delaying."); return 0; } @@ -300,6 +305,8 @@ int packet_send1(ssh_session session) { ssh_buffer_get_len(session->out_buffer)); #endif + /* session->out_buffer should have more than sizeof(uint32_t) bytes + in it as required for packet_encrypt */ packet_encrypt(session, (unsigned char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t), ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t)); @@ -315,7 +322,7 @@ int packet_send1(ssh_session session) { session->send_seq++; - if (buffer_reinit(session->out_buffer) < 0) { + if (ssh_buffer_reinit(session->out_buffer) < 0) { rc = SSH_ERROR; } error: diff --git a/libssh/src/packet_cb.c b/libssh/src/packet_cb.c index f5d4f055..a10dd1ab 100644 --- a/libssh/src/packet_cb.c +++ b/libssh/src/packet_cb.c @@ -43,29 +43,35 @@ * @brief Handle a SSH_DISCONNECT packet. */ SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){ - uint32_t code; - char *error=NULL; - ssh_string error_s; - (void)user; - (void)type; - buffer_get_u32(packet, &code); + int rc; + uint32_t code = 0; + char *error = NULL; + ssh_string error_s; + (void)user; + (void)type; + + rc = buffer_get_u32(packet, &code); + if (rc != 0) { + code = ntohl(code); + } + error_s = buffer_get_ssh_string(packet); if (error_s != NULL) { error = ssh_string_to_char(error_s); ssh_string_free(error_s); } - SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s",code, - error != NULL ? error : "no error"); + SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s", + code, error != NULL ? error : "no error"); ssh_set_error(session, SSH_FATAL, - "Received SSH_MSG_DISCONNECT: %d:%s",code, - error != NULL ? error : "no error"); + "Received SSH_MSG_DISCONNECT: %d:%s", + code, error != NULL ? error : "no error"); SAFE_FREE(error); ssh_socket_close(session->socket); session->alive = 0; - session->session_state= SSH_SESSION_STATE_ERROR; - /* TODO: handle a graceful disconnect */ - return SSH_PACKET_USED; + session->session_state = SSH_SESSION_STATE_ERROR; + /* TODO: handle a graceful disconnect */ + return SSH_PACKET_USED; } /** diff --git a/libssh/src/packet_crypt.c b/libssh/src/packet_crypt.c index 50b81893..914727e0 100644 --- a/libssh/src/packet_crypt.c +++ b/libssh/src/packet_crypt.c @@ -22,6 +22,7 @@ */ #include "config.h" +#include #include #include #include @@ -59,6 +60,9 @@ uint32_t packet_decrypt_len(ssh_session session, char *crypted){ int packet_decrypt(ssh_session session, void *data,uint32_t len) { struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher; char *out = NULL; + + assert(len); + if(len % session->current_crypto->in_cipher->blocksize != 0){ ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); return SSH_ERROR; @@ -73,11 +77,10 @@ int packet_decrypt(ssh_session session, void *data,uint32_t len) { SAFE_FREE(out); return -1; } - crypto->cbc_decrypt(crypto,data,out,len); + crypto->decrypt(crypto,data,out,len); memcpy(data,out,len); - memset(out,0,len); - + BURN_BUFFER(out, len); SAFE_FREE(out); return 0; } @@ -88,6 +91,9 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { char *out = NULL; unsigned int finallen; uint32_t seq; + enum ssh_hmac_e type; + + assert(len); if (!session->current_crypto) { return NULL; /* nothing to do here */ @@ -101,6 +107,7 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { return NULL; } + type = session->current_crypto->out_hmac; seq = ntohl(session->send_seq); crypto = session->current_crypto->out_cipher; @@ -111,7 +118,7 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { } if (session->version == 2) { - ctx = hmac_init(session->current_crypto->encryptMAC,20,SSH_HMAC_SHA1); + ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { SAFE_FREE(out); return NULL; @@ -120,18 +127,18 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { hmac_update(ctx,data,len); hmac_final(ctx,session->current_crypto->hmacbuf,&finallen); #ifdef DEBUG_CRYPTO - ssh_print_hexa("mac: ",data,len); - if (finallen != 20) { + ssh_print_hexa("mac: ",data,hmac_digest_len(type)); + if (finallen != hmac_digest_len(type)) { printf("Final len is %d\n",finallen); } - ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, 20); + ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, hmac_digest_len(type)); #endif } - crypto->cbc_encrypt(crypto, data, out, len); + crypto->encrypt(crypto, data, out, len); memcpy(data, out, len); - memset(out, 0, len); + BURN_BUFFER(out, len); SAFE_FREE(out); if (session->version == 2) { @@ -154,13 +161,13 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { * occurred. */ int packet_hmac_verify(ssh_session session, ssh_buffer buffer, - unsigned char *mac) { - unsigned char hmacbuf[EVP_MAX_MD_SIZE] = {0}; + unsigned char *mac, enum ssh_hmac_e type) { + unsigned char hmacbuf[DIGEST_MAX_LEN] = {0}; HMACCTX ctx; unsigned int len; uint32_t seq; - ctx = hmac_init(session->current_crypto->decryptMAC, 20, SSH_HMAC_SHA1); + ctx = hmac_init(session->current_crypto->decryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { return -1; } diff --git a/libssh/src/pcap.c b/libssh/src/pcap.c index 6e688962..134bdf10 100644 --- a/libssh/src/pcap.c +++ b/libssh/src/pcap.c @@ -342,7 +342,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx){ int ssh_pcap_context_write(ssh_pcap_context ctx,enum ssh_pcap_direction direction , void *data, uint32_t len, uint32_t origlen){ ssh_buffer ip; - int err; + int rc; if(ctx==NULL || ctx->file ==NULL) return SSH_ERROR; if(ctx->connected==0) @@ -353,147 +353,104 @@ int ssh_pcap_context_write(ssh_pcap_context ctx,enum ssh_pcap_direction directio ssh_set_error_oom(ctx->session); return SSH_ERROR; } - /* build an IP packet */ - /* V4, 20 bytes */ - err = buffer_add_u8(ip,4 << 4 | 5); - if (err < 0) { - goto error; - } - /* tos */ - err = buffer_add_u8(ip,0); - if (err < 0) { - goto error; - } - /* total len */ - err = buffer_add_u16(ip,htons(origlen + TCPIPHDR_LEN)); - if (err < 0) { - goto error; - } - /* IP id number */ - err = buffer_add_u16(ip,htons(ctx->file->ipsequence)); - if (err < 0) { - goto error; - } + + /* build an IP packet */ + rc = ssh_buffer_pack(ip, + "bbwwwbbw", + 4 << 4 | 5, /* V4, 20 bytes */ + 0, /* tos */ + origlen + TCPIPHDR_LEN, /* total len */ + ctx->file->ipsequence, /* IP id number */ + 0, /* fragment offset */ + 64, /* TTL */ + 6, /* protocol TCP=6 */ + 0); /* checksum */ + ctx->file->ipsequence++; - /* fragment offset */ - err = buffer_add_u16(ip,htons(0)); - if (err < 0) { - goto error; - } - /* TTL */ - err = buffer_add_u8(ip,64); - if (err < 0) { - goto error; - } - /* protocol TCP=6 */ - err = buffer_add_u8(ip,6); - if (err < 0) { - goto error; - } - /* checksum */ - err = buffer_add_u16(ip,0); - if (err < 0) { - goto error; - } + if (rc != SSH_OK){ + goto error; + } if(direction==SSH_PCAP_DIR_OUT){ - err = buffer_add_u32(ip,ctx->ipsource); - if (err < 0) { + rc = buffer_add_u32(ip,ctx->ipsource); + if (rc < 0) { goto error; } - err = buffer_add_u32(ip,ctx->ipdest); - if (err < 0) { + rc = buffer_add_u32(ip,ctx->ipdest); + if (rc < 0) { goto error; } } else { - err = buffer_add_u32(ip,ctx->ipdest); - if (err < 0) { + rc = buffer_add_u32(ip,ctx->ipdest); + if (rc < 0) { goto error; } - err = buffer_add_u32(ip,ctx->ipsource); - if (err < 0) { + rc = buffer_add_u32(ip,ctx->ipsource); + if (rc < 0) { goto error; } } /* TCP */ if(direction==SSH_PCAP_DIR_OUT){ - err = buffer_add_u16(ip,ctx->portsource); - if (err < 0) { + rc = buffer_add_u16(ip,ctx->portsource); + if (rc < 0) { goto error; } - err = buffer_add_u16(ip,ctx->portdest); - if (err < 0) { + rc = buffer_add_u16(ip,ctx->portdest); + if (rc < 0) { goto error; } } else { - err = buffer_add_u16(ip,ctx->portdest); - if (err < 0) { + rc = buffer_add_u16(ip,ctx->portdest); + if (rc < 0) { goto error; } - err = buffer_add_u16(ip,ctx->portsource); - if (err < 0) { + rc = buffer_add_u16(ip,ctx->portsource); + if (rc < 0) { goto error; } } /* sequence number */ if(direction==SSH_PCAP_DIR_OUT){ - err = buffer_add_u32(ip,ntohl(ctx->outsequence)); - if (err < 0) { + rc = ssh_buffer_pack(ip, "d", ctx->outsequence); + if (rc != SSH_OK) { goto error; } ctx->outsequence+=origlen; } else { - err = buffer_add_u32(ip,ntohl(ctx->insequence)); - if (err < 0) { + rc = ssh_buffer_pack(ip, "d", ctx->insequence); + if (rc != SSH_OK) { goto error; } ctx->insequence+=origlen; } /* ack number */ if(direction==SSH_PCAP_DIR_OUT){ - err = buffer_add_u32(ip,ntohl(ctx->insequence)); - if (err < 0) { + rc = ssh_buffer_pack(ip, "d", ctx->insequence); + if (rc != SSH_OK) { goto error; } } else { - err = buffer_add_u32(ip,ntohl(ctx->outsequence)); - if (err < 0) { + rc = ssh_buffer_pack(ip, "d", ctx->outsequence); + if (rc != SSH_OK) { goto error; } } - /* header len = 20 = 5 * 32 bits, at offset 4*/ - err = buffer_add_u8(ip,5 << 4); - if (err < 0) { + + rc = ssh_buffer_pack(ip, + "bbwwwP", + 5 << 4, /* header len = 20 = 5 * 32 bits, at offset 4*/ + TH_PUSH | TH_ACK, /* flags */ + 65535, /* window */ + 0, /* checksum */ + 0, /* urgent data ptr */ + (size_t)len, data); /* actual data */ + if (rc != SSH_OK) { goto error; } - /* flags */ - err = buffer_add_u8(ip,TH_PUSH | TH_ACK); - if (err < 0) { - goto error; - } - /* window */ - err = buffer_add_u16(ip,htons(65535)); - if (err < 0) { - goto error; - } - /* checksum */ - err = buffer_add_u16(ip,htons(0)); - if (err < 0) { - goto error; - } - /* urgent data ptr */ - err = buffer_add_u16(ip,0); - if (err < 0) { - goto error; - } - /* actual data */ - err = buffer_add_data(ip,data,len); - if (err < 0) { - goto error; - } - err=ssh_pcap_file_write_packet(ctx->file,ip,origlen + TCPIPHDR_LEN); + rc=ssh_pcap_file_write_packet(ctx->file,ip,origlen + TCPIPHDR_LEN); error: ssh_buffer_free(ip); - return err; + return rc; } /** @brief sets the pcap file used to trace the session diff --git a/libssh/src/pki.c b/libssh/src/pki.c index ec5a6883..af472ebb 100644 --- a/libssh/src/pki.c +++ b/libssh/src/pki.c @@ -3,7 +3,7 @@ * This file is part of the SSH Library * * Copyright (c) 2010 by Aris Adamantiadis - * Copyright (c) 2011-2012 Andreas Schneider + * Copyright (c) 2011-2013 Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -98,6 +98,25 @@ enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) { return SSH_KEYTYPE_UNKNOWN; } +/** + * @brief returns the ECDSA key name ("ecdsa-sha2-nistp256" for example) + * + * @param[in] key the ssh_key whose ECDSA name to get + * + * @returns the ECDSA key name ("ecdsa-sha2-nistp256" for example) + * + * @returns "unknown" if the ECDSA key name is not known + */ +const char *ssh_pki_key_ecdsa_name(const ssh_key key) +{ +#ifdef HAVE_OPENSSL_ECC /* FIXME Better ECC check needed */ + return pki_key_ecdsa_nid_to_name(key->ecdsa_nid); +#else + (void) key; /* unused */ + return NULL; +#endif +} + /** * @brief creates a new empty SSH key * @returns an empty ssh_key handle, or NULL on error. @@ -138,6 +157,11 @@ void ssh_key_clean (ssh_key key){ if(key->ecdsa) EC_KEY_free(key->ecdsa); #endif /* HAVE_OPENSSL_ECC */ #endif + if (key->ed25519_privkey != NULL){ + BURN_BUFFER(key->ed25519_privkey, sizeof(ed25519_privkey)); + SAFE_FREE(key->ed25519_privkey); + } + SAFE_FREE(key->ed25519_pubkey); key->flags=SSH_KEY_FLAG_EMPTY; key->type=SSH_KEYTYPE_UNKNOWN; key->ecdsa_nid = 0; @@ -188,6 +212,8 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) { return "ssh-rsa1"; case SSH_KEYTYPE_ECDSA: return "ssh-ecdsa"; + case SSH_KEYTYPE_ED25519: + return "ssh-ed25519"; case SSH_KEYTYPE_UNKNOWN: return NULL; } @@ -226,6 +252,8 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) { || strcmp(name, "ecdsa-sha2-nistp384") == 0 || strcmp(name, "ecdsa-sha2-nistp521") == 0) { return SSH_KEYTYPE_ECDSA; + } else if (strcmp(name, "ssh-ed25519") == 0){ + return SSH_KEYTYPE_ED25519; } return SSH_KEYTYPE_UNKNOWN; @@ -281,7 +309,7 @@ int ssh_key_cmp(const ssh_key k1, } if (k1->type != k2->type) { - ssh_pki_log("key types don't macth!"); + ssh_pki_log("key types don't match!"); return 1; } @@ -292,6 +320,10 @@ int ssh_key_cmp(const ssh_key k1, } } + if (k1->type == SSH_KEYTYPE_ED25519) { + return pki_ed25519_key_cmp(k1, k2, what); + } + return pki_key_compare(k1, k2, what); } @@ -331,6 +363,13 @@ void ssh_signature_free(ssh_signature sig) #endif break; case SSH_KEYTYPE_ECDSA: +#if defined(HAVE_LIBCRYPTO) && defined(HAVE_OPENSSL_ECC) + ECDSA_SIG_free(sig->ecdsa_sig); +#endif + break; + case SSH_KEYTYPE_ED25519: + SAFE_FREE(sig->ed25519_sig); + break; case SSH_KEYTYPE_UNKNOWN: break; } @@ -476,6 +515,65 @@ int ssh_pki_import_privkey_file(const char *filename, return SSH_OK; } +/** + * @brief Export a private key to a pam file on disk. + * + * @param[in] privkey The private key to export. + * + * @param[in] passphrase The passphrase to use to encrypt the key with or + * NULL. An empty string means no passphrase. + * + * @param[in] auth_fn An auth function you may want to use or NULL. + * + * @param[in] auth_data Private data passed to the auth function. + * + * @param[in] filename The path where to store the pem file. + * + * @return SSH_OK on success, SSH_ERROR on error. + */ +int ssh_pki_export_privkey_file(const ssh_key privkey, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data, + const char *filename) +{ + ssh_string blob; + FILE *fp; + int rc; + + if (privkey == NULL || !ssh_key_is_private(privkey)) { + return SSH_ERROR; + } + + fp = fopen(filename, "wb"); + if (fp == NULL) { + SSH_LOG(SSH_LOG_FUNCTIONS, "Error opening %s: %s", + filename, strerror(errno)); + return SSH_EOF; + } + + + blob = pki_private_key_to_pem(privkey, + passphrase, + auth_fn, + auth_data); + if (blob == NULL) { + fclose(fp); + return -1; + } + + rc = fwrite(ssh_string_data(blob), ssh_string_len(blob), 1, fp); + ssh_string_free(blob); + if (rc != 1 || ferror(fp)) { + fclose(fp); + unlink(filename); + return SSH_ERROR; + } + fclose(fp); + + return SSH_OK; +} + /* temporary function to migrate seemlessly to ssh_key */ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) { ssh_public_key pub; @@ -661,10 +759,36 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer, if (rc < 0) { goto fail; } + + /* Update key type */ + key->type_c = ssh_pki_key_ecdsa_name(key); } break; #endif + case SSH_KEYTYPE_ED25519: + { + ssh_string pubkey = buffer_get_ssh_string(buffer); + if (ssh_string_len(pubkey) != ED25519_PK_LEN) { + ssh_pki_log("Invalid public key length"); + ssh_string_burn(pubkey); + ssh_string_free(pubkey); + goto fail; + } + + key->ed25519_pubkey = malloc(ED25519_PK_LEN); + if (key->ed25519_pubkey == NULL) { + ssh_string_burn(pubkey); + ssh_string_free(pubkey); + goto fail; + } + + memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_PK_LEN); + ssh_string_burn(pubkey); + ssh_string_free(pubkey); + } + break; case SSH_KEYTYPE_UNKNOWN: + default: ssh_pki_log("Unknown public key protocol %d", type); goto fail; } @@ -752,7 +876,7 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob, return SSH_ERROR; } - rc = buffer_add_data(buffer, ssh_string_data(key_blob), + rc = ssh_buffer_add_data(buffer, ssh_string_data(key_blob), ssh_string_len(key_blob)); if (rc < 0) { ssh_pki_log("Out of memory!"); @@ -917,10 +1041,20 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter, case SSH_KEYTYPE_ECDSA: #ifdef HAVE_ECC rc = pki_key_generate_ecdsa(key, parameter); - if(rc == SSH_ERROR) + if (rc == SSH_ERROR) { goto error; + } + + /* Update key type */ + key->type_c = ssh_pki_key_ecdsa_name(key); break; #endif + case SSH_KEYTYPE_ED25519: + rc = pki_key_generate_ed25519(key); + if (rc == SSH_ERROR) { + goto error; + } + break; case SSH_KEYTYPE_UNKNOWN: goto error; } @@ -999,11 +1133,11 @@ int ssh_pki_export_pubkey_blob(const ssh_key key, } /** - * @brief Convert a public key to a base64 hased key. + * @brief Convert a public key to a base64 encoded key. * * @param[in] key The key to hash * - * @param[out] b64_key A pointer to store the allocated base64 hashed key. You + * @param[out] b64_key A pointer to store the allocated base64 encoded key. You * need to free the buffer. * * @return SSH_OK on success, SSH_ERROR on error. @@ -1177,9 +1311,9 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob, return SSH_ERROR; } - rc = buffer_add_data(buf, - ssh_string_data(sig_blob), - ssh_string_len(sig_blob)); + rc = ssh_buffer_add_data(buf, + ssh_string_data(sig_blob), + ssh_string_len(sig_blob)); if (rc < 0) { ssh_buffer_free(buf); return SSH_ERROR; @@ -1236,12 +1370,19 @@ int ssh_pki_signature_verify_blob(ssh_session session, evp(key->ecdsa_nid, digest, dlen, ehash, &elen); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Hash to be verified with ecdsa", + ehash, elen); +#endif + rc = pki_signature_verify(session, sig, key, ehash, elen); #endif + } else if (key->type == SSH_KEYTYPE_ED25519) { + rc = pki_signature_verify(session, sig, key, digest, dlen); } else { unsigned char hash[SHA_DIGEST_LEN] = {0}; @@ -1302,8 +1443,36 @@ ssh_string ssh_pki_do_sign(ssh_session session, evp_update(ctx, buffer_get_rest(sigbuf), buffer_get_rest_len(sigbuf)); evp_final(ctx, ehash, &elen); +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Hash being signed", ehash, elen); +#endif + sig = pki_do_sign(privkey, ehash, elen); #endif + } else if (privkey->type == SSH_KEYTYPE_ED25519){ + ssh_buffer buf; + + buf = ssh_buffer_new(); + if (buf == NULL) { + ssh_string_free(session_id); + return NULL; + } + + ssh_buffer_set_secure(buf); + rc = ssh_buffer_pack(buf, + "SP", + session_id, + buffer_get_rest_len(sigbuf), buffer_get_rest(sigbuf)); + if (rc != SSH_OK) { + ssh_string_free(session_id); + ssh_buffer_free(buf); + return NULL; + } + + sig = pki_do_sign(privkey, + ssh_buffer_get_begin(buf), + ssh_buffer_get_len(buf)); + ssh_buffer_free(buf); } else { unsigned char hash[SHA_DIGEST_LEN] = {0}; SHACTX ctx; @@ -1395,10 +1564,8 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, const ssh_key privkey) { struct ssh_crypto_struct *crypto; - unsigned char hash[SHA_DIGEST_LEN] = {0}; ssh_signature sig; ssh_string sig_blob; - SHACTX ctx; int rc; if (session == NULL || privkey == NULL || !ssh_key_is_private(privkey)) { @@ -1407,24 +1574,64 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, crypto = session->next_crypto ? session->next_crypto : session->current_crypto; - ctx = sha1_init(); - if (ctx == NULL) { - return NULL; - } if (crypto->secret_hash == NULL){ ssh_set_error(session,SSH_FATAL,"Missing secret_hash"); return NULL; } - sha1_update(ctx, crypto->secret_hash, crypto->digest_len); - sha1_final(hash, ctx); + + if (privkey->type == SSH_KEYTYPE_ECDSA) { +#ifdef HAVE_ECC + unsigned char ehash[EVP_DIGEST_LEN] = {0}; + uint32_t elen; + + evp(privkey->ecdsa_nid, crypto->secret_hash, crypto->digest_len, + ehash, &elen); #ifdef DEBUG_CRYPTO - ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN); + ssh_print_hexa("Hash being signed", ehash, elen); #endif - sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN); - if (sig == NULL) { - return NULL; + sig = pki_do_sign_sessionid(privkey, ehash, elen); + if (sig == NULL) { + return NULL; + } +#endif + } else if (privkey->type == SSH_KEYTYPE_ED25519) { + sig = ssh_signature_new(); + if (sig == NULL){ + return NULL; + } + + sig->type = privkey->type; + sig->type_c = privkey->type_c; + + rc = pki_ed25519_sign(privkey, + sig, + crypto->secret_hash, + crypto->digest_len); + if (rc != SSH_OK){ + ssh_signature_free(sig); + sig = NULL; + } + } else { + unsigned char hash[SHA_DIGEST_LEN] = {0}; + SHACTX ctx; + + ctx = sha1_init(); + if (ctx == NULL) { + return NULL; + } + sha1_update(ctx, crypto->secret_hash, crypto->digest_len); + sha1_final(hash, ctx); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN); +#endif + + sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN); + if (sig == NULL) { + return NULL; + } } rc = ssh_pki_export_signature_blob(sig, &sig_blob); diff --git a/libssh/src/pki_crypto.c b/libssh/src/pki_crypto.c index e87d7ace..5706fdf0 100644 --- a/libssh/src/pki_crypto.c +++ b/libssh/src/pki_crypto.c @@ -4,7 +4,7 @@ * This file is part of the SSH Library * * Copyright (c) 2003-2009 by Aris Adamantiadis - * Copyright (c) 2009-2012 by Andreas Schneider + * Copyright (c) 2009-2013 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -44,7 +44,7 @@ #include "libssh/session.h" #include "libssh/pki.h" #include "libssh/pki_priv.h" -#include "libssh/dh.h" +#include "libssh/bignum.h" struct pem_get_password_struct { ssh_auth_callback fn; @@ -89,7 +89,7 @@ static int pki_key_ecdsa_to_nid(EC_KEY *k) return -1; } -static const char *pki_key_ecdsa_nid_to_name(int nid) +const char *pki_key_ecdsa_nid_to_name(int nid) { switch (nid) { case NID_X9_62_prime256v1: @@ -200,9 +200,11 @@ int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e) return -1; } + /* EC_KEY_set_public_key duplicates p */ ok = EC_KEY_set_public_key(key->ecdsa, p); + EC_POINT_free(p); if (!ok) { - EC_POINT_free(p); + return -1; } return 0; @@ -212,6 +214,7 @@ int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e) ssh_key pki_key_dup(const ssh_key key, int demote) { ssh_key new; + int rc; new = ssh_key_new(); if (new == NULL) { @@ -343,13 +346,13 @@ ssh_key pki_key_dup(const ssh_key key, int demote) break; case SSH_KEYTYPE_ECDSA: #ifdef HAVE_OPENSSL_ECC + new->ecdsa_nid = key->ecdsa_nid; + /* privkey -> pubkey */ if (demote && ssh_key_is_private(key)) { const EC_POINT *p; int ok; - new->ecdsa_nid = key->ecdsa_nid; - new->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid); if (new->ecdsa == NULL) { goto fail; @@ -369,7 +372,14 @@ ssh_key pki_key_dup(const ssh_key key, int demote) } break; #endif + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_key_dup(new, key); + if (rc != SSH_OK) { + goto fail; + } + break; case SSH_KEYTYPE_UNKNOWN: + default: ssh_key_free(new); return NULL; } @@ -381,10 +391,20 @@ fail: } int pki_key_generate_rsa(ssh_key key, int parameter){ - key->rsa = RSA_generate_key(parameter, 65537, NULL, NULL); - if(key->rsa == NULL) - return SSH_ERROR; - return SSH_OK; + BIGNUM *e; + int rc; + + e = BN_new(); + key->rsa = RSA_new(); + + BN_set_word(e, 65537); + rc = RSA_generate_key_ex(key->rsa, parameter, e, NULL); + + BN_free(e); + + if (rc == -1 || key->rsa == NULL) + return SSH_ERROR; + return SSH_OK; } int pki_key_generate_dss(ssh_key key, int parameter){ @@ -521,13 +541,135 @@ int pki_key_compare(const ssh_key k1, break; } #endif + case SSH_KEYTYPE_ED25519: + /* ed25519 keys handled globaly */ case SSH_KEYTYPE_UNKNOWN: + default: return 1; } return 0; } +ssh_string pki_private_key_to_pem(const ssh_key key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data) +{ + ssh_string blob; + BUF_MEM *buf; + BIO *mem; + int rc; + + /* needed for openssl initialization */ + if (ssh_init() < 0) { + return NULL; + } + + mem = BIO_new(BIO_s_mem()); + if (mem == NULL) { + return NULL; + } + + switch (key->type) { + case SSH_KEYTYPE_DSS: + if (passphrase == NULL) { + struct pem_get_password_struct pgp = { auth_fn, auth_data }; + + rc = PEM_write_bio_DSAPrivateKey(mem, + key->dsa, + NULL, /* cipher */ + NULL, /* kstr */ + 0, /* klen */ + pem_get_password, + &pgp); + } else { + rc = PEM_write_bio_DSAPrivateKey(mem, + key->dsa, + NULL, /* cipher */ + NULL, /* kstr */ + 0, /* klen */ + NULL, /* auth_fn */ + (void*) passphrase); + } + if (rc != 1) { + goto err; + } + break; + case SSH_KEYTYPE_RSA: + case SSH_KEYTYPE_RSA1: + if (passphrase == NULL) { + struct pem_get_password_struct pgp = { auth_fn, auth_data }; + + rc = PEM_write_bio_RSAPrivateKey(mem, + key->rsa, + NULL, /* cipher */ + NULL, /* kstr */ + 0, /* klen */ + pem_get_password, + &pgp); + } else { + rc = PEM_write_bio_RSAPrivateKey(mem, + key->rsa, + NULL, /* cipher */ + NULL, /* kstr */ + 0, /* klen */ + NULL, /* auth_fn */ + (void*) passphrase); + } + if (rc != 1) { + goto err; + } + break; + case SSH_KEYTYPE_ECDSA: +#ifdef HAVE_ECC + if (passphrase == NULL) { + struct pem_get_password_struct pgp = { auth_fn, auth_data }; + + rc = PEM_write_bio_ECPrivateKey(mem, + key->ecdsa, + NULL, /* cipher */ + NULL, /* kstr */ + 0, /* klen */ + pem_get_password, + &pgp); + } else { + rc = PEM_write_bio_ECPrivateKey(mem, + key->ecdsa, + NULL, /* cipher */ + NULL, /* kstr */ + 0, /* klen */ + NULL, /* auth_fn */ + (void*) passphrase); + } + if (rc != 1) { + goto err; + } + break; +#endif + case SSH_KEYTYPE_ED25519: + case SSH_KEYTYPE_UNKNOWN: + BIO_free(mem); + ssh_pki_log("Unkown or invalid private key type %d", key->type); + return NULL; + } + + BIO_get_mem_ptr(mem, &buf); + + blob = ssh_string_new(buf->length); + if (blob == NULL) { + goto err; + } + + ssh_string_fill(blob, buf->data, buf->length); + BIO_free(mem); + + return blob; +err: + BIO_free(mem); + return NULL; +} + ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase, ssh_auth_callback auth_fn, @@ -629,6 +771,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key, break; #endif + case SSH_KEYTYPE_ED25519: case SSH_KEYTYPE_UNKNOWN: BIO_free(mem); ssh_pki_log("Unkown or invalid private key type %d", type); @@ -817,7 +960,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) break; case SSH_KEYTYPE_ECDSA: #ifdef HAVE_OPENSSL_ECC - rc = buffer_reinit(buffer); + rc = ssh_buffer_reinit(buffer); if (rc < 0) { ssh_buffer_free(buffer); return NULL; @@ -867,7 +1010,14 @@ ssh_string pki_publickey_to_blob(const ssh_key key) break; #endif + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_public_key_to_blob(buffer, key); + if (rc == SSH_ERROR){ + goto fail; + } + break; case SSH_KEYTYPE_UNKNOWN: + default: goto fail; } @@ -976,41 +1126,63 @@ static ssh_string _RSA_do_sign(const unsigned char *digest, return sig_blob; } +static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig) +{ + char buffer[40] = { 0 }; + ssh_string sig_blob = NULL; + + ssh_string r; + int r_len, r_offset_in, r_offset_out; + + ssh_string s; + int s_len, s_offset_in, s_offset_out; + + r = make_bignum_string(sig->dsa_sig->r); + if (r == NULL) { + return NULL; + } + + s = make_bignum_string(sig->dsa_sig->s); + if (s == NULL) { + ssh_string_free(r); + return NULL; + } + + r_len = ssh_string_len(r); + r_offset_in = (r_len > 20) ? (r_len - 20) : 0; + r_offset_out = (r_len < 20) ? (20 - r_len) : 0; + + s_len = ssh_string_len(s); + s_offset_in = (s_len > 20) ? (s_len - 20) : 0; + s_offset_out = (s_len < 20) ? (20 - s_len) : 0; + + memcpy(buffer + r_offset_out, + ((char *)ssh_string_data(r)) + r_offset_in, + r_len - r_offset_in); + memcpy(buffer + 20 + s_offset_out, + ((char *)ssh_string_data(s)) + s_offset_in, + s_len - s_offset_in); + + ssh_string_free(r); + ssh_string_free(s); + + sig_blob = ssh_string_new(40); + if (sig_blob == NULL) { + return NULL; + } + + ssh_string_fill(sig_blob, buffer, 40); + + return sig_blob; +} + ssh_string pki_signature_to_blob(const ssh_signature sig) { - char buffer[40] = {0}; ssh_string sig_blob = NULL; - ssh_string r; - ssh_string s; switch(sig->type) { case SSH_KEYTYPE_DSS: - r = make_bignum_string(sig->dsa_sig->r); - if (r == NULL) { - return NULL; - } - s = make_bignum_string(sig->dsa_sig->s); - if (s == NULL) { - ssh_string_free(r); - return NULL; - } - - memcpy(buffer, - ((char *)ssh_string_data(r)) + ssh_string_len(r) - 20, - 20); - memcpy(buffer + 20, - ((char *)ssh_string_data(s)) + ssh_string_len(s) - 20, - 20); - - ssh_string_free(r); - ssh_string_free(s); - - sig_blob = ssh_string_new(40); - if (sig_blob == NULL) { - return NULL; - } - - ssh_string_fill(sig_blob, buffer, 40); + sig_blob = pki_dsa_signature_to_blob(sig); break; case SSH_KEYTYPE_RSA: case SSH_KEYTYPE_RSA1: @@ -1019,6 +1191,8 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) case SSH_KEYTYPE_ECDSA: #ifdef HAVE_OPENSSL_ECC { + ssh_string r; + ssh_string s; ssh_buffer b; int rc; @@ -1062,6 +1236,10 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) break; } #endif + case SSH_KEYTYPE_ED25519: + sig_blob = pki_ed25519_sig_to_blob(sig); + break; + default: case SSH_KEYTYPE_UNKNOWN: ssh_pki_log("Unknown signature key type: %s", sig->type_c); return NULL; @@ -1070,6 +1248,67 @@ ssh_string pki_signature_to_blob(const ssh_signature sig) return sig_blob; } +static ssh_signature pki_signature_from_rsa_blob(const ssh_key pubkey, + const ssh_string sig_blob, + ssh_signature sig) +{ + uint32_t pad_len = 0; + char *blob_orig; + char *blob_padded_data; + ssh_string sig_blob_padded; + + size_t rsalen = 0; + size_t len = ssh_string_len(sig_blob); + + if (pubkey->rsa == NULL) { + ssh_pki_log("Pubkey RSA field NULL"); + goto errout; + } + + rsalen = RSA_size(pubkey->rsa); + if (len > rsalen) { + ssh_pki_log("Signature is too big: %lu > %lu", + (unsigned long)len, (unsigned long)rsalen); + goto errout; + } + +#ifdef DEBUG_CRYPTO + ssh_pki_log("RSA signature len: %lu", (unsigned long)len); + ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); +#endif + + if (len == rsalen) { + sig->rsa_sig = ssh_string_copy(sig_blob); + } else { + /* pad the blob to the expected rsalen size */ + ssh_pki_log("RSA signature len %lu < %lu", + (unsigned long)len, (unsigned long)rsalen); + + pad_len = rsalen - len; + + sig_blob_padded = ssh_string_new(rsalen); + if (sig_blob_padded == NULL) { + goto errout; + } + + blob_padded_data = (char *) ssh_string_data(sig_blob_padded); + blob_orig = (char *) ssh_string_data(sig_blob); + + /* front-pad the buffer with zeroes */ + BURN_BUFFER(blob_padded_data, pad_len); + /* fill the rest with the actual signature blob */ + memcpy(blob_padded_data + pad_len, blob_orig, len); + + sig->rsa_sig = sig_blob_padded; + } + + return sig; + +errout: + ssh_signature_free(sig); + return NULL; +} + ssh_signature pki_signature_from_blob(const ssh_key pubkey, const ssh_string sig_blob, enum ssh_keytypes_e type) @@ -1078,7 +1317,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, ssh_string r; ssh_string s; size_t len; - size_t rsalen; + int rc; sig = ssh_signature_new(); if (sig == NULL) { @@ -1142,29 +1381,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, break; case SSH_KEYTYPE_RSA: case SSH_KEYTYPE_RSA1: - rsalen = RSA_size(pubkey->rsa); - - if (len > rsalen) { - ssh_pki_log("Signature is to big size: %lu", - (unsigned long)len); - ssh_signature_free(sig); - return NULL; - } - - if (len < rsalen) { - ssh_pki_log("RSA signature len %lu < %lu", - (unsigned long)len, (unsigned long)rsalen); - } - -#ifdef DEBUG_CRYPTO - ssh_pki_log("RSA signature len: %lu", (unsigned long)len); - ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); -#endif - sig->rsa_sig = ssh_string_copy(sig_blob); - if (sig->rsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } + sig = pki_signature_from_rsa_blob(pubkey, sig_blob, sig); break; case SSH_KEYTYPE_ECDSA: #ifdef HAVE_OPENSSL_ECC @@ -1177,7 +1394,6 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, { /* build ecdsa siganature */ ssh_buffer b; uint32_t rlen; - int rc; b = ssh_buffer_new(); if (b == NULL) { @@ -1185,9 +1401,9 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, return NULL; } - rc = buffer_add_data(b, - ssh_string_data(sig_blob), - ssh_string_len(sig_blob)); + rc = ssh_buffer_add_data(b, + ssh_string_data(sig_blob), + ssh_string_len(sig_blob)); if (rc < 0) { ssh_buffer_free(b); ssh_signature_free(sig); @@ -1245,6 +1461,14 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, break; #endif + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_sig_from_blob(sig, sig_blob); + if (rc == SSH_ERROR){ + ssh_signature_free(sig); + return NULL; + } + break; + default: case SSH_KEYTYPE_UNKNOWN: ssh_pki_log("Unknown signature type"); ssh_signature_free(sig); @@ -1292,6 +1516,15 @@ int pki_signature_verify(ssh_session session, return SSH_ERROR; } break; + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_verify(key, sig, hash, hlen); + if (rc != SSH_OK){ + ssh_set_error(session, + SSH_FATAL, + "ed25519 signature verification error"); + return SSH_ERROR; + } + break; case SSH_KEYTYPE_ECDSA: #ifdef HAVE_OPENSSL_ECC rc = ECDSA_do_verify(hash, @@ -1308,6 +1541,7 @@ int pki_signature_verify(ssh_session session, break; #endif case SSH_KEYTYPE_UNKNOWN: + default: ssh_set_error(session, SSH_FATAL, "Unknown public key type"); return SSH_ERROR; } @@ -1319,6 +1553,7 @@ ssh_signature pki_do_sign(const ssh_key privkey, const unsigned char *hash, size_t hlen) { ssh_signature sig; + int rc; sig = ssh_signature_new(); if (sig == NULL) { @@ -1366,7 +1601,15 @@ ssh_signature pki_do_sign(const ssh_key privkey, break; #endif /* HAVE_OPENSSL_ECC */ + case SSH_KEYTYPE_ED25519: + rc = pki_ed25519_sign(privkey, sig, hash, hlen); + if (rc != SSH_OK){ + ssh_signature_free(sig); + return NULL; + } + break; case SSH_KEYTYPE_UNKNOWN: + default: ssh_signature_free(sig); return NULL; } @@ -1413,7 +1656,10 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key, } break; #endif + case SSH_KEYTYPE_ED25519: + /* ED25519 handled in caller */ case SSH_KEYTYPE_UNKNOWN: + default: ssh_signature_free(sig); return NULL; } diff --git a/libssh/src/pki_ed25519.c b/libssh/src/pki_ed25519.c new file mode 100644 index 00000000..7fb9827c --- /dev/null +++ b/libssh/src/pki_ed25519.c @@ -0,0 +1,305 @@ +/* + * pki_ed25519 .c - PKI infrastructure using ed25519 + * + * This file is part of the SSH Library + * + * Copyright (c) 2014 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "libssh/pki.h" +#include "libssh/pki_priv.h" +#include "libssh/ed25519.h" +#include "libssh/buffer.h" + +int pki_key_generate_ed25519(ssh_key key) +{ + int rc; + + key->ed25519_privkey = malloc(sizeof (ed25519_privkey)); + if (key->ed25519_privkey == NULL) { + goto error; + } + + key->ed25519_pubkey = malloc(sizeof (ed25519_privkey)); + if (key->ed25519_privkey == NULL) { + goto error; + } + + rc = crypto_sign_ed25519_keypair(*key->ed25519_pubkey, + *key->ed25519_privkey); + if (rc != 0) { + goto error; + } + + return SSH_OK; +error: + SAFE_FREE(key->ed25519_privkey); + SAFE_FREE(key->ed25519_pubkey); + + return SSH_ERROR; +} + +int pki_ed25519_sign(const ssh_key privkey, + ssh_signature sig, + const unsigned char *hash, + size_t hlen) +{ + int rc; + uint8_t *buffer; + unsigned long long dlen = 0; + + buffer = malloc(hlen + ED25519_SIG_LEN); + if (buffer == NULL) { + return SSH_ERROR; + } + + rc = crypto_sign_ed25519(buffer, + &dlen, + hash, + hlen, + *privkey->ed25519_privkey); + if (rc != 0) { + goto error; + } + sig->ed25519_sig = malloc(ED25519_SIG_LEN); + if (sig->ed25519_sig == NULL) { + goto error; + } + + /* This shouldn't happen */ + if (dlen - hlen != ED25519_SIG_LEN) { + goto error; + } + memcpy(sig->ed25519_sig, buffer, dlen - hlen); + SAFE_FREE(buffer); + + return SSH_OK; +error: + SAFE_FREE(buffer); + return SSH_ERROR; +} + +int pki_ed25519_verify(const ssh_key pubkey, + ssh_signature sig, + const unsigned char *hash, + size_t hlen) +{ + unsigned long long mlen = 0; + uint8_t *buffer; + uint8_t *buffer2; + int rc; + + if (pubkey == NULL || sig == NULL || + hash == NULL || sig->ed25519_sig == NULL) { + return SSH_ERROR; + } + + buffer = malloc(hlen + ED25519_SIG_LEN); + if (buffer == NULL) { + return SSH_ERROR; + } + + buffer2 = malloc(hlen + ED25519_SIG_LEN); + if (buffer2 == NULL) { + goto error; + } + + memcpy(buffer, sig->ed25519_sig, ED25519_SIG_LEN); + memcpy(buffer + ED25519_SIG_LEN, hash, hlen); + + rc = crypto_sign_ed25519_open(buffer2, + &mlen, + buffer, + hlen + ED25519_SIG_LEN, + *pubkey->ed25519_pubkey); + + BURN_BUFFER(buffer, hlen + ED25519_SIG_LEN); + BURN_BUFFER(buffer2, hlen); + SAFE_FREE(buffer); + SAFE_FREE(buffer2); + if (rc == 0) { + return SSH_OK; + } else { + return SSH_ERROR; + } +error: + SAFE_FREE(buffer); + SAFE_FREE(buffer2); + + return SSH_ERROR; +} + +/** + * @internal + * + * @brief Compare ed25519 keys if they are equal. + * + * @param[in] k1 The first key to compare. + * + * @param[in] k2 The second key to compare. + * + * @param[in] what What part or type of the key do you want to compare. + * + * @return 0 if equal, 1 if not. + */ +int pki_ed25519_key_cmp(const ssh_key k1, + const ssh_key k2, + enum ssh_keycmp_e what) +{ + int cmp; + + switch(what) { + case SSH_KEY_CMP_PRIVATE: + if (k1->ed25519_privkey == NULL || k2->ed25519_privkey == NULL) { + return 1; + } + cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, ED25519_SK_LEN); + if (cmp != 0) { + return 1; + } + /* FALL THROUGH */ + case SSH_KEY_CMP_PUBLIC: + if (k1->ed25519_pubkey == NULL || k2->ed25519_pubkey == NULL) { + return 1; + } + cmp = memcmp(k1->ed25519_pubkey, k2->ed25519_pubkey, ED25519_PK_LEN); + if (cmp != 0) { + return 1; + } + } + + return 0; +} + +/** + * @internal + * + * @brief duplicate an ed25519 key + * + * @param[out\ new preinitialized output ssh_ke + * + * @param[in] key key to copy + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_ed25519_key_dup(ssh_key new, const ssh_key key) +{ + if (key->ed25519_privkey == NULL || key->ed25519_pubkey == NULL) { + return SSH_ERROR; + } + + new->ed25519_privkey = malloc(ED25519_SK_LEN); + if (new->ed25519_privkey == NULL) { + return SSH_ERROR; + } + + new->ed25519_pubkey = malloc(ED25519_PK_LEN); + if (new->ed25519_privkey == NULL || new->ed25519_pubkey == NULL){ + SAFE_FREE(new->ed25519_privkey); + return SSH_ERROR; + } + + memcpy(new->ed25519_privkey, key->ed25519_privkey, ED25519_SK_LEN); + memcpy(new->ed25519_pubkey, key->ed25519_pubkey, ED25519_PK_LEN); + + return SSH_OK; +} + +/** + * @internal + * + * @brief outputs an ed25519 public key in a blob buffer. + * + * @param[out] buffer output buffer + * + * @param[in] key key to output + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key) +{ + int rc; + + if (key->ed25519_pubkey == NULL){ + return SSH_ERROR; + } + + rc = ssh_buffer_pack(buffer, + "dP", + (uint32_t)ED25519_PK_LEN, + (size_t)ED25519_PK_LEN, key->ed25519_pubkey); + + return rc; +} + +/** + * @internal + * + * @brief output a signature blob from an ed25519 signature + * + * @param[in] sig signature to convert + * + * @return Signature blob in SSH string, or NULL on error + */ +ssh_string pki_ed25519_sig_to_blob(ssh_signature sig) +{ + ssh_string sig_blob; + + if (sig->ed25519_sig == NULL) { + return NULL; + } + + sig_blob = ssh_string_new(ED25519_SIG_LEN); + if (sig_blob == NULL) { + return NULL; + } + ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN); + + return sig_blob; +} + +/** + * @internal + * + * @brief Convert a signature blob in an ed25519 signature. + * + * @param[out] sig a preinitialized signature + * + * @param[in] sig_blob a signature blob + * + * @return SSH_ERROR on error, SSH_OK on success + */ +int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob) +{ + size_t len; + + len = ssh_string_len(sig_blob); + if (len != ED25519_SIG_LEN){ + ssh_pki_log("Invalid ssh-ed25519 signature len: %zu", len); + return SSH_ERROR; + } + + sig->ed25519_sig = malloc(ED25519_SIG_LEN); + if (sig->ed25519_sig == NULL){ + return SSH_ERROR; + } + + memcpy(sig->ed25519_sig, ssh_string_data(sig_blob), ED25519_SIG_LEN); + + return SSH_OK; +} diff --git a/libssh/src/pki_gcrypt.c b/libssh/src/pki_gcrypt.c index a44ed73a..2811acce 100644 --- a/libssh/src/pki_gcrypt.c +++ b/libssh/src/pki_gcrypt.c @@ -388,7 +388,7 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, } } else { if(len > 0) { - if (buffer_add_data(buffer, p, len) < 0) { + if (ssh_buffer_add_data(buffer, p, len) < 0) { ssh_buffer_free(buffer); SAFE_FREE(iv); return NULL; @@ -398,7 +398,7 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, get_next_line(p, len); while(len > 0 && strncmp(p, header_end, header_end_size) != 0) { - if (buffer_add_data(buffer, p, len) < 0) { + if (ssh_buffer_add_data(buffer, p, len) < 0) { ssh_buffer_free(buffer); SAFE_FREE(iv); return NULL; @@ -412,7 +412,7 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, return NULL; } - if (buffer_add_data(buffer, "\0", 1) < 0) { + if (ssh_buffer_add_data(buffer, "\0", 1) < 0) { ssh_buffer_free(buffer); SAFE_FREE(iv); return NULL; @@ -591,6 +591,19 @@ int pki_key_ecdsa_nid_from_name(const char *name) } #endif +ssh_string pki_private_key_to_pem(const ssh_key key, + const char *passphrase, + ssh_auth_callback auth_fn, + void *auth_data) +{ + (void) key; + (void) passphrase; + (void) auth_fn; + (void) auth_data; + + return NULL; +} + ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase, ssh_auth_callback auth_fn, @@ -1141,7 +1154,7 @@ ssh_string pki_publickey_to_blob(const ssh_key key) } rc = buffer_add_ssh_string(buffer, type_s); - string_free(type_s); + ssh_string_free(type_s); if (rc < 0) { ssh_buffer_free(buffer); return NULL; @@ -1577,7 +1590,7 @@ ssh_signature pki_do_sign(const ssh_key privkey, return NULL; } sig->type = privkey->type; - + sig->type_c = privkey->type_c; switch (privkey->type) { case SSH_KEYTYPE_DSS: /* That is to mark the number as positive */ @@ -1644,6 +1657,7 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key, return NULL; } sig->type = key->type; + sig->type_c = key->type_c; switch(key->type) { case SSH_KEYTYPE_DSS: diff --git a/libssh/src/poll.c b/libssh/src/poll.c index 8e21e0d5..4e9f19f0 100644 --- a/libssh/src/poll.c +++ b/libssh/src/poll.c @@ -3,8 +3,8 @@ * * This file is part of the SSH Library * - * Copyright (c) 2009-2010 by Andreas Schneider - * Copyright (c) 2003-2009 by Aris Adamantiadis + * Copyright (c) 2009-2013 by Andreas Schneider + * Copyright (c) 2003-2013 by Aris Adamantiadis * Copyright (c) 2009 Aleksandar Kanchev * * The SSH Library is free software; you can redistribute it and/or modify @@ -64,6 +64,7 @@ struct ssh_poll_handle_struct { ssh_poll_ctx ctx; + ssh_session session; union { socket_t fd; size_t idx; @@ -472,14 +473,18 @@ static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) { if (pollptrs == NULL) { return -1; } + ctx->pollptrs = pollptrs; pollfds = realloc(ctx->pollfds, sizeof(ssh_pollfd_t) * new_size); if (pollfds == NULL) { - ctx->pollptrs = realloc(pollptrs, sizeof(ssh_poll_handle) * ctx->polls_allocated); + pollptrs = realloc(ctx->pollptrs, sizeof(ssh_poll_handle) * ctx->polls_allocated); + if (pollptrs == NULL) { + return -1; + } + ctx->pollptrs = pollptrs; return -1; } - ctx->pollptrs = pollptrs; ctx->pollfds = pollfds; ctx->polls_allocated = new_size; @@ -786,6 +791,10 @@ int ssh_event_add_session(ssh_event event, ssh_session session) { p = session->default_poll_ctx->pollptrs[i]; ssh_poll_ctx_remove(session->default_poll_ctx, p); ssh_poll_ctx_add(event->ctx, p); + /* associate the pollhandler with a session so we can put it back + * at ssh_event_free() + */ + p->session = session; } #ifdef WITH_SERVER iterator = ssh_list_get_iterator(event->sessions); @@ -848,12 +857,22 @@ int ssh_event_remove_fd(ssh_event event, socket_t fd) { for (i = 0; i < used; i++) { if(fd == event->ctx->pollfds[i].fd) { ssh_poll_handle p = event->ctx->pollptrs[i]; - struct ssh_event_fd_wrapper *pw = p->cb_data; + if (p->session != NULL){ + /* we cannot free that handle, it's owned by its session */ + continue; + } + if (p->cb == ssh_event_fd_wrapper_callback) { + struct ssh_event_fd_wrapper *pw = p->cb_data; + SAFE_FREE(pw); + } - ssh_poll_ctx_remove(event->ctx, p); - free(pw); + /* + * The free function calls ssh_poll_ctx_remove() and decrements + * event->ctx->polls_used. + */ ssh_poll_free(p); rc = SSH_OK; + /* restart the loop */ used = event->ctx->polls_used; i = 0; @@ -876,7 +895,6 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) { ssh_poll_handle p; register size_t i, used; int rc = SSH_ERROR; - socket_t session_fd; #ifdef WITH_SERVER struct ssh_iterator *iterator; #endif @@ -885,14 +903,15 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) { return SSH_ERROR; } - session_fd = ssh_get_fd(session); used = event->ctx->polls_used; for(i = 0; i < used; i++) { - if(session_fd == event->ctx->pollfds[i].fd) { - p = event->ctx->pollptrs[i]; + p = event->ctx->pollptrs[i]; + if(p->session == session){ ssh_poll_ctx_remove(event->ctx, p); + p->session = NULL; ssh_poll_ctx_add(session->default_poll_ctx, p); rc = SSH_OK; + used = 0; } } #ifdef WITH_SERVER @@ -919,10 +938,23 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) { * */ void ssh_event_free(ssh_event event) { - if(event == NULL) { + int used, i; + ssh_poll_handle p; + if(event == NULL) { return; } if(event->ctx != NULL) { + used = event->ctx->polls_used; + for(i = 0; i < used; i++) { + p = event->ctx->pollptrs[i]; + if(p->session != NULL){ + ssh_poll_ctx_remove(event->ctx, p); + ssh_poll_ctx_add(p->session->default_poll_ctx, p); + p->session = NULL; + used = 0; + } + } + ssh_poll_ctx_free(event->ctx); } #ifdef WITH_SERVER diff --git a/libssh/src/sc25519.c b/libssh/src/sc25519.c new file mode 100644 index 00000000..9576d416 --- /dev/null +++ b/libssh/src/sc25519.c @@ -0,0 +1,373 @@ +/* + * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, + * Peter Schwabe, Bo-Yin Yang. + * Copied from supercop-20130419/crypto_sign/ed25519/ref/sc25519.c + */ + +#include "libssh/priv.h" +#include "libssh/sc25519.h" + +/*Arithmetic modulo the group order m = 2^252 + 27742317777372353535851937790883648493 = 7237005577332262213973186563042994240857116359379907606001950938285454250989 */ + +static const uint32_t m[32] = { + 0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, + 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 +}; + +static const uint32_t mu[33] = { + 0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, + 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21, + 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F +}; + +static uint32_t lt(uint32_t a,uint32_t b) /* 16-bit inputs */ +{ + unsigned int x = a; + + x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */ + x >>= 31; /* 0: no; 1: yes */ + + return x; +} + +/* Reduce coefficients of r before calling reduce_add_sub */ +static void reduce_add_sub(sc25519 *r) +{ + uint32_t pb = 0; + uint32_t b; + uint32_t mask; + int i; + unsigned char t[32]; + + for (i = 0; i < 32; i++) { + pb += m[i]; + b = lt(r->v[i],pb); + t[i] = r->v[i]-pb+(b<<8); + pb = b; + } + mask = b - 1; + for (i = 0; i < 32; i++) { + r->v[i] ^= mask & (r->v[i] ^ t[i]); + } +} + +/* Reduce coefficients of x before calling barrett_reduce */ +static void barrett_reduce(sc25519 *r, const uint32_t x[64]) +{ + /* See HAC, Alg. 14.42 */ + int i,j; + uint32_t q2[66]; + uint32_t *q3 = q2 + 33; + uint32_t r1[33]; + uint32_t r2[33]; + uint32_t carry; + uint32_t pb = 0; + uint32_t b; + + for (i = 0; i < 66; i++) { + q2[i] = 0; + } + for (i = 0; i < 33; i++) { + r2[i] = 0; + } + + for (i = 0; i < 33; i++) { + for (j = 0; j < 33; j++) { + if (i + j >= 31) { + q2[i+j] += mu[i]*x[j+31]; + } + } + } + + carry = q2[31] >> 8; + q2[32] += carry; + carry = q2[32] >> 8; + q2[33] += carry; + + for (i = 0; i < 33; i++) { + r1[i] = x[i]; + } + + for (i = 0; i < 32; i++) { + for (j = 0; j < 33; j++) { + if (i + j < 33) { + r2[i+j] += m[i]*q3[j]; + } + } + } + + for (i = 0; i < 32; i++) { + carry = r2[i] >> 8; + r2[i+1] += carry; + r2[i] &= 0xff; + } + + for (i = 0; i < 32; i++) { + pb += r2[i]; + b = lt(r1[i],pb); + r->v[i] = r1[i]-pb+(b<<8); + pb = b; + } + + /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3 + * If so: Handle it here! + */ + + reduce_add_sub(r); + reduce_add_sub(r); +} + +void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]) +{ + int i; + uint32_t t[64]; + + for (i = 0; i < 32; i++) { + t[i] = x[i]; + } + for (i = 32; i < 64; i++) { + t[i] = 0; + } + + barrett_reduce(r, t); +} + +void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16]) +{ + int i; + + for (i = 0; i < 16; i++) { + r->v[i] = x[i]; + } +} + +void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]) +{ + int i; + uint32_t t[64]; + + for (i = 0; i < 64; i++) { + t[i] = x[i]; + } + + barrett_reduce(r, t); +} + +void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x) +{ + int i; + + for (i = 0; i < 16; i++) { + r->v[i] = x->v[i]; + } + for (i = 0; i < 16; i++) { + r->v[16+i] = 0; + } +} + +void sc25519_to32bytes(unsigned char r[32], const sc25519 *x) +{ + int i; + + for (i = 0; i < 32; i++) { + r[i] = x->v[i]; + } +} + +int sc25519_iszero_vartime(const sc25519 *x) +{ + int i; + + for (i = 0; i < 32; i++) { + if(x->v[i] != 0) { + return 0; + } + } + + return 1; +} + +int sc25519_isshort_vartime(const sc25519 *x) +{ + int i; + + for (i = 31; i > 15; i--) { + if (x->v[i] != 0) { + return 0; + } + } + + return 1; +} + +int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y) +{ + int i; + + for (i = 31; i >= 0; i--) { + if (x->v[i] < y->v[i]) { + return 1; + } + if (x->v[i] > y->v[i]) { + return 0; + } + } + + return 0; +} + +void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y) +{ + int i, carry; + + for (i = 0; i < 32; i++) { + r->v[i] = x->v[i] + y->v[i]; + } + + for (i = 0;i < 31; i++) { + carry = r->v[i] >> 8; + r->v[i+1] += carry; + r->v[i] &= 0xff; + } + + reduce_add_sub(r); +} + +void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y) +{ + uint32_t b = 0; + uint32_t t; + int i; + + for (i = 0; i < 32; i++) { + t = x->v[i] - y->v[i] - b; + r->v[i] = t & 255; + b = (t >> 8) & 1; + } +} + +void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y) +{ + int i,j,carry; + uint32_t t[64]; + + for (i = 0; i < 64; i++) { + t[i] = 0; + } + + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + t[i+j] += x->v[i] * y->v[j]; + } + } + + /* Reduce coefficients */ + for (i = 0; i < 63; i++) { + carry = t[i] >> 8; + t[i+1] += carry; + t[i] &= 0xff; + } + + barrett_reduce(r, t); +} + +void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y) +{ + sc25519 t; + sc25519_from_shortsc(&t, y); + sc25519_mul(r, x, &t); +} + +void sc25519_window3(signed char r[85], const sc25519 *s) +{ + char carry; + int i; + + for (i = 0; i < 10; i++) { + r[8*i+0] = s->v[3*i+0] & 7; + r[8*i+1] = (s->v[3*i+0] >> 3) & 7; + r[8*i+2] = (s->v[3*i+0] >> 6) & 7; + r[8*i+2] ^= (s->v[3*i+1] << 2) & 7; + r[8*i+3] = (s->v[3*i+1] >> 1) & 7; + r[8*i+4] = (s->v[3*i+1] >> 4) & 7; + r[8*i+5] = (s->v[3*i+1] >> 7) & 7; + r[8*i+5] ^= (s->v[3*i+2] << 1) & 7; + r[8*i+6] = (s->v[3*i+2] >> 2) & 7; + r[8*i+7] = (s->v[3*i+2] >> 5) & 7; + } + r[8*i+0] = s->v[3*i+0] & 7; + r[8*i+1] = (s->v[3*i+0] >> 3) & 7; + r[8*i+2] = (s->v[3*i+0] >> 6) & 7; + r[8*i+2] ^= (s->v[3*i+1] << 2) & 7; + r[8*i+3] = (s->v[3*i+1] >> 1) & 7; + r[8*i+4] = (s->v[3*i+1] >> 4) & 7; + + /* Making it signed */ + carry = 0; + for (i = 0; i < 84; i++) { + r[i] += carry; + r[i+1] += r[i] >> 3; + r[i] &= 7; + carry = r[i] >> 2; + r[i] -= carry<<3; + } + + r[84] += carry; +} + +void sc25519_window5(signed char r[51], const sc25519 *s) +{ + char carry; + int i; + + for (i = 0; i < 6; i++) { + r[8*i+0] = s->v[5*i+0] & 31; + r[8*i+1] = (s->v[5*i+0] >> 5) & 31; + r[8*i+1] ^= (s->v[5*i+1] << 3) & 31; + r[8*i+2] = (s->v[5*i+1] >> 2) & 31; + r[8*i+3] = (s->v[5*i+1] >> 7) & 31; + r[8*i+3] ^= (s->v[5*i+2] << 1) & 31; + r[8*i+4] = (s->v[5*i+2] >> 4) & 31; + r[8*i+4] ^= (s->v[5*i+3] << 4) & 31; + r[8*i+5] = (s->v[5*i+3] >> 1) & 31; + r[8*i+6] = (s->v[5*i+3] >> 6) & 31; + r[8*i+6] ^= (s->v[5*i+4] << 2) & 31; + r[8*i+7] = (s->v[5*i+4] >> 3) & 31; + } + r[8*i+0] = s->v[5*i+0] & 31; + r[8*i+1] = (s->v[5*i+0] >> 5) & 31; + r[8*i+1] ^= (s->v[5*i+1] << 3) & 31; + r[8*i+2] = (s->v[5*i+1] >> 2) & 31; + + /* Making it signed */ + carry = 0; + for (i = 0; i < 50; i++) { + r[i] += carry; + r[i+1] += r[i] >> 5; + r[i] &= 31; + carry = r[i] >> 4; + r[i] -= carry<<5; + } + + r[50] += carry; +} + +void sc25519_2interleave2(unsigned char r[127], + const sc25519 *s1, + const sc25519 *s2) +{ + int i; + + for (i = 0; i < 31; i++) { + r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2); + r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2); + r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2); + r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2); + } + r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2); + r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2); + r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2); +} diff --git a/libssh/src/scp.c b/libssh/src/scp.c index db07aed4..0208ddc5 100644 --- a/libssh/src/scp.c +++ b/libssh/src/scp.c @@ -545,7 +545,7 @@ int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len){ * @see ssh_scp_request_get_warning() */ int ssh_scp_pull_request(ssh_scp scp){ - char buffer[4096] = {0}; + char buffer[MAX_BUF_SIZE] = {0}; char *mode=NULL; char *p,*tmp; uint64_t size; @@ -642,7 +642,7 @@ int ssh_scp_pull_request(ssh_scp scp){ * the message failed, or sending it in a bad state. */ int ssh_scp_deny_request(ssh_scp scp, const char *reason){ - char buffer[4096]; + char buffer[MAX_BUF_SIZE]; int err; if(scp==NULL) return SSH_ERROR; @@ -814,7 +814,7 @@ int ssh_scp_integer_mode(const char *mode){ */ char *ssh_scp_string_mode(int mode){ char buffer[16]; - snprintf(buffer,sizeof(buffer),"%.4d",mode); + snprintf(buffer,sizeof(buffer),"%.4o",mode); return strdup(buffer); } diff --git a/libssh/src/server.c b/libssh/src/server.c index 8629c8ba..3a38fc7b 100644 --- a/libssh/src/server.c +++ b/libssh/src/server.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2004-2005 by Aris Adamantiadis + * Copyright (c) 2004-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -174,6 +174,15 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){ SSH_LOG(SSH_LOG_RARE,"Invalid state for SSH_MSG_KEXDH_INIT"); goto error; } + + /* If first_kex_packet_follows guess was wrong, ignore this message. */ + if (session->first_kex_follows_guess_wrong != 0) { + SSH_LOG(SSH_LOG_RARE, "first_kex_packet_follows guess was wrong, " + "ignoring first SSH_MSG_KEXDH_INIT message"); + session->first_kex_follows_guess_wrong = 0; + goto error; + } + switch(session->next_crypto->kex_type){ case SSH_KEX_DH_GROUP1_SHA1: case SSH_KEX_DH_GROUP14_SHA1: @@ -217,6 +226,7 @@ int ssh_get_key_params(ssh_session session, ssh_key *privkey){ *privkey = session->srv.ecdsa_key; break; case SSH_KEYTYPE_UNKNOWN: + default: *privkey = NULL; } @@ -241,9 +251,9 @@ int ssh_get_key_params(ssh_session session, ssh_key *privkey){ static int dh_handshake_server(ssh_session session) { ssh_key privkey; - //ssh_string pubkey_blob = NULL; ssh_string sig_blob; ssh_string f; + int rc; if (dh_generate_y(session) < 0) { ssh_set_error(session, SSH_FATAL, "Could not create y number"); @@ -284,25 +294,26 @@ static int dh_handshake_server(ssh_session session) { return -1; } - if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY) < 0 || - buffer_add_ssh_string(session->out_buffer, - session->next_crypto->server_pubkey) < 0 || - buffer_add_ssh_string(session->out_buffer, f) < 0 || - buffer_add_ssh_string(session->out_buffer, sig_blob) < 0) { - ssh_set_error(session, SSH_FATAL, "Not enough space"); - buffer_reinit(session->out_buffer); - ssh_string_free(f); - ssh_string_free(sig_blob); - return -1; - } + rc = ssh_buffer_pack(session->out_buffer, + "bSSS", + SSH2_MSG_KEXDH_REPLY, + session->next_crypto->server_pubkey, + f, + sig_blob); ssh_string_free(f); ssh_string_free(sig_blob); + if(rc != SSH_OK){ + ssh_set_error_oom(session); + ssh_buffer_reinit(session->out_buffer); + return -1; + } + if (packet_send(session) == SSH_ERROR) { return -1; } if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return -1; } @@ -581,12 +592,8 @@ int ssh_handle_key_exchange(ssh_session session) { */ int ssh_auth_reply_default(ssh_session session,int partial) { char methods_c[128] = {0}; - ssh_string methods = NULL; int rc = SSH_ERROR; - if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_FAILURE) < 0) { - return rc; - } if (session->auth_methods == 0) { session->auth_methods = SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD; @@ -622,63 +629,43 @@ int ssh_auth_reply_default(ssh_session session,int partial) { SSH_LOG(SSH_LOG_PACKET, "Sending a auth failure. methods that can continue: %s", methods_c); - methods = ssh_string_from_char(methods_c); - if (methods == NULL) { - goto error; + rc = ssh_buffer_pack(session->out_buffer, + "bsb", + SSH2_MSG_USERAUTH_FAILURE, + methods_c, + partial ? 1 : 0); + if (rc != SSH_OK){ + ssh_set_error_oom(session); + return SSH_ERROR; } - - if (buffer_add_ssh_string(session->out_buffer, methods) < 0) { - goto error; - } - - if (partial) { - if (buffer_add_u8(session->out_buffer, 1) < 0) { - goto error; - } - } else { - if (buffer_add_u8(session->out_buffer, 0) < 0) { - goto error; - } - } - rc = packet_send(session); -error: - ssh_string_free(methods); - return rc; } static int ssh_message_channel_request_open_reply_default(ssh_message msg) { - SSH_LOG(SSH_LOG_FUNCTIONS, "Refusing a channel"); + int rc; - if (buffer_add_u8(msg->session->out_buffer - , SSH2_MSG_CHANNEL_OPEN_FAILURE) < 0) { - goto error; - } - if (buffer_add_u32(msg->session->out_buffer, - htonl(msg->channel_request_open.sender)) < 0) { - goto error; - } - if (buffer_add_u32(msg->session->out_buffer, - htonl(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) < 0) { - goto error; - } - /* reason is an empty string */ - if (buffer_add_u32(msg->session->out_buffer, 0) < 0) { - goto error; - } - /* language too */ - if (buffer_add_u32(msg->session->out_buffer, 0) < 0) { - goto error; - } + SSH_LOG(SSH_LOG_FUNCTIONS, "Refusing a channel"); - return packet_send(msg->session); -error: - return SSH_ERROR; + rc = ssh_buffer_pack(msg->session->out_buffer, + "bdddd", + SSH2_MSG_CHANNEL_OPEN_FAILURE, + msg->channel_request_open.sender, + SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, + 0, /* reason is empty string */ + 0); /* language string */ + if (rc != SSH_OK){ + ssh_set_error_oom(msg->session); + return SSH_ERROR; + } + + rc = packet_send(msg->session); + return rc; } static int ssh_message_channel_request_reply_default(ssh_message msg) { uint32_t channel; + int rc; if (msg->channel_request.want_reply) { channel = msg->channel_request.channel->remote_channel; @@ -686,13 +673,14 @@ static int ssh_message_channel_request_reply_default(ssh_message msg) { SSH_LOG(SSH_LOG_PACKET, "Sending a default channel_request denied to channel %d", channel); - if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_CHANNEL_FAILURE) < 0) { - return SSH_ERROR; + rc = ssh_buffer_pack(msg->session->out_buffer, + "bd", + SSH2_MSG_CHANNEL_FAILURE, + channel); + if (rc != SSH_OK){ + ssh_set_error_oom(msg->session); + return SSH_ERROR; } - if (buffer_add_u32(msg->session->out_buffer, htonl(channel)) < 0) { - return SSH_ERROR; - } - return packet_send(msg->session); } @@ -708,33 +696,32 @@ static int ssh_message_service_request_reply_default(ssh_message msg) { } int ssh_message_service_reply_success(ssh_message msg) { - struct ssh_string_struct *service; - ssh_session session; + ssh_session session; + int rc; - if (msg == NULL) { - return SSH_ERROR; - } - session = msg->session; + if (msg == NULL) { + return SSH_ERROR; + } + session = msg->session; - SSH_LOG(SSH_LOG_PACKET, - "Sending a SERVICE_ACCEPT for service %s", msg->service_request.service); - if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_ACCEPT) < 0) { - return -1; - } - service=ssh_string_from_char(msg->service_request.service); - if (service == NULL) { - return -1; - } + SSH_LOG(SSH_LOG_PACKET, + "Sending a SERVICE_ACCEPT for service %s", msg->service_request.service); - if (buffer_add_ssh_string(session->out_buffer, service) < 0) { - ssh_string_free(service); - return -1; - } - ssh_string_free(service); - return packet_send(msg->session); + rc = ssh_buffer_pack(session->out_buffer, + "bs", + SSH2_MSG_SERVICE_ACCEPT, + msg->service_request.service); + if (rc != SSH_OK){ + ssh_set_error_oom(session); + return SSH_ERROR; + } + rc = packet_send(msg->session); + return rc; } int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) { + int rc; + SSH_LOG(SSH_LOG_FUNCTIONS, "Accepting a global request"); if (msg->global_request.want_reply) { @@ -745,7 +732,9 @@ int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_por if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD && msg->global_request.bind_port == 0) { - if (buffer_add_u32(msg->session->out_buffer, htonl(bound_port)) < 0) { + rc = ssh_buffer_pack(msg->session->out_buffer, "d", bound_port); + if (rc != SSH_ERROR) { + ssh_set_error_oom(msg->session); goto error; } } @@ -876,9 +865,8 @@ int ssh_message_auth_set_methods(ssh_message msg, int methods) { int ssh_message_auth_interactive_request(ssh_message msg, const char *name, const char *instruction, unsigned int num_prompts, const char **prompts, char *echo) { - int r; + int rc; unsigned int i = 0; - ssh_string tmp = NULL; if(name == NULL || instruction == NULL) { return SSH_ERROR; @@ -887,71 +875,30 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name, return SSH_ERROR; } - if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_USERAUTH_INFO_REQUEST) < 0) { - return SSH_ERROR; - } - - /* name */ - tmp = ssh_string_from_char(name); - if (tmp == NULL) { - return SSH_ERROR; - } - - r = buffer_add_ssh_string(msg->session->out_buffer, tmp); - ssh_string_free(tmp); - if (r < 0) { - return SSH_ERROR; - } - - /* instruction */ - tmp = ssh_string_from_char(instruction); - if (tmp == NULL) { - return SSH_ERROR; - } - - r = buffer_add_ssh_string(msg->session->out_buffer, tmp); - ssh_string_free(tmp); - if (r < 0) { - return SSH_ERROR; - } - - /* language tag */ - tmp = ssh_string_from_char(""); - if (tmp == NULL) { - return SSH_ERROR; - } - - r = buffer_add_ssh_string(msg->session->out_buffer, tmp); - ssh_string_free(tmp); - if (r < 0) { - return SSH_ERROR; - } - - /* num prompts */ - if (buffer_add_u32(msg->session->out_buffer, ntohl(num_prompts)) < 0) { + rc = ssh_buffer_pack(msg->session->out_buffer, + "bsssd", + SSH2_MSG_USERAUTH_INFO_REQUEST, + name, + instruction, + "", /* language tag */ + num_prompts); + if (rc != SSH_OK){ + ssh_set_error_oom(msg->session); return SSH_ERROR; } for(i = 0; i < num_prompts; i++) { - /* prompt[i] */ - tmp = ssh_string_from_char(prompts[i]); - if (tmp == NULL) { - return SSH_ERROR; - } - - r = buffer_add_ssh_string(msg->session->out_buffer, tmp); - ssh_string_free(tmp); - if (r < 0) { - return SSH_ERROR; - } - - /* echo[i] */ - if (buffer_add_u8(msg->session->out_buffer, echo[i]) < 0) { + rc = ssh_buffer_pack(msg->session->out_buffer, + "sb", + prompts[i], + echo[1] ? 1 : 0); + if (rc != SSH_OK){ + ssh_set_error_oom(msg->session); return SSH_ERROR; } } - r = packet_send(msg->session); + rc = packet_send(msg->session); /* fill in the kbdint structure */ if (msg->session->kbdint == NULL) { @@ -994,7 +941,7 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name, msg->session->kbdint = NULL; return SSH_ERROR; } - msg->session->kbdint->echo = malloc(num_prompts * sizeof(char)); + msg->session->kbdint->echo = malloc(num_prompts * sizeof(unsigned char)); if (msg->session->kbdint->echo == NULL) { ssh_set_error_oom(msg->session); ssh_kbdint_free(msg->session->kbdint); @@ -1017,7 +964,7 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name, msg->session->kbdint->echo = NULL; } - return r; + return rc; } int ssh_auth_reply_success(ssh_session session, int partial) { @@ -1058,17 +1005,23 @@ int ssh_message_auth_reply_success(ssh_message msg, int partial) { /* Answer OK to a pubkey auth request */ int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) { - if (msg == NULL) { - return SSH_ERROR; - } + int rc; + if (msg == NULL) { + return SSH_ERROR; + } - if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_USERAUTH_PK_OK) < 0 || - buffer_add_ssh_string(msg->session->out_buffer, algo) < 0 || - buffer_add_ssh_string(msg->session->out_buffer, pubkey) < 0) { - return SSH_ERROR; - } + rc = ssh_buffer_pack(msg->session->out_buffer, + "bSS", + SSH2_MSG_USERAUTH_PK_OK, + algo, + pubkey); + if(rc != SSH_OK){ + ssh_set_error_oom(msg->session); + return SSH_ERROR; + } - return packet_send(msg->session); + rc = packet_send(msg->session); + return rc; } int ssh_message_auth_reply_pk_ok_simple(ssh_message msg) { @@ -1223,27 +1176,14 @@ int ssh_execute_message_callbacks(ssh_session session){ int ssh_send_keepalive(ssh_session session) { - struct ssh_string_struct *req; int rc; - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_GLOBAL_REQUEST); - if (rc < 0) { - goto err; - } - - req = ssh_string_from_char("keepalive@openssh.com"); - if (req == NULL) { - goto err; - } - - rc = buffer_add_ssh_string(session->out_buffer, req); - ssh_string_free(req); - if (rc < 0) { - goto err; - } - - rc = buffer_add_u8(session->out_buffer, 1); - if (rc < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bsb", + SSH2_MSG_GLOBAL_REQUEST, + "keepalive@openssh.com", + 1); + if (rc != SSH_OK) { goto err; } @@ -1251,14 +1191,14 @@ int ssh_send_keepalive(ssh_session session) goto err; } - ssh_handle_packets(session, 0); + ssh_handle_packets(session, SSH_TIMEOUT_NONBLOCKING); SSH_LOG(SSH_LOG_PACKET, "Sent a keepalive"); return SSH_OK; err: ssh_set_error_oom(session); - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_ERROR; } diff --git a/libssh/src/session.c b/libssh/src/session.c index d4b3643f..a3b19ede 100644 --- a/libssh/src/session.c +++ b/libssh/src/session.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2005-2008 by Aris Adamantiadis + * Copyright (c) 2005-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -226,7 +226,11 @@ void ssh_free(ssh_session session) { #endif /* _WIN32 */ ssh_key_free(session->srv.dsa_key); + session->srv.dsa_key = NULL; ssh_key_free(session->srv.rsa_key); + session->srv.rsa_key = NULL; + ssh_key_free(session->srv.ecdsa_key); + session->srv.ecdsa_key = NULL; if (session->ssh_message_list) { ssh_message msg; @@ -255,16 +259,20 @@ void ssh_free(ssh_session session) { ssh_list_free(session->opts.identity); } + SAFE_FREE(session->auth_auto_state); SAFE_FREE(session->serverbanner); SAFE_FREE(session->clientbanner); SAFE_FREE(session->banner); SAFE_FREE(session->opts.bindaddr); + SAFE_FREE(session->opts.custombanner); SAFE_FREE(session->opts.username); SAFE_FREE(session->opts.host); SAFE_FREE(session->opts.sshdir); SAFE_FREE(session->opts.knownhosts); SAFE_FREE(session->opts.ProxyCommand); + SAFE_FREE(session->opts.gss_server_identity); + SAFE_FREE(session->opts.gss_client_identity); for (i = 0; i < 10; i++) { if (session->opts.wanted_methods[i]) { @@ -272,11 +280,26 @@ void ssh_free(ssh_session session) { } } - /* burn connection, it could hang sensitive datas */ + /* burn connection, it could contain sensitive data */ BURN_BUFFER(session, sizeof(struct ssh_session_struct)); SAFE_FREE(session); } +/** + * @brief get the client banner + * + * @param[in] session The SSH session + * + * @return Returns the client banner string or NULL. + */ +const char* ssh_get_clientbanner(ssh_session session) { + if (session == NULL) { + return NULL; + } + + return session->clientbanner; +} + /** * @brief get the server banner * @@ -291,6 +314,68 @@ const char* ssh_get_serverbanner(ssh_session session) { return session->serverbanner; } +/** + * @brief get the name of the input cipher for the given session. + * + * @param[in] session The SSH session. + * + * @return Returns cipher name or NULL. + */ +const char* ssh_get_cipher_in(ssh_session session) { + if ((session != NULL) && + (session->current_crypto != NULL) && + (session->current_crypto->in_cipher != NULL)) { + return session->current_crypto->in_cipher->name; + } + return NULL; +} + +/** + * @brief get the name of the output cipher for the given session. + * + * @param[in] session The SSH session. + * + * @return Returns cipher name or NULL. + */ +const char* ssh_get_cipher_out(ssh_session session) { + if ((session != NULL) && + (session->current_crypto != NULL) && + (session->current_crypto->out_cipher != NULL)) { + return session->current_crypto->out_cipher->name; + } + return NULL; +} + +/** + * @brief get the name of the input HMAC algorithm for the given session. + * + * @param[in] session The SSH session. + * + * @return Returns HMAC algorithm name or NULL if unknown. + */ +const char* ssh_get_hmac_in(ssh_session session) { + if ((session != NULL) && + (session->current_crypto != NULL)) { + return ssh_hmac_type_to_string(session->current_crypto->in_hmac); + } + return NULL; +} + +/** + * @brief get the name of the output HMAC algorithm for the given session. + * + * @param[in] session The SSH session. + * + * @return Returns HMAC algorithm name or NULL if unknown. + */ +const char* ssh_get_hmac_out(ssh_session session) { + if ((session != NULL) && + (session->current_crypto != NULL)) { + return ssh_hmac_type_to_string(session->current_crypto->out_hmac); + } + return NULL; +} + /** * @brief Disconnect impolitely from a remote host by closing the socket. * @@ -476,9 +561,7 @@ int ssh_handle_packets(ssh_session session, int timeout) { spoll_in = ssh_socket_get_poll_handle_in(session->socket); spoll_out = ssh_socket_get_poll_handle_out(session->socket); - if (session->server) { - ssh_poll_add_events(spoll_in, POLLIN); - } + ssh_poll_add_events(spoll_in, POLLIN); ctx = ssh_poll_get_ctx(spoll_in); if (!ctx) { @@ -551,7 +634,11 @@ int ssh_handle_packets_termination(ssh_session session, } } - ssh_timestamp_init(&ts); + /* avoid unnecessary syscall for the SSH_TIMEOUT_NONBLOCKING case */ + if (timeout != SSH_TIMEOUT_NONBLOCKING) { + ssh_timestamp_init(&ts); + } + tm = timeout; while(!fct(user)) { ret = ssh_handle_packets(session, tm); @@ -607,6 +694,25 @@ int ssh_get_status(ssh_session session) { return r; } +/** + * @brief Get poll flags for an external mainloop + * + * @param session The ssh session to use. + * + * @returns A bitmask including SSH_READ_PENDING or SSH_WRITE_PENDING. + * For SSH_READ_PENDING, your invocation of poll() should include + * POLLIN. For SSH_WRITE_PENDING, your invocation of poll() should + * include POLLOUT. + */ +int ssh_get_poll_flags(ssh_session session) +{ + if (session == NULL) { + return 0; + } + + return ssh_socket_get_poll_flags (session->socket); +} + /** * @brief Get the disconnect message from the server. * @@ -660,8 +766,13 @@ void ssh_socket_exception_callback(int code, int errno_code, void *user){ ssh_session session=(ssh_session)user; SSH_LOG(SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code); - session->session_state=SSH_SESSION_STATE_ERROR; - ssh_set_error(session,SSH_FATAL,"Socket error: %s",strerror(errno_code)); + session->session_state = SSH_SESSION_STATE_ERROR; + if (errno_code == 0 && code == SSH_SOCKET_EXCEPTION_EOF) { + ssh_set_error(session, SSH_FATAL, "Socket error: disconnected"); + } else { + ssh_set_error(session, SSH_FATAL, "Socket error: %s", strerror(errno_code)); + } + session->ssh_connection_callback(session); } @@ -674,33 +785,26 @@ void ssh_socket_exception_callback(int code, int errno_code, void *user){ * @return SSH_OK on success, SSH_ERROR otherwise. */ int ssh_send_ignore (ssh_session session, const char *data) { - ssh_string str; + int rc; if (ssh_socket_is_open(session->socket)) { - if (buffer_add_u8(session->out_buffer, SSH2_MSG_IGNORE) < 0) { + + rc = ssh_buffer_pack(session->out_buffer, + "bs", + SSH2_MSG_IGNORE, + data); + if (rc != SSH_OK){ + ssh_set_error_oom(session); goto error; } - - str = ssh_string_from_char(data); - if (str == NULL) { - goto error; - } - - if (buffer_add_ssh_string(session->out_buffer, str) < 0) { - ssh_string_free(str); - goto error; - } - packet_send(session); ssh_handle_packets(session, 0); - - ssh_string_free(str); } return SSH_OK; error: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_ERROR; } @@ -716,34 +820,19 @@ error: * @return SSH_OK on success, SSH_ERROR otherwise. */ int ssh_send_debug (ssh_session session, const char *message, int always_display) { - ssh_string str; int rc; if (ssh_socket_is_open(session->socket)) { - if (buffer_add_u8(session->out_buffer, SSH2_MSG_DEBUG) < 0) { + rc = ssh_buffer_pack(session->out_buffer, + "bbsd", + SSH2_MSG_DEBUG, + always_display != 0 ? 1 : 0, + message, + 0); /* empty language tag */ + if (rc != SSH_OK) { + ssh_set_error_oom(session); goto error; } - - if (buffer_add_u8(session->out_buffer, always_display) < 0) { - goto error; - } - - str = ssh_string_from_char(message); - if (str == NULL) { - goto error; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto error; - } - - /* Empty language tag */ - if (buffer_add_u32(session->out_buffer, 0) < 0) { - goto error; - } - packet_send(session); ssh_handle_packets(session, 0); } @@ -751,10 +840,49 @@ int ssh_send_debug (ssh_session session, const char *message, int always_display return SSH_OK; error: - buffer_reinit(session->out_buffer); + ssh_buffer_reinit(session->out_buffer); return SSH_ERROR; } + /** + * @brief Set the session data counters. + * + * This functions sets the counter structures to be used to calculate data + * which comes in and goes out through the session at different levels. + * + * @code + * struct ssh_counter_struct scounter = { + * .in_bytes = 0, + * .out_bytes = 0, + * .in_packets = 0, + * .out_packets = 0 + * }; + * + * struct ssh_counter_struct rcounter = { + * .in_bytes = 0, + * .out_bytes = 0, + * .in_packets = 0, + * .out_packets = 0 + * }; + * + * ssh_set_counters(session, &scounter, &rcounter); + * @endcode + * + * @param[in] session The SSH session. + * + * @param[in] scounter Counter for byte data handled by the session sockets. + * + * @param[in] rcounter Counter for byte and packet data handled by the session, + * prior compression and SSH overhead. + */ +void ssh_set_counters(ssh_session session, ssh_counter scounter, + ssh_counter rcounter) { + if (session != NULL) { + session->socket_counter = scounter; + session->raw_counter = rcounter; + } +} + /** @} */ /* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/sftp.c b/libssh/src/sftp.c index 62460b4d..b57da645 100644 --- a/libssh/src/sftp.c +++ b/libssh/src/sftp.c @@ -4,7 +4,7 @@ * This file is part of the SSH Library * * Copyright (c) 2005-2008 by Aris Adamantiadis - * Copyright (c) 2008-2009 by Andreas Schneider + * Copyright (c) 2008-2009 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -308,7 +308,7 @@ int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload){ } sftp_packet sftp_packet_read(sftp_session sftp) { - unsigned char buffer[4096]; + unsigned char buffer[MAX_BUF_SIZE]; sftp_packet packet = NULL; uint32_t size; int r; @@ -332,7 +332,7 @@ sftp_packet sftp_packet_read(sftp_session sftp) { SAFE_FREE(packet); return NULL; } - buffer_add_data(packet->payload,buffer, r); + ssh_buffer_add_data(packet->payload, buffer, r); if (buffer_get_u32(packet->payload, &size) != sizeof(uint32_t)) { ssh_set_error(sftp->session, SSH_FATAL, "Short sftp packet!"); ssh_buffer_free(packet->payload); @@ -348,7 +348,7 @@ sftp_packet sftp_packet_read(sftp_session sftp) { SAFE_FREE(packet); return NULL; } - buffer_add_data(packet->payload, buffer, r); + ssh_buffer_add_data(packet->payload, buffer, r); buffer_get_u8(packet->payload, &packet->type); size=size-1; while (size>0){ @@ -361,7 +361,7 @@ sftp_packet sftp_packet_read(sftp_session sftp) { SAFE_FREE(packet); return NULL; } - if(buffer_add_data(packet->payload,buffer,r)==SSH_ERROR){ + if (ssh_buffer_add_data(packet->payload, buffer, r) == SSH_ERROR) { ssh_buffer_free(packet->payload); SAFE_FREE(packet); ssh_set_error_oom(sftp->session); @@ -421,6 +421,7 @@ static void sftp_message_free(sftp_message msg) { static sftp_message sftp_get_message(sftp_packet packet) { sftp_session sftp = packet->sftp; sftp_message msg = NULL; + int rc; msg = sftp_message_new(sftp); if (msg == NULL) { @@ -439,7 +440,8 @@ static sftp_message sftp_get_message(sftp_packet packet) { return NULL; } - if (buffer_get_u32(packet->payload, &msg->id) != sizeof(uint32_t)) { + rc = ssh_buffer_unpack(packet->payload, "d", &msg->id); + if (rc != SSH_OK) { ssh_set_error(packet->sftp->session, SSH_FATAL, "Invalid packet %d: no ID", packet->type); sftp_message_free(msg); @@ -451,7 +453,7 @@ static sftp_message sftp_get_message(sftp_packet packet) { msg->id, msg->packet_type); - if (buffer_add_data(msg->payload, buffer_get_rest(packet->payload), + if (ssh_buffer_add_data(msg->payload, buffer_get_rest(packet->payload), buffer_get_rest_len(packet->payload)) < 0) { ssh_set_error_oom(sftp->session); sftp_message_free(msg); @@ -497,11 +499,10 @@ void sftp_packet_free(sftp_packet packet) { int sftp_init(sftp_session sftp) { sftp_packet packet = NULL; ssh_buffer buffer = NULL; - ssh_string ext_name_s = NULL; - ssh_string ext_data_s = NULL; char *ext_name = NULL; char *ext_data = NULL; - uint32_t version = htonl(LIBSFTP_VERSION); + uint32_t version; + int rc; buffer = ssh_buffer_new(); if (buffer == NULL) { @@ -509,7 +510,8 @@ int sftp_init(sftp_session sftp) { return -1; } - if (buffer_add_u32(buffer, version) < 0) { + rc = ssh_buffer_pack(buffer, "d", LIBSFTP_VERSION); + if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); return -1; @@ -533,68 +535,48 @@ int sftp_init(sftp_session sftp) { } /* TODO: are we sure there are 4 bytes ready? */ - buffer_get_u32(packet->payload, &version); - version = ntohl(version); + rc = ssh_buffer_unpack(packet->payload, "d", &version); + if (rc != SSH_OK){ + return -1; + } SSH_LOG(SSH_LOG_RARE, "SFTP server version %d", version); - - ext_name_s = buffer_get_ssh_string(packet->payload); - while (ext_name_s != NULL) { + rc = ssh_buffer_unpack(packet->payload, "s", &ext_name); + while (rc == SSH_OK) { int count = sftp->ext->count; - char **tmp; - ext_data_s = buffer_get_ssh_string(packet->payload); - if (ext_data_s == NULL) { - ssh_string_free(ext_name_s); + rc = ssh_buffer_unpack(packet->payload, "s", &ext_data); + if (rc == SSH_ERROR) { break; } - ext_name = ssh_string_to_char(ext_name_s); - ext_data = ssh_string_to_char(ext_data_s); - if (ext_name == NULL || ext_data == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(ext_name); - SAFE_FREE(ext_data); - ssh_string_free(ext_name_s); - ssh_string_free(ext_data_s); - return -1; - } SSH_LOG(SSH_LOG_RARE, "SFTP server extension: %s, version: %s", ext_name, ext_data); count++; - tmp = realloc(sftp->ext->name, count * sizeof(char *)); - if (tmp == NULL) { + sftp->ext->name = realloc(sftp->ext->name, count * sizeof(char *)); + if (sftp->ext->name == NULL) { ssh_set_error_oom(sftp->session); SAFE_FREE(ext_name); SAFE_FREE(ext_data); - ssh_string_free(ext_name_s); - ssh_string_free(ext_data_s); return -1; } - tmp[count - 1] = ext_name; - sftp->ext->name = tmp; + sftp->ext->name[count - 1] = ext_name; - tmp = realloc(sftp->ext->data, count * sizeof(char *)); - if (tmp == NULL) { + sftp->ext->data = realloc(sftp->ext->data, count * sizeof(char *)); + if (sftp->ext->data == NULL) { ssh_set_error_oom(sftp->session); SAFE_FREE(ext_name); SAFE_FREE(ext_data); - ssh_string_free(ext_name_s); - ssh_string_free(ext_data_s); return -1; } - tmp[count - 1] = ext_data; - sftp->ext->data = tmp; + sftp->ext->data[count - 1] = ext_data; sftp->ext->count = count; - ssh_string_free(ext_name_s); - ssh_string_free(ext_data_s); - - ext_name_s = buffer_get_ssh_string(packet->payload); + rc = ssh_buffer_unpack(packet->payload, "s", &ext_name); } sftp_packet_free(packet); @@ -766,6 +748,7 @@ static inline uint32_t sftp_get_new_id(sftp_session session) { static sftp_status_message parse_status_msg(sftp_message msg){ sftp_status_message status; + int rc; if (msg->packet_type != SSH_FXP_STATUS) { ssh_set_error(msg->sftp->session, SSH_FATAL, @@ -781,34 +764,28 @@ static sftp_status_message parse_status_msg(sftp_message msg){ ZERO_STRUCTP(status); status->id = msg->id; - if (buffer_get_u32(msg->payload,&status->status) != 4){ + rc = ssh_buffer_unpack(msg->payload, "d", + &status->status); + if (rc != SSH_OK){ SAFE_FREE(status); ssh_set_error(msg->sftp->session, SSH_FATAL, "Invalid SSH_FXP_STATUS message"); return NULL; } - status->error = buffer_get_ssh_string(msg->payload); - status->lang = buffer_get_ssh_string(msg->payload); - if(status->error == NULL || status->lang == NULL){ - if(msg->sftp->version >=3){ + rc = ssh_buffer_unpack(msg->payload, "ss", + &status->errormsg, + &status->langmsg); + + if(rc != SSH_OK && msg->sftp->version >=3){ /* These are mandatory from version 3 */ - ssh_string_free(status->error); - /* status->lang never get allocated if something failed */ SAFE_FREE(status); ssh_set_error(msg->sftp->session, SSH_FATAL, "Invalid SSH_FXP_STATUS message"); return NULL; - } } - - status->status = ntohl(status->status); - if(status->error) - status->errormsg = ssh_string_to_char(status->error); - else + if (status->errormsg == NULL) status->errormsg = strdup("No error message in packet"); - if(status->lang) - status->langmsg = ssh_string_to_char(status->lang); - else + if (status->langmsg == NULL) status->langmsg = strdup(""); if (status->errormsg == NULL || status->langmsg == NULL) { ssh_set_error_oom(msg->sftp->session); @@ -824,8 +801,6 @@ static void status_msg_free(sftp_status_message status){ return; } - ssh_string_free(status->error); - ssh_string_free(status->lang); SAFE_FREE(status->errormsg); SAFE_FREE(status->langmsg); SAFE_FREE(status); @@ -886,7 +861,7 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path){ } id = sftp_get_new_id(sftp); - if (buffer_add_u32(payload, id) < 0 || + if (buffer_add_u32(payload, htonl(id)) < 0 || buffer_add_ssh_string(payload, path_s) < 0) { ssh_set_error_oom(sftp->session); ssh_buffer_free(payload); @@ -998,7 +973,7 @@ static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf, break; } attr->owner = ssh_string_to_char(owner); - string_free(owner); + ssh_string_free(owner); if (attr->owner == NULL) { break; } @@ -1008,7 +983,7 @@ static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf, break; } attr->group = ssh_string_to_char(group); - string_free(group); + ssh_string_free(group); if (attr->group == NULL) { break; } @@ -1191,148 +1166,135 @@ static char *sftp_parse_longname(const char *longname, ... more extended data (extended_type - extended_data pairs), so that number of pairs equals extended_count */ static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, - int expectname) { - ssh_string longname; - ssh_string name; - sftp_attributes attr; - uint32_t flags = 0; - int ok = 0; + int expectname) { + sftp_attributes attr; + int rc; - attr = malloc(sizeof(struct sftp_attributes_struct)); - if (attr == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(attr); + attr = malloc(sizeof(struct sftp_attributes_struct)); + if (attr == NULL) { + ssh_set_error_oom(sftp->session); + return NULL; + } + ZERO_STRUCTP(attr); - /* This isn't really a loop, but it is like a try..catch.. */ - do { if (expectname) { - name = buffer_get_ssh_string(buf); - if (name == NULL) { - break; - } - attr->name = ssh_string_to_char(name); - ssh_string_free(name); - if (attr->name == NULL) { - break; - } - - SSH_LOG(SSH_LOG_RARE, "Name: %s", attr->name); - - longname = buffer_get_ssh_string(buf); - if (longname == NULL) { - break; - } - attr->longname = ssh_string_to_char(longname); - ssh_string_free(longname); - if (attr->longname == NULL) { - break; - } - - /* Set owner and group if we talk to openssh and have the longname */ - if (ssh_get_openssh_version(sftp->session)) { - attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER); - if (attr->owner == NULL) { - break; + rc = ssh_buffer_unpack(buf, "ss", + &attr->name, + &attr->longname); + if (rc != SSH_OK){ + goto error; } + SSH_LOG(SSH_LOG_RARE, "Name: %s", attr->name); - attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP); - if (attr->group == NULL) { - break; + /* Set owner and group if we talk to openssh and have the longname */ + if (ssh_get_openssh_version(sftp->session)) { + attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER); + if (attr->owner == NULL) { + goto error; + } + + attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP); + if (attr->group == NULL) { + goto error; + } } - } } - if (buffer_get_u32(buf, &flags) != sizeof(uint32_t)) { - break; + rc = ssh_buffer_unpack(buf, "d", &attr->flags); + if (rc != SSH_OK){ + goto error; } - flags = ntohl(flags); - attr->flags = flags; SSH_LOG(SSH_LOG_RARE, - "Flags: %.8lx\n", (long unsigned int) flags); + "Flags: %.8lx\n", (long unsigned int) attr->flags); - if (flags & SSH_FILEXFER_ATTR_SIZE) { - if(buffer_get_u64(buf, &attr->size) != sizeof(uint64_t)) { - break; - } - attr->size = ntohll(attr->size); - SSH_LOG(SSH_LOG_RARE, - "Size: %llu\n", - (long long unsigned int) attr->size); + if (attr->flags & SSH_FILEXFER_ATTR_SIZE) { + rc = ssh_buffer_unpack(buf, "q", &attr->size); + if(rc != SSH_OK) { + goto error; + } + SSH_LOG(SSH_LOG_RARE, + "Size: %llu\n", + (long long unsigned int) attr->size); } - if (flags & SSH_FILEXFER_ATTR_UIDGID) { - if (buffer_get_u32(buf, &attr->uid) != sizeof(uint32_t)) { - break; - } - if (buffer_get_u32(buf, &attr->gid) != sizeof(uint32_t)) { - break; - } - attr->uid = ntohl(attr->uid); - attr->gid = ntohl(attr->gid); + if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) { + rc = ssh_buffer_unpack(buf, "dd", + &attr->uid, + &attr->gid); + if (rc != SSH_OK){ + goto error; + } } - if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { - if (buffer_get_u32(buf, &attr->permissions) != sizeof(uint32_t)) { - break; - } - attr->permissions = ntohl(attr->permissions); + if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) { + rc = ssh_buffer_unpack(buf, "d", &attr->permissions); + if (rc != SSH_OK){ + goto error; + } - switch (attr->permissions & S_IFMT) { + switch (attr->permissions & S_IFMT) { case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: - attr->type = SSH_FILEXFER_TYPE_SPECIAL; - break; + attr->type = SSH_FILEXFER_TYPE_SPECIAL; + break; case S_IFLNK: - attr->type = SSH_FILEXFER_TYPE_SYMLINK; - break; + attr->type = SSH_FILEXFER_TYPE_SYMLINK; + break; case S_IFREG: - attr->type = SSH_FILEXFER_TYPE_REGULAR; - break; + attr->type = SSH_FILEXFER_TYPE_REGULAR; + break; case S_IFDIR: - attr->type = SSH_FILEXFER_TYPE_DIRECTORY; - break; + attr->type = SSH_FILEXFER_TYPE_DIRECTORY; + break; default: - attr->type = SSH_FILEXFER_TYPE_UNKNOWN; - break; - } + attr->type = SSH_FILEXFER_TYPE_UNKNOWN; + break; + } } - if (flags & SSH_FILEXFER_ATTR_ACMODTIME) { - if (buffer_get_u32(buf, &attr->atime) != sizeof(uint32_t)) { - break; - } - attr->atime = ntohl(attr->atime); - if (buffer_get_u32(buf, &attr->mtime) != sizeof(uint32_t)) { - break; - } - attr->mtime = ntohl(attr->mtime); + if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) { + rc = ssh_buffer_unpack(buf, "dd", + &attr->atime, + &attr->mtime); + if (rc != SSH_OK){ + goto error; + } } - if (flags & SSH_FILEXFER_ATTR_EXTENDED) { - if (buffer_get_u32(buf, &attr->extended_count) != sizeof(uint32_t)) { - break; - } + if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) { + rc = ssh_buffer_unpack(buf, "d", &attr->extended_count); + if (rc != SSH_OK){ + goto error; + } - attr->extended_count = ntohl(attr->extended_count); - while (attr->extended_count && - (attr->extended_type = buffer_get_ssh_string(buf)) - && (attr->extended_data = buffer_get_ssh_string(buf))) { - attr->extended_count--; - } + if (attr->extended_count > 0){ + rc = ssh_buffer_unpack(buf, "ss", + &attr->extended_type, + &attr->extended_data); + if (rc != SSH_OK){ + goto error; + } + attr->extended_count--; + } + /* just ignore the remaining extensions */ - if (attr->extended_count) { - break; - } + while (attr->extended_count > 0){ + ssh_string tmp1,tmp2; + rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2); + if (rc != SSH_OK){ + goto error; + } + SAFE_FREE(tmp1); + SAFE_FREE(tmp2); + attr->extended_count--; + } } - ok = 1; - } while (0); - if (!ok) { - /* break issued somewhere */ + return attr; + + error: ssh_string_free(attr->extended_type); ssh_string_free(attr->extended_data); SAFE_FREE(attr->name); @@ -1340,55 +1302,53 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, SAFE_FREE(attr->owner); SAFE_FREE(attr->group); SAFE_FREE(attr); - ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure"); return NULL; - } - - /* everything went smoothly */ - return attr; } /* FIXME is this really needed as a public function? */ int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr) { uint32_t flags = (attr ? attr->flags : 0); + int rc; flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); - if (buffer_add_u32(buffer, htonl(flags)) < 0) { + rc = ssh_buffer_pack(buffer, "d", flags); + if (rc != SSH_OK) { return -1; } - if (attr) { + if (attr != NULL) { if (flags & SSH_FILEXFER_ATTR_SIZE) { - if (buffer_add_u64(buffer, htonll(attr->size)) < 0) { + rc = ssh_buffer_pack(buffer, "q", attr->size); + if (rc != SSH_OK) { return -1; } } if (flags & SSH_FILEXFER_ATTR_UIDGID) { - if (buffer_add_u32(buffer,htonl(attr->uid)) < 0 || - buffer_add_u32(buffer,htonl(attr->gid)) < 0) { + rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid); + if (rc != SSH_OK) { return -1; } } if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { - if (buffer_add_u32(buffer, htonl(attr->permissions)) < 0) { + rc = ssh_buffer_pack(buffer, "d", attr->permissions); + if (rc != SSH_OK) { return -1; } } if (flags & SSH_FILEXFER_ATTR_ACMODTIME) { - if (buffer_add_u32(buffer, htonl(attr->atime)) < 0 || - buffer_add_u32(buffer, htonl(attr->mtime)) < 0) { + rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime); + if (rc != SSH_OK) { return -1; } } } - return 0; } @@ -1433,7 +1393,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) { } id = sftp_get_new_id(sftp); - if (buffer_add_u32(payload, id) < 0 || + if (buffer_add_u32(payload, htonl(id)) < 0 || buffer_add_ssh_string(payload, dir->handle) < 0) { ssh_set_error_oom(sftp->session); ssh_buffer_free(payload); @@ -1557,7 +1517,7 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle) { } id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, handle) < 0) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); @@ -1681,7 +1641,7 @@ sftp_file sftp_open(sftp_session sftp, const char *file, int flags, sftp_flags |= SSH_FXF_EXCL; SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags); id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, filename) < 0) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); @@ -1751,6 +1711,7 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { ssh_string datastring; ssh_buffer buffer; int id; + int rc; if (handle->eof) { return 0; @@ -1761,11 +1722,16 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { ssh_set_error_oom(sftp->session); return -1; } + id = sftp_get_new_id(handle->sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, handle->handle) < 0 || - buffer_add_u64(buffer, htonll(handle->offset)) < 0 || - buffer_add_u32(buffer,htonl(count)) < 0) { + + rc = ssh_buffer_pack(buffer, + "dSqd", + id, + handle->handle, + handle->offset, + count); + if (rc != SSH_OK){ ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); return -1; @@ -1847,6 +1813,7 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){ sftp_session sftp = file->sftp; ssh_buffer buffer; uint32_t id; + int rc; buffer = ssh_buffer_new(); if (buffer == NULL) { @@ -1855,10 +1822,14 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){ } id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, file->handle) < 0 || - buffer_add_u64(buffer, htonll(file->offset)) < 0 || - buffer_add_u32(buffer, htonl(len)) < 0) { + + rc = ssh_buffer_pack(buffer, + "dSqd", + id, + file->handle, + file->offset, + len); + if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); return -1; @@ -1961,11 +1932,11 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { sftp_session sftp = file->sftp; sftp_message msg = NULL; sftp_status_message status; - ssh_string datastring; ssh_buffer buffer; uint32_t id; int len; int packetlen; + int rc; buffer = ssh_buffer_new(); if (buffer == NULL) { @@ -1973,25 +1944,20 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { return -1; } - datastring = ssh_string_new(count); - if (datastring == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - ssh_string_fill(datastring, buf, count); - id = sftp_get_new_id(file->sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, file->handle) < 0 || - buffer_add_u64(buffer, htonll(file->offset)) < 0 || - buffer_add_ssh_string(buffer, datastring) < 0) { + + rc = ssh_buffer_pack(buffer, + "dSqdP", + id, + file->handle, + file->offset, + count, /* len of datastring */ + (size_t)count, buf); + if (rc != SSH_OK){ ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); - ssh_string_free(datastring); return -1; } - ssh_string_free(datastring); packetlen=buffer_get_rest_len(buffer); len = sftp_packet_write(file->sftp, SSH_FXP_WRITE, buffer); ssh_buffer_free(buffer); @@ -2083,9 +2049,9 @@ void sftp_rewind(sftp_file file) { int sftp_unlink(sftp_session sftp, const char *file) { sftp_status_message status = NULL; sftp_message msg = NULL; - ssh_string filename; ssh_buffer buffer; uint32_t id; + int rc; buffer = ssh_buffer_new(); if (buffer == NULL) { @@ -2093,27 +2059,22 @@ int sftp_unlink(sftp_session sftp, const char *file) { return -1; } - filename = ssh_string_from_char(file); - if (filename == NULL) { + id = sftp_get_new_id(sftp); + + rc = ssh_buffer_pack(buffer, + "ds", + id, + file); + if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); return -1; } - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, filename) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(filename); - return -1; - } if (sftp_packet_write(sftp, SSH_FXP_REMOVE, buffer) < 0) { ssh_buffer_free(buffer); - ssh_string_free(filename); return -1; } - ssh_string_free(filename); ssh_buffer_free(buffer); while (msg == NULL) { @@ -2160,9 +2121,9 @@ int sftp_unlink(sftp_session sftp, const char *file) { int sftp_rmdir(sftp_session sftp, const char *directory) { sftp_status_message status = NULL; sftp_message msg = NULL; - ssh_string filename; ssh_buffer buffer; uint32_t id; + int rc; buffer = ssh_buffer_new(); if (buffer == NULL) { @@ -2170,28 +2131,22 @@ int sftp_rmdir(sftp_session sftp, const char *directory) { return -1; } - filename = ssh_string_from_char(directory); - if (filename == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, filename) < 0) { + + rc = ssh_buffer_pack(buffer, + "ds", + id, + directory); + if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); - ssh_string_free(filename); return -1; } if (sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer) < 0) { ssh_buffer_free(buffer); - ssh_string_free(filename); return -1; } ssh_buffer_free(buffer); - ssh_string_free(filename); while (msg == NULL) { if (sftp_read_and_dispatch(sftp) < 0) { @@ -2258,7 +2213,7 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) { attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, path) < 0 || buffer_add_attributes(buffer, &attr) < 0 || sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer) < 0) { @@ -2327,9 +2282,8 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) { sftp_status_message status = NULL; sftp_message msg = NULL; ssh_buffer buffer; - ssh_string oldpath; - ssh_string newpath; uint32_t id; + int rc; buffer = ssh_buffer_new(); if (buffer == NULL) { @@ -2337,43 +2291,30 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) { return -1; } - oldpath = ssh_string_from_char(original); - if (oldpath == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - newpath = ssh_string_from_char(newname); - if (newpath == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(oldpath); - return -1; - } - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, oldpath) < 0 || - buffer_add_ssh_string(buffer, newpath) < 0 || + + rc = ssh_buffer_pack(buffer, + "dss", + id, + original, + newname); + if (rc != SSH_OK) { + ssh_set_error_oom(sftp->session); + ssh_buffer_free(buffer); + return -1; + } + + if (sftp->version >= 4){ /* POSIX rename atomically replaces newpath, we should do the same * only available on >=v4 */ - sftp->version>=4 ? (buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE) < 0):0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(oldpath); - ssh_string_free(newpath); - return -1; + buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE); } + if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) { ssh_buffer_free(buffer); - ssh_string_free(oldpath); - ssh_string_free(newpath); return -1; } ssh_buffer_free(buffer); - ssh_string_free(oldpath); - ssh_string_free(newpath); while (msg == NULL) { if (sftp_read_and_dispatch(sftp) < 0) { @@ -2438,7 +2379,7 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) { } id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, path) < 0 || buffer_add_attributes(buffer, attr) < 0) { ssh_set_error_oom(sftp->session); @@ -2537,10 +2478,9 @@ int sftp_utimes(sftp_session sftp, const char *file, int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { sftp_status_message status = NULL; sftp_message msg = NULL; - ssh_string target_s; - ssh_string dest_s; ssh_buffer buffer; uint32_t id; + int rc; if (sftp == NULL) return -1; @@ -2555,59 +2495,33 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { return -1; } - target_s = ssh_string_from_char(target); - if (target_s == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - dest_s = ssh_string_from_char(dest); - if (dest_s == NULL) { - ssh_set_error_oom(sftp->session); - ssh_string_free(target_s); - ssh_buffer_free(buffer); - return -1; - } - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); - return -1; - } + + /* TODO check for version number if they ever fix it. */ if (ssh_get_openssh_version(sftp->session)) { - /* TODO check for version number if they ever fix it. */ - if (buffer_add_ssh_string(buffer, target_s) < 0 || - buffer_add_ssh_string(buffer, dest_s) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); - return -1; - } + rc = ssh_buffer_pack(buffer, + "dss", + id, + target, + dest); } else { - if (buffer_add_ssh_string(buffer, dest_s) < 0 || - buffer_add_ssh_string(buffer, target_s) < 0) { + rc = ssh_buffer_pack(buffer, + "dss", + id, + dest, + target); + } + if (rc != SSH_OK){ ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); return -1; - } } if (sftp_packet_write(sftp, SSH_FXP_SYMLINK, buffer) < 0) { ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); return -1; } ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); while (msg == NULL) { if (sftp_read_and_dispatch(sftp) < 0) { @@ -2682,7 +2596,7 @@ char *sftp_readlink(sftp_session sftp, const char *path) { } id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, path_s) < 0) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); @@ -2737,9 +2651,8 @@ char *sftp_readlink(sftp_session sftp, const char *path) { } static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) { - sftp_statvfs_t statvfs; - uint64_t tmp; - int ok = 0; + sftp_statvfs_t statvfs; + int rc; statvfs = malloc(sizeof(struct sftp_statvfs_struct)); if (statvfs == NULL) { @@ -2748,78 +2661,20 @@ static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) { } ZERO_STRUCTP(statvfs); - /* try .. catch */ - do { - /* file system block size */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_bsize = ntohll(tmp); - - /* fundamental fs block size */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_frsize = ntohll(tmp); - - /* number of blocks (unit f_frsize) */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_blocks = ntohll(tmp); - - /* free blocks in file system */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_bfree = ntohll(tmp); - - /* free blocks for non-root */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_bavail = ntohll(tmp); - - /* total file inodes */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_files = ntohll(tmp); - - /* free file inodes */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_ffree = ntohll(tmp); - - /* free file inodes for to non-root */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_favail = ntohll(tmp); - - /* file system id */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_fsid = ntohll(tmp); - - /* bit mask of f_flag values */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_flag = ntohll(tmp); - - /* maximum filename length */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_namemax = ntohll(tmp); - - ok = 1; - } while(0); - - if (!ok) { + rc = ssh_buffer_unpack(buf, "qqqqqqqqqqq", + &statvfs->f_bsize, /* file system block size */ + &statvfs->f_frsize, /* fundamental fs block size */ + &statvfs->f_blocks, /* number of blocks (unit f_frsize) */ + &statvfs->f_bfree, /* free blocks in file system */ + &statvfs->f_bavail, /* free blocks for non-root */ + &statvfs->f_files, /* total file inodes */ + &statvfs->f_ffree, /* free file inodes */ + &statvfs->f_favail, /* free file inodes for to non-root */ + &statvfs->f_fsid, /* file system id */ + &statvfs->f_flag, /* bit mask of f_flag values */ + &statvfs->f_namemax/* maximum filename length */ + ); + if (rc != SSH_OK) { SAFE_FREE(statvfs); ssh_set_error(sftp->session, SSH_FATAL, "Invalid statvfs structure"); return NULL; @@ -2869,7 +2724,7 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) { } id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, ext) < 0 || buffer_add_ssh_string(buffer, pathstr) < 0) { ssh_set_error_oom(sftp->session); @@ -2948,7 +2803,7 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) { } id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, ext) < 0 || buffer_add_ssh_string(buffer, file->handle) < 0) { ssh_set_error_oom(sftp->session); @@ -3037,7 +2892,7 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path) { } id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, pathstr) < 0) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); @@ -3115,7 +2970,7 @@ static sftp_attributes sftp_xstat(sftp_session sftp, const char *path, } id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, pathstr) < 0) { ssh_set_error_oom(sftp->session); ssh_buffer_free(buffer); @@ -3182,7 +3037,7 @@ sftp_attributes sftp_fstat(sftp_file file) { } id = sftp_get_new_id(file->sftp); - if (buffer_add_u32(buffer, id) < 0 || + if (buffer_add_u32(buffer, htonl(id)) < 0 || buffer_add_ssh_string(buffer, file->handle) < 0) { ssh_set_error_oom(file->sftp->session); ssh_buffer_free(buffer); diff --git a/libssh/src/sftpserver.c b/libssh/src/sftpserver.c index 0986b6ce..60498794 100644 --- a/libssh/src/sftpserver.c +++ b/libssh/src/sftpserver.c @@ -42,7 +42,7 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { sftp_packet packet; sftp_client_message msg; ssh_buffer payload; - ssh_string tmp; + int rc; msg = malloc(sizeof (struct sftp_client_message_struct)); if (msg == NULL) { @@ -64,7 +64,9 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { /* take a copy of the whole packet */ msg->complete_message = ssh_buffer_new(); - buffer_add_data(msg->complete_message, buffer_get_rest(payload), buffer_get_rest_len(payload)); + ssh_buffer_add_data(msg->complete_message, + buffer_get_rest(payload), + buffer_get_rest_len(payload)); buffer_get_u32(payload, &msg->id); @@ -79,25 +81,24 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { } break; case SSH_FXP_READ: - msg->handle = buffer_get_ssh_string(payload); - if (msg->handle == NULL) { + rc = ssh_buffer_unpack(payload, + "Sqd", + &msg->handle, + &msg->offset, + &msg->len); + if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } - buffer_get_u64(payload, &msg->offset); - buffer_get_u32(payload, &msg->len); break; case SSH_FXP_WRITE: - msg->handle = buffer_get_ssh_string(payload); - if (msg->handle == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - buffer_get_u64(payload, &msg->offset); - msg->data = buffer_get_ssh_string(payload); - if (msg->data == NULL) { + rc = ssh_buffer_unpack(payload, + "SqS", + &msg->handle, + &msg->offset, + &msg->data); + if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; @@ -108,15 +109,10 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { case SSH_FXP_OPENDIR: case SSH_FXP_READLINK: case SSH_FXP_REALPATH: - tmp = buffer_get_ssh_string(payload); - if (tmp == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - msg->filename = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (msg->filename == NULL) { + rc = ssh_buffer_unpack(payload, + "s", + &msg->filename); + if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; @@ -124,21 +120,11 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { break; case SSH_FXP_RENAME: case SSH_FXP_SYMLINK: - tmp = buffer_get_ssh_string(payload); - if (tmp == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - msg->filename = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (msg->filename == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - msg->data = buffer_get_ssh_string(payload); - if (msg->data == NULL) { + rc = ssh_buffer_unpack(payload, + "sS", + &msg->filename, + &msg->data); + if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; @@ -146,15 +132,10 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { break; case SSH_FXP_MKDIR: case SSH_FXP_SETSTAT: - tmp = buffer_get_ssh_string(payload); - if (tmp == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - msg->filename=ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (msg->filename == NULL) { + rc = ssh_buffer_unpack(payload, + "s", + &msg->filename); + if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; @@ -182,38 +163,28 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { break; case SSH_FXP_LSTAT: case SSH_FXP_STAT: - tmp = buffer_get_ssh_string(payload); - if (tmp == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - msg->filename = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (msg->filename == NULL) { + rc = ssh_buffer_unpack(payload, + "s", + &msg->filename); + if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } if(sftp->version > 3) { - buffer_get_u32(payload,&msg->flags); + ssh_buffer_unpack(payload, "d", &msg->flags); } break; case SSH_FXP_OPEN: - tmp=buffer_get_ssh_string(payload); - if (tmp == NULL) { + rc = ssh_buffer_unpack(payload, + "sd", + &msg->filename, + &msg->flags); + if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } - msg->filename = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (msg->filename == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - buffer_get_u32(payload,&msg->flags); msg->attr = sftp_parse_attr(sftp, payload, 0); if (msg->attr == NULL) { ssh_set_error_oom(session); @@ -222,13 +193,15 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { } break; case SSH_FXP_FSTAT: - msg->handle = buffer_get_ssh_string(payload); - if (msg->handle == NULL) { + rc = ssh_buffer_unpack(payload, + "Sd", + &msg->handle, + &msg->flags); + if (rc != SSH_OK) { ssh_set_error_oom(session); sftp_client_message_free(msg); return NULL; } - buffer_get_u32(payload, &msg->flags); break; default: ssh_set_error(sftp->session, SSH_FATAL, @@ -237,9 +210,6 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { return NULL; } - msg->flags = ntohl(msg->flags); - msg->offset = ntohll(msg->offset); - msg->len = ntohl(msg->len); sftp_packet_free(packet); return msg; @@ -407,7 +377,7 @@ int sftp_reply_names(sftp_client_message msg) { if (buffer_add_u32(out, msg->id) < 0 || buffer_add_u32(out, htonl(msg->attr_num)) < 0 || - buffer_add_data(out, buffer_get_rest(msg->attrbuf), + ssh_buffer_add_data(out, buffer_get_rest(msg->attrbuf), buffer_get_rest_len(msg->attrbuf)) < 0 || sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) { ssh_buffer_free(out); @@ -466,7 +436,7 @@ int sftp_reply_data(sftp_client_message msg, const void *data, int len) { if (buffer_add_u32(out, msg->id) < 0 || buffer_add_u32(out, ntohl(len)) < 0 || - buffer_add_data(out, data, len) < 0 || + ssh_buffer_add_data(out, data, len) < 0 || sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) { ssh_buffer_free(out); return -1; diff --git a/libssh/src/socket.c b/libssh/src/socket.c index c76ef5ae..498da77e 100644 --- a/libssh/src/socket.c +++ b/libssh/src/socket.c @@ -182,8 +182,8 @@ void ssh_socket_reset(ssh_socket s){ s->fd_out= SSH_INVALID_SOCKET; s->last_errno = -1; s->fd_is_socket = 1; - buffer_reinit(s->in_buffer); - buffer_reinit(s->out_buffer); + ssh_buffer_reinit(s->in_buffer); + ssh_buffer_reinit(s->out_buffer); s->read_wontblock = 0; s->write_wontblock = 0; s->data_except = 0; @@ -204,7 +204,7 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){ } /** - * @brief SSH poll callback. This callback will be used when an event + * @brief SSH poll callback. This callback will be used when an event * caught on the socket. * * @param p Poll object this callback belongs to. @@ -216,123 +216,131 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){ * @return 0 on success, < 0 when the poll object has been removed * from its poll context. */ -int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s){ - ssh_socket s=(ssh_socket )v_s; - char buffer[4096]; - int r; - int err=0; - socklen_t errlen=sizeof(err); - /* Do not do anything if this socket was already closed */ - if(!ssh_socket_is_open(s)){ - return -1; - } - if(revents & POLLERR || revents & POLLHUP){ - /* Check if we are in a connecting state */ - if(s->state==SSH_SOCKET_CONNECTING){ - s->state=SSH_SOCKET_ERROR; - r = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); +int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, + int revents, void *v_s) { + ssh_socket s = (ssh_socket)v_s; + char buffer[MAX_BUF_SIZE]; + int r; + int err = 0; + socklen_t errlen = sizeof(err); + /* Do not do anything if this socket was already closed */ + if (!ssh_socket_is_open(s)) { + return -1; + } + if (revents & POLLERR || revents & POLLHUP) { + /* Check if we are in a connecting state */ + if (s->state == SSH_SOCKET_CONNECTING) { + s->state = SSH_SOCKET_ERROR; + r = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); if (r < 0) { err = errno; } - s->last_errno=err; - ssh_socket_close(s); - if(s->callbacks && s->callbacks->connected) - s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,err, - s->callbacks->userdata); - return -1; - } - /* Then we are in a more standard kind of error */ - /* force a read to get an explanation */ - revents |= POLLIN; - } - if(revents & POLLIN){ - s->read_wontblock=1; - r=ssh_socket_unbuffered_read(s,buffer,sizeof(buffer)); - if(r<0){ - if(p != NULL) { - ssh_poll_remove_events(p, POLLIN); - } - if(s->callbacks && s->callbacks->exception){ - s->callbacks->exception( - SSH_SOCKET_EXCEPTION_ERROR, - s->last_errno,s->callbacks->userdata); - /* p may have been freed, so don't use it - * anymore in this function */ - p = NULL; - return -2; - } - } - if(r==0){ - if(p != NULL) { - ssh_poll_remove_events(p, POLLIN); - } - if(p != NULL) { - ssh_poll_remove_events(p, POLLIN); - } - if(s->callbacks && s->callbacks->exception){ - s->callbacks->exception( - SSH_SOCKET_EXCEPTION_EOF, - 0,s->callbacks->userdata); - /* p may have been freed, so don't use it - * anymore in this function */ - p = NULL; - return -2; - } - } - if(r>0){ - /* Bufferize the data and then call the callback */ - r = buffer_add_data(s->in_buffer,buffer,r); + s->last_errno = err; + ssh_socket_close(s); + if (s->callbacks && s->callbacks->connected) { + s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR, err, + s->callbacks->userdata); + } + return -1; + } + /* Then we are in a more standard kind of error */ + /* force a read to get an explanation */ + revents |= POLLIN; + } + if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) { + s->read_wontblock = 1; + r = ssh_socket_unbuffered_read(s, buffer, sizeof(buffer)); + if (r < 0) { + if (p != NULL) { + ssh_poll_remove_events(p, POLLIN); + } + if (s->callbacks && s->callbacks->exception) { + s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR, + s->last_errno, s->callbacks->userdata); + /* p may have been freed, so don't use it + * anymore in this function */ + p = NULL; + return -2; + } + } + if (r == 0) { + if (p != NULL) { + ssh_poll_remove_events(p, POLLIN); + } + if (p != NULL) { + ssh_poll_remove_events(p, POLLIN); + } + if (s->callbacks && s->callbacks->exception) { + s->callbacks->exception(SSH_SOCKET_EXCEPTION_EOF, + 0, s->callbacks->userdata); + /* p may have been freed, so don't use it + * anymore in this function */ + p = NULL; + return -2; + } + } + if (r > 0) { + if (s->session->socket_counter != NULL) { + s->session->socket_counter->in_bytes += r; + } + /* Bufferize the data and then call the callback */ + r = ssh_buffer_add_data(s->in_buffer, buffer, r); if (r < 0) { return -1; } - if(s->callbacks && s->callbacks->data){ - do { - r= s->callbacks->data(buffer_get_rest(s->in_buffer), - buffer_get_rest_len(s->in_buffer), - s->callbacks->userdata); - buffer_pass_bytes(s->in_buffer,r); - } while (r > 0); - /* p may have been freed, so don't use it - * anymore in this function */ - p = NULL; - } - } - } + if (s->callbacks && s->callbacks->data) { + do { + r = s->callbacks->data(buffer_get_rest(s->in_buffer), + buffer_get_rest_len(s->in_buffer), + s->callbacks->userdata); + buffer_pass_bytes(s->in_buffer, r); + } while ((r > 0) && (s->state == SSH_SOCKET_CONNECTED)); + /* p may have been freed, so don't use it + * anymore in this function */ + p = NULL; + } + } + } #ifdef _WIN32 - if(revents & POLLOUT || revents & POLLWRNORM){ + if (revents & POLLOUT || revents & POLLWRNORM) { #else - if(revents & POLLOUT){ + if (revents & POLLOUT) { #endif - /* First, POLLOUT is a sign we may be connected */ - if(s->state == SSH_SOCKET_CONNECTING){ - SSH_LOG(SSH_LOG_PACKET,"Received POLLOUT in connecting state"); - s->state = SSH_SOCKET_CONNECTED; - ssh_poll_set_events(p,POLLOUT | POLLIN); + /* First, POLLOUT is a sign we may be connected */ + if (s->state == SSH_SOCKET_CONNECTING) { + SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state"); + s->state = SSH_SOCKET_CONNECTED; + if (p != NULL) { + ssh_poll_set_events(p, POLLOUT | POLLIN); + } r = ssh_socket_set_blocking(ssh_socket_get_fd_in(s)); if (r < 0) { return -1; } - if(s->callbacks && s->callbacks->connected) - s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); - return 0; - } - /* So, we can write data */ - s->write_wontblock=1; - if(p != NULL) { + if (s->callbacks && s->callbacks->connected) { + s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, + s->callbacks->userdata); + } + return 0; + } + /* So, we can write data */ + s->write_wontblock=1; + if (p != NULL) { ssh_poll_remove_events(p, POLLOUT); } - /* If buffered data is pending, write it */ - if(buffer_get_rest_len(s->out_buffer) > 0){ - ssh_socket_nonblocking_flush(s); - } else if(s->callbacks && s->callbacks->controlflow){ - /* Otherwise advertise the upper level that write can be done */ - s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,s->callbacks->userdata); - } - /* TODO: Find a way to put back POLLOUT when buffering occurs */ - } - /* Return -1 if one of the poll handlers disappeared */ - return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0; + /* If buffered data is pending, write it */ + if (buffer_get_rest_len(s->out_buffer) > 0) { + ssh_socket_nonblocking_flush(s); + } else if (s->callbacks && s->callbacks->controlflow) { + /* Otherwise advertise the upper level that write can be done */ + s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK, + s->callbacks->userdata); + } + /* TODO: Find a way to put back POLLOUT when buffering occurs */ + } + /* Return -1 if one of the poll handlers disappeared */ + return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0; } /** @internal @@ -438,6 +446,8 @@ void ssh_socket_close(ssh_socket s){ ssh_poll_free(s->poll_out); s->poll_out=NULL; } + + s->state = SSH_SOCKET_CLOSED; } /** @@ -449,9 +459,19 @@ void ssh_socket_close(ssh_socket s){ * file descriptors */ void ssh_socket_set_fd(ssh_socket s, socket_t fd) { - s->fd_in = s->fd_out = fd; - if(s->poll_in) - ssh_poll_set_fd(s->poll_in,fd); + s->fd_in = s->fd_out = fd; + + if (s->poll_in) { + ssh_poll_set_fd(s->poll_in,fd); + } else { + s->state = SSH_SOCKET_CONNECTING; + + /* POLLOUT is the event to wait for in a nonblocking connect */ + ssh_poll_set_events(ssh_socket_get_poll_handle_in(s), POLLOUT); +#ifdef _WIN32 + ssh_poll_add_events(ssh_socket_get_poll_handle_in(s), POLLWRNORM); +#endif + } } /** @@ -594,7 +614,7 @@ void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) { */ int ssh_socket_write(ssh_socket s, const void *buffer, int len) { if(len > 0) { - if (buffer_add_data(s->out_buffer, buffer, len) < 0) { + if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) { ssh_set_error_oom(s->session); return SSH_ERROR; } @@ -645,6 +665,9 @@ int ssh_socket_nonblocking_flush(ssh_socket s) { return SSH_ERROR; } buffer_pass_bytes(s->out_buffer, w); + if (s->session->socket_counter != NULL) { + s->session->socket_counter->out_bytes += w; + } } /* Is there some data pending? */ @@ -695,11 +718,11 @@ int ssh_socket_buffered_write_bytes(ssh_socket s){ int ssh_socket_get_status(ssh_socket s) { int r = 0; - if (buffer_get_len(s->in_buffer) > 0) { + if (ssh_buffer_get_len(s->in_buffer) > 0) { r |= SSH_READ_PENDING; } - if (buffer_get_len(s->out_buffer) > 0) { + if (ssh_buffer_get_len(s->out_buffer) > 0) { r |= SSH_WRITE_PENDING; } @@ -710,6 +733,17 @@ int ssh_socket_get_status(ssh_socket s) { return r; } +int ssh_socket_get_poll_flags(ssh_socket s) { + int r = 0; + if (s->poll_in != NULL && (ssh_poll_get_events (s->poll_in) & POLLIN) > 0) { + r |= SSH_READ_PENDING; + } + if (s->poll_out != NULL && (ssh_poll_get_events (s->poll_out) & POLLOUT) > 0) { + r |= SSH_WRITE_PENDING; + } + return r; +} + #ifdef _WIN32 int ssh_socket_set_nonblocking(socket_t fd) { u_long nonblocking = 1; @@ -759,12 +793,6 @@ int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bin if(fd == SSH_INVALID_SOCKET) return SSH_ERROR; ssh_socket_set_fd(s,fd); - s->state=SSH_SOCKET_CONNECTING; - /* POLLOUT is the event to wait for in a nonblocking connect */ - ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLOUT); -#ifdef _WIN32 - ssh_poll_add_events(ssh_socket_get_poll_handle_in(s),POLLWRNORM); -#endif return SSH_OK; } diff --git a/libssh/src/string.c b/libssh/src/string.c index 5ef90b0e..9002478f 100644 --- a/libssh/src/string.c +++ b/libssh/src/string.c @@ -235,10 +235,11 @@ struct ssh_string_struct *ssh_string_copy(struct ssh_string_struct *s) { * @param[in] s The string to burn. */ void ssh_string_burn(struct ssh_string_struct *s) { - if (s == NULL) { - return; - } - memset(s->data, 'X', ssh_string_len(s)); + if (s == NULL || s->size == 0) { + return; + } + + BURN_BUFFER(s->data, ssh_string_len(s)); } /** diff --git a/libssh/src/threads.c b/libssh/src/threads.c index 107c65d2..7f3a304e 100644 --- a/libssh/src/threads.c +++ b/libssh/src/threads.c @@ -59,8 +59,28 @@ struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void) { static struct ssh_threads_callbacks_struct *user_callbacks =&ssh_threads_noop; #ifdef HAVE_LIBGCRYPT +#if (GCRYPT_VERSION_NUMBER >= 0x010600) +/* libgcrypt >= 1.6 does not support custom callbacks */ +GCRY_THREAD_OPTION_PTHREAD_IMPL; -/* Libgcrypt specific way of handling thread callbacks */ +static int libgcrypt_thread_init(void){ + if(user_callbacks == NULL) + return SSH_ERROR; + if(user_callbacks == &ssh_threads_noop) + return SSH_OK; + if (strcmp(user_callbacks->type, "threads_pthread") == 0){ + gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + return SSH_OK; + } else { + /* not supported */ + SSH_LOG(SSH_LOG_WARN, "Custom thread handlers not supported with libgcrypt >=1.6, using pthreads"); + gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + return SSH_OK; + } +} + +#else +/* Libgcrypt < 1.6 specific way of handling thread callbacks */ static struct gcry_thread_cbs gcrypt_threads_callbacks; @@ -79,7 +99,8 @@ static int libgcrypt_thread_init(void){ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcrypt_threads_callbacks); return SSH_OK; } -#else +#endif /* GCRYPT_VERSION_NUMBER */ +#else /* HAVE_LIBGCRYPT */ /* Libcrypto specific stuff */ diff --git a/libssh/src/threads/CMakeLists.txt b/libssh/src/threads/CMakeLists.txt index b95525e4..a32d601e 100644 --- a/libssh/src/threads/CMakeLists.txt +++ b/libssh/src/threads/CMakeLists.txt @@ -53,73 +53,75 @@ include_directories( ${LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS} ) -add_library(${LIBSSH_THREADS_SHARED_LIBRARY} SHARED ${libssh_threads_SRCS}) +if (libssh_threads_SRCS) + add_library(${LIBSSH_THREADS_SHARED_LIBRARY} SHARED ${libssh_threads_SRCS}) -target_link_libraries(${LIBSSH_THREADS_SHARED_LIBRARY} ${LIBSSH_THREADS_LINK_LIBRARIES}) + target_link_libraries(${LIBSSH_THREADS_SHARED_LIBRARY} ${LIBSSH_THREADS_LINK_LIBRARIES}) -set_target_properties( - ${LIBSSH_THREADS_SHARED_LIBRARY} - PROPERTIES - VERSION - ${LIBRARY_VERSION} - SOVERSION - ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh_threads - DEFINE_SYMBOL - LIBSSH_EXPORTS -) - -if (WITH_VISIBILITY_HIDDEN) - set_target_properties(${LIBSSH_THREADS_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") -endif (WITH_VISIBILITY_HIDDEN) - -install( - TARGETS - ${LIBSSH_THREADS_SHARED_LIBRARY} - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - COMPONENT libraries -) - -if (WITH_STATIC_LIB) - add_library(${LIBSSH_THREADS_STATIC_LIBRARY} STATIC ${libssh_threads_SRCS}) - - if (MSVC) - set(OUTPUT_SUFFIX static) - else (MSVC) - set(OUTPUT_SUFFIX ) - endif (MSVC) - - set_target_properties( - ${LIBSSH_THREADS_STATIC_LIBRARY} - PROPERTIES - VERSION - ${LIBRARY_VERSION} - SOVERSION - ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh_threads - ARCHIVE_OUTPUT_DIRECTORY - ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX} - ) - - if (WIN32) set_target_properties( - ${LIBSSH_THREADS_STATIC_LIBRARY} - PROPERTIES - COMPILE_FLAGS - "-DLIBSSH_STATIC" - ) - endif (WIN32) + ${LIBSSH_THREADS_SHARED_LIBRARY} + PROPERTIES + VERSION + ${LIBRARY_VERSION} + SOVERSION + ${LIBRARY_SOVERSION} + OUTPUT_NAME + ssh_threads + DEFINE_SYMBOL + LIBSSH_EXPORTS + ) - install( - TARGETS - ${LIBSSH_THREADS_STATIC_LIBRARY} - DESTINATION - ${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX} - COMPONENT - libraries - ) -endif (WITH_STATIC_LIB) + if (WITH_VISIBILITY_HIDDEN) + set_target_properties(${LIBSSH_THREADS_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") + endif (WITH_VISIBILITY_HIDDEN) + + install( + TARGETS + ${LIBSSH_THREADS_SHARED_LIBRARY} + RUNTIME DESTINATION ${BIN_INSTALL_DIR} + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR} + COMPONENT libraries + ) + + if (WITH_STATIC_LIB) + add_library(${LIBSSH_THREADS_STATIC_LIBRARY} STATIC ${libssh_threads_SRCS}) + + if (MSVC) + set(OUTPUT_SUFFIX static) + else (MSVC) + set(OUTPUT_SUFFIX ) + endif (MSVC) + + set_target_properties( + ${LIBSSH_THREADS_STATIC_LIBRARY} + PROPERTIES + VERSION + ${LIBRARY_VERSION} + SOVERSION + ${LIBRARY_SOVERSION} + OUTPUT_NAME + ssh_threads + ARCHIVE_OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX} + ) + + if (WIN32) + set_target_properties( + ${LIBSSH_THREADS_STATIC_LIBRARY} + PROPERTIES + COMPILE_FLAGS + "-DLIBSSH_STATIC" + ) + endif (WIN32) + + install( + TARGETS + ${LIBSSH_THREADS_STATIC_LIBRARY} + DESTINATION + ${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX} + COMPONENT + libraries + ) + endif (WITH_STATIC_LIB) +endif (libssh_threads_SRCS) diff --git a/libssh/src/wrapper.c b/libssh/src/wrapper.c index 51688753..bcd941b3 100644 --- a/libssh/src/wrapper.c +++ b/libssh/src/wrapper.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2003 by Aris Adamantiadis + * Copyright (c) 2003-2013 by Aris Adamantiadis * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -48,6 +48,46 @@ #include "libssh/wrapper.h" #include "libssh/pki.h" +static struct ssh_hmac_struct ssh_hmac_tab[] = { + { "hmac-sha1", SSH_HMAC_SHA1 }, + { "hmac-sha2-256", SSH_HMAC_SHA256 }, + { "hmac-sha2-384", SSH_HMAC_SHA384 }, + { "hmac-sha2-512", SSH_HMAC_SHA512 }, + { "hmac-md5", SSH_HMAC_MD5 }, + { NULL, 0} +}; + +struct ssh_hmac_struct *ssh_get_hmactab(void) { + return ssh_hmac_tab; +} + +size_t hmac_digest_len(enum ssh_hmac_e type) { + switch(type) { + case SSH_HMAC_SHA1: + return SHA_DIGEST_LEN; + case SSH_HMAC_SHA256: + return SHA256_DIGEST_LEN; + case SSH_HMAC_SHA384: + return SHA384_DIGEST_LEN; + case SSH_HMAC_SHA512: + return SHA512_DIGEST_LEN; + case SSH_HMAC_MD5: + return MD5_DIGEST_LEN; + default: + return 0; + } +} + +const char *ssh_hmac_type_to_string(enum ssh_hmac_e hmac_type) +{ + int i = 0; + struct ssh_hmac_struct *ssh_hmactab = ssh_get_hmactab(); + while (ssh_hmactab[i].name && (ssh_hmactab[i].hmac_type != hmac_type)) { + i++; + } + return ssh_hmactab[i].name; +} + /* it allocates a new cipher structure based on its offset into the global table */ static struct ssh_cipher_struct *cipher_new(int offset) { struct ssh_cipher_struct *cipher = NULL; @@ -130,10 +170,13 @@ void crypto_free(struct ssh_crypto_struct *crypto){ (deflateEnd(crypto->compress_out_ctx) != 0)) { inflateEnd(crypto->compress_out_ctx); } + SAFE_FREE(crypto->compress_out_ctx); + if (crypto->compress_in_ctx && (deflateEnd(crypto->compress_in_ctx) != 0)) { inflateEnd(crypto->compress_in_ctx); } + SAFE_FREE(crypto->compress_in_ctx); #endif /* WITH_ZLIB */ if(crypto->encryptIV) SAFE_FREE(crypto->encryptIV); @@ -167,6 +210,7 @@ static int crypt_set_algorithms2(ssh_session session){ const char *wanted; int i = 0; struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); + struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab(); /* we must scan the kex entries to find crypto algorithms and set their appropriate structure */ /* out */ @@ -190,6 +234,24 @@ static int crypt_set_algorithms2(ssh_session session){ } i = 0; + /* we must scan the kex entries to find hmac algorithms and set their appropriate structure */ + /* out */ + wanted = session->next_crypto->kex_methods[SSH_MAC_C_S]; + while (ssh_hmactab[i].name && strcmp(wanted, ssh_hmactab[i].name)) { + i++; + } + + if (ssh_hmactab[i].name == NULL) { + ssh_set_error(session, SSH_FATAL, + "crypt_set_algorithms2: no hmac algorithm function found for %s", + wanted); + return SSH_ERROR; + } + SSH_LOG(SSH_LOG_PACKET, "Set HMAC output algorithm to %s", wanted); + + session->next_crypto->out_hmac = ssh_hmactab[i].hmac_type; + i = 0; + /* in */ wanted = session->next_crypto->kex_methods[SSH_CRYPT_S_C]; while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) { @@ -209,6 +271,24 @@ static int crypt_set_algorithms2(ssh_session session){ ssh_set_error_oom(session); return SSH_ERROR; } + i = 0; + + /* we must scan the kex entries to find hmac algorithms and set their appropriate structure */ + wanted = session->next_crypto->kex_methods[SSH_MAC_S_C]; + while (ssh_hmactab[i].name && strcmp(wanted, ssh_hmactab[i].name)) { + i++; + } + + if (ssh_hmactab[i].name == NULL) { + ssh_set_error(session, SSH_FATAL, + "crypt_set_algorithms2: no hmac algorithm function found for %s", + wanted); + return SSH_ERROR; + } + SSH_LOG(SSH_LOG_PACKET, "Set HMAC output algorithm to %s", wanted); + + session->next_crypto->in_hmac = ssh_hmactab[i].hmac_type; + i = 0; /* compression */ if (strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib") == 0) { @@ -267,6 +347,7 @@ int crypt_set_algorithms_server(ssh_session session){ char *method = NULL; int i = 0; struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); + struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab(); if (session == NULL) { return SSH_ERROR; @@ -309,6 +390,40 @@ int crypt_set_algorithms_server(ssh_session session){ ssh_set_error_oom(session); return SSH_ERROR; } + i=0; + + /* HMAC algorithm selection */ + method = session->next_crypto->kex_methods[SSH_MAC_S_C]; + while (ssh_hmactab[i].name && strcmp(method, ssh_hmactab[i].name)) { + i++; + } + + if (ssh_hmactab[i].name == NULL) { + ssh_set_error(session, SSH_FATAL, + "crypt_set_algorithms_server: no hmac algorithm function found for %s", + method); + return SSH_ERROR; + } + SSH_LOG(SSH_LOG_PACKET, "Set HMAC output algorithm to %s", method); + + session->next_crypto->out_hmac = ssh_hmactab[i].hmac_type; + i=0; + + method = session->next_crypto->kex_methods[SSH_MAC_C_S]; + while (ssh_hmactab[i].name && strcmp(method, ssh_hmactab[i].name)) { + i++; + } + + if (ssh_hmactab[i].name == NULL) { + ssh_set_error(session, SSH_FATAL, + "crypt_set_algorithms_server: no hmac algorithm function found for %s", + method); + return SSH_ERROR; + } + SSH_LOG(SSH_LOG_PACKET, "Set HMAC input algorithm to %s", method); + + session->next_crypto->in_hmac = ssh_hmactab[i].hmac_type; + i=0; /* compression */ method = session->next_crypto->kex_methods[SSH_COMP_C_S]; diff --git a/libssh/tests/CMakeLists.txt b/libssh/tests/CMakeLists.txt index bb39755a..7cdb2c48 100644 --- a/libssh/tests/CMakeLists.txt +++ b/libssh/tests/CMakeLists.txt @@ -1,8 +1,8 @@ project(tests C) -if (BSD OR SOLARIS) +if (BSD OR SOLARIS OR OSX) find_package(Argp) -endif (BSD OR SOLARIS) +endif (BSD OR SOLARIS OR OSX) set(TORTURE_LIBRARY torture) @@ -38,6 +38,7 @@ set(TEST_TARGET_LIBRARIES ) add_subdirectory(unittests) + if (WITH_CLIENT_TESTING) add_subdirectory(client) endif (WITH_CLIENT_TESTING) @@ -46,3 +47,6 @@ if (WITH_BENCHMARKS) add_subdirectory(benchmarks) endif (WITH_BENCHMARKS) +if (WITH_SERVER) + add_subdirectory(pkd) +endif (WITH_SERVER) diff --git a/libssh/tests/client/torture_algorithms.c b/libssh/tests/client/torture_algorithms.c index 180efb71..8a466380 100644 --- a/libssh/tests/client/torture_algorithms.c +++ b/libssh/tests/client/torture_algorithms.c @@ -37,7 +37,7 @@ static void teardown(void **state) { ssh_free(*state); } -static void test_algorithm(ssh_session session, const char *algo) { +static void test_algorithm(ssh_session session, const char *algo, const char *hmac) { int rc; rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); @@ -49,6 +49,12 @@ static void test_algorithm(ssh_session session, const char *algo) { rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, algo); assert_true(rc == SSH_OK); + rc = ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, hmac); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, hmac); + assert_true(rc == SSH_OK); + rc = ssh_connect(session); assert_true(rc == SSH_OK); @@ -61,36 +67,100 @@ static void test_algorithm(ssh_session session, const char *algo) { ssh_disconnect(session); } -static void torture_algorithms_aes128_cbc(void **state) { - test_algorithm(*state, "aes128-cbc"); +static void torture_algorithms_aes128_cbc_hmac_sha1(void **state) { + test_algorithm(*state, "aes128-cbc", "hmac-sha1"); } -static void torture_algorithms_aes192_cbc(void **state) { - test_algorithm(*state, "aes192-cbc"); +static void torture_algorithms_aes128_cbc_hmac_sha2_256(void **state) { + test_algorithm(*state, "aes128-cbc", "hmac-sha2-256"); } -static void torture_algorithms_aes256_cbc(void **state) { - test_algorithm(*state, "aes256-cbc"); +static void torture_algorithms_aes128_cbc_hmac_sha2_512(void **state) { + test_algorithm(*state, "aes128-cbc", "hmac-sha2-512"); } -static void torture_algorithms_aes128_ctr(void **state) { - test_algorithm(*state, "aes128-ctr"); +static void torture_algorithms_aes192_cbc_hmac_sha1(void **state) { + test_algorithm(*state, "aes192-cbc", "hmac-sha1"); } -static void torture_algorithms_aes192_ctr(void **state) { - test_algorithm(*state, "aes192-ctr"); +static void torture_algorithms_aes192_cbc_hmac_sha2_256(void **state) { + test_algorithm(*state, "aes192-cbc", "hmac-sha2-256"); } -static void torture_algorithms_aes256_ctr(void **state) { - test_algorithm(*state, "aes256-ctr"); +static void torture_algorithms_aes192_cbc_hmac_sha2_512(void **state) { + test_algorithm(*state, "aes192-cbc", "hmac-sha2-512"); } -static void torture_algorithms_3des_cbc(void **state) { - test_algorithm(*state, "3des-cbc"); +static void torture_algorithms_aes256_cbc_hmac_sha1(void **state) { + test_algorithm(*state, "aes256-cbc", "hmac-sha1"); } -static void torture_algorithms_blowfish_cbc(void **state) { - test_algorithm(*state, "blowfish-cbc"); +static void torture_algorithms_aes256_cbc_hmac_sha2_256(void **state) { + test_algorithm(*state, "aes256-cbc", "hmac-sha2-256"); +} + +static void torture_algorithms_aes256_cbc_hmac_sha2_512(void **state) { + test_algorithm(*state, "aes256-cbc", "hmac-sha2-512"); +} + +static void torture_algorithms_aes128_ctr_hmac_sha1(void **state) { + test_algorithm(*state, "aes128-ctr", "hmac-sha1"); +} + +static void torture_algorithms_aes128_ctr_hmac_sha2_256(void **state) { + test_algorithm(*state, "aes128-ctr", "hmac-sha2-256"); +} + +static void torture_algorithms_aes128_ctr_hmac_sha2_512(void **state) { + test_algorithm(*state, "aes128-ctr", "hmac-sha2-512"); +} + +static void torture_algorithms_aes192_ctr_hmac_sha1(void **state) { + test_algorithm(*state, "aes192-ctr", "hmac-sha1"); +} + +static void torture_algorithms_aes192_ctr_hmac_sha2_256(void **state) { + test_algorithm(*state, "aes192-ctr", "hmac-sha2-256"); +} + +static void torture_algorithms_aes192_ctr_hmac_sha2_512(void **state) { + test_algorithm(*state, "aes192-ctr", "hmac-sha2-512"); +} + +static void torture_algorithms_aes256_ctr_hmac_sha1(void **state) { + test_algorithm(*state, "aes256-ctr", "hmac-sha1"); +} + +static void torture_algorithms_aes256_ctr_hmac_sha2_256(void **state) { + test_algorithm(*state, "aes256-ctr", "hmac-sha2-256"); +} + +static void torture_algorithms_aes256_ctr_hmac_sha2_512(void **state) { + test_algorithm(*state, "aes256-ctr", "hmac-sha2-512"); +} + +static void torture_algorithms_3des_cbc_hmac_sha1(void **state) { + test_algorithm(*state, "3des-cbc", "hmac-sha1"); +} + +static void torture_algorithms_3des_cbc_hmac_sha2_256(void **state) { + test_algorithm(*state, "3des-cbc", "hmac-sha2-256"); +} + +static void torture_algorithms_3des_cbc_hmac_sha2_512(void **state) { + test_algorithm(*state, "3des-cbc", "hmac-sha2-512"); +} + +static void torture_algorithms_blowfish_cbc_hmac_sha1(void **state) { + test_algorithm(*state, "blowfish-cbc", "hmac-sha1"); +} + +static void torture_algorithms_blowfish_cbc_hmac_sha2_256(void **state) { + test_algorithm(*state, "blowfish-cbc", "hmac-sha2-256"); +} + +static void torture_algorithms_blowfish_cbc_hmac_sha2_512(void **state) { + test_algorithm(*state, "blowfish-cbc", "hmac-sha2-512"); } static void torture_algorithms_zlib(void **state) { @@ -220,14 +290,30 @@ static void torture_algorithms_dh_group1(void **state) { int torture_run_tests(void) { int rc; const UnitTest tests[] = { - unit_test_setup_teardown(torture_algorithms_aes128_cbc, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes192_cbc, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes256_cbc, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes128_ctr, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes192_ctr, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes256_ctr, setup, teardown), - unit_test_setup_teardown(torture_algorithms_3des_cbc, setup, teardown), - unit_test_setup_teardown(torture_algorithms_blowfish_cbc, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes128_cbc_hmac_sha1, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes128_cbc_hmac_sha2_256, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes128_cbc_hmac_sha2_512, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes192_cbc_hmac_sha1, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes192_cbc_hmac_sha2_256, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes192_cbc_hmac_sha2_512, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes256_cbc_hmac_sha1, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes256_cbc_hmac_sha2_256, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes256_cbc_hmac_sha2_512, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes128_ctr_hmac_sha1, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes128_ctr_hmac_sha2_256, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes128_ctr_hmac_sha2_512, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes192_ctr_hmac_sha1, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes192_ctr_hmac_sha2_256, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes192_ctr_hmac_sha2_512, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes256_ctr_hmac_sha1, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes256_ctr_hmac_sha2_256, setup, teardown), + unit_test_setup_teardown(torture_algorithms_aes256_ctr_hmac_sha2_512, setup, teardown), + unit_test_setup_teardown(torture_algorithms_3des_cbc_hmac_sha1, setup, teardown), + unit_test_setup_teardown(torture_algorithms_3des_cbc_hmac_sha2_256, setup, teardown), + unit_test_setup_teardown(torture_algorithms_3des_cbc_hmac_sha2_512, setup, teardown), + unit_test_setup_teardown(torture_algorithms_blowfish_cbc_hmac_sha1, setup, teardown), + unit_test_setup_teardown(torture_algorithms_blowfish_cbc_hmac_sha2_256, setup, teardown), + unit_test_setup_teardown(torture_algorithms_blowfish_cbc_hmac_sha2_512, setup, teardown), unit_test_setup_teardown(torture_algorithms_zlib, setup, teardown), unit_test_setup_teardown(torture_algorithms_zlib_openssh, setup, teardown), unit_test_setup_teardown(torture_algorithms_dh_group1,setup,teardown), diff --git a/libssh/tests/client/torture_auth.c b/libssh/tests/client/torture_auth.c index 83bbd406..af36b79f 100644 --- a/libssh/tests/client/torture_auth.c +++ b/libssh/tests/client/torture_auth.c @@ -64,9 +64,10 @@ static void torture_auth_autopubkey(void **state) { if (rc == SSH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); - rc = ssh_userauth_autopubkey(session, NULL); + rc = ssh_userauth_publickey_auto(session, NULL, NULL); assert_true(rc == SSH_AUTH_SUCCESS); } @@ -87,16 +88,21 @@ static void torture_auth_autopubkey_nonblocking(void **state) { rc = ssh_connect(session); assert_true(rc == SSH_OK); - rc = ssh_userauth_none(session,NULL); + ssh_set_blocking(session,0); + do { + rc = ssh_userauth_none(session, NULL); + } while (rc == SSH_AUTH_AGAIN); + /* This request should return a SSH_REQUEST_DENIED error */ if (rc == SSH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); - ssh_set_blocking(session, 0); + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); + do { - rc = ssh_userauth_autopubkey(session, NULL); + rc = ssh_userauth_publickey_auto(session, NULL, NULL); } while (rc == SSH_AUTH_AGAIN); assert_true(rc == SSH_AUTH_SUCCESS); } @@ -130,7 +136,8 @@ static void torture_auth_kbdint(void **state) { if (rc == SSH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_INTERACTIVE); + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_INTERACTIVE); rc = ssh_userauth_kbdint(session, NULL, NULL); assert_true(rc == SSH_AUTH_INFO); @@ -172,13 +179,18 @@ static void torture_auth_kbdint_nonblocking(void **state) { rc = ssh_connect(session); assert_true(rc == SSH_OK); - rc = ssh_userauth_none(session,NULL); + ssh_set_blocking(session,0); + do { + rc = ssh_userauth_none(session, NULL); + } while (rc == SSH_AUTH_AGAIN); + /* This request should return a SSH_REQUEST_DENIED error */ if (rc == SSH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_INTERACTIVE); - ssh_set_blocking(session,0); + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_INTERACTIVE); + do { rc = ssh_userauth_kbdint(session, NULL, NULL); } while (rc == SSH_AUTH_AGAIN); @@ -231,7 +243,8 @@ static void torture_auth_password(void **state) { if (rc == SSH_AUTH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PASSWORD); + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PASSWORD); rc = ssh_userauth_password(session, NULL, password); assert_true(rc == SSH_AUTH_SUCCESS); @@ -260,17 +273,19 @@ static void torture_auth_password_nonblocking(void **state) { rc = ssh_connect(session); assert_true(rc == SSH_OK); - ssh_set_blocking(session,0); + ssh_set_blocking(session,0); do { rc = ssh_userauth_none(session, NULL); - } while (rc==SSH_AUTH_AGAIN); + } while (rc == SSH_AUTH_AGAIN); /* This request should return a SSH_REQUEST_DENIED error */ if (rc == SSH_AUTH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PASSWORD); + + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PASSWORD); do { rc = ssh_userauth_password(session, NULL, password); @@ -290,7 +305,7 @@ static void torture_auth_agent(void **state) { return; } if (!agent_is_running(session)){ - print_message("*** Agent not running. Test ignored"); + print_message("*** Agent not running. Test ignored\n"); return; } rc = ssh_options_set(session, SSH_OPTIONS_USER, user); @@ -304,7 +319,8 @@ static void torture_auth_agent(void **state) { if (rc == SSH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); rc = ssh_userauth_agent(session, NULL); assert_true(rc == SSH_AUTH_SUCCESS); @@ -321,7 +337,7 @@ static void torture_auth_agent_nonblocking(void **state) { return; } if (!agent_is_running(session)){ - print_message("*** Agent not running. Test ignored"); + print_message("*** Agent not running. Test ignored\n"); return; } rc = ssh_options_set(session, SSH_OPTIONS_USER, user); @@ -335,14 +351,74 @@ static void torture_auth_agent_nonblocking(void **state) { if (rc == SSH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); - ssh_set_blocking(session, 0); + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); + + ssh_set_blocking(session,0); + do { rc = ssh_userauth_agent(session, NULL); } while (rc == SSH_AUTH_AGAIN); assert_true(rc == SSH_AUTH_SUCCESS); } + +static void torture_auth_none(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_userauth_none(session,NULL); + + assert_true(rc == SSH_AUTH_DENIED); + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } +} + +static void torture_auth_none_nonblocking(void **state) { + ssh_session session = *state; + char *user = getenv("TORTURE_USER"); + int rc; + + if (user == NULL) { + print_message("*** Please set the environment variable TORTURE_USER" + " to enable this test!!\n"); + return; + } + rc = ssh_options_set(session, SSH_OPTIONS_USER, user); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + /* This request should return a SSH_REQUEST_DENIED error */ + if (rc == SSH_ERROR) { + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + } + + ssh_set_blocking(session,0); + + do { + rc = ssh_userauth_none(session,NULL); + } while (rc == SSH_AUTH_AGAIN); + assert_true(rc == SSH_AUTH_DENIED); + assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); + +} + int torture_run_tests(void) { int rc; const UnitTest tests[] = { @@ -354,6 +430,8 @@ int torture_run_tests(void) { unit_test_setup_teardown(torture_auth_autopubkey_nonblocking, setup, teardown), unit_test_setup_teardown(torture_auth_agent, setup, teardown), unit_test_setup_teardown(torture_auth_agent_nonblocking, setup, teardown), + unit_test_setup_teardown(torture_auth_none, setup, teardown), + unit_test_setup_teardown(torture_auth_none_nonblocking, setup, teardown), }; ssh_init(); diff --git a/libssh/tests/client/torture_connect.c b/libssh/tests/client/torture_connect.c index 0e23fcd7..31573ea3 100644 --- a/libssh/tests/client/torture_connect.c +++ b/libssh/tests/client/torture_connect.c @@ -24,6 +24,7 @@ #include "torture.h" #include #include +#include #define HOST "localhost" /* Should work until Apnic decides to assign it :) */ @@ -54,12 +55,11 @@ static void torture_connect_nonblocking(void **state) { ssh_set_blocking(session,0); do { - rc = ssh_connect(session); - assert_true(rc != SSH_ERROR); + rc = ssh_connect(session); + assert_true(rc != SSH_ERROR); } while(rc == SSH_AGAIN); - assert_true(rc==SSH_OK); - + assert_true(rc == SSH_OK); } static void torture_connect_timeout(void **state) { @@ -84,9 +84,9 @@ static void torture_connect_timeout(void **state) { sec = after.tv_sec - before.tv_sec; usec = after.tv_usec - before.tv_usec; /* Borrow a second for the missing usecs, but don't bother calculating */ - if(usec < 0) + if (usec < 0) sec--; - assert_in_range(sec,1,3); + assert_in_range(sec, 1, 3); } static void torture_connect_double(void **state) { @@ -102,10 +102,9 @@ static void torture_connect_double(void **state) { rc = ssh_connect(session); assert_true(rc == SSH_OK); - } -static void torture_connect_failure(void **state){ +static void torture_connect_failure(void **state) { /* * The intent of this test is to check that a fresh * ssh_new/ssh_disconnect/ssh_free sequence doesn't crash/leak @@ -114,6 +113,30 @@ static void torture_connect_failure(void **state){ ssh_session session = *state; ssh_disconnect(session); } + +static void torture_connect_socket(void **state) { + ssh_session session = *state; + + int rc; + int sock_fd = 0; + struct sockaddr_in server_addr; + + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + assert_true(sock_fd > 0); + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(22); + server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + rc = connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); + assert_true(rc == 0); + + ssh_options_set(session, SSH_OPTIONS_FD, &sock_fd); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); +} + int torture_run_tests(void) { int rc; const UnitTest tests[] = { @@ -121,6 +144,7 @@ int torture_run_tests(void) { unit_test_setup_teardown(torture_connect_double, setup, teardown), unit_test_setup_teardown(torture_connect_failure, setup, teardown), unit_test_setup_teardown(torture_connect_timeout, setup, teardown), + unit_test_setup_teardown(torture_connect_socket, setup, teardown), }; ssh_init(); diff --git a/libssh/tests/client/torture_forward.c b/libssh/tests/client/torture_forward.c index 1440552e..5754386f 100644 --- a/libssh/tests/client/torture_forward.c +++ b/libssh/tests/client/torture_forward.c @@ -41,15 +41,15 @@ static void setup(void **state) session = torture_ssh_session(host, user, password); - assert_false(session == NULL); + assert_non_null(session); *state = session; } static void teardown(void **state) { - ssh_session session = *state; + ssh_session session = (ssh_session) *state; - assert_false(session == NULL); + assert_non_null(session); if (ssh_is_connected(session)) { ssh_disconnect(session); @@ -59,14 +59,14 @@ static void teardown(void **state) static void torture_ssh_forward(void **state) { - ssh_session session = *state; + ssh_session session = (ssh_session) *state; #if 0 ssh_channel c; #endif int bound_port; int rc; - rc = ssh_forward_listen(session, "127.0.0.1", 8080, &bound_port); + rc = ssh_channel_listen_forward(session, "127.0.0.1", 8080, &bound_port); assert_int_equal(rc, SSH_OK); #if 0 diff --git a/libssh/tests/client/torture_knownhosts.c b/libssh/tests/client/torture_knownhosts.c index fe827cbc..df99ae90 100644 --- a/libssh/tests/client/torture_knownhosts.c +++ b/libssh/tests/client/torture_knownhosts.c @@ -23,8 +23,26 @@ #include "torture.h" #include "session.c" +#include "known_hosts.c" #define KNOWNHOSTFILES "libssh_torture_knownhosts" +#define BADRSA "AAAAB3NzaC1yc2EAAAADAQABAAABAQChm5" \ + "a6Av65O8cKtx5YXOnui3wJnYE6A6J/I4kZSAibbn14Jcl+34VJQwv96f25AxNmo" \ + "NwoiZV93IzdypQmiuieh6s6wB9WhYjU9K/6CkIpNhpCxswA90b3ePjS7LnR9B9J" \ + "slPSbG1H0KC1c5lb7G3utXteXtM+4YvCvpN5VdC4CpghT+p0cwN2Na8Md5vRItz" \ + "YgIytryNn7LLiwYfoSxvWigFrTTZsrVtCOYyNgklmffpGdzuC43wdANvTewfI9G" \ + "o71r8EXmEc228CrYPmb8Scv3mpXFK/BosohSGkPlEHu9lf3YjnknBicDaVtJOYp" \ + "wnXJPjZo2EhG79HxDRpjJHH" +#define BADDSA "AAAAB3NzaC1kc3MAAACBAITDKqGQ5aC5wHySG6ZdL1+BVBY2nLP5vzw3i3pvZfP" \ + "yNUS0UCwrt5pajsMvDRGXXebTJhWVonDnv8tpSgiuIBXMZrma8CU1KCFGRzwb/n8" \ + "cc5tJmIphlOUTrObjBmsRz7u1eZmoaddXC9ask6BNnt0DmhzYi2esL3mbardy8IN" \ + "zAAAAFQDlPFCm410pgQQPb3X5FWjyVEIl+QAAAIAp0vqfir8K8p+zP4dzFG7ppnt" \ + "DjaXf3ge6URF7f5xPDo6CClGo2JQ2REF8NxM7K9cLgR9Ifx2ahO48UMgrXEl/BOp" \ + "IQHpeBqUz26a49O5J0WEW16YSUHxWwMxWVe/SRmyKdTUZJ6fcepH88JNqm3XudNn" \ + "s78grM+yx9mcXnK2AsAAAAIBxpF8ZQIlGrSgwCmCfwjP156bC3Ya6LYf9ZpLJ0dX" \ + "EcxqLVllrNEvd2EGD9p16BYO2yaalYon8im59PtOcul2ay5XQ6rVDQ2T0pgNUpsI" \ + "h0dSi8VJXI1wes5HTyLsv9VBmU1uCXUUvufoQKfF/OcSH0ufcCpnd62g1/adZcy2" \ + "WJg==" static void setup(void **state) { int verbosity=torture_libssh_verbosity(); @@ -93,10 +111,184 @@ static void torture_knownhosts_port(void **state) { assert_true(rc == SSH_SERVER_KNOWN_OK); } +static void torture_knownhosts_fail(void **state) { + ssh_session session = *state; + FILE *file; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa"); + assert_true(rc == SSH_OK); + + file = fopen(KNOWNHOSTFILES, "w"); + assert_true(file != NULL); + fprintf(file, "localhost ssh-rsa %s\n", BADRSA); + fclose(file); + + rc = ssh_connect(session); + assert_true(rc==SSH_OK); + + rc = ssh_is_server_known(session); + assert_true(rc == SSH_SERVER_KNOWN_CHANGED); +} + +static void torture_knownhosts_other(void **state) { + ssh_session session = *state; + FILE *file; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-dss"); + assert_true(rc == SSH_OK); + + file = fopen(KNOWNHOSTFILES, "w"); + assert_true(file != NULL); + fprintf(file, "localhost ssh-rsa %s\n", BADRSA); + fclose(file); + + rc = ssh_connect(session); + assert_true(rc==SSH_OK); + + rc = ssh_is_server_known(session); + assert_true(rc == SSH_SERVER_FOUND_OTHER); +} + +static void torture_knownhosts_other_auto(void **state) { + ssh_session session = *state; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-dss"); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc==SSH_OK); + + rc = ssh_is_server_known(session); + assert_true(rc == SSH_SERVER_NOT_KNOWN); + + rc = ssh_write_knownhost(session); + assert_true(rc == SSH_OK); + + ssh_disconnect(session); + ssh_free(session); + + /* connect again and check host key */ + *state = session = ssh_new(); + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc==SSH_OK); + + /* ssh-rsa is the default but libssh should try ssh-dss instead */ + rc = ssh_is_server_known(session); + assert_true(rc == SSH_SERVER_KNOWN_OK); +} + +static void torture_knownhosts_conflict(void **state) { + ssh_session session = *state; + FILE *file; + int rc; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa"); + assert_true(rc == SSH_OK); + + file = fopen(KNOWNHOSTFILES, "w"); + assert_true(file != NULL); + fprintf(file, "localhost ssh-rsa %s\n", BADRSA); + fprintf(file, "localhost ssh-dss %s\n", BADDSA); + fclose(file); + + rc = ssh_connect(session); + assert_true(rc==SSH_OK); + + rc = ssh_is_server_known(session); + assert_true(rc == SSH_SERVER_KNOWN_CHANGED); + + rc = ssh_write_knownhost(session); + assert_true(rc==SSH_OK); + + ssh_disconnect(session); + ssh_free(session); + + /* connect again and check host key */ + *state = session = ssh_new(); + + ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa"); + assert_true(rc == SSH_OK); + + rc = ssh_connect(session); + assert_true(rc == SSH_OK); + + rc = ssh_is_server_known(session); + assert_true(rc == SSH_SERVER_KNOWN_OK); +} + +static void torture_knownhosts_precheck(void **state) { + ssh_session session = *state; + FILE *file; + int rc; + char **kex; + + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); + assert_true(rc == SSH_OK); + + rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); + assert_true(rc == SSH_OK); + + file = fopen(KNOWNHOSTFILES, "w"); + assert_true(file != NULL); + fprintf(file, "localhost ssh-rsa %s\n", BADRSA); + fprintf(file, "localhost ssh-dss %s\n", BADDSA); + fclose(file); + + kex = ssh_knownhosts_algorithms(session); + assert_true(kex != NULL); + assert_string_equal(kex[0],"ssh-rsa"); + assert_string_equal(kex[1],"ssh-dss"); + assert_true(kex[2]==NULL); + free(kex[1]); + free(kex[0]); + free(kex); +} + int torture_run_tests(void) { int rc; const UnitTest tests[] = { unit_test_setup_teardown(torture_knownhosts_port, setup, teardown), + unit_test_setup_teardown(torture_knownhosts_fail, setup, teardown), + unit_test_setup_teardown(torture_knownhosts_other, setup, teardown), + unit_test_setup_teardown(torture_knownhosts_other_auto, setup, teardown), + unit_test_setup_teardown(torture_knownhosts_conflict, setup, teardown), + unit_test_setup_teardown(torture_knownhosts_precheck, setup, teardown) }; ssh_init(); diff --git a/libssh/tests/client/torture_request_env.c b/libssh/tests/client/torture_request_env.c index dbe7fb21..7c7338ed 100644 --- a/libssh/tests/client/torture_request_env.c +++ b/libssh/tests/client/torture_request_env.c @@ -61,7 +61,7 @@ static void torture_request_env(void **state) { ssh_session session = *state; ssh_channel c; - char buffer[4096]; + char buffer[4096] = {0}; int nbytes; int rc; int lang_found = 0; @@ -72,20 +72,20 @@ static void torture_request_env(void **state) rc = ssh_channel_open_session(c); assert_int_equal(rc, SSH_OK); - rc = ssh_channel_request_env(c, "LANG", "LIBSSH"); + rc = ssh_channel_request_env(c, "LC_LIBSSH", "LIBSSH"); assert_int_equal(rc, SSH_OK); rc = ssh_channel_request_exec(c, "bash -c export"); assert_int_equal(rc, SSH_OK); - nbytes = ssh_channel_read(c, buffer, sizeof(buffer), 0); + nbytes = ssh_channel_read(c, buffer, sizeof(buffer) - 1, 0); while (nbytes > 0) { #if 0 rc = fwrite(buffer, 1, nbytes, stdout); assert_int_equal(rc, nbytes); #endif - - if (strstr(buffer, "LANG=\"LIBSSH\"")) { + buffer[nbytes]='\0'; + if (strstr(buffer, "LC_LIBSSH=\"LIBSSH\"")) { lang_found = 1; break; } diff --git a/libssh/tests/client/torture_session.c b/libssh/tests/client/torture_session.c index f2e062b4..ef9ef653 100644 --- a/libssh/tests/client/torture_session.c +++ b/libssh/tests/client/torture_session.c @@ -68,9 +68,10 @@ static void torture_channel_read_error(void **state) { if (rc == SSH_ERROR) { assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); } - assert_true(ssh_auth_list(session) & SSH_AUTH_METHOD_PUBLICKEY); + rc = ssh_userauth_list(session, NULL); + assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); - rc = ssh_userauth_autopubkey(session, NULL); + rc = ssh_userauth_publickey_auto(session, NULL, NULL); assert_true(rc == SSH_AUTH_SUCCESS); channel = ssh_channel_new(session); diff --git a/libssh/tests/client/torture_sftp_read.c b/libssh/tests/client/torture_sftp_read.c index 8efaafe9..1e40e2cf 100644 --- a/libssh/tests/client/torture_sftp_read.c +++ b/libssh/tests/client/torture_sftp_read.c @@ -29,7 +29,7 @@ static void setup(void **state) { } static void teardown(void **state) { - struct torture_sftp *t = *state; + struct torture_sftp *t = (struct torture_sftp*) *state; assert_false(t == NULL); @@ -38,7 +38,7 @@ static void teardown(void **state) { } static void torture_sftp_read_blocking(void **state) { - struct torture_sftp *t = *state; + struct torture_sftp *t = (struct torture_sftp*) *state; char libssh_tmp_file[] = "/tmp/libssh_sftp_test_XXXXXX"; char buf[MAX_XFER_BUF_SIZE]; ssize_t bytesread; diff --git a/libssh/tests/pkd/CMakeLists.txt b/libssh/tests/pkd/CMakeLists.txt new file mode 100644 index 00000000..515dae10 --- /dev/null +++ b/libssh/tests/pkd/CMakeLists.txt @@ -0,0 +1,35 @@ +project(pkd C) + +if (WITH_SERVER AND UNIX AND NOT WIN32) + +include_directories( + ${LIBSSH_PUBLIC_INCLUDE_DIRS} + ${CMOCKA_INCLUDE_DIR} + ${OPENSSL_INCLUDE_DIRS} + ${GCRYPT_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set(pkd_hello_src + pkd_daemon.c + pkd_hello.c + pkd_keyutil.c + pkd_util.c +) + +set(pkd_libs + ${CMOCKA_LIBRARY} + ${LIBSSH_STATIC_LIBRARY} + ${LIBSSH_LINK_LIBRARIES} + ${LIBSSH_THREADS_STATIC_LIBRARY} + ${LIBSSH_THREADS_LINK_LIBRARIES} + ${ARGP_LIBRARIES} +) + +add_executable(pkd_hello ${pkd_hello_src}) +target_link_libraries(pkd_hello ${pkd_libs}) + +endif (WITH_SERVER AND UNIX AND NOT WIN32) diff --git a/libssh/tests/pkd/pkd_client.h b/libssh/tests/pkd/pkd_client.h new file mode 100644 index 00000000..c4a8a601 --- /dev/null +++ b/libssh/tests/pkd/pkd_client.h @@ -0,0 +1,69 @@ +/* + * pkd_client.h -- macros for generating client-specific command + * invocations for use with pkd testing + * + * (c) 2014 Jon Simons + */ + +#ifndef __PKD_CLIENT_H__ +#define __PKD_CLIENT_H__ + +/* OpenSSH */ + +#define OPENSSH_BINARY "ssh" +#define OPENSSH_KEYGEN "ssh-keygen" + +#define OPENSSH_CMD_START \ + OPENSSH_BINARY " " \ + "-o UserKnownHostsFile=/dev/null " \ + "-o StrictHostKeyChecking=no " \ + "-i " CLIENT_ID_FILE " " \ + "1> %s.out " \ + "2> %s.err " \ + "-vvv " + +#define OPENSSH_CMD_END "-p 1234 localhost ls" + +#define OPENSSH_CMD \ + OPENSSH_CMD_START OPENSSH_CMD_END + +#define OPENSSH_KEX_CMD(kexalgo) \ + OPENSSH_CMD_START "-o KexAlgorithms=" kexalgo " " OPENSSH_CMD_END + +#define OPENSSH_CIPHER_CMD(ciphers) \ + OPENSSH_CMD_START "-c " ciphers " " OPENSSH_CMD_END + +#define OPENSSH_MAC_CMD(macs) \ + OPENSSH_CMD_START "-o MACs=" macs " " OPENSSH_CMD_END + + +/* Dropbear */ + +#define DROPBEAR_BINARY "dbclient" +#define DROPBEAR_KEYGEN "dropbearkey" + +#define DROPBEAR_CMD_START \ + DROPBEAR_BINARY " " \ + "-y -y " \ + "-i " CLIENT_ID_FILE " " \ + "-v " \ + "1> %s.out " \ + "2> %s.err " + +#define DROPBEAR_CMD_END "-p 1234 localhost ls" + +#define DROPBEAR_CMD \ + DROPBEAR_CMD_START DROPBEAR_CMD_END + +#if 0 /* dbclient does not expose control over kex algo */ +#define DROPBEAR_KEX_CMD(kexalgo) \ + DROPBEAR_CMD +#endif + +#define DROPBEAR_CIPHER_CMD(ciphers) \ + DROPBEAR_CMD_START "-c " ciphers " " DROPBEAR_CMD_END + +#define DROPBEAR_MAC_CMD(macs) \ + DROPBEAR_CMD_START "-m " macs " " DROPBEAR_CMD_END + +#endif /* __PKD_CLIENT_H__ */ diff --git a/libssh/tests/pkd/pkd_daemon.c b/libssh/tests/pkd/pkd_daemon.c new file mode 100644 index 00000000..07736fa6 --- /dev/null +++ b/libssh/tests/pkd/pkd_daemon.c @@ -0,0 +1,500 @@ +/* + * pkd_daemon.c -- a sample public-key testing daemon using libssh + * + * Uses public key authentication to establish an exec channel and + * echo back payloads to the user. + * + * (c) 2014 Jon Simons + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pkd_daemon.h" + +#include // for cmocka +#include + +static int pkdout_enabled; +static int pkderr_enabled; + +static void pkdout(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); +static void pkderr(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); + +static void pkdout(const char *fmt, ...) { + va_list vargs; + if (pkdout_enabled) { + va_start(vargs, fmt); + vfprintf(stdout, fmt, vargs); + va_end(vargs); + } +} + +static void pkderr(const char *fmt, ...) { + va_list vargs; + if (pkderr_enabled) { + va_start(vargs, fmt); + vfprintf(stderr, fmt, vargs); + va_end(vargs); + } +} + +/* + * pkd state: only one thread can run pkd at a time --------------------- + */ + +static struct { + int rc; + pthread_t tid; + int keep_going; + int pkd_ready; +} ctx; + +static struct { + int server_fd; + int req_exec_received; + int close_received; + int eof_received; +} pkd_state; + +static void pkd_sighandler(int signum) { + (void) signum; +} + +static int pkd_init_libssh() { + int rc = ssh_threads_set_callbacks(ssh_threads_get_pthread()); + return (rc == SSH_OK) ? 0 : 1; +} + +static int pkd_init_server_fd(short port) { + int rc = 0; + int yes = 1; + struct sockaddr_in addr; + + int server_fd = socket(PF_INET, SOCK_STREAM, 0); + if (server_fd < 0) { + rc = -1; + goto out; + } + + rc = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + if (rc != 0) { + goto outclose; + } + + memset(&addr, 0x0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + rc = bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)); + if (rc != 0) { + goto outclose; + } + + rc = listen(server_fd, 128); + if (rc == 0) { + goto out; + } + +outclose: + close(server_fd); + server_fd = -1; +out: + pkd_state.server_fd = server_fd; + return rc; +} + +static int pkd_accept_fd() { + int fd = -1; + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + + do { + fd = accept(pkd_state.server_fd, (struct sockaddr *) &addr, &len); + } while ((ctx.keep_going != 0) && (fd < 0) && (errno == EINTR)); + + return fd; +} + +static void pkd_eof(ssh_session session, + ssh_channel channel, + void *userdata) { + (void) session; + (void) channel; + (void) userdata; + pkdout("pkd_eof\n"); + pkd_state.eof_received = 1; +} + +static void pkd_chan_close(ssh_session session, + ssh_channel channel, + void *userdata) { + (void) session; + (void) channel; + (void) userdata; + pkdout("pkd_chan_close\n"); + pkd_state.close_received = 1; +} + +static int pkd_req_exec(ssh_session s, + ssh_channel c, + const char *cmd, + void *userdata) { + (void) s; + (void) c; + (void) cmd; + (void) userdata; + /* assumes pubkey authentication has already succeeded */ + pkdout("pkd_req_exec\n"); + pkd_state.req_exec_received = 1; + return 0; +} + +/* assumes there is only ever a single channel */ +static struct ssh_channel_callbacks_struct pkd_channel_cb = { + .channel_eof_function = pkd_eof, + .channel_close_function = pkd_chan_close, + .channel_exec_request_function = pkd_req_exec, +}; + +static int pkd_auth_pubkey_cb(ssh_session s, + const char *user, + ssh_key key, + char state, + void *userdata) { + (void) s; + (void) user; + (void) key; + (void) state; + (void) userdata; + pkdout("pkd_auth_pubkey_cb keytype %s, state: %d\n", + ssh_key_type_to_char(ssh_key_type(key)), state); + if ((state == SSH_PUBLICKEY_STATE_NONE) || + (state == SSH_PUBLICKEY_STATE_VALID)) { + return SSH_AUTH_SUCCESS; + } + return SSH_AUTH_DENIED; +} + +static int pkd_service_request_cb(ssh_session session, + const char *service, + void *userdata) { + (void) session; + (void) userdata; + pkdout("pkd_service_request_cb: %s\n", service); + return (0 == (strcmp(service, "ssh-userauth"))) ? 0 : -1; +} + +static ssh_channel pkd_channel_openreq_cb(ssh_session s, + void *userdata) { + ssh_channel c = NULL; + ssh_channel *out = (ssh_channel *) userdata; + + /* assumes pubkey authentication has already succeeded */ + pkdout("pkd_channel_openreq_cb\n"); + + c = ssh_channel_new(s); + if (c == NULL) { + pkderr("ssh_channel_new: %s\n", ssh_get_error(s)); + return NULL; + } + + ssh_callbacks_init(&pkd_channel_cb); + pkd_channel_cb.userdata = userdata; + if (ssh_set_channel_callbacks(c, &pkd_channel_cb) != SSH_OK) { + pkderr("ssh_set_channel_callbacks: %s\n", ssh_get_error(s)); + ssh_channel_free(c); + c = NULL; + } + + *out = c; + + return c; +} + +static struct ssh_server_callbacks_struct pkd_server_cb = { + .auth_pubkey_function = pkd_auth_pubkey_cb, + .service_request_function = pkd_service_request_cb, + .channel_open_request_session_function = pkd_channel_openreq_cb, +}; + +static int pkd_exec_hello(int fd, struct pkd_daemon_args *args) { + int rc = -1; + ssh_bind b = NULL; + ssh_session s = NULL; + ssh_event e = NULL; + ssh_channel c = NULL; + enum ssh_bind_options_e opts = -1; + + int level = args->opts.libssh_log_level; + enum pkd_hostkey_type_e type = args->type; + const char *hostkeypath = args->hostkeypath; + + pkd_state.eof_received = 0; + pkd_state.close_received = 0; + pkd_state.req_exec_received = 0; + + b = ssh_bind_new(); + if (b == NULL) { + pkderr("ssh_bind_new\n"); + goto outclose; + } + + if (type == PKD_RSA) { + opts = SSH_BIND_OPTIONS_RSAKEY; + } else if (type == PKD_DSA) { + opts = SSH_BIND_OPTIONS_DSAKEY; + } else if (type == PKD_ECDSA) { + opts = SSH_BIND_OPTIONS_ECDSAKEY; + } else { + pkderr("unknown kex algorithm: %d\n", type); + rc = -1; + goto outclose; + } + + rc = ssh_bind_options_set(b, opts, hostkeypath); + if (rc != 0) { + pkderr("ssh_bind_options_set: %s\n", ssh_get_error(b)); + goto outclose; + } + + rc = ssh_bind_options_set(b, SSH_BIND_OPTIONS_LOG_VERBOSITY, &level); + if (rc != 0) { + pkderr("ssh_bind_options_set log verbosity: %s\n", ssh_get_error(b)); + goto outclose; + } + + s = ssh_new(); + if (s == NULL) { + pkderr("ssh_new\n"); + goto outclose; + } + + /* + * ssh_bind_accept loads host key as side-effect. If this + * succeeds, the given 'fd' will be closed upon 'ssh_free(s)'. + */ + rc = ssh_bind_accept_fd(b, s, fd); + if (rc != SSH_OK) { + pkderr("ssh_bind_accept_fd: %s\n", ssh_get_error(b)); + goto outclose; + } + + /* accept only publickey-based auth */ + ssh_set_auth_methods(s, SSH_AUTH_METHOD_PUBLICKEY); + + /* initialize callbacks */ + ssh_callbacks_init(&pkd_server_cb); + pkd_server_cb.userdata = &c; + rc = ssh_set_server_callbacks(s, &pkd_server_cb); + if (rc != SSH_OK) { + pkderr("ssh_set_server_callbacks: %s\n", ssh_get_error(s)); + goto out; + } + + /* first do key exchange */ + rc = ssh_handle_key_exchange(s); + if (rc != SSH_OK) { + pkderr("ssh_handle_key_exchange: %s\n", ssh_get_error(s)); + goto out; + } + + /* setup and pump event to carry out exec channel */ + e = ssh_event_new(); + if (e == NULL) { + pkderr("ssh_event_new\n"); + goto out; + } + + rc = ssh_event_add_session(e, s); + if (rc != SSH_OK) { + pkderr("ssh_event_add_session\n"); + goto out; + } + + /* poll until exec channel established */ + while ((ctx.keep_going != 0) && + (rc != SSH_ERROR) && (pkd_state.req_exec_received == 0)) { + rc = ssh_event_dopoll(e, -1 /* infinite timeout */); + } + + if (rc == SSH_ERROR) { + pkderr("ssh_event_dopoll\n"); + goto out; + } else if (c == NULL) { + pkderr("poll loop exited but exec channel not ready\n"); + rc = -1; + goto out; + } + + rc = ssh_channel_write(c, "hello\n", 6); /* XXX: customizable payloads */ + if (rc != 6) { + pkderr("ssh_channel_write partial (%d)\n", rc); + } + + rc = ssh_channel_request_send_exit_status(c, 0); + if (rc != SSH_OK) { + pkderr("ssh_channel_request_send_exit_status: %s\n", + ssh_get_error(s)); + goto out; + } + + rc = ssh_channel_send_eof(c); + if (rc != SSH_OK) { + pkderr("ssh_channel_send_eof: %s\n", ssh_get_error(s)); + goto out; + } + + rc = ssh_channel_close(c); + if (rc != SSH_OK) { + pkderr("ssh_channel_close: %s\n", ssh_get_error(s)); + goto out; + } + + while ((ctx.keep_going != 0) && + (pkd_state.eof_received == 0) && + (pkd_state.close_received == 0) && + (ssh_channel_is_closed(c) == 0)) { + rc = ssh_event_dopoll(e, 1000 /* milliseconds */); + if (rc == SSH_ERROR) { + pkderr("ssh_event_dopoll for eof + close: %s\n", ssh_get_error(s)); + break; + } else { + rc = 0; + } + } + goto out; + +outclose: + close(fd); +out: + if (c != NULL) { + ssh_channel_free(c); + } + if (e != NULL) { + ssh_event_remove_session(e, s); + ssh_event_free(e); + } + if (s != NULL) { + ssh_disconnect(s); + ssh_free(s); + } + if (b != NULL) { + ssh_bind_free(b); + } + return rc; +} + +/* + * main loop ------------------------------------------------------------ + */ + +static void *pkd_main(void *args) { + int rc = -1; + struct pkd_daemon_args *a = (struct pkd_daemon_args *) args; + + struct sigaction act = { .sa_handler = pkd_sighandler, }; + + pkd_state.server_fd = -1; + pkd_state.req_exec_received = 0; + pkd_state.close_received = 0; + pkd_state.eof_received = 0; + + /* SIGUSR1 is used to interrupt 'pkd_accept_fd'. */ + rc = sigaction(SIGUSR1, &act, NULL); + if (rc != 0) { + pkderr("sigaction: %d\n", rc); + goto out; + } + + rc = pkd_init_libssh(); + if (rc != 0) { + pkderr("pkd_init_libssh: %d\n", rc); + goto out; + } + + rc = pkd_init_server_fd(1234); + if (rc != 0) { + pkderr("pkd_init_server_fd: %d\n", rc); + goto out; + } + + ctx.pkd_ready = 1; + + while (ctx.keep_going != 0) { + int fd = pkd_accept_fd(); + if (fd < 0) { + if (ctx.keep_going != 0) { + pkderr("pkd_accept_fd"); + rc = -1; + } else { + rc = 0; + } + break; + } + + rc = pkd_exec_hello(fd, a); + if (rc != 0) { + pkderr("pkd_exec_hello: %d\n", rc); + break; + } + } + + close(pkd_state.server_fd); + pkd_state.server_fd = -1; +out: + ctx.rc = rc; + + return NULL; +} + +/* + * pkd start and stop used by setup/teardown test scaffolding ----------- + */ + +int pkd_start(struct pkd_daemon_args *args) { + int rc = 0; + + pkdout_enabled = args->opts.log_stdout; + pkderr_enabled = args->opts.log_stderr; + + /* Initialize the pkd context. */ + ctx.rc = -1; + ctx.keep_going = 1; + ctx.pkd_ready = 0; + rc = pthread_create(&ctx.tid, NULL, &pkd_main, args); + assert_int_equal(rc, 0); + + /* Busy-spin until pkd thread is ready. */ + while (ctx.pkd_ready == 0); + + return rc; +} + +void pkd_stop(struct pkd_result *out) { + int rc = 0; + + ctx.keep_going = 0; + + rc = pthread_kill(ctx.tid, SIGUSR1); + assert_int_equal(rc, 0); + + rc = pthread_join(ctx.tid, NULL); + assert_int_equal(rc, 0); + + assert_non_null(out); + out->ok = (ctx.rc == 0); + + return; +} diff --git a/libssh/tests/pkd/pkd_daemon.h b/libssh/tests/pkd/pkd_daemon.h new file mode 100644 index 00000000..c42573c1 --- /dev/null +++ b/libssh/tests/pkd/pkd_daemon.h @@ -0,0 +1,40 @@ +/* + * pkd_daemon.h -- tests use this interface to start, stop pkd + * instances and get results + * + * (c) 2014 Jon Simons + */ + +#ifndef __PKD_DAEMON_H__ +#define __PKD_DAEMON_H__ + +enum pkd_hostkey_type_e { + PKD_RSA, + PKD_DSA, + PKD_ECDSA +}; + +struct pkd_daemon_args { + enum pkd_hostkey_type_e type; + const char *hostkeypath; + + struct { + int list; + + int log_stdout; + int log_stderr; + int libssh_log_level; + + const char *testname; + unsigned int iterations; + } opts; +}; + +struct pkd_result { + int ok; +}; + +int pkd_start(struct pkd_daemon_args *args); +void pkd_stop(struct pkd_result *out); + +#endif /* __PKD_DAEMON_H__ */ diff --git a/libssh/tests/pkd/pkd_hello.c b/libssh/tests/pkd/pkd_hello.c new file mode 100644 index 00000000..2183f897 --- /dev/null +++ b/libssh/tests/pkd/pkd_hello.c @@ -0,0 +1,534 @@ +/* + * pkd_hello.c -- + * + * (c) 2014 Jon Simons + */ + +#include // for cmocka +#include // for cmocka +#include +#include +#include // for cmocka +#include + +#include "libssh/priv.h" + +#include "pkd_client.h" +#include "pkd_daemon.h" +#include "pkd_keyutil.h" +#include "pkd_util.h" + +#define DEFAULT_ITERATIONS 10 +static struct pkd_daemon_args pkd_dargs; + +#ifdef HAVE_ARGP_H +#include +#define PROGNAME "pkd_hello" +#define ARGP_PROGNAME "libssh " PROGNAME +const char *argp_program_version = ARGP_PROGNAME " 2014-04-12"; +const char *argp_program_bug_address = "Jon Simons "; +//static char **cmdline; +static char doc[] = \ + "\nExample usage:\n\n" + " " PROGNAME "\n" + " Run all tests with default number of iterations.\n" + " " PROGNAME " --list\n" + " List available individual test names.\n" + " " PROGNAME " -i 1000 -t torture_pkd_rsa_ecdh_sha2_nistp256\n" + " Run only the torture_pkd_rsa_ecdh_sha2_nistp256 testcase 1000 times.\n" + " " PROGNAME " -v -v -v -v -e -o\n" + " Run all tests with maximum libssh and pkd logging.\n" +; + +static struct argp_option options[] = { + { "stderr", 'e', NULL, 0, + "Emit pkd stderr messages", 0 }, + { "list", 'l', NULL, 0, + "List available individual test names", 0 }, + { "iterations", 'i', "number", 0, + "Run each test for the given number of iterations (default is 10)", 0 }, + { "stdout", 'o', NULL, 0, + "Emit pkd stdout messages", 0 }, + { "test", 't', "testname", 0, + "Run tests matching the given testname", 0 }, + { "verbose", 'v', NULL, 0, + "Increase libssh verbosity (can be used multiple times)", 0 }, + { NULL, 0, NULL, 0, + NULL, 0 }, +}; + +static error_t parse_opt(int key, char *arg, struct argp_state *state) { + (void) arg; + (void) state; + + switch(key) { + case 'e': + pkd_dargs.opts.log_stderr = 1; + break; + case 'l': + pkd_dargs.opts.list = 1; + break; + case 'i': + pkd_dargs.opts.iterations = atoi(arg); + break; + case 'o': + pkd_dargs.opts.log_stdout = 1; + break; + case 't': + pkd_dargs.opts.testname = arg; + break; + case 'v': + pkd_dargs.opts.libssh_log_level += 1; + break; + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +static struct argp parser = { + options, + parse_opt, + NULL, + doc, + NULL, + NULL, + NULL +}; +#endif /* HAVE_ARGP_H */ + +static struct pkd_state *torture_pkd_setup(enum pkd_hostkey_type_e type, + const char *hostkeypath) { + int rc = 0; + + pkd_dargs.type = type; + pkd_dargs.hostkeypath = hostkeypath; + + rc = pkd_start(&pkd_dargs); + assert_int_equal(rc, 0); + + return NULL; +} + +static void torture_pkd_teardown(void **state) { + struct pkd_result result = { .ok = 0 }; + + (void) state; + + pkd_stop(&result); + assert_int_equal(result.ok, 1); +} + +/* + * one setup for each server keytype ------------------------------------ + */ + +static void torture_pkd_setup_noop(void **state) { + *state = (void *) torture_pkd_setup(PKD_RSA, NULL /*path*/); +} + +static void torture_pkd_setup_rsa(void **state) { + setup_rsa_key(); + *state = (void *) torture_pkd_setup(PKD_RSA, LIBSSH_RSA_TESTKEY); +} + +static void torture_pkd_setup_dsa(void **state) { + setup_dsa_key(); + *state = (void *) torture_pkd_setup(PKD_DSA, LIBSSH_DSA_TESTKEY); +} + +static void torture_pkd_setup_ecdsa_256(void **state) { + setup_ecdsa_keys(); + *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_256_TESTKEY); +} + +static void torture_pkd_setup_ecdsa_384(void **state) { + setup_ecdsa_keys(); + *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_384_TESTKEY); +} + +static void torture_pkd_setup_ecdsa_521(void **state) { + setup_ecdsa_keys(); + *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_521_TESTKEY); +} + +/* + * Test matrices: f(clientname, testname, ssh-command, setup-function, teardown-function). + */ + +#define PKDTESTS_DEFAULT(f, client, cmd) \ + /* Default passes by server key type. */ \ + f(client, rsa_default, cmd, setup_rsa, teardown) \ + f(client, dsa_default, cmd, setup_dsa, teardown) \ + f(client, ecdsa_256_default, cmd, setup_ecdsa_256, teardown) \ + f(client, ecdsa_384_default, cmd, setup_ecdsa_384, teardown) \ + f(client, ecdsa_521_default, cmd, setup_ecdsa_521, teardown) + +#define PKDTESTS_KEX(f, client, kexcmd) \ + /* Kex algorithms. */ \ + f(client, rsa_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_rsa, teardown) \ + f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_rsa, teardown) \ + f(client, rsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_rsa, teardown) \ + f(client, rsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_rsa, teardown) \ + f(client, dsa_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_dsa, teardown) \ + f(client, dsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_dsa, teardown) \ + f(client, dsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_dsa, teardown) \ + f(client, dsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_dsa, teardown) \ + f(client, ecdsa_256_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_384_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_521_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_521, teardown) + +#define PKDTESTS_CIPHER(f, client, ciphercmd) \ + /* Ciphers. */ \ + f(client, rsa_3des_cbc, ciphercmd("3des-cbc"), setup_rsa, teardown) \ + f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown) \ + f(client, rsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_rsa, teardown) \ + f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \ + f(client, rsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_rsa, teardown) \ + f(client, rsa_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_rsa, teardown) \ + f(client, dsa_3des_cbc, ciphercmd("3des-cbc"), setup_dsa, teardown) \ + f(client, dsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_dsa, teardown) \ + f(client, dsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_dsa, teardown) \ + f(client, dsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_dsa, teardown) \ + f(client, dsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_dsa, teardown) \ + f(client, dsa_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_dsa, teardown) \ + f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_521, teardown) + +#define PKDTESTS_CIPHER_AES192(f, client, ciphercmd) \ + /* Ciphers. */ \ + f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \ + f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \ + f(client, dsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_dsa, teardown) \ + f(client, dsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_dsa, teardown) \ + f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \ + f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown) + +#define PKDTESTS_MAC(f, client, maccmd) \ + /* MACs. */ \ + f(client, rsa_hmac_sha1, maccmd("hmac-sha1"), setup_rsa, teardown) \ + f(client, dsa_hmac_sha1, maccmd("hmac-sha1"), setup_dsa, teardown) \ + f(client, ecdsa_256_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_384_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_521_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_521, teardown) \ + f(client, rsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_rsa, teardown) \ + f(client, dsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_dsa, teardown) \ + f(client, ecdsa_256_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_384_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_521_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_521, teardown) \ + f(client, rsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_rsa, teardown) \ + f(client, dsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_dsa, teardown) \ + f(client, ecdsa_256_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_256, teardown) \ + f(client, ecdsa_384_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_384, teardown) \ + f(client, ecdsa_521_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_521, teardown) + +static void torture_pkd_client_noop(void **state) { + struct pkd_state *pstate = (struct pkd_state *) (*state); + (void) pstate; + return; +} + +static void torture_pkd_runtest(const char *testname, + const char *testcmd) +{ + int i, rc; + char logfile[1024] = { 0 }; + int iterations = + (pkd_dargs.opts.iterations != 0) ? pkd_dargs.opts.iterations + : DEFAULT_ITERATIONS; + + for (i = 0; i < iterations; i++) { + rc = system_checked(testcmd); + assert_int_equal(rc, 0); + } + + /* Asserts did not trip: cleanup logs. */ + snprintf(&logfile[0], sizeof(logfile), "%s.out", testname); + unlink(logfile); + snprintf(&logfile[0], sizeof(logfile), "%s.err", testname); + unlink(logfile); +} + +/* + * Though each keytest function body is the same, separate functions are + * defined here to result in distinct output when running the tests. + */ + +#define emit_keytest(client, testname, sshcmd, setup, teardown) \ + static void torture_pkd_## client ## _ ## testname(void **state) { \ + const char *tname = "torture_pkd_" #client "_" #testname; \ + char testcmd[1024] = { 0 }; \ + (void) state; \ + snprintf(&testcmd[0], sizeof(testcmd), sshcmd, tname, tname); \ + torture_pkd_runtest(tname, testcmd); \ + } + +/* + * Actual test functions are emitted here. + */ + +#define CLIENT_ID_FILE OPENSSH_DSA_TESTKEY +PKDTESTS_DEFAULT(emit_keytest, openssh_dsa, OPENSSH_CMD) +PKDTESTS_KEX(emit_keytest, openssh_dsa, OPENSSH_KEX_CMD) +PKDTESTS_CIPHER(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD) +PKDTESTS_CIPHER_AES192(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD) +PKDTESTS_MAC(emit_keytest, openssh_dsa, OPENSSH_MAC_CMD) +#undef CLIENT_ID_FILE + +#define CLIENT_ID_FILE OPENSSH_RSA_TESTKEY +PKDTESTS_DEFAULT(emit_keytest, openssh_rsa, OPENSSH_CMD) +PKDTESTS_KEX(emit_keytest, openssh_rsa, OPENSSH_KEX_CMD) +PKDTESTS_CIPHER(emit_keytest, openssh_rsa, OPENSSH_CIPHER_CMD) +PKDTESTS_CIPHER_AES192(emit_keytest, openssh_rsa, OPENSSH_CIPHER_CMD) +PKDTESTS_MAC(emit_keytest, openssh_rsa, OPENSSH_MAC_CMD) +#undef CLIENT_ID_FILE + +#define CLIENT_ID_FILE OPENSSH_ECDSA256_TESTKEY +PKDTESTS_DEFAULT(emit_keytest, openssh_e256, OPENSSH_CMD) +PKDTESTS_KEX(emit_keytest, openssh_e256, OPENSSH_KEX_CMD) +PKDTESTS_CIPHER(emit_keytest, openssh_e256, OPENSSH_CIPHER_CMD) +PKDTESTS_CIPHER_AES192(emit_keytest, openssh_e256, OPENSSH_CIPHER_CMD) +PKDTESTS_MAC(emit_keytest, openssh_e256, OPENSSH_MAC_CMD) +#undef CLIENT_ID_FILE + +/* Could add these passes, too: */ +//#define CLIENT_ID_FILE OPENSSH_ECDSA384_TESTKEY +//#define CLIENT_ID_FILE OPENSSH_ECDSA521_TESTKEY + +#define CLIENT_ID_FILE OPENSSH_ED25519_TESTKEY +PKDTESTS_DEFAULT(emit_keytest, openssh_ed, OPENSSH_CMD) +PKDTESTS_KEX(emit_keytest, openssh_ed, OPENSSH_KEX_CMD) +PKDTESTS_CIPHER(emit_keytest, openssh_ed, OPENSSH_CIPHER_CMD) +PKDTESTS_CIPHER_AES192(emit_keytest, openssh_ed, OPENSSH_CIPHER_CMD) +PKDTESTS_MAC(emit_keytest, openssh_ed, OPENSSH_MAC_CMD) +#undef CLIENT_ID_FILE + +#define CLIENT_ID_FILE DROPBEAR_RSA_TESTKEY +PKDTESTS_DEFAULT(emit_keytest, dropbear, DROPBEAR_CMD) +PKDTESTS_CIPHER(emit_keytest, dropbear, DROPBEAR_CIPHER_CMD) +PKDTESTS_MAC(emit_keytest, dropbear, DROPBEAR_MAC_CMD) +#undef CLIENT_ID_FILE + +/* + * Define an array of testname strings mapped to their associated + * test function. Enables running tests individually by name from + * the command line. + */ + +#define emit_testmap(client, testname, sshcmd, setup, teardown) \ + { "torture_pkd_" #client "_" #testname, \ + { emit_unit_test(client, testname, sshcmd, setup, teardown) } }, + +#define emit_unit_test(client, testname, sshcmd, setup, teardown) \ + unit_test_setup_teardown(torture_pkd_ ## client ## _ ## testname, \ + torture_pkd_ ## setup, \ + torture_pkd_ ## teardown) + +#define emit_unit_test_comma(client, testname, sshcmd, setup, teardown) \ + emit_unit_test(client, testname, sshcmd, setup, teardown), + +struct { + const char *testname; + const UnitTest test[3]; /* requires setup + test + teardown */ +} testmap[] = { + /* OpenSSH */ + PKDTESTS_DEFAULT(emit_testmap, openssh_dsa, OPENSSH_CMD) + PKDTESTS_KEX(emit_testmap, openssh_dsa, OPENSSH_KEX_CMD) + PKDTESTS_CIPHER(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD) + PKDTESTS_CIPHER_AES192(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD) + PKDTESTS_MAC(emit_testmap, openssh_dsa, OPENSSH_MAC_CMD) + + PKDTESTS_DEFAULT(emit_testmap, openssh_rsa, OPENSSH_CMD) + PKDTESTS_KEX(emit_testmap, openssh_rsa, OPENSSH_KEX_CMD) + PKDTESTS_CIPHER(emit_testmap, openssh_rsa, OPENSSH_CIPHER_CMD) + PKDTESTS_CIPHER_AES192(emit_testmap, openssh_rsa, OPENSSH_CIPHER_CMD) + PKDTESTS_MAC(emit_testmap, openssh_rsa, OPENSSH_MAC_CMD) + + PKDTESTS_DEFAULT(emit_testmap, openssh_e256, OPENSSH_CMD) + PKDTESTS_KEX(emit_testmap, openssh_e256, OPENSSH_KEX_CMD) + PKDTESTS_CIPHER(emit_testmap, openssh_e256, OPENSSH_CIPHER_CMD) + PKDTESTS_CIPHER_AES192(emit_testmap, openssh_e256, OPENSSH_CIPHER_CMD) + PKDTESTS_MAC(emit_testmap, openssh_e256, OPENSSH_MAC_CMD) + + PKDTESTS_DEFAULT(emit_testmap, openssh_ed, OPENSSH_CMD) + PKDTESTS_KEX(emit_testmap, openssh_ed, OPENSSH_KEX_CMD) + PKDTESTS_CIPHER(emit_testmap, openssh_ed, OPENSSH_CIPHER_CMD) + PKDTESTS_CIPHER_AES192(emit_testmap, openssh_ed, OPENSSH_CIPHER_CMD) + PKDTESTS_MAC(emit_testmap, openssh_ed, OPENSSH_MAC_CMD) + + /* Dropbear */ + PKDTESTS_DEFAULT(emit_testmap, dropbear, DROPBEAR_CMD) + PKDTESTS_CIPHER(emit_testmap, dropbear, DROPBEAR_CIPHER_CMD) + PKDTESTS_MAC(emit_testmap, dropbear, DROPBEAR_MAC_CMD) + + /* Noop */ + emit_testmap(client, noop, "", setup_noop, teardown) + + /* NULL tail entry */ + { NULL, { { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 } } } +}; + +static int pkd_run_tests(void) { + int rc = -1; + int tindex = 0; + + const UnitTest openssh_tests[] = { + PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_dsa, OPENSSH_CMD) + PKDTESTS_KEX(emit_unit_test_comma, openssh_dsa, OPENSSH_KEX_CMD) + PKDTESTS_CIPHER(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD) + PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD) + PKDTESTS_MAC(emit_unit_test_comma, openssh_dsa, OPENSSH_MAC_CMD) + + PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_rsa, OPENSSH_CMD) + PKDTESTS_KEX(emit_unit_test_comma, openssh_rsa, OPENSSH_KEX_CMD) + PKDTESTS_CIPHER(emit_unit_test_comma, openssh_rsa, OPENSSH_CIPHER_CMD) + PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_rsa, OPENSSH_CIPHER_CMD) + PKDTESTS_MAC(emit_unit_test_comma, openssh_rsa, OPENSSH_MAC_CMD) + + PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_e256, OPENSSH_CMD) + PKDTESTS_KEX(emit_unit_test_comma, openssh_e256, OPENSSH_KEX_CMD) + PKDTESTS_CIPHER(emit_unit_test_comma, openssh_e256, OPENSSH_CIPHER_CMD) + PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_e256, OPENSSH_CIPHER_CMD) + PKDTESTS_MAC(emit_unit_test_comma, openssh_e256, OPENSSH_MAC_CMD) + + PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_ed, OPENSSH_CMD) + PKDTESTS_KEX(emit_unit_test_comma, openssh_ed, OPENSSH_KEX_CMD) + PKDTESTS_CIPHER(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD) + PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD) + PKDTESTS_MAC(emit_unit_test_comma, openssh_ed, OPENSSH_MAC_CMD) + }; + + const UnitTest dropbear_tests[] = { + PKDTESTS_DEFAULT(emit_unit_test_comma, dropbear, DROPBEAR_CMD) + PKDTESTS_CIPHER(emit_unit_test_comma, dropbear, DROPBEAR_CIPHER_CMD) + PKDTESTS_MAC(emit_unit_test_comma, dropbear, DROPBEAR_MAC_CMD) + }; + + const UnitTest noop_tests[] = { + emit_unit_test(client, noop, "", setup_noop, teardown) + }; + + /* Test list is populated depending on which clients are enabled. */ + UnitTest all_tests[(sizeof(openssh_tests) / sizeof(openssh_tests[0])) + + (sizeof(dropbear_tests) / sizeof(dropbear_tests[0])) + + (sizeof(noop_tests) / sizeof(noop_tests[0]))]; + memset(&all_tests[0], 0x0, sizeof(all_tests)); + + /* Generate client keys and populate test list for each enabled client. */ + if (is_openssh_client_enabled()) { + setup_openssh_client_keys(); + memcpy(&all_tests[tindex], &openssh_tests[0], sizeof(openssh_tests)); + tindex += (sizeof(openssh_tests) / sizeof(openssh_tests[0])); + } + + if (is_dropbear_client_enabled()) { + setup_dropbear_client_rsa_key(); + memcpy(&all_tests[tindex], &dropbear_tests[0], sizeof(dropbear_tests)); + tindex += (sizeof(dropbear_tests) / sizeof(dropbear_tests[0])); + } + + memcpy(&all_tests[tindex], &noop_tests[0], sizeof(noop_tests)); + tindex += (sizeof(noop_tests) / sizeof(noop_tests[0])); + + if (pkd_dargs.opts.testname == NULL) { + rc = _run_tests(all_tests, tindex); + } else { + int i = 0; + const UnitTest *found = NULL; + const char *testname = pkd_dargs.opts.testname; + + while (testmap[i].testname != NULL) { + if (strcmp(testmap[i].testname, testname) == 0) { + found = &testmap[i].test[0]; + break; + } + i += 1; + } + + if (found != NULL) { + rc = _run_tests(found, 3); + } else { + fprintf(stderr, "Did not find test '%s'\n", testname); + } + } + + /* Clean up client keys for each enabled client. */ + if (is_dropbear_client_enabled()) { + cleanup_dropbear_client_rsa_key(); + } + + if (is_openssh_client_enabled()) { + cleanup_openssh_client_keys(); + } + + /* Clean up any server keys that were generated. */ + cleanup_rsa_key(); + cleanup_dsa_key(); + cleanup_ecdsa_keys(); + + return rc; +} + +int main(int argc, char **argv) { + int i = 0; + int rc = 0; + + unsetenv("SSH_AUTH_SOCK"); + + rc = ssh_init(); + if (rc != 0) { + rc = SSH_ERROR; + goto out; + } + +#ifdef HAVE_ARGP_H + argp_parse(&parser, argc, argv, 0, 0, NULL); +#else /* HAVE_ARGP_H */ + (void) argc; (void) argv; +#endif /* HAVE_ARGP_H */ + + if (pkd_dargs.opts.list != 0) { + while (testmap[i].testname != NULL) { + printf("%s\n", testmap[i++].testname); + } + } else { + rc = pkd_run_tests(); + } + + rc = ssh_finalize(); + if (rc != 0) { + fprintf(stderr, "ssh_finalize: %d\n", rc); + } +out: + return rc; +} diff --git a/libssh/tests/pkd/pkd_keyutil.c b/libssh/tests/pkd/pkd_keyutil.c new file mode 100644 index 00000000..e1e1ecb8 --- /dev/null +++ b/libssh/tests/pkd/pkd_keyutil.c @@ -0,0 +1,138 @@ +/* + * pkd_keyutil.c -- pkd test key utilities + * + * (c) 2014 Jon Simons + */ + +#include // for cmocka +#include // for cmocka +#include // for cmocka +#include + +#include +#include +#include +#include + +#include "pkd_client.h" +#include "pkd_keyutil.h" +#include "pkd_util.h" + +void setup_rsa_key() { + int rc = 0; + if (access(LIBSSH_RSA_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f " + LIBSSH_RSA_TESTKEY); + } + assert_int_equal(rc, 0); +} + +void setup_dsa_key() { + int rc = 0; + if (access(LIBSSH_DSA_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f " + LIBSSH_DSA_TESTKEY); + } + assert_int_equal(rc, 0); +} + +void setup_ecdsa_keys() { + int rc = 0; + + if (access(LIBSSH_ECDSA_256_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 256 -q -N \"\" -f " + LIBSSH_ECDSA_256_TESTKEY); + assert_int_equal(rc, 0); + } + if (access(LIBSSH_ECDSA_384_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 384 -q -N \"\" -f " + LIBSSH_ECDSA_384_TESTKEY); + assert_int_equal(rc, 0); + } + if (access(LIBSSH_ECDSA_521_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 521 -q -N \"\" -f " + LIBSSH_ECDSA_521_TESTKEY); + assert_int_equal(rc, 0); + } +} + +static void cleanup_key(const char *privkey, const char *pubkey) { + unlink(privkey); + unlink(pubkey); +} + +void cleanup_rsa_key() { + cleanup_key(LIBSSH_RSA_TESTKEY, LIBSSH_RSA_TESTKEY ".pub"); +} + +void cleanup_dsa_key() { + cleanup_key(LIBSSH_DSA_TESTKEY, LIBSSH_DSA_TESTKEY ".pub"); +} + +void cleanup_ecdsa_keys() { + cleanup_key(LIBSSH_ECDSA_256_TESTKEY, LIBSSH_ECDSA_256_TESTKEY ".pub"); + cleanup_key(LIBSSH_ECDSA_384_TESTKEY, LIBSSH_ECDSA_384_TESTKEY ".pub"); + cleanup_key(LIBSSH_ECDSA_521_TESTKEY, LIBSSH_ECDSA_521_TESTKEY ".pub"); +} + +void setup_openssh_client_keys() { + int rc = 0; + + if (access(OPENSSH_DSA_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f " + OPENSSH_DSA_TESTKEY); + } + assert_int_equal(rc, 0); + + if (access(OPENSSH_RSA_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f " + OPENSSH_RSA_TESTKEY); + } + assert_int_equal(rc, 0); + + if (access(OPENSSH_ECDSA256_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 256 -q -N \"\" -f " + OPENSSH_ECDSA256_TESTKEY); + } + assert_int_equal(rc, 0); + + if (access(OPENSSH_ECDSA384_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 384 -q -N \"\" -f " + OPENSSH_ECDSA384_TESTKEY); + } + assert_int_equal(rc, 0); + + if (access(OPENSSH_ECDSA521_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 521 -q -N \"\" -f " + OPENSSH_ECDSA521_TESTKEY); + } + assert_int_equal(rc, 0); + + if (access(OPENSSH_ED25519_TESTKEY, F_OK) != 0) { + rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f " + OPENSSH_ED25519_TESTKEY); + } + assert_int_equal(rc, 0); +} + +void cleanup_openssh_client_keys() { + cleanup_key(OPENSSH_DSA_TESTKEY, OPENSSH_DSA_TESTKEY ".pub"); + cleanup_key(OPENSSH_RSA_TESTKEY, OPENSSH_RSA_TESTKEY ".pub"); + cleanup_key(OPENSSH_ECDSA256_TESTKEY, OPENSSH_ECDSA256_TESTKEY ".pub"); + cleanup_key(OPENSSH_ECDSA384_TESTKEY, OPENSSH_ECDSA384_TESTKEY ".pub"); + cleanup_key(OPENSSH_ECDSA521_TESTKEY, OPENSSH_ECDSA521_TESTKEY ".pub"); + cleanup_key(OPENSSH_ED25519_TESTKEY, OPENSSH_ED25519_TESTKEY ".pub"); +} + +void setup_dropbear_client_rsa_key() { + int rc = 0; + if (access(DROPBEAR_RSA_TESTKEY, F_OK) != 0) { + rc = system_checked(DROPBEAR_KEYGEN " -t rsa -f " + DROPBEAR_RSA_TESTKEY " 1>/dev/null 2>/dev/null"); + } + assert_int_equal(rc, 0); +} + +void cleanup_dropbear_client_rsa_key() { + unlink(DROPBEAR_RSA_TESTKEY); +} diff --git a/libssh/tests/pkd/pkd_keyutil.h b/libssh/tests/pkd/pkd_keyutil.h new file mode 100644 index 00000000..8e9de009 --- /dev/null +++ b/libssh/tests/pkd/pkd_keyutil.h @@ -0,0 +1,40 @@ +/* + * pkd_keyutil.h -- + * + * (c) 2014 Jon Simons + */ + +#ifndef __PKD_KEYUTIL_H__ +#define __PKD_KEYUTIL_H__ + +/* Server keys. */ +#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa" +#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa" +#define LIBSSH_ECDSA_256_TESTKEY "libssh_testkey.id_ecdsa256" +#define LIBSSH_ECDSA_384_TESTKEY "libssh_testkey.id_ecdsa384" +#define LIBSSH_ECDSA_521_TESTKEY "libssh_testkey.id_ecdsa521" + +void setup_dsa_key(void); +void setup_rsa_key(void); +void setup_ecdsa_keys(void); +void cleanup_dsa_key(void); +void cleanup_rsa_key(void); +void cleanup_ecdsa_keys(void); + +/* Client keys. */ +#define OPENSSH_DSA_TESTKEY "openssh_testkey.id_dsa" +#define OPENSSH_RSA_TESTKEY "openssh_testkey.id_rsa" +#define OPENSSH_ECDSA256_TESTKEY "openssh_testkey.id_ecdsa256" +#define OPENSSH_ECDSA384_TESTKEY "openssh_testkey.id_ecdsa384" +#define OPENSSH_ECDSA521_TESTKEY "openssh_testkey.id_ecdsa521" +#define OPENSSH_ED25519_TESTKEY "openssh_testkey.id_ed25519" + +#define DROPBEAR_RSA_TESTKEY "dropbear_testkey.id_rsa" + +void setup_openssh_client_keys(void); +void cleanup_openssh_client_keys(void); + +void setup_dropbear_client_rsa_key(void); +void cleanup_dropbear_client_rsa_key(void); + +#endif /* __PKD_KEYUTIL_H__ */ diff --git a/libssh/tests/pkd/pkd_util.c b/libssh/tests/pkd/pkd_util.c new file mode 100644 index 00000000..963b58d6 --- /dev/null +++ b/libssh/tests/pkd/pkd_util.c @@ -0,0 +1,46 @@ +/* + * pkd_util.c -- pkd utilities + * + * (c) 2014 Jon Simons + */ + +#include +#include +#include +#include + +#include "pkd_client.h" +#include "pkd_util.h" + +/** + * @brief runs system(3); exits if that is interrupted with SIGINT/QUIT + * @returns 0 upon success, non-zero otherwise + */ +int system_checked(const char *cmd) { + int rc = system(cmd); + + if (WIFSIGNALED(rc) && + ((WTERMSIG(rc) == SIGINT) || (WTERMSIG(rc) == SIGQUIT))) { + exit(1); + } + + if (rc == -1) { + return -1; + } + + return WEXITSTATUS(rc); +} + +static int bin_exists(const char *binary) { + char bin[1024] = { 0 }; + snprintf(&bin[0], sizeof(bin), "type %s 1>/dev/null 2>/dev/null", binary); + return (system_checked(bin) == 0); +} + +int is_openssh_client_enabled(void) { + return (bin_exists(OPENSSH_BINARY) && bin_exists(OPENSSH_KEYGEN)); +} + +int is_dropbear_client_enabled(void) { + return (bin_exists(DROPBEAR_BINARY) && bin_exists(DROPBEAR_KEYGEN)); +} diff --git a/libssh/tests/pkd/pkd_util.h b/libssh/tests/pkd/pkd_util.h new file mode 100644 index 00000000..aedbbe9f --- /dev/null +++ b/libssh/tests/pkd/pkd_util.h @@ -0,0 +1,16 @@ +/* + * pkd_keyutil.h -- + * + * (c) 2014 Jon Simons + */ + +#ifndef __PKD_UTIL_H__ +#define __PKD_UTIL_H__ + +int system_checked(const char *cmd); + +/* Is client 'X' enabled? */ +int is_openssh_client_enabled(void); +int is_dropbear_client_enabled(void); + +#endif /* __PKD_UTIL_H__ */ diff --git a/libssh/tests/test_ssh_bind_accept_fd.c b/libssh/tests/test_ssh_bind_accept_fd.c new file mode 100644 index 00000000..7611cf4c --- /dev/null +++ b/libssh/tests/test_ssh_bind_accept_fd.c @@ -0,0 +1,139 @@ +/* Test the ability to use ssh_bind_accept_fd. + * + * Expected behavior: Prints "SUCCESS!" + * + * Faulty behavior observed before change: Connection timeout + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct options { + const char *server_keyfile; +} options; + +const char HOST[] = "127.0.0.1"; +const int PORT = 3333; + +int get_connection() { + int rc, server_socket, client_conn = -1; + struct sockaddr_in server_socket_addr; + struct sockaddr_storage client_conn_addr; + socklen_t client_conn_addr_size = sizeof(client_conn_addr); + + server_socket = socket(PF_INET, SOCK_STREAM, 0); + if (server_socket < 0) { + goto out; + } + + server_socket_addr.sin_family = AF_INET; + server_socket_addr.sin_port = htons(PORT); + if (inet_pton(AF_INET, HOST, &server_socket_addr.sin_addr) != 1) { + goto out; + } + + rc = bind(server_socket, (struct sockaddr *)&server_socket_addr, + sizeof(server_socket_addr)); + if (rc < 0) { + goto out; + } + + if (listen(server_socket, 0) < 0) { + goto out; + } + + client_conn = accept(server_socket, + (struct sockaddr *)&client_conn_addr, + &client_conn_addr_size); + + out: + return client_conn; +} + +void ssh_server() { + ssh_bind bind; + ssh_session session; + + int client_conn = get_connection(); + if (client_conn < 0) { + err(1, "get_connection"); + } + + bind = ssh_bind_new(); + if (!bind) { + errx(1, "ssh_bind_new"); + } + + if (ssh_bind_options_set(bind, SSH_BIND_OPTIONS_DSAKEY, + options.server_keyfile) != SSH_OK) { + errx(1, "ssh_bind_options_set(SSH_BIND_OPTIONS_DSAKEY"); + } + + session = ssh_new(); + if (!session) { + errx(1, "ssh_new"); + } + + if (ssh_bind_accept_fd(bind, session, client_conn) != SSH_OK) { + errx(1, "ssh_bind_accept: %s", ssh_get_error(bind)); + } + + if (ssh_handle_key_exchange(session) != SSH_OK) { + errx(1, "ssh_handle_key_exchange: %s", ssh_get_error(session)); + } + + printf("SUCCESS!\n"); +} + +void ssh_client() { + ssh_session session; + + session = ssh_new(); + if (!session) { + errx(1, "ssh_new"); + } + + if (ssh_options_set(session, SSH_OPTIONS_HOST, HOST) < 0) { + errx(1, "ssh_options_set(SSH_OPTIONS_HOST)"); + } + if (ssh_options_set(session, SSH_OPTIONS_PORT, &PORT) < 0) { + errx(1, "ssh_options_set(SSH_OPTIONS_PORT)"); + } + + if (ssh_connect(session) != SSH_OK) { + errx(1, "ssh_connect: %s", ssh_get_error(session)); + } +} + +int main(int argc, const char *argv[]) { + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + exit(1); + } + + options.server_keyfile = argv[1]; + + pid_t pid = fork(); + if (pid < 0) { + errx(1, "fork"); + } + if (pid == 0) { + /* Allow the server to get set up */ + sleep(3); + + ssh_client(); + } else { + ssh_server(); + } + + return 0; +} diff --git a/libssh/tests/torture.c b/libssh/tests/torture.c index c7031fcb..0b8eb3da 100644 --- a/libssh/tests/torture.c +++ b/libssh/tests/torture.c @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2008-2009 by Andreas Schneider + * Copyright (c) 2008-2009 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -25,9 +25,11 @@ #include #include +#include +#include +#include + #ifndef _WIN32 -# include -# include # include # include #endif @@ -38,6 +40,162 @@ #include "torture.h" +#define TORTURE_TESTKEY_PASSWORD "libssh-rocks" + +static const char torture_rsa_testkey[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEowIBAAKCAQEArAOREUWlBXJAKZ5hABYyxnRayDZP1bJeLbPVK+npxemrhHyZ\n" + "gjdbY3ADot+JRyWjvll2w2GI+3blt0j+x/ZWwjMKu/QYcycYp5HL01goxOxuusZb\n" + "i+KiHRGB6z0EMdXM7U82U7lA/j//HyZppyDjUDniWabXQJge8ksGXGTiFeAJ/687\n" + "uV+JJcjGPxAGFQxzyjitf/FrL9S0WGKZbyqeGDzyeBZ1NLIuaiOORyLGSW4duHLD\n" + "N78EmsJnwqg2gJQmRSaD4BNZMjtbfiFcSL9Uw4XQFTsWugUDEY1AU4c5g11nhzHz\n" + "Bi9qMOt5DzrZQpD4j0gA2LOHpHhoOdg1ZuHrGQIDAQABAoIBAFJTaqy/jllq8vZ4\n" + "TKiD900wBvrns5HtSlHJTe80hqQoT+Sa1cWSxPR0eekL32Hjy9igbMzZ83uWzh7I\n" + "mtgNODy9vRdznfgO8CfTCaBfAzQsjFpr8QikMT6EUI/LpiRL1UaGsNOlSEvnSS0Z\n" + "b1uDzAdrjL+nsEHEDJud+K9jwSkCRifVMy7fLfaum+YKpdeEz7K2Mgm5pJ/Vg+9s\n" + "vI2V1q7HAOI4eUVTgJNHXy5ediRJlajQHf/lNUzHKqn7iH+JRl01gt62X8roG62b\n" + "TbFylbheqMm9awuSF2ucOcx+guuwhkPir8BEMb08j3hiK+TfwPdY0F6QH4OhiKK7\n" + "MTqTVgECgYEA0vmmu5GOBtwRmq6gVNCHhdLDQWaxAZqQRmRbzxVhFpbv0GjbQEF7\n" + "tttq3fjDrzDf6CE9RtZWw2BUSXVq+IXB/bXb1kgWU2xWywm+OFDk9OXQs8ui+MY7\n" + "FiP3yuq3YJob2g5CCsVQWl2CHvWGmTLhE1ODll39t7Y1uwdcDobJN+ECgYEA0LlR\n" + "hfMjydWmwqooU9TDjXNBmwufyYlNFTH351amYgFUDpNf35SMCP4hDosUw/zCTDpc\n" + "+1w04BJJfkH1SNvXSOilpdaYRTYuryDvGmWC66K2KX1nLErhlhs17CwzV997nYgD\n" + "H3OOU4HfqIKmdGbjvWlkmY+mLHyG10bbpOTbujkCgYAc68xHejSWDCT9p2KjPdLW\n" + "LYZGuOUa6y1L+QX85Vlh118Ymsczj8Z90qZbt3Zb1b9b+vKDe255agMj7syzNOLa\n" + "/MseHNOyq+9Z9gP1hGFekQKDIy88GzCOYG/fiT2KKJYY1kuHXnUdbiQgSlghODBS\n" + "jehD/K6DOJ80/FVKSH/dAQKBgQDJ+apTzpZhJ2f5k6L2jDq3VEK2ACedZEm9Kt9T\n" + "c1wKFnL6r83kkuB3i0L9ycRMavixvwBfFDjuY4POs5Dh8ip/mPFCa0hqISZHvbzi\n" + "dDyePJO9zmXaTJPDJ42kfpkofVAnfohXFQEy+cguTk848J+MmMIKfyE0h0QMabr9\n" + "86BUsQKBgEVgoi4RXwmtGovtMew01ORPV9MOX3v+VnsCgD4/56URKOAngiS70xEP\n" + "ONwNbTCWuuv43HGzJoVFiAMGnQP1BAJ7gkHkjSegOGKkiw12EPUWhFcMg+GkgPhc\n" + "pOqNt/VMBPjJ/ysHJqmLfQK9A35JV6Cmdphe+OIl28bcKhAOz8Dw\n" + "-----END RSA PRIVATE KEY-----\n"; + +static const char torture_rsa_testkey_pub[] = + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsA5ERRaUFckApnmEAFjLGdFrIN" + "k/Vsl4ts9Ur6enF6auEfJmCN1tjcAOi34lHJaO+WXbDYYj7duW3SP7H9lbCMwq79B" + "hzJxinkcvTWCjE7G66xluL4qIdEYHrPQQx1cztTzZTuUD+P/8fJmmnIONQOeJZptd" + "AmB7ySwZcZOIV4An/rzu5X4klyMY/EAYVDHPKOK1/8Wsv1LRYYplvKp4YPPJ4FnU0" + "si5qI45HIsZJbh24csM3vwSawmfCqDaAlCZFJoPgE1kyO1t+IVxIv1TDhdAVOxa6B" + "QMRjUBThzmDXWeHMfMGL2ow63kPOtlCkPiPSADYs4ekeGg52DVm4esZ " + "aris@aris-air\n"; + +static const char torture_dsa_testkey[] = + "-----BEGIN DSA PRIVATE KEY-----\n" + "MIIBuwIBAAKBgQCUyvVPEkn3UnZDjzCzSzSHpTltzr0Ec+1mz/JACjHMBJ9C/W/P\n" + "wvH3yjkfoFhhREvoY7IPnwAu5bcxw8TkISq7YROQ409PqwwPvy0N3GUp/+kKS268\n" + "BIJ+VKN513XRf7eL1e4aHUJ+al9x1JxTmc6T0GBq1lyu+CTUUyh25aNDFwIVAK84\n" + "j20GmU+zewjQwsIXuVb6C/PHAoGAXhuIVsJxUQJ5nWQRLf7o3XEGQ+EcVmHOzMB1\n" + "xCsHjYnpEhhco+r/HDZSD31kzDeAZUycz31WqGL8yXr+OZRLqEsGC7dwEAzPiXDu\n" + "l0zHcl0yiKPrRrLgNJHeKcT6JflBngK7jQRIVUg3F3104fbVa2rwaniLl4GSBZPX\n" + "MpUdng8CgYB4roDQBfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7\n" + "n2mw3iqZG0xnG3iv1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2u\n" + "ADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIgIVAI1Hd8/i\n" + "Pzsg7bTzoNvjQL+Noyiy\n" + "-----END DSA PRIVATE KEY-----\n"; + +static const char torture_dsa_testkey_pub[] = + "ssh-dss AAAAB3NzaC1kc3MAAACBAJTK9U8SSfdSdkOPMLNLNIelOW3OvQRz7WbP8k" + "AKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+fAC7ltzHDxOQhKrthE5DjT0+rDA+/LQ3c" + "ZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hodQn5qX3HUnFOZzpPQYGrWXK74JNRTKHblo0" + "MXAAAAFQCvOI9tBplPs3sI0MLCF7lW+gvzxwAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD" + "4RxWYc7MwHXEKweNiekSGFyj6v8cNlIPfWTMN4BlTJzPfVaoYvzJev45lEuoSwYLt3" + "AQDM+JcO6XTMdyXTKIo+tGsuA0kd4pxPol+UGeAruNBEhVSDcXfXTh9tVravBqeIuX" + "gZIFk9cylR2eDwAAAIB4roDQBfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9R" + "bUIVS7n2mw3iqZG0xnG3iv1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM" + "7b2uADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIg== " + "aris@aris-air\n"; + +static const char torture_rsa_testkey_pp[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: AES-128-CBC,5375534F40903DD66B3851A0DA03F6FA\n" + "\n" + "m5YYTNOMd1xCKfifwCX4R1iLJoAc4cn1aFiL7f2kBbfE2jF1LTQBJV1h1CqYZfAB\n" + "WtM/7FkQPnKXqsMndP+v+1Xc+PYigE3AezJj/0g7xn/zIBwGjkLAp435AdL5i6Fg\n" + "OhOL8LyolRrcGn17jE4S4iGbzw8PVyfzNzdj0Emwql5F6M7pgLbInRNKM/TF4z2h\n" + "b6Pi9Bw43dwaJ7wiiy/vo/v4MyXsJBoeKbc4VCmxiYFvAYCvVFlDkyIw/QnR3MKQ\n" + "g/Zsk7Pw3aOioxk6LJpZ5x0tO23nXDG1aOZHWykI0BpJV+LIpD2oSYOHJyVO83XT\n" + "RQUMSTXc2K2+ejs0XQoLt/GxDDHe+8W8fWQK3C7Lyvl9oKjmb5sTWi3mdSv0C+zR\n" + "n5KSVbUKNXrjix7qPKkv5rWqb84CKVnCMb7tWaPLR19nQqKVYBIs6v0OTTvS6Le7\n" + "lz4lxBkcUy6vi0tWH9MvLuT+ugdHLJZ4UXBthCgV58pM1o+L+WMIl+SZXckiCAO3\n" + "7ercA57695IA6iHskmr3eazJsYFEVFdR/cm+IDy2FPkKmJMjXeIWuh3yASBk7LBR\n" + "EQq3CC7AioO+Vj8m/fEIiNZJSQ6p0NmgnPoO3rTYT/IobmE99/Ht6oNLmFX4Pr7e\n" + "F4CGWKzwxWpCnw2vVolCFByASmZycbJvrIonZBKY1toU28lRm4tCM6eCNISVLMeE\n" + "VtQ+1PH9/2KZspZl+SX/kjV3egggy0TFKRU8EcYPJFC3Vpy+shEai35KBVo44Z18\n" + "apza7exm3igNEqOqe07hLs3Bjhvk1oS+WhMbAG9ARTOKuyBOJh/ZV9tFMNZ6v+q5\n" + "TofgNcIhNYNascymU1io18xTW9c3RRcmRKqIWnj4EH8o7Aojv/l+zvdV7/GVlR4W\n" + "pR9cuJEiyiEjS46axoc6dSOtdnvag+BpFQb+lGY97F9nNGyBdtLD5ASVh5OVG4fu\n" + "Pf0O7Bdj1kIuBhV8axE/slf6UHANiodeqkR9B24+0Cy+miPiHazzUkbdSJ4r03g5\n" + "J1Y5S8qbl9++sqhQMLMUkeK4pDWh1aocA9bDA2RcBNuXGiZeRFUiqxcBS+iO418n\n" + "DFyWz4UfI/m1IRSjoo/PEpgu5GmosUzs3Dl4nAcf/REBEX6M/kKKxHTLjE8DxDsz\n" + "fn/vfsXV3s0tbN7YyJdP8aU+ApZntw1OF2TS2qS8CPWHTcCGGTab5WEGC3xFXKp0\n" + "uyonCxV7vNLOiIiHdQX+1bLu7ps7GBH92xGkPg7FrNNcMc07soP7jjjB578n9Gpl\n" + "cIDBdgovTRFHiWu3yRspVt0zPfMJB/hqn+IAp98wfvjl8OZM1ZZkejnwXnQil5ZU\n" + "wjEBEtx+nX56vdxipzKoHh5yDXmPbNajBYkg3rXJrLFh3Tsf0CzHcLdHNz/qJ9LO\n" + "wH16grjR1Q0CzCW3FAv0Q0euqkXac+TfuIg3HiTPrBPnJQW1uivrx1F5tpO/uboG\n" + "h28LwqJLYh+1T0V//uiy3SMATpYKvzg2byGct9VUib8QVop8LvVF/n42RaxtTCfw\n" + "JSvUyxoaZUjQkT7iF94HsF+FVVJdI55UjgnMiZ0d5vKffWyTHYcYHkFYaSloAMWN\n" + "-----END RSA PRIVATE KEY-----\n"; + +static const char torture_dsa_testkey_pp[] = + "-----BEGIN DSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: AES-128-CBC,266023B64B1B814BCD0D0E477257F06D\n" + "\n" + "QJQErZrvYsfeMNMnU+6yVHH5Zze/zUFdPip7Bon4T1wCGlVasn4x/GQcMm1+mgmb\n" + "PCK/qJ5qw9nCepLYJq2xh8gohbwF/XKxeaNGcRA2+ancTooDUjeRTlk1WRtS1+bq\n" + "LBkwhxLXW26lIuQUHzfi93rRqQI2LC4McngY7L7WVJer7sH7hk5//4Gf6zHtPEl+\n" + "Tr2ub1zNrVbh6e1Bitw7DaGZNX6XEWpyTTsAd42sQWh6o23MC6GyfS1YFsPGHzGe\n" + "WYQbWn2AZ1mK32z2mLZfVg41qu9RKG20iCyaczZ2YmuYyOkoLHijOAHC8vZbHwYC\n" + "+lN9Yc8/BoMuMMwDTMDaJD0TsBX02hi9YI7Gu88PMCJO+SRe5400MonUMXTwCa91\n" + "Tt3RhYpBzx2XGOq5199+oLdTJAaXHJcuB6viKNdSLBuhx6RAEJXZnVexchaHs4Q6\n" + "HweIv6Et8MjVoqwkaQDmcIGA73qZ0lbUJFZAu2YDJ6TpHc1lHZes763HoMYfuvkX\n" + "HTSuHZ7edjoWqwnl/vkc3+nG//IEj8LqAacx0i4krDcQpGuQ6BnPfwPFco2NQQpw\n" + "wHBOL6HrOnD+gGs6DUFwzA==\n" + "-----END DSA PRIVATE KEY-----\n"; + +static const char torture_ecdsa256_testkey[] = + "-----BEGIN EC PRIVATE KEY-----\n" + "MHcCAQEEIBCDeeYYAtX3EnsP0ratwVpNTaA/4K1N6VvHMiUZlVdhoAoGCCqGSM49\n" + "AwEHoUQDQgAEx+9ud88Q5GWtLd+yMtYaapC85g+2ZLp7VtFHA0EbNHqBUQxoh+Ik\n" + "89Mlr7AUxcFPd+kCo+NE6yq/mNQcL7E6iQ==\n" + "-----END EC PRIVATE KEY-----\n"; + +static const char torture_ecdsa256_testkey_pub[] = + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNT" + "YAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPtmS6e1bRRwNBGzR6gVEMaIfiJPPTJa+w" + "FMXBT3fpAqPjROsqv5jUHC+xOok= aris@kalix86\n"; + +static const char torture_ecdsa384_testkey[] = + "-----BEGIN EC PRIVATE KEY-----\n" + "MIGkAgEBBDBY8jEa5DtRy4AVeTWhPJ/TK257behiC3uafEi6YA2oHORibqX55EDN\n" + "wz29MT40mQSgBwYFK4EEACKhZANiAARXc4BN6BrVo1QMi3+i/B85Lu7SMuzBi+1P\n" + "bJti8xz+Szgq64gaBGOK9o+WOdLAd/w7p7DJLdztJ0bYoyT4V3B3ZqR9RyGq6mYC\n" + "jkXlc5YbYHjueBbp0oeNXqsXHNAWQZo=\n" + "-----END EC PRIVATE KEY-----\n"; + +static const char torture_ecdsa384_testkey_pub[] = + "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzOD" + "QAAABhBFdzgE3oGtWjVAyLf6L8Hzku7tIy7MGL7U9sm2LzHP5LOCrriBoEY4r2j5Y5" + "0sB3/DunsMkt3O0nRtijJPhXcHdmpH1HIarqZgKOReVzlhtgeO54FunSh41eqxcc0B" + "ZBmg== aris@kalix86"; + +static const char torture_ecdsa521_testkey[] = + "-----BEGIN EC PRIVATE KEY-----\n" + "MIHbAgEBBEG83nSJ2SLoiBvEku1JteQKWx/Xt6THksgC7rrIaTUmNzk+60f0sCCm\n" + "Gll0dgrZLmeIw+TtnG1E20VZflCKq+IdkaAHBgUrgQQAI6GBiQOBhgAEAc6D728d\n" + "baQkHnSPtztaRwJw63CBl15cykB4SXXuwWdNOtPzBijUULMTTvBXbra8gL4ATd9d\n" + "Qnuwn8KQUh2T/z+BARjWPKhcHcGx57XpXCEkawzMYaHUUnRdeFEmNRsbXypsf0mJ\n" + "KATU3h8gzTMkbrx8DJTFHEIjXBShs44HsSYVl3Xy\n" + "-----END EC PRIVATE KEY-----\n"; + +static const char torture_ecdsa521_testkey_pub[] = + "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1Mj" + "EAAACFBAHOg+9vHW2kJB50j7c7WkcCcOtwgZdeXMpAeEl17sFnTTrT8wYo1FCzE07w" + "V262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jyoXB3Bsee16VwhJGsMzGGh1FJ0XXhRJj" + "UbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI1wUobOOB7EmFZd18g== aris@kalix86"; + static int verbosity = 0; #ifndef _WIN32 @@ -302,6 +460,84 @@ void torture_sftp_close(struct torture_sftp *t) { #endif /* _WIN32 */ +void torture_write_file(const char *filename, const char *data){ + int fd; + int rc; + + assert_non_null(filename); + assert_true(filename[0] != '\0'); + assert_non_null(data); + + fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0755); + assert_true(fd >= 0); + + rc = write(fd, data, strlen(data)); + assert_int_equal(rc, strlen(data)); + + close(fd); +} + +static const char *torture_get_testkey_internal(enum ssh_keytypes_e type, + int bits, + int with_passphrase, + int pubkey) +{ + switch (type) { + case SSH_KEYTYPE_DSS: + if (pubkey) { + return torture_dsa_testkey_pub; + } else if (with_passphrase) { + return torture_dsa_testkey_pp; + } + return torture_dsa_testkey; + case SSH_KEYTYPE_RSA: + if (pubkey) { + return torture_rsa_testkey_pub; + } else if (with_passphrase) { + return torture_rsa_testkey_pp; + } + return torture_rsa_testkey; + case SSH_KEYTYPE_ECDSA: + if (bits == 521) { + if (pubkey) { + return torture_ecdsa521_testkey_pub; + } + return torture_ecdsa521_testkey; + } else if (bits == 384) { + if (pubkey) { + return torture_ecdsa384_testkey_pub; + } + return torture_ecdsa384_testkey; + } + + if (pubkey) { + return torture_ecdsa256_testkey_pub; + } + return torture_ecdsa256_testkey; + case SSH_KEYTYPE_RSA1: + case SSH_KEYTYPE_UNKNOWN: + return NULL; + } + + return NULL; +} + +const char *torture_get_testkey(enum ssh_keytypes_e type, + int ecda_bits, + int with_passphrase) +{ + return torture_get_testkey_internal(type, ecda_bits, with_passphrase, 0); +} + +const char *torture_get_testkey_pub(enum ssh_keytypes_e type, int ecda_bits) +{ + return torture_get_testkey_internal(type, ecda_bits, 0, 1); +} + +const char *torture_get_testkey_passphrase(void) +{ + return TORTURE_TESTKEY_PASSWORD; +} int torture_libssh_verbosity(void){ return verbosity; diff --git a/libssh/tests/torture.h b/libssh/tests/torture.h index dbc1906a..ffcea8bb 100644 --- a/libssh/tests/torture.h +++ b/libssh/tests/torture.h @@ -3,7 +3,7 @@ * * This file is part of the SSH Library * - * Copyright (c) 2008-2009 by Andreas Schneider + * Copyright (c) 2008-2009 by Andreas Schneider * * The SSH Library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -68,6 +68,14 @@ ssh_session torture_ssh_session(const char *host, struct torture_sftp *torture_sftp_session(ssh_session session); void torture_sftp_close(struct torture_sftp *t); +const char *torture_get_testkey(enum ssh_keytypes_e type, + int ecdsa_bits, + int with_passphrase); +const char *torture_get_testkey_pub(enum ssh_keytypes_e type, int ecdsa_bits); +const char *torture_get_testkey_passphrase(void); + +void torture_write_file(const char *filename, const char *data); + /* * This function must be defined in every unit test file. */ diff --git a/libssh/tests/unittests/torture_buffer.c b/libssh/tests/unittests/torture_buffer.c index dee6e7d4..ec87259d 100644 --- a/libssh/tests/unittests/torture_buffer.c +++ b/libssh/tests/unittests/torture_buffer.c @@ -9,6 +9,7 @@ static void setup(void **state) { ssh_buffer buffer; buffer = ssh_buffer_new(); + ssh_buffer_set_secure(buffer); *state = (void *) buffer; } @@ -25,7 +26,7 @@ static void torture_growing_buffer(void **state) { int i; for(i=0;iused >= 128){ if(buffer_get_rest_len(buffer) * 2 < buffer->allocated){ assert_true(buffer_get_rest_len(buffer) * 2 >= buffer->allocated); @@ -43,11 +44,11 @@ static void torture_growing_buffer_shifting(void **state) { int i; unsigned char c; for(i=0; i<1024;++i){ - buffer_add_data(buffer,"S",1); + ssh_buffer_add_data(buffer,"S",1); } for(i=0;iused >= 128){ if(buffer_get_rest_len(buffer) * 4 < buffer->allocated){ assert_true(buffer_get_rest_len(buffer) * 4 >= buffer->allocated); @@ -63,7 +64,7 @@ static void torture_growing_buffer_shifting(void **state) { static void torture_buffer_prepend(void **state) { ssh_buffer buffer = *state; uint32_t v; - buffer_add_data(buffer,"abcdef",6); + ssh_buffer_add_data(buffer,"abcdef",6); buffer_prepend_data(buffer,"xyz",3); assert_int_equal(buffer_get_rest_len(buffer),9); assert_memory_equal(buffer_get_rest(buffer), "xyzabcdef", 9); @@ -102,27 +103,150 @@ static void torture_buffer_get_ssh_string(void **state) { for(i=0; i < (int)(sizeof(values)/sizeof(values[0]));++i){ for(j=0; j< (int)sizeof(data);++j){ for(k=1;k<5;++k){ - buffer=buffer_new(); + buffer = ssh_buffer_new(); assert_non_null(buffer); for(l=0;lexecuted++; +} + +static void torture_log_callback(void **state) +{ + struct test_mock_state t = { + .executed = 0, + }; + + (void)state; /* unused */ + + ssh_set_log_callback(test_mock_ssh_logging_callback); + ssh_set_log_userdata(&t); + ssh_set_log_level(1); + + expect_value(test_mock_ssh_logging_callback, priority, 1); + expect_string(test_mock_ssh_logging_callback, function, "torture_log_callback"); + expect_string(test_mock_ssh_logging_callback, buffer, "torture_log_callback: test"); + + SSH_LOG(SSH_LOG_WARN, "test"); + + assert_int_equal(t.executed, 1); +} + int torture_run_tests(void) { int rc; const UnitTest tests[] = { unit_test_setup_teardown(torture_callbacks_size, setup, teardown), unit_test_setup_teardown(torture_callbacks_exists, setup, teardown), + unit_test(torture_log_callback), }; ssh_init(); diff --git a/libssh/tests/unittests/torture_channel.c b/libssh/tests/unittests/torture_channel.c index 5bf34fd9..1d928b84 100644 --- a/libssh/tests/unittests/torture_channel.c +++ b/libssh/tests/unittests/torture_channel.c @@ -20,6 +20,7 @@ static void torture_channel_select(void **state) fd = open("/dev/null", 0); assert_true(fd > 2); + FD_ZERO(&readfds); FD_SET(fd, &readfds); for (i = 0; i < 10; i++) { diff --git a/libssh/tests/unittests/torture_pki.c b/libssh/tests/unittests/torture_pki.c index 7324177e..efcbb01b 100644 --- a/libssh/tests/unittests/torture_pki.c +++ b/libssh/tests/unittests/torture_pki.c @@ -8,44 +8,57 @@ #define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa" #define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa" #define LIBSSH_ECDSA_TESTKEY "libssh_testkey.id_ecdsa" -#define LIBSSH_PASSPHRASE "libssh-rocks" + const unsigned char HASH[] = "12345678901234567890"; static void setup_rsa_key(void **state) { - int rc; - (void) state; /* unused */ unlink(LIBSSH_RSA_TESTKEY); unlink(LIBSSH_RSA_TESTKEY ".pub"); - rc = system("ssh-keygen -t rsa -q -N \"\" -f " LIBSSH_RSA_TESTKEY); - assert_true(rc == 0); + torture_write_file(LIBSSH_RSA_TESTKEY, + torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0)); + torture_write_file(LIBSSH_RSA_TESTKEY ".pub", + torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0)); } static void setup_dsa_key(void **state) { - int rc; - (void) state; /* unused */ unlink(LIBSSH_DSA_TESTKEY); unlink(LIBSSH_DSA_TESTKEY ".pub"); - rc = system("ssh-keygen -t dsa -q -N \"\" -f " LIBSSH_DSA_TESTKEY); - assert_true(rc == 0); + torture_write_file(LIBSSH_DSA_TESTKEY, + torture_get_testkey(SSH_KEYTYPE_DSS, 0, 0)); + torture_write_file(LIBSSH_DSA_TESTKEY ".pub", + torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0)); } #ifdef HAVE_OPENSSL_ECC -static void setup_ecdsa_key(void **state) { - int rc; +static void setup_ecdsa_key(void **state, int ecdsa_bits) { (void) state; /* unused */ unlink(LIBSSH_ECDSA_TESTKEY); unlink(LIBSSH_ECDSA_TESTKEY ".pub"); - rc = system("ssh-keygen -t ecdsa -q -N \"\" -f " LIBSSH_ECDSA_TESTKEY); - assert_true(rc == 0); + torture_write_file(LIBSSH_ECDSA_TESTKEY, + torture_get_testkey(SSH_KEYTYPE_ECDSA, ecdsa_bits, 0)); + torture_write_file(LIBSSH_ECDSA_TESTKEY ".pub", + torture_get_testkey_pub(SSH_KEYTYPE_ECDSA, ecdsa_bits)); +} + +static void setup_ecdsa_key_521(void **state) { + setup_ecdsa_key(state, 521); +} + +static void setup_ecdsa_key_384(void **state) { + setup_ecdsa_key(state, 384); +} + +static void setup_ecdsa_key_256(void **state) { + setup_ecdsa_key(state, 256); } #endif @@ -56,18 +69,6 @@ static void setup_both_keys(void **state) { setup_dsa_key(state); } -static void setup_both_keys_passphrase(void **state) { - int rc; - - (void) state; /* unused */ - - rc = system("ssh-keygen -t rsa -q -N " LIBSSH_PASSPHRASE " -f " LIBSSH_RSA_TESTKEY); - assert_true(rc == 0); - - rc = system("ssh-keygen -t dsa -q -N " LIBSSH_PASSPHRASE " -f " LIBSSH_DSA_TESTKEY); - assert_true(rc == 0); -} - static void teardown(void **state) { (void) state; /* unused */ @@ -111,24 +112,40 @@ static char *read_file(const char *filename) { static int torture_read_one_line(const char *filename, char *buffer, size_t len) { FILE *fp; - size_t rc; + size_t nmemb; fp = fopen(filename, "r"); if (fp == NULL) { return -1; } - rc = fread(buffer, len, 1, fp); - if (rc != 0 || ferror(fp)) { + nmemb = fread(buffer, len - 2, 1, fp); + if (nmemb != 0 || ferror(fp)) { fclose(fp); return -1; } + buffer[len - 1] = '\0'; fclose(fp); return 0; } +/** @internal + * returns the character len of a public key string, omitting the comment part + */ +static int torture_pubkey_len(const char *pubkey){ + const char *ptr; + ptr=strchr(pubkey, ' '); + if (ptr != NULL){ + ptr = strchr(ptr + 1, ' '); + if (ptr != NULL){ + return ptr - pubkey; + } + } + return 0; +} + static void torture_pki_keytype(void **state) { enum ssh_keytypes_e type; const char *type_c; @@ -167,7 +184,7 @@ static void torture_pki_import_privkey_base64_RSA(void **state) { int rc; char *key_str; ssh_key key; - const char *passphrase = LIBSSH_PASSPHRASE; + const char *passphrase = torture_get_testkey_passphrase(); enum ssh_keytypes_e type; (void) state; /* unused */ @@ -190,60 +207,48 @@ static void torture_pki_import_privkey_base64_RSA(void **state) { static void torture_pki_import_privkey_base64_NULL_key(void **state) { int rc; - char *key_str; - ssh_key key; - const char *passphrase = LIBSSH_PASSPHRASE; + const char *passphrase = torture_get_testkey_passphrase(); (void) state; /* unused */ - key_str = read_file(LIBSSH_RSA_TESTKEY); - assert_true(key_str != NULL); - - key = ssh_key_new(); - assert_true(key != NULL); - /* test if it returns -1 if key is NULL */ - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, NULL); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0), + passphrase, + NULL, + NULL, + NULL); assert_true(rc == -1); - free(key_str); - ssh_key_free(key); } static void torture_pki_import_privkey_base64_NULL_str(void **state) { int rc; - char *key_str; ssh_key key = NULL; - const char *passphrase = LIBSSH_PASSPHRASE; + const char *passphrase = torture_get_testkey_passphrase(); (void) state; /* unused */ - key_str = read_file(LIBSSH_RSA_TESTKEY); - assert_true(key_str != NULL); - /* test if it returns -1 if key_str is NULL */ rc = ssh_pki_import_privkey_base64(NULL, passphrase, NULL, NULL, &key); assert_true(rc == -1); - free(key_str); ssh_key_free(key); } static void torture_pki_import_privkey_base64_DSA(void **state) { int rc; - char *key_str; ssh_key key; - const char *passphrase = LIBSSH_PASSPHRASE; + const char *passphrase = torture_get_testkey_passphrase(); (void) state; /* unused */ - key_str = read_file(LIBSSH_DSA_TESTKEY); - assert_true(key_str != NULL); - - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 0), + passphrase, + NULL, + NULL, + &key); assert_true(rc == 0); - free(key_str); ssh_key_free(key); } @@ -252,7 +257,7 @@ static void torture_pki_import_privkey_base64_ECDSA(void **state) { int rc; char *key_str; ssh_key key; - const char *passphrase = LIBSSH_PASSPHRASE; + const char *passphrase = torture_get_testkey_passphrase(); (void) state; /* unused */ @@ -269,97 +274,110 @@ static void torture_pki_import_privkey_base64_ECDSA(void **state) { static void torture_pki_import_privkey_base64_passphrase(void **state) { int rc; - char *key_str; ssh_key key = NULL; - const char *passphrase = LIBSSH_PASSPHRASE; + const char *passphrase = torture_get_testkey_passphrase(); (void) state; /* unused */ - key_str = read_file(LIBSSH_RSA_TESTKEY); - assert_true(key_str != NULL); - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 1), + passphrase, + NULL, + NULL, + &key); assert_true(rc == 0); ssh_key_free(key); /* test if it returns -1 if passphrase is wrong */ - rc = ssh_pki_import_privkey_base64(key_str, "wrong passphrase !!", NULL, - NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 1), + "wrong passphrase !!", + NULL, + NULL, + &key); assert_true(rc == -1); #ifndef HAVE_LIBCRYPTO /* test if it returns -1 if passphrase is NULL */ /* libcrypto asks for a passphrase, so skip this test */ - rc = ssh_pki_import_privkey_base64(key_str, NULL, NULL, NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 1), + NULL, + NULL, + NULL, + &key); assert_true(rc == -1); #endif - free(key_str); - /* same for DSA */ - key_str = read_file(LIBSSH_DSA_TESTKEY); - assert_true(key_str != NULL); - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 1), + passphrase, + NULL, + NULL, + &key); assert_true(rc == 0); ssh_key_free(key); /* test if it returns -1 if passphrase is wrong */ - rc = ssh_pki_import_privkey_base64(key_str, "wrong passphrase !!", NULL, NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 1), + "wrong passphrase !!", + NULL, + NULL, + &key); assert_true(rc == -1); #ifndef HAVE_LIBCRYPTO /* test if it returns -1 if passphrase is NULL */ /* libcrypto asks for a passphrase, so skip this test */ - rc = ssh_pki_import_privkey_base64(key_str, NULL, NULL, NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 1), + NULL, + NULL, + NULL, + &key); assert_true(rc == -1); #endif - free(key_str); } static void torture_pki_pki_publickey_from_privatekey_RSA(void **state) { int rc; - char *key_str; ssh_key key; ssh_key pubkey; const char *passphrase = NULL; (void) state; /* unused */ - key_str = read_file(LIBSSH_RSA_TESTKEY); - assert_true(key_str != NULL); - - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0), + passphrase, + NULL, + NULL, + &key); assert_true(rc == 0); rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); assert_true(rc == SSH_OK); - free(key_str); ssh_key_free(key); ssh_key_free(pubkey); } static void torture_pki_pki_publickey_from_privatekey_DSA(void **state) { int rc; - char *key_str; ssh_key key; ssh_key pubkey; const char *passphrase = NULL; (void) state; /* unused */ - key_str = read_file(LIBSSH_DSA_TESTKEY); - assert_true(key_str != NULL); - - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); + rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 0), + passphrase, + NULL, + NULL, + &key); assert_true(rc == 0); rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); assert_true(rc == SSH_OK); - free(key_str); ssh_key_free(key); ssh_key_free(pubkey); } @@ -399,7 +417,7 @@ static void torture_pki_publickey_dsa_base64(void **state) (void) state; /* unused */ - key_buf = read_file(LIBSSH_DSA_TESTKEY ".pub"); + key_buf = strdup(torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0)); assert_true(key_buf != NULL); q = p = key_buf; @@ -475,7 +493,7 @@ static void torture_pki_publickey_rsa_base64(void **state) (void) state; /* unused */ - key_buf = read_file(LIBSSH_RSA_TESTKEY ".pub"); + key_buf = strdup(torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0)); assert_true(key_buf != NULL); q = p = key_buf; @@ -504,19 +522,14 @@ static void torture_pki_publickey_rsa_base64(void **state) } static void torture_generate_pubkey_from_privkey_rsa(void **state) { - char pubkey_original[4096] = {0}; char pubkey_generated[4096] = {0}; ssh_key privkey; ssh_key pubkey; int rc; + int len; (void) state; /* unused */ - rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub", - pubkey_original, - sizeof(pubkey_original)); - assert_true(rc == 0); - /* remove the public key, generate it from the private key and write it. */ unlink(LIBSSH_RSA_TESTKEY ".pub"); @@ -538,26 +551,24 @@ static void torture_generate_pubkey_from_privkey_rsa(void **state) { sizeof(pubkey_generated)); assert_true(rc == 0); - assert_string_equal(pubkey_original, pubkey_generated); + len = torture_pubkey_len(torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0)); + assert_memory_equal(torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0), + pubkey_generated, + len); ssh_key_free(privkey); ssh_key_free(pubkey); } static void torture_generate_pubkey_from_privkey_dsa(void **state) { - char pubkey_original[4096] = {0}; char pubkey_generated[4096] = {0}; ssh_key privkey; ssh_key pubkey; + int len; int rc; (void) state; /* unused */ - rc = torture_read_one_line(LIBSSH_DSA_TESTKEY ".pub", - pubkey_original, - sizeof(pubkey_original)); - assert_true(rc == 0); - /* remove the public key, generate it from the private key and write it. */ unlink(LIBSSH_DSA_TESTKEY ".pub"); @@ -579,7 +590,10 @@ static void torture_generate_pubkey_from_privkey_dsa(void **state) { sizeof(pubkey_generated)); assert_true(rc == 0); - assert_string_equal(pubkey_original, pubkey_generated); + len = torture_pubkey_len(torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0)); + assert_memory_equal(torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0), + pubkey_generated, + len); ssh_key_free(privkey); ssh_key_free(pubkey); @@ -592,6 +606,7 @@ static void torture_generate_pubkey_from_privkey_ecdsa(void **state) { ssh_key privkey; ssh_key pubkey; int rc; + int len; (void) state; /* unused */ @@ -620,8 +635,8 @@ static void torture_generate_pubkey_from_privkey_ecdsa(void **state) { pubkey_generated, sizeof(pubkey_generated)); assert_true(rc == 0); - - assert_string_equal(pubkey_original, pubkey_generated); + len = torture_pubkey_len(pubkey_original); + assert_int_equal(strncmp(pubkey_original, pubkey_generated, len), 0); ssh_key_free(privkey); ssh_key_free(pubkey); @@ -766,6 +781,39 @@ static void torture_pki_duplicate_key_ecdsa(void **state) ssh_string_free_char(b64_key); ssh_string_free_char(b64_key_gen); } + +/* Test case for bug #147: Private ECDSA key duplication did not carry + * over parts of the key that then caused subsequent key demotion to + * fail. + */ +static void torture_pki_ecdsa_duplicate_then_demote(void **state) +{ + ssh_key pubkey; + ssh_key privkey; + ssh_key privkey_dup; + int rc; + + (void) state; + + rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + privkey_dup = ssh_key_dup(privkey); + assert_true(privkey_dup != NULL); + assert_int_equal(privkey->ecdsa_nid, privkey_dup->ecdsa_nid); + + rc = ssh_pki_export_privkey_to_pubkey(privkey_dup, &pubkey); + assert_true(rc == 0); + assert_int_equal(pubkey->ecdsa_nid, privkey->ecdsa_nid); + + ssh_key_free(pubkey); + ssh_key_free(privkey); + ssh_key_free(privkey_dup); +} #endif static void torture_pki_generate_key_rsa(void **state) @@ -906,6 +954,9 @@ static void torture_pki_generate_key_ecdsa(void **state) int rc; ssh_key key; ssh_signature sign; + enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN; + const char *type_char = NULL; + const char *etype_char = NULL; ssh_session session=ssh_new(); (void) state; @@ -916,6 +967,13 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_true(sign != NULL); rc = pki_signature_verify(session,sign,key,HASH,20); assert_true(rc == SSH_OK); + type = ssh_key_type(key); + assert_true(type == SSH_KEYTYPE_ECDSA); + type_char = ssh_key_type_to_char(type); + assert_true(strcmp(type_char, "ssh-ecdsa") == 0); + etype_char = ssh_pki_key_ecdsa_name(key); + assert_true(strcmp(etype_char, "ecdsa-sha2-nistp256") == 0); + ssh_signature_free(sign); ssh_key_free(key); key=NULL; @@ -927,6 +985,13 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_true(sign != NULL); rc = pki_signature_verify(session,sign,key,HASH,20); assert_true(rc == SSH_OK); + type = ssh_key_type(key); + assert_true(type == SSH_KEYTYPE_ECDSA); + type_char = ssh_key_type_to_char(type); + assert_true(strcmp(type_char, "ssh-ecdsa") == 0); + etype_char =ssh_pki_key_ecdsa_name(key); + assert_true(strcmp(etype_char, "ecdsa-sha2-nistp384") == 0); + ssh_signature_free(sign); ssh_key_free(key); key=NULL; @@ -938,6 +1003,13 @@ static void torture_pki_generate_key_ecdsa(void **state) assert_true(sign != NULL); rc = pki_signature_verify(session,sign,key,HASH,20); assert_true(rc == SSH_OK); + type = ssh_key_type(key); + assert_true(type == SSH_KEYTYPE_ECDSA); + type_char = ssh_key_type_to_char(type); + assert_true(strcmp(type_char, "ssh-ecdsa") == 0); + etype_char =ssh_pki_key_ecdsa_name(key); + assert_true(strcmp(etype_char, "ecdsa-sha2-nistp521") == 0); + ssh_signature_free(sign); ssh_key_free(key); key=NULL; @@ -946,6 +1018,166 @@ static void torture_pki_generate_key_ecdsa(void **state) } #endif +#ifdef HAVE_LIBCRYPTO +static void torture_pki_write_privkey_rsa(void **state) +{ + ssh_key origkey; + ssh_key privkey; + int rc; + + (void) state; /* unused */ + + ssh_set_log_level(5); + + rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, + NULL, + NULL, + NULL, + &origkey); + assert_true(rc == 0); + + unlink(LIBSSH_RSA_TESTKEY); + + rc = ssh_pki_export_privkey_file(origkey, + "", + NULL, + NULL, + LIBSSH_RSA_TESTKEY); + assert_true(rc == 0); + + rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE); + assert_true(rc == 0); + + ssh_key_free(origkey); + ssh_key_free(privkey); +} + +static void torture_pki_write_privkey_dsa(void **state) +{ + ssh_key origkey; + ssh_key privkey; + int rc; + + (void) state; /* unused */ + + ssh_set_log_level(5); + + rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY, + NULL, + NULL, + NULL, + &origkey); + assert_true(rc == 0); + + unlink(LIBSSH_DSA_TESTKEY); + + rc = ssh_pki_export_privkey_file(origkey, + "", + NULL, + NULL, + LIBSSH_DSA_TESTKEY); + assert_true(rc == 0); + + rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE); + assert_true(rc == 0); + + ssh_key_free(origkey); + ssh_key_free(privkey); +} + +#ifdef HAVE_ECC +static void torture_pki_write_privkey_ecdsa(void **state) +{ + ssh_key origkey; + ssh_key privkey; + int rc; + + (void) state; /* unused */ + + ssh_set_log_level(5); + + rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, + NULL, + NULL, + NULL, + &origkey); + assert_true(rc == 0); + + unlink(LIBSSH_ECDSA_TESTKEY); + + rc = ssh_pki_export_privkey_file(origkey, + "", + NULL, + NULL, + LIBSSH_ECDSA_TESTKEY); + assert_true(rc == 0); + + rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, + NULL, + NULL, + NULL, + &privkey); + assert_true(rc == 0); + + rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE); + assert_true(rc == 0); + + ssh_key_free(origkey); + ssh_key_free(privkey); +} +#endif +#endif /* HAVE_LIBCRYPTO */ + +#ifdef HAVE_ECC +static void torture_pki_ecdsa_name(void **state, const char *expected_name) +{ + int rc; + ssh_key key; + const char *etype_char = NULL; + + (void) state; /* unused */ + + ssh_set_log_level(5); + + rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, NULL, NULL, NULL, &key); + assert_true(rc == 0); + + etype_char =ssh_pki_key_ecdsa_name(key); + assert_true(strcmp(etype_char, expected_name) == 0); + + ssh_key_free(key); +} + +static void torture_pki_ecdsa_name256(void **state) +{ + torture_pki_ecdsa_name(state, "ecdsa-sha2-nistp256"); +} + +static void torture_pki_ecdsa_name384(void **state) +{ + torture_pki_ecdsa_name(state, "ecdsa-sha2-nistp384"); +} + +static void torture_pki_ecdsa_name521(void **state) +{ + torture_pki_ecdsa_name(state, "ecdsa-sha2-nistp521"); +} +#endif + int torture_run_tests(void) { int rc; const UnitTest tests[] = { @@ -968,12 +1200,16 @@ int torture_run_tests(void) { teardown), #ifdef HAVE_ECC unit_test_setup_teardown(torture_pki_import_privkey_base64_ECDSA, - setup_ecdsa_key, + setup_ecdsa_key_256, + teardown), + unit_test_setup_teardown(torture_pki_import_privkey_base64_ECDSA, + setup_ecdsa_key_384, + teardown), + unit_test_setup_teardown(torture_pki_import_privkey_base64_ECDSA, + setup_ecdsa_key_521, teardown), #endif - unit_test_setup_teardown(torture_pki_import_privkey_base64_passphrase, - setup_both_keys_passphrase, - teardown), + unit_test(torture_pki_import_privkey_base64_passphrase), /* ssh_pki_export_privkey_to_pubkey */ unit_test_setup_teardown(torture_pki_pki_publickey_from_privatekey_RSA, setup_rsa_key, @@ -983,7 +1219,22 @@ int torture_run_tests(void) { teardown), #ifdef HAVE_ECC unit_test_setup_teardown(torture_pki_publickey_from_privatekey_ECDSA, - setup_ecdsa_key, + setup_ecdsa_key_256, + teardown), + unit_test_setup_teardown(torture_pki_publickey_from_privatekey_ECDSA, + setup_ecdsa_key_384, + teardown), + unit_test_setup_teardown(torture_pki_publickey_from_privatekey_ECDSA, + setup_ecdsa_key_521, + teardown), + unit_test_setup_teardown(torture_pki_ecdsa_duplicate_then_demote, + setup_ecdsa_key_256, + teardown), + unit_test_setup_teardown(torture_pki_ecdsa_duplicate_then_demote, + setup_ecdsa_key_384, + teardown), + unit_test_setup_teardown(torture_pki_ecdsa_duplicate_then_demote, + setup_ecdsa_key_521, teardown), #endif /* public key */ @@ -995,7 +1246,13 @@ int torture_run_tests(void) { teardown), #ifdef HAVE_ECC unit_test_setup_teardown(torture_pki_publickey_ecdsa_base64, - setup_ecdsa_key, + setup_ecdsa_key_256, + teardown), + unit_test_setup_teardown(torture_pki_publickey_ecdsa_base64, + setup_ecdsa_key_384, + teardown), + unit_test_setup_teardown(torture_pki_publickey_ecdsa_base64, + setup_ecdsa_key_521, teardown), #endif @@ -1007,7 +1264,13 @@ int torture_run_tests(void) { teardown), #ifdef HAVE_ECC unit_test_setup_teardown(torture_generate_pubkey_from_privkey_ecdsa, - setup_ecdsa_key, + setup_ecdsa_key_256, + teardown), + unit_test_setup_teardown(torture_generate_pubkey_from_privkey_ecdsa, + setup_ecdsa_key_384, + teardown), + unit_test_setup_teardown(torture_generate_pubkey_from_privkey_ecdsa, + setup_ecdsa_key_521, teardown), #endif @@ -1019,7 +1282,13 @@ int torture_run_tests(void) { teardown), #ifdef HAVE_ECC unit_test_setup_teardown(torture_pki_duplicate_key_ecdsa, - setup_ecdsa_key, + setup_ecdsa_key_256, + teardown), + unit_test_setup_teardown(torture_pki_duplicate_key_ecdsa, + setup_ecdsa_key_384, + teardown), + unit_test_setup_teardown(torture_pki_duplicate_key_ecdsa, + setup_ecdsa_key_521, teardown), #endif unit_test(torture_pki_generate_key_rsa), @@ -1027,6 +1296,36 @@ int torture_run_tests(void) { unit_test(torture_pki_generate_key_dsa), #ifdef HAVE_ECC unit_test(torture_pki_generate_key_ecdsa), +#endif +#ifdef HAVE_LIBCRYPTO + unit_test_setup_teardown(torture_pki_write_privkey_rsa, + setup_rsa_key, + teardown), + unit_test_setup_teardown(torture_pki_write_privkey_dsa, + setup_dsa_key, + teardown), +#ifdef HAVE_ECC + unit_test_setup_teardown(torture_pki_write_privkey_ecdsa, + setup_ecdsa_key_256, + teardown), + unit_test_setup_teardown(torture_pki_write_privkey_ecdsa, + setup_ecdsa_key_384, + teardown), + unit_test_setup_teardown(torture_pki_write_privkey_ecdsa, + setup_ecdsa_key_521, + teardown), +#endif +#endif /* HAVE_LIBCRYPTO */ +#ifdef HAVE_ECC + unit_test_setup_teardown(torture_pki_ecdsa_name256, + setup_ecdsa_key_256, + teardown), + unit_test_setup_teardown(torture_pki_ecdsa_name384, + setup_ecdsa_key_384, + teardown), + unit_test_setup_teardown(torture_pki_ecdsa_name521, + setup_ecdsa_key_521, + teardown), #endif }; From 2f43ed93b7190839dfe890b97a168f766ca2376e Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 31 Oct 2014 00:05:44 -0400 Subject: [PATCH 060/703] Allow clients to extract the ssh connection strings from cmd line --- Makefile.am | 1 + cmd-wait-for.c | 35 +++++++++++++++++++++++++++++++++++ format.c | 5 +++++ tmate-decoder.c | 27 +++++++++++++++++++++++++-- tmate-env.c | 36 ++++++++++++++++++++++++++++++++++++ tmate.h | 11 ++++++++++- 6 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 tmate-env.c diff --git a/Makefile.am b/Makefile.am index e8b35802..fe520a01 100644 --- a/Makefile.am +++ b/Makefile.am @@ -178,6 +178,7 @@ dist_tmate_SOURCES = \ tmate-ssh-client.c \ tmate-encoder.c \ tmate-decoder.c \ + tmate-env.c \ tmate-msg.c \ tmate-session.c \ tmux.c \ diff --git a/cmd-wait-for.c b/cmd-wait-for.c index d40ba49e..ea36255d 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -23,6 +23,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Block or wake a client on a named wait channel. @@ -116,10 +117,44 @@ cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, return (CMD_RETURN_NORMAL); } +#ifdef TMATE +void signal_waiting_clients(const char *name) +{ + struct wait_channel *wc, wc0; + struct cmd_q *wq, *wq1; + + wc0.name = name; + wc = RB_FIND(wait_channels, &wait_channels, &wc0); + + if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { + return; + } + + TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { + TAILQ_REMOVE(&wc->waiters, wq, waitentry); + if (!cmdq_free(wq)) + cmdq_continue(wq); + } + + if (!wc->locked) { + RB_REMOVE(wait_channels, &wait_channels, wc); + free((void*) wc->name); + free(wc); + } +} +#endif + enum cmd_retval cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { + +#ifdef TMATE + if (!strcmp(name, "tmate-ready") && tmate_session.decoder.ready) + return (CMD_RETURN_NORMAL); +#endif + + if (cmdq->client == NULL || cmdq->client->session != NULL) { cmdq_error(cmdq, "not able to wait"); return (CMD_RETURN_ERROR); diff --git a/format.c b/format.c index fa2dd0b2..4acae393 100644 --- a/format.c +++ b/format.c @@ -26,6 +26,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Build a list of key-value pairs and use them to expand #{key} entries in a @@ -214,6 +215,10 @@ format_expand(struct format_tree *ft, const char *fmt) size_t off, len, n; int ch; +#ifdef TMATE + tmate_format(ft); +#endif + len = 64; buf = xmalloc(len); off = 0; diff --git a/tmate-decoder.c b/tmate-decoder.c index 3e780dc5..7489d76f 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -148,7 +148,27 @@ out: free(cmd_str); } -static void handle_message(msgpack_object obj) +static void tmate_client_env(struct tmate_unpacker *uk) +{ + char *name = unpack_string(uk); + char *value = unpack_string(uk); + + tmate_set_env(name, value); + + free(name); + free(value); +} + + +extern void signal_waiting_clients(const char *name); +static void tmate_client_ready(struct tmate_decoder *decoder, + struct tmate_unpacker *uk) +{ + decoder->ready = 1; + signal_waiting_clients("tmate-ready"); +} + +static void handle_message(struct tmate_decoder *decoder, msgpack_object obj) { struct tmate_unpacker _uk; struct tmate_unpacker *uk = &_uk; @@ -160,6 +180,8 @@ static void handle_message(msgpack_object obj) case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(uk); break; + case TMATE_CLIENT_ENV: tmate_client_env(uk); break; + case TMATE_CLIENT_READY: tmate_client_ready(decoder, uk); break; default: decoder_error(); } } @@ -172,7 +194,7 @@ void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) msgpack_unpacked_init(&result); while (msgpack_unpacker_next(&decoder->unpacker, &result)) { - handle_message(result.data); + handle_message(decoder, result.data); } msgpack_unpacked_destroy(&result); @@ -199,4 +221,5 @@ void tmate_decoder_init(struct tmate_decoder *decoder) { if (!msgpack_unpacker_init(&decoder->unpacker, 2*TMATE_MAX_MESSAGE_SIZE)) tmate_fatal("cannot initialize the unpacker"); + decoder->ready = 0; } diff --git a/tmate-env.c b/tmate-env.c new file mode 100644 index 00000000..92abf069 --- /dev/null +++ b/tmate-env.c @@ -0,0 +1,36 @@ +#include "tmate.h" + +struct tmate_env { + TAILQ_ENTRY(tmate_env) entry; + char *name; + char *value; +}; + +TAILQ_HEAD(, tmate_env) tmate_env_list; + +void tmate_set_env(const char *name, const char *value) +{ + struct tmate_env *tmate_env; + + TAILQ_FOREACH(tmate_env, &tmate_env_list, entry) { + if (!strcmp(tmate_env->name, name)) { + free(tmate_env->value); + tmate_env->value = xstrdup(value); + return; + } + } + + tmate_env = xmalloc(sizeof(*tmate_env)); + tmate_env->name = xstrdup(name); + tmate_env->value = xstrdup(value); + TAILQ_INSERT_HEAD(&tmate_env_list, tmate_env, entry); +} + +void tmate_format(struct format_tree *ft) +{ + struct tmate_env *tmate_env; + + TAILQ_FOREACH(tmate_env, &tmate_env_list, entry) { + format_add(ft, tmate_env->name, "%s", tmate_env->value); + } +} diff --git a/tmate.h b/tmate.h index ab202cf7..344015d1 100644 --- a/tmate.h +++ b/tmate.h @@ -18,7 +18,7 @@ #define TMATE_MAX_MESSAGE_SIZE (16*1024) -#define TMATE_PROTOCOL_VERSION 3 +#define TMATE_PROTOCOL_VERSION 4 enum tmate_commands { TMATE_HEADER, @@ -56,10 +56,13 @@ enum tmate_client_commands { TMATE_CLIENT_PANE_KEY, TMATE_CLIENT_RESIZE, TMATE_CLIENT_EXEC_CMD, + TMATE_CLIENT_ENV, + TMATE_CLIENT_READY, }; struct tmate_decoder { struct msgpack_unpacker unpacker; + int ready; }; extern int tmate_sx; @@ -147,4 +150,10 @@ extern void tmate_catch_sigsegv(void); extern void __tmate_status_message(const char *fmt, va_list ap); extern void printflike1 tmate_status_message(const char *fmt, ...); +/* tmate-env.c */ + +extern int tmate_has_received_env(void); +extern void tmate_set_env(const char *name, const char *value); +extern void tmate_format(struct format_tree *ft); + #endif From a1c9d8784f55f5aa8f5a3ed00d997bd1217fcc9b Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 31 Oct 2014 00:13:02 -0400 Subject: [PATCH 061/703] Version bump to 1.8.10 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index fcc4ded1..90d109ff 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmate, 1.8.9) +AC_INIT(tmate, 1.8.10) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) From e203bff96e16bce2f0924ce8a0168b1fedf4d052 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 30 Jan 2015 12:08:13 -0500 Subject: [PATCH 062/703] Start tmate after the config files are processed Closes #50 --- cfg.c | 3 +++ server.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cfg.c b/cfg.c index 1153de67..0557bca1 100644 --- a/cfg.c +++ b/cfg.c @@ -26,6 +26,7 @@ #include #include "tmux.h" +#include "tmate.h" struct cmd_q *cfg_cmd_q; int cfg_finished; @@ -122,6 +123,8 @@ cfg_default_done(unused struct cmd_q *cmdq) return; cfg_finished = 1; + tmate_session_start(); + if (!RB_EMPTY(&sessions)) cfg_show_causes(RB_MIN(sessions, &sessions)); diff --git a/server.c b/server.c index 739386ee..80c17689 100644 --- a/server.c +++ b/server.c @@ -205,7 +205,7 @@ server_start(int lockfd, char *lockfile) set_signals(server_signal_callback); - tmate_session_start(); + /* tmate_session_start() is called in cfg_default_done */ server_loop(); exit(0); } From cd9ccbc1e98b48fd8bfb64356664313a8eb1f7b0 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Mar 2015 08:17:37 +0000 Subject: [PATCH 063/703] set-titles-string now uses formats, not the status bits (so no #() for now). Reported by landry. --- tmux.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index ca7d392a..7052484c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2499,9 +2499,9 @@ variable is set. String used to set the window title if .Ic set-titles is on. -Character sequences are replaced as for the -.Ic status-left -option. +Formats are expanded, see the +.Sx FORMATS +section. .It Xo Ic status .Op Ic on | off .Xc From 5e956f114819294e03166e6c66128feb6e0571a2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 31 Mar 2015 09:25:51 +0100 Subject: [PATCH 064/703] Make place const to avoid a warning, from Ben Boeckel. --- compat/getopt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compat/getopt.c b/compat/getopt.c index a93f368a..03ad9e80 100644 --- a/compat/getopt.c +++ b/compat/getopt.c @@ -52,7 +52,7 @@ char *BSDoptarg; /* argument associated with option */ int BSDgetopt(int nargc, char *const *nargv, const char *ostr) { - static char *place = EMSG; /* option letter processing */ + static const char *place = EMSG; /* option letter processing */ char *oli; /* option letter list index */ if (ostr == NULL) @@ -105,7 +105,7 @@ BSDgetopt(int nargc, char *const *nargv, const char *ostr) __progname, BSDoptopt); return (BADCH); } - else /* white space */ + else /* white space */ BSDoptarg = nargv[BSDoptind]; place = EMSG; ++BSDoptind; From 02df86079b1f3155313ebb6f891cf6cf593d3ad9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2015 17:45:10 +0000 Subject: [PATCH 065/703] Fix some format specifier nits, from Ben Boeckel. --- client.c | 6 +++--- cmd-capture-pane.c | 2 +- cmd-respawn-pane.c | 2 +- colour.c | 2 +- format.c | 30 +++++++++++++++--------------- input-keys.c | 2 +- input.c | 2 +- server-client.c | 2 +- server-fn.c | 4 ++-- server-window.c | 6 +++--- window-choose.c | 2 +- window-copy.c | 2 +- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/client.c b/client.c index 5458dfc9..6d4b8a5a 100644 --- a/client.c +++ b/client.c @@ -557,7 +557,7 @@ client_dispatch_wait(void *data0) data = imsg.data; datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - log_debug("got %d from server", imsg.hdr.type); + log_debug("got %u from server", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_EXIT: case MSG_SHUTDOWN: @@ -604,7 +604,7 @@ client_dispatch_wait(void *data0) fatalx("bad MSG_VERSION size"); fprintf(stderr, "protocol version mismatch " - "(client %u, server %u)\n", PROTOCOL_VERSION, + "(client %d, server %u)\n", PROTOCOL_VERSION, imsg.hdr.peerid); client_exitval = 1; @@ -648,7 +648,7 @@ client_dispatch_attached(void) data = imsg.data; datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - log_debug("got %d from server", imsg.hdr.type); + log_debug("got %u from server", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_DETACH: case MSG_DETACHKILL: diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index ce60b4c5..b44ebe9d 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -74,7 +74,7 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp, tmp[0] = line[i]; tmp[1] = '\0'; } else - xsnprintf(tmp, sizeof tmp, "\\%03o", line[i]); + xsnprintf(tmp, sizeof tmp, "\\%03hho", line[i]); buf = cmd_capture_pane_append(buf, len, tmp, strlen(tmp)); } diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 47031539..6575e8e4 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -59,7 +59,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (!args_has(self->args, 'k') && wp->fd != -1) { if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); - cmdq_error(cmdq, "pane still active: %s:%u.%u", + cmdq_error(cmdq, "pane still active: %s:%d.%u", s->name, wl->idx, idx); return (CMD_RETURN_ERROR); } diff --git a/colour.c b/colour.c index b5efd6f1..82f8533a 100644 --- a/colour.c +++ b/colour.c @@ -147,7 +147,7 @@ colour_tostring(int c) static char s[32]; if (c & 0x100) { - xsnprintf(s, sizeof s, "colour%u", c & ~0x100); + xsnprintf(s, sizeof s, "colour%d", c & ~0x100); return (s); } diff --git a/format.c b/format.c index 01fa3ade..92271753 100644 --- a/format.c +++ b/format.c @@ -503,7 +503,7 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_created_string", "%s", tim); format_add(ft, "session_attached", "%u", s->attached); - format_add(ft, "session_many_attached", "%u", s->attached > 1); + format_add(ft, "session_many_attached", "%d", s->attached > 1); } /* Set default format keys for a client. */ @@ -572,7 +572,7 @@ format_defaults_window(struct format_tree *ft, struct window *w) format_add(ft, "window_height", "%u", w->sy); format_add(ft, "window_layout", "%s", layout); format_add(ft, "window_panes", "%u", window_count_panes(w)); - format_add(ft, "window_zoomed_flag", "%u", + format_add(ft, "window_zoomed_flag", "%d", !!(w->flags & WINDOW_ZOOMED)); free(layout); @@ -597,13 +597,13 @@ format_defaults_winlink(struct format_tree *ft, struct session *s, format_add(ft, "window_flags", "%s", flags); format_add(ft, "window_active", "%d", wl == s->curw); - format_add(ft, "window_bell_flag", "%u", + format_add(ft, "window_bell_flag", "%d", !!(wl->flags & WINLINK_BELL)); - format_add(ft, "window_activity_flag", "%u", + format_add(ft, "window_activity_flag", "%d", !!(wl->flags & WINLINK_ACTIVITY)); - format_add(ft, "window_silence_flag", "%u", + format_add(ft, "window_silence_flag", "%d", !!(wl->flags & WINLINK_SILENCE)); - format_add(ft, "window_last_flag", "%u", + format_add(ft, "window_last_flag", "%d", !!(wl == TAILQ_FIRST(&s->lastw))); free(flags); @@ -623,7 +623,7 @@ format_defaults_pane_tabs(struct format_tree *ft, struct window_pane *wp) if (EVBUFFER_LENGTH(buffer) > 0) evbuffer_add(buffer, ",", 1); - evbuffer_add_printf(buffer, "%d", i); + evbuffer_add_printf(buffer, "%u", i); } format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer), @@ -694,16 +694,16 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) free(cmd); } - format_add(ft, "cursor_x", "%d", wp->base.cx); - format_add(ft, "cursor_y", "%d", wp->base.cy); - format_add(ft, "scroll_region_upper", "%d", wp->base.rupper); - format_add(ft, "scroll_region_lower", "%d", wp->base.rlower); - format_add(ft, "saved_cursor_x", "%d", wp->ictx.old_cx); - format_add(ft, "saved_cursor_y", "%d", wp->ictx.old_cy); + format_add(ft, "cursor_x", "%u", wp->base.cx); + format_add(ft, "cursor_y", "%u", wp->base.cy); + format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); + format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); + format_add(ft, "saved_cursor_x", "%u", wp->ictx.old_cx); + format_add(ft, "saved_cursor_y", "%u", wp->ictx.old_cy); format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); - format_add(ft, "alternate_saved_x", "%d", wp->saved_cx); - format_add(ft, "alternate_saved_y", "%d", wp->saved_cy); + format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); + format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); format_add(ft, "cursor_flag", "%d", !!(wp->base.mode & MODE_CURSOR)); diff --git a/input-keys.c b/input-keys.c index 040a0605..f2d010d8 100644 --- a/input-keys.c +++ b/input-keys.c @@ -219,7 +219,7 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) * legacy format. */ if (m->sgr && (wp->screen->mode & MODE_MOUSE_SGR)) { - len = xsnprintf(buf, sizeof buf, "\033[<%d;%d;%d%c", + len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_xb, m->x + 1, m->y + 1, m->sgr_rel ? 'm' : 'M'); } else if (wp->screen->mode & MODE_MOUSE_UTF8) { diff --git a/input.c b/input.c index de11f629..9f7d4413 100644 --- a/input.c +++ b/input.c @@ -1717,7 +1717,7 @@ void input_exit_osc(struct input_ctx *ictx) { u_char *p = ictx->input_buf; - int option; + u_int option; if (ictx->flags & INPUT_DISCARD) return; diff --git a/server-client.c b/server-client.c index 280815cb..8373a0ef 100644 --- a/server-client.c +++ b/server-client.c @@ -824,7 +824,7 @@ server_client_msg_dispatch(struct client *c) continue; } - log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); + log_debug("got %u from client %d", imsg.hdr.type, c->ibuf.fd); switch (imsg.hdr.type) { case MSG_IDENTIFY_FLAGS: case MSG_IDENTIFY_TERM: diff --git a/server-fn.c b/server-fn.c index b8cdb31a..f89eca8e 100644 --- a/server-fn.c +++ b/server-fn.c @@ -41,9 +41,9 @@ server_fill_environ(struct session *s, struct environ *env) idx = s->id; } else - idx = -1; + idx = (u_int)-1; pid = getpid(); - xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx); + xsnprintf(var, sizeof var, "%s,%ld,%u", socket_path, pid, idx); environ_set(env, "TMUX", var); } diff --git a/server-window.c b/server-window.c index a14c3150..a2355701 100644 --- a/server-window.c +++ b/server-window.c @@ -90,7 +90,7 @@ server_window_check_bell(struct session *s, struct winlink *wl) if (c->session->curw->window == w) status_message_set(c, "Bell in current window"); else if (action == BELL_ANY) - status_message_set(c, "Bell in window %u", wl->idx); + status_message_set(c, "Bell in window %d", wl->idx); } return (1); @@ -124,7 +124,7 @@ server_window_check_activity(struct session *s, struct winlink *wl) c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; - status_message_set(c, "Activity in window %u", wl->idx); + status_message_set(c, "Activity in window %d", wl->idx); } } @@ -175,7 +175,7 @@ server_window_check_silence(struct session *s, struct winlink *wl) c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; - status_message_set(c, "Silence in window %u", wl->idx); + status_message_set(c, "Silence in window %d", wl->idx); } } diff --git a/window-choose.c b/window-choose.c index 69141676..8bed8d45 100644 --- a/window-choose.c +++ b/window-choose.c @@ -98,7 +98,7 @@ window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) item->pos = ARRAY_LENGTH(&data->list) - 1; item->state = 0; - data->width = xsnprintf(tmp, sizeof tmp , "%u", item->pos); + data->width = xsnprintf(tmp, sizeof tmp , "%d", item->pos); } void diff --git a/window-copy.c b/window-copy.c index 223df88a..074e7310 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1216,7 +1216,7 @@ window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, limit = screen_size_x(s) + 1; if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { xoff = size = xsnprintf(hdr, limit, - "Repeat: %u", data->numprefix); + "Repeat: %d", data->numprefix); } else { xoff = size = xsnprintf(hdr, limit, "%s: %s", data->inputprompt, data->inputstr); From 3aa72b42b2a317b0ae531219d269f2a636d6482a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 31 Mar 2015 17:58:36 +0000 Subject: [PATCH 066/703] Add a helper function to convert time, and add session_activity formats (the latter from Takatoshi Matsumoto). --- format.c | 33 +++++++++++++++++++++------------ tmux.1 | 2 ++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/format.c b/format.c index 92271753..cf1713ba 100644 --- a/format.c +++ b/format.c @@ -37,6 +37,7 @@ int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); +char *format_time_string(time_t); char *format_get_command(struct window_pane *); void format_defaults_pane_tabs(struct format_tree *, struct window_pane *); @@ -453,6 +454,18 @@ format_get_command(struct window_pane *wp) return (out); } +/* Get time as a string. */ +char * +format_time_string(time_t t) +{ + char *tim; + + tim = ctime(&t); + *strchr(tim, '\n') = '\0'; + + return (tim); +} + /* Set defaults for any of arguments that are not NULL. */ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, @@ -480,7 +493,6 @@ void format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; - char *tim; time_t t; ft->s = s; @@ -498,9 +510,11 @@ format_defaults_session(struct format_tree *ft, struct session *s) t = s->creation_time.tv_sec; format_add(ft, "session_created", "%lld", (long long) t); - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - format_add(ft, "session_created_string", "%s", tim); + format_add(ft, "session_created_string", "%s", format_time_string(t)); + + t = s->activity_time.tv_sec; + format_add(ft, "session_activity", "%lld", (long long) t); + format_add(ft, "session_activity_string", "%s", format_time_string(t)); format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); @@ -510,9 +524,8 @@ format_defaults_session(struct format_tree *ft, struct session *s) void format_defaults_client(struct format_tree *ft, struct client *c) { - char *tim; - time_t t; struct session *s; + time_t t; if (ft->s == NULL) ft->s = c->session; @@ -526,15 +539,11 @@ format_defaults_client(struct format_tree *ft, struct client *c) t = c->creation_time.tv_sec; format_add(ft, "client_created", "%lld", (long long) t); - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - format_add(ft, "client_created_string", "%s", tim); + format_add(ft, "client_created_string", "%s", format_time_string(t)); t = c->activity_time.tv_sec; format_add(ft, "client_activity", "%lld", (long long) t); - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - format_add(ft, "client_activity_string", "%s", tim); + format_add(ft, "client_activity_string", "%s", format_time_string(t)); format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX)); diff --git a/tmux.1 b/tmux.1 index 7052484c..c8fab92c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3148,6 +3148,8 @@ The following variables are available, where appropriate: .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" +.It Li "session_activity" Ta "" Ta "Integer time of session last activity" +.It Li "session_activity_string" Ta "" Ta "String time of session last activity" .It Li "session_created" Ta "" Ta "Integer time session created" .It Li "session_created_string" Ta "" Ta "String time session created" .It Li "session_group" Ta "" Ta "Number of session group" From 6920be311b276277ad7c38a96ccca4746b94bd95 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 7 Apr 2015 13:06:22 +0000 Subject: [PATCH 067/703] When replacing, don't free the old paste until after the new one's name has been copied. Fixes a use-after-free in window-copy.c. Bug reported by J Raynor (who also provided a different fix). --- paste.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paste.c b/paste.c index de80115e..2ccc3cd2 100644 --- a/paste.c +++ b/paste.c @@ -247,9 +247,6 @@ paste_set(char *data, size_t size, const char *name, char **cause) return (-1); } - pb = paste_get_name(name); - if (pb != NULL) - paste_free_name(name); pb = xmalloc(sizeof *pb); @@ -261,6 +258,9 @@ paste_set(char *data, size_t size, const char *name, char **cause) pb->automatic = 0; pb->order = paste_next_order++; + if (paste_get_name(name) != NULL) + paste_free_name(name); + RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); From 009a5e4213a04555be0fb654f80fe8685082ba20 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Apr 2015 07:23:14 +0000 Subject: [PATCH 068/703] in the case -> in this case. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index c8fab92c..2764626e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -747,7 +747,7 @@ behave like .Ic attach-session if .Ar session-name -already exists; in the case, +already exists; in this case, .Fl D behaves like .Fl d From 0cd55eb1e7823a75810b7f43f53b6266cb8b0bc0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 10 Apr 2015 16:00:08 +0000 Subject: [PATCH 069/703] Add a -x flag to copy-selection, append-selection and start-named-buffer to prevent it exiting copy mode after copying. From J Raynor with a few tweaks by me. --- cmd-bind-key.c | 28 ++++++++++++++++++++++------ tmux.1 | 18 +++++++++++++++--- window-copy.c | 27 +++++++++++++++++++++------ 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 5d68d486..47c58e51 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -104,18 +104,34 @@ cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) return (CMD_RETURN_ERROR); } - if (cmd != MODEKEYCOPY_COPYPIPE) { - if (args->argc != 2) { - cmdq_error(cmdq, "no argument allowed"); - return (CMD_RETURN_ERROR); + switch (cmd) { + case MODEKEYCOPY_APPENDSELECTION: + case MODEKEYCOPY_COPYSELECTION: + case MODEKEYCOPY_STARTNAMEDBUFFER: + if (args->argc == 2) + arg = NULL; + else { + arg = args->argv[2]; + if (strcmp(arg, "-x") != 0) { + cmdq_error(cmdq, "unknown argument"); + return (CMD_RETURN_ERROR); + } } - arg = NULL; - } else { + break; + case MODEKEYCOPY_COPYPIPE: if (args->argc != 3) { cmdq_error(cmdq, "no argument given"); return (CMD_RETURN_ERROR); } arg = args->argv[2]; + break; + default: + if (args->argc != 2) { + cmdq_error(cmdq, "no argument allowed"); + return (CMD_RETURN_ERROR); + } + arg = NULL; + break; } mtmp.key = key; diff --git a/tmux.1 b/tmux.1 index 2764626e..abdeba54 100644 --- a/tmux.1 +++ b/tmux.1 @@ -987,15 +987,27 @@ command and keys modified or removed with .Ic bind-key and .Ic unbind-key . -One command accepts an argument, -.Ic copy-pipe , -which copies the selection and pipes it to a command. +If +.Ic append-selection , +.Ic copy-selection , +or +.Ic start-named-buffer +are given the +.Fl x +flag, +.Nm +will not exit copy mode after copying. +.Ic copy-pipe +copies the selection and pipes it to a command. For example the following will bind +.Ql C-w +not to exit after copying and .Ql C-q to copy the selection into .Pa /tmp as well as the paste buffer: .Bd -literal -offset indent +bind-key -temacs-copy C-w copy-selection -x bind-key -temacs-copy C-q copy-pipe "cat >/tmp/out" .Ed .Pp diff --git a/window-copy.c b/window-copy.c index 074e7310..feb8c481 100644 --- a/window-copy.c +++ b/window-copy.c @@ -147,6 +147,7 @@ struct window_copy_mode_data { enum window_copy_input_type inputtype; const char *inputprompt; char *inputstr; + int inputexit; int numprefix; @@ -424,8 +425,12 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) case MODEKEYCOPY_APPENDSELECTION: if (sess != NULL) { window_copy_append_selection(wp, NULL); - window_pane_reset_mode(wp); - return; + if (arg == NULL) { + window_pane_reset_mode(wp); + return; + } + window_copy_clear_selection(wp); + window_copy_redraw_screen(wp); } break; case MODEKEYCOPY_CANCEL: @@ -572,8 +577,12 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) case MODEKEYCOPY_COPYSELECTION: if (sess != NULL) { window_copy_copy_selection(wp, NULL); - window_pane_reset_mode(wp); - return; + if (arg == NULL) { + window_pane_reset_mode(wp); + return; + } + window_copy_clear_selection(wp); + window_copy_redraw_screen(wp); } break; case MODEKEYCOPY_STARTOFLINE: @@ -718,6 +727,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) goto input_on; case MODEKEYCOPY_STARTNAMEDBUFFER: data->inputtype = WINDOW_COPY_NAMEDBUFFER; + data->inputexit = (arg == NULL); data->inputprompt = "Buffer"; *data->inputstr = '\0'; goto input_on; @@ -828,8 +838,13 @@ window_copy_key_input(struct window_pane *wp, int key) case WINDOW_COPY_NAMEDBUFFER: window_copy_copy_selection(wp, data->inputstr); *data->inputstr = '\0'; - window_pane_reset_mode(wp); - return (0); + if (data->inputexit) { + window_pane_reset_mode(wp); + return (0); + } + window_copy_clear_selection(wp); + window_copy_redraw_screen(wp); + break; case WINDOW_COPY_GOTOLINE: window_copy_goto_line(wp, data->inputstr); *data->inputstr = '\0'; From f922920609063ff5ac1ee11a48bf8f1bd13e8d16 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Apr 2015 15:44:40 +0000 Subject: [PATCH 070/703] Fix setting old-style window -fg/-bg/-attr options that aren't global. --- style.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/style.c b/style.c index d8ab07a3..5534f118 100644 --- a/style.c +++ b/style.c @@ -160,13 +160,21 @@ style_update_new(struct options *oo, const char *name, const char *newname) { int value; struct grid_cell *gc; + struct options_entry *o; /* It's a colour or attribute, but with no -style equivalent. */ if (newname == NULL) return; - gc = options_get_style(oo, newname); - value = options_get_number(oo, name); + o = options_find1(oo, newname); + if (o == NULL) + o = options_set_style (oo, newname, "default", 0); + gc = &o->style; + + o = options_find1(oo, name); + if (o == NULL) + o = options_set_number (oo, name, 8); + value = o->num; if (strstr(name, "-bg") != NULL) colour_set_bg(gc, value); From eec27f9257976d063db279594bff264c2e32d52c Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Apr 2015 22:10:13 +0000 Subject: [PATCH 071/703] Use tty_term_flag not _has for flags, also fix a typo (position not permission). --- tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty.c b/tty.c index 1bb89811..05d422d0 100644 --- a/tty.c +++ b/tty.c @@ -629,7 +629,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) sx = tty->sx; /* - * Don't move the cursor to the start permission if it will wrap there + * Don't move the cursor to the start position if it will wrap there * itself. */ gl = NULL; @@ -1407,7 +1407,7 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) * * Otherwise, try to set the default colour only as needed. */ - have_ax = tty_term_has(tty->term, TTYC_AX); + have_ax = tty_term_flag(tty->term, TTYC_AX); if (!have_ax && tty_term_has(tty->term, TTYC_OP)) tty_reset(tty); else { From 4a7587931ce54aa1a94a104480113d658c295b6b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 15 Apr 2015 22:34:46 +0000 Subject: [PATCH 072/703] Fix some issues in bright colour handling. Bold background doesn't exist so there is no reason for tty_check_bg to mess with the BRIGHT flag at all, ever. Also use aixterm colours for 256-to-16 translation if the terminal supports them. And there is no reason for tty_colours_bg to worry about whether the terminal supports them - tty_check_bg has already taken care of it. --- tty.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tty.c b/tty.c index 05d422d0..83ae8282 100644 --- a/tty.c +++ b/tty.c @@ -1453,6 +1453,8 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) { u_int colours; + colours = tty_term_number(tty->term, TTYC_COLORS); + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { /* And not a 256 colour mode? */ @@ -1461,7 +1463,10 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) gc->fg = colour_256to16(gc->fg); if (gc->fg & 8) { gc->fg &= 7; - gc->attr |= GRID_ATTR_BRIGHT; + if (colours >= 16) + gc->fg += 90; + else + gc->attr |= GRID_ATTR_BRIGHT; } else gc->attr &= ~GRID_ATTR_BRIGHT; gc->flags &= ~GRID_FLAG_FG256; @@ -1470,7 +1475,6 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) } /* Is this an aixterm colour? */ - colours = tty_term_number(tty->term, TTYC_COLORS); if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) { gc->fg -= 90; gc->attr |= GRID_ATTR_BRIGHT; @@ -1482,6 +1486,8 @@ tty_check_bg(struct tty *tty, struct grid_cell *gc) { u_int colours; + colours = tty_term_number(tty->term, TTYC_COLORS); + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { /* @@ -1492,20 +1498,19 @@ tty_check_bg(struct tty *tty, struct grid_cell *gc) if (!(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->bg = colour_256to16(gc->bg); - if (gc->bg & 8) + if (gc->bg & 8) { gc->bg &= 7; - gc->attr &= ~GRID_ATTR_BRIGHT; + if (colours >= 16) + gc->fg += 90; + } gc->flags &= ~GRID_FLAG_BG256; } return; } /* Is this an aixterm colour? */ - colours = tty_term_number(tty->term, TTYC_COLORS); - if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) { + if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) gc->bg -= 90; - gc->attr |= GRID_ATTR_BRIGHT; - } } void @@ -1559,14 +1564,9 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) /* Is this an aixterm bright colour? */ if (bg >= 90 && bg <= 97) { - /* 16 colour terminals or above only. */ - if (tty_term_number(tty->term, TTYC_COLORS) >= 16) { - xsnprintf(s, sizeof s, "\033[%dm", bg + 10); - tty_puts(tty, s); - goto save_bg; - } - bg -= 90; - /* no such thing as a bold background */ + xsnprintf(s, sizeof s, "\033[%dm", bg + 10); + tty_puts(tty, s); + goto save_bg; } /* Otherwise set the background colour. */ From aaad44bbe7ed7ff258598e38dce8cf62ae8ff658 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 19 Apr 2015 19:34:58 +0100 Subject: [PATCH 073/703] +. --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index a0a7570a..7a473c78 100644 --- a/TODO +++ b/TODO @@ -85,6 +85,7 @@ * append to buffer * paste w/o trailing whitespace * command to toggle selection not to move it in copy-mode + * regex searching - layout stuff * way to tag a layout as a number/name From 24c8f523eb8b294d057d617bc27cfbd2a4773f19 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 19 Apr 2015 19:40:12 +0100 Subject: [PATCH 074/703] +. --- TODO | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/TODO b/TODO index 7a473c78..35e218b5 100644 --- a/TODO +++ b/TODO @@ -65,7 +65,7 @@ way to bind repeat count to mode keys so that wheel up/down can do multiple lines c) send-keys -M to pass a mouse event through? - what does the mouse->KEYC_* conversion and find-the-pane bit? - server_client_handle_key? + server_client_handle_key? - hooks! @@ -90,7 +90,7 @@ - layout stuff * way to tag a layout as a number/name * maybe keep last layout + size around and if size reverts just put it - back + back * revamp layouts: they are too complicated, should be more closely integrated, should support hints, layout sets should just be a special case of custom layouts, and we should support panes that are @@ -99,9 +99,10 @@ * way to set hints/limits about pane size for resizing * panning over window (window larger than visible) * a mode where one application can cross two panes (ie x|y, width = - COLUMNS/2 but height = ROWS * 2) - * general key to space cells out evenly (horiz or vert) within their - parent cell (could replace even-vert/even-horiz layouts) + COLUMNS/2 but height = ROWS * 2) + * general key to space cells out evenly (horiz or vert) within their + parent cell (could replace even-vert/even-horiz layouts) + * separate active panes for different clients - terminfo bits * use a better termcap internally instead of screen, perhaps xterm @@ -126,7 +127,7 @@ know about the client, notably: - is this the config file? (cmdq->c == NULL) - is this a command client? (cmdq->c != NULL && - cmdq->c->session == NULL) + cmdq->c->session == NULL) - is this a control client? - can i do stdin or stdout to this client? or even guarantee that cmdq->c != NULL and provide a better way to @@ -134,9 +135,9 @@ client w/o a session else cmd_current_client * optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx * cmd_find_* could be much simpler - parse everything the same, only - difference is what to choose when not given a ":" or "." (such as a - plain "0" could be session, window or pane). So just cmd_find_target - with a type (session, window, or pane).. + difference is what to choose when not given a ":" or "." (such as a + plain "0" could be session, window or pane). So just cmd_find_target + with a type (session, window, or pane).. - miscellaneous * way to keep a job running just read its last line of output for #() From ee123c248951450100475717f5bd45f292d9bb4d Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 19 Apr 2015 21:05:27 +0000 Subject: [PATCH 075/703] Support setting the default window and pane background colours (window and active pane via window-style and window-active-style options, an individual pane by a new select-pane -P flag). From J Raynor. --- cmd-select-pane.c | 20 +++++- options-table.c | 10 +++ screen-redraw.c | 20 +++--- server-client.c | 3 +- tmux.1 | 41 ++++++++++- tmux.h | 11 ++- tty-term.c | 1 + tty.c | 170 +++++++++++++++++++++++++++++++++------------- window.c | 2 + 9 files changed, 212 insertions(+), 66 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 5810eeab..c84b4149 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -28,8 +28,8 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", - "DdeLlRt:U", 0, 0, - "[-DdeLlRU] " CMD_TARGET_PANE_USAGE, + "DdegLlP:Rt:U", 0, 0, + "[-DdegLlRU] [-P style] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_exec }; @@ -48,6 +48,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct winlink *wl; struct window_pane *wp; + const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); @@ -82,6 +83,21 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } + if (args_has(self->args, 'P') || args_has(self->args, 'g')) { + if (args_has(args, 'P')) { + style = args_get(args, 'P'); + if (style_parse(&grid_default_cell, &wp->colgc, + style) == -1) { + cmdq_error(cmdq, "bad style: %s", style); + return (CMD_RETURN_ERROR); + } + wp->flags |= PANE_REDRAW; + } + if (args_has(self->args, 'g')) + cmdq_print(cmdq, "%s", style_tostring(&wp->colgc)); + return (CMD_RETURN_NORMAL); + } + if (args_has(self->args, 'L')) wp = window_pane_find_left(wp); else if (args_has(self->args, 'R')) diff --git a/options-table.c b/options-table.c index fb231f7d..5e72d14f 100644 --- a/options-table.c +++ b/options-table.c @@ -668,6 +668,16 @@ const struct options_table_entry window_options_table[] = { .default_num = 0 /* overridden in main() */ }, + { .name = "window-active-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" + }, + + { .name = "window-style", + .type = OPTIONS_TABLE_STYLE, + .default_str = "default" + }, + { .name = "window-status-activity-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = GRID_ATTR_REVERSE, diff --git a/screen-redraw.c b/screen-redraw.c index c2b2ece6..e3369b82 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -266,7 +266,7 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) yoff++; for (i = 0; i < wp->sy; i++) - tty_draw_line(&c->tty, wp->screen, i, wp->xoff, yoff); + tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff); tty_reset(&c->tty); } @@ -323,9 +323,9 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) small && i > msgx && j == msgy) continue; if (screen_redraw_check_active(i, j, type, w, wp)) - tty_attributes(tty, &active_gc); + tty_attributes(tty, &active_gc, NULL); else - tty_attributes(tty, &other_gc); + tty_attributes(tty, &other_gc, NULL); tty_cursor(tty, i, top + j); tty_putc(tty, CELL_BORDERS[type]); } @@ -333,7 +333,7 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) if (small) { memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc); - tty_attributes(tty, &msg_gc); + tty_attributes(tty, &msg_gc, NULL); tty_cursor(tty, msgx, msgy); tty_puts(tty, msg); } @@ -346,15 +346,13 @@ screen_redraw_draw_panes(struct client *c, u_int top) struct window *w = c->session->curw->window; struct tty *tty = &c->tty; struct window_pane *wp; - struct screen *s; u_int i; TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; - s = wp->screen; for (i = 0; i < wp->sy; i++) - tty_draw_line(tty, s, i, wp->xoff, top + wp->yoff); + tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff); if (c->flags & CLIENT_IDENTIFY) screen_redraw_draw_number(c, wp); } @@ -367,9 +365,9 @@ screen_redraw_draw_status(struct client *c, u_int top) struct tty *tty = &c->tty; if (top) - tty_draw_line(tty, &c->status, 0, 0, 0); + tty_draw_line(tty, NULL, &c->status, 0, 0, 0); else - tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); + tty_draw_line(tty, NULL, &c->status, 0, 0, tty->sy - 1); } /* Draw number on a pane. */ @@ -411,7 +409,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) colour_set_bg(&gc, active_colour); else colour_set_bg(&gc, colour); - tty_attributes(tty, &gc); + tty_attributes(tty, &gc, wp); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; @@ -438,7 +436,7 @@ draw_text: colour_set_fg(&gc, active_colour); else colour_set_fg(&gc, colour); - tty_attributes(tty, &gc); + tty_attributes(tty, &gc, wp); tty_puts(tty, buf); tty_cursor(tty, 0, 0); diff --git a/server-client.c b/server-client.c index 8373a0ef..352e8ab6 100644 --- a/server-client.c +++ b/server-client.c @@ -329,8 +329,7 @@ server_client_check_mouse(struct client *c, struct window_pane *wp) if (options_get_number(oo, "mouse-select-pane") && (m->event == MOUSE_EVENT_DOWN || m->event == MOUSE_EVENT_WHEEL)) { window_set_active_at(wp->window, m->x, m->y); - server_status_window(wp->window); - server_redraw_window_borders(wp->window); + server_redraw_window(wp->window); wp = wp->window->active; /* may have changed */ } diff --git a/tmux.1 b/tmux.1 index abdeba54..a3dac237 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1730,14 +1730,17 @@ and .Ic previous-layout commands. .It Xo Ic select-pane -.Op Fl DdeLlRU +.Op Fl DdegLlRU +.Op Fl P Ar style .Op Fl t Ar target-pane .Xc .D1 (alias: Ic selectp ) Make pane .Ar target-pane the active pane in window -.Ar target-window . +.Ar target-window , +or set it's style (with +.Fl P ) . If one of .Fl D , .Fl L , @@ -1754,6 +1757,22 @@ command. enables or .Fl d disables input to the pane. +.Pp +Each pane has a style: by default the +.Ic window-style +and +.Ic window-active-style +options are used, +.Ic select-pane +.Fl P +sets the style for a single pane. +For example, to set the pane 1 background to red: +.Bd -literal -offset indent +select-pane -t:.1 -P 'bg=red' +.Ed +.Pp +.Fl g +shows the current pane style. .It Xo Ic select-window .Op Fl lnpT .Op Fl t Ar target-window @@ -2070,7 +2089,7 @@ also supports user options which are prefixed with a User options may have any name, so long as they are prefixed with .Ql \&@ , and be set to any string. -For example +For example: .Bd -literal -offset indent $ tmux setw -q @foo "abc123" $ tmux showw -v @foo @@ -2936,6 +2955,14 @@ Instructs .Nm to expect UTF-8 sequences to appear in this window. .Pp +.It Ic window-active-style Ar style +Set the style for the window's active pane. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Ic window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify @@ -2993,6 +3020,14 @@ see the .Ic message-command-style option. .Pp +.It Ic window-style Ar style +Set the default window style. +For how to specify +.Ar style , +see the +.Ic message-command-style +option. +.Pp .It Xo Ic xterm-keys .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index 4e1ee1b3..19a8aa3a 100644 --- a/tmux.h +++ b/tmux.h @@ -156,6 +156,7 @@ enum key_code { enum tty_code_code { TTYC_AX = 0, TTYC_ACSC, /* acs_chars, ac */ + TTYC_BCE, /* back_color_erase, ut */ TTYC_BEL, /* bell, bl */ TTYC_BLINK, /* enter_blink_mode, mb */ TTYC_BOLD, /* enter_bold_mode, md */ @@ -902,6 +903,8 @@ struct window_pane { struct input_ctx ictx; + struct grid_cell colgc; + int pipe_fd; struct bufferevent *pipe_event; size_t pipe_off; @@ -1606,7 +1609,8 @@ void environ_push(struct environ *); /* tty.c */ void tty_init_termios(int, struct termios *, struct bufferevent *); void tty_raw(struct tty *, const char *); -void tty_attributes(struct tty *, const struct grid_cell *); +void tty_attributes(struct tty *, const struct grid_cell *, + const struct window_pane *); void tty_reset(struct tty *); void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); void tty_region(struct tty *, u_int, u_int); @@ -1630,7 +1634,10 @@ void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); void tty_update_mode(struct tty *, int, struct screen *); void tty_force_cursor_colour(struct tty *, const char *); -void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int); +void tty_draw_pane(struct tty *, const struct window_pane *, u_int, u_int, + u_int); +void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, + u_int, u_int, u_int); int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); diff --git a/tty-term.c b/tty-term.c index c8a6ba8b..f6f6b444 100644 --- a/tty-term.c +++ b/tty-term.c @@ -35,6 +35,7 @@ struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { { TTYC_ACSC, TTYCODE_STRING, "acsc" }, { TTYC_AX, TTYCODE_FLAG, "AX" }, + { TTYC_BCE, TTYCODE_FLAG, "bce" }, { TTYC_BEL, TTYCODE_STRING, "bel" }, { TTYC_BLINK, TTYCODE_STRING, "blink" }, { TTYC_BOLD, TTYCODE_STRING, "bold" }, diff --git a/tty.c b/tty.c index 83ae8282..59ed3bb8 100644 --- a/tty.c +++ b/tty.c @@ -43,11 +43,14 @@ void tty_colours_fg(struct tty *, const struct grid_cell *); void tty_colours_bg(struct tty *, const struct grid_cell *); int tty_large_region(struct tty *, const struct tty_ctx *); +int tty_fake_bce(const struct tty *, const struct window_pane *); void tty_redraw_region(struct tty *, const struct tty_ctx *); void tty_emulate_repeat( struct tty *, enum tty_code_code, enum tty_code_code, u_int); void tty_repeat_space(struct tty *, u_int); -void tty_cell(struct tty *, const struct grid_cell *); +void tty_cell(struct tty *, const struct grid_cell *, + const struct window_pane *); +void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_use_acs(tty) \ (tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8)) @@ -581,6 +584,23 @@ tty_large_region(unused struct tty *tty, const struct tty_ctx *ctx) return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2); } +/* + * Return if BCE is needed but the terminal doesn't have it - it'll need to be + * emulated. + */ +int +tty_fake_bce(const struct tty *tty, const struct window_pane *wp) +{ + struct grid_cell gc; + + memcpy(&gc, &grid_default_cell, sizeof gc); + tty_default_colours(&gc, wp); + + if (gc.bg == 8 && !(gc.flags & GRID_FLAG_BG256)) + return (0); + return (!tty_term_flag(tty->term, TTYC_BCE)); +} + /* * Redraw scroll region using data from screen (already updated). Used when * CSR not supported, or window is a pane that doesn't take up the full @@ -604,15 +624,23 @@ tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { for (i = ctx->ocy; i < screen_size_y(s); i++) - tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) - tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); } } void -tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) +tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox, + u_int oy) +{ + tty_draw_line(tty, wp, wp->screen, py, ox, oy); +} + +void +tty_draw_line(struct tty *tty, const struct window_pane *wp, + struct screen *s, u_int py, u_int ox, u_int oy) { const struct grid_cell *gc; struct grid_line *gl; @@ -650,20 +678,20 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmpgc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); - tty_cell(tty, &tmpgc); + tty_cell(tty, &tmpgc, wp); } else - tty_cell(tty, gc); + tty_cell(tty, gc, wp); } if (sx >= tty->sx) { tty_update_mode(tty, tty->mode, s); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor(tty, ox + sx, oy + py); if (sx != screen_size_x(s) && ox + screen_size_x(s) >= tty->sx && - tty_term_has(tty->term, TTYC_EL)) + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s) - sx); @@ -713,19 +741,19 @@ tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; if (!tty_pane_full_width(tty, ctx)) { - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_term_has(tty->term, TTYC_ICH) || - tty_term_has(tty->term, TTYC_ICH1)) + if (!tty_fake_bce(tty, wp) && (tty_term_has(tty->term, TTYC_ICH) || + tty_term_has(tty->term, TTYC_ICH1))) tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); else - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); } void @@ -733,14 +761,14 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -754,11 +782,11 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { u_int i; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_term_has(tty->term, TTYC_ECH)) + if (tty_term_has(tty->term, TTYC_ECH) && !tty_fake_bce(tty, ctx->wp)) tty_putcode1(tty, TTYC_ECH, ctx->num); else { for (i = 0; i < ctx->num; i++) @@ -769,14 +797,14 @@ tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -787,14 +815,14 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -808,11 +836,12 @@ tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, 0, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) + if (tty_pane_full_width(tty, ctx) && !tty_fake_bce(tty, wp) && + tty_term_has(tty->term, TTYC_EL)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s)); @@ -824,11 +853,12 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s) - ctx->ocx); @@ -837,9 +867,10 @@ tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); - if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) { + if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1) && + !tty_fake_bce(tty, ctx->wp)) { tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putcode(tty, TTYC_EL1); } else { @@ -854,14 +885,14 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orupper) return; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_RI)) { tty_redraw_region(tty, ctx); return; } - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); @@ -877,7 +908,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orlower) return; - if (!tty_pane_full_width(tty, ctx) || + if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp) || !tty_term_has(tty->term, TTYC_CSR)) { if (tty_large_region(tty, ctx)) wp->flags |= PANE_REDRAW; @@ -894,7 +925,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP)) return; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); @@ -909,12 +940,13 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { tty_putcode(tty, TTYC_EL); if (ctx->ocy != screen_size_y(s) - 1) { tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); @@ -942,12 +974,13 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, 0, 0); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { for (i = 0; i < ctx->ocy; i++) { tty_putcode(tty, TTYC_EL); tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); @@ -969,12 +1002,13 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, 0, 0); - if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { + if (tty_pane_full_width(tty, ctx) && + tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) { for (i = 0; i < screen_size_y(s); i++) { tty_putcode(tty, TTYC_EL); if (i != screen_size_y(s) - 1) { @@ -997,7 +1031,7 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) struct screen *s = wp->screen; u_int i, j; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); @@ -1038,12 +1072,12 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) */ cx = screen_size_x(s) - grid_cell_width(&ctx->last_cell); tty_cursor_pane(tty, ctx, cx, ctx->ocy); - tty_cell(tty, &ctx->last_cell); + tty_cell(tty, &ctx->last_cell, wp); } } else tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); - tty_cell(tty, ctx->cell); + tty_cell(tty, ctx->cell, wp); } void @@ -1055,7 +1089,7 @@ tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx) * Cannot rely on not being a partial character, so just redraw the * whole line. */ - tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); + tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); } void @@ -1088,12 +1122,13 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) tty->cx = tty->cy = UINT_MAX; tty->rupper = tty->rlower = UINT_MAX; - tty_reset(tty); + tty_attributes(tty, &grid_default_cell, ctx->wp); tty_cursor(tty, 0, 0); } void -tty_cell(struct tty *tty, const struct grid_cell *gc) +tty_cell(struct tty *tty, const struct grid_cell *gc, + const struct window_pane *wp) { struct utf8_data ud; u_int i; @@ -1108,7 +1143,7 @@ tty_cell(struct tty *tty, const struct grid_cell *gc) return; /* Set the attributes. */ - tty_attributes(tty, gc); + tty_attributes(tty, gc, wp); /* Get the cell and if ASCII write with putc to do ACS translation. */ grid_cell_get(gc, &ud); @@ -1312,12 +1347,14 @@ out: } void -tty_attributes(struct tty *tty, const struct grid_cell *gc) +tty_attributes(struct tty *tty, const struct grid_cell *gc, + const struct window_pane *wp) { struct grid_cell *tc = &tty->cell, gc2; u_char changed; memcpy(&gc2, gc, sizeof gc2); + tty_default_colours(&gc2, wp); /* * If no setab, try to use the reverse attribute as a best-effort for a @@ -1609,6 +1646,47 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) return (-1); } +void +tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) +{ + const struct grid_cell *agc, *pgc, *wgc; + + if (wp == NULL) + return; + + pgc = &wp->colgc; + agc = options_get_style(&wp->window->options, "window-active-style"); + wgc = options_get_style(&wp->window->options, "window-style"); + + if (gc->fg == 8 && !(gc->flags & GRID_FLAG_FG256)) { + if (pgc->fg != 8 || (pgc->flags & GRID_FLAG_FG256)) { + gc->fg = pgc->fg; + gc->flags |= (pgc->flags & GRID_FLAG_FG256); + } else if (wp == wp->window->active && + (agc->fg != 8 || (agc->flags & GRID_FLAG_FG256))) { + gc->fg = agc->fg; + gc->flags |= (agc->flags & GRID_FLAG_FG256); + } else { + gc->fg = wgc->fg; + gc->flags |= (wgc->flags & GRID_FLAG_FG256); + } + } + + if (gc->bg == 8 && !(gc->flags & GRID_FLAG_BG256)) { + if (pgc->bg != 8 || (pgc->flags & GRID_FLAG_BG256)) { + gc->bg = pgc->bg; + gc->flags |= (pgc->flags & GRID_FLAG_BG256); + } else if (wp == wp->window->active && + (agc->bg != 8 || (agc->flags & GRID_FLAG_BG256))) { + gc->bg = agc->bg; + gc->flags |= (agc->flags & GRID_FLAG_BG256); + } else { + gc->bg = wgc->bg; + gc->flags |= (wgc->flags & GRID_FLAG_BG256); + } + } +} + void tty_bell(struct tty *tty) { diff --git a/window.c b/window.c index 6a742a2e..4abcf495 100644 --- a/window.c +++ b/window.c @@ -705,6 +705,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->saved_grid = NULL; + memcpy(&wp->colgc, &grid_default_cell, sizeof wp->colgc); + screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; From bf635e7741f7b881f67ec7e4a5caa02f7ff3d786 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 19 Apr 2015 21:34:21 +0000 Subject: [PATCH 076/703] Rewrite of tmux mouse support which was a mess. Instead of having options for "mouse-this" and "mouse-that", mouse events may be bound as keys and there is one option "mouse" that turns on mouse support entirely (set -g mouse on). See the new MOUSE SUPPORT section of the man page for description of the key names and new flags (-t= to specify the pane or window under mouse as a target, and send-keys -M to pass through a mouse event). The default builtin bindings for the mouse are: bind -n MouseDown1Pane select-pane -t=; send-keys -M bind -n MouseDown1Status select-window -t= bind -n MouseDrag1Pane copy-mode -M bind -n MouseDrag1Border resize-pane -M To get the effect of turning mode-mouse off, do: unbind -n MouseDrag1Pane unbind -temacs-copy MouseDrag1Pane The old mouse options are now gone, set-option -q may be used to suppress warnings if mixing configuration files. --- cfg.c | 2 +- cmd-command-prompt.c | 2 +- cmd-confirm-before.c | 2 +- cmd-copy-mode.c | 15 +- cmd-if-shell.c | 4 +- cmd-queue.c | 12 +- cmd-resize-pane.c | 66 ++++++++- cmd-send-keys.c | 21 ++- cmd.c | 94 ++++++++++++- control.c | 2 +- input-keys.c | 95 ++++++------- key-bindings.c | 13 +- key-string.c | 17 ++- layout.c | 52 ------- mode-key.c | 14 ++ options-table.c | 21 +-- server-client.c | 325 ++++++++++++++++++++++++++++++++----------- server-fn.c | 7 +- status.c | 13 +- tmux.1 | 99 +++++++++---- tmux.h | 140 +++++++++++-------- tty-keys.c | 81 +++-------- tty.c | 4 + window-choose.c | 107 ++++++-------- window-clock.c | 8 +- window-copy.c | 198 +++++++++++++------------- window.c | 49 +++---- 27 files changed, 879 insertions(+), 584 deletions(-) diff --git a/cfg.c b/cfg.c index 23828923..b4c9bff4 100644 --- a/cfg.c +++ b/cfg.c @@ -75,7 +75,7 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) if (cmdlist == NULL) continue; - cmdq_append(cmdq, cmdlist); + cmdq_append(cmdq, cmdlist, NULL); cmd_list_free(cmdlist); found++; } diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 22b1d84e..1622e0b7 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -151,7 +151,7 @@ cmd_command_prompt_callback(void *data, const char *s) return (0); } - cmdq_run(c->cmdq, cmdlist); + cmdq_run(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 0bf58449..5e4816ed 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -105,7 +105,7 @@ cmd_confirm_before_callback(void *data, const char *s) return (0); } - cmdq_run(c->cmdq, cmdlist); + cmdq_run(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); return (0); diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index b46a5a46..19dca5ff 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -28,8 +28,8 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { "copy-mode", NULL, - "t:u", 0, 0, - "[-u] " CMD_TARGET_PANE_USAGE, + "Mt:u", 0, 0, + "[-Mu] " CMD_TARGET_PANE_USAGE, 0, cmd_copy_mode_exec }; @@ -46,9 +46,16 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct client *c = cmdq->client; + struct session *s; struct window_pane *wp; - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) + if (args_has(args, 'M')) { + if ((wp = cmd_mouse_pane(&cmdq->item->mouse, &s, NULL)) == NULL) + return (CMD_RETURN_NORMAL); + if (c == NULL || c->session != s) + return (CMD_RETURN_NORMAL); + } else if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); if (self->entry == &cmd_clock_mode_entry) { @@ -61,6 +68,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); window_copy_init_from_pane(wp); } + if (args_has(args, 'M')) + window_copy_start_drag(c, &cmdq->item->mouse); if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) window_copy_pageup(wp); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 8c6620da..9659511e 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -95,7 +95,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) } return (CMD_RETURN_ERROR); } - cmdq_run(cmdq, cmdlist); + cmdq_run(cmdq, cmdlist, NULL); cmd_list_free(cmdlist); return (CMD_RETURN_NORMAL); } @@ -152,7 +152,7 @@ cmd_if_shell_callback(struct job *job) cmdq1->emptyfn = cmd_if_shell_done; cmdq1->data = cdata; - cmdq_run(cmdq1, cmdlist); + cmdq_run(cmdq1, cmdlist, NULL); cmd_list_free(cmdlist); } diff --git a/cmd-queue.c b/cmd-queue.c index 6be532a8..61b14147 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -20,6 +20,7 @@ #include #include +#include #include #include "tmux.h" @@ -132,9 +133,9 @@ cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) /* Add command list to queue and begin processing if needed. */ void -cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist) +cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) { - cmdq_append(cmdq, cmdlist); + cmdq_append(cmdq, cmdlist, m); if (cmdq->item == NULL) { cmdq->cmd = NULL; @@ -144,7 +145,7 @@ cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist) /* Add command list to queue. */ void -cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist) +cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) { struct cmd_q_item *item; @@ -152,6 +153,11 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist) item->cmdlist = cmdlist; TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry); cmdlist->references++; + + if (m != NULL) + memcpy(&item->mouse, m, sizeof item->mouse); + else + item->mouse.valid = 0; } /* Continue processing command queue. Returns 1 if finishes empty. */ diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 42f0f39a..b342307d 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -28,10 +28,13 @@ enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *); +void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); + const struct cmd_entry cmd_resize_pane_entry = { "resize-pane", "resizep", - "DLRt:Ux:y:Z", 0, 1, - "[-DLRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", + "DLMRt:Ux:y:Z", 0, 1, + "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE + " [adjustment]", 0, cmd_resize_pane_exec }; @@ -40,6 +43,8 @@ enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct client *c = cmdq->client; + struct session *s; struct winlink *wl; struct window *w; const char *errstr; @@ -48,6 +53,16 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) u_int adjust; int x, y; + if (args_has(args, 'M')) { + if (cmd_mouse_window(&cmdq->item->mouse, &s) == NULL) + return (CMD_RETURN_NORMAL); + if (c == NULL || c->session != s) + return (CMD_RETURN_NORMAL); + c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; + cmd_resize_pane_mouse_update(c, &cmdq->item->mouse); + return (CMD_RETURN_NORMAL); + } + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; @@ -106,3 +121,50 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } + +void +cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) +{ + struct winlink *wl; + struct window_pane *wp; + int found; + u_int y, ly; + + wl = cmd_mouse_window(m, NULL); + if (wl == NULL) { + c->tty.mouse_drag_update = NULL; + return; + } + + y = m->y; + if (m->statusat == 0 && y > 0) + y--; + else if (m->statusat > 0 && y >= (u_int)m->statusat) + y = m->statusat - 1; + ly = m->ly; + if (m->statusat == 0 && ly > 0) + ly--; + else if (m->statusat > 0 && ly >= (u_int)m->statusat) + ly = m->statusat - 1; + + found = 0; + TAILQ_FOREACH(wp, &wl->window->panes, entry) { + if (!window_pane_visible(wp)) + continue; + + if (wp->xoff + wp->sx == m->lx && + wp->yoff <= 1 + ly && wp->yoff + wp->sy >= ly) { + layout_resize_pane(wp, LAYOUT_LEFTRIGHT, m->x - m->lx); + found = 1; + } + if (wp->yoff + wp->sy == ly && + wp->xoff <= 1 + m->lx && wp->xoff + wp->sx >= m->lx) { + layout_resize_pane(wp, LAYOUT_TOPBOTTOM, y - ly); + found = 1; + } + } + if (found) + server_redraw_window(wl->window); + else + c->tty.mouse_drag_update = NULL; +} diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 7a4d97d5..27da410d 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_send_keys_entry = { "send-keys", "send", - "lRt:", 0, -1, - "[-lR] " CMD_TARGET_PANE_USAGE " key ...", + "lRMt:", 0, -1, + "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", 0, cmd_send_keys_exec }; @@ -49,12 +49,23 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct mouse_event *m = &cmdq->item->mouse; struct window_pane *wp; struct session *s; struct input_ctx *ictx; const u_char *str; int i, key; + if (args_has(args, 'M')) { + wp = cmd_mouse_pane(m, &s, NULL); + if (wp == NULL) { + cmdq_error(cmdq, "no mouse target"); + return (CMD_RETURN_ERROR); + } + window_pane_key(wp, NULL, s, m->key, m); + return (CMD_RETURN_NORMAL); + } + if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); @@ -63,7 +74,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) key = options_get_number(&s->options, "prefix2"); else key = options_get_number(&s->options, "prefix"); - window_pane_key(wp, s, key); + window_pane_key(wp, NULL, s, key, NULL); return (CMD_RETURN_NORMAL); } @@ -88,10 +99,10 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) if (!args_has(args, 'l') && (key = key_string_lookup_string(str)) != KEYC_NONE) { - window_pane_key(wp, s, key); + window_pane_key(wp, NULL, s, key, NULL); } else { for (; *str != '\0'; str++) - window_pane_key(wp, s, *str); + window_pane_key(wp, NULL, s, *str, NULL); } } diff --git a/cmd.c b/cmd.c index 66ac58b8..dd1a0a7b 100644 --- a/cmd.c +++ b/cmd.c @@ -348,6 +348,7 @@ cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) const char *path; int found; + /* Try the queue session. */ if (c != NULL && c->session != NULL) return (c->session); @@ -504,6 +505,74 @@ cmd_choose_client(struct clients *cc) return (cbest); } +/* Adjust current mouse position for a pane. */ +int +cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, + u_int *yp, int last) +{ + u_int x, y; + + if (last) { + x = m->lx; + y = m->ly; + } else { + x = m->x; + y = m->y; + } + + if (m->statusat == 0 && y > 0) + y--; + else if (m->statusat > 0 && y >= (u_int)m->statusat) + y = m->statusat - 1; + + if (x < wp->xoff || x >= wp->xoff + wp->sx) + return (-1); + if (y < wp->yoff || y >= wp->yoff + wp->sy) + return (-1); + + *xp = x - wp->xoff; + *yp = y - wp->yoff; + return (0); +} + +/* Get current mouse window if any. */ +struct winlink * +cmd_mouse_window(struct mouse_event *m, struct session **sp) +{ + struct session *s; + struct window *w; + + if (!m->valid || m->s == -1 || m->w == -1) + return (NULL); + if ((s = session_find_by_id(m->s)) == NULL) + return (NULL); + if ((w = window_find_by_id(m->w)) == NULL) + return (NULL); + + if (sp != NULL) + *sp = s; + return (winlink_find_by_window(&s->windows, w)); +} + +/* Get current mouse pane if any. */ +struct window_pane * +cmd_mouse_pane(struct mouse_event *m, struct session **sp, struct winlink **wlp) +{ + struct winlink *wl; + struct window_pane *wp; + + if ((wl = cmd_mouse_window(m, sp)) == NULL) + return (NULL); + if ((wp = window_pane_find_by_id(m->wp)) == NULL) + return (NULL); + if (!window_has_pane(wl->window, wp)) + return (NULL); + + if (wlp != NULL) + *wlp = wl; + return (wp); +} + /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *arg, int quiet) @@ -928,7 +997,12 @@ no_colon: * No colon in the string, first try special cases, then as a window * and lastly as a session. */ - if (arg[0] == '!' && arg[1] == '\0') { + if (arg[0] == '=' && arg[1] == '\0') { + if ((wl = cmd_mouse_window(&cmdq->item->mouse, &s)) == NULL) { + cmdq_error(cmdq, "no mouse target"); + goto error; + } + } else if (arg[0] == '!' && arg[1] == '\0') { if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) goto not_found; } else if (arg[0] == '+' || arg[0] == '-') { @@ -959,14 +1033,16 @@ no_session: cmdq_error(cmdq, "multiple sessions: %s", arg); else cmdq_error(cmdq, "session not found: %s", arg); - free(sessptr); - return (NULL); + goto error; not_found: if (ambiguous) cmdq_error(cmdq, "multiple windows: %s", arg); else cmdq_error(cmdq, "window not found: %s", arg); + goto error; + +error: free(sessptr); return (NULL); } @@ -1228,6 +1304,18 @@ lookup_string: return (wl); no_period: + /* Check mouse event. */ + if (arg[0] == '=' && arg[1] == '\0') { + *wpp = cmd_mouse_pane(&cmdq->item->mouse, &s, &wl); + if (*wpp == NULL) { + cmdq_error(cmdq, "no mouse target"); + return (NULL); + } + if (sp != NULL) + *sp = s; + return (wl); + } + /* Try as a pane number alone. */ idx = strtonum(arg, 0, INT_MAX, &errstr); if (errstr != NULL) diff --git a/control.c b/control.c index 0ace6c12..11fa2d80 100644 --- a/control.c +++ b/control.c @@ -81,7 +81,7 @@ control_callback(struct client *c, int closed, unused void *data) } else { TAILQ_FOREACH(cmd, &cmdlist->list, qentry) cmd->flags |= CMD_CONTROL; - cmdq_run(c->cmdq, cmdlist); + cmdq_run(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); } diff --git a/input-keys.c b/input-keys.c index f2d010d8..ae00e4a9 100644 --- a/input-keys.c +++ b/input-keys.c @@ -31,6 +31,8 @@ * direction with output). */ +void input_key_mouse(struct window_pane *, struct mouse_event *); + struct input_key_ent { int key; const char *data; @@ -135,7 +137,7 @@ const struct input_key_ent input_keys[] = { /* Translate a key code into an output key sequence. */ void -input_key(struct window_pane *wp, int key) +input_key(struct window_pane *wp, int key, struct mouse_event *m) { const struct input_key_ent *ike; u_int i; @@ -143,7 +145,14 @@ input_key(struct window_pane *wp, int key) char *out; u_char ch; - log_debug("writing key 0x%x", key); + log_debug("writing key 0x%x (%s)", key, key_string_lookup_key(key)); + + /* If this is a mouse key, pass off to mouse function. */ + if (KEYC_IS_MOUSE(key)) { + if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) + input_key_mouse(wp, m); + return; + } /* * If this is a normal 7-bit key, just send it, with a leading escape @@ -200,55 +209,47 @@ input_key(struct window_pane *wp, int key) /* Translate mouse and output. */ void -input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) +input_key_mouse(struct window_pane *wp, struct mouse_event *m) { - char buf[40]; - size_t len; - struct paste_buffer *pb; - int event; + char buf[40]; + size_t len; + u_int x, y; - if (wp->screen->mode & ALL_MOUSE_MODES) { - /* - * Use the SGR (1006) extension only if the application - * requested it and the underlying terminal also sent the event - * in this format (this is because an old style mouse release - * event cannot be converted into the new SGR format, since the - * released button is unknown). Otherwise pretend that tmux - * doesn't speak this extension, and fall back to the UTF-8 - * (1005) extension if the application requested, or to the - * legacy format. - */ - if (m->sgr && (wp->screen->mode & MODE_MOUSE_SGR)) { - len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", - m->sgr_xb, m->x + 1, m->y + 1, - m->sgr_rel ? 'm' : 'M'); - } else if (wp->screen->mode & MODE_MOUSE_UTF8) { - len = xsnprintf(buf, sizeof buf, "\033[M"); - len += utf8_split2(m->xb + 32, &buf[len]); - len += utf8_split2(m->x + 33, &buf[len]); - len += utf8_split2(m->y + 33, &buf[len]); - } else { - if (m->xb > 223) - return; - len = xsnprintf(buf, sizeof buf, "\033[M"); - buf[len++] = m->xb + 32; - buf[len++] = m->x + 33; - buf[len++] = m->y + 33; - } - bufferevent_write(wp->event, buf, len); + if ((wp->screen->mode & ALL_MOUSE_MODES) == 0) + return; + if (!window_pane_visible(wp)) + return; + if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; - } - if (options_get_number(&wp->window->options, "mode-mouse") != 1) + /* If this pane is not in button mode, discard motion events. */ + if (!(wp->screen->mode & MODE_MOUSE_BUTTON) && (m->b & MOUSE_MASK_DRAG)) return; - event = m->event & (MOUSE_EVENT_CLICK|MOUSE_EVENT_WHEEL); - if (wp->mode == NULL && m->button == 1 && event == MOUSE_EVENT_CLICK) { - pb = paste_get_top(); - if (pb != NULL) - paste_send_pane(pb, wp, "\r", 1); - } else if (window_pane_set_mode(wp, &window_copy_mode) == 0) { - window_copy_init_from_pane(wp); - if (wp->mode->mouse != NULL) - wp->mode->mouse(wp, s, m); + + /* + * Use the SGR (1006) extension only if the application requested it + * and the underlying terminal also sent the event in this format (this + * is because an old style mouse release event cannot be converted into + * the new SGR format, since the released button is unknown). Otherwise + * pretend that tmux doesn't speak this extension, and fall back to the + * UTF-8 (1005) extension if the application requested, or to the + * legacy format. + */ + if (m->sgr_type != ' ' && (wp->screen->mode & MODE_MOUSE_SGR)) { + len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", + m->sgr_b, x + 1, y + 1, m->sgr_type); + } else if (wp->screen->mode & MODE_MOUSE_UTF8) { + len = xsnprintf(buf, sizeof buf, "\033[M"); + len += utf8_split2(m->b + 32, &buf[len]); + len += utf8_split2(x + 33, &buf[len]); + len += utf8_split2(y + 33, &buf[len]); + } else { + if (m->b > 223) + return; + len = xsnprintf(buf, sizeof buf, "\033[M"); + buf[len++] = m->b + 32; + buf[len++] = x + 33; + buf[len++] = y + 33; } + bufferevent_write(wp->event, buf, len); } diff --git a/key-bindings.c b/key-bindings.c index 00f73d73..c6cdeeb7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -158,6 +158,10 @@ key_bindings_init(void) "bind -r C-Down resize-pane -D", "bind -r C-Left resize-pane -L", "bind -r C-Right resize-pane -R", + "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", + "bind -n MouseDrag1Border resize-pane -M", + "bind -n MouseDown1Status select-window -t=", + "bind -n MouseDrag1Pane copy-mode -M", }; u_int i; struct cmd_list *cmdlist; @@ -173,14 +177,15 @@ key_bindings_init(void) "", i, &cause); if (error != 0) fatalx("bad default key"); - cmdq_run(cmdq, cmdlist); - cmd_list_free(cmdlist); + cmdq_run(cmdq, cmdlist, NULL); + cmd_list_free (cmdlist); } cmdq_free(cmdq); } void -key_bindings_dispatch(struct key_binding *bd, struct client *c) +key_bindings_dispatch(struct key_binding *bd, struct client *c, + struct mouse_event *m) { struct cmd *cmd; int readonly; @@ -195,5 +200,5 @@ key_bindings_dispatch(struct key_binding *bd, struct client *c) return; } - cmdq_run(c->cmdq, bd->cmdlist); + cmdq_run(c->cmdq, bd->cmdlist, m); } diff --git a/key-string.c b/key-string.c index db968279..b6474c4f 100644 --- a/key-string.c +++ b/key-string.c @@ -82,6 +82,19 @@ const struct { { "KPEnter", KEYC_KP_ENTER }, { "KP0", KEYC_KP_ZERO }, { "KP.", KEYC_KP_PERIOD }, + + /* Mouse keys. */ + KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1), + KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2), + KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3), + KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1), + KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2), + KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3), + KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), + KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), + KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), + KEYC_MOUSE_STRING(WHEELUP, WheelUp), + KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), }; /* Find key string in table. */ @@ -192,7 +205,9 @@ key_string_lookup_key(int key) /* Handle no key. */ if (key == KEYC_NONE) - return ("none"); + return (""); + if (key == KEYC_MOUSE) + return (""); /* * Special case: display C-@ as C-Space. Could do this below in diff --git a/layout.c b/layout.c index b91b86cd..bb1bbf8d 100644 --- a/layout.c +++ b/layout.c @@ -519,58 +519,6 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) notify_window_layout_changed(wp->window); } -/* Resize pane based on mouse events. */ -void -layout_resize_pane_mouse(struct client *c) -{ - struct window *w; - struct window_pane *wp; - struct mouse_event *m = &c->tty.mouse; - int pane_border; - - w = c->session->curw->window; - - pane_border = 0; - if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) { - TAILQ_FOREACH(wp, &w->panes, entry) { - if (!window_pane_visible(wp)) - continue; - - if (wp->xoff + wp->sx == m->lx && - wp->yoff <= 1 + m->ly && - wp->yoff + wp->sy >= m->ly) { - layout_resize_pane(wp, LAYOUT_LEFTRIGHT, - m->x - m->lx); - pane_border = 1; - } - if (wp->yoff + wp->sy == m->ly && - wp->xoff <= 1 + m->lx && - wp->xoff + wp->sx >= m->lx) { - layout_resize_pane(wp, LAYOUT_TOPBOTTOM, - m->y - m->ly); - pane_border = 1; - } - } - if (pane_border) - server_redraw_window(w); - } else if (m->event & MOUSE_EVENT_DOWN) { - TAILQ_FOREACH(wp, &w->panes, entry) { - if ((wp->xoff + wp->sx == m->x && - wp->yoff <= 1 + m->y && - wp->yoff + wp->sy >= m->y) || - (wp->yoff + wp->sy == m->y && - wp->xoff <= 1 + m->x && - wp->xoff + wp->sx >= m->x)) { - pane_border = 1; - } - } - } - if (pane_border) - m->flags |= MOUSE_RESIZE_PANE; - else - m->flags &= ~MOUSE_RESIZE_PANE; -} - /* Helper function to grow pane. */ int layout_resize_pane_grow( diff --git a/mode-key.c b/mode-key.c index 72d66f37..c06d7ed5 100644 --- a/mode-key.c +++ b/mode-key.c @@ -251,6 +251,10 @@ const struct mode_key_entry mode_key_vi_choice[] = { { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, + { KEYC_MOUSEDOWN1_PANE, 0, MODEKEYCHOICE_CHOOSE }, + { KEYC_MOUSEDOWN3_PANE, 0, MODEKEYCHOICE_TREE_TOGGLE }, + { KEYC_WHEELUP_PANE, 0, MODEKEYCHOICE_UP }, + { KEYC_WHEELDOWN_PANE, 0, MODEKEYCHOICE_DOWN }, { 0, -1, 0 } }; @@ -326,6 +330,9 @@ const struct mode_key_entry mode_key_vi_copy[] = { { KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT }, { KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_UP, 0, MODEKEYCOPY_UP }, + { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, + { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, + { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, { 0, -1, 0 } }; @@ -405,6 +412,10 @@ const struct mode_key_entry mode_key_emacs_choice[] = { { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, + { KEYC_MOUSEDOWN1_PANE, 0, MODEKEYCHOICE_CHOOSE }, + { KEYC_MOUSEDOWN3_PANE, 0, MODEKEYCHOICE_TREE_TOGGLE }, + { KEYC_WHEELUP_PANE, 0, MODEKEYCHOICE_UP }, + { KEYC_WHEELDOWN_PANE, 0, MODEKEYCHOICE_DOWN }, { 0, -1, 0 } }; @@ -467,6 +478,9 @@ const struct mode_key_entry mode_key_emacs_copy[] = { { KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_UP | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEUP }, { KEYC_UP, 0, MODEKEYCOPY_UP }, + { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, + { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, + { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, { 0, -1, 0 } }; diff --git a/options-table.c b/options-table.c index 5e72d14f..5e21c692 100644 --- a/options-table.c +++ b/options-table.c @@ -36,9 +36,6 @@ const char *options_table_mode_keys_list[] = { "emacs", "vi", NULL }; -const char *options_table_mode_mouse_list[] = { - "off", "on", "copy-mode", NULL -}; const char *options_table_clock_mode_style_list[] = { "12", "24", NULL }; @@ -255,17 +252,7 @@ const struct options_table_entry session_options_table[] = { .default_str = "bg=yellow,fg=black" }, - { .name = "mouse-resize-pane", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "mouse-select-pane", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - - { .name = "mouse-select-window", + { .name = "mouse", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, @@ -575,12 +562,6 @@ const struct options_table_entry window_options_table[] = { .default_num = MODEKEY_EMACS }, - { .name = "mode-mouse", - .type = OPTIONS_TABLE_CHOICE, - .choices = options_table_mode_mouse_list, - .default_num = 0 - }, - { .name = "mode-style", .type = OPTIONS_TABLE_STYLE, .default_str = "bg=yellow,fg=black" diff --git a/server-client.c b/server-client.c index 352e8ab6..3968c50b 100644 --- a/server-client.c +++ b/server-client.c @@ -32,7 +32,7 @@ void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); -void server_client_check_mouse(struct client *, struct window_pane *); +int server_client_check_mouse(struct client *); void server_client_repeat_timer(int, short, void *); void server_client_check_exit(struct client *); void server_client_check_redraw(struct client *); @@ -91,13 +91,6 @@ server_client_create(int fd) c->prompt_buffer = NULL; c->prompt_index = 0; - c->tty.mouse.xb = c->tty.mouse.button = 3; - c->tty.mouse.x = c->tty.mouse.y = -1; - c->tty.mouse.lx = c->tty.mouse.ly = -1; - c->tty.mouse.sx = c->tty.mouse.sy = -1; - c->tty.mouse.event = MOUSE_EVENT_UP; - c->tty.mouse.flags = 0; - c->flags |= CLIENT_FOCUSED; evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); @@ -289,56 +282,228 @@ server_client_status_timer(void) } /* Check for mouse keys. */ -void -server_client_check_mouse(struct client *c, struct window_pane *wp) +int +server_client_check_mouse(struct client *c) { - struct session *s = c->session; - struct options *oo = &s->options; - struct mouse_event *m = &c->tty.mouse; - int statusat; + struct session *s = c->session; + struct mouse_event *m = &c->tty.mouse; + struct window *w; + struct window_pane *wp; + enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE; + enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE; + u_int x, y, b; + int key; - statusat = status_at_line(c); + log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y, + m->lx, m->ly, c->tty.mouse_drag_flag); - /* Is this a window selection click on the status line? */ - if (statusat != -1 && m->y == (u_int)statusat && - options_get_number(oo, "mouse-select-window")) { - if (m->event & MOUSE_EVENT_CLICK) { - status_set_window_at(c, m->x); - } else if (m->event == MOUSE_EVENT_WHEEL) { - if (m->wheel == MOUSE_WHEEL_UP) - session_previous(c->session, 0); - else if (m->wheel == MOUSE_WHEEL_DOWN) - session_next(c->session, 0); - server_redraw_session(s); + /* What type of event is this? */ + if (MOUSE_DRAG(m->b)) { + type = DRAG; + if (c->tty.mouse_drag_flag) { + x = m->x, y = m->y, b = m->b; + log_debug("drag update at %u,%u", x, y); + } else { + x = m->lx, y = m->ly, b = m->lb; + log_debug("drag start at %u,%u", x, y); } - recalculate_sizes(); - return; + } else if (MOUSE_WHEEL(m->b)) { + type = WHEEL; + x = m->x, y = m->y, b = m->b; + log_debug("wheel at %u,%u", x, y); + } else if (MOUSE_BUTTONS(m->b) == 3) { + type = UP; + x = m->x, y = m->y, b = m->lb; + log_debug("up at %u,%u", x, y); + } else { + type = DOWN; + x = m->x, y = m->y, b = m->b; + log_debug("down at %u,%u", x, y); + } + if (type == NOTYPE) + return (KEYC_NONE); + + /* Always save the session. */ + m->s = s->id; + + /* Is this on the status line? */ + m->statusat = status_at_line(c); + if (m->statusat != -1 && y == (u_int)m->statusat) { + w = status_get_window_at(c, x); + if (w == NULL) + return (KEYC_NONE); + m->w = w->id; + where = STATUS; + } else + m->w = -1; + + /* Not on status line. Adjust position and check for border or pane. */ + if (where == NOWHERE) { + if (m->statusat == 0 && y > 0) + y--; + else if (m->statusat > 0 && y >= (u_int)m->statusat) + y = m->statusat - 1; + + TAILQ_FOREACH(wp, &s->curw->window->panes, entry) { + if ((wp->xoff + wp->sx == x && + wp->yoff <= 1 + y && + wp->yoff + wp->sy >= y) || + (wp->yoff + wp->sy == y && + wp->xoff <= 1 + x && + wp->xoff + wp->sx >= x)) + break; + } + if (wp != NULL) + where = BORDER; + else { + wp = window_get_active_at(s->curw->window, x, y); + if (wp != NULL) + where = PANE; + } + if (where == NOWHERE) + return (KEYC_NONE); + m->wp = wp->id; + m->w = wp->window->id; + } else + m->wp = -1; + + /* Stop dragging if needed. */ + if (type != DRAG && c->tty.mouse_drag_flag) { + if (c->tty.mouse_drag_release != NULL) + c->tty.mouse_drag_release(c, m); + + c->tty.mouse_drag_update = NULL; + c->tty.mouse_drag_release = NULL; + + c->tty.mouse_drag_flag = 0; + return (KEYC_NONE); } - /* - * Not on status line - adjust mouse position if status line is at the - * top and limit if at the bottom. From here on a struct mouse - * represents the offset onto the window itself. - */ - if (statusat == 0 && m->y > 0) - m->y--; - else if (statusat > 0 && m->y >= (u_int)statusat) - m->y = statusat - 1; + /* Convert to a key binding. */ + key = KEYC_NONE; + switch (type) { + case NOTYPE: + break; + case DRAG: + if (c->tty.mouse_drag_update != NULL) + c->tty.mouse_drag_update(c, m); + else { + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_MOUSEDRAG1_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAG1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_MOUSEDRAG2_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAG2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEDRAG3_PANE; + if (where == STATUS) + key = KEYC_MOUSEDRAG3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDRAG3_BORDER; + break; + } + } - /* Is this a pane selection? */ - if (options_get_number(oo, "mouse-select-pane") && - (m->event == MOUSE_EVENT_DOWN || m->event == MOUSE_EVENT_WHEEL)) { - window_set_active_at(wp->window, m->x, m->y); - server_redraw_window(wp->window); - wp = wp->window->active; /* may have changed */ + c->tty.mouse_drag_flag = 1; + break; + case WHEEL: + if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) { + if (where == PANE) + key = KEYC_WHEELUP_PANE; + if (where == STATUS) + key = KEYC_WHEELUP_STATUS; + if (where == BORDER) + key = KEYC_WHEELUP_BORDER; + } else { + if (where == PANE) + key = KEYC_WHEELDOWN_PANE; + if (where == STATUS) + key = KEYC_WHEELDOWN_STATUS; + if (where == BORDER) + key = KEYC_WHEELDOWN_BORDER; + } + break; + case UP: + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_MOUSEUP1_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_MOUSEUP2_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEUP3_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP3_BORDER; + break; + } + break; + case DOWN: + switch (MOUSE_BUTTONS(b)) { + case 0: + if (where == PANE) + key = KEYC_MOUSEDOWN1_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDOWN1_BORDER; + break; + case 1: + if (where == PANE) + key = KEYC_MOUSEDOWN2_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDOWN2_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEDOWN3_PANE; + if (where == STATUS) + key = KEYC_MOUSEDOWN3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEDOWN3_BORDER; + break; + } + break; } + if (key == KEYC_NONE) + return (KEYC_NONE); - /* Check if trying to resize pane. */ - if (options_get_number(oo, "mouse-resize-pane")) - layout_resize_pane_mouse(c); + /* Apply modifiers if any. */ + if (b & MOUSE_MASK_META) + key |= KEYC_ESCAPE; + if (b & MOUSE_MASK_CTRL) + key |= KEYC_CTRL; + if (b & MOUSE_MASK_SHIFT) + key |= KEYC_SHIFT; - /* Update last and pass through to client. */ - window_pane_mouse(wp, c->session, m); + return (key); } /* Is this fast enough to probably be a paste? */ @@ -361,6 +526,7 @@ server_client_assume_paste(struct session *s) void server_client_handle_key(struct client *c, int key) { + struct mouse_event *m = &c->tty.mouse; struct session *s; struct window *w; struct window_pane *wp; @@ -372,21 +538,20 @@ server_client_handle_key(struct client *c, int key) if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; + /* No session, do nothing. */ if (c->session == NULL) return; s = c->session; + w = c->session->curw->window; + wp = w->active; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); - memcpy(&s->last_activity_time, &s->activity_time, sizeof s->last_activity_time); memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); - w = c->session->curw->window; - wp = w->active; - /* Special case: number keys jump to pane in identify mode. */ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { if (c->flags & CLIENT_READONLY) @@ -414,9 +579,19 @@ server_client_handle_key(struct client *c, int key) if (key == KEYC_MOUSE) { if (c->flags & CLIENT_READONLY) return; - server_client_check_mouse(c, wp); - return; - } + key = server_client_check_mouse(c); + if (key == KEYC_NONE) + return; + + m->valid = 1; + m->key = key; + + if (!options_get_number(&s->options, "mouse")) { + window_pane_key(wp, c, s, key, m); + return; + } + } else + m->valid = 0; /* Is this a prefix key? */ if (key == options_get_number(&s->options, "prefix")) @@ -442,9 +617,9 @@ server_client_handle_key(struct client *c, int key) /* Try as a non-prefix key binding. */ if (ispaste || (bd = key_bindings_lookup(key)) == NULL) { if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); + window_pane_key(wp, c, s, key, m); } else - key_bindings_dispatch(bd, c); + key_bindings_dispatch(bd, c, m); return; } @@ -458,7 +633,7 @@ server_client_handle_key(struct client *c, int key) if (isprefix) c->flags |= CLIENT_PREFIX; else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); + window_pane_key(wp, c, s, key, m); } return; } @@ -469,7 +644,7 @@ server_client_handle_key(struct client *c, int key) if (isprefix) c->flags |= CLIENT_PREFIX; else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, s, key); + window_pane_key(wp, c, s, key, m); return; } @@ -485,7 +660,7 @@ server_client_handle_key(struct client *c, int key) } /* Dispatch the command. */ - key_bindings_dispatch(bd, c); + key_bindings_dispatch(bd, c, m); } /* Client functions that need to happen every loop. */ @@ -622,7 +797,6 @@ server_client_reset_state(struct client *c) struct window_pane *wp = w->active; struct screen *s = wp->screen; struct options *oo = &c->session->options; - struct options *wo = &w->options; int status, mode, o; if (c->flags & CLIENT_SUSPENDED) @@ -642,29 +816,12 @@ server_client_reset_state(struct client *c) } /* - * Resizing panes with the mouse requires at least button mode to give - * a smooth appearance. + * Set mouse mode if requested. To support dragging, always use button + * mode. */ mode = s->mode; - if ((c->tty.mouse.flags & MOUSE_RESIZE_PANE) && - !(mode & MODE_MOUSE_BUTTON)) - mode |= MODE_MOUSE_BUTTON; - - /* - * Any mode will do for mouse-select-pane, but set standard mode if - * none. - */ - if ((mode & ALL_MOUSE_MODES) == 0) { - if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL && - options_get_number(oo, "mouse-select-pane")) - mode |= MODE_MOUSE_STANDARD; - else if (options_get_number(oo, "mouse-resize-pane")) - mode |= MODE_MOUSE_STANDARD; - else if (options_get_number(oo, "mouse-select-window")) - mode |= MODE_MOUSE_STANDARD; - else if (options_get_number(wo, "mode-mouse")) - mode |= MODE_MOUSE_STANDARD; - } + if (options_get_number(oo, "mouse")) + mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON; /* * Set UTF-8 mouse input if required. If the terminal is UTF-8, the @@ -945,9 +1102,9 @@ server_client_msg_command(struct client *c, struct imsg *imsg) cmd_free_argv(argc, argv); if (c != cfg_client || cfg_finished) - cmdq_run(c->cmdq, cmdlist); + cmdq_run(c->cmdq, cmdlist, NULL); else - cmdq_append(c->cmdq, cmdlist); + cmdq_append(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); return; diff --git a/server-fn.c b/server-fn.c index f89eca8e..83ea9474 100644 --- a/server-fn.c +++ b/server-fn.c @@ -604,7 +604,8 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, void server_unzoom_window(struct window *w) { - window_unzoom(w); - server_redraw_window(w); - server_status_window(w); + if (window_unzoom(w) == 0) { + server_redraw_window(w); + server_status_window(w); + } } diff --git a/status.c b/status.c index 5f8895fb..ae5d99ab 100644 --- a/status.c +++ b/status.c @@ -118,9 +118,9 @@ status_redraw_get_right(struct client *c, time_t t, int utf8flag, return (right); } -/* Set window at window list position. */ -void -status_set_window_at(struct client *c, u_int x) +/* Get window at window list position. */ +struct window * +status_get_window_at(struct client *c, u_int x) { struct session *s = c->session; struct winlink *wl; @@ -130,12 +130,13 @@ status_set_window_at(struct client *c, u_int x) x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { oo = &wl->window->options; - len = strlen(options_get_string(oo, "window-status-separator")); - if (x < wl->status_width && session_select(s, wl->idx) == 0) - server_redraw_session(s); + + if (x < wl->status_width) + return (wl->window); x -= wl->status_width + len; } + return (NULL); } /* Draw status for client on the last lines of given context. */ diff --git a/tmux.1 b/tmux.1 index a3dac237..fb08ddc1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1019,13 +1019,16 @@ The synopsis for the command is: .Bl -tag -width Ds .It Xo Ic copy-mode -.Op Fl u +.Op Fl Mu .Op Fl t Ar target-pane .Xc Enter copy mode. The .Fl u option scrolls one page up. +.Fl M +begins a mouse drag (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT Ns ). .El .Pp Each window displayed by @@ -1643,7 +1646,7 @@ Rename the current window, or the window at if specified, to .Ar new-name . .It Xo Ic resize-pane -.Op Fl DLRUZ +.Op Fl DLMRUZ .Op Fl t Ar target-pane .Op Fl x Ar width .Op Fl y Ar height @@ -1672,6 +1675,10 @@ With .Fl Z , the active pane is toggled between zoomed (occupying the whole of the window) and unzoomed (its normal position in the layout). +.Pp +.Fl M +begins mouse resizing (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT Ns ). .It Xo Ic respawn-pane .Op Fl k .Op Fl t Ar target-pane @@ -1980,7 +1987,7 @@ are listed; this may be one of: or .Em emacs-copy . .It Xo Ic send-keys -.Op Fl lR +.Op Fl lMR .Op Fl t Ar target-pane .Ar key Ar ... .Xc @@ -2001,6 +2008,10 @@ All arguments are sent sequentially from first to last. The .Fl R flag causes the terminal state to be reset. +.Pp +.Fl M +passes through a mouse event (only valid if bound to a mouse key binding, see +.Sx MOUSE SUPPORT Ns ). .It Xo Ic send-prefix .Op Fl 2 .Op Fl t Ar target-pane @@ -2449,25 +2460,15 @@ For how to specify see the .Ic message-command-style option. -.It Xo Ic mouse-resize-pane +.It Xo Ic mouse .Op Ic on | off .Xc If on, .Nm -captures the mouse and allows panes to be resized by dragging on their borders. -.It Xo Ic mouse-select-pane -.Op Ic on | off -.Xc -If on, -.Nm -captures the mouse and when a window is split into multiple panes the mouse may -be used to select the current pane. -The mouse click is also passed through to the application as normal. -.It Xo Ic mouse-select-window -.Op Ic on | off -.Xc -If on, clicking the mouse on a window name in the status line will select that -window. +captures the mouse and allows mouse events to be bound as key bindings. +See the +.Sx MOUSE SUPPORT +section for details. .It Xo Ic mouse-utf8 .Op Ic on | off .Xc @@ -2855,18 +2856,6 @@ or contains .Ql vi . .Pp -.It Xo Ic mode-mouse -.Op Ic on | off | copy-mode -.Xc -Mouse state in modes. -If on, the mouse may be used to enter copy mode and copy a selection by -dragging, to enter copy mode and scroll with the mouse wheel, or to select an -option in choice mode. -If set to -.Em copy-mode , -the mouse behaves as set to on, but cannot be used to enter copy -mode. -.Pp .It Ic mode-style Ar style Set window modes style. For how to specify @@ -3083,6 +3072,56 @@ is used. .Fl v shows only the option value, not the name. .El +.Sh MOUSE SUPPORT +If the +.Ic mouse +option is on (the default is off), +.Nm +allows mouse events to be bound as keys. +The name of each key is made up of a mouse event (such as +.Ql MouseUp1 ) +and a location suffix (one of +.Ql Pane +for the contents of a pane, +.Ql Border +for a pane border or +.Ql Status +for the status line). +The following mouse events are available: +.Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent +.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" +.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" +.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" +.It Li "WheelUp" Ta "WheelDown" +.El +.Pp +Each should be suffixed with a location, for example +.Ql MouseDown1Status . +.Pp +The special character +.Ql = +may be used as +.Ar target-window +or +.Ar target-pane +in commands bound to mouse key bindings. +It resolves to the window or pane over which the mouse event took place +(for example, the window in the status line over which button 1 was released for a +.Ql MouseUp1Status +binding, or the pane over which the wheel was scrolled for a +.Ql WheelDownPane +binding). +.Pp +The +.Ic send-keys +.Fl M +flag may be used to forward a mouse event to a pane. +.Pp +The default key bindings allow the mouse to be used to select and resize panes, +to copy text and to change window using the status line. +These take effect if the +.Ic mouse +option is turned on. .Sh FORMATS Certain commands accept the .Fl F diff --git a/tmux.h b/tmux.h index 19a8aa3a..477fd436 100644 --- a/tmux.h +++ b/tmux.h @@ -95,10 +95,39 @@ extern char **environ; #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) -/* Other key codes. */ +/* Is this a mouse key? */ +#define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ + ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) + +/* Mouse key codes. */ +#define KEYC_MOUSE_KEY(name) \ + KEYC_ ## name ## _PANE, \ + KEYC_ ## name ## _STATUS, \ + KEYC_ ## name ## _BORDER +#define KEYC_MOUSE_STRING(name, s) \ + { #s "Pane", KEYC_ ## name ## _PANE }, \ + { #s "Status", KEYC_ ## name ## _STATUS }, \ + { #s "Border", KEYC_ ## name ## _BORDER } + +/* Special key codes. */ enum key_code { - /* Mouse key. */ - KEYC_MOUSE = KEYC_BASE, + /* Focus events. */ + KEYC_FOCUS_IN = KEYC_BASE, + KEYC_FOCUS_OUT, + + /* Mouse keys. */ + KEYC_MOUSE, /* unclassified mouse event */ + KEYC_MOUSE_KEY(MOUSEDOWN1), + KEYC_MOUSE_KEY(MOUSEDOWN2), + KEYC_MOUSE_KEY(MOUSEDOWN3), + KEYC_MOUSE_KEY(MOUSEUP1), + KEYC_MOUSE_KEY(MOUSEUP2), + KEYC_MOUSE_KEY(MOUSEUP3), + KEYC_MOUSE_KEY(MOUSEDRAG1), + KEYC_MOUSE_KEY(MOUSEDRAG2), + KEYC_MOUSE_KEY(MOUSEDRAG3), + KEYC_MOUSE_KEY(WHEELUP), + KEYC_MOUSE_KEY(WHEELDOWN), /* Backspace key. */ KEYC_BSPACE, @@ -147,9 +176,6 @@ enum key_code { KEYC_KP_ENTER, KEYC_KP_ZERO, KEYC_KP_PERIOD, - - KEYC_FOCUS_IN, - KEYC_FOCUS_OUT, }; /* Termcap codes. */ @@ -818,16 +844,15 @@ struct input_ctx { * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. */ +struct client; struct session; -struct window; struct mouse_event; struct window_mode { struct screen *(*init)(struct window_pane *); void (*free)(struct window_pane *); void (*resize)(struct window_pane *, u_int, u_int); - void (*key)(struct window_pane *, struct session *, int); - void (*mouse)(struct window_pane *, - struct session *, struct mouse_event *); + void (*key)(struct window_pane *, struct client *, struct session *, + int, struct mouse_event *); void (*timer)(struct window_pane *); }; @@ -1114,54 +1139,35 @@ LIST_HEAD(tty_terms, tty_term); /* Mouse wheel states. */ #define MOUSE_WHEEL_UP 0 -#define MOUSE_WHEEL_DOWN 1 +#define MOUSE_WHEEL_DOWN 64 -/* Mouse wheel multipler. */ -#define MOUSE_WHEEL_SCALE 3 +/* Mouse helpers. */ +#define MOUSE_BUTTONS(b) ((b) & MOUSE_MASK_BUTTONS) +#define MOUSE_WHEEL(b) ((b) & MOUSE_MASK_WHEEL) +#define MOUSE_DRAG(b) ((b) & MOUSE_MASK_DRAG) +#define MOUSE_RELEASE(b) (((b) & MOUSE_MASK_BUTTONS) == 3) -/* Mouse event bits. */ -#define MOUSE_EVENT_DOWN 0x1 -#define MOUSE_EVENT_DRAG 0x2 -#define MOUSE_EVENT_UP 0x4 -#define MOUSE_EVENT_CLICK 0x8 -#define MOUSE_EVENT_WHEEL 0x10 - -/* Mouse flag bits. */ -#define MOUSE_RESIZE_PANE 0x1 - -/* - * Mouse input. When sent by xterm: - * - * - buttons are in the bottom two bits: 0 = b1; 1 = b2; 2 = b3; 3 = released - * - bits 3, 4 and 5 are for keys - * - bit 6 is set for dragging - * - bit 7 for buttons 4 and 5 - * - * With the SGR 1006 extension the released button becomes known. Store these - * in separate fields and store the value converted to the old format in xb. - */ +/* Mouse input. */ struct mouse_event { - u_int xb; + int valid; + + int key; + int statusat; u_int x; - u_int lx; - u_int sx; - u_int y; + u_int b; + + u_int lx; u_int ly; - u_int sy; + u_int lb; - u_int sgr; /* whether the input arrived in SGR format */ - u_int sgr_xb; /* only for SGR: the unmangled button */ - u_int sgr_rel; /* only for SGR: if it is a release event */ + int s; + int w; + int wp; - u_int button; - u_int clicks; - u_int scroll; - - int wheel; - int event; - int flags; + u_int sgr_type; + u_int sgr_b; }; struct tty { @@ -1207,6 +1213,11 @@ struct tty { int term_flags; struct mouse_event mouse; + int mouse_drag_flag; + void (*mouse_drag_update)(struct client *, + struct mouse_event *); + void (*mouse_drag_release)(struct client *, + struct mouse_event *); struct event key_timer; struct tty_key *key_tree; @@ -1382,6 +1393,9 @@ enum cmd_retval { /* Command queue entry. */ struct cmd_q_item { struct cmd_list *cmdlist; + + struct mouse_event mouse; + TAILQ_ENTRY(cmd_q_item) qentry; }; TAILQ_HEAD(cmd_q_items, cmd_q_item); @@ -1723,6 +1737,11 @@ void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); size_t cmd_print(struct cmd *, char *, size_t); +int cmd_mouse_at(struct window_pane *, struct mouse_event *, + u_int *, u_int *, int); +struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); +struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, + struct winlink **); struct session *cmd_current_session(struct cmd_q *, int); struct client *cmd_current_client(struct cmd_q *); struct client *cmd_find_client(struct cmd_q *, const char *, int); @@ -1839,8 +1858,10 @@ int cmdq_free(struct cmd_q *); void printflike(2, 3) cmdq_print(struct cmd_q *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmd_q *, const char *, ...); void cmdq_guard(struct cmd_q *, const char *, int); -void cmdq_run(struct cmd_q *, struct cmd_list *); -void cmdq_append(struct cmd_q *, struct cmd_list *); +void cmdq_run(struct cmd_q *, struct cmd_list *, + struct mouse_event *); +void cmdq_append(struct cmd_q *, struct cmd_list *, + struct mouse_event *); int cmdq_continue(struct cmd_q *); void cmdq_flush(struct cmd_q *); @@ -1862,7 +1883,8 @@ struct key_binding *key_bindings_lookup(int); void key_bindings_add(int, int, struct cmd_list *); void key_bindings_remove(int); void key_bindings_init(void); -void key_bindings_dispatch(struct key_binding *, struct client *); +void key_bindings_dispatch(struct key_binding *, struct client *, + struct mouse_event *); /* key-string.c */ int key_string_lookup_string(const char *); @@ -1930,7 +1952,7 @@ RB_PROTOTYPE(status_out_tree, status_out, entry, status_out_cmp); int status_at_line(struct client *); void status_free_jobs(struct status_out_tree *); void status_update_jobs(struct client *); -void status_set_window_at(struct client *, u_int); +struct window *status_get_window_at(struct client *, u_int); int status_redraw(struct client *); void printflike(2, 3) status_message_set(struct client *, const char *, ...); void status_message_clear(struct client *); @@ -1951,9 +1973,7 @@ void input_free(struct window_pane *); void input_parse(struct window_pane *); /* input-key.c */ -void input_key(struct window_pane *, int); -void input_mouse(struct window_pane *, struct session *, - struct mouse_event *); +void input_key(struct window_pane *, int, struct mouse_event *); /* xterm-keys.c */ char *xterm_keys_lookup(int); @@ -2118,6 +2138,7 @@ void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); void window_set_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); +int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, u_int); void window_resize(struct window *, u_int, u_int); @@ -2148,9 +2169,8 @@ void window_pane_alternate_off(struct window_pane *, int window_pane_set_mode( struct window_pane *, const struct window_mode *); void window_pane_reset_mode(struct window_pane *); -void window_pane_key(struct window_pane *, struct session *, int); -void window_pane_mouse(struct window_pane *, - struct session *, struct mouse_event *); +void window_pane_key(struct window_pane *, struct client *, + struct session *, int, struct mouse_event *); int window_pane_visible(struct window_pane *); char *window_pane_search( struct window_pane *, const char *, u_int *); @@ -2186,7 +2206,6 @@ void layout_resize_pane(struct window_pane *, enum layout_type, int); void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); -void layout_resize_pane_mouse(struct client *); void layout_assign_pane(struct layout_cell *, struct window_pane *); struct layout_cell *layout_split_pane( struct window_pane *, enum layout_type, int, int); @@ -2215,6 +2234,7 @@ void window_copy_init_for_output(struct window_pane *); void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_pageup(struct window_pane *); +void window_copy_start_drag(struct client *, struct mouse_event *); /* window-choose.c */ extern const struct window_mode window_choose_mode; diff --git a/tty-keys.c b/tty-keys.c index a987c44b..1df22d4c 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -644,8 +644,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; struct utf8_data utf8data; - u_int i, value, x, y, b, sgr, sgr_b, sgr_rel; - unsigned char c; + u_int i, value, x, y, b, sgr_b; + u_char sgr_type, c; /* * Standard mouse sequences are \033[M followed by three characters @@ -661,7 +661,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) */ *size = 0; - x = y = b = sgr = sgr_b = sgr_rel = 0; + x = y = b = sgr_b = 0; + sgr_type = ' '; /* First two bytes are always \033[. */ if (buf[0] != '\033') @@ -708,7 +709,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) else y = value; } - log_debug("mouse input: %.*s", (int) *size, buf); + log_debug("mouse input: %.*s", (int)*size, buf); /* Check and return the mouse input. */ if (b < 32) @@ -748,22 +749,26 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) while (1) { if (len <= *size) return (1); - c = (u_char) buf[(*size)++]; + c = (u_char)buf[(*size)++]; if (c == 'M' || c == 'm') break; if (c < '0' || c > '9') return (-1); y = 10 * y + (c - '0'); } - log_debug("mouse input (sgr): %.*s", (int) *size, buf); + log_debug("mouse input (SGR): %.*s", (int)*size, buf); /* Check and return the mouse input. */ if (x < 1 || y < 1) return (-1); x--; y--; - sgr = 1; - sgr_rel = (c == 'm'); + b = sgr_b; + + /* Type is M for press, m for release. */ + sgr_type = c; + if (sgr_type == 'm') + b |= 3; /* * Some terminals (like PuTTY 0.63) mistakenly send @@ -771,64 +776,20 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) * Discard it before it reaches any program running inside * tmux. */ - if (sgr_rel && (sgr_b & 64)) + if (sgr_type == 'm' && (sgr_b & 64)) return (-2); - - /* Figure out what b would be in old format. */ - b = sgr_b; - if (sgr_rel) - b |= 3; } else return (-1); - /* Fill in mouse structure. */ - if (~m->event & MOUSE_EVENT_WHEEL) { - m->lx = m->x; - m->ly = m->y; - } - m->xb = b; - m->sgr = sgr; - m->sgr_xb = sgr_b; - m->sgr_rel = sgr_rel; + /* Fill mouse event. */ + m->lx = m->x; m->x = x; + m->ly = m->y; m->y = y; - if (b & MOUSE_MASK_WHEEL) { - if (b & MOUSE_MASK_SHIFT) - m->scroll = 1; - else - m->scroll = MOUSE_WHEEL_SCALE; - if (b & MOUSE_MASK_META) - m->scroll *= MOUSE_WHEEL_SCALE; - if (b & MOUSE_MASK_CTRL) - m->scroll *= MOUSE_WHEEL_SCALE; - - b &= MOUSE_MASK_BUTTONS; - if (b == 0) - m->wheel = MOUSE_WHEEL_UP; - else if (b == 1) - m->wheel = MOUSE_WHEEL_DOWN; - m->event = MOUSE_EVENT_WHEEL; - - m->button = 3; - } else if ((b & MOUSE_MASK_BUTTONS) == 3) { - if (~m->event & MOUSE_EVENT_DRAG && x == m->sx && y == m->sy) { - m->event = MOUSE_EVENT_CLICK; - m->clicks = (m->clicks + 1) % 3; - } else - m->event = MOUSE_EVENT_DRAG; - m->event |= MOUSE_EVENT_UP; - } else { - if (b & MOUSE_MASK_DRAG) - m->event = MOUSE_EVENT_DRAG; - else { - m->event = MOUSE_EVENT_DOWN; - if (x != m->sx || y != m->sy) - m->clicks = 0; - } - m->button = (b & MOUSE_MASK_BUTTONS); - } - m->sx = x; - m->sy = y; + m->lb = m->b; + m->b = b; + m->sgr_type = sgr_type; + m->sgr_b = sgr_b; return (0); } diff --git a/tty.c b/tty.c index 59ed3bb8..a5067b12 100644 --- a/tty.c +++ b/tty.c @@ -241,6 +241,10 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_STARTED; tty_force_cursor_colour(tty, ""); + + tty->mouse_drag_flag = 0; + tty->mouse_drag_update = NULL; + tty->mouse_drag_release = NULL; } void diff --git a/window-choose.c b/window-choose.c index 8bed8d45..5de87572 100644 --- a/window-choose.c +++ b/window-choose.c @@ -27,11 +27,12 @@ struct screen *window_choose_init(struct window_pane *); void window_choose_free(struct window_pane *); void window_choose_resize(struct window_pane *, u_int, u_int); -void window_choose_key(struct window_pane *, struct session *, int); -void window_choose_mouse( - struct window_pane *, struct session *, struct mouse_event *); +void window_choose_key(struct window_pane *, struct client *, + struct session *, int, struct mouse_event *); void window_choose_default_callback(struct window_choose_data *); +struct window_choose_mode_item *window_choose_get_item(struct window_pane *, + int, struct mouse_event *); void window_choose_fire_callback( struct window_pane *, struct window_choose_data *); @@ -42,7 +43,7 @@ void window_choose_write_line( void window_choose_scroll_up(struct window_pane *); void window_choose_scroll_down(struct window_pane *); -void window_choose_collapse(struct window_pane *, struct session *); +void window_choose_collapse(struct window_pane *, struct session *, u_int); void window_choose_expand(struct window_pane *, struct session *, u_int); enum window_choose_input_type { @@ -55,7 +56,6 @@ const struct window_mode window_choose_mode = { window_choose_free, window_choose_resize, window_choose_key, - window_choose_mouse, NULL, }; @@ -160,8 +160,6 @@ window_choose_init(struct window_pane *wp) s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; - if (options_get_number(&wp->window->options, "mode-mouse")) - s->mode |= MODE_MOUSE_STANDARD; keys = options_get_number(&wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) @@ -237,7 +235,7 @@ window_choose_data_run(struct window_choose_data *cdata) return; } - cmdq_run(cdata->start_client->cmdq, cmdlist); + cmdq_run(cdata->start_client->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); } @@ -325,7 +323,7 @@ window_choose_prompt_input(enum window_choose_input_type input_type, } void -window_choose_collapse(struct window_pane *wp, struct session *s) +window_choose_collapse(struct window_pane *wp, struct session *s, u_int pos) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item, *chosen; @@ -335,7 +333,7 @@ window_choose_collapse(struct window_pane *wp, struct session *s) ARRAY_DECL(, struct window_choose_mode_item) list_copy; ARRAY_INIT(&list_copy); - chosen = &ARRAY_ITEM(&data->list, data->selected); + chosen = &ARRAY_ITEM(&data->list, pos); chosen->state &= ~TREE_EXPANDED; /* @@ -383,7 +381,7 @@ window_choose_collapse_all(struct window_pane *wp) chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session; RB_FOREACH(s, sessions, &sessions) - window_choose_collapse(wp, s); + window_choose_collapse(wp, s, data->selected); /* Reset the selection back to the starting session. */ for (i = 0; i < ARRAY_LENGTH(&data->list); i++) { @@ -483,8 +481,27 @@ window_choose_expand(struct window_pane *wp, struct session *s, u_int pos) } } +struct window_choose_mode_item * +window_choose_get_item(struct window_pane *wp, int key, struct mouse_event *m) +{ + struct window_choose_mode_data *data = wp->modedata; + u_int x, y, idx; + + if (!KEYC_IS_MOUSE(key)) + return (&ARRAY_ITEM(&data->list, data->selected)); + + if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) + return (NULL); + + idx = data->top + y; + if (idx >= ARRAY_LENGTH(&data->list)) + return (NULL); + return (&ARRAY_ITEM(&data->list, idx)); +} + void -window_choose_key(struct window_pane *wp, unused struct session *sess, int key) +window_choose_key(struct window_pane *wp, unused struct client *c, + unused struct session *sess, int key, struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -533,23 +550,28 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) window_choose_fire_callback(wp, NULL); break; case MODEKEYCHOICE_CHOOSE: - item = &ARRAY_ITEM(&data->list, data->selected); + if ((item = window_choose_get_item(wp, key, m)) == NULL) + break; window_choose_fire_callback(wp, item->wcd); break; case MODEKEYCHOICE_TREE_TOGGLE: - item = &ARRAY_ITEM(&data->list, data->selected); - if (item->state & TREE_EXPANDED) - window_choose_collapse(wp, item->wcd->tree_session); - else { + if ((item = window_choose_get_item(wp, key, m)) == NULL) + break; + if (item->state & TREE_EXPANDED) { + window_choose_collapse(wp, item->wcd->tree_session, + item->wcd->idx); + } else { window_choose_expand(wp, item->wcd->tree_session, - data->selected); + item->wcd->idx); } window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_TREE_COLLAPSE: - item = &ARRAY_ITEM(&data->list, data->selected); + if ((item = window_choose_get_item(wp, key, m)) == NULL) + break; if (item->state & TREE_EXPANDED) { - window_choose_collapse(wp, item->wcd->tree_session); + window_choose_collapse(wp, item->wcd->tree_session, + data->selected); window_choose_redraw_screen(wp); } break; @@ -557,7 +579,8 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) window_choose_collapse_all(wp); break; case MODEKEYCHOICE_TREE_EXPAND: - item = &ARRAY_ITEM(&data->list, data->selected); + if ((item = window_choose_get_item(wp, key, m)) == NULL) + break; if (!(item->state & TREE_EXPANDED)) { window_choose_expand(wp, item->wcd->tree_session, data->selected); @@ -711,48 +734,6 @@ window_choose_key(struct window_pane *wp, unused struct session *sess, int key) } } -void -window_choose_mouse(struct window_pane *wp, struct session *sess, - struct mouse_event *m) -{ - struct window_choose_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - struct window_choose_mode_item *item; - u_int idx, i, n; - - if (m->event == MOUSE_EVENT_WHEEL) { - /* - * Multiple line scrolling by default is annoying, so scale - * m->scroll back down. - */ - n = m->scroll; - if (n >= MOUSE_WHEEL_SCALE) - n /= MOUSE_WHEEL_SCALE; - for (i = 0; i < n; i++) { - if (m->wheel == MOUSE_WHEEL_UP) - window_choose_key(wp, sess, KEYC_UP); - else - window_choose_key(wp, sess, KEYC_DOWN); - } - return; - } - - if (~m->event & MOUSE_EVENT_CLICK) - return; - if (m->x >= screen_size_x(s)) - return; - if (m->y >= screen_size_y(s)) - return; - - idx = data->top + m->y; - if (idx >= ARRAY_LENGTH(&data->list)) - return; - data->selected = idx; - - item = &ARRAY_ITEM(&data->list, data->selected); - window_choose_fire_callback(wp, item->wcd); -} - void window_choose_write_line( struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) diff --git a/window-clock.c b/window-clock.c index ede8df5b..3cabd9e9 100644 --- a/window-clock.c +++ b/window-clock.c @@ -27,7 +27,8 @@ struct screen *window_clock_init(struct window_pane *); void window_clock_free(struct window_pane *); void window_clock_resize(struct window_pane *, u_int, u_int); -void window_clock_key(struct window_pane *, struct session *, int); +void window_clock_key(struct window_pane *, struct client *, + struct session *, int, struct mouse_event *); void window_clock_timer(struct window_pane *); void window_clock_draw_screen(struct window_pane *); @@ -37,7 +38,6 @@ const struct window_mode window_clock_mode = { window_clock_free, window_clock_resize, window_clock_key, - NULL, window_clock_timer, }; @@ -157,8 +157,8 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) } void -window_clock_key( - struct window_pane *wp, unused struct session *sess, unused int key) +window_clock_key(struct window_pane *wp, unused struct client *c, + unused struct session *sess, unused int key, unused struct mouse_event *m) { window_pane_reset_mode(wp); } diff --git a/window-copy.c b/window-copy.c index feb8c481..a0560716 100644 --- a/window-copy.c +++ b/window-copy.c @@ -27,11 +27,10 @@ struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); -void window_copy_key(struct window_pane *, struct session *, int); +void window_copy_key(struct window_pane *, struct client *, struct session *, + int, struct mouse_event *); int window_copy_key_input(struct window_pane *, int); int window_copy_key_numeric_prefix(struct window_pane *, int); -void window_copy_mouse(struct window_pane *, struct session *, - struct mouse_event *); void window_copy_redraw_selection(struct window_pane *, u_int); void window_copy_redraw_lines(struct window_pane *, u_int, u_int); @@ -84,13 +83,14 @@ void window_copy_cursor_previous_word(struct window_pane *, const char *); void window_copy_scroll_up(struct window_pane *, u_int); void window_copy_scroll_down(struct window_pane *, u_int); void window_copy_rectangle_toggle(struct window_pane *); +void window_copy_drag_update(struct client *, struct mouse_event *); +void window_copy_drag_release(struct client *, struct mouse_event *); const struct window_mode window_copy_mode = { window_copy_init, window_copy_free, window_copy_resize, window_copy_key, - window_copy_mouse, NULL, }; @@ -124,38 +124,38 @@ enum window_copy_input_type { * mode ends). */ struct window_copy_mode_data { - struct screen screen; + struct screen screen; - struct screen *backing; - int backing_written; /* backing display has started */ + struct screen *backing; + int backing_written; /* backing display started */ - struct mode_key_data mdata; + struct mode_key_data mdata; - u_int oy; + u_int oy; - u_int selx; - u_int sely; + u_int selx; + u_int sely; - u_int rectflag; /* are we in rectangle copy mode? */ + u_int rectflag; /* are we in rectangle copy mode? */ - u_int cx; - u_int cy; + u_int cx; + u_int cy; - u_int lastcx; /* position in last line with content */ - u_int lastsx; /* size of last line with content */ + u_int lastcx; /* position in last line w/ content */ + u_int lastsx; /* size of last line w/ content */ enum window_copy_input_type inputtype; - const char *inputprompt; - char *inputstr; - int inputexit; + const char *inputprompt; + char *inputstr; + int inputexit; - int numprefix; + int numprefix; enum window_copy_input_type searchtype; - char *searchstr; + char *searchstr; enum window_copy_input_type jumptype; - char jumpchar; + char jumpchar; }; struct screen * @@ -193,8 +193,6 @@ window_copy_init(struct window_pane *wp) s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); - if (options_get_number(&wp->window->options, "mode-mouse")) - s->mode |= MODE_MOUSE_STANDARD; keys = options_get_number(&wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) @@ -367,19 +365,20 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) } void -window_copy_key(struct window_pane *wp, struct session *sess, int key) +window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, + int key, struct mouse_event *m) { const char *word_separators; struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - u_int n; - int np, keys; + u_int n, np; + int keys; enum mode_key_cmd cmd; const char *arg, *ss; - np = data->numprefix; - if (np <= 0) - np = 1; + np = 1; + if (data->numprefix > 0) + np = data->numprefix; if (data->inputtype == WINDOW_COPY_JUMPFORWARD || data->inputtype == WINDOW_COPY_JUMPBACK || @@ -536,9 +535,14 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key) window_copy_redraw_screen(wp); break; case MODEKEYCOPY_STARTSELECTION: - s->sel.lineflag = LINE_SEL_NONE; - window_copy_start_selection(wp); - window_copy_redraw_screen(wp); + if (KEYC_IS_MOUSE(key)) { + if (c != NULL) + window_copy_start_drag(c, m); + } else { + s->sel.lineflag = LINE_SEL_NONE; + window_copy_start_selection(wp); + window_copy_redraw_screen(wp); + } break; case MODEKEYCOPY_SELECTLINE: s->sel.lineflag = LINE_SEL_LEFT_RIGHT; @@ -887,75 +891,6 @@ window_copy_key_numeric_prefix(struct window_pane *wp, int key) return (0); } -void -window_copy_mouse(struct window_pane *wp, struct session *sess, - struct mouse_event *m) -{ - struct window_copy_mode_data *data = wp->modedata; - struct screen *s = &data->screen; - u_int i, old_cy; - - if (m->x >= screen_size_x(s)) - return; - if (m->y >= screen_size_y(s)) - return; - - /* If mouse wheel (buttons 4 and 5), scroll. */ - if (m->event == MOUSE_EVENT_WHEEL) { - for (i = 0; i < m->scroll; i++) { - if (m->wheel == MOUSE_WHEEL_UP) - window_copy_cursor_up(wp, 1); - else { - window_copy_cursor_down(wp, 1); - - /* - * We reached the bottom, leave copy mode, but - * only if no selection is in progress. - */ - if (data->oy == 0 && !s->sel.flag && - s->sel.lineflag == LINE_SEL_NONE) - goto reset_mode; - } - } - return; - } - - /* - * If already reading motion, move the cursor while buttons are still - * pressed, or stop the selection on their release. - */ - if (s->mode & MODE_MOUSE_BUTTON) { - if (~m->event & MOUSE_EVENT_UP) { - old_cy = data->cy; - window_copy_update_cursor(wp, m->x, m->y); - if (window_copy_update_selection(wp, 1)) - window_copy_redraw_selection(wp, old_cy); - return; - } - goto reset_mode; - } - - /* Otherwise if other buttons pressed, start selection and motion. */ - if (~m->event & MOUSE_EVENT_UP) { - s->mode &= ~MODE_MOUSE_STANDARD; - s->mode |= MODE_MOUSE_BUTTON; - - window_copy_update_cursor(wp, m->x, m->y); - window_copy_start_selection(wp); - window_copy_redraw_screen(wp); - } - - return; - -reset_mode: - s->mode &= ~MODE_MOUSE_BUTTON; - s->mode |= MODE_MOUSE_STANDARD; - if (sess != NULL) { - window_copy_copy_selection(wp, NULL); - window_pane_reset_mode(wp); - } -} - void window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) { @@ -2274,3 +2209,62 @@ window_copy_rectangle_toggle(struct window_pane *wp) window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } + +void +window_copy_start_drag(struct client *c, unused struct mouse_event *m) +{ + struct window_pane *wp; + struct window_copy_mode_data *data; + u_int x, y; + + wp = cmd_mouse_pane(m, NULL, NULL); + if (wp->mode != &window_copy_mode) + return; + data = wp->modedata; + + if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) + return; + + c->tty.mouse_drag_update = window_copy_drag_update; + c->tty.mouse_drag_release = window_copy_drag_release; + + window_copy_update_cursor(wp, x, y); + window_copy_start_selection(wp); + window_copy_redraw_screen(wp); +} + +void +window_copy_drag_update(unused struct client *c, struct mouse_event *m) +{ + struct window_pane *wp; + struct window_copy_mode_data *data; + u_int x, y, old_cy; + + wp = cmd_mouse_pane(m, NULL, NULL); + if (wp->mode != &window_copy_mode) + return; + data = wp->modedata; + + if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) + return; + old_cy = data->cy; + + window_copy_update_cursor(wp, x, y); + if (window_copy_update_selection(wp, 1)) + window_copy_redraw_selection(wp, old_cy); +} + +void +window_copy_drag_release(unused struct client *c, struct mouse_event *m) +{ + struct window_pane *wp; + struct window_copy_mode_data *data; + + wp = cmd_mouse_pane(m, NULL, NULL); + if (wp->mode != &window_copy_mode) + return; + data = wp->modedata; + + window_copy_copy_selection(wp, NULL); + window_pane_reset_mode(wp); +} diff --git a/window.c b/window.c index 4abcf495..53e74a1d 100644 --- a/window.c +++ b/window.c @@ -386,6 +386,18 @@ window_resize(struct window *w, u_int sx, u_int sy) w->sy = sy; } +int +window_has_pane(struct window *w, struct window_pane *wp) +{ + struct window_pane *wp1; + + TAILQ_FOREACH(wp1, &w->panes, entry) { + if (wp1 == wp) + return (1); + } + return (0); +} + int window_set_active_pane(struct window *w, struct window_pane *wp) { @@ -1052,52 +1064,37 @@ window_pane_reset_mode(struct window_pane *wp) } void -window_pane_key(struct window_pane *wp, struct session *sess, int key) +window_pane_key(struct window_pane *wp, struct client *c, struct session *s, + int key, struct mouse_event *m) { struct window_pane *wp2; + if (KEYC_IS_MOUSE(key) && m == NULL) + return; + if (wp->mode != NULL) { if (wp->mode->key != NULL) - wp->mode->key(wp, sess, key); + wp->mode->key(wp, c, s, key, m); return; } if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) return; - input_key(wp, key); + input_key(wp, key, m); + + if (KEYC_IS_MOUSE(key)) + return; if (options_get_number(&wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 == wp || wp2->mode != NULL) continue; if (wp2->fd != -1 && window_pane_visible(wp2)) - input_key(wp2, key); + input_key(wp2, key, NULL); } } } -void -window_pane_mouse(struct window_pane *wp, struct session *sess, - struct mouse_event *m) -{ - if (!window_pane_visible(wp)) - return; - - if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx) - return; - if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy) - return; - m->x -= wp->xoff; - m->y -= wp->yoff; - - if (wp->mode != NULL) { - if (wp->mode->mouse != NULL && - options_get_number(&wp->window->options, "mode-mouse")) - wp->mode->mouse(wp, sess, m); - } else if (wp->fd != -1) - input_mouse(wp, sess, m); -} - int window_pane_visible(struct window_pane *wp) { From bbac2aee1fe16b1ff67b0e2dd8faada68e157a5e Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 19 Apr 2015 21:46:52 +0000 Subject: [PATCH 077/703] Honour renumber-windows when unlinking a window, from Thomas Adam. --- server-fn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-fn.c b/server-fn.c index 83ea9474..a065dd76 100644 --- a/server-fn.c +++ b/server-fn.c @@ -351,6 +351,7 @@ server_unlink_window(struct session *s, struct winlink *wl) server_destroy_session_group(s); else server_redraw_session_group(s); + session_renumber_windows(s); } void From 8101f1ef16d2f163dde885a42ef5480ff2562e43 Mon Sep 17 00:00:00 2001 From: jmc Date: Sun, 19 Apr 2015 22:10:30 +0000 Subject: [PATCH 078/703] tweak previous; --- tmux.1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tmux.1 b/tmux.1 index fb08ddc1..9b6f8341 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1028,7 +1028,7 @@ The option scrolls one page up. .Fl M begins a mouse drag (only valid if bound to a mouse key binding, see -.Sx MOUSE SUPPORT Ns ). +.Sx MOUSE SUPPORT ) . .El .Pp Each window displayed by @@ -1678,7 +1678,7 @@ and unzoomed (its normal position in the layout). .Pp .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see -.Sx MOUSE SUPPORT Ns ). +.Sx MOUSE SUPPORT ) . .It Xo Ic respawn-pane .Op Fl k .Op Fl t Ar target-pane @@ -1746,7 +1746,7 @@ Make pane .Ar target-pane the active pane in window .Ar target-window , -or set it's style (with +or set its style (with .Fl P ) . If one of .Fl D , @@ -2011,7 +2011,7 @@ flag causes the terminal state to be reset. .Pp .Fl M passes through a mouse event (only valid if bound to a mouse key binding, see -.Sx MOUSE SUPPORT Ns ). +.Sx MOUSE SUPPORT ) . .It Xo Ic send-prefix .Op Fl 2 .Op Fl t Ar target-pane @@ -3092,7 +3092,7 @@ The following mouse events are available: .It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" .It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" .It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" -.It Li "WheelUp" Ta "WheelDown" +.It Li "WheelUp" Ta "WheelDown" Ta "" .El .Pp Each should be suffixed with a location, for example From acb8248ba64e1c9770d39d9790439c74eb9e5d80 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 20 Apr 2015 08:46:21 +0100 Subject: [PATCH 079/703] +. --- TODO | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 35e218b5..34b6cfe3 100644 --- a/TODO +++ b/TODO @@ -137,7 +137,9 @@ * cmd_find_* could be much simpler - parse everything the same, only difference is what to choose when not given a ":" or "." (such as a plain "0" could be session, window or pane). So just cmd_find_target - with a type (session, window, or pane).. + with a type (session, window, or pane) + * instead of all the obscure =, ^ -t codes, we should support something + simpler and easier like -t:mouse:, -t:first: - miscellaneous * way to keep a job running just read its last line of output for #() From 6f587570ed8c0e2db8bd4e676f4363b0824beb5f Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2015 07:50:49 +0000 Subject: [PATCH 080/703] Use a more sensible buffer size for flags string. --- window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window.c b/window.c index 53e74a1d..86cc8bb0 100644 --- a/window.c +++ b/window.c @@ -650,7 +650,7 @@ window_destroy_panes(struct window *w) char * window_printable_flags(struct session *s, struct winlink *wl) { - char flags[BUFSIZ]; + char flags[32]; int pos; pos = 0; From 0fd9a97202dc2878d9cf21f3bea01b599c21e61b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2015 09:39:21 +0000 Subject: [PATCH 081/703] Make jump-to-backward/jump-to-forward repeatable with jump-reverse/jump-again, from Jacob Niehus. --- window-copy.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/window-copy.c b/window-copy.c index a0560716..afa609ed 100644 --- a/window-copy.c +++ b/window-copy.c @@ -75,8 +75,8 @@ void window_copy_cursor_up(struct window_pane *, int); void window_copy_cursor_down(struct window_pane *, int); void window_copy_cursor_jump(struct window_pane *); void window_copy_cursor_jump_back(struct window_pane *); -void window_copy_cursor_jump_to(struct window_pane *); -void window_copy_cursor_jump_to_back(struct window_pane *); +void window_copy_cursor_jump_to(struct window_pane *, int); +void window_copy_cursor_jump_to_back(struct window_pane *, int); void window_copy_cursor_next_word(struct window_pane *, const char *); void window_copy_cursor_next_word_end(struct window_pane *, const char *); void window_copy_cursor_previous_word(struct window_pane *, const char *); @@ -397,11 +397,11 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, } if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + window_copy_cursor_jump_to(wp, 0); } if (data->inputtype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 0); } } data->jumptype = data->inputtype; @@ -643,10 +643,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, window_copy_cursor_jump_back(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + window_copy_cursor_jump_to(wp, 1); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 1); } break; case MODEKEYCOPY_JUMPREVERSE: @@ -658,10 +658,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, window_copy_cursor_jump(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + window_copy_cursor_jump_to_back(wp, 1); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + window_copy_cursor_jump_to(wp, 1); } break; case MODEKEYCOPY_JUMPBACK: @@ -1944,7 +1944,7 @@ window_copy_cursor_jump_back(struct window_pane *wp) } void -window_copy_cursor_jump_to(struct window_pane *wp) +window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; @@ -1952,7 +1952,7 @@ window_copy_cursor_jump_to(struct window_pane *wp) struct utf8_data ud; u_int px, py, xx; - px = data->cx + 1; + px = data->cx + 1 + jump_again; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); @@ -1971,7 +1971,7 @@ window_copy_cursor_jump_to(struct window_pane *wp) } void -window_copy_cursor_jump_to_back(struct window_pane *wp) +window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; @@ -1985,6 +1985,9 @@ window_copy_cursor_jump_to_back(struct window_pane *wp) if (px > 0) px--; + if (jump_again && px > 0) + px--; + for (;;) { gc = grid_peek_cell(back_s->grid, px, py); grid_cell_get(gc, &ud); From 3497843f0272e573d0a63cb6e94948591ae07667 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2015 14:48:55 +0000 Subject: [PATCH 082/703] Style nit - unnecessary brackets. --- utf8.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utf8.c b/utf8.c index 2940ebb9..12a0b29e 100644 --- a/utf8.c +++ b/utf8.c @@ -290,9 +290,9 @@ utf8_build(void) while (*ptr != NULL) { node = *ptr; if (item->last < node->first) - ptr = &(node->left); + ptr = &node->left; else if (item->first > node->last) - ptr = &(node->right); + ptr = &node->right; } *ptr = item; } From bded7437064c76dd6cf4e76e558d826859adcc79 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2015 15:34:56 +0000 Subject: [PATCH 083/703] Support for multiple key tables to commands to be bound to sequences of keys. The default key bindings become the "prefix" table and -n the "root" table. Keys may be bound in new tables with bind -T and switch-client -T used to specify the table in which the next key should be looked up. Based on a diff from Keith Amling. --- cmd-bind-key.c | 16 +++-- cmd-list-keys.c | 86 ++++++++++++---------- cmd-switch-client.c | 19 ++++- cmd-unbind-key.c | 37 +++++++--- format.c | 6 +- key-bindings.c | 112 ++++++++++++++++++++++------- server-client.c | 170 ++++++++++++++++++++++++++------------------ tmux.1 | 106 +++++++++++++++++++-------- tmux.h | 39 ++++++---- 9 files changed, 394 insertions(+), 197 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 47c58e51..fda39efc 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -33,8 +33,8 @@ enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_bind_key_entry = { "bind-key", "bind", - "cnrt:", 1, -1, - "[-cnr] [-t mode-table] key command [arguments]", + "cnrt:T:", 1, -1, + "[-cnr] [-t mode-table] [-T key-table] key command [arguments]", 0, cmd_bind_key_exec }; @@ -46,6 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) char *cause; struct cmd_list *cmdlist; int key; + const char *tablename; if (args_has(args, 't')) { if (args->argc != 2 && args->argc != 3) { @@ -68,6 +69,13 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 't')) return (cmd_bind_key_mode_table(self, cmdq, key)); + if (args_has(args, 'T')) + tablename = args_get(args, 'T'); + else if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; + cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, &cause); if (cmdlist == NULL) { @@ -76,9 +84,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if (!args_has(args, 'n')) - key |= KEYC_PREFIX; - key_bindings_add(key, args_has(args, 'r'), cmdlist); + key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist); return (CMD_RETURN_NORMAL); } diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 0733ee28..e2d5dc52 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -33,8 +33,8 @@ enum cmd_retval cmd_list_keys_commands(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { "list-keys", "lsk", - "t:", 0, 0, - "[-t key-table]", + "t:T:", 0, 0, + "[-t mode-table] [-T key-table]", 0, cmd_list_keys_exec }; @@ -51,11 +51,12 @@ enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct key_table *table; struct key_binding *bd; - const char *key; - char tmp[BUFSIZ], flags[8]; + const char *key, *tablename, *r; + char tmp[BUFSIZ]; size_t used; - int width, keywidth; + int repeat, width, tablewidth, keywidth; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, cmdq)); @@ -63,46 +64,57 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); - width = 0; - - RB_FOREACH(bd, key_bindings, &key_bindings) { - key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); - if (key == NULL) - continue; - - keywidth = strlen(key); - if (!(bd->key & KEYC_PREFIX)) { - if (bd->can_repeat) - keywidth += 4; - else - keywidth += 3; - } else if (bd->can_repeat) - keywidth += 3; - if (keywidth > width) - width = keywidth; + tablename = args_get(args, 'T'); + if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); } - RB_FOREACH(bd, key_bindings, &key_bindings) { - key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); - if (key == NULL) + repeat = 0; + tablewidth = keywidth = 0; + RB_FOREACH(table, key_tables, &key_tables) { + if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; + RB_FOREACH(bd, key_bindings, &table->key_bindings) { + key = key_string_lookup_key(bd->key); + if (key == NULL) + continue; - *flags = '\0'; - if (!(bd->key & KEYC_PREFIX)) { if (bd->can_repeat) - xsnprintf(flags, sizeof flags, "-rn "); - else - xsnprintf(flags, sizeof flags, "-n "); - } else if (bd->can_repeat) - xsnprintf(flags, sizeof flags, "-r "); + repeat = 1; - used = xsnprintf(tmp, sizeof tmp, "%s%*s ", - flags, (int) (width - strlen(flags)), key); - if (used >= sizeof tmp) + width = strlen(table->name); + if (width > tablewidth) + tablewidth =width; + width = strlen(key); + if (width > keywidth) + keywidth = width; + } + } + + RB_FOREACH(table, key_tables, &key_tables) { + if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; + RB_FOREACH(bd, key_bindings, &table->key_bindings) { + key = key_string_lookup_key(bd->key); + if (key == NULL) + continue; - cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); - cmdq_print(cmdq, "bind-key %s", tmp); + if (!repeat) + r = ""; + else if (bd->can_repeat) + r = "-r "; + else + r = " "; + used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ", r, + (int)tablewidth, table->name, (int)keywidth, key); + if (used < sizeof tmp) { + cmd_list_print(bd->cmdlist, tmp + used, + (sizeof tmp) - used); + } + + cmdq_print(cmdq, "bind-key %s", tmp); + } } return (CMD_RETURN_NORMAL); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 439f593b..8c4daf97 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { "switch-client", "switchc", - "lc:npt:r", 0, 0, - "[-lnpr] [-c target-client] [-t target-session]", + "lc:npt:rT:", 0, 0, + "[-lnpr] [-c target-client] [-t target-session] [-T key-table]", CMD_READONLY, cmd_switch_client_exec }; @@ -46,7 +46,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; - const char *tflag; + const char *tflag, *tablename; + struct key_table *table; if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); @@ -58,6 +59,18 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) c->flags |= CLIENT_READONLY; } + tablename = args_get(args, 'T'); + if (tablename != NULL) { + table = key_bindings_get_table(tablename, 0); + if (table == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } + table->references++; + key_bindings_unref_table(c->keytable); + c->keytable = table; + } + tflag = args_get(args, 't'); if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 710210ce..493f6dde 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_unbind_key_entry = { "unbind-key", "unbind", - "acnt:", 0, 1, - "[-acn] [-t mode-table] key", + "acnt:T:", 0, 1, + "[-acn] [-t mode-table] [-T key-table] key", 0, cmd_unbind_key_exec }; @@ -40,9 +40,9 @@ const struct cmd_entry cmd_unbind_key_entry = { enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct key_binding *bd; - int key; + struct args *args = self->args; + int key; + const char *tablename; if (!args_has(args, 'a')) { if (args->argc != 1) { @@ -66,16 +66,31 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) return (cmd_unbind_key_mode_table(self, cmdq, key)); if (key == KEYC_NONE) { - while (!RB_EMPTY(&key_bindings)) { - bd = RB_ROOT(&key_bindings); - key_bindings_remove(bd->key); + tablename = args_get(args, 'T'); + if (tablename == NULL) { + key_bindings_remove_table("root"); + key_bindings_remove_table("prefix"); + return (CMD_RETURN_NORMAL); } + if (key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } + key_bindings_remove_table(tablename); return (CMD_RETURN_NORMAL); } - if (!args_has(args, 'n')) - key |= KEYC_PREFIX; - key_bindings_remove(key); + if (args_has(args, 'T')) { + tablename = args_get(args, 'T'); + if (key_bindings_get_table(tablename, 0) == NULL) { + cmdq_error(cmdq, "table %s doesn't exist", tablename); + return (CMD_RETURN_ERROR); + } + } else if (args_has(args, 'n')) + tablename = "root"; + else + tablename = "prefix"; + key_bindings_remove(tablename, key); return (CMD_RETURN_NORMAL); } diff --git a/format.c b/format.c index cf1713ba..eb4664d1 100644 --- a/format.c +++ b/format.c @@ -545,7 +545,11 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_activity", "%lld", (long long) t); format_add(ft, "client_activity_string", "%s", format_time_string(t)); - format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX)); + if (strcmp(c->keytable->name, "root") == 0) + format_add(ft, "client_prefix", "%d", 0); + else + format_add(ft, "client_prefix", "%d", 1); + format_add(ft, "client_key_table", "%s", c->keytable->name); if (c->tty.flags & TTY_UTF8) format_add(ft, "client_utf8", "%d", 1); diff --git a/key-bindings.c b/key-bindings.c index c6cdeeb7..c34db710 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -25,60 +25,120 @@ #include "tmux.h" RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); +RB_GENERATE(key_tables, key_table, entry, key_table_cmp); +struct key_tables key_tables = RB_INITIALIZER(&key_tables); -struct key_bindings key_bindings; +int +key_table_cmp(struct key_table *e1, struct key_table *e2) +{ + return (strcmp(e1->name, e2->name)); +} int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { - int key1, key2; - - key1 = bd1->key & ~KEYC_PREFIX; - key2 = bd2->key & ~KEYC_PREFIX; - if (key1 != key2) - return (key1 - key2); - - if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX)) - return (-1); - if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX)) - return (1); - return (0); + return (bd1->key - bd2->key); } -struct key_binding * -key_bindings_lookup(int key) +struct key_table * +key_bindings_get_table(const char *name, int create) { - struct key_binding bd; + struct key_table table_find, *table; - bd.key = key; - return (RB_FIND(key_bindings, &key_bindings, &bd)); + table_find.name = name; + table = RB_FIND(key_tables, &key_tables, &table_find); + if (table != NULL || !create) + return (table); + + table = xmalloc(sizeof *table); + table->name = xstrdup(name); + RB_INIT(&table->key_bindings); + + table->references = 1; /* one reference in key_tables */ + RB_INSERT(key_tables, &key_tables, table); + + return (table); } void -key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist) +key_bindings_unref_table(struct key_table *table) { struct key_binding *bd; - key_bindings_remove(key); + if (--table->references != 0) + return; + + while (!RB_EMPTY(&table->key_bindings)) { + bd = RB_ROOT(&table->key_bindings); + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(bd); + } + + free((void *)table->name); + free(table); +} + +void +key_bindings_add(const char *name, int key, int can_repeat, + struct cmd_list *cmdlist) +{ + struct key_table *table; + struct key_binding bd_find, *bd; + + table = key_bindings_get_table(name, 1); + + bd_find.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + if (bd != NULL) { + RB_REMOVE(key_bindings, &table->key_bindings, bd); + cmd_list_free(bd->cmdlist); + free(bd); + } bd = xmalloc(sizeof *bd); bd->key = key; - RB_INSERT(key_bindings, &key_bindings, bd); + RB_INSERT(key_bindings, &table->key_bindings, bd); bd->can_repeat = can_repeat; bd->cmdlist = cmdlist; } void -key_bindings_remove(int key) +key_bindings_remove(const char *name, int key) { - struct key_binding *bd; + struct key_table *table; + struct key_binding bd_find, *bd; - if ((bd = key_bindings_lookup(key)) == NULL) + table = key_bindings_get_table(name, 0); + if (table == NULL) return; - RB_REMOVE(key_bindings, &key_bindings, bd); + + bd_find.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + if (bd == NULL) + return; + + RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); + + if (RB_EMPTY(&table->key_bindings)) { + RB_REMOVE(key_tables, &key_tables, table); + key_bindings_unref_table(table); + } +} + +void +key_bindings_remove_table(const char *name) +{ + struct key_table *table; + + table = key_bindings_get_table(name, 0); + if (table != NULL) { + RB_REMOVE(key_tables, &key_tables, table); + key_bindings_unref_table(table); + } } void @@ -169,8 +229,6 @@ key_bindings_init(void) int error; struct cmd_q *cmdq; - RB_INIT(&key_bindings); - cmdq = cmdq_new(NULL); for (i = 0; i < nitems(defaults); i++) { error = cmd_string_parse(defaults[i], &cmdlist, diff --git a/server-client.c b/server-client.c index 3968c50b..b4d1f744 100644 --- a/server-client.c +++ b/server-client.c @@ -30,6 +30,7 @@ #include "tmux.h" +void server_client_key_table(struct client *, const char *); void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); int server_client_check_mouse(struct client *); @@ -45,6 +46,15 @@ void server_client_msg_command(struct client *, struct imsg *); void server_client_msg_identify(struct client *, struct imsg *); void server_client_msg_shell(struct client *); +/* Set client key table. */ +void +server_client_key_table(struct client *c, const char *name) +{ + key_bindings_unref_table(c->keytable); + c->keytable = key_bindings_get_table(name, 1); + c->keytable->references++; +} + /* Create a new client. */ void server_client_create(int fd) @@ -93,6 +103,9 @@ server_client_create(int fd) c->flags |= CLIENT_FOCUSED; + c->keytable = key_bindings_get_table("root", 1); + c->keytable->references++; + evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { @@ -164,6 +177,8 @@ server_client_lost(struct client *c) evtimer_del(&c->repeat_timer); + key_bindings_unref_table(c->keytable); + if (event_initialized(&c->identify_timer)) evtimer_del(&c->identify_timer); @@ -527,16 +542,19 @@ void server_client_handle_key(struct client *c, int key) { struct mouse_event *m = &c->tty.mouse; - struct session *s; + struct session *s = c->session; struct window *w; struct window_pane *wp; struct timeval tv; - struct key_binding *bd; - int xtimeout, isprefix, ispaste; + struct key_table *table = c->keytable; + struct key_binding bd_find, *bd; + int xtimeout; /* Check the client is good to accept input. */ - if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) + if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; + w = s->curw->window; + wp = w->active; /* No session, do nothing. */ if (c->session == NULL) @@ -552,7 +570,7 @@ server_client_handle_key(struct client *c, int key) sizeof s->last_activity_time); memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); - /* Special case: number keys jump to pane in identify mode. */ + /* Number keys jump to pane in identify mode. */ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { if (c->flags & CLIENT_READONLY) return; @@ -593,74 +611,88 @@ server_client_handle_key(struct client *c, int key) } else m->valid = 0; - /* Is this a prefix key? */ - if (key == options_get_number(&s->options, "prefix")) - isprefix = 1; - else if (key == options_get_number(&s->options, "prefix2")) - isprefix = 1; - else - isprefix = 0; - - /* Treat prefix as a regular key when pasting is detected. */ - ispaste = server_client_assume_paste(s); - if (ispaste) - isprefix = 0; - - /* No previous prefix key. */ - if (!(c->flags & CLIENT_PREFIX)) { - if (isprefix) { - c->flags |= CLIENT_PREFIX; - server_status_client(c); - return; - } - - /* Try as a non-prefix key binding. */ - if (ispaste || (bd = key_bindings_lookup(key)) == NULL) { - if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, c, s, key, m); - } else - key_bindings_dispatch(bd, c, m); - return; - } - - /* Prefix key already pressed. Reset prefix and lookup key. */ - c->flags &= ~CLIENT_PREFIX; - server_status_client(c); - if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { - /* If repeating, treat this as a key, else ignore. */ - if (c->flags & CLIENT_REPEAT) { - c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, c, s, key, m); - } - return; - } - - /* If already repeating, but this key can't repeat, skip it. */ - if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { - c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else if (!(c->flags & CLIENT_READONLY)) + /* Treat everything as a regular key when pasting is detected. */ + if (server_client_assume_paste(s)) { + if (!(c->flags & CLIENT_READONLY)) window_pane_key(wp, c, s, key, m); return; } - /* If this key can repeat, reset the repeat flags and timer. */ - xtimeout = options_get_number(&s->options, "repeat-time"); - if (xtimeout != 0 && bd->can_repeat) { - c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; +retry: + /* Try to see if there is a key binding in the current table. */ + bd_find.key = key; + bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + if (bd != NULL) { + /* + * Key was matched in this table. If currently repeating but a + * non-repeating binding was found, stop repeating and try + * again in the root table. + */ + if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { + server_client_key_table(c, "root"); + c->flags &= ~CLIENT_REPEAT; + server_status_client(c); + goto retry; + } - tv.tv_sec = xtimeout / 1000; - tv.tv_usec = (xtimeout % 1000) * 1000L; - evtimer_del(&c->repeat_timer); - evtimer_add(&c->repeat_timer, &tv); + /* + * Take a reference to this table to make sure the key binding + * doesn't disappear. + */ + table->references++; + + /* + * If this is a repeating key, start the timer. Otherwise reset + * the client back to the root table. + */ + xtimeout = options_get_number(&s->options, "repeat-time"); + if (xtimeout != 0 && bd->can_repeat) { + c->flags |= CLIENT_REPEAT; + + tv.tv_sec = xtimeout / 1000; + tv.tv_usec = (xtimeout % 1000) * 1000L; + evtimer_del(&c->repeat_timer); + evtimer_add(&c->repeat_timer, &tv); + } else { + c->flags &= ~CLIENT_REPEAT; + server_client_key_table(c, "root"); + } + server_status_client(c); + + /* Dispatch the key binding. */ + key_bindings_dispatch(bd, c, m); + key_bindings_unref_table(table); + return; } - /* Dispatch the command. */ - key_bindings_dispatch(bd, c, m); + /* + * No match in this table. If repeating, switch the client back to the + * root table and try again. + */ + if (c->flags & CLIENT_REPEAT) { + server_client_key_table(c, "root"); + c->flags &= ~CLIENT_REPEAT; + server_status_client(c); + goto retry; + } + + /* If no match and we're not in the root table, that's it. */ + if (strcmp(c->keytable->name, "root") != 0) { + server_client_key_table(c, "root"); + server_status_client(c); + return; + } + + /* + * No match, but in the root table. Prefix switches to the prefix table + * and everything else is passed through. + */ + if (key == options_get_number(&s->options, "prefix") || + key == options_get_number(&s->options, "prefix2")) { + server_client_key_table(c, "prefix"); + server_status_client(c); + } else if (!(c->flags & CLIENT_READONLY)) + window_pane_key(wp, c, s, key, m); } /* Client functions that need to happen every loop. */ @@ -848,9 +880,9 @@ server_client_repeat_timer(unused int fd, unused short events, void *data) struct client *c = data; if (c->flags & CLIENT_REPEAT) { - if (c->flags & CLIENT_PREFIX) - server_status_client(c); - c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); + server_client_key_table(c, "root"); + c->flags &= ~CLIENT_REPEAT; + server_status_client(c); } } diff --git a/tmux.1 b/tmux.1 index 9b6f8341..ab762387 100644 --- a/tmux.1 +++ b/tmux.1 @@ -838,6 +838,7 @@ Suspend a client by sending .Op Fl lnpr .Op Fl c Ar target-client .Op Fl t Ar target-session +.Op Fl T Ar key-table .Xc .D1 (alias: Ic switchc ) Switch the current session for client @@ -855,6 +856,22 @@ respectively. toggles whether a client is read-only (see the .Ic attach-session command). +.Pp +.Fl T +sets the client's key table; the next key from the client will be interpreted from +.Ar key-table . +This may be used to configure multiple prefix keys, or to bind commands to +sequences of keys. +For example, to make typing +.Ql abc +run the +.Ic list-keys +command: +.Bd -literal -offset indent +bind-key -Ttable2 c list-keys +bind-key -Ttable1 b switch-client -Ttable2 +bind-key -Troot a switch-client -Ttable1 +.Ed .El .Sh WINDOWS AND PANES A @@ -1931,6 +1948,7 @@ Commands related to key bindings are as follows: .It Xo Ic bind-key .Op Fl cnr .Op Fl t Ar mode-table +.Op Fl T Ar key-table .Ar key Ar command Op Ar arguments .Xc .D1 (alias: Ic bind ) @@ -1938,16 +1956,40 @@ Bind key .Ar key to .Ar command . -By default (without -.Fl t ) -the primary key bindings are modified (those normally activated with the prefix -key); in this case, if -.Fl n -is specified, it is not necessary to use the prefix key, -.Ar command +Keys are bound in a key table. +By default (without -T), the key is bound in +the +.Em prefix +key table. +This table is used for keys pressed after the prefix key (for example, +by default +.Ql c is bound to -.Ar key -alone. +.Ic new-window +in the +.Em prefix +table, so +.Ql C-b c +creates a new window). +The +.Em root +table is used for keys pressed without the prefix key: binding +.Ql c +to +.Ic new-window +in the +.Em root +table (not recommended) means a plain +.Ql c +will create a new window. +.Fl n +is an alias +for +.Fl T Ar root . +Keys may also be bound in custom key tables and the +.Ic switch-client +.Fl T +command used to switch to them from a key binding. The .Fl r flag indicates this key may repeat, see the @@ -1962,22 +2004,33 @@ is bound in .Ar mode-table : the binding for command mode with .Fl c -or for normal mode without. +or for normal mode without. See the +.Sx WINDOWS AND PANES +section and the +.Ic list-keys +command for information on mode key bindings. +.Pp To view the default bindings and possible commands, see the .Ic list-keys command. -.It Ic list-keys Op Fl t Ar key-table +.It Xo Ic list-keys +.Op Fl t Ar mode-table +.Op Fl T Ar key-table +.Xc .D1 (alias: Ic lsk ) List all key bindings. Without -.Fl t -the primary key bindings - those executed when preceded by the prefix key - -are printed. +.Fl T +all key tables are printed. +With +.Fl T +only +.Ar key-table . .Pp With .Fl t , the key bindings in -.Ar key-table +.Ar mode-table are listed; this may be one of: .Em vi-edit , .Em emacs-edit , @@ -2022,31 +2075,22 @@ the secondary prefix key, to a window as if it was pressed. .It Xo Ic unbind-key .Op Fl acn .Op Fl t Ar mode-table +.Op Fl T Ar key-table .Ar key .Xc .D1 (alias: Ic unbind ) Unbind the command bound to .Ar key . -Without +.Fl c , +.Fl n , +.Fl T +and .Fl t -the primary key bindings are modified; in this case, if -.Fl n -is specified, the command bound to -.Ar key -without a prefix (if any) is removed. +are the same as for +.Ic bind-key . If .Fl a is present, all key bindings are removed. -.Pp -If -.Fl t -is present, -.Ar key -in -.Ar mode-table -is unbound: the binding for command mode with -.Fl c -or for normal mode without. .El .Sh OPTIONS The appearance and behaviour of diff --git a/tmux.h b/tmux.h index 477fd436..1af4d073 100644 --- a/tmux.h +++ b/tmux.h @@ -89,10 +89,9 @@ extern char **environ; #define KEYC_ESCAPE 0x2000 #define KEYC_CTRL 0x4000 #define KEYC_SHIFT 0x8000 -#define KEYC_PREFIX 0x10000 /* Mask to obtain key w/o modifiers. */ -#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX) +#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) /* Is this a mouse key? */ @@ -1301,7 +1300,7 @@ struct client { struct screen status; #define CLIENT_TERMINAL 0x1 -#define CLIENT_PREFIX 0x2 +/* 0x2 unused */ #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 @@ -1320,6 +1319,7 @@ struct client { #define CLIENT_256COLOURS 0x20000 #define CLIENT_IDENTIFIED 0x40000 int flags; + struct key_table *keytable; struct event identify_timer; @@ -1440,15 +1440,24 @@ struct cmd_entry { enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; -/* Key binding. */ +/* Key binding and key table. */ struct key_binding { - int key; - struct cmd_list *cmdlist; - int can_repeat; + int key; + struct cmd_list *cmdlist; + int can_repeat; - RB_ENTRY(key_binding) entry; + RB_ENTRY(key_binding) entry; }; RB_HEAD(key_bindings, key_binding); +struct key_table { + const char *name; + struct key_bindings key_bindings; + + u_int references; + + RB_ENTRY(key_table) entry; +}; +RB_HEAD(key_tables, key_table); /* * Option table entries. The option table is the user-visible part of the @@ -1876,12 +1885,16 @@ void cmd_wait_for_flush(void); int client_main(int, char **, int); /* key-bindings.c */ -extern struct key_bindings key_bindings; -int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); -struct key_binding *key_bindings_lookup(int); -void key_bindings_add(int, int, struct cmd_list *); -void key_bindings_remove(int); +RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); +extern struct key_tables key_tables; +int key_table_cmp(struct key_table *, struct key_table *); +int key_bindings_cmp(struct key_binding *, struct key_binding *); +struct key_table *key_bindings_get_table(const char *, int); +void key_bindings_unref_table(struct key_table *); +void key_bindings_add(const char *, int, int, struct cmd_list *); +void key_bindings_remove(const char *, int); +void key_bindings_remove_table(const char *); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *, struct mouse_event *); From 07d93db427751512e6ae96a54e69846189da2dc2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Apr 2015 15:41:32 +0000 Subject: [PATCH 084/703] Remove unused-but-set variables, from Thomas Adam. --- window-copy.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/window-copy.c b/window-copy.c index afa609ed..4dc07354 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2216,14 +2216,12 @@ window_copy_rectangle_toggle(struct window_pane *wp) void window_copy_start_drag(struct client *c, unused struct mouse_event *m) { - struct window_pane *wp; - struct window_copy_mode_data *data; - u_int x, y; + struct window_pane *wp; + u_int x, y; wp = cmd_mouse_pane(m, NULL, NULL); if (wp->mode != &window_copy_mode) return; - data = wp->modedata; if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) return; @@ -2260,13 +2258,11 @@ window_copy_drag_update(unused struct client *c, struct mouse_event *m) void window_copy_drag_release(unused struct client *c, struct mouse_event *m) { - struct window_pane *wp; - struct window_copy_mode_data *data; + struct window_pane *wp; wp = cmd_mouse_pane(m, NULL, NULL); if (wp->mode != &window_copy_mode) return; - data = wp->modedata; window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); From 0610f443808cc284171a4abc14bd3f95a9f3a57b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 15:16:06 +0000 Subject: [PATCH 085/703] cmd_mouse_pane can return NULL, check for that. --- window-copy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/window-copy.c b/window-copy.c index 4dc07354..eb00131e 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2220,7 +2220,7 @@ window_copy_start_drag(struct client *c, unused struct mouse_event *m) u_int x, y; wp = cmd_mouse_pane(m, NULL, NULL); - if (wp->mode != &window_copy_mode) + if (wp == NULL || wp->mode != &window_copy_mode) return; if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) @@ -2242,7 +2242,7 @@ window_copy_drag_update(unused struct client *c, struct mouse_event *m) u_int x, y, old_cy; wp = cmd_mouse_pane(m, NULL, NULL); - if (wp->mode != &window_copy_mode) + if (wp == NULL || wp->mode != &window_copy_mode) return; data = wp->modedata; @@ -2261,7 +2261,7 @@ window_copy_drag_release(unused struct client *c, struct mouse_event *m) struct window_pane *wp; wp = cmd_mouse_pane(m, NULL, NULL); - if (wp->mode != &window_copy_mode) + if (wp == NULL || wp->mode != &window_copy_mode) return; window_copy_copy_selection(wp, NULL); From bc3786ece940dc9c68f731734b45c129e0542ac7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 15:18:06 +0000 Subject: [PATCH 086/703] Pass mouse events through to commands for if-shell. --- cmd-if-shell.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 9659511e..9c577616 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -44,11 +44,14 @@ const struct cmd_entry cmd_if_shell_entry = { }; struct cmd_if_shell_data { - char *cmd_if; - char *cmd_else; - struct cmd_q *cmdq; - int bflag; - int started; + char *cmd_if; + char *cmd_else; + + struct cmd_q *cmdq; + struct mouse_event mouse; + + int bflag; + int started; }; enum cmd_retval @@ -95,21 +98,24 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) } return (CMD_RETURN_ERROR); } - cmdq_run(cmdq, cmdlist, NULL); + cmdq_run(cmdq, cmdlist, &cmdq->item->mouse); cmd_list_free(cmdlist); return (CMD_RETURN_NORMAL); } cdata = xmalloc(sizeof *cdata); + cdata->cmd_if = xstrdup(args->argv[1]); if (args->argc == 3) cdata->cmd_else = xstrdup(args->argv[2]); else cdata->cmd_else = NULL; + cdata->bflag = args_has(args, 'b'); cdata->started = 0; cdata->cmdq = cmdq; + memcpy(&cdata->mouse, &cmdq->item->mouse, sizeof cdata->mouse); cmdq->references++; job_run(shellcmd, s, cmd_if_shell_callback, cmd_if_shell_free, cdata); @@ -152,7 +158,7 @@ cmd_if_shell_callback(struct job *job) cmdq1->emptyfn = cmd_if_shell_done; cmdq1->data = cdata; - cmdq_run(cmdq1, cmdlist, NULL); + cmdq_run(cmdq1, cmdlist, &cdata->mouse); cmd_list_free(cmdlist); } From 1f404f6a23969516007c77eeec89876154c9a4a4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 15:18:38 +0000 Subject: [PATCH 087/703] Put mouse_any_flag back, don't know where it went to (still in man page). --- format.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/format.c b/format.c index eb4664d1..2be9834f 100644 --- a/format.c +++ b/format.c @@ -729,6 +729,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "wrap_flag", "%d", !!(wp->base.mode & MODE_WRAP)); + format_add(ft, "mouse_any_flag", "%d", + !!(wp->base.mode & (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON))); format_add(ft, "mouse_standard_flag", "%d", !!(wp->base.mode & MODE_MOUSE_STANDARD)); format_add(ft, "mouse_button_flag", "%d", From 4cf4302962558be4d5e7a87c57f084d1187fa330 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 15:21:41 +0000 Subject: [PATCH 088/703] Don't eat the mouse event that triggers a drag end because we may want to pass it on to application inside the pane. --- input-keys.c | 1 + server-client.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index ae00e4a9..d812a906 100644 --- a/input-keys.c +++ b/input-keys.c @@ -251,5 +251,6 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) buf[len++] = x + 33; buf[len++] = y + 33; } + log_debug("writing mouse %.*s", (int)len, buf); bufferevent_write(wp->event, buf, len); } diff --git a/server-client.c b/server-client.c index b4d1f744..c022a36f 100644 --- a/server-client.c +++ b/server-client.c @@ -391,7 +391,7 @@ server_client_check_mouse(struct client *c) c->tty.mouse_drag_release = NULL; c->tty.mouse_drag_flag = 0; - return (KEYC_NONE); + return (KEYC_MOUSE); /* not a key, but still may want to pass */ } /* Convert to a key binding. */ From d1337053b6119c4aab0c38b5fc082acf2fdfcbb1 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 15:34:32 +0000 Subject: [PATCH 089/703] Bind mouse dragging so that it is passed through to applications if they want it rather than entering copy mode. --- cmd-copy-mode.c | 5 ++++- key-bindings.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 19dca5ff..d729ada6 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -68,8 +68,11 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); window_copy_init_from_pane(wp); } - if (args_has(args, 'M')) + if (args_has(args, 'M')) { + if (wp->mode != NULL && wp->mode != &window_copy_mode) + return (CMD_RETURN_NORMAL); window_copy_start_drag(c, &cmdq->item->mouse); + } if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) window_copy_pageup(wp); diff --git a/key-bindings.c b/key-bindings.c index c34db710..670300a7 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -221,7 +221,7 @@ key_bindings_init(void) "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", - "bind -n MouseDrag1Pane copy-mode -M", + "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", }; u_int i; struct cmd_list *cmdlist; From 0e7219d437ce565a518b336a5db112eaeaeddc5c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 21:24:49 +0000 Subject: [PATCH 090/703] Fix moving windows to nonexistent indexes when renumber-windows is off. From Thomas Adam, reported by Daniel Levai and Theo Buehler. --- cmd-move-window.c | 12 +++++++++++- server-fn.c | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/cmd-move-window.c b/cmd-move-window.c index 3064cd6e..e765b625 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -51,7 +51,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) struct session *src, *dst, *s; struct winlink *wl; char *cause; - int idx, kflag, dflag; + int idx, kflag, dflag, sflag; if (args_has(args, 'r')) { s = cmd_find_session(cmdq, args_get(args, 't'), 0); @@ -71,6 +71,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); + sflag = args_has(self->args, 's'); if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(cmdq, "can't link window: %s", cause); @@ -79,6 +80,15 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) } if (self->entry == &cmd_move_window_entry) server_unlink_window(src, wl); + + /* + * Renumber the winlinks in the src session only, the destination + * session already has the correct winlink id to us, either + * automatically or specified by -s. + */ + if (!sflag && options_get_number(&src->options, "renumber-windows")) + session_renumber_windows(src); + recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/server-fn.c b/server-fn.c index a065dd76..83ea9474 100644 --- a/server-fn.c +++ b/server-fn.c @@ -351,7 +351,6 @@ server_unlink_window(struct session *s, struct winlink *wl) server_destroy_session_group(s); else server_redraw_session_group(s); - session_renumber_windows(s); } void From d16b640fe8583878d23436bd7515a8adade9faeb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 21:31:02 +0000 Subject: [PATCH 091/703] The free callback could end up being fired before the done callback (happens on Cygwin), so use a reference count instead of a single flag. SF bug 188 reported by "iceboy". --- cmd-if-shell.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 9c577616..cdd2135c 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -51,7 +51,7 @@ struct cmd_if_shell_data { struct mouse_event mouse; int bflag; - int started; + int references; }; enum cmd_retval @@ -113,11 +113,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) cdata->bflag = args_has(args, 'b'); - cdata->started = 0; cdata->cmdq = cmdq; memcpy(&cdata->mouse, &cmdq->item->mouse, sizeof cdata->mouse); cmdq->references++; + cdata->references = 1; job_run(shellcmd, s, cmd_if_shell_callback, cmd_if_shell_free, cdata); free(shellcmd); @@ -152,12 +152,11 @@ cmd_if_shell_callback(struct job *job) return; } - cdata->started = 1; - cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_if_shell_done; cmdq1->data = cdata; + cdata->references++; cmdq_run(cmdq1, cmdlist, &cdata->mouse); cmd_list_free(cmdlist); } @@ -170,12 +169,14 @@ cmd_if_shell_done(struct cmd_q *cmdq1) if (cmdq1->client_exit >= 0) cmdq->client_exit = cmdq1->client_exit; + cmdq_free(cmdq1); + + if (--cdata->references != 0) + return; if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); - cmdq_free(cmdq1); - free(cdata->cmd_else); free(cdata->cmd_if); free(cdata); @@ -187,7 +188,7 @@ cmd_if_shell_free(void *data) struct cmd_if_shell_data *cdata = data; struct cmd_q *cmdq = cdata->cmdq; - if (cdata->started) + if (--cdata->references != 0) return; if (!cmdq_free(cmdq) && !cdata->bflag) From 7a72eff4a4717e975ab7ef39baff5667b12e8a13 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 22:21:41 +0000 Subject: [PATCH 092/703] Simplify error messages when socket connect fails, suggested by "Karthik K". --- client.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 6d4b8a5a..d3ff05a6 100644 --- a/client.c +++ b/client.c @@ -265,8 +265,13 @@ client_main(int argc, char **argv, int flags) /* Initialize the client socket and start the server. */ fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { - fprintf(stderr, "failed to connect to server: %s\n", - strerror(errno)); + if (errno == ECONNREFUSED) { + fprintf(stderr, "no server running on %s\n", + socket_path); + } else { + fprintf(stderr, "error connecting to %s (%s)\n", + socket_path, strerror(errno)); + } return (1); } From 93b2871cab399388a16ace64d8dfb9a8e2143543 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 22:32:40 +0000 Subject: [PATCH 093/703] Do not die on USR1 if any of the socket parent directories are missing. Reported by Robin Powell. --- server.c | 22 ++++++++++++++-------- tmux.1 | 6 ++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/server.c b/server.c index 26bfddfe..c8101775 100644 --- a/server.c +++ b/server.c @@ -79,24 +79,22 @@ server_create_socket(void) size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); if (size >= sizeof sa.sun_path) { errno = ENAMETOOLONG; - fatal("socket failed"); + return (-1); } unlink(sa.sun_path); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("socket failed"); + return (-1); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) - fatal("bind failed"); + return (-1); umask(mask); if (listen(fd, 16) == -1) - fatal("listen failed"); + return (-1); setblocking(fd, 0); - server_update_socket(); - return (fd); } @@ -155,6 +153,9 @@ server_start(int lockfd, char *lockfile) setproctitle("server (%s)", socket_path); server_fd = server_create_socket(); + if (server_fd == -1) + fatal("couldn't create socket"); + server_update_socket(); server_client_create(pair[1]); unlink(lockfile); @@ -387,6 +388,7 @@ server_add_accept(int timeout) void server_signal_callback(int sig, unused short events, unused void *data) { + int fd; switch (sig) { case SIGTERM: server_shutdown = 1; @@ -397,8 +399,12 @@ server_signal_callback(int sig, unused short events, unused void *data) break; case SIGUSR1: event_del(&server_ev_accept); - close(server_fd); - server_fd = server_create_socket(); + fd = server_create_socket(); + if (fd != -1) { + close(server_fd); + server_fd = fd; + server_update_socket(); + } server_add_accept(0); break; } diff --git a/tmux.1 b/tmux.1 index ab762387..68e9b9de 100644 --- a/tmux.1 +++ b/tmux.1 @@ -163,7 +163,8 @@ If the socket is accidentally removed, the .Dv SIGUSR1 signal may be sent to the .Nm -server process to recreate it. +server process to recreate it (note that this will fail if any parent +directories are missing). .It Fl l Behave as a login shell. This flag currently has no effect and is for compatibility with other shells @@ -2004,7 +2005,8 @@ is bound in .Ar mode-table : the binding for command mode with .Fl c -or for normal mode without. See the +or for normal mode without. +See the .Sx WINDOWS AND PANES section and the .Ic list-keys From 69f292a90e2fb9d48a77a9753127da13554715de Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 22:38:49 +0000 Subject: [PATCH 094/703] Always format real layout even when zoomed. --- format.c | 5 ++++- layout-custom.c | 4 ++-- tmux.h | 2 +- window.c | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index 2be9834f..7ded05d2 100644 --- a/format.c +++ b/format.c @@ -577,7 +577,10 @@ format_defaults_window(struct format_tree *ft, struct window *w) ft->w = w; - layout = layout_dump(w); + if (w->saved_layout_root != NULL) + layout = layout_dump(w->saved_layout_root); + else + layout = layout_dump(w->layout_root); format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); diff --git a/layout-custom.c b/layout-custom.c index c9cf49c2..57503518 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -55,12 +55,12 @@ layout_checksum(const char *layout) /* Dump layout as a string. */ char * -layout_dump(struct window *w) +layout_dump(struct layout_cell *root) { char layout[BUFSIZ], *out; *layout = '\0'; - if (layout_append(w->layout_root, layout, sizeof layout) != 0) + if (layout_append(root, layout, sizeof layout) != 0) return (NULL); xasprintf(&out, "%04x,%s", layout_checksum(layout), layout); diff --git a/tmux.h b/tmux.h index 1af4d073..53fd9b28 100644 --- a/tmux.h +++ b/tmux.h @@ -2225,7 +2225,7 @@ struct layout_cell *layout_split_pane( void layout_close_pane(struct window_pane *); /* layout-custom.c */ -char *layout_dump(struct window *); +char *layout_dump(struct layout_cell *); int layout_parse(struct window *, const char *); /* layout-set.c */ diff --git a/window.c b/window.c index 86cc8bb0..3c8d4955 100644 --- a/window.c +++ b/window.c @@ -519,6 +519,7 @@ window_unzoom(struct window *w) w->flags &= ~WINDOW_ZOOMED; layout_free(w); w->layout_root = w->saved_layout_root; + w->saved_layout_root = NULL; TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; From 3909aff06aa6de748ae057cab1e723eec2387edd Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 21 Apr 2015 22:42:27 +0000 Subject: [PATCH 095/703] Look up indexes as number before name, makes more sense if windows are named starting with numbers. From Thomas Adam. --- cmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd.c b/cmd.c index dd1a0a7b..991e079b 100644 --- a/cmd.c +++ b/cmd.c @@ -781,15 +781,15 @@ cmd_lookup_index(struct session *s, const char *name, int *ambiguous) const char *errstr; u_int idx; + idx = strtonum(name, 0, INT_MAX, &errstr); + if (errstr == NULL) + return (idx); + if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL) return (wl->idx); if (*ambiguous) return (-1); - idx = strtonum(name, 0, INT_MAX, &errstr); - if (errstr == NULL) - return (idx); - return (-1); } From 89e80cabd56bf2f7fa783575fe9b1f6192fade42 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2015 15:05:03 +0000 Subject: [PATCH 096/703] window_index is only used in one place (window_destroy) so inline it there. --- server.c | 1 + tmux.h | 1 - window.c | 16 +++++----------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/server.c b/server.c index c8101775..bb96d9af 100644 --- a/server.c +++ b/server.c @@ -389,6 +389,7 @@ void server_signal_callback(int sig, unused short events, unused void *data) { int fd; + switch (sig) { case SIGTERM: server_shutdown = 1; diff --git a/tmux.h b/tmux.h index 53fd9b28..52dc1529 100644 --- a/tmux.h +++ b/tmux.h @@ -2141,7 +2141,6 @@ struct winlink *winlink_previous_by_number(struct winlink *, struct session *, int); void winlink_stack_push(struct winlink_stack *, struct winlink *); void winlink_stack_remove(struct winlink_stack *, struct winlink *); -int window_index(struct window *, u_int *); struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); struct window *window_create(const char *, int, char **, const char *, diff --git a/window.c b/window.c index 3c8d4955..d8f29026 100644 --- a/window.c +++ b/window.c @@ -245,16 +245,6 @@ winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) } } -int -window_index(struct window *s, u_int *i) -{ - for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) { - if (s == ARRAY_ITEM(&windows, *i)) - return (0); - } - return (-1); -} - struct window * window_find_by_id(u_int id) { @@ -341,7 +331,11 @@ window_destroy(struct window *w) window_unzoom(w); - if (window_index(w, &i) != 0) + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + if (w == ARRAY_ITEM(&windows, i)) + break; + } + if (i == ARRAY_LENGTH(&windows)) fatalx("index not found"); ARRAY_SET(&windows, i, NULL); while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) From 8d66f4fba4972d45be64d108c7c8d952f85016a8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2015 15:30:11 +0000 Subject: [PATCH 097/703] Change the windows array into an RB tree and fix some places where we were only looking at the first winlink for a window in a session. --- cmd-set-option.c | 4 +-- resize.c | 11 ++++---- server-client.c | 6 +---- server-window.c | 24 +++++++---------- server.c | 21 ++++----------- tmux.h | 6 ++++- window.c | 68 ++++++++++++++++-------------------------------- 7 files changed, 50 insertions(+), 90 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 01d691d5..e236b95d 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -176,9 +176,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Start or stop timers when automatic-rename changed. */ if (strcmp(oe->name, "automatic-rename") == 0) { - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if ((w = ARRAY_ITEM(&windows, i)) == NULL) - continue; + RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) queue_window_name(w); else if (event_initialized(&w->name_timer)) diff --git a/resize.c b/resize.c index 9ad73c81..5b89b093 100644 --- a/resize.c +++ b/resize.c @@ -49,7 +49,7 @@ recalculate_sizes(void) struct client *c; struct window *w; struct window_pane *wp; - u_int i, j, ssx, ssy, has, limit; + u_int i, ssx, ssy, has, limit; int flag, has_status, is_zoomed, forced; RB_FOREACH(s, sessions, &sessions) { @@ -57,8 +57,8 @@ recalculate_sizes(void) s->attached = 0; ssx = ssy = UINT_MAX; - for (j = 0; j < ARRAY_LENGTH(&clients); j++) { - c = ARRAY_ITEM(&clients, j); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); if (c == NULL || c->flags & CLIENT_SUSPENDED) continue; if (c->session == s) { @@ -92,9 +92,8 @@ recalculate_sizes(void) s->sy = ssy; } - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL || w->active == NULL) + RB_FOREACH(w, windows, &windows) { + if (w->active == NULL) continue; flag = options_get_number(&w->options, "aggressive-resize"); diff --git a/server-client.c b/server-client.c index c022a36f..e2f65e71 100644 --- a/server-client.c +++ b/server-client.c @@ -720,11 +720,7 @@ server_client_loop(void) * Any windows will have been redrawn as part of clients, so clear * their flags now. Also check pane focus and resize. */ - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL) - continue; - + RB_FOREACH(w, windows, &windows) { w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { diff --git a/server-window.c b/server-window.c index a2355701..f157da35 100644 --- a/server-window.c +++ b/server-window.c @@ -34,24 +34,20 @@ void server_window_loop(void) { struct window *w; - struct winlink *wl; struct session *s; - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL) - continue; + struct winlink *wl; + RB_FOREACH(w, windows, &windows) { RB_FOREACH(s, sessions, &sessions) { - wl = session_has(s, w); - if (wl == NULL) - continue; + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window != w) + continue; - if (server_window_check_bell(s, wl) || - server_window_check_activity(s, wl) || - server_window_check_silence(s, wl)) - server_status_session(s); + if (server_window_check_bell(s, wl) || + server_window_check_activity(s, wl) || + server_window_check_silence(s, wl)) + server_status_session(s); + } } } } diff --git a/server.c b/server.c index bb96d9af..1183d64d 100644 --- a/server.c +++ b/server.c @@ -137,7 +137,7 @@ server_start(int lockfd, char *lockfile) logfile("server"); log_debug("server started, pid %ld", (long) getpid()); - ARRAY_INIT(&windows); + RB_INIT(&windows); RB_INIT(&all_window_panes); ARRAY_INIT(&clients); ARRAY_INIT(&dead_clients); @@ -438,14 +438,11 @@ server_child_signal(void) void server_child_exited(pid_t pid, int status) { - struct window *w; + struct window *w, *w1; struct window_pane *wp; struct job *job; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if ((w = ARRAY_ITEM(&windows, i)) == NULL) - continue; + RB_FOREACH_SAFE(w, windows, &windows, w1) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { wp->status = status; @@ -469,14 +466,11 @@ server_child_stopped(pid_t pid, int status) { struct window *w; struct window_pane *wp; - u_int i; if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) return; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if ((w = ARRAY_ITEM(&windows, i)) == NULL) - continue; + RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { if (killpg(pid, SIGCONT) != 0) @@ -493,18 +487,13 @@ server_second_callback(unused int fd, unused short events, unused void *arg) struct window *w; struct window_pane *wp; struct timeval tv; - u_int i; if (options_get_number(&global_s_options, "lock-server")) server_lock_server(); else server_lock_sessions(); - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL) - continue; - + RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->mode != NULL && wp->mode->timer != NULL) wp->mode->timer(wp); diff --git a/tmux.h b/tmux.h index 52dc1529..e52b14f1 100644 --- a/tmux.h +++ b/tmux.h @@ -983,8 +983,10 @@ struct window { struct options options; u_int references; + + RB_ENTRY(window) entry; }; -ARRAY_DECL(windows, struct window *); +RB_HEAD(windows, window); /* Entry on local window list. */ struct winlink { @@ -2121,6 +2123,8 @@ void screen_reflow(struct screen *, u_int); /* window.c */ extern struct windows windows; extern struct window_pane_tree all_window_panes; +int window_cmp(struct window *, struct window *); +RB_PROTOTYPE(windows, window, entry, window_cmp); int winlink_cmp(struct winlink *, struct winlink *); RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp); int window_pane_cmp(struct window_pane *, struct window_pane *); diff --git a/window.c b/window.c index d8f29026..5ca8aa0c 100644 --- a/window.c +++ b/window.c @@ -64,6 +64,14 @@ void window_pane_error_callback(struct bufferevent *, short, void *); struct window_pane *window_pane_choose_best(struct window_pane_list *); +RB_GENERATE(windows, window, entry, window_cmp); + +int +window_cmp(struct window *w1, struct window *w2) +{ + return (w1->id - w2->id); +} + RB_GENERATE(winlinks, winlink, entry, winlink_cmp); int @@ -248,25 +256,18 @@ winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) struct window * window_find_by_id(u_int id) { - struct window *w; - u_int i; + struct window w; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w != NULL && w->id == id) - return (w); - } - return (NULL); + w.id = id; + return (RB_FIND(windows, &windows, &w)); } struct window * window_create1(u_int sx, u_int sy) { struct window *w; - u_int i; w = xcalloc(1, sizeof *w); - w->id = next_window_id++; w->name = NULL; w->flags = 0; @@ -283,16 +284,11 @@ window_create1(u_int sx, u_int sy) if (options_get_number(&w->options, "automatic-rename")) queue_window_name(w); - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if (ARRAY_ITEM(&windows, i) == NULL) { - ARRAY_SET(&windows, i, w); - break; - } - } - if (i == ARRAY_LENGTH(&windows)) - ARRAY_ADD(&windows, w); w->references = 0; + w->id = next_window_id++; + RB_INSERT (windows, &windows, w); + return (w); } @@ -327,19 +323,9 @@ window_create(const char *name, int argc, char **argv, const char *path, void window_destroy(struct window *w) { - u_int i; - window_unzoom(w); - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if (w == ARRAY_ITEM(&windows, i)) - break; - } - if (i == ARRAY_LENGTH(&windows)) - fatalx("index not found"); - ARRAY_SET(&windows, i, NULL); - while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) - ARRAY_TRUNC(&windows, 1); + RB_REMOVE(windows, &windows, w); if (w->layout_root != NULL) layout_free(w); @@ -1328,26 +1314,18 @@ window_pane_find_right(struct window_pane *wp) void winlink_clear_flags(struct winlink *wl) { - struct winlink *wm; struct session *s; - struct window *w; - u_int i; + struct winlink *wl_loop; - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - if ((w = ARRAY_ITEM(&windows, i)) == NULL) - continue; - - RB_FOREACH(s, sessions, &sessions) { - if ((wm = session_has(s, w)) == NULL) + RB_FOREACH(s, sessions, &sessions) { + RB_FOREACH(wl_loop, winlinks, &s->windows) { + if (wl_loop->window != wl->window) + continue; + if ((wl_loop->flags & WINLINK_ALERTFLAGS) == 0) continue; - if (wm->window != wl->window) - continue; - if ((wm->flags & WINLINK_ALERTFLAGS) == 0) - continue; - - wm->flags &= ~WINLINK_ALERTFLAGS; - wm->window->flags &= ~WINDOW_ALERTFLAGS; + wl_loop->flags &= ~WINLINK_ALERTFLAGS; + wl_loop->window->flags &= ~WINDOW_ALERTFLAGS; server_status_session(s); } } From 9a453dd3546b2c3053e8e34bf4d6775b1a05d3a8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 22 Apr 2015 15:32:33 +0000 Subject: [PATCH 098/703] Make session_has return a flag, returning the first winlink found is a recipe for errors. --- resize.c | 2 +- server-fn.c | 4 ++-- session.c | 6 +++--- tmux.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/resize.c b/resize.c index 5b89b093..330c37b5 100644 --- a/resize.c +++ b/resize.c @@ -104,7 +104,7 @@ recalculate_sizes(void) if (flag) has = s->curw->window == w; else - has = session_has(s, w) != NULL; + has = session_has(s, w); if (has) { if (s->sx < ssx) ssx = s->sx; diff --git a/server-fn.c b/server-fn.c index 83ea9474..c5487aa6 100644 --- a/server-fn.c +++ b/server-fn.c @@ -199,7 +199,7 @@ server_status_window(struct window *w) */ RB_FOREACH(s, sessions, &sessions) { - if (session_has(s, w) != NULL) + if (session_has(s, w)) server_status_session(s); } } @@ -268,7 +268,7 @@ server_kill_window(struct window *w) s = next_s; next_s = RB_NEXT(sessions, &sessions, s); - if (session_has(s, w) == NULL) + if (!session_has(s, w)) continue; server_unzoom_window(w); while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { diff --git a/session.c b/session.c index 03ddb10d..c0535725 100644 --- a/session.c +++ b/session.c @@ -309,16 +309,16 @@ session_detach(struct session *s, struct winlink *wl) } /* Return if session has window. */ -struct winlink * +int session_has(struct session *s, struct window *w) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window == w) - return (wl); + return (1); } - return (NULL); + return (0); } struct winlink * diff --git a/tmux.h b/tmux.h index e52b14f1..a47218e5 100644 --- a/tmux.h +++ b/tmux.h @@ -2321,7 +2321,7 @@ struct winlink *session_new(struct session *, const char *, int, char **, struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); -struct winlink *session_has(struct session *, struct window *); +int session_has(struct session *, struct window *); int session_next(struct session *, int); int session_previous(struct session *, int); int session_select(struct session *, int); From ab73997cc5bd652370beef93db5fca439448734a Mon Sep 17 00:00:00 2001 From: deraadt Date: Thu, 23 Apr 2015 07:45:50 +0000 Subject: [PATCH 099/703] use reallocarray instead of calloc; avoid the zero before infill ok nicm --- grid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid.c b/grid.c index ef7c374b..2dc266d7 100644 --- a/grid.c +++ b/grid.c @@ -657,7 +657,7 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, memcpy(dstl, srcl, sizeof *dstl); if (srcl->cellsize != 0) { - dstl->celldata = xcalloc( + dstl->celldata = xreallocarray(NULL, srcl->cellsize, sizeof *dstl->celldata); memcpy(dstl->celldata, srcl->celldata, srcl->cellsize * sizeof *dstl->celldata); From a5a873dcccaabd55dbb0ad32abafbf1049316392 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 24 Apr 2015 20:58:44 +0000 Subject: [PATCH 100/703] Set up signal handler earlier so that we don't get zombies, reported by sobrado@. --- client.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client.c b/client.c index d3ff05a6..e2ffa546 100644 --- a/client.c +++ b/client.c @@ -262,6 +262,9 @@ client_main(int argc, char **argv, int flags) setproctitle("client (%s)", socket_path); logfile("client"); + /* Establish signal handlers. */ + set_signals(client_signal); + /* Initialize the client socket and start the server. */ fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { @@ -301,9 +304,6 @@ client_main(int argc, char **argv, int flags) tcsetattr(STDIN_FILENO, TCSANOW, &tio); } - /* Establish signal handlers. */ - set_signals(client_signal); - /* Send identify messages. */ client_send_identify(flags); From 5a2d0533a84a13613fb356b66ed8ada2a65f6edf Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 24 Apr 2015 21:38:18 +0000 Subject: [PATCH 101/703] Allow choice options (multiple states) to be toggled between states 0 and 1. --- cmd-set-option.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index e236b95d..e0d1ae6c 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -289,9 +289,15 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, { struct options_entry *o; - if (oe->type != OPTIONS_TABLE_FLAG && value == NULL) { - cmdq_error(cmdq, "empty value"); - return (-1); + switch (oe->type) { + case OPTIONS_TABLE_FLAG: + case OPTIONS_TABLE_CHOICE: + break; + default: + if (value == NULL) { + cmdq_error(cmdq, "empty value"); + return (-1); + } } o = NULL; @@ -455,21 +461,27 @@ cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq, const char **choicep; int n, choice = -1; - n = 0; - for (choicep = oe->choices; *choicep != NULL; choicep++) { - n++; - if (strncmp(*choicep, value, strlen(value)) != 0) - continue; + if (value == NULL) { + choice = options_get_number(oo, oe->name); + if (choice < 2) + choice = !choice; + } else { + n = 0; + for (choicep = oe->choices; *choicep != NULL; choicep++) { + n++; + if (strncmp(*choicep, value, strlen(value)) != 0) + continue; - if (choice != -1) { - cmdq_error(cmdq, "ambiguous value: %s", value); + if (choice != -1) { + cmdq_error(cmdq, "ambiguous value: %s", value); + return (NULL); + } + choice = n - 1; + } + if (choice == -1) { + cmdq_error(cmdq, "unknown value: %s", value); return (NULL); } - choice = n - 1; - } - if (choice == -1) { - cmdq_error(cmdq, "unknown value: %s", value); - return (NULL); } return (options_set_number(oo, oe->name, choice)); From 583b4ab72b7bf66fda8ab63a08fe435483de5e5a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 24 Apr 2015 22:19:36 +0000 Subject: [PATCH 102/703] Set working directory for run-shell and if-shell. --- cmd-if-shell.c | 15 ++++++++++++--- cmd-run-shell.c | 15 ++++++++++++--- job.c | 5 ++++- status.c | 2 +- tmux.h | 2 +- window-copy.c | 2 +- 6 files changed, 31 insertions(+), 10 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index cdd2135c..a307bd2f 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -66,16 +66,24 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window_pane *wp = NULL; struct format_tree *ft; + int cwd; - if (args_has(args, 't')) + if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - else { + cwd = wp->cwd; + } else { c = cmd_find_client(cmdq, NULL, 1); if (c != NULL && c->session != NULL) { s = c->session; wl = s->curw; wp = wl->window->active; } + if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else if (s != NULL) + cwd = s->cwd; + else + cwd = -1; } ft = format_create(); @@ -118,7 +126,8 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) cmdq->references++; cdata->references = 1; - job_run(shellcmd, s, cmd_if_shell_callback, cmd_if_shell_free, cdata); + job_run(shellcmd, s, cwd, cmd_if_shell_callback, cmd_if_shell_free, + cdata); free(shellcmd); if (cdata->bflag) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 5d6d178b..134cbeba 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -80,16 +80,24 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window_pane *wp = NULL; struct format_tree *ft; + int cwd; - if (args_has(args, 't')) + if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - else { + cwd = wp->cwd; + } else { c = cmd_find_client(cmdq, NULL, 1); if (c != NULL && c->session != NULL) { s = c->session; wl = s->curw; wp = wl->window->active; } + if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else if (s != NULL) + cwd = s->cwd; + else + cwd = -1; } ft = format_create(); @@ -105,7 +113,8 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) cdata->cmdq = cmdq; cmdq->references++; - job_run(shellcmd, s, cmd_run_shell_callback, cmd_run_shell_free, cdata); + job_run(shellcmd, s, cwd, cmd_run_shell_callback, cmd_run_shell_free, + cdata); if (cdata->bflag) return (CMD_RETURN_NORMAL); diff --git a/job.c b/job.c index c04a70b0..5675eec3 100644 --- a/job.c +++ b/job.c @@ -41,7 +41,7 @@ struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running, if it isn't already. */ struct job * -job_run(const char *cmd, struct session *s, +job_run(const char *cmd, struct session *s, int cwd, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; @@ -67,6 +67,9 @@ job_run(const char *cmd, struct session *s, case 0: /* child */ clear_signals(1); + if (cwd != -1 && fchdir(cwd) != 0) + chdir("/"); + environ_push(&env); environ_free(&env); diff --git a/status.c b/status.c index ae5d99ab..fd0292c7 100644 --- a/status.c +++ b/status.c @@ -515,7 +515,7 @@ status_find_job(struct client *c, char **iptr) /* If not found at all, start the job and add to the tree. */ if (so == NULL) { - job_run(cmd, NULL, status_job_callback, status_job_free, c); + job_run(cmd, NULL, -1, status_job_callback, status_job_free, c); c->references++; so = xmalloc(sizeof *so); diff --git a/tmux.h b/tmux.h index a47218e5..9a6e4c83 100644 --- a/tmux.h +++ b/tmux.h @@ -1613,7 +1613,7 @@ int options_table_find(const char *, const struct options_table_entry **, /* job.c */ extern struct joblist all_jobs; -struct job *job_run(const char *, struct session *, +struct job *job_run(const char *, struct session *, int, void (*)(struct job *), void (*)(void *), void *); void job_free(struct job *); void job_died(struct job *, int); diff --git a/window-copy.c b/window-copy.c index eb00131e..7d5bae4c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1465,7 +1465,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); - job = job_run(expanded, sess, NULL, NULL, NULL); + job = job_run(expanded, sess, -1, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); free(expanded); From aeedb464a6ee038289ddcfefae437928ab020cb1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 24 Apr 2015 23:17:11 +0000 Subject: [PATCH 103/703] Convert clients list into a TAILQ. --- cmd-attach-session.c | 8 ++--- cmd-choose-client.c | 24 ++++++++------- cmd-detach-client.c | 13 +++----- cmd-list-clients.c | 15 +++++----- cmd-set-option.c | 6 ++-- cmd.c | 26 ++++++++-------- control-notify.c | 28 +++++------------ notify.c | 6 ++-- resize.c | 7 ++--- server-client.c | 45 +++++----------------------- server-fn.c | 58 ++++++++++-------------------------- server-window.c | 24 +++++---------- server.c | 71 +++++++++++++++++--------------------------- tmux.h | 4 ++- tty.c | 6 ++-- 15 files changed, 118 insertions(+), 223 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a67ec82c..79e14616 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -51,7 +51,6 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, struct window_pane *wp = NULL; const char *update; char *cause; - u_int i; int fd; struct format_tree *ft; char *cp; @@ -92,11 +91,8 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, * Can't use server_write_session in case attaching to * the same session as currently attached to. */ - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) - continue; - if (c == cmdq->client) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s || c == cmdq->client) continue; server_write_client(c, MSG_DETACH, c->session->name, diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 3002f7ba..49fe2a34 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -59,7 +59,7 @@ cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl; const char *template; char *action; - u_int i, idx, cur; + u_int idx, cur; if ((c = cmd_current_client(cmdq)) == NULL) { cmdq_error(cmdq, "no client available"); @@ -81,24 +81,24 @@ cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) action = xstrdup("detach-client -t '%%'"); cur = idx = 0; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c1 = ARRAY_ITEM(&clients, i); - if (c1 == NULL || c1->session == NULL || c1->tty.path == NULL) + TAILQ_FOREACH(c1, &clients, entry) { + if (c1->session == NULL || c1->tty.path == NULL) continue; if (c1 == cmdq->client) cur = idx; - idx++; cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = i; + cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_add(cdata->ft, "line", "%u", i); + format_add(cdata->ft, "line", "%u", idx); format_defaults(cdata->ft, c1, NULL, NULL, NULL); cdata->command = cmd_template_replace(action, c1->tty.path, 1); window_choose_add(wl->window->active, cdata); + + idx++; } free(action); @@ -112,15 +112,19 @@ void cmd_choose_client_callback(struct window_choose_data *cdata) { struct client *c; + u_int idx; if (cdata == NULL) return; if (cdata->start_client->flags & CLIENT_DEAD) return; - if (cdata->idx > ARRAY_LENGTH(&clients) - 1) - return; - c = ARRAY_ITEM(&clients, cdata->idx); + idx = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (idx == cdata->idx) + break; + idx++; + } if (c == NULL || c->session == NULL) return; diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 7f87d2c6..4bae9997 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -51,7 +51,6 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c, *cloop; struct session *s; enum msgtype msgtype; - u_int i; if (self->entry == &cmd_suspend_client_entry) { if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) @@ -72,9 +71,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) if (s == NULL) return (CMD_RETURN_ERROR); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - cloop = ARRAY_ITEM(&clients, i); - if (cloop == NULL || cloop->session != s) + TAILQ_FOREACH(cloop, &clients, entry) { + if (cloop->session != s) continue; server_write_client(cloop, msgtype, cloop->session->name, @@ -88,11 +86,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); if (args_has(args, 'a')) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - cloop = ARRAY_ITEM(&clients, i); - if (cloop == NULL || cloop->session == NULL) - continue; - if (cloop == c) + TAILQ_FOREACH(cloop, &clients, entry) { + if (cloop->session == NULL || cloop == c) continue; server_write_client(cloop, msgtype, cloop->session->name, diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 893a6d05..372b5283 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -51,7 +51,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) struct session *s; struct format_tree *ft; const char *template; - u_int i; + u_int idx; char *line; if (args_has(args, 't')) { @@ -64,16 +64,13 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = LIST_CLIENTS_TEMPLATE; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - - if (s != NULL && s != c->session) + idx = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || (s != NULL && s != c->session)) continue; ft = format_create(); - format_add(ft, "line", "%u", i); + format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); line = format_expand(ft, template); @@ -81,6 +78,8 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) free(line); format_free(ft); + + idx++; } return (CMD_RETURN_NORMAL); diff --git a/cmd-set-option.c b/cmd-set-option.c index e0d1ae6c..83d31b93 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -91,7 +91,6 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) struct options *oo; struct window *w; const char *optstr, *valstr; - u_int i; /* Get the option name and value. */ optstr = args->argv[0]; @@ -186,9 +185,8 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session != NULL) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL) server_redraw_client(c); } diff --git a/cmd.c b/cmd.c index 991e079b..b81ffb36 100644 --- a/cmd.c +++ b/cmd.c @@ -116,10 +116,12 @@ const struct cmd_entry *cmd_table[] = { NULL }; +ARRAY_DECL(client_list, struct client *); + int cmd_session_better(struct session *, struct session *, int); struct session *cmd_choose_session_list(struct sessionslist *); struct session *cmd_choose_session(int); -struct client *cmd_choose_client(struct clients *); +struct client *cmd_choose_client(struct client_list *); struct client *cmd_lookup_client(const char *); struct session *cmd_lookup_session(struct cmd_q *, const char *, int *); struct session *cmd_lookup_session_id(const char *); @@ -452,8 +454,7 @@ cmd_current_client(struct cmd_q *cmdq) { struct session *s; struct client *c; - struct clients cc; - u_int i; + struct client_list cc; if (cmdq->client != NULL && cmdq->client->session != NULL) return (cmdq->client); @@ -465,9 +466,7 @@ cmd_current_client(struct cmd_q *cmdq) s = cmd_current_session(cmdq, 0); if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { ARRAY_INIT(&cc); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if ((c = ARRAY_ITEM(&clients, i)) == NULL) - continue; + TAILQ_FOREACH(c, &clients, entry) { if (s == c->session) ARRAY_ADD(&cc, c); } @@ -478,12 +477,17 @@ cmd_current_client(struct cmd_q *cmdq) return (c); } - return (cmd_choose_client(&clients)); + ARRAY_INIT(&cc); + TAILQ_FOREACH(c, &clients, entry) + ARRAY_ADD(&cc, c); + c = cmd_choose_client(&cc); + ARRAY_FREE(&cc); + return (c); } /* Choose the most recently used client from a list. */ struct client * -cmd_choose_client(struct clients *cc) +cmd_choose_client(struct client_list *cc) { struct client *c, *cbest; struct timeval *tv = NULL; @@ -615,11 +619,9 @@ cmd_lookup_client(const char *name) { struct client *c; const char *path; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL || c->tty.path == NULL) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || c->tty.path == NULL) continue; path = c->tty.path; diff --git a/control-notify.c b/control-notify.c index 747ef5b4..943d670c 100644 --- a/control-notify.c +++ b/control-notify.c @@ -64,11 +64,9 @@ control_notify_window_layout_changed(struct window *w) struct session *s; struct format_tree *ft; struct winlink *wl; - u_int i; const char *template; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; s = c->session; @@ -100,10 +98,8 @@ control_notify_window_unlinked(unused struct session *s, struct window *w) { struct client *c; struct session *cs; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; @@ -120,10 +116,8 @@ control_notify_window_linked(unused struct session *s, struct window *w) { struct client *c; struct session *cs; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; @@ -140,10 +134,8 @@ control_notify_window_renamed(struct window *w) { struct client *c; struct session *cs; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; @@ -174,10 +166,8 @@ void control_notify_session_renamed(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; @@ -189,10 +179,8 @@ void control_notify_session_created(unused struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; @@ -204,10 +192,8 @@ void control_notify_session_close(unused struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); + TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; diff --git a/notify.c b/notify.c index 75a16de9..19bf17e8 100644 --- a/notify.c +++ b/notify.c @@ -136,7 +136,6 @@ void notify_input(struct window_pane *wp, struct evbuffer *input) { struct client *c; - u_int i; /* * notify_input() is not queued and only does anything when @@ -145,9 +144,8 @@ notify_input(struct window_pane *wp, struct evbuffer *input) if (!notify_enabled) return; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && (c->flags & CLIENT_CONTROL)) + TAILQ_FOREACH(c, &clients, entry) { + if (c->flags & CLIENT_CONTROL) control_notify_input(c, wp, input); } } diff --git a/resize.c b/resize.c index 330c37b5..3606bfeb 100644 --- a/resize.c +++ b/resize.c @@ -49,7 +49,7 @@ recalculate_sizes(void) struct client *c; struct window *w; struct window_pane *wp; - u_int i, ssx, ssy, has, limit; + u_int ssx, ssy, has, limit; int flag, has_status, is_zoomed, forced; RB_FOREACH(s, sessions, &sessions) { @@ -57,9 +57,8 @@ recalculate_sizes(void) s->attached = 0; ssx = ssy = UINT_MAX; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->flags & CLIENT_SUSPENDED) + TAILQ_FOREACH(c, &clients, entry) { + if (c->flags & CLIENT_SUSPENDED) continue; if (c->session == s) { if (c->tty.sx < ssx) diff --git a/server-client.c b/server-client.c index e2f65e71..a0f81b73 100644 --- a/server-client.c +++ b/server-client.c @@ -60,7 +60,6 @@ void server_client_create(int fd) { struct client *c; - u_int i; setblocking(fd, 0); @@ -108,13 +107,7 @@ server_client_create(int fd) evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) == NULL) { - ARRAY_SET(&clients, i, c); - return; - } - } - ARRAY_ADD(&clients, c); + TAILQ_INSERT_TAIL(&clients, c, entry); log_debug("new client %d", fd); } @@ -148,10 +141,7 @@ server_client_lost(struct client *c) struct message_entry *msg; u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) == c) - ARRAY_SET(&clients, i, NULL); - } + TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %d", c->ibuf.fd); /* @@ -205,14 +195,7 @@ server_client_lost(struct client *c) if (event_initialized(&c->event)) event_del(&c->event); - for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { - if (ARRAY_ITEM(&dead_clients, i) == NULL) { - ARRAY_SET(&dead_clients, i, c); - break; - } - } - if (i == ARRAY_LENGTH(&dead_clients)) - ARRAY_ADD(&dead_clients, c); + TAILQ_INSERT_TAIL(&dead_clients, c, entry); c->flags |= CLIENT_DEAD; server_add_accept(0); /* may be more file descriptors now */ @@ -263,16 +246,14 @@ server_client_status_timer(void) struct client *c; struct session *s; struct timeval tv; - u_int i; int interval; time_t difference; if (gettimeofday(&tv, NULL) != 0) fatal("gettimeofday failed"); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL) continue; if (c->message_string != NULL || c->prompt_string != NULL) { /* @@ -702,13 +683,8 @@ server_client_loop(void) struct client *c; struct window *w; struct window_pane *wp; - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL) - continue; + TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); if (c->session != NULL) { server_client_check_redraw(c); @@ -755,7 +731,6 @@ server_client_check_resize(struct window_pane *wp) void server_client_check_focus(struct window_pane *wp) { - u_int i; struct client *c; int push; @@ -783,12 +758,8 @@ server_client_check_focus(struct window_pane *wp) * If our window is the current window in any focused clients with an * attached session, we're focused. */ - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - - if (!(c->flags & CLIENT_FOCUSED)) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || !(c->flags & CLIENT_FOCUSED)) continue; if (c->session->flags & SESSION_UNATTACHED) continue; diff --git a/server-fn.c b/server-fn.c index c5487aa6..85067a87 100644 --- a/server-fn.c +++ b/server-fn.c @@ -77,12 +77,8 @@ server_write_session(struct session *s, enum msgtype type, const void *buf, size_t len) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; + TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_write_client(c, type, buf, len); } @@ -104,12 +100,8 @@ void server_redraw_session(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; + TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_redraw_client(c); } @@ -132,12 +124,8 @@ void server_status_session(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; + TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_status_client(c); } @@ -160,13 +148,9 @@ void server_redraw_window(struct window *w) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - if (c->session->curw->window == w) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && c->session->curw->window == w) server_redraw_client(c); } w->flags |= WINDOW_REDRAW; @@ -176,13 +160,9 @@ void server_redraw_window_borders(struct window *w) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - if (c->session->curw->window == w) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL && c->session->curw->window == w) c->flags |= CLIENT_BORDERS; } } @@ -208,13 +188,10 @@ void server_lock(void) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - server_lock_client(c); + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL) + server_lock_client(c); } } @@ -222,13 +199,10 @@ void server_lock_session(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL || c->session != s) - continue; - server_lock_client(c); + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == s) + server_lock_client(c); } } @@ -430,16 +404,14 @@ server_destroy_session(struct session *s) { struct client *c; struct session *s_new; - u_int i; if (!options_get_number(&s->options, "detach-on-destroy")) s_new = server_next_session(s); else s_new = NULL; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s) continue; if (s_new == NULL) { c->session = NULL; diff --git a/server-window.c b/server-window.c index f157da35..4385dd90 100644 --- a/server-window.c +++ b/server-window.c @@ -58,7 +58,6 @@ server_window_check_bell(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; - u_int i; int action, visual; if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) @@ -74,9 +73,8 @@ server_window_check_bell(struct session *s, struct winlink *wl) action = options_get_number(&s->options, "bell-action"); if (action == BELL_NONE) return (0); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s || c->flags & CLIENT_CONTROL) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s || c->flags & CLIENT_CONTROL) continue; if (!visual) { if (c->session->curw->window == w || action == BELL_ANY) @@ -98,7 +96,6 @@ server_window_check_activity(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; - u_int i; if (s->curw->window == w) w->flags &= ~WINDOW_ACTIVITY; @@ -116,9 +113,8 @@ server_window_check_activity(struct session *s, struct winlink *wl) wl->flags |= WINLINK_ACTIVITY; if (options_get_number(&s->options, "visual-activity")) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s) continue; status_message_set(c, "Activity in window %d", wl->idx); } @@ -134,7 +130,6 @@ server_window_check_silence(struct session *s, struct winlink *wl) struct client *c; struct window *w = wl->window; struct timeval timer; - u_int i; int silence_interval, timer_difference; if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) @@ -167,9 +162,8 @@ server_window_check_silence(struct session *s, struct winlink *wl) wl->flags |= WINLINK_SILENCE; if (options_get_number(&s->options, "visual-silence")) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s) continue; status_message_set(c, "Silence in window %d", wl->idx); } @@ -183,11 +177,9 @@ void ring_bell(struct session *s) { struct client *c; - u_int i; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session == s && !(c->flags & CLIENT_CONTROL)) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == s && !(c->flags & CLIENT_CONTROL)) tty_bell(&c->tty); } } diff --git a/server.c b/server.c index 1183d64d..ba161c4b 100644 --- a/server.c +++ b/server.c @@ -139,8 +139,8 @@ server_start(int lockfd, char *lockfile) RB_INIT(&windows); RB_INIT(&all_window_panes); - ARRAY_INIT(&clients); - ARRAY_INIT(&dead_clients); + TAILQ_INIT(&clients); + TAILQ_INIT(&dead_clients); RB_INIT(&sessions); RB_INIT(&dead_sessions); TAILQ_INIT(&session_groups); @@ -166,7 +166,7 @@ server_start(int lockfd, char *lockfile) cfg_cmd_q->emptyfn = cfg_default_done; cfg_finished = 0; cfg_references = 1; - cfg_client = ARRAY_FIRST(&clients); + cfg_client = TAILQ_FIRST(&clients); if (cfg_client != NULL) cfg_client->references++; @@ -212,16 +212,14 @@ int server_should_shutdown(void) { struct client *c; - u_int i; if (!options_get_number(&global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL && c->session != NULL) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != NULL) return (0); } @@ -230,10 +228,8 @@ server_should_shutdown(void) * clients but don't actually exit until they've gone. */ cmd_wait_for_flush(); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) != NULL) - return (0); - } + if (!TAILQ_EMPTY(&clients)) + return (0); return (1); } @@ -242,55 +238,42 @@ server_should_shutdown(void) void server_send_shutdown(void) { - struct client *c; - struct session *s, *next_s; - u_int i; + struct client *c, *c1; + struct session *s, *s1; cmd_wait_for_flush(); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c != NULL) { - if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) - server_client_lost(c); - else - server_write_client(c, MSG_SHUTDOWN, NULL, 0); - c->session = NULL; - } + TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { + if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) + server_client_lost(c); + else + server_write_client(c, MSG_SHUTDOWN, NULL, 0); + c->session = NULL; } - s = RB_MIN(sessions, &sessions); - while (s != NULL) { - next_s = RB_NEXT(sessions, &sessions, s); + RB_FOREACH_SAFE(s, sessions, &sessions, s1) session_destroy(s); - s = next_s; - } } /* Free dead, unreferenced clients and sessions. */ void server_clean_dead(void) { - struct session *s, *next_s; - struct client *c; - u_int i; + struct session *s, *s1; + struct client *c, *c1; - s = RB_MIN(sessions, &dead_sessions); - while (s != NULL) { - next_s = RB_NEXT(sessions, &dead_sessions, s); - if (s->references == 0) { - RB_REMOVE(sessions, &dead_sessions, s); - free(s->name); - free(s); - } - s = next_s; + RB_FOREACH_SAFE(s, sessions, &dead_sessions, s1) { + if (s->references != 0) + continue; + RB_REMOVE(sessions, &dead_sessions, s); + free(s->name); + free(s); } - for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { - c = ARRAY_ITEM(&dead_clients, i); - if (c == NULL || c->references != 0) + TAILQ_FOREACH_SAFE(c, &dead_clients, entry, c1) { + if (c->references != 0) continue; - ARRAY_SET(&dead_clients, i, NULL); + TAILQ_REMOVE(&dead_clients, c, entry); free(c); } } diff --git a/tmux.h b/tmux.h index 9a6e4c83..c38149ab 100644 --- a/tmux.h +++ b/tmux.h @@ -1349,8 +1349,10 @@ struct client { struct cmd_q *cmdq; int references; + + TAILQ_ENTRY(client) entry; }; -ARRAY_DECL(clients, struct client *); +TAILQ_HEAD(clients, client); /* Parsed arguments structures. */ struct args_entry { diff --git a/tty.c b/tty.c index a5067b12..aae7b893 100644 --- a/tty.c +++ b/tty.c @@ -708,7 +708,6 @@ tty_write( { struct window_pane *wp = ctx->wp; struct client *c; - u_int i; /* wp can be NULL if updating the screen but not the terminal. */ if (wp == NULL) @@ -719,9 +718,8 @@ tty_write( if (!window_pane_visible(wp) || wp->flags & PANE_DROP) return; - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL || c->tty.term == NULL) + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || c->tty.term == NULL) continue; if (c->flags & CLIENT_SUSPENDED) continue; From d23af6cca0c4d55097d567bbfea42aecf0ca36b0 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 25 Apr 2015 15:57:48 +0000 Subject: [PATCH 104/703] Explicitly cancel mouse "button" mode, this happens implicitly with some one of the other things we send with xterm, but not with urxvt. Reported by sthen@. --- tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tty.c b/tty.c index aae7b893..ee52d038 100644 --- a/tty.c +++ b/tty.c @@ -220,7 +220,7 @@ tty_start_tty(struct tty *tty) tty_putcode(tty, TTYC_CNORM); if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); + tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) { if (options_get_number(&global_options, "focus-events")) { @@ -294,7 +294,7 @@ tty_stop_tty(struct tty *tty) tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); if (tty_term_has(tty->term, TTYC_KMOUS)) - tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); + tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { From 6dbd63ba4f2da666d55267692c610b9ed8d1d8dd Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 25 Apr 2015 18:09:28 +0000 Subject: [PATCH 105/703] Move the functions to convert ids from strings into session.c and window.c. --- cmd-attach-session.c | 9 ++++--- cmd-switch-client.c | 10 +++++--- cmd.c | 60 +++++--------------------------------------- session.c | 16 ++++++++++++ tmux.h | 5 ++-- window.c | 31 ++++++++++++++++++++++- 6 files changed, 67 insertions(+), 64 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 79e14616..46d568b4 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -69,9 +69,12 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } else { if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) return (CMD_RETURN_ERROR); - w = cmd_lookup_windowid(tflag); - if (w == NULL && (wp = cmd_lookup_paneid(tflag)) != NULL) - w = wp->window; + w = window_find_by_id_str(tflag); + if (w == NULL) { + wp = window_pane_find_by_id_str(tflag); + if (wp != NULL) + w = wp->window; + } if (w != NULL) wl = winlink_find_by_window(&s->windows, w); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 8c4daf97..18de0eb1 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -99,10 +99,12 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } else { if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) return (CMD_RETURN_ERROR); - w = cmd_lookup_windowid(tflag); - if (w == NULL && - (wp = cmd_lookup_paneid(tflag)) != NULL) - w = wp->window; + w = window_find_by_id_str(tflag); + if (w == NULL) { + wp = window_pane_find_by_id_str(tflag); + if (wp != NULL) + w = wp->window; + } if (w != NULL) wl = winlink_find_by_window(&s->windows, w); } diff --git a/cmd.c b/cmd.c index b81ffb36..4be5a4d6 100644 --- a/cmd.c +++ b/cmd.c @@ -124,7 +124,6 @@ struct session *cmd_choose_session(int); struct client *cmd_choose_client(struct client_list *); struct client *cmd_lookup_client(const char *); struct session *cmd_lookup_session(struct cmd_q *, const char *, int *); -struct session *cmd_lookup_session_id(const char *); struct winlink *cmd_lookup_window(struct session *, const char *, int *); int cmd_lookup_index(struct session *, const char *, int *); struct winlink *cmd_lookup_winlink_windowid(struct session *, const char *); @@ -639,21 +638,6 @@ cmd_lookup_client(const char *name) return (NULL); } -/* Find the target session or report an error and return NULL. */ -struct session * -cmd_lookup_session_id(const char *arg) -{ - char *endptr; - long id; - - if (arg[0] != '$') - return (NULL); - id = strtol(arg + 1, &endptr, 10); - if (arg[1] != '\0' && *endptr == '\0') - return (session_find_by_id(id)); - return (NULL); -} - /* Lookup a session by name. If no session is found, NULL is returned. */ struct session * cmd_lookup_session(struct cmd_q *cmdq, const char *name, int *ambiguous) @@ -665,13 +649,13 @@ cmd_lookup_session(struct cmd_q *cmdq, const char *name, int *ambiguous) *ambiguous = 0; /* Look for $id first. */ - if ((s = cmd_lookup_session_id(name)) != NULL) + if ((s = session_find_by_id_str(name)) != NULL) return (s); /* Try as pane or window id. */ - if ((wp = cmd_lookup_paneid(name)) != NULL) + if ((wp = window_pane_find_by_id_str(name)) != NULL) return (cmd_window_session(cmdq, wp->window, NULL)); - if ((w = cmd_lookup_windowid(name)) != NULL) + if ((w = window_find_by_id_str(name)) != NULL) return (cmd_window_session(cmdq, w, NULL)); /* @@ -722,12 +706,12 @@ cmd_lookup_window(struct session *s, const char *name, int *ambiguous) return (wl); /* Lookup as pane or window id. */ - if ((wp = cmd_lookup_paneid(name)) != NULL) { + if ((wp = window_pane_find_by_id_str(name)) != NULL) { wl = winlink_find_by_window(&s->windows, wp->window); if (wl != NULL) return (wl); } - if ((w = cmd_lookup_windowid(name)) != NULL) { + if ((w = window_find_by_id_str(name)) != NULL) { wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) return (wl); @@ -795,22 +779,6 @@ cmd_lookup_index(struct session *s, const char *name, int *ambiguous) return (-1); } -/* Lookup pane id. An initial % means a pane id. */ -struct window_pane * -cmd_lookup_paneid(const char *arg) -{ - const char *errstr; - u_int paneid; - - if (*arg != '%') - return (NULL); - - paneid = strtonum(arg + 1, 0, UINT_MAX, &errstr); - if (errstr != NULL) - return (NULL); - return (window_pane_find_by_id(paneid)); -} - /* Lookup window id in a session. An initial @ means a window id. */ struct winlink * cmd_lookup_winlink_windowid(struct session *s, const char *arg) @@ -827,22 +795,6 @@ cmd_lookup_winlink_windowid(struct session *s, const char *arg) return (winlink_find_by_window_id(&s->windows, windowid)); } -/* Lookup window id. An initial @ means a window id. */ -struct window * -cmd_lookup_windowid(const char *arg) -{ - const char *errstr; - u_int windowid; - - if (*arg != '@') - return (NULL); - - windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr); - if (errstr != NULL) - return (NULL); - return (window_find_by_id(windowid)); -} - /* Find session and winlink for window. */ struct session * cmd_window_session(struct cmd_q *cmdq, struct window *w, struct winlink **wlp) @@ -1252,7 +1204,7 @@ cmd_find_pane(struct cmd_q *cmdq, } /* Lookup as pane id. */ - if ((*wpp = cmd_lookup_paneid(arg)) != NULL) { + if ((*wpp = window_pane_find_by_id_str(arg)) != NULL) { s = cmd_window_session(cmdq, (*wpp)->window, &wl); if (sp != NULL) *sp = s; diff --git a/session.c b/session.c index c0535725..224f1f32 100644 --- a/session.c +++ b/session.c @@ -70,6 +70,22 @@ session_find(const char *name) return (RB_FIND(sessions, &sessions, &s)); } +/* Find session by id parsed from a string. */ +struct session * +session_find_by_id_str(const char *s) +{ + const char *errstr; + u_int id; + + if (*s != '$') + return (NULL); + + id = strtonum(s + 1, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return (NULL); + return (session_find_by_id(id)); +} + /* Find session by id. */ struct session * session_find_by_id(u_int id) diff --git a/tmux.h b/tmux.h index c38149ab..3f8829ce 100644 --- a/tmux.h +++ b/tmux.h @@ -1766,8 +1766,6 @@ int cmd_find_index(struct cmd_q *, const char *, struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); char *cmd_template_replace(const char *, const char *, int); -struct window *cmd_lookup_windowid(const char *); -struct window_pane *cmd_lookup_paneid(const char *); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; @@ -2147,6 +2145,7 @@ struct winlink *winlink_previous_by_number(struct winlink *, struct session *, int); void winlink_stack_push(struct winlink_stack *, struct winlink *); void winlink_stack_remove(struct winlink_stack *, struct winlink *); +struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); struct window *window_create(const char *, int, char **, const char *, @@ -2172,6 +2171,7 @@ struct window_pane *window_pane_previous_by_number(struct window *, int window_pane_index(struct window_pane *, u_int *); u_int window_count_panes(struct window *); void window_destroy_panes(struct window *); +struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); @@ -2309,6 +2309,7 @@ int session_cmp(struct session *, struct session *); RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); +struct session *session_find_by_id_str(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, int, char **, const char *, int, struct environ *, struct termios *, int, u_int, diff --git a/window.c b/window.c index 5ca8aa0c..acbcd33d 100644 --- a/window.c +++ b/window.c @@ -253,6 +253,21 @@ winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) } } +struct window * +window_find_by_id_str(const char* s) +{ + const char *errstr; + u_int id; + + if (*s != '@') + return (NULL); + + id = strtonum(s + 1, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return (NULL); + return (window_find_by_id(id)); +} + struct window * window_find_by_id(u_int id) { @@ -653,7 +668,21 @@ window_printable_flags(struct session *s, struct winlink *wl) return (xstrdup(flags)); } -/* Find pane in global tree by id. */ +struct window_pane * +window_pane_find_by_id_str(const char *s) +{ + const char *errstr; + u_int id; + + if (*s != '%') + return (NULL); + + id = strtonum(s + 1, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return (NULL); + return (window_pane_find_by_id(id)); +} + struct window_pane * window_pane_find_by_id(u_int id) { From 07dfdb974d36f1eee084b9739d2b8a5b64172ec8 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 25 Apr 2015 18:33:59 +0000 Subject: [PATCH 106/703] Make message log a TAILQ. --- cmd-show-messages.c | 5 +---- cmd.c | 1 + server-client.c | 11 +++++------ status.c | 26 ++++++++++++++------------ tmux.h | 7 ++++--- window.c | 2 ++ 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 3b7baa89..028b4bb0 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -130,7 +130,6 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c; struct message_entry *msg; char *tim; - u_int i; int done; done = 0; @@ -156,9 +155,7 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); - for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { - msg = &ARRAY_ITEM(&c->message_log, i); - + TAILQ_FOREACH(msg, &c->message_log, entry) { tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; diff --git a/cmd.c b/cmd.c index 4be5a4d6..ea1a29d0 100644 --- a/cmd.c +++ b/cmd.c @@ -117,6 +117,7 @@ const struct cmd_entry *cmd_table[] = { }; ARRAY_DECL(client_list, struct client *); +ARRAY_DECL(sessionslist, struct session *); int cmd_session_better(struct session *, struct session *, int); struct session *cmd_choose_session_list(struct sessionslist *); diff --git a/server-client.c b/server-client.c index a0f81b73..b24e1afd 100644 --- a/server-client.c +++ b/server-client.c @@ -94,7 +94,7 @@ server_client_create(int fd) RB_INIT(&c->status_old); c->message_string = NULL; - ARRAY_INIT(&c->message_log); + TAILQ_INIT(&c->message_log); c->prompt_string = NULL; c->prompt_buffer = NULL; @@ -138,8 +138,7 @@ server_client_open(struct client *c, char **cause) void server_client_lost(struct client *c) { - struct message_entry *msg; - u_int i; + struct message_entry *msg, *msg1; TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %d", c->ibuf.fd); @@ -175,11 +174,11 @@ server_client_lost(struct client *c) free(c->message_string); if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); - for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { - msg = &ARRAY_ITEM(&c->message_log, i); + TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { free(msg->msg); + TAILQ_REMOVE(&c->message_log, msg, entry); + free(msg); } - ARRAY_FREE(&c->message_log); free(c->prompt_string); free(c->prompt_buffer); diff --git a/status.c b/status.c index fd0292c7..e3d335fd 100644 --- a/status.c +++ b/status.c @@ -636,10 +636,12 @@ void status_message_set(struct client *c, const char *fmt, ...) { struct timeval tv; - struct message_entry *msg; + struct message_entry *msg, *msg1; va_list ap; int delay; - u_int i, limit; + u_int first, limit; + + limit = options_get_number(&global_options, "message-limit"); status_prompt_clear(c); status_message_clear(c); @@ -648,19 +650,19 @@ status_message_set(struct client *c, const char *fmt, ...) xvasprintf(&c->message_string, fmt, ap); va_end(ap); - ARRAY_EXPAND(&c->message_log, 1); - msg = &ARRAY_LAST(&c->message_log); + msg = xcalloc(1, sizeof *msg); msg->msg_time = time(NULL); + msg->msg_num = c->message_next++; msg->msg = xstrdup(c->message_string); + TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - limit = options_get_number(&global_options, "message-limit"); - if (ARRAY_LENGTH(&c->message_log) > limit) { - limit = ARRAY_LENGTH(&c->message_log) - limit; - for (i = 0; i < limit; i++) { - msg = &ARRAY_FIRST(&c->message_log); - free(msg->msg); - ARRAY_REMOVE(&c->message_log, 0); - } + first = c->message_next - limit; + TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { + if (msg->msg_num >= first) + continue; + free(msg->msg); + TAILQ_REMOVE(&c->message_log, msg, entry); + free(msg); } delay = options_get_number(&c->session->options, "display-time"); diff --git a/tmux.h b/tmux.h index 3f8829ce..170ad692 100644 --- a/tmux.h +++ b/tmux.h @@ -950,7 +950,6 @@ struct window_pane { }; TAILQ_HEAD(window_panes, window_pane); RB_HEAD(window_pane_tree, window_pane); -ARRAY_DECL(window_pane_list, struct window_pane *); /* Window structure. */ struct window { @@ -1101,7 +1100,6 @@ struct session { RB_ENTRY(session) entry; }; RB_HEAD(sessions, session); -ARRAY_DECL(sessionslist, struct session *); /* TTY information. */ struct tty_key { @@ -1255,7 +1253,9 @@ struct tty_ctx { /* Saved message entry. */ struct message_entry { char *msg; + u_int msg_num; time_t msg_time; + TAILQ_ENTRY(message_entry) entry; }; /* Status output data from a job. */ @@ -1327,7 +1327,8 @@ struct client { char *message_string; struct event message_timer; - ARRAY_DECL(, struct message_entry) message_log; + u_int message_next; + TAILQ_HEAD(, message_entry) message_log; char *prompt_string; char *prompt_buffer; diff --git a/window.c b/window.c index acbcd33d..c60404d0 100644 --- a/window.c +++ b/window.c @@ -49,6 +49,8 @@ * it reaches zero. */ +ARRAY_DECL(window_pane_list, struct window_pane *); + /* Global window list. */ struct windows windows; From a568b9cadce002f6f7e8ee914c995242bd70cce5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 25 Apr 2015 18:47:01 +0000 Subject: [PATCH 107/703] Use a char **,u_int pair for cfg_causes. --- cfg.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/cfg.c b/cfg.c index b4c9bff4..54ba7f73 100644 --- a/cfg.c +++ b/cfg.c @@ -30,7 +30,8 @@ struct cmd_q *cfg_cmd_q; int cfg_finished; int cfg_references; -ARRAY_DECL (, char *) cfg_causes = ARRAY_INITIALIZER; +char** cfg_causes; +u_int cfg_ncauses; struct client *cfg_client; int @@ -122,40 +123,42 @@ cfg_add_cause(const char* fmt, ...) xvasprintf(&msg, fmt, ap); va_end (ap); - ARRAY_ADD(&cfg_causes, msg); + cfg_ncauses++; + cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); + cfg_causes[cfg_ncauses - 1] = msg; } void cfg_print_causes(struct cmd_q *cmdq) { - char *cause; u_int i; - for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { - cause = ARRAY_ITEM(&cfg_causes, i); - cmdq_print(cmdq, "%s", cause); - free(cause); + for (i = 0; i < cfg_ncauses; i++) { + cmdq_print(cmdq, "%s", cfg_causes[i]); + free(cfg_causes[i]); } - ARRAY_FREE(&cfg_causes); + + free(cfg_causes); + cfg_causes = NULL; } void cfg_show_causes(struct session *s) { struct window_pane *wp; - char *cause; u_int i; - if (s == NULL || ARRAY_EMPTY(&cfg_causes)) + if (s == NULL || cfg_ncauses == 0) return; wp = s->curw->window->active; window_pane_set_mode(wp, &window_copy_mode); window_copy_init_for_output(wp); - for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { - cause = ARRAY_ITEM(&cfg_causes, i); - window_copy_add(wp, "%s", cause); - free(cause); + for (i = 0; i < cfg_ncauses; i++) { + window_copy_add(wp, "%s", cfg_causes[i]); + free(cfg_causes[i]); } - ARRAY_FREE(&cfg_causes); + + free(cfg_causes); + cfg_causes = NULL; } From 1d1208e335644ee10ebe47363b2ca8db45bf9142 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 25 Apr 2015 18:49:01 +0000 Subject: [PATCH 108/703] Fix some char* -> char *. --- cfg.c | 18 +++++++++--------- key-bindings.c | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cfg.c b/cfg.c index 54ba7f73..bacec996 100644 --- a/cfg.c +++ b/cfg.c @@ -27,12 +27,12 @@ #include "tmux.h" -struct cmd_q *cfg_cmd_q; -int cfg_finished; -int cfg_references; -char** cfg_causes; -u_int cfg_ncauses; -struct client *cfg_client; +struct cmd_q *cfg_cmd_q; +int cfg_finished; +int cfg_references; +char **cfg_causes; +u_int cfg_ncauses; +struct client *cfg_client; int load_cfg(const char *path, struct cmd_q *cmdq, char **cause) @@ -114,10 +114,10 @@ cfg_default_done(unused struct cmd_q *cmdq) } void -cfg_add_cause(const char* fmt, ...) +cfg_add_cause(const char *fmt, ...) { - va_list ap; - char* msg; + va_list ap; + char *msg; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); diff --git a/key-bindings.c b/key-bindings.c index 670300a7..d56428b1 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -144,7 +144,7 @@ key_bindings_remove_table(const char *name) void key_bindings_init(void) { - static const char* defaults[] = { + static const char *defaults[] = { "bind C-b send-prefix", "bind C-o rotate-window", "bind C-z suspend-client", @@ -225,7 +225,7 @@ key_bindings_init(void) }; u_int i; struct cmd_list *cmdlist; - char* cause; + char *cause; int error; struct cmd_q *cmdq; From 05e7fbd60fcd1f5c82049dc2f416e7dd42577cfd Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 25 Apr 2015 18:56:05 +0000 Subject: [PATCH 109/703] Get rid of window_choose_list type. --- window.c | 92 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/window.c b/window.c index c60404d0..31f20a1c 100644 --- a/window.c +++ b/window.c @@ -49,8 +49,6 @@ * it reaches zero. */ -ARRAY_DECL(window_pane_list, struct window_pane *); - /* Global window list. */ struct windows windows; @@ -64,7 +62,7 @@ void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); -struct window_pane *window_pane_choose_best(struct window_pane_list *); +struct window_pane *window_pane_choose_best(struct window_pane **, u_int); RB_GENERATE(windows, window, entry, window_cmp); @@ -256,7 +254,7 @@ winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) } struct window * -window_find_by_id_str(const char* s) +window_find_by_id_str(const char *s) { const char *errstr; u_int id; @@ -1149,17 +1147,17 @@ window_pane_search(struct window_pane *wp, const char *searchstr, /* Get MRU pane from a list. */ struct window_pane * -window_pane_choose_best(struct window_pane_list *list) +window_pane_choose_best(struct window_pane **list, u_int size) { struct window_pane *next, *best; u_int i; - if (ARRAY_LENGTH(list) == 0) + if (size == 0) return (NULL); - best = ARRAY_FIRST(list); - for (i = 1; i < ARRAY_LENGTH(list); i++) { - next = ARRAY_ITEM(list, i); + best = list[0]; + for (i = 1; i < size; i++) { + next = list[i]; if (next->active_point > best->active_point) best = next; } @@ -1173,14 +1171,15 @@ window_pane_choose_best(struct window_pane_list *list) struct window_pane * window_pane_find_up(struct window_pane *wp) { - struct window_pane *next, *best; - u_int edge, left, right, end; - struct window_pane_list list; + struct window_pane *next, *best, **list; + u_int edge, left, right, end, size; int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); - ARRAY_INIT(&list); + + list = NULL; + size = 0; edge = wp->yoff; if (edge == 0) @@ -1203,12 +1202,14 @@ window_pane_find_up(struct window_pane *wp) found = 1; else if (end >= left && end <= right) found = 1; - if (found) - ARRAY_ADD(&list, next); + if (!found) + continue; + list = xreallocarray(list, size + 1, sizeof *list); + list[size++] = next; } - best = window_pane_choose_best(&list); - ARRAY_FREE(&list); + best = window_pane_choose_best(list, size); + free(list); return (best); } @@ -1216,14 +1217,15 @@ window_pane_find_up(struct window_pane *wp) struct window_pane * window_pane_find_down(struct window_pane *wp) { - struct window_pane *next, *best; - u_int edge, left, right, end; - struct window_pane_list list; + struct window_pane *next, *best, **list; + u_int edge, left, right, end, size; int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); - ARRAY_INIT(&list); + + list = NULL; + size = 0; edge = wp->yoff + wp->sy + 1; if (edge >= wp->window->sy) @@ -1246,12 +1248,14 @@ window_pane_find_down(struct window_pane *wp) found = 1; else if (end >= left && end <= right) found = 1; - if (found) - ARRAY_ADD(&list, next); + if (!found) + continue; + list = xreallocarray(list, size + 1, sizeof *list); + list[size++] = next; } - best = window_pane_choose_best(&list); - ARRAY_FREE(&list); + best = window_pane_choose_best(list, size); + free(list); return (best); } @@ -1259,14 +1263,15 @@ window_pane_find_down(struct window_pane *wp) struct window_pane * window_pane_find_left(struct window_pane *wp) { - struct window_pane *next, *best; - u_int edge, top, bottom, end; - struct window_pane_list list; + struct window_pane *next, *best, **list; + u_int edge, top, bottom, end, size; int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); - ARRAY_INIT(&list); + + list = NULL; + size = 0; edge = wp->xoff; if (edge == 0) @@ -1289,12 +1294,14 @@ window_pane_find_left(struct window_pane *wp) found = 1; else if (end >= top && end <= bottom) found = 1; - if (found) - ARRAY_ADD(&list, next); + if (!found) + continue; + list = xreallocarray(list, size + 1, sizeof *list); + list[size++] = next; } - best = window_pane_choose_best(&list); - ARRAY_FREE(&list); + best = window_pane_choose_best(list, size); + free(list); return (best); } @@ -1302,14 +1309,15 @@ window_pane_find_left(struct window_pane *wp) struct window_pane * window_pane_find_right(struct window_pane *wp) { - struct window_pane *next, *best; - u_int edge, top, bottom, end; - struct window_pane_list list; + struct window_pane *next, *best, **list; + u_int edge, top, bottom, end, size; int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); - ARRAY_INIT(&list); + + list = NULL; + size = 0; edge = wp->xoff + wp->sx + 1; if (edge >= wp->window->sx) @@ -1332,12 +1340,14 @@ window_pane_find_right(struct window_pane *wp) found = 1; else if (end >= top && end <= bottom) found = 1; - if (found) - ARRAY_ADD(&list, next); + if (!found) + continue; + list = xreallocarray(list, size + 1, sizeof *list); + list[size++] = next; } - best = window_pane_choose_best(&list); - ARRAY_FREE(&list); + best = window_pane_choose_best(list, size); + free(list); return (best); } From 5bd5c9c84e4bde3461a50f15e387c5e9fb78d7cb Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 26 Apr 2015 20:25:20 +0000 Subject: [PATCH 110/703] Remove panes from layout if spawning them fails, reported by Anthony J Bentley. --- cmd-split-window.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd-split-window.c b/cmd-split-window.c index 680b1560..4e85a0f3 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -147,6 +147,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) goto error; } new_wp = window_add_pane(w, hlimit); + layout_assign_pane(lc, new_wp); path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -159,7 +160,6 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, &env, s->tio, &cause) != 0) goto error; - layout_assign_pane(lc, new_wp); server_redraw_window(w); @@ -194,8 +194,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) error: environ_free(&env); - if (new_wp != NULL) + if (new_wp != NULL) { + layout_close_pane(new_wp); window_remove_pane(w, new_wp); + } cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); if (fd != -1) From a70762c9b50e9d9249fa9a56233c0df4129c82fc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Apr 2015 07:49:36 +0000 Subject: [PATCH 111/703] If the requested pane is already active, do not unzoom the window (or do anything else). Prevents mouse clicking when zoomed causing unzoom, reported by Jose Antonio Delgado Alfonso (with a different fix). --- cmd-select-pane.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index c84b4149..e7f2249e 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -77,12 +77,6 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) return (CMD_RETURN_ERROR); - server_unzoom_window(wp->window); - if (!window_pane_visible(wp)) { - cmdq_error(cmdq, "pane not visible"); - return (CMD_RETURN_ERROR); - } - if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if (args_has(args, 'P')) { style = args_get(args, 'P'); @@ -111,11 +105,23 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if (args_has(self->args, 'e')) + if (args_has(self->args, 'e')) { wp->flags &= ~PANE_INPUTOFF; - else if (args_has(self->args, 'd')) + return (CMD_RETURN_NORMAL); + } + if (args_has(self->args, 'd')) { wp->flags |= PANE_INPUTOFF; - else if (window_set_active_pane(wl->window, wp)) { + return (CMD_RETURN_NORMAL); + } + + if (wp == wl->window->active) + return (CMD_RETURN_NORMAL); + server_unzoom_window(wp->window); + if (!window_pane_visible(wp)) { + cmdq_error(cmdq, "pane not visible"); + return (CMD_RETURN_ERROR); + } + if (window_set_active_pane(wl->window, wp)) { server_status_window(wl->window); server_redraw_window_borders(wl->window); } From 95195f52584565483bf9850840f6d81cd88bf9b2 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Apr 2015 16:25:57 +0000 Subject: [PATCH 112/703] Rewrite of the target resolution internals to be simpler and more consistent but with much less duplication, but keeping the same internal API. Also adds more readable aliases for some of the special tokens used in targets (eg "{start}" instead of "^"). Some behaviours may have changed, for example prefix matches now happen before fnmatch. --- Makefile | 1 + cmd-choose-buffer.c | 2 +- cmd-choose-client.c | 2 +- cmd-choose-tree.c | 2 +- cmd-display-message.c | 2 +- cmd-find-window.c | 2 +- cmd-find.c | 1114 +++++++++++++++++++++++++++++++++++++++++ cmd-load-buffer.c | 2 +- cmd-new-session.c | 2 +- cmd-save-buffer.c | 2 +- cmd.c | 937 +--------------------------------- tmux.1 | 184 ++++--- tmux.h | 25 +- 13 files changed, 1250 insertions(+), 1027 deletions(-) create mode 100644 cmd-find.c diff --git a/Makefile b/Makefile index a719eaf1..ac7e6e54 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,7 @@ SRCS= arguments.c \ cmd-unbind-key.c \ cmd-wait-for.c \ cmd.c \ + cmd-find.c \ cmd-queue.c \ colour.c \ control.c \ diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 33125a0e..af178976 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -53,7 +53,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) u_int idx; int utf8flag; - if ((c = cmd_current_client(cmdq)) == NULL) { + if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 49fe2a34..93ac28a8 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -61,7 +61,7 @@ cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) char *action; u_int idx, cur; - if ((c = cmd_current_client(cmdq)) == NULL) { + if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 823d0423..0b0fea6a 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -87,7 +87,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) ses_template = win_template = NULL; ses_action = win_action = NULL; - if ((c = cmd_current_client(cmdq)) == NULL) { + if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } diff --git a/cmd-display-message.c b/cmd-display-message.c index 0a61fd1e..ee9eafe9 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -78,7 +78,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) if (c == NULL) return (CMD_RETURN_ERROR); } else { - c = cmd_current_client(cmdq); + c = cmd_find_client(cmdq, NULL, 1); if (c == NULL && !args_has(self->args, 'p')) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); diff --git a/cmd-find-window.c b/cmd-find-window.c index 25155f7b..64e092bf 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -143,7 +143,7 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) const char *template; u_int i, match_flags; - if ((c = cmd_current_client(cmdq)) == NULL) { + if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } diff --git a/cmd-find.c b/cmd-find.c new file mode 100644 index 00000000..a71968a1 --- /dev/null +++ b/cmd-find.c @@ -0,0 +1,1114 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +#define CMD_FIND_PREFER_UNATTACHED 0x1 +#define CMD_FIND_QUIET 0x2 +#define CMD_FIND_WINDOW_INDEX 0x4 + +enum cmd_find_type { + CMD_FIND_PANE, + CMD_FIND_WINDOW, + CMD_FIND_SESSION, +}; + +struct cmd_find_state { + struct cmd_q *cmdq; + int flags; + struct cmd_find_state *current; + + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + int idx; +}; + +int cmd_find_client_better(struct client *, struct client *); +struct client *cmd_find_best_client(struct client **, u_int); +int cmd_find_session_better(struct session *, struct session *, + int); +struct session *cmd_find_best_session(struct session **, u_int, int); +int cmd_find_best_session_with_window(struct cmd_find_state *); +int cmd_find_best_winlink_with_window(struct cmd_find_state *); + +int cmd_find_current_session_with_client(struct cmd_find_state *); +int cmd_find_current_session(struct cmd_find_state *); +struct client *cmd_find_current_client(struct cmd_q *); + +const char *cmd_find_map_table(const char *[][2], const char *); + +int cmd_find_get_session(struct cmd_find_state *, const char *); +int cmd_find_get_window(struct cmd_find_state *, const char *); +int cmd_find_get_window_with_session(struct cmd_find_state *, const char *); +int cmd_find_get_window_with_pane(struct cmd_find_state *); +int cmd_find_get_pane(struct cmd_find_state *, const char *); +int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *); +int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); + +void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); +void cmd_find_log_state(const char *, const char *, struct cmd_find_state *); + +struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, + enum cmd_find_type, int); + +const char *cmd_find_session_table[][2] = { + { NULL, NULL } +}; +const char *cmd_find_window_table[][2] = { + { "{start}", "^" }, + { "{last}", "!" }, + { "{end}", "$" }, + { "{next}", "+" }, + { "{previous}", "-" }, + { NULL, NULL } +}; +const char *cmd_find_pane_table[][2] = { + { "{last}", "!" }, + { "{next}", "+" }, + { "{previous}", "-" }, + { "{top}", "top" }, + { "{bottom}", "bottom" }, + { "{left}", "left" }, + { "{right}", "right" }, + { "{top-left}", "top-left" }, + { "{top-right}", "top-right" }, + { "{bottom-left}", "bottom-left" }, + { "{bottom-right}", "bottom-right" }, + { "{up}", "{up}" }, + { "{down}", "{down}" }, + { "{left}", "{left}" }, + { "{right}", "{right}" }, + { NULL, NULL } +}; + +/* Is this client better? */ +int +cmd_find_client_better(struct client *c, struct client *than) +{ + if (than == NULL) + return (1); + return (timercmp(&c->activity_time, &than->activity_time, >)); +} + +/* Find best client from a list, or all if list is NULL. */ +struct client * +cmd_find_best_client(struct client **clist, u_int csize) +{ + struct client *c_loop, *c; + u_int i; + + c = NULL; + if (clist != NULL) { + for (i = 0; i < csize; i++) { + if (cmd_find_client_better(clist[i], c)) + c = clist[i]; + } + } else { + TAILQ_FOREACH(c_loop, &clients, entry) { + if (cmd_find_client_better(c_loop, c)) + c_loop = c; + } + } + return (c); +} + +/* Is this session better? */ +int +cmd_find_session_better(struct session *s, struct session *than, int flags) +{ + int attached; + + if (than == NULL) + return (1); + if (flags & CMD_FIND_PREFER_UNATTACHED) { + attached = (~than->flags & SESSION_UNATTACHED); + if (attached && (s->flags & SESSION_UNATTACHED)) + return (1); + else if (!attached && (~s->flags & SESSION_UNATTACHED)) + return (0); + } + return (timercmp(&s->activity_time, &than->activity_time, >)); +} + +/* Find best session from a list, or all if list is NULL. */ +struct session * +cmd_find_best_session(struct session **slist, u_int ssize, int flags) +{ + struct session *s_loop, *s; + u_int i; + + s = NULL; + if (slist != NULL) { + for (i = 0; i < ssize; i++) { + if (cmd_find_session_better(slist[i], s, flags)) + s = slist[i]; + } + } else { + RB_FOREACH(s_loop, sessions, &sessions) { + if (cmd_find_session_better(s_loop, s, flags)) + s = s_loop; + } + } + return (s); +} + +/* Find best session and winlink for window. */ +int +cmd_find_best_session_with_window(struct cmd_find_state *fs) +{ + struct session **slist = NULL; + u_int ssize; + struct session *s; + + ssize = 0; + RB_FOREACH(s, sessions, &sessions) { + if (!session_has(s, fs->w)) + continue; + slist = xreallocarray (slist, ssize + 1, sizeof *slist); + slist[ssize++] = s; + } + if (ssize == 0) + goto fail; + fs->s = cmd_find_best_session(slist, ssize, fs->flags); + if (fs->s == NULL) + goto fail; + free (slist); + return (cmd_find_best_winlink_with_window(fs)); + +fail: + free(slist); + return (-1); +} + +/* + * Find the best winlink for a window (the current if it contains the pane, + * otherwise the first). + */ +int +cmd_find_best_winlink_with_window(struct cmd_find_state *fs) +{ + struct winlink *wl, *wl_loop; + + wl = NULL; + if (fs->s->curw->window == fs->w) + wl = fs->s->curw; + else { + RB_FOREACH(wl_loop, winlinks, &fs->s->windows) { + if (wl_loop->window == fs->w) { + wl = wl_loop; + break; + } + } + } + if (wl == NULL) + return (-1); + fs->wl = wl; + fs->idx = fs->wl->idx; + return (0); +} + +/* Find current session when we have an unattached client. */ +int +cmd_find_current_session_with_client(struct cmd_find_state *fs) +{ + struct window_pane *wp; + + /* If this is running in a pane, that's great. */ + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) + break; + } + + /* Not running in a pane. We know nothing. Find the best session. */ + if (wp == NULL) { + fs->s = cmd_find_best_session(NULL, 0, fs->flags); + if (fs->s == NULL) + return (-1); + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + return (0); + } + + /* We now know the window and pane. */ + fs->w = wp->window; + fs->wp = wp; + + /* Find the best session and winlink. */ + if (cmd_find_best_session_with_window(fs) != 0) + return (-1); + return (0); +} + +/* + * Work out the best current state. If this function succeeds, the state is + * guaranteed to be completely filled in. + */ +int +cmd_find_current_session(struct cmd_find_state *fs) +{ + /* If we know the current client, use it. */ + if (fs->cmdq->client != NULL) { + if (fs->cmdq->client->session == NULL) + return (cmd_find_current_session_with_client(fs)); + fs->s = fs->cmdq->client->session; + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + return (0); + } + + /* We know nothing, find the best session and client. */ + fs->s = cmd_find_best_session(NULL, 0, fs->flags); + if (fs->s == NULL) + return (-1); + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + + return (0); +} + +/* Work out the best current client. */ +struct client * +cmd_find_current_client(struct cmd_q *cmdq) +{ + struct cmd_find_state current; + struct session *s; + struct client *c, **clist = NULL; + u_int csize; + + /* If the queue client has a session, use it. */ + if (cmdq->client != NULL && cmdq->client->session != NULL) + return (cmdq->client); + + /* Otherwise find the current session. */ + cmd_find_clear_state(¤t, cmdq, 0); + if (cmd_find_current_session(¤t) != 0) + return (NULL); + + /* If it is attached, find the best of it's clients. */ + s = current.s; + if (~s->flags & SESSION_UNATTACHED) { + csize = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (c->session != s) + continue; + clist = xreallocarray (clist, csize + 1, sizeof *clist); + clist[csize++] = c; + } + if (csize != 0) { + c = cmd_find_best_client(clist, csize); + if (c != NULL) { + free(clist); + return (c); + } + } + free(clist); + } + + /* Otherwise pick best of all clients. */ + return (cmd_find_best_client(NULL, 0)); +} + +/* Maps string in table. */ +const char * +cmd_find_map_table(const char *table[][2], const char *s) +{ + u_int i; + + for (i = 0; table[i][0] != NULL; i++) { + if (strcmp(s, table[i][0]) == 0) + return (table[i][1]); + } + return (s); +} + +/* Find session from string. Fills in s. */ +int +cmd_find_get_session(struct cmd_find_state *fs, const char *session) +{ + struct session *s, *s_loop; + + log_debug("%s: %s", __func__, session); + + /* Check for session ids starting with $. */ + if (*session == '$') { + fs->s = session_find_by_id_str(session); + if (fs->s == NULL) + return (-1); + return (0); + } + + /* Look for exactly this session. */ + fs->s = session_find(session); + if (fs->s != NULL) + return (0); + + /* Otherwise look for prefix. */ + s = NULL; + RB_FOREACH(s_loop, sessions, &sessions) { + if (strncmp(session, s_loop->name, strlen(session)) == 0) { + if (s != NULL) + return (-1); + s = s_loop; + } + } + if (s != NULL) { + fs->s = s; + return (0); + } + + /* Then as a pattern. */ + s = NULL; + RB_FOREACH(s_loop, sessions, &sessions) { + if (fnmatch(session, s_loop->name, 0) == 0) { + if (s != NULL) + return (-1); + s = s_loop; + } + } + if (s != NULL) { + fs->s = s; + return (0); + } + + return (-1); +} + +/* Find window from string. Fills in s, wl, w. */ +int +cmd_find_get_window(struct cmd_find_state *fs, const char *window) +{ + log_debug("%s: %s", __func__, window); + + /* Check for window ids starting with @. */ + if (*window == '@') { + fs->w = window_find_by_id_str(window); + if (fs->w == NULL) + return (-1); + return (cmd_find_best_session_with_window(fs)); + } + + /* Not a window id, so use the current session. */ + fs->s = fs->current->s; + + /* We now only need to find the winlink in this session. */ + return (cmd_find_get_window_with_session(fs, window)); +} + +/* + * Find window from string, assuming it is in given session. Needs s, fills in + * wl and w. + */ +int +cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) +{ + struct winlink *wl; + const char *errstr; + int idx, n; + struct session *s; + + log_debug("%s: %s", __func__, window); + + /* Check for window ids starting with @. */ + if (*window == '@') { + fs->w = window_find_by_id_str(window); + if (fs->w == NULL || !session_has(fs->s, fs->w)) + return (-1); + return (cmd_find_best_winlink_with_window(fs)); + } + + /* Try as an offset. */ + if (window[0] == '+' || window[0] == '-') { + if (window[1] != '\0') + n = strtonum(window + 1, 1, INT_MAX, NULL); + else + n = 1; + s = fs->s; + if (fs->flags & CMD_FIND_WINDOW_INDEX) { + if (window[0] == '+') { + if (INT_MAX - s->curw->idx < n) + return (-1); + fs->idx = s->curw->idx + n; + } else { + if (n < s->curw->idx) + return (-1); + fs->idx = s->curw->idx - n; + } + return (0); + } + if (window[0] == '+') + fs->wl = winlink_next_by_number(s->curw, s, n); + else + fs->wl = winlink_previous_by_number(s->curw, s, n); + if (fs->wl != NULL) { + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + } + + /* Try special characters. */ + if (strcmp(window, "!") == 0) { + fs->wl = TAILQ_FIRST(&fs->s->lastw); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } else if (strcmp(window, "^") == 0) { + fs->wl = RB_MIN(winlinks, &fs->s->windows); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } else if (strcmp(window, "$") == 0) { + fs->wl = RB_MAX(winlinks, &fs->s->windows); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + + /* First see if this is a valid window index in this session. */ + idx = strtonum(window, 0, INT_MAX, &errstr); + if (errstr == NULL) { + if (fs->flags & CMD_FIND_WINDOW_INDEX) { + fs->idx = idx; + return (0); + } + fs->wl = winlink_find_by_index(&fs->s->windows, idx); + if (fs->wl != NULL) { + fs->w = fs->wl->window; + return (0); + } + } + + /* Look for exact matches, error if more than one. */ + fs->wl = NULL; + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (strcmp(window, wl->window->name) == 0) { + if (fs->wl != NULL) + return (-1); + fs->wl = wl; + } + } + if (fs->wl != NULL) { + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + + /* Try as the start of a window name, error if multiple. */ + fs->wl = NULL; + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (strncmp(window, wl->window->name, strlen(window)) == 0) { + if (fs->wl != NULL) + return (-1); + fs->wl = wl; + } + } + if (fs->wl != NULL) { + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + + /* Now look for pattern matches, again error if multiple. */ + fs->wl = NULL; + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (fnmatch(window, wl->window->name, 0) == 0) { + if (fs->wl != NULL) + return (-1); + fs->wl = wl; + } + } + if (fs->wl != NULL) { + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + + return (-1); +} + +/* Find window from given pane. Needs wp, fills in s and wl and w. */ +int +cmd_find_get_window_with_pane(struct cmd_find_state *fs) +{ + log_debug("%s", __func__); + + fs->w = fs->wp->window; + return (cmd_find_best_session_with_window(fs)); +} + +/* Find pane from string. Fills in s, wl, w, wp. */ +int +cmd_find_get_pane(struct cmd_find_state *fs, const char *pane) +{ + log_debug("%s: %s", __func__, pane); + + /* Check for pane ids starting with %. */ + if (*pane == '%') { + fs->wp = window_pane_find_by_id_str(pane); + if (fs->wp == NULL) + return (-1); + fs->w = fs->wp->window; + return (cmd_find_best_session_with_window(fs)); + } + + /* Not a pane id, so use the current session and window. */ + fs->s = fs->current->s; + fs->wl = fs->current->wl; + fs->idx = fs->current->idx; + fs->w = fs->current->w; + + /* We now only need to find the pane in this window. */ + return (cmd_find_get_pane_with_window(fs, pane)); +} + +/* + * Find pane from string, assuming it is in given session. Needs s, fills in wl + * and w and wp. + */ +int +cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane) +{ + log_debug("%s: %s", __func__, pane); + + /* Check for pane ids starting with %. */ + if (*pane == '%') { + fs->wp = window_pane_find_by_id_str(pane); + if (fs->wp == NULL) + return (-1); + fs->w = fs->wp->window; + return (cmd_find_best_winlink_with_window(fs)); + } + + /* Otherwise use the current window. */ + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + + /* Now we just need to look up the pane. */ + return (cmd_find_get_pane_with_window(fs, pane)); +} + +/* + * Find pane from string, assuming it is in the given window. Needs w, fills in + * wp. + */ +int +cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) +{ + const char *errstr; + int idx; + struct window_pane *wp; + u_int n; + + log_debug("%s: %s", __func__, pane); + + /* Check for pane ids starting with %. */ + if (*pane == '%') { + fs->wp = window_pane_find_by_id_str(pane); + if (fs->wp == NULL || fs->wp->window != fs->w) + return (-1); + return (0); + } + + /* Try special characters. */ + if (strcmp(pane, "!") == 0) { + if (fs->w->last == NULL) + return (-1); + fs->wp = fs->w->last; + return (0); + } else if (strcmp(pane, "{up}") == 0) { + fs->wp = window_pane_find_up(fs->w->active); + if (fs->wp == NULL) + return (-1); + return (0); + } else if (strcmp(pane, "{down}") == 0) { + fs->wp = window_pane_find_down(fs->w->active); + if (fs->wp == NULL) + return (-1); + return (0); + } else if (strcmp(pane, "{left}") == 0) { + fs->wp = window_pane_find_left(fs->w->active); + if (fs->wp == NULL) + return (-1); + return (0); + } else if (strcmp(pane, "{right}") == 0) { + fs->wp = window_pane_find_right(fs->w->active); + if (fs->wp == NULL) + return (-1); + return (0); + } + + /* Try as an offset. */ + if (pane[0] == '+' || pane[0] == '-') { + if (pane[1] != '\0') + n = strtonum(pane + 1, 1, INT_MAX, NULL); + else + n = 1; + wp = fs->w->active; + if (pane[0] == '+') + fs->wp = window_pane_next_by_number(fs->w, wp, n); + else + fs->wp = window_pane_previous_by_number(fs->w, wp, n); + if (fs->wp != NULL) + return (0); + } + + /* Get pane by index. */ + idx = strtonum(pane, 0, INT_MAX, &errstr); + if (errstr == NULL) { + fs->wp = window_pane_at_index(fs->w, idx); + if (fs->wp != NULL) + return (0); + } + + /* Try as a description. */ + fs->wp = window_find_string(fs->w, pane); + if (fs->wp != NULL) + return (0); + + return (-1); +} + +/* Clear state. */ +void +cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) +{ + memset (fs, 0, sizeof *fs); + + fs->cmdq = cmdq; + fs->flags = flags; + + fs->idx = -1; +} + +/* Split target into pieces and resolve for the given type. */ +struct cmd_find_state * +cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, + int flags) +{ + static struct cmd_find_state fs, current; + struct mouse_event *m; + char *colon, *period, *copy = NULL; + const char *session, *window, *pane; + + /* Find current state. */ + cmd_find_clear_state(¤t, cmdq, flags); + if (cmd_find_current_session(¤t) != 0) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no current session"); + goto error; + } + + /* Clear new state. */ + cmd_find_clear_state(&fs, cmdq, flags); + fs.current = ¤t; + + /* An empty or NULL target is the current. */ + if (target == NULL || *target == '\0') + goto current; + + /* Mouse target is a plain = or {mouse}. */ + if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { + m = &cmdq->item->mouse; + switch (type) { + case CMD_FIND_PANE: + fs.wp = cmd_mouse_pane(m, &fs.s, &fs.wl); + if (fs.wp != NULL) + fs.w = fs.wl->window; + break; + case CMD_FIND_WINDOW: + case CMD_FIND_SESSION: + fs.wl = cmd_mouse_window(m, &fs.s); + if (fs.wl != NULL) { + fs.w = fs.wl->window; + fs.wp = fs.w->active; + } + break; + } + if (fs.wp == NULL) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no mouse target"); + goto error; + } + return (&fs); + } + copy = xstrdup(target); + + /* Find separators if they exist. */ + colon = strchr(copy, ':'); + if (colon != NULL) + *colon++ = '\0'; + if (colon == NULL) + period = strchr(copy, '.'); + else + period = strchr(colon, '.'); + if (period != NULL) + *period++ = '\0'; + + /* Set session, window and pane parts. */ + session = window = pane = NULL; + if (colon != NULL && period != NULL) { + session = copy; + window = colon; + pane = period; + } else if (colon != NULL && period == NULL) { + session = copy; + window = colon; + } else if (colon == NULL && period != NULL) { + window = copy; + pane = period; + } else { + if (*copy == '$') + session = copy; + else if (*copy == '@') + window = copy; + else if (*copy == '%') + pane = copy; + else { + switch (type) { + case CMD_FIND_SESSION: + session = copy; + break; + case CMD_FIND_WINDOW: + window = copy; + break; + case CMD_FIND_PANE: + pane = copy; + break; + } + } + } + + /* Empty is the same as NULL. */ + if (session != NULL && *session == '\0') + session = NULL; + if (window != NULL && *window == '\0') + window = NULL; + if (pane != NULL && *pane == '\0') + pane = NULL; + + /* Map though conversion table. */ + if (session != NULL) + session = cmd_find_map_table(cmd_find_session_table, session); + if (window != NULL) + window = cmd_find_map_table(cmd_find_window_table, window); + if (pane != NULL) + pane = cmd_find_map_table(cmd_find_pane_table, pane); + + log_debug("target %s (flags %#x): session=%s, window=%s, pane=%s", + target, flags, session == NULL ? "none" : session, + window == NULL ? "none" : window, pane == NULL ? "none" : pane); + + /* No pane is allowed if want an index. */ + if (pane != NULL && (flags & CMD_FIND_WINDOW_INDEX)) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "can't specify pane here"); + goto error; + } + + /* If the session isn't NULL, look it up. */ + if (session != NULL) { + /* This will fill in session. */ + if (cmd_find_get_session(&fs, session) != 0) + goto no_session; + + /* If window and pane are NULL, use that session's current. */ + if (window == NULL && pane == NULL) { + fs.wl = fs.s->curw; + fs.idx = -1; + fs.w = fs.wl->window; + fs.wp = fs.w->active; + goto found; + } + + /* If window is present but pane not, find window in session. */ + if (window != NULL && pane == NULL) { + /* This will fill in winlink and window. */ + if (cmd_find_get_window_with_session(&fs, window) != 0) + goto no_window; + if (~flags & CMD_FIND_WINDOW_INDEX) + fs.wp = fs.wl->window->active; + goto found; + } + + /* If pane is present but window not, find pane. */ + if (window == NULL && pane != NULL) { + /* This will fill in winlink and window and pane. */ + if (cmd_find_get_pane_with_session(&fs, pane) != 0) + goto no_pane; + goto found; + } + + /* + * If window and pane are present, find both in session. This + * will fill in winlink and window. + */ + if (cmd_find_get_window_with_session(&fs, window) != 0) + goto no_window; + /* This will fill in pane. */ + if (cmd_find_get_pane_with_window(&fs, pane) != 0) + goto no_pane; + goto found; + } + + /* No session. If window and pane, try them. */ + if (window != NULL && pane != NULL) { + /* This will fill in session, winlink and window. */ + if (cmd_find_get_window(&fs, window) != 0) + goto no_window; + /* This will fill in pane. */ + if (cmd_find_get_pane_with_window(&fs, pane) != 0) + goto no_pane; + goto found; + } + + /* If just window is present, try it. */ + if (window != NULL && pane == NULL) { + /* This will fill in session, winlink and window. */ + if (cmd_find_get_window(&fs, window) != 0) + goto no_window; + if (~flags & CMD_FIND_WINDOW_INDEX) + fs.wp = fs.wl->window->active; + goto found; + } + + /* If just pane is present, try it. */ + if (window == NULL && pane != NULL) { + /* This will fill in session, winlink, window and pane. */ + if (cmd_find_get_pane(&fs, pane) != 0) + goto no_pane; + goto found; + } + +current: + /* None is the current session. */ + free(copy); + if (flags & CMD_FIND_WINDOW_INDEX) + current.idx = -1; + return (¤t); + +error: + free(copy); + return (NULL); + +found: + free(copy); + return (&fs); + +no_session: + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "can't find session %s", session); + goto error; + +no_window: + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "can't find window %s", window); + goto error; + +no_pane: + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "can't find pane %s", pane); + goto error; +} + +/* Log the result. */ +void +cmd_find_log_state(const char *f, const char *target, struct cmd_find_state *fs) +{ + log_debug("%s: target %s%s", f, target == NULL ? "none" : target, + fs != NULL ? "" : " (failed)"); + if (fs == NULL) + return; + if (fs->s != NULL) + log_debug("\ts=$%u", fs->s->id); + else + log_debug("\ts=none"); + if (fs->wl != NULL) { + log_debug("\twl=%u %d w=@%u %s", fs->wl->idx, + fs->wl->window == fs->w, fs->w->id, fs->w->name); + } else + log_debug("\twl=none"); + if (fs->wp != NULL) + log_debug("\twp=%%%u", fs->wp->id); + else + log_debug("\twp=none"); + if (fs->idx != -1) + log_debug("\tidx=%d", fs->idx); + else + log_debug("\tidx=none"); +} + +/* Find the current session. */ +struct session * +cmd_find_current(struct cmd_q *cmdq) +{ + struct cmd_find_state *fs; + int flags = CMD_FIND_QUIET; + + fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, flags); + cmd_find_log_state(__func__, NULL, fs); + if (fs == NULL) + return (NULL); + + return (fs->s); +} + +/* Find the target session or report an error and return NULL. */ +struct session * +cmd_find_session(struct cmd_q *cmdq, const char *target, int prefer_unattached) +{ + struct cmd_find_state *fs; + int flags = 0; + + if (prefer_unattached) + flags |= CMD_FIND_PREFER_UNATTACHED; + + fs = cmd_find_target(cmdq, target, CMD_FIND_SESSION, flags); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + return (fs->s); +} + +/* Find the target window or report an error and return NULL. */ +struct winlink * +cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) +{ + struct cmd_find_state *fs; + + fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, 0); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + if (sp != NULL) + *sp = fs->s; + return (fs->wl); +} + +/* Find the target pane and report an error and return NULL. */ +struct winlink * +cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, + struct window_pane **wpp) +{ + struct cmd_find_state *fs; + + fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, 0); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + if (sp != NULL) + *sp = fs->s; + if (wpp != NULL) + *wpp = fs->wp; + return (fs->wl); +} + +/* Find the target client or report an error and return NULL. */ +struct client * +cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) +{ + struct client *c; + char *copy; + size_t size; + const char *path; + + /* A NULL argument means the current client. */ + if (target == NULL) { + c = cmd_find_current_client(cmdq); + if (c == NULL && !quiet) + cmdq_error(cmdq, "no current client"); + return (c); + } + copy = xstrdup(target); + + /* Trim a single trailing colon if any. */ + size = strlen(copy); + if (size != 0 && copy[size - 1] == ':') + copy[size - 1] = '\0'; + + /* Check path of each client. */ + TAILQ_FOREACH(c, &clients, entry) { + if (c->session == NULL || c->tty.path == NULL) + continue; + path = c->tty.path; + + /* Try for exact match. */ + if (strcmp(copy, path) == 0) + break; + + /* Try without leading /dev. */ + if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) + continue; + if (strcmp(copy, path + (sizeof _PATH_DEV) - 1) == 0) + break; + } + + /* If no client found, report an error. */ + if (c == NULL && !quiet) + cmdq_error(cmdq, "can't find client %s", copy); + + free(copy); + return (c); +} + +/* + * Find the target session and window index, whether or not it exists in the + * session. Return -2 on error or -1 if no window index is specified. This is + * used when parsing an argument for a window target that may not exist (for + * example if it is going to be created). + */ +int +cmd_find_index(struct cmd_q *cmdq, const char *target, struct session **sp) +{ + struct cmd_find_state *fs; + int flags = CMD_FIND_WINDOW_INDEX; + + fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (-2); + + if (sp != NULL) + *sp = fs->s; + return (fs->idx); +} diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 785a7011..3a26db39 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -72,7 +72,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((s = cmd_current_session(cmdq, 0)) != NULL) + else if ((s = cmd_find_current(cmdq)) != NULL) cwd = s->cwd; else cwd = AT_FDCWD; diff --git a/cmd-new-session.c b/cmd-new-session.c index ec292fa8..199e82c2 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -137,7 +137,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) cwd = fd; } else if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((c0 = cmd_current_client(cmdq)) != NULL) + else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) cwd = c0->session->cwd; else { fd = open(".", O_RDONLY); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 368c5178..62c3989e 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -93,7 +93,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((s = cmd_current_session(cmdq, 0)) != NULL) + else if ((s = cmd_find_current(cmdq)) != NULL) cwd = s->cwd; else cwd = AT_FDCWD; diff --git a/cmd.c b/cmd.c index ea1a29d0..e8ce932d 100644 --- a/cmd.c +++ b/cmd.c @@ -116,24 +116,6 @@ const struct cmd_entry *cmd_table[] = { NULL }; -ARRAY_DECL(client_list, struct client *); -ARRAY_DECL(sessionslist, struct session *); - -int cmd_session_better(struct session *, struct session *, int); -struct session *cmd_choose_session_list(struct sessionslist *); -struct session *cmd_choose_session(int); -struct client *cmd_choose_client(struct client_list *); -struct client *cmd_lookup_client(const char *); -struct session *cmd_lookup_session(struct cmd_q *, const char *, int *); -struct winlink *cmd_lookup_window(struct session *, const char *, int *); -int cmd_lookup_index(struct session *, const char *, int *); -struct winlink *cmd_lookup_winlink_windowid(struct session *, const char *); -struct session *cmd_window_session(struct cmd_q *, struct window *, - struct winlink **); -struct winlink *cmd_find_window_offset(const char *, struct session *, int *); -int cmd_find_index_offset(const char *, struct session *, int *); -struct window_pane *cmd_find_pane_offset(const char *, struct winlink *); - int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { @@ -332,183 +314,6 @@ cmd_print(struct cmd *cmd, char *buf, size_t len) return (off); } -/* - * Figure out the current session. Use: 1) the current session, if the command - * context has one; 2) the most recently used session containing the pty of the - * calling client, if any; 3) the session specified in the TMUX variable from - * the environment (as passed from the client); 4) the most recently used - * session from all sessions. - */ -struct session * -cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) -{ - struct client *c = cmdq->client; - struct session *s; - struct sessionslist ss; - struct winlink *wl; - struct window_pane *wp; - const char *path; - int found; - - /* Try the queue session. */ - if (c != NULL && c->session != NULL) - return (c->session); - - /* - * If the name of the calling client's pty is known, build a list of - * the sessions that contain it and if any choose either the first or - * the newest. - */ - path = c == NULL ? NULL : c->tty.path; - if (path != NULL) { - ARRAY_INIT(&ss); - RB_FOREACH(s, sessions, &sessions) { - found = 0; - RB_FOREACH(wl, winlinks, &s->windows) { - TAILQ_FOREACH(wp, &wl->window->panes, entry) { - if (strcmp(wp->tty, path) == 0) { - found = 1; - break; - } - } - if (found) - break; - } - if (found) - ARRAY_ADD(&ss, s); - } - - s = cmd_choose_session_list(&ss); - ARRAY_FREE(&ss); - if (s != NULL) - return (s); - } - - return (cmd_choose_session(prefer_unattached)); -} - -/* Is this session better? */ -int -cmd_session_better(struct session *s, struct session *best, - int prefer_unattached) -{ - if (best == NULL) - return (1); - if (prefer_unattached) { - if (!(best->flags & SESSION_UNATTACHED) && - (s->flags & SESSION_UNATTACHED)) - return (1); - else if ((best->flags & SESSION_UNATTACHED) && - !(s->flags & SESSION_UNATTACHED)) - return (0); - } - return (timercmp(&s->activity_time, &best->activity_time, >)); -} - -/* - * Find the most recently used session, preferring unattached if the flag is - * set. - */ -struct session * -cmd_choose_session(int prefer_unattached) -{ - struct session *s, *best; - - best = NULL; - RB_FOREACH(s, sessions, &sessions) { - if (cmd_session_better(s, best, prefer_unattached)) - best = s; - } - return (best); -} - -/* Find the most recently used session from a list. */ -struct session * -cmd_choose_session_list(struct sessionslist *ss) -{ - struct session *s, *sbest; - struct timeval *tv = NULL; - u_int i; - - sbest = NULL; - for (i = 0; i < ARRAY_LENGTH(ss); i++) { - if ((s = ARRAY_ITEM(ss, i)) == NULL) - continue; - - if (tv == NULL || timercmp(&s->activity_time, tv, >)) { - sbest = s; - tv = &s->activity_time; - } - } - - return (sbest); -} - -/* - * Find the current client. First try the current client if set, then pick the - * most recently used of the clients attached to the current session if any, - * then of all clients. - */ -struct client * -cmd_current_client(struct cmd_q *cmdq) -{ - struct session *s; - struct client *c; - struct client_list cc; - - if (cmdq->client != NULL && cmdq->client->session != NULL) - return (cmdq->client); - - /* - * No current client set. Find the current session and return the - * newest of its clients. - */ - s = cmd_current_session(cmdq, 0); - if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { - ARRAY_INIT(&cc); - TAILQ_FOREACH(c, &clients, entry) { - if (s == c->session) - ARRAY_ADD(&cc, c); - } - - c = cmd_choose_client(&cc); - ARRAY_FREE(&cc); - if (c != NULL) - return (c); - } - - ARRAY_INIT(&cc); - TAILQ_FOREACH(c, &clients, entry) - ARRAY_ADD(&cc, c); - c = cmd_choose_client(&cc); - ARRAY_FREE(&cc); - return (c); -} - -/* Choose the most recently used client from a list. */ -struct client * -cmd_choose_client(struct client_list *cc) -{ - struct client *c, *cbest; - struct timeval *tv = NULL; - u_int i; - - cbest = NULL; - for (i = 0; i < ARRAY_LENGTH(cc); i++) { - if ((c = ARRAY_ITEM(cc, i)) == NULL) - continue; - if (c->session == NULL) - continue; - - if (tv == NULL || timercmp(&c->activity_time, tv, >)) { - cbest = c; - tv = &c->activity_time; - } - } - - return (cbest); -} - /* Adjust current mouse position for a pane. */ int cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, @@ -560,7 +365,8 @@ cmd_mouse_window(struct mouse_event *m, struct session **sp) /* Get current mouse pane if any. */ struct window_pane * -cmd_mouse_pane(struct mouse_event *m, struct session **sp, struct winlink **wlp) +cmd_mouse_pane(struct mouse_event *m, struct session **sp, + struct winlink **wlp) { struct winlink *wl; struct window_pane *wp; @@ -577,745 +383,6 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp, struct winlink **wlp) return (wp); } -/* Find the target client or report an error and return NULL. */ -struct client * -cmd_find_client(struct cmd_q *cmdq, const char *arg, int quiet) -{ - struct client *c; - char *tmparg; - size_t arglen; - - /* A NULL argument means the current client. */ - if (arg == NULL) { - c = cmd_current_client(cmdq); - if (c == NULL && !quiet) - cmdq_error(cmdq, "no clients"); - return (c); - } - tmparg = xstrdup(arg); - - /* Trim a single trailing colon if any. */ - arglen = strlen(tmparg); - if (arglen != 0 && tmparg[arglen - 1] == ':') - tmparg[arglen - 1] = '\0'; - - /* Find the client, if any. */ - c = cmd_lookup_client(tmparg); - - /* If no client found, report an error. */ - if (c == NULL && !quiet) - cmdq_error(cmdq, "client not found: %s", tmparg); - - free(tmparg); - return (c); -} - -/* - * Lookup a client by device path. Either of a full match and a match without a - * leading _PATH_DEV ("/dev/") is accepted. - */ -struct client * -cmd_lookup_client(const char *name) -{ - struct client *c; - const char *path; - - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || c->tty.path == NULL) - continue; - path = c->tty.path; - - /* Check for exact matches. */ - if (strcmp(name, path) == 0) - return (c); - - /* Check without leading /dev if present. */ - if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) - continue; - if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0) - return (c); - } - - return (NULL); -} - -/* Lookup a session by name. If no session is found, NULL is returned. */ -struct session * -cmd_lookup_session(struct cmd_q *cmdq, const char *name, int *ambiguous) -{ - struct session *s, *sfound; - struct window *w; - struct window_pane *wp; - - *ambiguous = 0; - - /* Look for $id first. */ - if ((s = session_find_by_id_str(name)) != NULL) - return (s); - - /* Try as pane or window id. */ - if ((wp = window_pane_find_by_id_str(name)) != NULL) - return (cmd_window_session(cmdq, wp->window, NULL)); - if ((w = window_find_by_id_str(name)) != NULL) - return (cmd_window_session(cmdq, w, NULL)); - - /* - * Look for matches. First look for exact matches - session names must - * be unique so an exact match can't be ambigious and can just be - * returned. - */ - if ((s = session_find(name)) != NULL) - return (s); - - /* - * Otherwise look for partial matches, returning early if it is found to - * be ambiguous. - */ - sfound = NULL; - RB_FOREACH(s, sessions, &sessions) { - if (strncmp(name, s->name, strlen(name)) == 0 || - fnmatch(name, s->name, 0) == 0) { - if (sfound != NULL) { - *ambiguous = 1; - return (NULL); - } - sfound = s; - } - } - return (sfound); -} - -/* - * Lookup a window or return -1 if not found or ambigious. First try as an - * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in - * idx if the window index is a valid number but there is no window with that - * index. - */ -struct winlink * -cmd_lookup_window(struct session *s, const char *name, int *ambiguous) -{ - struct winlink *wl, *wlfound; - struct window *w; - struct window_pane *wp; - const char *errstr; - u_int idx; - - *ambiguous = 0; - - /* Try as pane or window id. */ - if ((wl = cmd_lookup_winlink_windowid(s, name)) != NULL) - return (wl); - - /* Lookup as pane or window id. */ - if ((wp = window_pane_find_by_id_str(name)) != NULL) { - wl = winlink_find_by_window(&s->windows, wp->window); - if (wl != NULL) - return (wl); - } - if ((w = window_find_by_id_str(name)) != NULL) { - wl = winlink_find_by_window(&s->windows, w); - if (wl != NULL) - return (wl); - } - - /* First see if this is a valid window index in this session. */ - idx = strtonum(name, 0, INT_MAX, &errstr); - if (errstr == NULL) { - if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL) - return (wl); - } - - /* Look for exact matches, error if more than one. */ - wlfound = NULL; - RB_FOREACH(wl, winlinks, &s->windows) { - if (strcmp(name, wl->window->name) == 0) { - if (wlfound != NULL) { - *ambiguous = 1; - return (NULL); - } - wlfound = wl; - } - } - if (wlfound != NULL) - return (wlfound); - - /* Now look for pattern matches, again error if multiple. */ - wlfound = NULL; - RB_FOREACH(wl, winlinks, &s->windows) { - if (strncmp(name, wl->window->name, strlen(name)) == 0 || - fnmatch(name, wl->window->name, 0) == 0) { - if (wlfound != NULL) { - *ambiguous = 1; - return (NULL); - } - wlfound = wl; - } - } - if (wlfound != NULL) - return (wlfound); - - return (NULL); -} - -/* - * Find a window index - if the window doesn't exist, check if it is a - * potential index and return it anyway. - */ -int -cmd_lookup_index(struct session *s, const char *name, int *ambiguous) -{ - struct winlink *wl; - const char *errstr; - u_int idx; - - idx = strtonum(name, 0, INT_MAX, &errstr); - if (errstr == NULL) - return (idx); - - if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL) - return (wl->idx); - if (*ambiguous) - return (-1); - - return (-1); -} - -/* Lookup window id in a session. An initial @ means a window id. */ -struct winlink * -cmd_lookup_winlink_windowid(struct session *s, const char *arg) -{ - const char *errstr; - u_int windowid; - - if (*arg != '@') - return (NULL); - - windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr); - if (errstr != NULL) - return (NULL); - return (winlink_find_by_window_id(&s->windows, windowid)); -} - -/* Find session and winlink for window. */ -struct session * -cmd_window_session(struct cmd_q *cmdq, struct window *w, struct winlink **wlp) -{ - struct session *s; - struct sessionslist ss; - struct winlink *wl; - - /* If this window is in the current session, return that winlink. */ - s = cmd_current_session(cmdq, 0); - if (s != NULL) { - wl = winlink_find_by_window(&s->windows, w); - if (wl != NULL) { - if (wlp != NULL) - *wlp = wl; - return (s); - } - } - - /* Otherwise choose from all sessions with this window. */ - ARRAY_INIT(&ss); - RB_FOREACH(s, sessions, &sessions) { - if (winlink_find_by_window(&s->windows, w) != NULL) - ARRAY_ADD(&ss, s); - } - s = cmd_choose_session_list(&ss); - ARRAY_FREE(&ss); - if (wlp != NULL) - *wlp = winlink_find_by_window(&s->windows, w); - return (s); -} - -/* Find the target session or report an error and return NULL. */ -struct session * -cmd_find_session(struct cmd_q *cmdq, const char *arg, int prefer_unattached) -{ - struct session *s; - struct client *c; - char *tmparg; - size_t arglen; - int ambiguous; - - /* A NULL argument means the current session. */ - if (arg == NULL) { - if ((s = cmd_current_session(cmdq, prefer_unattached)) == NULL) - cmdq_error(cmdq, "can't establish current session"); - return (s); - } - - /* Trim a single trailing colon if any. */ - tmparg = xstrdup(arg); - arglen = strlen(tmparg); - if (arglen != 0 && tmparg[arglen - 1] == ':') - tmparg[arglen - 1] = '\0'; - - /* An empty session name is the current session. */ - if (*tmparg == '\0') { - free(tmparg); - if ((s = cmd_current_session(cmdq, prefer_unattached)) == NULL) - cmdq_error(cmdq, "can't establish current session"); - return (s); - } - - /* Find the session, if any. */ - s = cmd_lookup_session(cmdq, tmparg, &ambiguous); - - /* If it doesn't, try to match it as a client. */ - if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL) - s = c->session; - - /* If no session found, report an error. */ - if (s == NULL) { - if (ambiguous) - cmdq_error(cmdq, "more than one session: %s", tmparg); - else - cmdq_error(cmdq, "session not found: %s", tmparg); - } - - free(tmparg); - return (s); -} - -/* Find the target session and window or report an error and return NULL. */ -struct winlink * -cmd_find_window(struct cmd_q *cmdq, const char *arg, struct session **sp) -{ - struct session *s; - struct winlink *wl; - const char *winptr; - char *sessptr = NULL; - int ambiguous = 0; - - /* - * Find the current session. There must always be a current session, if - * it can't be found, report an error. - */ - if ((s = cmd_current_session(cmdq, 0)) == NULL) { - cmdq_error(cmdq, "can't establish current session"); - return (NULL); - } - - /* A NULL argument means the current session and window. */ - if (arg == NULL) { - if (sp != NULL) - *sp = s; - return (s->curw); - } - - /* Time to look at the argument. If it is empty, that is an error. */ - if (*arg == '\0') - goto not_found; - - /* Find the separating colon and split into window and session. */ - winptr = strchr(arg, ':'); - if (winptr == NULL) - goto no_colon; - winptr++; /* skip : */ - sessptr = xstrdup(arg); - *strchr(sessptr, ':') = '\0'; - - /* Try to lookup the session if present. */ - if (*sessptr != '\0') { - if ((s = cmd_lookup_session(cmdq, sessptr, &ambiguous)) == NULL) - goto no_session; - } - if (sp != NULL) - *sp = s; - - /* - * Then work out the window. An empty string is the current window, - * otherwise try special cases then to look it up in the session. - */ - if (*winptr == '\0') - wl = s->curw; - else if (winptr[0] == '!' && winptr[1] == '\0') - wl = TAILQ_FIRST(&s->lastw); - else if (winptr[0] == '^' && winptr[1] == '\0') - wl = RB_MIN(winlinks, &s->windows); - else if (winptr[0] == '$' && winptr[1] == '\0') - wl = RB_MAX(winlinks, &s->windows); - else if (winptr[0] == '+' || winptr[0] == '-') - wl = cmd_find_window_offset(winptr, s, &ambiguous); - else - wl = cmd_lookup_window(s, winptr, &ambiguous); - if (wl == NULL) - goto not_found; - - if (sessptr != NULL) - free(sessptr); - return (wl); - -no_colon: - /* - * No colon in the string, first try special cases, then as a window - * and lastly as a session. - */ - if (arg[0] == '=' && arg[1] == '\0') { - if ((wl = cmd_mouse_window(&cmdq->item->mouse, &s)) == NULL) { - cmdq_error(cmdq, "no mouse target"); - goto error; - } - } else if (arg[0] == '!' && arg[1] == '\0') { - if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) - goto not_found; - } else if (arg[0] == '+' || arg[0] == '-') { - if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL) - goto lookup_session; - } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL) - goto lookup_session; - - if (sp != NULL) - *sp = s; - - return (wl); - -lookup_session: - if (ambiguous) - goto not_found; - if (*arg != '\0' && - (s = cmd_lookup_session(cmdq, arg, &ambiguous)) == NULL) - goto no_session; - - if (sp != NULL) - *sp = s; - - return (s->curw); - -no_session: - if (ambiguous) - cmdq_error(cmdq, "multiple sessions: %s", arg); - else - cmdq_error(cmdq, "session not found: %s", arg); - goto error; - -not_found: - if (ambiguous) - cmdq_error(cmdq, "multiple windows: %s", arg); - else - cmdq_error(cmdq, "window not found: %s", arg); - goto error; - -error: - free(sessptr); - return (NULL); -} - -struct winlink * -cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous) -{ - struct winlink *wl; - int offset = 1; - - if (winptr[1] != '\0') - offset = strtonum(winptr + 1, 1, INT_MAX, NULL); - if (offset == 0) - wl = cmd_lookup_window(s, winptr, ambiguous); - else { - if (winptr[0] == '+') - wl = winlink_next_by_number(s->curw, s, offset); - else - wl = winlink_previous_by_number(s->curw, s, offset); - } - - return (wl); -} - -/* - * Find the target session and window index, whether or not it exists in the - * session. Return -2 on error or -1 if no window index is specified. This is - * used when parsing an argument for a window target that may not exist (for - * example if it is going to be created). - */ -int -cmd_find_index(struct cmd_q *cmdq, const char *arg, struct session **sp) -{ - struct session *s; - struct winlink *wl; - const char *winptr; - char *sessptr = NULL; - int idx, ambiguous = 0; - - /* - * Find the current session. There must always be a current session, if - * it can't be found, report an error. - */ - if ((s = cmd_current_session(cmdq, 0)) == NULL) { - cmdq_error(cmdq, "can't establish current session"); - return (-2); - } - - /* A NULL argument means the current session and "no window" (-1). */ - if (arg == NULL) { - if (sp != NULL) - *sp = s; - return (-1); - } - - /* Time to look at the argument. If it is empty, that is an error. */ - if (*arg == '\0') - goto not_found; - - /* Find the separating colon. If none, assume the current session. */ - winptr = strchr(arg, ':'); - if (winptr == NULL) - goto no_colon; - winptr++; /* skip : */ - sessptr = xstrdup(arg); - *strchr(sessptr, ':') = '\0'; - - /* Try to lookup the session if present. */ - if (sessptr != NULL && *sessptr != '\0') { - if ((s = cmd_lookup_session(cmdq, sessptr, &ambiguous)) == NULL) - goto no_session; - } - if (sp != NULL) - *sp = s; - - /* - * Then work out the window. An empty string is a new window otherwise - * try to look it up in the session. - */ - if (*winptr == '\0') - idx = -1; - else if (winptr[0] == '!' && winptr[1] == '\0') { - if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) - goto not_found; - idx = wl->idx; - } else if (winptr[0] == '+' || winptr[0] == '-') { - if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0) - goto invalid_index; - } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1) - goto invalid_index; - - free(sessptr); - return (idx); - -no_colon: - /* - * No colon in the string, first try special cases, then as a window - * and lastly as a session. - */ - if (arg[0] == '!' && arg[1] == '\0') { - if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) - goto not_found; - idx = wl->idx; - } else if (arg[0] == '+' || arg[0] == '-') { - if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0) - goto lookup_session; - } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1) - goto lookup_session; - - if (sp != NULL) - *sp = s; - - return (idx); - -lookup_session: - if (ambiguous) - goto not_found; - if (*arg != '\0' && - (s = cmd_lookup_session(cmdq, arg, &ambiguous)) == NULL) - goto no_session; - - if (sp != NULL) - *sp = s; - - return (-1); - -no_session: - if (ambiguous) - cmdq_error(cmdq, "multiple sessions: %s", arg); - else - cmdq_error(cmdq, "session not found: %s", arg); - free(sessptr); - return (-2); - -invalid_index: - if (ambiguous) - goto not_found; - cmdq_error(cmdq, "invalid index: %s", arg); - - free(sessptr); - return (-2); - -not_found: - if (ambiguous) - cmdq_error(cmdq, "multiple windows: %s", arg); - else - cmdq_error(cmdq, "window not found: %s", arg); - free(sessptr); - return (-2); -} - -int -cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous) -{ - int idx, offset = 1; - - if (winptr[1] != '\0') - offset = strtonum(winptr + 1, 1, INT_MAX, NULL); - if (offset == 0) - idx = cmd_lookup_index(s, winptr, ambiguous); - else { - if (winptr[0] == '+') { - if (s->curw->idx == INT_MAX) - idx = cmd_lookup_index(s, winptr, ambiguous); - else - idx = s->curw->idx + offset; - } else { - if (s->curw->idx == 0) - idx = cmd_lookup_index(s, winptr, ambiguous); - else - idx = s->curw->idx - offset; - } - } - - return (idx); -} - -/* - * Find the target session, window and pane number or report an error and - * return NULL. The pane number is separated from the session:window by a ., - * such as mysession:mywindow.0. - */ -struct winlink * -cmd_find_pane(struct cmd_q *cmdq, - const char *arg, struct session **sp, struct window_pane **wpp) -{ - struct session *s; - struct winlink *wl; - const char *period, *errstr; - char *winptr, *paneptr; - u_int idx; - - /* Get the current session. */ - if ((s = cmd_current_session(cmdq, 0)) == NULL) { - cmdq_error(cmdq, "can't establish current session"); - return (NULL); - } - if (sp != NULL) - *sp = s; - - /* A NULL argument means the current session, window and pane. */ - if (arg == NULL) { - *wpp = s->curw->window->active; - return (s->curw); - } - - /* Lookup as pane id. */ - if ((*wpp = window_pane_find_by_id_str(arg)) != NULL) { - s = cmd_window_session(cmdq, (*wpp)->window, &wl); - if (sp != NULL) - *sp = s; - return (wl); - } - - /* Look for a separating period. */ - if ((period = strrchr(arg, '.')) == NULL) - goto no_period; - - /* Pull out the window part and parse it. */ - winptr = xstrdup(arg); - winptr[period - arg] = '\0'; - if (*winptr == '\0') - wl = s->curw; - else if ((wl = cmd_find_window(cmdq, winptr, sp)) == NULL) - goto error; - - /* Find the pane section and look it up. */ - paneptr = winptr + (period - arg) + 1; - if (*paneptr == '\0') - *wpp = wl->window->active; - else if (paneptr[0] == '+' || paneptr[0] == '-') - *wpp = cmd_find_pane_offset(paneptr, wl); - else if (paneptr[0] == '!' && paneptr[1] == '\0') { - if (wl->window->last == NULL) { - cmdq_error(cmdq, "no last pane"); - goto error; - } - *wpp = wl->window->last; - } else { - idx = strtonum(paneptr, 0, INT_MAX, &errstr); - if (errstr != NULL) - goto lookup_string; - *wpp = window_pane_at_index(wl->window, idx); - if (*wpp == NULL) - goto lookup_string; - } - - free(winptr); - return (wl); - -lookup_string: - /* Try pane string description. */ - if ((*wpp = window_find_string(wl->window, paneptr)) == NULL) { - cmdq_error(cmdq, "can't find pane: %s", paneptr); - goto error; - } - - free(winptr); - return (wl); - -no_period: - /* Check mouse event. */ - if (arg[0] == '=' && arg[1] == '\0') { - *wpp = cmd_mouse_pane(&cmdq->item->mouse, &s, &wl); - if (*wpp == NULL) { - cmdq_error(cmdq, "no mouse target"); - return (NULL); - } - if (sp != NULL) - *sp = s; - return (wl); - } - - /* Try as a pane number alone. */ - idx = strtonum(arg, 0, INT_MAX, &errstr); - if (errstr != NULL) - goto lookup_window; - - /* Try index in the current session and window. */ - if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL) - goto lookup_window; - - return (s->curw); - -lookup_window: - /* Try pane string description. */ - if ((*wpp = window_find_string(s->curw->window, arg)) != NULL) - return (s->curw); - - /* Try as a window and use the active pane. */ - if ((wl = cmd_find_window(cmdq, arg, sp)) != NULL) - *wpp = wl->window->active; - return (wl); - -error: - free(winptr); - return (NULL); -} - -struct window_pane * -cmd_find_pane_offset(const char *paneptr, struct winlink *wl) -{ - struct window *w = wl->window; - struct window_pane *wp = w->active; - u_int offset = 1; - - if (paneptr[1] != '\0') - offset = strtonum(paneptr + 1, 1, INT_MAX, NULL); - if (offset > 0) { - if (paneptr[0] == '+') - wp = window_pane_next_by_number(w, wp, offset); - else - wp = window_pane_previous_by_number(w, wp, offset); - } - - return (wp); -} - /* Replace the first %% or %idx in template by s. */ char * cmd_template_replace(const char *template, const char *s, int idx) diff --git a/tmux.1 b/tmux.1 index 68e9b9de..cd7ec393 100644 --- a/tmux.1 +++ b/tmux.1 @@ -358,8 +358,9 @@ argument with one of or .Ar target-pane . These specify the client, session, window or pane which a command should affect. +.Pp .Ar target-client -is the name of the +should be the name of the .Xr pty 4 file to which the client is connected, for example either of .Pa /dev/ttyp1 @@ -367,27 +368,35 @@ or .Pa ttyp1 for the client attached to .Pa /dev/ttyp1 . -If no client is specified, the current client is chosen, if possible, or an -error is reported. +If no client is specified, +.Nm +attempts to work out the client currently in use; if that fails, an error is +reported. Clients may be listed with the .Ic list-clients command. .Pp .Ar target-session -is the session id prefixed with a $, the name of a session (as listed by the +is tried as, in order: +.Bl -enum -offset Ds +.It +A session ID prefixed with a $. +.It +An exact name of a session (as listed by the .Ic list-sessions -command), or the name of a client with the same syntax as -.Ar target-client , -in which case the session attached to the client is used. -When looking for the session name, -.Nm -initially searches for an exact match; if none is found, the session names -are checked for any for which -.Ar target-session -is a prefix or for which it matches as an +command). +.It +The start of a session name, for example +.Ql mysess +would match a session named +.Ql mysession . +.It +An .Xr fnmatch 3 -pattern. -If a single match is found, it is used as the target session; multiple matches +pattern which is matched against the session name. +.El +.Pp +If a single session is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no current session is available, the most recently used is chosen. @@ -400,12 +409,29 @@ follows the same rules as for .Ar target-session , and .Em window -is looked for in order: as a window index, for example mysession:1; -as a window ID, such as @1; -as an exact window name, such as mysession:mywindow; then as an +is looked for in order as: +.Bl -enum -offset Ds +.It +A special token, listed below. +.It +A window index, for example +.Ql mysession:1 +is window 1 in session +.Ql mysession . +.It +A window ID, such as @1. +.It +An exact window name, such as +.Ql mysession:mywindow . +.It +The start of a window name, such as +.Ql mysession:mywin . +.It +As an .Xr fnmatch 3 -pattern or the start of a window name, such as mysession:mywin* or -mysession:mywin. +pattern matched against the window name. +.El +.Pp An empty window name specifies the next unused index if appropriate (for example the .Ic new-window @@ -415,53 +441,50 @@ commands) otherwise the current window in .Em session is chosen. -The special character -.Ql \&! -uses the last (previously current) window, -.Ql ^ -selects the highest numbered window, -.Ql $ -selects the lowest numbered window, and -.Ql + -and -.Ql - -select the next window or the previous window by number. -When the argument does not contain a colon, -.Nm -first attempts to parse it as window; if that fails, an attempt is made to -match a session. +.Pp +The following special tokens are available to indicate particular windows. Each +has a single-character alternative form. +.Bl -column "XXXXXXXXXX" "X" +.It Sy "Token" Ta Sy "" Ta Sy "Meaning" +.It Li "{start}" Ta "^" Ta "The lowest-numbered window" +.It Li "{end}" Ta "$" Ta "The highest-numbered window" +.It Li "{last}" Ta "!" Ta "The last (previously current) window" +.It Li "{next}" Ta "+" Ta "The next window by number" +.It Li "{previous}" Ta "-" Ta "The previous window by number" +.It Li "{mouse}" Ta "=" Ta "The window where the mouse event happened" +.El .Pp .Ar target-pane -takes a similar form to +may be a +pane ID or takes a similar form to .Ar target-window -but with the optional addition of a period followed by a pane index, for -example: mysession:mywindow.1. +but with the optional addition of a period followed by a pane index or pane ID, +for example: +.Ql mysession:mywindow.1 . If the pane index is omitted, the currently active pane in the specified window is used. -If neither a colon nor period appears, -.Nm -first attempts to use the argument as a pane index; if that fails, it is looked -up as for -.Ar target-window . -A -.Ql + , -.Ql - -or -.Ql \&! -indicate the next, previous or last pane. -One of the strings -.Em top , -.Em bottom , -.Em left , -.Em right , -.Em top-left , -.Em top-right , -.Em bottom-left -or -.Em bottom-right -may be used instead of a pane index. +The following special tokens are available for the pane index: +.Bl -column "XXXXXXXXXXXXXX" "X" +.It Sy "Token" Ta Sy "" Ta Sy "Meaning" +.It Li "{last}" Ta "!" Ta "The last (previously active) pane" +.It Li "{next}" Ta "+" Ta "The next pane by number" +.It Li "{previous}" Ta "-" Ta "The previous pane by number" +.It Li "{top}" Ta "" Ta "The top pane" +.It Li "{bottom}" Ta "" Ta "The bottom pane" +.It Li "{left}" Ta "" Ta "The leftmost pane" +.It Li "{right}" Ta "" Ta "The rightmost pane" +.It Li "{top-left}" Ta "" Ta "The top-left pane" +.It Li "{top-right}" Ta "" Ta "The top-right pane" +.It Li "{bottom-left}" Ta "" Ta "The bottom-left pane" +.It Li "{bottom-right}" Ta "" Ta "The bottom-right pane" +.It Li "{up}" Ta "" Ta "The pane above the active pane" +.It Li "{down}" Ta "" Ta "The pane below the active pane" +.It Li "{left}" Ta "" Ta "The pane to the left of the active pane" +.It Li "{right}" Ta "" Ta "The pane to the right of the active pane" +.It Li "{mouse}" Ta "=" Ta "The pane where the mouse event happened" +.El .Pp -The special characters +The tokens .Ql + and .Ql - @@ -470,19 +493,34 @@ may be followed by an offset, for example: select-window -t:+2 .Ed .Pp -When dealing with a session that doesn't contain sequential window indexes, -they will be correctly skipped. -.Pp +Sessions, window and panes are each numbered with a unique ID; session IDs are +prefixed with a +.Ql $ , +windows with a +.Ql @ , +and panes with a +.Ql % . +These are unique and are unchanged for the life of the session, window or pane +in the .Nm -also gives each pane created in a server an identifier consisting of a -.Ql % -and a number, starting from zero. -A pane's identifier is unique for the life of the -.Nm -server and is passed to the child process of the pane in the +server. +The pane ID is passed to the child process of the pane in the .Ev TMUX_PANE environment variable. -It may be used alone to target a pane or the window containing it. +IDs may be displayed using the +.Ql session_id , +.Ql window_id , +or +.Ql pane_id +formats (see the +.Sx FORMATS +section) and the +.Ic display-message , +.Ic list-sessions , +.Ic list-windows +or +.Ic list-panes +commands. .Pp .Ar shell-command arguments are @@ -3144,7 +3182,9 @@ The following mouse events are available: Each should be suffixed with a location, for example .Ql MouseDown1Status . .Pp -The special character +The special token +.Ql {mouse} +or .Ql = may be used as .Ar target-window diff --git a/tmux.h b/tmux.h index 170ad692..ee36f4bb 100644 --- a/tmux.h +++ b/tmux.h @@ -1740,8 +1740,19 @@ size_t args_print(struct args *, char *, size_t); int args_has(struct args *, u_char); void args_set(struct args *, u_char, const char *); const char *args_get(struct args *, u_char); -long long args_strtonum( - struct args *, u_char, long long, long long, char **); +long long args_strtonum(struct args *, u_char, long long, long long, + char **); + +/* cmd-find.c */ +struct session *cmd_find_current(struct cmd_q *); +struct session *cmd_find_session(struct cmd_q *, const char *, int); +struct winlink *cmd_find_window(struct cmd_q *, const char *, + struct session **); +struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, + struct window_pane **); +struct client *cmd_find_client(struct cmd_q *, const char *, int); +int cmd_find_index(struct cmd_q *, const char *, + struct session **); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); @@ -1756,16 +1767,6 @@ int cmd_mouse_at(struct window_pane *, struct mouse_event *, struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, struct winlink **); -struct session *cmd_current_session(struct cmd_q *, int); -struct client *cmd_current_client(struct cmd_q *); -struct client *cmd_find_client(struct cmd_q *, const char *, int); -struct session *cmd_find_session(struct cmd_q *, const char *, int); -struct winlink *cmd_find_window(struct cmd_q *, const char *, - struct session **); -int cmd_find_index(struct cmd_q *, const char *, - struct session **); -struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, - struct window_pane **); char *cmd_template_replace(const char *, const char *, int); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; From 91f6347485a2efe714cf0bc50e4d1fbc2a33c01e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Apr 2015 22:42:10 +0000 Subject: [PATCH 113/703] Assign to the right variable when comparing clients. --- cmd-find.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index a71968a1..14124a36 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -131,7 +131,7 @@ cmd_find_best_client(struct client **clist, u_int csize) } else { TAILQ_FOREACH(c_loop, &clients, entry) { if (cmd_find_client_better(c_loop, c)) - c_loop = c; + c = c_loop; } } return (c); From b7777e7ef37a6a5b9a231602c1ac66a307ae23db Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Apr 2015 22:50:35 +0000 Subject: [PATCH 114/703] Reset cfg_ncauses to 0 as well or we could allocate the wrong size if called again. --- cfg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cfg.c b/cfg.c index bacec996..8b44ce24 100644 --- a/cfg.c +++ b/cfg.c @@ -140,6 +140,7 @@ cfg_print_causes(struct cmd_q *cmdq) free(cfg_causes); cfg_causes = NULL; + cfg_ncauses = 0; } void @@ -161,4 +162,5 @@ cfg_show_causes(struct session *s) free(cfg_causes); cfg_causes = NULL; + cfg_ncauses = 0; } From c2bc84aa4dd30c46930994321de0541edf6a30e9 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Apr 2015 22:58:58 +0000 Subject: [PATCH 115/703] Do not include unattached clients when trying to find one for target. --- cmd-find.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd-find.c b/cmd-find.c index 14124a36..f7c1ba74 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -125,11 +125,15 @@ cmd_find_best_client(struct client **clist, u_int csize) c = NULL; if (clist != NULL) { for (i = 0; i < csize; i++) { + if (clist[i]->session == NULL) + continue; if (cmd_find_client_better(clist[i], c)) c = clist[i]; } } else { TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session == NULL) + continue; if (cmd_find_client_better(c_loop, c)) c = c_loop; } From 3eb40a520a3965b4eaed063891a91f890703c1ba Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 Apr 2015 10:36:17 +0100 Subject: [PATCH 116/703] No paths.h on Solaris. --- cmd-find.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index f7c1ba74..fab3849f 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "tmux.h" From 771744426e3cd3fbf746e5c9554b8119e5f6faf2 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Apr 2015 10:43:13 +0000 Subject: [PATCH 117/703] Add select-layout -o to undo the last layout change (apply the previously set layout). --- cmd-select-layout.c | 77 ++++++++++++++++++++++++++++++--------------- tmux.1 | 8 +++-- tmux.h | 1 + window.c | 1 + 4 files changed, 59 insertions(+), 28 deletions(-) diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 77137b74..14737dc8 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -18,6 +18,8 @@ #include +#include + #include "tmux.h" /* @@ -28,8 +30,8 @@ enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_layout_entry = { "select-layout", "selectl", - "npt:", 0, 1, - "[-np] " CMD_TARGET_WINDOW_USAGE " [layout-name]", + "nopt:", 0, 1, + "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", 0, cmd_select_layout_exec }; @@ -55,46 +57,71 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; + struct window *w; const char *layoutname; + char *oldlayout; int next, previous, layout; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); - server_unzoom_window(wl->window); + w = wl->window; + + server_unzoom_window(w); next = self->entry == &cmd_next_layout_entry; - if (args_has(self->args, 'n')) + if (args_has(args, 'n')) next = 1; previous = self->entry == &cmd_previous_layout_entry; - if (args_has(self->args, 'p')) + if (args_has(args, 'p')) previous = 1; + oldlayout = w->old_layout; + w->old_layout = layout_dump(w->layout_root); + if (next || previous) { if (next) - layout = layout_set_next(wl->window); + layout_set_next(w); else - layout = layout_set_previous(wl->window); - server_redraw_window(wl->window); - return (CMD_RETURN_NORMAL); + layout_set_previous(w); + goto changed; } - if (args->argc == 0) - layout = wl->window->lastlayout; - else - layout = layout_set_lookup(args->argv[0]); - if (layout != -1) { - layout = layout_set_select(wl->window, layout); - server_redraw_window(wl->window); - return (CMD_RETURN_NORMAL); - } - - if (args->argc != 0) { - layoutname = args->argv[0]; - if (layout_parse(wl->window, layoutname) == -1) { - cmdq_error(cmdq, "can't set layout: %s", layoutname); - return (CMD_RETURN_ERROR); + if (!args_has(args, 'o')) { + if (args->argc == 0) + layout = w->lastlayout; + else + layout = layout_set_lookup(args->argv[0]); + if (layout != -1) { + layout_set_select(w, layout); + goto changed; } - server_redraw_window(wl->window); } + + if (args->argc != 0) + layoutname = args->argv[0]; + else if (args_has(args, 'o')) + layoutname = oldlayout; + else + layoutname = NULL; + + if (layoutname != NULL) { + if (layout_parse(w, layoutname) == -1) { + cmdq_error(cmdq, "can't set layout: %s", layoutname); + goto error; + } + goto changed; + } + + free(oldlayout); return (CMD_RETURN_NORMAL); + +changed: + free(oldlayout); + server_redraw_window(w); + return (CMD_RETURN_NORMAL); + +error: + free(w->old_layout); + w->old_layout = oldlayout; + return (CMD_RETURN_ERROR); } diff --git a/tmux.1 b/tmux.1 index cd7ec393..5bf77829 100644 --- a/tmux.1 +++ b/tmux.1 @@ -442,8 +442,8 @@ otherwise the current window in .Em session is chosen. .Pp -The following special tokens are available to indicate particular windows. Each -has a single-character alternative form. +The following special tokens are available to indicate particular windows. +Each has a single-character alternative form. .Bl -column "XXXXXXXXXX" "X" .It Sy "Token" Ta Sy "" Ta Sy "Meaning" .It Li "{start}" Ta "^" Ta "The lowest-numbered window" @@ -1775,7 +1775,7 @@ lower) with .Fl U or downward (numerically higher). .It Xo Ic select-layout -.Op Fl np +.Op Fl nop .Op Fl t Ar target-window .Op Ar layout-name .Xc @@ -1792,6 +1792,8 @@ are equivalent to the and .Ic previous-layout commands. +.Fl o +applies the last set layout if possible (undoes the most recent layout change). .It Xo Ic select-pane .Op Fl DdegLlRU .Op Fl P Ar style diff --git a/tmux.h b/tmux.h index ee36f4bb..b315114e 100644 --- a/tmux.h +++ b/tmux.h @@ -965,6 +965,7 @@ struct window { int lastlayout; struct layout_cell *layout_root; struct layout_cell *saved_layout_root; + char *old_layout; u_int sx; u_int sy; diff --git a/window.c b/window.c index 31f20a1c..e4e41155 100644 --- a/window.c +++ b/window.c @@ -344,6 +344,7 @@ window_destroy(struct window *w) if (w->layout_root != NULL) layout_free(w); + free(w->old_layout); if (event_initialized(&w->name_timer)) evtimer_del(&w->name_timer); From 094a047ddfe43817ac7f670668fd7d648349f6cd Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Apr 2015 11:33:17 +0000 Subject: [PATCH 118/703] If can't find pane as a pane, try as a window; likewise if can't find window as a session. --- cmd-find.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index f7c1ba74..e937b6dc 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -426,7 +426,18 @@ cmd_find_get_window(struct cmd_find_state *fs, const char *window) fs->s = fs->current->s; /* We now only need to find the winlink in this session. */ - return (cmd_find_get_window_with_session(fs, window)); + if (cmd_find_get_window_with_session(fs, window) == 0) + return (0); + + /* Otherwise try as a session itself. */ + if (cmd_find_get_session(fs, window) == 0) { + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } + + return (-1); } /* @@ -592,14 +603,23 @@ cmd_find_get_pane(struct cmd_find_state *fs, const char *pane) return (cmd_find_best_session_with_window(fs)); } - /* Not a pane id, so use the current session and window. */ + /* Not a pane id, so try the current session and window. */ fs->s = fs->current->s; fs->wl = fs->current->wl; fs->idx = fs->current->idx; fs->w = fs->current->w; /* We now only need to find the pane in this window. */ - return (cmd_find_get_pane_with_window(fs, pane)); + if (cmd_find_get_pane_with_window(fs, pane) == 0) + return (0); + + /* Otherwise try as a window itself (this will also try as session). */ + if (cmd_find_get_window(fs, pane) == 0) { + fs->wp = fs->w->active; + return (0); + } + + return (-1); } /* From 14d8cd64455e34c1c3f5803210e1162d4a03fb48 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Apr 2015 11:57:20 +0000 Subject: [PATCH 119/703] Do not do a search for the tty path if there isn't one. --- cmd-find.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index e937b6dc..e9d80cbe 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -243,10 +243,13 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) struct window_pane *wp; /* If this is running in a pane, that's great. */ - RB_FOREACH(wp, window_pane_tree, &all_window_panes) { - if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) - break; - } + if (fs->cmdq->client->tty.path != NULL) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) + break; + } + } else + wp = NULL; /* Not running in a pane. We know nothing. Find the best session. */ if (wp == NULL) { From e36fab2f7093e018de51af35e2c37b7a11201c2e Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Apr 2015 12:09:24 +0000 Subject: [PATCH 120/703] If looking for an index, don't fill in window when given a session. --- cmd-find.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index e9d80cbe..ec4cf6c1 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -434,9 +434,11 @@ cmd_find_get_window(struct cmd_find_state *fs, const char *window) /* Otherwise try as a session itself. */ if (cmd_find_get_session(fs, window) == 0) { - fs->wl = fs->s->curw; - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; + if (~fs->flags & CMD_FIND_WINDOW_INDEX) { + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + fs->idx = fs->wl->idx; + } return (0); } From bb210ce77354b168a82fb8e760fb82732656a227 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 Apr 2015 13:46:47 +0100 Subject: [PATCH 121/703] Add tmux logo, createed by Jason Long. --- logo/LICENSE | 13 + logo/favicon.ico | Bin 0 -> 6518 bytes logo/tmux-logo-1-color.eps | 922 ++++++++++++++++++++++++++++++++++++ logo/tmux-logo-1-color.svg | 18 + logo/tmux-logo-huge.png | Bin 0 -> 47121 bytes logo/tmux-logo-large.png | Bin 0 -> 12190 bytes logo/tmux-logo-medium.png | Bin 0 -> 5400 bytes logo/tmux-logo-small.png | Bin 0 -> 2701 bytes logo/tmux-logo.eps | 925 +++++++++++++++++++++++++++++++++++++ logo/tmux-logo.svg | 18 + logo/tmux-logomark.eps | 829 +++++++++++++++++++++++++++++++++ logo/tmux-logomark.svg | 15 + 12 files changed, 2740 insertions(+) create mode 100644 logo/LICENSE create mode 100644 logo/favicon.ico create mode 100644 logo/tmux-logo-1-color.eps create mode 100644 logo/tmux-logo-1-color.svg create mode 100644 logo/tmux-logo-huge.png create mode 100644 logo/tmux-logo-large.png create mode 100644 logo/tmux-logo-medium.png create mode 100644 logo/tmux-logo-small.png create mode 100644 logo/tmux-logo.eps create mode 100644 logo/tmux-logo.svg create mode 100644 logo/tmux-logomark.eps create mode 100644 logo/tmux-logomark.svg diff --git a/logo/LICENSE b/logo/LICENSE new file mode 100644 index 00000000..3f44eb59 --- /dev/null +++ b/logo/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2015, Jason Long + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/logo/favicon.ico b/logo/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6e5398a7ab2be493892911b021e80015865f396d GIT binary patch literal 6518 zcmeHLL2BDT6rCEpEZ89=!J@Vxw8Vg&=d3kZBn{zFX1@J5wh(C>_wp= zWD_Ts$fEVCn@}9LrRsl@C%B5nnab75BY6Drpa1@wc^bypj6GmERwyuvCfmw0_JlEJ zn$yp#jJ>4X7UigfDxy3E#wZ_u3a7cZ>z|50jgz(D(dYFL7#lRMoRvag9G?fLaF*Mv z?ORvYQT5wSuljZS&>C&`tWoX2`cmswuOA<8egrT^4s)nMEjYEVb;Z~Cgg@kCXO-VL zD^nDmYrpIVZr*(bVC+!tcAjd%3AvsdaQ|=EPwijc3qP=*+wXvIOu2X+C$8(>1?T*S z?-Sp)cL9u%OR7!E34Xm^-vbumH=E7Zp63OQ<9tWoIOgetOY48%Znp#Uz&hfP@lg+M zqtP%aexUe9>}NJ+#m71qSwHw#=Y9T>ufOAaPm#ndO%zBn8z12;D8&y50>pS?n#sIjDY(zeCQ5MdMf>mPI*uoR{F-IGBAQnK^l?P zMh9zwcBXps_9ON^uO`E}a^E}Pb{UQBd&{5G!s##PY+US7@AO7`Sq|q0i3vJOu MN9}-iVEG*Q1FWUhEdT%j literal 0 HcmV?d00001 diff --git a/logo/tmux-logo-1-color.eps b/logo/tmux-logo-1-color.eps new file mode 100644 index 00000000..20018e67 --- /dev/null +++ b/logo/tmux-logo-1-color.eps @@ -0,0 +1,922 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%APL_DSC_Encoding: UTF8 +%APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) +%%Title: (Unknown) +%%Creator: (Unknown) +%%CreationDate: (Unknown) +%%For: (Unknown) +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%Pages: 1 +%%BoundingBox: 0 0 608 160 +%%EndComments +%%BeginProlog +%%BeginFile: cg-pdf.ps +%%Copyright: Copyright 2000-2004 Apple Computer Incorporated. +%%Copyright: All Rights Reserved. +currentpacking true setpacking +/cg_md 141 dict def +cg_md begin +/L3? languagelevel 3 ge def +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/xd{exch def}bd +/cmmtx matrix def +mark +/sc/setcolor +/scs/setcolorspace +/dr/defineresource +/fr/findresource +/T/true +/F/false +/d/setdash +/w/setlinewidth +/J/setlinecap +/j/setlinejoin +/M/setmiterlimit +/i/setflat +/rc/rectclip +/rf/rectfill +/rs/rectstroke +/f/fill +/f*/eofill +/sf/selectfont +/s/show +/xS/xshow +/yS/yshow +/xyS/xyshow +/S/stroke +/m/moveto +/l/lineto +/c/curveto +/h/closepath +/n/newpath +/q/gsave +/Q/grestore +counttomark 2 idiv +{ld}repeat pop +/SC{ + /ColorSpace fr scs +}bd +/sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld +/soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld +/cgmtx matrix def +/sdmtx{cgmtx currentmatrix pop}bd +/CM {cgmtx setmatrix}bd +/cm {cmmtx astore CM concat}bd +/W{clip newpath}bd +/W*{eoclip newpath}bd +statusdict begin product end dup (HP) anchorsearch{ + pop pop pop + true +}{ + pop + (hp) anchorsearch{ + pop pop true + }{ + pop false + }ifelse +}ifelse +{ + { + { + pop pop + (0)dup 0 4 -1 roll put + F charpath + }cshow + } +}{ + {F charpath} +}ifelse +/cply exch bd +/cps {cply stroke}bd +/pgsave 0 def +/bp{/pgsave save store}bd +/ep{pgsave restore showpage}def +/re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd +/scrdict 10 dict def +/scrmtx matrix def +/patarray 0 def +/createpat{patarray 3 1 roll put}bd +/makepat{ +scrmtx astore pop +gsave +initgraphics +CM +patarray exch get +scrmtx +makepattern +grestore +setpattern +}bd +/cg_BeginEPSF{ + userdict save/cg_b4_Inc_state exch put + userdict/cg_endepsf/cg_EndEPSF load put + count userdict/cg_op_count 3 -1 roll put + countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put + 3 sub{end}repeat + /showpage {} def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash newpath + false setstrokeadjust false setoverprint +}bd +/cg_EndEPSF{ + countdictstack 3 sub { end } repeat + cg_dict_array 3 1 index length 3 sub getinterval + {begin}forall + count userdict/cg_op_count get sub{pop}repeat + userdict/cg_b4_Inc_state get restore + F setpacking +}bd +/cg_biproc{currentfile/RunLengthDecode filter}bd +/cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd +/ImageDataSource 0 def +L3?{ + /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd + /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd +}{ + /ImageBandMask 0 def + /ImageBandData 0 def + /cg_mibiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd + /cg_miaiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter + dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd +}ifelse +/imsave 0 def +/BI{save/imsave xd mark}bd +/EI{imsave restore}bd +/ID{ +counttomark 2 idiv +dup 2 add +dict begin +{def} repeat +pop +/ImageType 1 def +/ImageMatrix[Width 0 0 Height neg 0 Height]def +currentdict dup/ImageMask known{ImageMask}{F}ifelse exch +L3?{ + dup/MaskedImage known + { + pop + << + /ImageType 3 + /InterleaveType 2 + /DataDict currentdict + /MaskDict + << /ImageType 1 + /Width Width + /Height Height + /ImageMatrix ImageMatrix + /BitsPerComponent 1 + /Decode [0 1] + currentdict/Interpolate known + {/Interpolate Interpolate}if + >> + >> + }if +}if +exch +{imagemask}{image}ifelse +end +}bd +/cguidfix{statusdict begin mark version end +{cvr}stopped{cleartomark 0}{exch pop}ifelse +2012 lt{dup findfont dup length dict begin +{1 index/FID ne 2 index/UniqueID ne and +{def} {pop pop} ifelse}forall +currentdict end definefont pop +}{pop}ifelse +}bd +/t_array 0 def +/t_i 0 def +/t_c 1 string def +/x_proc{ + exch t_array t_i get add exch moveto + /t_i t_i 1 add store +}bd +/y_proc{ + t_array t_i get add moveto + /t_i t_i 1 add store +}bd +/xy_proc{ + + t_array t_i 2 copy 1 add get 3 1 roll get + 4 -1 roll add 3 1 roll add moveto + /t_i t_i 2 add store +}bd +/sop 0 def +/cp_proc/x_proc ld +/base_charpath +{ + /t_array xs + /t_i 0 def + { + t_c 0 3 -1 roll put + currentpoint + t_c cply sop + cp_proc + }forall + /t_array 0 def +}bd +/sop/stroke ld +/nop{}def +/xsp/base_charpath ld +/ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd +/xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd +/xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd +/ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd +/xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd +/refnt{ +findfont dup length dict copy dup +/Encoding 4 -1 roll put +definefont pop +}bd +/renmfont{ +findfont dup length dict copy definefont pop +}bd +L3? dup dup{save exch}if +/Range 0 def +/DataSource 0 def +/val 0 def +/nRange 0 def +/mulRange 0 def +/d0 0 def +/r0 0 def +/di 0 def +/ri 0 def +/a0 0 def +/a1 0 def +/r1 0 def +/r2 0 def +/dx 0 def +/Nsteps 0 def +/sh3tp 0 def +/ymax 0 def +/ymin 0 def +/xmax 0 def +/xmin 0 def +/setupFunEval +{ + begin + /nRange Range length 2 idiv store + /mulRange + + [ + 0 1 nRange 1 sub + { + 2 mul/nDim2 xd + Range nDim2 get + Range nDim2 1 add get + 1 index sub + + 255 div + exch + }for + ]store + end +}bd +/FunEval +{ + begin + + nRange mul /val xd + + 0 1 nRange 1 sub + { + dup 2 mul/nDim2 xd + val + add DataSource exch get + mulRange nDim2 get mul + mulRange nDim2 1 add get + add + }for + end +}bd +/max +{ + 2 copy lt + {exch pop}{pop}ifelse +}bd +/sh2 +{ + /Coords load aload pop + 3 index 3 index translate + + 3 -1 roll sub + 3 1 roll exch + sub + 2 copy + dup mul exch dup mul add sqrt + dup + scale + atan + + rotate + + /Function load setupFunEval + + + clippath {pathbbox}stopped {0 0 0 0}if newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + currentdict/Extend known + { + /Extend load 0 get + { + 0/Function load FunEval sc + xmin ymin xmin abs ymax ymin sub rectfill + }if + }if + + /Nsteps/Function load/Size get 0 get 1 sub store + /dx 1 Nsteps div store + gsave + /di ymax ymin sub store + /Function load + + 0 1 Nsteps + { + 1 index FunEval sc + 0 ymin dx di rectfill + dx 0 translate + }for + pop + grestore + currentdict/Extend known + { + /Extend load 1 get + { + Nsteps/Function load FunEval sc + 1 ymin xmax 1 sub abs ymax ymin sub rectfill + }if + }if +}bd +/shp +{ + 4 copy + + dup 0 gt{ + 0 exch a1 a0 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a0 a1 arcn + }{ + pop 0 lineto + }ifelse + + fill + + dup 0 gt{ + 0 exch a0 a1 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a1 a0 arcn + }{ + pop 0 lineto + }ifelse + + fill +}bd +/calcmaxs +{ + + xmin dup mul ymin dup mul add sqrt + xmax dup mul ymin dup mul add sqrt + xmin dup mul ymax dup mul add sqrt + xmax dup mul ymax dup mul add sqrt + max max max +}bd +/sh3 +{ + /Coords load aload pop + 5 index 5 index translate + 3 -1 roll 6 -1 roll sub + 3 -1 roll 5 -1 roll sub + 2 copy dup mul exch dup mul add sqrt + /dx xs + 2 copy 0 ne exch 0 ne or + { + + exch atan rotate + }{ + pop pop + }ifelse + + /r2 xs + /r1 xs + /Function load + dup/Size get 0 get 1 sub + /Nsteps xs + setupFunEval + + + + + + dx r2 add r1 lt{ + + 0 + }{ + dx r1 add r2 le + { + 1 + }{ + r1 r2 eq + { + 2 + }{ + 3 + }ifelse + }ifelse + }ifelse + /sh3tp xs + clippath {pathbbox}stopped {0 0 0 0}if + newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + + dx dup mul r2 r1 sub dup mul sub dup 0 gt + { + sqrt r2 r1 sub atan + /a0 exch 180 exch sub store + /a1 a0 neg store + }{ + pop + /a0 0 store + /a1 360 store + }ifelse + currentdict/Extend known + { + /Extend load 0 get r1 0 gt and + { + 0/Function load FunEval sc + + + + + { + { + dx 0 r1 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + r1 0 gt{0 0 r1 0 360 arc fill}if + } + { + + + + + 0 r1 xmin abs r1 add neg r1 shp + } + { + + + r2 r1 gt{ + + 0 r1 + r1 neg r2 r1 sub div dx mul + 0 + shp + }{ + + + + 0 r1 calcmaxs + dup + + r2 add dx mul dx r1 r2 sub sub div + neg + exch 1 index + abs exch sub + shp + }ifelse + } + }sh3tp get exec + }if + }if + + /d0 0 store + /r0 r1 store + /di dx Nsteps div store + /ri r2 r1 sub Nsteps div store + /Function load + 0 1 Nsteps + { + 1 index FunEval sc + d0 di add r0 ri add d0 r0 shp + { + + d0 0 r0 a1 a0 arc + d0 di add 0 r0 ri add a0 a1 arcn + fill + + + d0 0 r0 a0 a1 arc + d0 di add 0 r0 ri add a1 a0 arcn + fill + }pop + + + /d0 d0 di add store + /r0 r0 ri add store + }for + pop + + currentdict/Extend known + { + /Extend load 1 get r2 0 gt and + { + Nsteps/Function load FunEval sc + + + + + { + { + dx 0 r2 0 360 arc fill + } + { + dx 0 r2 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + + + xmax abs r1 add r1 dx r1 shp + } + { + + r2 r1 gt{ + + + + calcmaxs dup + + r1 add dx mul dx r2 r1 sub sub div + exch 1 index + exch sub + dx r2 + shp + }{ + + r1 neg r2 r1 sub div dx mul + 0 + dx + r2 + shp + }ifelse + } + } + sh3tp get exec + }if + }if +}bd +/sh +{ + begin + /ShadingType load dup dup 2 eq exch 3 eq or + { + gsave + newpath + /ColorSpace load scs + currentdict/BBox known + { + /BBox load aload pop + 2 index sub + 3 index + 3 -1 roll exch sub + exch rectclip + }if + 2 eq + {sh2}{sh3}ifelse + grestore + }{ + + pop + (DEBUG: shading type unimplemented\n)print flush + }ifelse + end +}bd +{restore}if not dup{save exch}if + L3?{ + /sh/shfill ld + /csq/clipsave ld + /csQ/cliprestore ld + }if +{restore}if +end +setpacking +%%EndFile +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%PageBoundingBox: 0 0 608 160 +%%BeginPageSetup +cg_md begin +bp +sdmtx +[ /CIEBasedABC 4 dict dup begin +/WhitePoint [ 0.9505 1.0000 1.0891 ] def +/DecodeABC [ +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind +] def +/MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def +/RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def +end ] /Cs1 exch/ColorSpace dr pop +%%EndPageSetup +0.60000002 i +/Cs1 SC +0.23529412 0.23529412 0.23529412 sc +q +2 15.003872 m +2 7.8149743 7.8157911 2 14.998466 2 c +145.00154 2 l +152.17584 2 158 7.8244047 158 15.003872 c +160 15.003872 m +160 6.7174625 153.27803 0 145.00154 0 c +14.998466 0 l +6.7150416 0 0 6.7065849 0 15.003872 c +2 14 m +0 16 l +160 16 l +158 14 l +160 14 m +0 14 l +W +0 0 608 160 rc +-5 21 m +165 21 l +165 -5 l +-5 -5 l +h +f +Q +q +83 90 m +83 160 l +77 160 l +77 14 l +83 14 l +83 84 l +160 84 l +160 90 l +83 90 l +h +0 144.99352 m +0 153.28137 6.7219648 160 14.998466 160 c +145.00154 160 l +153.28496 160 160 153.27509 160 144.99352 c +160 14 l +0 14 l +0 144.99352 l +h +0 144.99352 m +W* +0 0 608 160 rc +-5 165 m +165 165 l +165 9 l +-5 9 l +h +f +Q +q +242 12 m +230.85426 12.165432 222.63789 15.032893 217.12346 20.7679 c +211.60902 26.502909 208.85185 34.995007 209 46.244446 c +209 98 l +189 98 l +189 113 l +209 113 l +209 146 l +225 146 l +225 113 l +262 112.91358 l +262 98.024689 l +225 98 l +225 46.079014 l +225.39507 39.571983 226.99422 34.802074 230.1926 31.769136 c +233.39096 28.736198 237.58186 27.219753 242.76543 27.219753 c +245.19179 27.219753 247.89381 27.49547 250.87161 28.046913 c +253.8494 28.598356 256.88229 29.425508 259.97037 30.528395 c +263.27902 15.97037 l +259.52921 14.646907 255.86215 13.681896 252.27777 13.075309 c +248.69341 12.468721 245.19179 12.165432 241.77284 12.165432 c +242 12 l +h +295 14 m +278.15918 14 l +278 112.91358 l +295 112.91358 l +295 96.370369 l +295 96.370369 304.76617 106.29628 309.67401 109.60493 c +314.58185 112.9136 320.34436 114.5679 326.96167 114.5679 c +332.80695 114.5679 337.79745 112.94117 341.93326 109.68765 c +346.06909 106.43414 349.01926 101.99509 350.78387 96.370369 c +350.78387 96.370369 361.92294 106.29628 366.99622 109.60493 c +372.06952 112.9136 378.08014 114.5679 385.02832 114.5679 c +392.74854 114.5679 398.92459 111.72801 403.55673 106.04815 c +408.18884 100.36829 410.50488 92.730911 410 83.139999 c +410 14 l +394 14 l +393.96167 80.488892 l +393.96167 87.106209 392.69336 91.820976 390.15674 94.633331 c +387.62009 97.445694 383.98062 98.851852 379.23822 98.851852 c +374.49579 98.851852 369.78104 97.418121 365.09375 94.550621 c +360.40649 91.683113 356.24316 87.878212 353 83.135803 c +353 14 l +336.06042 14 l +336.06042 80.488892 l +336.06042 87.106209 334.79211 91.820976 332.25549 94.633331 c +329.71884 97.445694 326.07938 98.851852 321.33698 98.851852 c +316.59457 98.851852 311.87979 97.418121 307.19254 94.550621 c +302.50525 91.683113 298.34192 87.878212 295 83.135803 c +295 14 l +h +438.9505 21.264198 m +433.10519 27.440361 430.18259 35.656738 430 46 c +430 113 l +447 113 l +447 49.220001 l +446.7258 42.16375 448.3801 36.814835 451.68875 33.175308 c +454.99741 29.535784 459.85004 27.716049 466.2468 27.716049 c +471.32007 27.716049 476.36569 29.177351 481.38382 32.099998 c +486.40195 35.022648 491.39243 39.35141 496 45.086418 c +496 112.91358 l +513 113 l +512.89862 14 l +496 14 l +496 30.197531 l +496 30.197531 485.13364 20.271622 479.89493 16.962963 c +474.65622 13.654305 468.78345 12 462.2764 12 c +452.57101 12 444.79578 15.088035 438.9505 21.264198 c +h +588.65784 14 m +564.50476 51.041977 l +541.84052 14 l +523.4776 14 l +556.56403 63.449383 l +524.63562 113 l +543.164 113 l +566.15906 77.180244 l +589.48499 113 l +607.84796 113 l +574.43066 64.441978 l +607.18622 14 l +588.65784 14 l +h +588.65784 14 m +W* +0 0 608 160 rc +184 151 m +612.84796 151 l +612.84796 7 l +184 7 l +h +f +ep +end +%%Trailer +%%EOF diff --git a/logo/tmux-logo-1-color.svg b/logo/tmux-logo-1-color.svg new file mode 100644 index 00000000..2e6dda44 --- /dev/null +++ b/logo/tmux-logo-1-color.svg @@ -0,0 +1,18 @@ + + + + logomark + wordmark copy 3 + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/logo/tmux-logo-huge.png b/logo/tmux-logo-huge.png new file mode 100644 index 0000000000000000000000000000000000000000..141796d4eda21d871e1bd8a7092e68b7a59aaff9 GIT binary patch literal 47121 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 literal 0 HcmV?d00001 diff --git a/logo/tmux-logo-medium.png b/logo/tmux-logo-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..be2fa5c7be363f5cd0a7a0813fa2ad9e1adbefc9 GIT binary patch literal 5400 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 Hu0mjf@);+v literal 0 HcmV?d00001 diff --git a/logo/tmux-logo.eps b/logo/tmux-logo.eps new file mode 100644 index 00000000..23db6a09 --- /dev/null +++ b/logo/tmux-logo.eps @@ -0,0 +1,925 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%APL_DSC_Encoding: UTF8 +%APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) +%%Title: (Unknown) +%%Creator: (Unknown) +%%CreationDate: (Unknown) +%%For: (Unknown) +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%Pages: 1 +%%BoundingBox: 0 0 608 160 +%%EndComments +%%BeginProlog +%%BeginFile: cg-pdf.ps +%%Copyright: Copyright 2000-2004 Apple Computer Incorporated. +%%Copyright: All Rights Reserved. +currentpacking true setpacking +/cg_md 141 dict def +cg_md begin +/L3? languagelevel 3 ge def +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/xd{exch def}bd +/cmmtx matrix def +mark +/sc/setcolor +/scs/setcolorspace +/dr/defineresource +/fr/findresource +/T/true +/F/false +/d/setdash +/w/setlinewidth +/J/setlinecap +/j/setlinejoin +/M/setmiterlimit +/i/setflat +/rc/rectclip +/rf/rectfill +/rs/rectstroke +/f/fill +/f*/eofill +/sf/selectfont +/s/show +/xS/xshow +/yS/yshow +/xyS/xyshow +/S/stroke +/m/moveto +/l/lineto +/c/curveto +/h/closepath +/n/newpath +/q/gsave +/Q/grestore +counttomark 2 idiv +{ld}repeat pop +/SC{ + /ColorSpace fr scs +}bd +/sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld +/soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld +/cgmtx matrix def +/sdmtx{cgmtx currentmatrix pop}bd +/CM {cgmtx setmatrix}bd +/cm {cmmtx astore CM concat}bd +/W{clip newpath}bd +/W*{eoclip newpath}bd +statusdict begin product end dup (HP) anchorsearch{ + pop pop pop + true +}{ + pop + (hp) anchorsearch{ + pop pop true + }{ + pop false + }ifelse +}ifelse +{ + { + { + pop pop + (0)dup 0 4 -1 roll put + F charpath + }cshow + } +}{ + {F charpath} +}ifelse +/cply exch bd +/cps {cply stroke}bd +/pgsave 0 def +/bp{/pgsave save store}bd +/ep{pgsave restore showpage}def +/re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd +/scrdict 10 dict def +/scrmtx matrix def +/patarray 0 def +/createpat{patarray 3 1 roll put}bd +/makepat{ +scrmtx astore pop +gsave +initgraphics +CM +patarray exch get +scrmtx +makepattern +grestore +setpattern +}bd +/cg_BeginEPSF{ + userdict save/cg_b4_Inc_state exch put + userdict/cg_endepsf/cg_EndEPSF load put + count userdict/cg_op_count 3 -1 roll put + countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put + 3 sub{end}repeat + /showpage {} def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash newpath + false setstrokeadjust false setoverprint +}bd +/cg_EndEPSF{ + countdictstack 3 sub { end } repeat + cg_dict_array 3 1 index length 3 sub getinterval + {begin}forall + count userdict/cg_op_count get sub{pop}repeat + userdict/cg_b4_Inc_state get restore + F setpacking +}bd +/cg_biproc{currentfile/RunLengthDecode filter}bd +/cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd +/ImageDataSource 0 def +L3?{ + /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd + /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd +}{ + /ImageBandMask 0 def + /ImageBandData 0 def + /cg_mibiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd + /cg_miaiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter + dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd +}ifelse +/imsave 0 def +/BI{save/imsave xd mark}bd +/EI{imsave restore}bd +/ID{ +counttomark 2 idiv +dup 2 add +dict begin +{def} repeat +pop +/ImageType 1 def +/ImageMatrix[Width 0 0 Height neg 0 Height]def +currentdict dup/ImageMask known{ImageMask}{F}ifelse exch +L3?{ + dup/MaskedImage known + { + pop + << + /ImageType 3 + /InterleaveType 2 + /DataDict currentdict + /MaskDict + << /ImageType 1 + /Width Width + /Height Height + /ImageMatrix ImageMatrix + /BitsPerComponent 1 + /Decode [0 1] + currentdict/Interpolate known + {/Interpolate Interpolate}if + >> + >> + }if +}if +exch +{imagemask}{image}ifelse +end +}bd +/cguidfix{statusdict begin mark version end +{cvr}stopped{cleartomark 0}{exch pop}ifelse +2012 lt{dup findfont dup length dict begin +{1 index/FID ne 2 index/UniqueID ne and +{def} {pop pop} ifelse}forall +currentdict end definefont pop +}{pop}ifelse +}bd +/t_array 0 def +/t_i 0 def +/t_c 1 string def +/x_proc{ + exch t_array t_i get add exch moveto + /t_i t_i 1 add store +}bd +/y_proc{ + t_array t_i get add moveto + /t_i t_i 1 add store +}bd +/xy_proc{ + + t_array t_i 2 copy 1 add get 3 1 roll get + 4 -1 roll add 3 1 roll add moveto + /t_i t_i 2 add store +}bd +/sop 0 def +/cp_proc/x_proc ld +/base_charpath +{ + /t_array xs + /t_i 0 def + { + t_c 0 3 -1 roll put + currentpoint + t_c cply sop + cp_proc + }forall + /t_array 0 def +}bd +/sop/stroke ld +/nop{}def +/xsp/base_charpath ld +/ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd +/xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd +/xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd +/ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd +/xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd +/refnt{ +findfont dup length dict copy dup +/Encoding 4 -1 roll put +definefont pop +}bd +/renmfont{ +findfont dup length dict copy definefont pop +}bd +L3? dup dup{save exch}if +/Range 0 def +/DataSource 0 def +/val 0 def +/nRange 0 def +/mulRange 0 def +/d0 0 def +/r0 0 def +/di 0 def +/ri 0 def +/a0 0 def +/a1 0 def +/r1 0 def +/r2 0 def +/dx 0 def +/Nsteps 0 def +/sh3tp 0 def +/ymax 0 def +/ymin 0 def +/xmax 0 def +/xmin 0 def +/setupFunEval +{ + begin + /nRange Range length 2 idiv store + /mulRange + + [ + 0 1 nRange 1 sub + { + 2 mul/nDim2 xd + Range nDim2 get + Range nDim2 1 add get + 1 index sub + + 255 div + exch + }for + ]store + end +}bd +/FunEval +{ + begin + + nRange mul /val xd + + 0 1 nRange 1 sub + { + dup 2 mul/nDim2 xd + val + add DataSource exch get + mulRange nDim2 get mul + mulRange nDim2 1 add get + add + }for + end +}bd +/max +{ + 2 copy lt + {exch pop}{pop}ifelse +}bd +/sh2 +{ + /Coords load aload pop + 3 index 3 index translate + + 3 -1 roll sub + 3 1 roll exch + sub + 2 copy + dup mul exch dup mul add sqrt + dup + scale + atan + + rotate + + /Function load setupFunEval + + + clippath {pathbbox}stopped {0 0 0 0}if newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + currentdict/Extend known + { + /Extend load 0 get + { + 0/Function load FunEval sc + xmin ymin xmin abs ymax ymin sub rectfill + }if + }if + + /Nsteps/Function load/Size get 0 get 1 sub store + /dx 1 Nsteps div store + gsave + /di ymax ymin sub store + /Function load + + 0 1 Nsteps + { + 1 index FunEval sc + 0 ymin dx di rectfill + dx 0 translate + }for + pop + grestore + currentdict/Extend known + { + /Extend load 1 get + { + Nsteps/Function load FunEval sc + 1 ymin xmax 1 sub abs ymax ymin sub rectfill + }if + }if +}bd +/shp +{ + 4 copy + + dup 0 gt{ + 0 exch a1 a0 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a0 a1 arcn + }{ + pop 0 lineto + }ifelse + + fill + + dup 0 gt{ + 0 exch a0 a1 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a1 a0 arcn + }{ + pop 0 lineto + }ifelse + + fill +}bd +/calcmaxs +{ + + xmin dup mul ymin dup mul add sqrt + xmax dup mul ymin dup mul add sqrt + xmin dup mul ymax dup mul add sqrt + xmax dup mul ymax dup mul add sqrt + max max max +}bd +/sh3 +{ + /Coords load aload pop + 5 index 5 index translate + 3 -1 roll 6 -1 roll sub + 3 -1 roll 5 -1 roll sub + 2 copy dup mul exch dup mul add sqrt + /dx xs + 2 copy 0 ne exch 0 ne or + { + + exch atan rotate + }{ + pop pop + }ifelse + + /r2 xs + /r1 xs + /Function load + dup/Size get 0 get 1 sub + /Nsteps xs + setupFunEval + + + + + + dx r2 add r1 lt{ + + 0 + }{ + dx r1 add r2 le + { + 1 + }{ + r1 r2 eq + { + 2 + }{ + 3 + }ifelse + }ifelse + }ifelse + /sh3tp xs + clippath {pathbbox}stopped {0 0 0 0}if + newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + + dx dup mul r2 r1 sub dup mul sub dup 0 gt + { + sqrt r2 r1 sub atan + /a0 exch 180 exch sub store + /a1 a0 neg store + }{ + pop + /a0 0 store + /a1 360 store + }ifelse + currentdict/Extend known + { + /Extend load 0 get r1 0 gt and + { + 0/Function load FunEval sc + + + + + { + { + dx 0 r1 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + r1 0 gt{0 0 r1 0 360 arc fill}if + } + { + + + + + 0 r1 xmin abs r1 add neg r1 shp + } + { + + + r2 r1 gt{ + + 0 r1 + r1 neg r2 r1 sub div dx mul + 0 + shp + }{ + + + + 0 r1 calcmaxs + dup + + r2 add dx mul dx r1 r2 sub sub div + neg + exch 1 index + abs exch sub + shp + }ifelse + } + }sh3tp get exec + }if + }if + + /d0 0 store + /r0 r1 store + /di dx Nsteps div store + /ri r2 r1 sub Nsteps div store + /Function load + 0 1 Nsteps + { + 1 index FunEval sc + d0 di add r0 ri add d0 r0 shp + { + + d0 0 r0 a1 a0 arc + d0 di add 0 r0 ri add a0 a1 arcn + fill + + + d0 0 r0 a0 a1 arc + d0 di add 0 r0 ri add a1 a0 arcn + fill + }pop + + + /d0 d0 di add store + /r0 r0 ri add store + }for + pop + + currentdict/Extend known + { + /Extend load 1 get r2 0 gt and + { + Nsteps/Function load FunEval sc + + + + + { + { + dx 0 r2 0 360 arc fill + } + { + dx 0 r2 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + + + xmax abs r1 add r1 dx r1 shp + } + { + + r2 r1 gt{ + + + + calcmaxs dup + + r1 add dx mul dx r2 r1 sub sub div + exch 1 index + exch sub + dx r2 + shp + }{ + + r1 neg r2 r1 sub div dx mul + 0 + dx + r2 + shp + }ifelse + } + } + sh3tp get exec + }if + }if +}bd +/sh +{ + begin + /ShadingType load dup dup 2 eq exch 3 eq or + { + gsave + newpath + /ColorSpace load scs + currentdict/BBox known + { + /BBox load aload pop + 2 index sub + 3 index + 3 -1 roll exch sub + exch rectclip + }if + 2 eq + {sh2}{sh3}ifelse + grestore + }{ + + pop + (DEBUG: shading type unimplemented\n)print flush + }ifelse + end +}bd +{restore}if not dup{save exch}if + L3?{ + /sh/shfill ld + /csq/clipsave ld + /csQ/cliprestore ld + }if +{restore}if +end +setpacking +%%EndFile +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%PageBoundingBox: 0 0 608 160 +%%BeginPageSetup +cg_md begin +bp +sdmtx +[ /CIEBasedABC 4 dict dup begin +/WhitePoint [ 0.9505 1.0000 1.0891 ] def +/DecodeABC [ +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind +] def +/MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def +/RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def +end ] /Cs1 exch/ColorSpace dr pop +%%EndPageSetup +0.60000002 i +/Cs1 SC +0.10588235 0.72549021 0.12156863 sc +q +0 44 m +160 44 l +160 15.003872 l +160 6.7174625 153.27803 0 145.00154 0 c +14.998466 0 l +6.7150416 0 0 6.7065849 0 15.003872 c +0 44 l +h +0 44 m +160 44 l +160 14 l +0 14 l +0 44 l +h +0 44 m +W* +0 0 608 160 rc +-5 49 m +165 49 l +165 -5 l +-5 -5 l +h +f +Q +0.23529412 0.23529412 0.23529412 sc +q +83 90 m +83 160 l +77 160 l +77 14 l +83 14 l +83 84 l +160 84 l +160 90 l +83 90 l +h +0 144.99352 m +0 153.28137 6.7219648 160 14.998466 160 c +145.00154 160 l +153.28496 160 160 153.27509 160 144.99352 c +160 14 l +0 14 l +0 144.99352 l +h +0 144.99352 m +W* +0 0 608 160 rc +-5 165 m +165 165 l +165 9 l +-5 9 l +h +f +Q +0.10588235 0.72549021 0.12156863 sc +q +241.77284 12.165432 m +230.85426 12.165432 222.63789 15.032893 217.12346 20.7679 c +211.60902 26.502909 208.85185 34.995007 208.85185 46.244446 c +208.85185 98.024689 l +189 98.024689 l +189 112.91358 l +208.85185 112.91358 l +208.85185 146 l +225.39507 146 l +225.39507 112.91358 l +261.79013 112.91358 l +261.79013 98.024689 l +225.39507 98.024689 l +225.39507 46.079014 l +225.39507 39.571983 226.99422 34.802074 230.1926 31.769136 c +233.39096 28.736198 237.58186 27.219753 242.76543 27.219753 c +245.19179 27.219753 247.89381 27.49547 250.87161 28.046913 c +253.8494 28.598356 256.88229 29.425508 259.97037 30.528395 c +263.27902 15.97037 l +259.52921 14.646907 255.86215 13.681896 252.27777 13.075309 c +248.69341 12.468721 245.19179 12.165432 241.77284 12.165432 c +241.77284 12.165432 l +h +294.70239 13.654321 m +278.15918 13.654321 l +278.15918 112.91358 l +294.70239 112.91358 l +294.70239 96.370369 l +294.70239 96.370369 304.76617 106.29628 309.67401 109.60493 c +314.58185 112.9136 320.34436 114.5679 326.96167 114.5679 c +332.80695 114.5679 337.79745 112.94117 341.93326 109.68765 c +346.06909 106.43414 349.01926 101.99509 350.78387 96.370369 c +350.78387 96.370369 361.92294 106.29628 366.99622 109.60493 c +372.06952 112.9136 378.08014 114.5679 385.02832 114.5679 c +392.74854 114.5679 398.92459 111.72801 403.55673 106.04815 c +408.18884 100.36829 410.50488 92.730911 410.50488 83.135803 c +410.50488 13.654321 l +393.96167 13.654321 l +393.96167 80.488892 l +393.96167 87.106209 392.69336 91.820976 390.15674 94.633331 c +387.62009 97.445694 383.98062 98.851852 379.23822 98.851852 c +374.49579 98.851852 369.78104 97.418121 365.09375 94.550621 c +360.40649 91.683113 356.24316 87.878212 352.60364 83.135803 c +352.60364 13.654321 l +336.06042 13.654321 l +336.06042 80.488892 l +336.06042 87.106209 334.79211 91.820976 332.25549 94.633331 c +329.71884 97.445694 326.07938 98.851852 321.33698 98.851852 c +316.59457 98.851852 311.87979 97.418121 307.19254 94.550621 c +302.50525 91.683113 298.34192 87.878212 294.70239 83.135803 c +294.70239 13.654321 l +h +438.9505 21.264198 m +433.10519 27.440361 430.18259 35.656738 430.18259 45.913582 c +430.18259 112.91358 l +446.7258 112.91358 l +446.7258 49.222221 l +446.7258 42.16375 448.3801 36.814835 451.68875 33.175308 c +454.99741 29.535784 459.85004 27.716049 466.2468 27.716049 c +471.32007 27.716049 476.36569 29.177351 481.38382 32.099998 c +486.40195 35.022648 491.39243 39.35141 496.35544 45.086418 c +496.35544 112.91358 l +512.89862 112.91358 l +512.89862 13.654321 l +496.35544 13.654321 l +496.35544 30.197531 l +496.35544 30.197531 485.13364 20.271622 479.89493 16.962963 c +474.65622 13.654305 468.78345 12 462.2764 12 c +452.57101 12 444.79578 15.088035 438.9505 21.264198 c +h +588.65784 13.654321 m +564.50476 51.041977 l +541.84052 13.654321 l +523.4776 13.654321 l +556.56403 63.449383 l +524.63562 112.91358 l +543.164 112.91358 l +566.15906 77.180244 l +589.48499 112.91358 l +607.84796 112.91358 l +574.43066 64.441978 l +607.18622 13.654321 l +588.65784 13.654321 l +h +588.65784 13.654321 m +W* +0 0 608 160 rc +184 151 m +612.84796 151 l +612.84796 7 l +184 7 l +h +f +ep +end +%%Trailer +%%EOF diff --git a/logo/tmux-logo.svg b/logo/tmux-logo.svg new file mode 100644 index 00000000..061cddd9 --- /dev/null +++ b/logo/tmux-logo.svg @@ -0,0 +1,18 @@ + + + + logomark + wordmark + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/logo/tmux-logomark.eps b/logo/tmux-logomark.eps new file mode 100644 index 00000000..8924983b --- /dev/null +++ b/logo/tmux-logomark.eps @@ -0,0 +1,829 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%APL_DSC_Encoding: UTF8 +%APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) +%%Title: (Unknown) +%%Creator: (Unknown) +%%CreationDate: (Unknown) +%%For: (Unknown) +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%Pages: 1 +%%BoundingBox: 0 0 160 160 +%%EndComments +%%BeginProlog +%%BeginFile: cg-pdf.ps +%%Copyright: Copyright 2000-2004 Apple Computer Incorporated. +%%Copyright: All Rights Reserved. +currentpacking true setpacking +/cg_md 141 dict def +cg_md begin +/L3? languagelevel 3 ge def +/bd{bind def}bind def +/ld{load def}bd +/xs{exch store}bd +/xd{exch def}bd +/cmmtx matrix def +mark +/sc/setcolor +/scs/setcolorspace +/dr/defineresource +/fr/findresource +/T/true +/F/false +/d/setdash +/w/setlinewidth +/J/setlinecap +/j/setlinejoin +/M/setmiterlimit +/i/setflat +/rc/rectclip +/rf/rectfill +/rs/rectstroke +/f/fill +/f*/eofill +/sf/selectfont +/s/show +/xS/xshow +/yS/yshow +/xyS/xyshow +/S/stroke +/m/moveto +/l/lineto +/c/curveto +/h/closepath +/n/newpath +/q/gsave +/Q/grestore +counttomark 2 idiv +{ld}repeat pop +/SC{ + /ColorSpace fr scs +}bd +/sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld +/soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld +/cgmtx matrix def +/sdmtx{cgmtx currentmatrix pop}bd +/CM {cgmtx setmatrix}bd +/cm {cmmtx astore CM concat}bd +/W{clip newpath}bd +/W*{eoclip newpath}bd +statusdict begin product end dup (HP) anchorsearch{ + pop pop pop + true +}{ + pop + (hp) anchorsearch{ + pop pop true + }{ + pop false + }ifelse +}ifelse +{ + { + { + pop pop + (0)dup 0 4 -1 roll put + F charpath + }cshow + } +}{ + {F charpath} +}ifelse +/cply exch bd +/cps {cply stroke}bd +/pgsave 0 def +/bp{/pgsave save store}bd +/ep{pgsave restore showpage}def +/re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd +/scrdict 10 dict def +/scrmtx matrix def +/patarray 0 def +/createpat{patarray 3 1 roll put}bd +/makepat{ +scrmtx astore pop +gsave +initgraphics +CM +patarray exch get +scrmtx +makepattern +grestore +setpattern +}bd +/cg_BeginEPSF{ + userdict save/cg_b4_Inc_state exch put + userdict/cg_endepsf/cg_EndEPSF load put + count userdict/cg_op_count 3 -1 roll put + countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put + 3 sub{end}repeat + /showpage {} def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash newpath + false setstrokeadjust false setoverprint +}bd +/cg_EndEPSF{ + countdictstack 3 sub { end } repeat + cg_dict_array 3 1 index length 3 sub getinterval + {begin}forall + count userdict/cg_op_count get sub{pop}repeat + userdict/cg_b4_Inc_state get restore + F setpacking +}bd +/cg_biproc{currentfile/RunLengthDecode filter}bd +/cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd +/ImageDataSource 0 def +L3?{ + /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd + /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd +}{ + /ImageBandMask 0 def + /ImageBandData 0 def + /cg_mibiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd + /cg_miaiproc{ + string/ImageBandMask xs + string/ImageBandData xs + /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter + dup ImageBandMask/readstring cvx + /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd + }bd +}ifelse +/imsave 0 def +/BI{save/imsave xd mark}bd +/EI{imsave restore}bd +/ID{ +counttomark 2 idiv +dup 2 add +dict begin +{def} repeat +pop +/ImageType 1 def +/ImageMatrix[Width 0 0 Height neg 0 Height]def +currentdict dup/ImageMask known{ImageMask}{F}ifelse exch +L3?{ + dup/MaskedImage known + { + pop + << + /ImageType 3 + /InterleaveType 2 + /DataDict currentdict + /MaskDict + << /ImageType 1 + /Width Width + /Height Height + /ImageMatrix ImageMatrix + /BitsPerComponent 1 + /Decode [0 1] + currentdict/Interpolate known + {/Interpolate Interpolate}if + >> + >> + }if +}if +exch +{imagemask}{image}ifelse +end +}bd +/cguidfix{statusdict begin mark version end +{cvr}stopped{cleartomark 0}{exch pop}ifelse +2012 lt{dup findfont dup length dict begin +{1 index/FID ne 2 index/UniqueID ne and +{def} {pop pop} ifelse}forall +currentdict end definefont pop +}{pop}ifelse +}bd +/t_array 0 def +/t_i 0 def +/t_c 1 string def +/x_proc{ + exch t_array t_i get add exch moveto + /t_i t_i 1 add store +}bd +/y_proc{ + t_array t_i get add moveto + /t_i t_i 1 add store +}bd +/xy_proc{ + + t_array t_i 2 copy 1 add get 3 1 roll get + 4 -1 roll add 3 1 roll add moveto + /t_i t_i 2 add store +}bd +/sop 0 def +/cp_proc/x_proc ld +/base_charpath +{ + /t_array xs + /t_i 0 def + { + t_c 0 3 -1 roll put + currentpoint + t_c cply sop + cp_proc + }forall + /t_array 0 def +}bd +/sop/stroke ld +/nop{}def +/xsp/base_charpath ld +/ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd +/xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd +/xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd +/ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd +/xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd +/refnt{ +findfont dup length dict copy dup +/Encoding 4 -1 roll put +definefont pop +}bd +/renmfont{ +findfont dup length dict copy definefont pop +}bd +L3? dup dup{save exch}if +/Range 0 def +/DataSource 0 def +/val 0 def +/nRange 0 def +/mulRange 0 def +/d0 0 def +/r0 0 def +/di 0 def +/ri 0 def +/a0 0 def +/a1 0 def +/r1 0 def +/r2 0 def +/dx 0 def +/Nsteps 0 def +/sh3tp 0 def +/ymax 0 def +/ymin 0 def +/xmax 0 def +/xmin 0 def +/setupFunEval +{ + begin + /nRange Range length 2 idiv store + /mulRange + + [ + 0 1 nRange 1 sub + { + 2 mul/nDim2 xd + Range nDim2 get + Range nDim2 1 add get + 1 index sub + + 255 div + exch + }for + ]store + end +}bd +/FunEval +{ + begin + + nRange mul /val xd + + 0 1 nRange 1 sub + { + dup 2 mul/nDim2 xd + val + add DataSource exch get + mulRange nDim2 get mul + mulRange nDim2 1 add get + add + }for + end +}bd +/max +{ + 2 copy lt + {exch pop}{pop}ifelse +}bd +/sh2 +{ + /Coords load aload pop + 3 index 3 index translate + + 3 -1 roll sub + 3 1 roll exch + sub + 2 copy + dup mul exch dup mul add sqrt + dup + scale + atan + + rotate + + /Function load setupFunEval + + + clippath {pathbbox}stopped {0 0 0 0}if newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + currentdict/Extend known + { + /Extend load 0 get + { + 0/Function load FunEval sc + xmin ymin xmin abs ymax ymin sub rectfill + }if + }if + + /Nsteps/Function load/Size get 0 get 1 sub store + /dx 1 Nsteps div store + gsave + /di ymax ymin sub store + /Function load + + 0 1 Nsteps + { + 1 index FunEval sc + 0 ymin dx di rectfill + dx 0 translate + }for + pop + grestore + currentdict/Extend known + { + /Extend load 1 get + { + Nsteps/Function load FunEval sc + 1 ymin xmax 1 sub abs ymax ymin sub rectfill + }if + }if +}bd +/shp +{ + 4 copy + + dup 0 gt{ + 0 exch a1 a0 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a0 a1 arcn + }{ + pop 0 lineto + }ifelse + + fill + + dup 0 gt{ + 0 exch a0 a1 arc + }{ + pop 0 moveto + }ifelse + dup 0 gt{ + 0 exch a1 a0 arcn + }{ + pop 0 lineto + }ifelse + + fill +}bd +/calcmaxs +{ + + xmin dup mul ymin dup mul add sqrt + xmax dup mul ymin dup mul add sqrt + xmin dup mul ymax dup mul add sqrt + xmax dup mul ymax dup mul add sqrt + max max max +}bd +/sh3 +{ + /Coords load aload pop + 5 index 5 index translate + 3 -1 roll 6 -1 roll sub + 3 -1 roll 5 -1 roll sub + 2 copy dup mul exch dup mul add sqrt + /dx xs + 2 copy 0 ne exch 0 ne or + { + + exch atan rotate + }{ + pop pop + }ifelse + + /r2 xs + /r1 xs + /Function load + dup/Size get 0 get 1 sub + /Nsteps xs + setupFunEval + + + + + + dx r2 add r1 lt{ + + 0 + }{ + dx r1 add r2 le + { + 1 + }{ + r1 r2 eq + { + 2 + }{ + 3 + }ifelse + }ifelse + }ifelse + /sh3tp xs + clippath {pathbbox}stopped {0 0 0 0}if + newpath + /ymax xs + /xmax xs + /ymin xs + /xmin xs + + dx dup mul r2 r1 sub dup mul sub dup 0 gt + { + sqrt r2 r1 sub atan + /a0 exch 180 exch sub store + /a1 a0 neg store + }{ + pop + /a0 0 store + /a1 360 store + }ifelse + currentdict/Extend known + { + /Extend load 0 get r1 0 gt and + { + 0/Function load FunEval sc + + + + + { + { + dx 0 r1 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + r1 0 gt{0 0 r1 0 360 arc fill}if + } + { + + + + + 0 r1 xmin abs r1 add neg r1 shp + } + { + + + r2 r1 gt{ + + 0 r1 + r1 neg r2 r1 sub div dx mul + 0 + shp + }{ + + + + 0 r1 calcmaxs + dup + + r2 add dx mul dx r1 r2 sub sub div + neg + exch 1 index + abs exch sub + shp + }ifelse + } + }sh3tp get exec + }if + }if + + /d0 0 store + /r0 r1 store + /di dx Nsteps div store + /ri r2 r1 sub Nsteps div store + /Function load + 0 1 Nsteps + { + 1 index FunEval sc + d0 di add r0 ri add d0 r0 shp + { + + d0 0 r0 a1 a0 arc + d0 di add 0 r0 ri add a0 a1 arcn + fill + + + d0 0 r0 a0 a1 arc + d0 di add 0 r0 ri add a1 a0 arcn + fill + }pop + + + /d0 d0 di add store + /r0 r0 ri add store + }for + pop + + currentdict/Extend known + { + /Extend load 1 get r2 0 gt and + { + Nsteps/Function load FunEval sc + + + + + { + { + dx 0 r2 0 360 arc fill + } + { + dx 0 r2 360 0 arcn + xmin ymin moveto + xmax ymin lineto + xmax ymax lineto + xmin ymax lineto + xmin ymin lineto + eofill + } + { + + + xmax abs r1 add r1 dx r1 shp + } + { + + r2 r1 gt{ + + + + calcmaxs dup + + r1 add dx mul dx r2 r1 sub sub div + exch 1 index + exch sub + dx r2 + shp + }{ + + r1 neg r2 r1 sub div dx mul + 0 + dx + r2 + shp + }ifelse + } + } + sh3tp get exec + }if + }if +}bd +/sh +{ + begin + /ShadingType load dup dup 2 eq exch 3 eq or + { + gsave + newpath + /ColorSpace load scs + currentdict/BBox known + { + /BBox load aload pop + 2 index sub + 3 index + 3 -1 roll exch sub + exch rectclip + }if + 2 eq + {sh2}{sh3}ifelse + grestore + }{ + + pop + (DEBUG: shading type unimplemented\n)print flush + }ifelse + end +}bd +{restore}if not dup{save exch}if + L3?{ + /sh/shfill ld + /csq/clipsave ld + /csQ/cliprestore ld + }if +{restore}if +end +setpacking +%%EndFile +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%PageBoundingBox: 0 0 160 160 +%%BeginPageSetup +cg_md begin +bp +sdmtx +[ /CIEBasedABC 4 dict dup begin +/WhitePoint [ 0.9505 1.0000 1.0891 ] def +/DecodeABC [ +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind + +{ 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse + 1 index 1 index ge { exch pop } { pop } ifelse < +0000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000001010101010101010101010101 +0101010101010101010101010101010101010101010101020202020202020202 +0202020202020202020202020202020202030303030303030303030303030303 +0303030303030304040404040404040404040404040404040404050505050505 +0505050505050505050506060606060606060606060606060607070707070707 +0707070707070708080808080808080808080808090909090909090909090909 +0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c +0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 +1010101010111111111111111112121212121212121313131313131313141414 +1414141414151515151515151616161616161616171717171717171818181818 +18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d +1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 +2323232323242424242425252525252526262626262727272727282828282829 +292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f +2f2f303030303131313131323232323333333333343434343535353535363636 +36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e +3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 +4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f +4f50505051515151525252535353535454545555555656565657575758585859 +59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 +63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e +6e6e6f6f6f707070717171727273737374747475757576767677777878787979 +797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 +86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 +9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f +a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae +aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd +bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc +cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd +dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee +eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff +> dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling + cvi 3 index exch get 4 -1 roll 3 -1 roll get + dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind +] def +/MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def +/RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def +end ] /Cs1 exch/ColorSpace dr pop +%%EndPageSetup +0.60000002 i +/Cs1 SC +0.10588235 0.72549021 0.12156863 sc +q +0 44 m +160 44 l +160 15.003872 l +160 6.7174625 153.27803 0 145.00154 0 c +14.998466 0 l +6.7150416 0 0 6.7065849 0 15.003872 c +0 44 l +h +0 44 m +160 44 l +160 14 l +0 14 l +0 44 l +h +0 44 m +W* +0 0 160 160 rc +-5 49 m +165 49 l +165 -5 l +-5 -5 l +h +f +Q +0.23529412 0.23529412 0.23529412 sc +q +83 90 m +83 160 l +77 160 l +77 14 l +83 14 l +83 84 l +160 84 l +160 90 l +83 90 l +h +0 144.99352 m +0 153.28137 6.7219648 160 14.998466 160 c +145.00154 160 l +153.28496 160 160 153.27509 160 144.99352 c +160 14 l +0 14 l +0 144.99352 l +h +0 144.99352 m +W* +0 0 160 160 rc +-5 165 m +165 165 l +165 9 l +-5 9 l +h +f +ep +end +%%Trailer +%%EOF diff --git a/logo/tmux-logomark.svg b/logo/tmux-logomark.svg new file mode 100644 index 00000000..c543709d --- /dev/null +++ b/logo/tmux-logomark.svg @@ -0,0 +1,15 @@ + + + + logomark copy + Created with Sketch. + + + + + + + + + + \ No newline at end of file From f54f3e2abe6266a4c8b335ec2a83ebc1c2cdd1a5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 28 Apr 2015 13:47:54 +0100 Subject: [PATCH 122/703] Add logo to www, also centre the page. --- www/favicon.ico | Bin 0 -> 6518 bytes www/index.html.in | 2 +- www/logo.png | Bin 0 -> 2701 bytes www/main.css | 3 +++ 4 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 www/favicon.ico create mode 100644 www/logo.png diff --git a/www/favicon.ico b/www/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6e5398a7ab2be493892911b021e80015865f396d GIT binary patch literal 6518 zcmeHLL2BDT6rCEpEZ89=!J@Vxw8Vg&=d3kZBn{zFX1@J5wh(C>_wp= zWD_Ts$fEVCn@}9LrRsl@C%B5nnab75BY6Drpa1@wc^bypj6GmERwyuvCfmw0_JlEJ zn$yp#jJ>4X7UigfDxy3E#wZ_u3a7cZ>z|50jgz(D(dYFL7#lRMoRvag9G?fLaF*Mv z?ORvYQT5wSuljZS&>C&`tWoX2`cmswuOA<8egrT^4s)nMEjYEVb;Z~Cgg@kCXO-VL zD^nDmYrpIVZr*(bVC+!tcAjd%3AvsdaQ|=EPwijc3qP=*+wXvIOu2X+C$8(>1?T*S z?-Sp)cL9u%OR7!E34Xm^-vbumH=E7Zp63OQ<9tWoIOgetOY48%Znp#Uz&hfP@lg+M zqtP%aexUe9>}NJ+#m71qSwHw#=Y9T>ufOAaPm#ndO%zBn8z12;D8&y50>pS?n#sIjDY(zeCQ5MdMf>mPI*uoR{F-IGBAQnK^l?P zMh9zwcBXps_9ON^uO`E}a^E}Pb{UQBd&{5G!s##PY+US7@AO7`Sq|q0i3vJOu MN9}-iVEG*Q1FWUhEdT%j literal 0 HcmV?d00001 diff --git a/www/index.html.in b/www/index.html.in index e9ec46bd..cd42cd9f 100644 --- a/www/index.html.in +++ b/www/index.html.in @@ -9,7 +9,7 @@
-

tmux

+ tmux
From 7382ba82c5b366be84ca55c7842426bcf3d1f521 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Apr 2015 15:59:08 +0000 Subject: [PATCH 129/703] If default-terminal is set to "screen" or "screen-*", emulate screen's historical (incorrect) behaviour for SGR 3 and send smso (standout). Previously, we would send sitm (italics) if the terminal outside had it and smso otherwise. This was acceptably until recently because xterm's terminfo entry lacked sitm, so most users got smso. People who want italics should set default-terminal to the forthcoming "tmux" entry (and be prepared to deal with it being missing on older hosts). As a side-effect this changes default-terminal to be a server rather than a session option. suggested by and ok naddy --- options-table.c | 10 +++++----- server-fn.c | 2 +- tmux.1 | 25 +++++++++++++------------ tty.c | 24 ++++++++++++++++++------ 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/options-table.c b/options-table.c index 5e21c692..089a9b86 100644 --- a/options-table.c +++ b/options-table.c @@ -61,6 +61,11 @@ const struct options_table_entry server_options_table[] = { .default_num = 20 }, + { .name = "default-terminal", + .type = OPTIONS_TABLE_STRING, + .default_str = "screen" + }, + { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, @@ -143,11 +148,6 @@ const struct options_table_entry session_options_table[] = { .default_str = _PATH_BSHELL }, - { .name = "default-terminal", - .type = OPTIONS_TABLE_STRING, - .default_str = "screen" - }, - { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, .default_num = 0 diff --git a/server-fn.c b/server-fn.c index 85067a87..0e6e4d46 100644 --- a/server-fn.c +++ b/server-fn.c @@ -36,7 +36,7 @@ server_fill_environ(struct session *s, struct environ *env) long pid; if (s != NULL) { - term = options_get_string(&s->options, "default-terminal"); + term = options_get_string(&global_options, "default-terminal"); environ_set(env, "TERM", term); idx = s->id; diff --git a/tmux.1 b/tmux.1 index 5bf77829..a7f5f3ef 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2266,6 +2266,19 @@ Available server options are: Set the number of buffers; as new buffers are added to the top of the stack, old ones are removed from the bottom if necessary to maintain this maximum length. +.It Ic default-terminal Ar terminal +Set the default terminal for new windows created in this session - the +default value of the +.Ev TERM +environment variable. +For +.Nm +to work correctly, this +.Em must +be set to +.Ql screen , +.Ql tmux +or a derivative of them. .It Ic escape-time Ar time Set the time in milliseconds for which .Nm @@ -2405,18 +2418,6 @@ or This option should be configured when .Nm is used as a login shell. -.It Ic default-terminal Ar terminal -Set the default terminal for new windows created in this session - the -default value of the -.Ev TERM -environment variable. -For -.Nm -to work correctly, this -.Em must -be set to -.Ql screen -or a derivative of it. .It Xo Ic destroy-unattached .Op Ic on | off .Xc diff --git a/tty.c b/tty.c index ee52d038..f6db8e20 100644 --- a/tty.c +++ b/tty.c @@ -34,6 +34,7 @@ void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); +void tty_set_italics(struct tty *); int tty_try_256(struct tty *, u_char, const char *); void tty_colours(struct tty *, const struct grid_cell *); @@ -456,6 +457,21 @@ tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) tty->cx += width; } +void +tty_set_italics(struct tty *tty) +{ + const char *s; + + if (tty_term_has(tty->term, TTYC_SITM)) { + s = options_get_string(&global_options, "default-terminal"); + if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { + tty_putcode(tty, TTYC_SITM); + return; + } + } + tty_putcode(tty, TTYC_SMSO); +} + void tty_set_title(struct tty *tty, const char *title) { @@ -1396,12 +1412,8 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc, tty_putcode(tty, TTYC_BOLD); if (changed & GRID_ATTR_DIM) tty_putcode(tty, TTYC_DIM); - if (changed & GRID_ATTR_ITALICS) { - if (tty_term_has(tty->term, TTYC_SITM)) - tty_putcode(tty, TTYC_SITM); - else - tty_putcode(tty, TTYC_SMSO); - } + if (changed & GRID_ATTR_ITALICS) + tty_set_italics(tty); if (changed & GRID_ATTR_UNDERSCORE) tty_putcode(tty, TTYC_SMUL); if (changed & GRID_ATTR_BLINK) From 69b8f100b70061ee2520fb30368a955cf39e47db Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Apr 2015 16:26:17 +0000 Subject: [PATCH 130/703] Do not complain when directions fail. --- cmd-select-pane.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e7f2249e..f237e8fd 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -100,10 +100,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) wp = window_pane_find_up(wp); else if (args_has(self->args, 'D')) wp = window_pane_find_down(wp); - if (wp == NULL) { - cmdq_error(cmdq, "pane not found"); - return (CMD_RETURN_ERROR); - } + if (wp == NULL) + return (CMD_RETURN_NORMAL); if (args_has(self->args, 'e')) { wp->flags &= ~PANE_INPUTOFF; From 672df72b715117e9994e72f0248c530eda8fbb8d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 4 May 2015 13:04:10 +0000 Subject: [PATCH 131/703] Use the right index when expanding/collapsing tree, from Thomas Adam. --- window-choose.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window-choose.c b/window-choose.c index 5de87572..8672212a 100644 --- a/window-choose.c +++ b/window-choose.c @@ -559,10 +559,10 @@ window_choose_key(struct window_pane *wp, unused struct client *c, break; if (item->state & TREE_EXPANDED) { window_choose_collapse(wp, item->wcd->tree_session, - item->wcd->idx); + data->selected); } else { window_choose_expand(wp, item->wcd->tree_session, - item->wcd->idx); + data->selected); } window_choose_redraw_screen(wp); break; From 33a585c47fcceb55e79a67a5fc47902d6992e68d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 May 2015 07:52:06 +0000 Subject: [PATCH 132/703] Turn cursor off during redraw, pointed out by George Nachman. --- server-client.c | 24 +++++++++++++++++------- tty.c | 33 +++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/server-client.c b/server-client.c index b24e1afd..ce3f3d5e 100644 --- a/server-client.c +++ b/server-client.c @@ -875,15 +875,13 @@ void server_client_check_redraw(struct client *c) { struct session *s = c->session; + struct tty *tty = &c->tty; struct window_pane *wp; int flags, redraw; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; - flags = c->tty.flags & TTY_FREEZE; - c->tty.flags &= ~TTY_FREEZE; - if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { if (options_get_number(&s->options, "set-titles")) server_client_set_title(c); @@ -898,27 +896,39 @@ server_client_check_redraw(struct client *c) c->flags &= ~CLIENT_STATUS; } + flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR); + tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR; + if (c->flags & CLIENT_REDRAW) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 1, 1, 1); c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); } else if (c->flags & CLIENT_REDRAWWINDOW) { + tty_update_mode(tty, tty->mode, NULL); TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) screen_redraw_pane(c, wp); c->flags &= ~CLIENT_REDRAWWINDOW; } else { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { - if (wp->flags & PANE_REDRAW) + if (wp->flags & PANE_REDRAW) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_pane(c, wp); + } } } - if (c->flags & CLIENT_BORDERS) + if (c->flags & CLIENT_BORDERS) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 0, 0, 1); + } - if (c->flags & CLIENT_STATUS) + if (c->flags & CLIENT_STATUS) { + tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 0, 1, 0); + } - c->tty.flags |= flags; + tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; + tty_update_mode(tty, tty->mode, NULL); c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS); } diff --git a/tty.c b/tty.c index f6db8e20..a9f49c9b 100644 --- a/tty.c +++ b/tty.c @@ -500,7 +500,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) { int changed; - if (strcmp(s->ccolour, tty->ccolour)) + if (s != NULL && strcmp(s->ccolour, tty->ccolour)) tty_force_cursor_colour(tty, s->ccolour); if (tty->flags & TTY_NOCURSOR) @@ -517,7 +517,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } else tty_putcode(tty, TTYC_CIVIS); } - if (tty->cstyle != s->cstyle) { + if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) @@ -667,8 +667,11 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, struct grid_cell tmpgc; struct utf8_data ud; u_int i, sx; + int flags; - tty_update_mode(tty, tty->mode & ~MODE_CURSOR, s); + flags = tty->flags & TTY_NOCURSOR; + tty->flags |= TTY_NOCURSOR; + tty_update_mode(tty, tty->mode, s); sx = screen_size_x(s); if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) @@ -703,18 +706,20 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, tty_cell(tty, gc, wp); } - if (sx >= tty->sx) { - tty_update_mode(tty, tty->mode, s); - return; - } - tty_attributes(tty, &grid_default_cell, wp); + if (sx < tty->sx) { + tty_attributes(tty, &grid_default_cell, wp); - tty_cursor(tty, ox + sx, oy + py); - if (sx != screen_size_x(s) && ox + screen_size_x(s) >= tty->sx && - tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) - tty_putcode(tty, TTYC_EL); - else - tty_repeat_space(tty, screen_size_x(s) - sx); + tty_cursor(tty, ox + sx, oy + py); + if (sx != screen_size_x(s) && + ox + screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL) && + !tty_fake_bce(tty, wp)) + tty_putcode(tty, TTYC_EL); + else + tty_repeat_space(tty, screen_size_x(s) - sx); + } + + tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; tty_update_mode(tty, tty->mode, s); } From 31b1ab48521b4b608d87abd5413441905da84da8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 May 2015 08:35:39 +0000 Subject: [PATCH 133/703] Add a format window_linked which is 1 if a window has been linked multiple times, also remove the default space in window_flags and use a conditional to add it in window-status-format (this means additional flags can be added in the option without extra spaces). From Thomas Adam with tweaks by me. --- cmd-kill-window.c | 17 +++++------------ format.c | 1 + options-table.c | 4 ++-- session.c | 14 ++++++++++++++ tmux.1 | 1 + tmux.h | 1 + window.c | 4 +--- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/cmd-kill-window.c b/cmd-kill-window.c index d402acce..4d346a71 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -45,24 +45,17 @@ const struct cmd_entry cmd_unlink_window_entry = { enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl, *wl2, *wl3; - struct window *w; - struct session *s; - struct session_group *sg; - u_int references; + struct args *args = self->args; + struct winlink *wl, *wl2, *wl3; + struct window *w; + struct session *s; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; if (self->entry == &cmd_unlink_window_entry) { - sg = session_group_find(s); - if (sg != NULL) - references = session_group_count(sg); - else - references = 1; - if (!args_has(self->args, 'k') && w->references == references) { + if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { cmdq_error(cmdq, "window only linked to one session"); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index 7ded05d2..ac28604d 100644 --- a/format.c +++ b/format.c @@ -621,6 +621,7 @@ format_defaults_winlink(struct format_tree *ft, struct session *s, !!(wl->flags & WINLINK_SILENCE)); format_add(ft, "window_last_flag", "%d", !!(wl == TAILQ_FIRST(&s->lastw))); + format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); free(flags); } diff --git a/options-table.c b/options-table.c index 089a9b86..21758543 100644 --- a/options-table.c +++ b/options-table.c @@ -737,7 +737,7 @@ const struct options_table_entry window_options_table[] = { { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, - .default_str = "#I:#W#F" + .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-current-style", @@ -753,7 +753,7 @@ const struct options_table_entry window_options_table[] = { { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, - .default_str = "#I:#W#F" + .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-last-attr", diff --git a/session.c b/session.c index 224f1f32..907bdee9 100644 --- a/session.c +++ b/session.c @@ -337,6 +337,20 @@ session_has(struct session *s, struct window *w) return (0); } +/* + * Return 1 if a window is linked outside this session (not including session + * groups). The window must be in this session! + */ +int +session_is_linked(struct session *s, struct window *w) +{ + struct session_group *sg; + + if ((sg = session_group_find(s)) != NULL) + return (w->references != session_group_count(sg)); + return (w->references != 1); +} + struct winlink * session_next_alert(struct winlink *wl) { diff --git a/tmux.1 b/tmux.1 index a7f5f3ef..ea178b78 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3345,6 +3345,7 @@ The following variables are available, where appropriate: .It Li "window_index" Ta "#I" Ta "Index of window" .It Li "window_last_flag" Ta "" Ta "1 if window is the last used" .It Li "window_layout" Ta "" Ta "Window layout description" +.It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" diff --git a/tmux.h b/tmux.h index b315114e..83b6e4a8 100644 --- a/tmux.h +++ b/tmux.h @@ -2328,6 +2328,7 @@ struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); int session_has(struct session *, struct window *); +int session_is_linked(struct session *, struct window *); int session_next(struct session *, int); int session_previous(struct session *, int); int session_select(struct session *, int); diff --git a/window.c b/window.c index e4e41155..a2277e2e 100644 --- a/window.c +++ b/window.c @@ -643,7 +643,7 @@ window_destroy_panes(struct window *w) } } -/* Return list of printable window flag symbols. No flags is just a space. */ +/* Retuns the printable flags on a window, empty string if no flags set. */ char * window_printable_flags(struct session *s, struct winlink *wl) { @@ -663,8 +663,6 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '-'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; - if (pos == 0) - flags[pos++] = ' '; flags[pos] = '\0'; return (xstrdup(flags)); } From e362d42dc68472621d9f8bd09e40cc8f480ad851 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Wed, 6 May 2015 23:45:58 +0100 Subject: [PATCH 134/703] CHANGES for tmux 2.0 --- CHANGES | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/CHANGES b/CHANGES index abd1ac0a..6e0d8f80 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,62 @@ +CHANGES FROM 1.9a to 2.0 6 March 2015 + +Incompatible Changes +==================== + +* The choose-list command has been removed. +* 'terminal-overrides' is now a server option, not a session option. +* 'message-limit' is now a server option, not a session option. +* 'monitor-content' option has been removed. +* 'pane_start_path' option has been removed. +* The "info" mechanism which used to (for some commands) provide feedback + has been removed, and like other commands, they now produce nothing on + success. + +Normal Changes +============== + +* tmux can now write an entry to utmp if the library 'utempter' is present + at compile time. +* set-buffer learned append mode (-a), and a corresponding + 'append-selection' command has been added to copy-mode. +* choose-mode now has the following commands which can be bound: + - start-of-list + - end-of-list + - top-line + - bottom-line + +* choose-buffer now understands UTF-8. +* Pane navigation has changed: + - The old way of always using the top or left if the choice is ambiguous. + - The new way of remembering the last used pane is annoying if the + layout is balanced and the leftmost is obvious to the user (because + clearly if we go right from the top-left in a tiled set of four we want + to end up in top-right, even if we were last using the bottom-right). + + So instead, use a combination of both: if there is only one possible + pane alongside the current pane, move to it, otherwise choose the most + recently used of the choice. +* 'set-buffer' can now be told to give names to buffers. +* The 'new-session', 'new-window', 'split-window', and 'respawn-pane' commands + now understand multiple arguments and handle quoting problems correctly. +* 'capture-pane' understands '-S-' to mean the start of the pane, and '-E-' to + mean the end of the pane. +* Support for function keys beyond F12 has changed. The following explains: + - F13-F24 are S-F1 to S-F12 + - F25-F36 are C-F1 to C-F12 + - F37-F48 are C-S-F1 to C-S-F12 + - F49-F60 are M-F1 to M-F12 + - F61-F63 are M-S-F1 to M-S-F3 + + Therefore, F13 becomes a binding of S-F1, etc. +* Support using pane id as part of session or window specifier (so % means + session-of-%1 or window-of-%1) and window id as part of session + (so @1 means session-of-@1). +* 'copy-pipe' command now understands formats via -F +* 'if-shell' command now understands formats via -F +* 'split-window' and 'join-window' understand -b to create the pane to the left + or above the target pane. + CHANGES FROM 1.9 to 1.9a 22 February 2014 NOTE: This is a bug-fix release to address some important bugs which just From 6525ca51584636ef781bda147d37d5d0d10899e0 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 7 May 2015 00:00:02 +0100 Subject: [PATCH 135/703] Start working on 2.1 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 4a321d5c..cccd656b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ # $Id$ # Miscellaneous autofoo bullshit. -AC_INIT(tmux, 2.0) -RELEASE=1.9a +AC_INIT(tmux, 2.1) +RELEASE=2.0 AC_SUBST(RELEASE) AC_CONFIG_AUX_DIR(etc) From 0b39e6427fde3fadb9a3453b6423865829a59ad6 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 6 May 2015 23:56:46 +0000 Subject: [PATCH 136/703] Remove ARRAY_* from history and expand completion to complete a) layout names and b) targets beginning with -t or -s. --- status.c | 270 ++++++++++++++++++++++++++++++++++++++++--------------- tmux.h | 3 - 2 files changed, 198 insertions(+), 75 deletions(-) diff --git a/status.c b/status.c index e3d335fd..9bd5111c 100644 --- a/status.c +++ b/status.c @@ -45,10 +45,15 @@ void status_message_callback(int, short, void *); const char *status_prompt_up_history(u_int *); const char *status_prompt_down_history(u_int *); void status_prompt_add_history(const char *); -char *status_prompt_complete(const char *); + +const char **status_prompt_complete_list(u_int *, const char *); +char *status_prompt_complete_prefix(const char **, u_int); +char *status_prompt_complete(struct session *, const char *); /* Status prompt history. */ -ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER; +#define PROMPT_HISTORY 100 +char **status_prompt_hlist; +u_int status_prompt_hsize; /* Status output tree. */ RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); @@ -977,7 +982,7 @@ status_prompt_key(struct client *c, int key) word[last - first] = '\0'; /* And try to complete it. */ - if ((s = status_prompt_complete(word)) == NULL) + if ((s = status_prompt_complete(sess, word)) == NULL) break; /* Trim out word. */ @@ -1235,114 +1240,235 @@ status_prompt_key(struct client *c, int key) const char * status_prompt_up_history(u_int *idx) { - u_int size; - /* - * History runs from 0 to size - 1. - * - * Index is from 0 to size. Zero is empty. + * History runs from 0 to size - 1. Index is from 0 to size. Zero is + * empty. */ - size = ARRAY_LENGTH(&status_prompt_history); - if (size == 0 || *idx == size) + if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) return (NULL); (*idx)++; - return (ARRAY_ITEM(&status_prompt_history, size - *idx)); + return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Get next line from the history. */ const char * status_prompt_down_history(u_int *idx) { - u_int size; - - size = ARRAY_LENGTH(&status_prompt_history); - if (size == 0 || *idx == 0) + if (status_prompt_hsize == 0 || *idx == 0) return (""); (*idx)--; if (*idx == 0) return (""); - return (ARRAY_ITEM(&status_prompt_history, size - *idx)); + return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Add line to the history. */ void status_prompt_add_history(const char *line) { - u_int size; + size_t size; - size = ARRAY_LENGTH(&status_prompt_history); - if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0) + if (status_prompt_hsize > 0 && + strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) return; - if (size == PROMPT_HISTORY) { - free(ARRAY_FIRST(&status_prompt_history)); - ARRAY_REMOVE(&status_prompt_history, 0); + if (status_prompt_hsize == PROMPT_HISTORY) { + free(status_prompt_hlist[0]); + + size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; + memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); + + status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); + return; } - ARRAY_ADD(&status_prompt_history, xstrdup(line)); + status_prompt_hlist = xreallocarray(status_prompt_hlist, + status_prompt_hsize + 1, sizeof *status_prompt_hlist); + status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); +} + +/* Build completion list. */ +const char ** +status_prompt_complete_list(u_int *size, const char *s) +{ + const char **list = NULL, **layout; + const struct cmd_entry **cmdent; + const struct options_table_entry *oe; + const char *layouts[] = { + "even-horizontal", "even-vertical", "main-horizontal", + "main-vertical", "tiled", NULL + }; + + *size = 0; + for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { + if (strncmp((*cmdent)->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = (*cmdent)->name; + } + } + for (oe = server_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = oe->name; + } + } + for (oe = session_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = oe->name; + } + } + for (oe = window_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = oe->name; + } + } + for (layout = layouts; *layout != NULL; layout++) { + if (strncmp(*layout, s, strlen(s)) == 0) { + list = xreallocarray(list, (*size) + 1, sizeof *list); + list[(*size)++] = *layout; + } + } + return (list); +} + +/* Find longest prefix. */ +char * +status_prompt_complete_prefix(const char **list, u_int size) +{ + char *out; + u_int i; + size_t j; + + out = xstrdup(list[0]); + for (i = 1; i < size; i++) { + j = strlen(list[i]); + if (j > strlen(out)) + j = strlen(out); + for (; j > 0; j--) { + if (out[j - 1] != list[i][j - 1]) + out[j - 1] = '\0'; + } + } + return (out); } /* Complete word. */ char * -status_prompt_complete(const char *s) +status_prompt_complete(struct session *sess, const char *s) { - const struct cmd_entry **cmdent; - const struct options_table_entry *oe; - ARRAY_DECL(, const char *) list; - char *prefix, *s2; - u_int i; - size_t j; + const char **list = NULL, *colon; + u_int size = 0, i; + struct session *s_loop; + struct winlink *wl; + struct window *w; + char *copy, *out, *tmp; if (*s == '\0') return (NULL); + out = NULL; - /* First, build a list of all the possible matches. */ - ARRAY_INIT(&list); - for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { - if (strncmp((*cmdent)->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, (*cmdent)->name); - } - for (oe = server_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); - } - for (oe = session_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); - } - for (oe = window_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) - ARRAY_ADD(&list, oe->name); + if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { + list = status_prompt_complete_list(&size, s); + if (size == 0) + out = NULL; + else if (size == 1) + xasprintf(&out, "%s ", list[0]); + else + out = status_prompt_complete_prefix(list, size); + free(list); + return (out); } + copy = xstrdup(s); - /* If none, bail now. */ - if (ARRAY_LENGTH(&list) == 0) { - ARRAY_FREE(&list); - return (NULL); - } + colon = ":"; + if (copy[strlen(copy) - 1] == ':') + copy[strlen(copy) - 1] = '\0'; + else + colon = ""; + s = copy + 2; - /* If an exact match, return it, with a trailing space. */ - if (ARRAY_LENGTH(&list) == 1) { - xasprintf(&s2, "%s ", ARRAY_FIRST(&list)); - ARRAY_FREE(&list); - return (s2); - } - - /* Now loop through the list and find the longest common prefix. */ - prefix = xstrdup(ARRAY_FIRST(&list)); - for (i = 1; i < ARRAY_LENGTH(&list); i++) { - s = ARRAY_ITEM(&list, i); - - j = strlen(s); - if (j > strlen(prefix)) - j = strlen(prefix); - for (; j > 0; j--) { - if (prefix[j - 1] != s[j - 1]) - prefix[j - 1] = '\0'; + RB_FOREACH(s_loop, sessions, &sessions) { + if (strncmp(s_loop->name, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 2, sizeof *list); + list[size++] = s_loop->name; } } + if (size == 1) { + out = xstrdup(list[0]); + if (session_find(list[0]) != NULL) + colon = ":"; + } else if (size != 0) + out = status_prompt_complete_prefix(list, size); + if (out != NULL) { + xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); + out = tmp; + goto found; + } - ARRAY_FREE(&list); - return (prefix); + colon = ""; + if (*s == ':') { + RB_FOREACH(wl, winlinks, &sess->windows) { + xasprintf(&tmp, ":%s", wl->window->name); + if (strncmp(tmp, s, strlen(s)) == 0){ + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + + xasprintf(&tmp, ":%d", wl->idx); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + } + } else { + RB_FOREACH(s_loop, sessions, &sessions) { + RB_FOREACH(wl, winlinks, &s_loop->windows) { + w = wl->window; + + xasprintf(&tmp, "%s:%s", s_loop->name, w->name); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + + xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); + if (strncmp(tmp, s, strlen(s)) == 0) { + list = xreallocarray(list, size + 1, + sizeof *list); + list[size++] = tmp; + continue; + } + free(tmp); + } + } + } + if (size == 1) { + out = xstrdup(list[0]); + colon = " "; + } else if (size != 0) + out = status_prompt_complete_prefix(list, size); + if (out != NULL) { + xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); + out = tmp; + } + + for (i = 0; i < size; i++) + free((void *)list[i]); + +found: + free(copy); + free(list); + return (out); } diff --git a/tmux.h b/tmux.h index 83b6e4a8..9783a65f 100644 --- a/tmux.h +++ b/tmux.h @@ -43,9 +43,6 @@ extern char **environ; /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" -/* Default prompt history length. */ -#define PROMPT_HISTORY 100 - /* * Minimum layout cell size, NOT including separator line. The scroll region * cannot be one line in height so this must be at least two. From b6be03f01aaf10230bf4de4c49d56bc7d9bee9bf Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 07:16:14 +0000 Subject: [PATCH 137/703] If status line is at the top, the offset needs to be adjusted when drawing pane numbers. Based on a diff from John O'Meara. --- screen-redraw.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index e3369b82..babc74a9 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -32,7 +32,7 @@ int screen_redraw_check_active(u_int, u_int, int, struct window *, void screen_redraw_draw_borders(struct client *, int, u_int); void screen_redraw_draw_panes(struct client *, u_int); void screen_redraw_draw_status(struct client *, u_int); -void screen_redraw_draw_number(struct client *, struct window_pane *); +void screen_redraw_draw_number(struct client *, struct window_pane *, u_int); #define CELL_INSIDE 0 #define CELL_LEFTRIGHT 1 @@ -354,7 +354,7 @@ screen_redraw_draw_panes(struct client *c, u_int top) for (i = 0; i < wp->sy; i++) tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff); if (c->flags & CLIENT_IDENTIFY) - screen_redraw_draw_number(c, wp); + screen_redraw_draw_number(c, wp, top); } } @@ -372,7 +372,7 @@ screen_redraw_draw_status(struct client *c, u_int top) /* Draw number on a pane. */ void -screen_redraw_draw_number(struct client *c, struct window_pane *wp) +screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top) { struct tty *tty = &c->tty; struct session *s = c->session; @@ -396,6 +396,9 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp) px = wp->sx / 2; py = wp->sy / 2; xoff = wp->xoff; yoff = wp->yoff; + if (top) + yoff++; + if (wp->sx < len * 6 || wp->sy < 5) { tty_cursor(tty, xoff + px - len / 2, yoff + py); goto draw_text; From 73c871ba0a075bdd458a1b4208afd0e9bb293096 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 07:35:31 +0000 Subject: [PATCH 138/703] Simplify environ_push so it doesn't need the ARRAY_* functions. --- environ.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/environ.c b/environ.c index 0dd91783..11b8c849 100644 --- a/environ.c +++ b/environ.c @@ -157,23 +157,16 @@ environ_update(const char *vars, struct environ *srcenv, void environ_push(struct environ *env) { - ARRAY_DECL(, char *) varlist; - struct environ_entry *envent; - char **varp, *var; - u_int i; + struct environ_entry *envent; + char **vp, *v; - ARRAY_INIT(&varlist); - for (varp = environ; *varp != NULL; varp++) { - var = xstrdup(*varp); - var[strcspn(var, "=")] = '\0'; - ARRAY_ADD(&varlist, var); + for (vp = environ; *vp != NULL; vp++) { + v = xstrdup(*vp); + v[strcspn(v, "=")] = '\0'; + + unsetenv(v); + free(v); } - for (i = 0; i < ARRAY_LENGTH(&varlist); i++) { - var = ARRAY_ITEM(&varlist, i); - unsetenv(var); - free(var); - } - ARRAY_FREE(&varlist); RB_FOREACH(envent, environ, env) { if (envent->value != NULL) From 7becf326e3b756016f2df6230db9fcf714d44955 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 07:59:52 +0000 Subject: [PATCH 139/703] Use a TAILQ not array for find-window. --- cmd-find-window.c | 67 ++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/cmd-find-window.c b/cmd-find-window.c index 64e092bf..e92ae60f 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -59,11 +59,12 @@ struct cmd_find_window_data { struct winlink *wl; char *list_ctx; u_int pane_id; + TAILQ_ENTRY(cmd_find_window_data) entry; }; -ARRAY_DECL(cmd_find_window_data_list, struct cmd_find_window_data); +TAILQ_HEAD(cmd_find_window_list, cmd_find_window_data); u_int cmd_find_window_match_flags(struct args *); -void cmd_find_window_match(struct cmd_find_window_data_list *, int, +void cmd_find_window_match(struct cmd_find_window_list *, int, struct winlink *, const char *, const char *); u_int @@ -87,16 +88,16 @@ cmd_find_window_match_flags(struct args *args) } void -cmd_find_window_match(struct cmd_find_window_data_list *find_list, +cmd_find_window_match(struct cmd_find_window_list *find_list, int match_flags, struct winlink *wl, const char *str, const char *searchstr) { - struct cmd_find_window_data find_data; + struct cmd_find_window_data *find_data; struct window_pane *wp; u_int i, line; char *sres; - memset(&find_data, 0, sizeof find_data); + find_data = xcalloc(1, sizeof *find_data); i = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { @@ -104,30 +105,32 @@ cmd_find_window_match(struct cmd_find_window_data_list *find_list, if ((match_flags & CMD_FIND_WINDOW_BY_NAME) && fnmatch(searchstr, wl->window->name, 0) == 0) { - find_data.list_ctx = xstrdup(""); + find_data->list_ctx = xstrdup(""); break; } if ((match_flags & CMD_FIND_WINDOW_BY_TITLE) && fnmatch(searchstr, wp->base.title, 0) == 0) { - xasprintf(&find_data.list_ctx, + xasprintf(&find_data->list_ctx, "pane %u title: \"%s\"", i - 1, wp->base.title); break; } if (match_flags & CMD_FIND_WINDOW_BY_CONTENT && (sres = window_pane_search(wp, str, &line)) != NULL) { - xasprintf(&find_data.list_ctx, + xasprintf(&find_data->list_ctx, "pane %u line %u: \"%s\"", i - 1, line + 1, sres); free(sres); break; } } - if (find_data.list_ctx != NULL) { - find_data.wl = wl; - find_data.pane_id = i - 1; - ARRAY_ADD(find_list, find_data); - } + + if (find_data->list_ctx != NULL) { + find_data->wl = wl; + find_data->pane_id = i - 1; + TAILQ_INSERT_TAIL(find_list, find_data, entry); + } else + free(find_data); } enum cmd_retval @@ -138,7 +141,9 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window_choose_data *cdata; struct session *s; struct winlink *wl, *wm; - struct cmd_find_window_data_list find_list; + struct cmd_find_window_list find_list; + struct cmd_find_window_data *find_data; + struct cmd_find_window_data *find_data1; char *str, *searchstr; const char *template; u_int i, match_flags; @@ -158,21 +163,20 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) match_flags = cmd_find_window_match_flags(args); str = args->argv[0]; - ARRAY_INIT(&find_list); + TAILQ_INIT(&find_list); xasprintf(&searchstr, "*%s*", str); RB_FOREACH(wm, winlinks, &s->windows) cmd_find_window_match(&find_list, match_flags, wm, str, searchstr); free(searchstr); - if (ARRAY_LENGTH(&find_list) == 0) { + if (TAILQ_EMPTY(&find_list)) { cmdq_error(cmdq, "no windows matching: %s", str); - ARRAY_FREE(&find_list); return (CMD_RETURN_ERROR); } - if (ARRAY_LENGTH(&find_list) == 1) { - if (session_select(s, ARRAY_FIRST(&find_list).wl->idx) == 0) + if (TAILQ_NEXT(TAILQ_FIRST(&find_list), entry) == NULL) { + if (session_select(s, TAILQ_FIRST(&find_list)->wl->idx) == 0) server_redraw_session(s); recalculate_sizes(); goto out; @@ -181,30 +185,33 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) goto out; - for (i = 0; i < ARRAY_LENGTH(&find_list); i++) { - wm = ARRAY_ITEM(&find_list, i).wl; - + i = 0; + TAILQ_FOREACH(find_data, &find_list, entry) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); - cdata->idx = wm->idx; - cdata->wl = wm; + cdata->idx = find_data->wl->idx; + cdata->wl = find_data->wl; cdata->ft_template = xstrdup(template); - cdata->pane_id = ARRAY_ITEM(&find_list, i).pane_id; + cdata->pane_id = find_data->pane_id; format_add(cdata->ft, "line", "%u", i); format_add(cdata->ft, "window_find_matches", "%s", - ARRAY_ITEM(&find_list, i).list_ctx); - format_defaults(cdata->ft, NULL, s, wm, NULL); + find_data->list_ctx); + format_defaults(cdata->ft, NULL, s, find_data->wl, NULL); window_choose_add(wl->window->active, cdata); + + i++; } window_choose_ready(wl->window->active, 0, cmd_find_window_callback); out: - for (i = 0; i < ARRAY_LENGTH(&find_list); i++) - free(ARRAY_ITEM(&find_list, i).list_ctx); - ARRAY_FREE(&find_list); + TAILQ_FOREACH_SAFE(find_data, &find_list, entry, find_data1) { + free(find_data->list_ctx); + TAILQ_REMOVE(&find_list, find_data, entry); + free(find_data); + } return (CMD_RETURN_NORMAL); } From 1282bb81fe9fbb1b2ff1b63a9c4e8978444703c4 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 08:08:54 +0000 Subject: [PATCH 140/703] array.h can be local to window-choose.c now. --- tmux.h | 2 -- window-choose.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tmux.h b/tmux.h index 9783a65f..72e172e6 100644 --- a/tmux.h +++ b/tmux.h @@ -35,8 +35,6 @@ #include #include -#include "array.h" - extern char *__progname; extern char **environ; diff --git a/window-choose.c b/window-choose.c index 8672212a..d4b20bca 100644 --- a/window-choose.c +++ b/window-choose.c @@ -22,6 +22,7 @@ #include #include +#include "array.h" #include "tmux.h" struct screen *window_choose_init(struct window_pane *); From 63b7a031a52786e9ec72d8d284a46806bfdc848b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 7 May 2015 11:43:52 +0100 Subject: [PATCH 141/703] queue.h should come from compat.h. --- compat/imsg-buffer.c | 1 - compat/imsg.c | 1 - 2 files changed, 2 deletions(-) diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 450fdf1b..6756de0a 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -17,7 +17,6 @@ */ #include -#include #include #include diff --git a/compat/imsg.c b/compat/imsg.c index af0da44f..9db26ad6 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -17,7 +17,6 @@ */ #include -#include #include #include From 8e9b6e09485f3400aac9e6cd8a711e2cc1819b03 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 11:42:56 +0000 Subject: [PATCH 142/703] Style spacing nits. --- cfg.c | 2 +- cmd-find.c | 8 ++++---- key-bindings.c | 2 +- style.c | 4 ++-- window.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cfg.c b/cfg.c index 8b44ce24..ff43976e 100644 --- a/cfg.c +++ b/cfg.c @@ -121,7 +121,7 @@ cfg_add_cause(const char *fmt, ...) va_start(ap, fmt); xvasprintf(&msg, fmt, ap); - va_end (ap); + va_end(ap); cfg_ncauses++; cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); diff --git a/cmd-find.c b/cmd-find.c index ec4cf6c1..98f1e187 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -193,7 +193,7 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) RB_FOREACH(s, sessions, &sessions) { if (!session_has(s, fs->w)) continue; - slist = xreallocarray (slist, ssize + 1, sizeof *slist); + slist = xreallocarray(slist, ssize + 1, sizeof *slist); slist[ssize++] = s; } if (ssize == 0) @@ -201,7 +201,7 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) fs->s = cmd_find_best_session(slist, ssize, fs->flags); if (fs->s == NULL) goto fail; - free (slist); + free(slist); return (cmd_find_best_winlink_with_window(fs)); fail: @@ -329,7 +329,7 @@ cmd_find_current_client(struct cmd_q *cmdq) TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; - clist = xreallocarray (clist, csize + 1, sizeof *clist); + clist = xreallocarray(clist, csize + 1, sizeof *clist); clist[csize++] = c; } if (csize != 0) { @@ -739,7 +739,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) void cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) { - memset (fs, 0, sizeof *fs); + memset(fs, 0, sizeof *fs); fs->cmdq = cmdq; fs->flags = flags; diff --git a/key-bindings.c b/key-bindings.c index d56428b1..a3cb0bea 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -236,7 +236,7 @@ key_bindings_init(void) if (error != 0) fatalx("bad default key"); cmdq_run(cmdq, cmdlist, NULL); - cmd_list_free (cmdlist); + cmd_list_free(cmdlist); } cmdq_free(cmdq); } diff --git a/style.c b/style.c index 5534f118..9fafdd1d 100644 --- a/style.c +++ b/style.c @@ -168,12 +168,12 @@ style_update_new(struct options *oo, const char *name, const char *newname) o = options_find1(oo, newname); if (o == NULL) - o = options_set_style (oo, newname, "default", 0); + o = options_set_style(oo, newname, "default", 0); gc = &o->style; o = options_find1(oo, name); if (o == NULL) - o = options_set_number (oo, name, 8); + o = options_set_number(oo, name, 8); value = o->num; if (strstr(name, "-bg") != NULL) diff --git a/window.c b/window.c index a2277e2e..8366833a 100644 --- a/window.c +++ b/window.c @@ -302,7 +302,7 @@ window_create1(u_int sx, u_int sy) w->references = 0; w->id = next_window_id++; - RB_INSERT (windows, &windows, w); + RB_INSERT(windows, &windows, w); return (w); } From d174b9cfcc2d4c60bd89804ef63e5ee41ca6d490 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2015 14:07:16 +0000 Subject: [PATCH 143/703] Update environment when switching sessions as well as attaching, from Si Beaumont. --- cmd-switch-client.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 18de0eb1..369fc917 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -46,7 +46,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; - const char *tflag, *tablename; + const char *tflag, *tablename, *update; struct key_table *table; if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) @@ -119,6 +119,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } } + if (c != NULL && s != c->session) { + update = options_get_string(&s->options, "update-environment"); + environ_update(update, &c->environ, &s->environ); + } + if (c->session != NULL) c->last_session = c->session; c->session = s; From 879de25583a5a0bce57bd816ced96e0caf62c436 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 15:56:49 +0000 Subject: [PATCH 144/703] Remove some stuff that accidentally ended up here from portable, and remove a little-used debug function. --- grid.c | 29 ++++++++++------------------- server-client.c | 5 ----- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/grid.c b/grid.c index 2dc266d7..7e086143 100644 --- a/grid.c +++ b/grid.c @@ -49,15 +49,16 @@ const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; int grid_check_y(struct grid *, u_int); -#ifdef DEBUG -int -grid_check_y(struct grid *gd, u_int py) -{ - if ((py) >= (gd)->hsize + (gd)->sy) - log_fatalx("y out of range: %u", py); - return (0); -} -#else +void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); +void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, + u_int); +void grid_reflow_move(struct grid *, u_int *, struct grid_line *); +size_t grid_string_cells_fg(const struct grid_cell *, int *); +size_t grid_string_cells_bg(const struct grid_cell *, int *); +void grid_string_cells_code(const struct grid_cell *, + const struct grid_cell *, char *, size_t, int); + +/* Check grid y position. */ int grid_check_y(struct grid *gd, u_int py) { @@ -67,16 +68,6 @@ grid_check_y(struct grid *gd, u_int py) } return (0); } -#endif - -void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); -void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, - u_int); -void grid_reflow_move(struct grid *, u_int *, struct grid_line *); -size_t grid_string_cells_fg(const struct grid_cell *, int *); -size_t grid_string_cells_bg(const struct grid_cell *, int *); -void grid_string_cells_code(const struct grid_cell *, - const struct grid_cell *, char *, size_t, int); /* Create a new grid. */ struct grid * diff --git a/server-client.c b/server-client.c index ce3f3d5e..dd6eda1d 100644 --- a/server-client.c +++ b/server-client.c @@ -1178,11 +1178,6 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) return; c->flags |= CLIENT_IDENTIFIED; -#ifdef __CYGWIN__ - c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); - c->cwd = open(".", O_RDONLY); -#endif - if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; From c4a4bd6ac5748939c7c17beba9e14bee5929c552 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:18:04 +0000 Subject: [PATCH 145/703] Move input parser structs into input.c (removing fairly useless saved_cursor_[xy] formats as a side-effect). --- cmd-capture-pane.c | 14 ++++--- cmd-send-keys.c | 18 +-------- format.c | 2 - input.c | 93 ++++++++++++++++++++++++++++++++++++++++++---- tmux.1 | 2 - tmux.h | 54 ++------------------------- 6 files changed, 100 insertions(+), 83 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index b44ebe9d..a348e155 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -57,15 +57,17 @@ char * cmd_capture_pane_pending(struct args *args, struct window_pane *wp, size_t *len) { - char *buf, *line, tmp[5]; - size_t linelen; - u_int i; + struct evbuffer *pending; + char *buf, *line, tmp[5]; + size_t linelen; + u_int i; - if (wp->ictx.since_ground == NULL) + pending = input_pending(wp); + if (pending == NULL) return (xstrdup("")); - line = EVBUFFER_DATA(wp->ictx.since_ground); - linelen = EVBUFFER_LENGTH(wp->ictx.since_ground); + line = EVBUFFER_DATA(pending); + linelen = EVBUFFER_LENGTH(pending); buf = xstrdup(""); if (args_has(args, 'C')) { diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 27da410d..b1f8d672 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -52,7 +52,6 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct mouse_event *m = &cmdq->item->mouse; struct window_pane *wp; struct session *s; - struct input_ctx *ictx; const u_char *str; int i, key; @@ -78,21 +77,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'R')) { - ictx = &wp->ictx; - - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; - - if (wp->mode == NULL) - screen_write_start(&ictx->ctx, wp, &wp->base); - else - screen_write_start(&ictx->ctx, NULL, &wp->base); - screen_write_reset(&ictx->ctx); - screen_write_stop(&ictx->ctx); - } + if (args_has(args, 'R')) + input_reset(wp); for (i = 0; i < args->argc; i++) { str = args->argv[i]; diff --git a/format.c b/format.c index ac28604d..39351fc3 100644 --- a/format.c +++ b/format.c @@ -715,8 +715,6 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "cursor_y", "%u", wp->base.cy); format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); - format_add(ft, "saved_cursor_x", "%u", wp->ictx.old_cx); - format_add(ft, "saved_cursor_y", "%u", wp->ictx.old_cy); format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); diff --git a/input.c b/input.c index 9f7d4413..384ab45d 100644 --- a/input.c +++ b/input.c @@ -46,6 +46,55 @@ * be passed to the underlying teminal(s). */ +/* Input parser cell. */ +struct input_cell { + struct grid_cell cell; + int set; + int g0set; /* 1 if ACS */ + int g1set; /* 1 if ACS */ +}; + +/* Input parser context. */ +struct input_ctx { + struct window_pane *wp; + struct screen_write_ctx ctx; + + struct input_cell cell; + + struct input_cell old_cell; + u_int old_cx; + u_int old_cy; + + u_char interm_buf[4]; + size_t interm_len; + + u_char param_buf[64]; + size_t param_len; + +#define INPUT_BUF_START 32 +#define INPUT_BUF_LIMIT 1048576 + u_char *input_buf; + size_t input_len; + size_t input_space; + + int param_list[24]; /* -1 not present */ + u_int param_list_len; + + struct utf8_data utf8data; + + int ch; + int flags; +#define INPUT_DISCARD 0x1 + + const struct input_state *state; + + /* + * All input received since we were last in the ground state. Sent to + * control clients on connection. + */ + struct evbuffer *since_ground; +}; + /* Helper functions. */ struct input_transition; int input_split(struct input_ctx *); @@ -706,7 +755,9 @@ input_reset_cell(struct input_ctx *ictx) void input_init(struct window_pane *wp) { - struct input_ctx *ictx = &wp->ictx; + struct input_ctx *ictx; + + ictx = wp->ictx = xcalloc(1, sizeof *ictx); input_reset_cell(ictx); @@ -732,18 +783,46 @@ input_init(struct window_pane *wp) void input_free(struct window_pane *wp) { - if (wp == NULL) - return; + struct input_ctx *ictx = wp->ictx; - free(wp->ictx.input_buf); - evbuffer_free(wp->ictx.since_ground); + free(ictx->input_buf); + evbuffer_free(ictx->since_ground); + + free (ictx); + wp->ictx = NULL; +} + +/* Reset input state and clear screen. */ +void +input_reset(struct window_pane *wp) +{ + struct input_ctx *ictx = wp->ictx; + + memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; + + if (wp->mode == NULL) + screen_write_start(&ictx->ctx, wp, &wp->base); + else + screen_write_start(&ictx->ctx, NULL, &wp->base); + screen_write_reset(&ictx->ctx); + screen_write_stop(&ictx->ctx); +} + +/* Return pending data. */ +struct evbuffer * +input_pending(struct window_pane *wp) +{ + return (wp->ictx->since_ground); } /* Change input state. */ void input_set_state(struct window_pane *wp, const struct input_transition *itr) { - struct input_ctx *ictx = &wp->ictx; + struct input_ctx *ictx = wp->ictx; if (ictx->state->exit != NULL) ictx->state->exit(ictx); @@ -756,7 +835,7 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr) void input_parse(struct window_pane *wp) { - struct input_ctx *ictx = &wp->ictx; + struct input_ctx *ictx = wp->ictx; const struct input_transition *itr; struct evbuffer *evb = wp->event->input; u_char *buf; diff --git a/tmux.1 b/tmux.1 index ea178b78..9debeaf4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3318,8 +3318,6 @@ The following variables are available, where appropriate: .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" -.It Li "saved_cursor_x" Ta "" Ta "Saved cursor X in pane" -.It Li "saved_cursor_y" Ta "" Ta "Saved cursor Y in pane" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" diff --git a/tmux.h b/tmux.h index 72e172e6..ad79d79d 100644 --- a/tmux.h +++ b/tmux.h @@ -785,55 +785,6 @@ struct screen_write_ctx { #define screen_hsize(s) ((s)->grid->hsize) #define screen_hlimit(s) ((s)->grid->hlimit) -/* Input parser cell. */ -struct input_cell { - struct grid_cell cell; - int set; - int g0set; /* 1 if ACS */ - int g1set; /* 1 if ACS */ -}; - -/* Input parser context. */ -struct input_ctx { - struct window_pane *wp; - struct screen_write_ctx ctx; - - struct input_cell cell; - - struct input_cell old_cell; - u_int old_cx; - u_int old_cy; - - u_char interm_buf[4]; - size_t interm_len; - - u_char param_buf[64]; - size_t param_len; - -#define INPUT_BUF_START 32 -#define INPUT_BUF_LIMIT 1048576 - u_char *input_buf; - size_t input_len; - size_t input_space; - - int param_list[24]; /* -1 not present */ - u_int param_list_len; - - struct utf8_data utf8data; - - int ch; - int flags; -#define INPUT_DISCARD 0x1 - - const struct input_state *state; - - /* - * All input received since we were last in the ground state. Sent to - * control clients on connection. - */ - struct evbuffer *since_ground; -}; - /* * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. @@ -881,6 +832,7 @@ struct window_choose_mode_item { }; /* Child window structure. */ +struct input_ctx; struct window_pane { u_int id; u_int active_point; @@ -920,7 +872,7 @@ struct window_pane { int fd; struct bufferevent *event; - struct input_ctx ictx; + struct input_ctx *ictx; struct grid_cell colgc; @@ -1983,6 +1935,8 @@ void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); +void input_reset(struct window_pane *); +struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); /* input-key.c */ From a538141a72882e10988b68d7d1ede94cbb7140c1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:23:34 +0000 Subject: [PATCH 146/703] window_choose_mode_item can move into window-choose.c. --- tmux.h | 8 -------- window-choose.c | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tmux.h b/tmux.h index ad79d79d..f48797dc 100644 --- a/tmux.h +++ b/tmux.h @@ -823,14 +823,6 @@ struct window_choose_data { char *command; }; -struct window_choose_mode_item { - struct window_choose_data *wcd; - char *name; - int pos; - int state; -#define TREE_EXPANDED 0x1 -}; - /* Child window structure. */ struct input_ctx; struct window_pane { diff --git a/window-choose.c b/window-choose.c index d4b20bca..2af56e23 100644 --- a/window-choose.c +++ b/window-choose.c @@ -60,6 +60,14 @@ const struct window_mode window_choose_mode = { NULL, }; +struct window_choose_mode_item { + struct window_choose_data *wcd; + char *name; + int pos; + int state; +#define TREE_EXPANDED 0x1 +}; + struct window_choose_mode_data { struct screen screen; From 74b2c40b1b2f38b1aafe4178cb33db63d3be8de0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:33:29 +0000 Subject: [PATCH 147/703] mode_key_entry can go into mode-key.c; also a few spaces->tabs. --- mode-key.c | 14 ++++++++++++++ tmux.h | 27 ++++++++------------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/mode-key.c b/mode-key.c index c06d7ed5..5ed45bd8 100644 --- a/mode-key.c +++ b/mode-key.c @@ -38,6 +38,20 @@ * (any matching MODEKEYEDIT_SWITCHMODE*) are special-cased to do this. */ +/* Entry in the default mode key tables. */ +struct mode_key_entry { + int key; + + /* + * Editing mode for vi: 0 is edit mode, keys not in the table are + * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table + * are returned as MODEKEY_NONE. This is also matched on, allowing some + * keys to be bound in edit mode. + */ + int mode; + enum mode_key_cmd cmd; +}; + /* Edit keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { { MODEKEYEDIT_BACKSPACE, "backspace" }, diff --git a/tmux.h b/tmux.h index f48797dc..3c9db501 100644 --- a/tmux.h +++ b/tmux.h @@ -566,20 +566,6 @@ enum mode_key_cmd { MODEKEYCOPY_UP, }; -/* Entry in the default mode key tables. */ -struct mode_key_entry { - int key; - - /* - * Editing mode for vi: 0 is edit mode, keys not in the table are - * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table - * are returned as MODEKEY_NONE. This is also matched on, allowing some - * keys to be bound in edit mode. - */ - int mode; - enum mode_key_cmd cmd; -}; - /* Data required while mode keys are in use. */ struct mode_key_data { struct mode_key_tree *tree; @@ -607,6 +593,7 @@ struct mode_key_cmdstr { }; /* Named mode key table description. */ +struct mode_key_entry; struct mode_key_table { const char *name; const struct mode_key_cmdstr *cmdstr; @@ -1192,16 +1179,16 @@ struct tty_ctx { /* Saved message entry. */ struct message_entry { - char *msg; - u_int msg_num; - time_t msg_time; + char *msg; + u_int msg_num; + time_t msg_time; TAILQ_ENTRY(message_entry) entry; }; /* Status output data from a job. */ struct status_out { - char *cmd; - char *out; + char *cmd; + char *out; RB_ENTRY(status_out) entry; }; @@ -1322,6 +1309,7 @@ struct cmd { TAILQ_ENTRY(cmd) qentry; }; + struct cmd_list { int references; TAILQ_HEAD(, cmd) list; @@ -1394,6 +1382,7 @@ struct key_binding { RB_ENTRY(key_binding) entry; }; RB_HEAD(key_bindings, key_binding); + struct key_table { const char *name; struct key_bindings key_bindings; From 592cb73a69e23d321a29ae367d52b9e9b4f506c2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:44:03 +0000 Subject: [PATCH 148/703] grid_marker_cell is no longer used. --- tmux.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.h b/tmux.h index 3c9db501..f1575d21 100644 --- a/tmux.h +++ b/tmux.h @@ -1940,7 +1940,6 @@ int attributes_fromstring(const char *); /* grid.c */ extern const struct grid_cell grid_default_cell; -extern const struct grid_cell grid_marker_cell; struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); From 92faa2eaebd32117f01b0b7d7ae81abdfde2d935 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 8 May 2015 16:48:12 +0000 Subject: [PATCH 149/703] Put the tty structs together, and tabify. --- tmux.h | 96 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/tmux.h b/tmux.h index f1575d21..6d4e29fd 100644 --- a/tmux.h +++ b/tmux.h @@ -689,7 +689,7 @@ struct options_entry { } type; char *str; - long long num; + long long num; struct grid_cell style; RB_ENTRY(options_entry) entry; @@ -804,7 +804,7 @@ struct window_choose_data { struct winlink *wl; int pane_id; - char *ft_template; + char *ft_template; struct format_tree *ft; char *command; @@ -882,7 +882,7 @@ struct window { u_int id; char *name; struct event name_timer; - struct timeval silence_timer; + struct timeval silence_timer; struct window_pane *active; struct window_pane *last; @@ -923,7 +923,7 @@ struct winlink { struct grid_cell status_cell; char *status_text; - int flags; + int flags; #define WINLINK_BELL 0x1 #define WINLINK_ACTIVITY 0x2 #define WINLINK_SILENCE 0x4 @@ -1015,7 +1015,7 @@ struct session { #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; - u_int attached; + u_int attached; struct termios *tio; @@ -1028,33 +1028,6 @@ struct session { }; RB_HEAD(sessions, session); -/* TTY information. */ -struct tty_key { - char ch; - int key; - - struct tty_key *left; - struct tty_key *right; - - struct tty_key *next; -}; - -struct tty_term { - char *name; - u_int references; - - char acs[UCHAR_MAX + 1][2]; - - struct tty_code codes[NTTYCODE]; - -#define TERM_256COLOURS 0x1 -#define TERM_EARLYWRAP 0x2 - int flags; - - LIST_ENTRY(tty_term) entry; -}; -LIST_HEAD(tty_terms, tty_term); - /* Mouse button masks. */ #define MOUSE_MASK_BUTTONS 3 #define MOUSE_MASK_SHIFT 4 @@ -1096,6 +1069,33 @@ struct mouse_event { u_int sgr_b; }; +/* TTY information. */ +struct tty_key { + char ch; + int key; + + struct tty_key *left; + struct tty_key *right; + + struct tty_key *next; +}; + +struct tty_term { + char *name; + u_int references; + + char acs[UCHAR_MAX + 1][2]; + + struct tty_code codes[NTTYCODE]; + +#define TERM_256COLOURS 0x1 +#define TERM_EARLYWRAP 0x2 + int flags; + + LIST_ENTRY(tty_term) entry; +}; +LIST_HEAD(tty_terms, tty_term); + struct tty { struct client *client; @@ -1143,7 +1143,7 @@ struct tty { void (*mouse_drag_update)(struct client *, struct mouse_event *); void (*mouse_drag_release)(struct client *, - struct mouse_event *); + struct mouse_event *); struct event key_timer; struct tty_key *key_tree; @@ -1217,7 +1217,7 @@ struct client { void (*stdin_callback)(struct client *, int, void *); void *stdin_callback_data; struct evbuffer *stdin_data; - int stdin_closed; + int stdin_closed; struct evbuffer *stdout_data; struct evbuffer *stderr_data; @@ -1263,7 +1263,7 @@ struct client { int (*prompt_callbackfn)(void *, const char *); void (*prompt_freefn)(void *); void *prompt_data; - u_int prompt_hindex; + u_int prompt_hindex; #define PROMPT_SINGLE 0x1 int prompt_flags; @@ -1292,8 +1292,8 @@ RB_HEAD(args_tree, args_entry); struct args { struct args_tree tree; - int argc; - char **argv; + int argc; + char **argv; }; /* Command and list of commands. */ @@ -1311,8 +1311,8 @@ struct cmd { }; struct cmd_list { - int references; - TAILQ_HEAD(, cmd) list; + int references; + TAILQ_HEAD(, cmd) list; }; /* Command return values. */ @@ -1351,7 +1351,7 @@ struct cmd_q { void (*emptyfn)(struct cmd_q *); void *data; - TAILQ_ENTRY(cmd_q) waitentry; + TAILQ_ENTRY(cmd_q) waitentry; }; /* Command definition. */ @@ -1413,8 +1413,8 @@ struct options_table_entry { const char *name; enum options_table_type type; - u_int minimum; - u_int maximum; + u_int minimum; + u_int maximum; const char **choices; const char *default_str; @@ -1801,7 +1801,7 @@ void printflike(2, 3) cmdq_print(struct cmd_q *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmd_q *, const char *, ...); void cmdq_guard(struct cmd_q *, const char *, int); void cmdq_run(struct cmd_q *, struct cmd_list *, - struct mouse_event *); + struct mouse_event *); void cmdq_append(struct cmd_q *, struct cmd_list *, struct mouse_event *); int cmdq_continue(struct cmd_q *); @@ -1823,8 +1823,8 @@ RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); extern struct key_tables key_tables; int key_table_cmp(struct key_table *, struct key_table *); int key_bindings_cmp(struct key_binding *, struct key_binding *); -struct key_table *key_bindings_get_table(const char *, int); -void key_bindings_unref_table(struct key_table *); +struct key_table *key_bindings_get_table(const char *, int); +void key_bindings_unref_table(struct key_table *); void key_bindings_add(const char *, int, int, struct cmd_list *); void key_bindings_remove(const char *, int); void key_bindings_remove_table(const char *); @@ -1846,7 +1846,7 @@ void server_add_accept(int); /* server-client.c */ void server_client_handle_key(struct client *, int); void server_client_create(int); -int server_client_open(struct client *, char **); +int server_client_open(struct client *, char **); void server_client_lost(struct client *); void server_client_callback(int, short, void *); void server_client_status_timer(void); @@ -2097,9 +2097,9 @@ void window_lost_pane(struct window *, struct window_pane *); void window_remove_pane(struct window *, struct window_pane *); struct window_pane *window_pane_at_index(struct window *, u_int); struct window_pane *window_pane_next_by_number(struct window *, - struct window_pane *, u_int); + struct window_pane *, u_int); struct window_pane *window_pane_previous_by_number(struct window *, - struct window_pane *, u_int); + struct window_pane *, u_int); int window_pane_index(struct window_pane *, u_int *); u_int window_count_panes(struct window *); void window_destroy_panes(struct window *); From 4165ed96f8f0c494c7bd4f793176e508b6b6581d Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 9 May 2015 20:03:24 +0100 Subject: [PATCH 150/703] Add back __CYGWIN__ block This went missing during the merge from OpenBSD. --- server-client.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server-client.c b/server-client.c index a9348997..84ae86bc 100644 --- a/server-client.c +++ b/server-client.c @@ -1187,6 +1187,11 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) return; c->flags |= CLIENT_IDENTIFIED; +#ifdef __CYGWIN__ + c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); + c->cwd = open(".", O_RDONLY); +#endif + if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; From 44364d7112037bebb0bb1d20f72b12b1f736fa95 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 May 2015 10:10:16 +0000 Subject: [PATCH 151/703] Remove the c0-* options which never really worked satisfactorily. Going to try something else... --- input.c | 16 +++------------- tmux.1 | 18 ------------------ tmux.h | 5 ----- window.c | 40 ---------------------------------------- 4 files changed, 3 insertions(+), 76 deletions(-) diff --git a/input.c b/input.c index 384ab45d..886d561d 100644 --- a/input.c +++ b/input.c @@ -1069,7 +1069,6 @@ input_c0_dispatch(struct input_ctx *ictx) struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; - u_int trigger; log_debug("%s: '%c", __func__, ictx->ch); @@ -1081,7 +1080,7 @@ input_c0_dispatch(struct input_ctx *ictx) break; case '\010': /* BS */ screen_write_backspace(sctx); - goto count_c0; + break; case '\011': /* HT */ /* Don't tab beyond the end of the line. */ if (s->cx >= screen_size_x(s) - 1) @@ -1098,10 +1097,10 @@ input_c0_dispatch(struct input_ctx *ictx) case '\013': /* VT */ case '\014': /* FF */ screen_write_linefeed(sctx, 0); - goto count_c0; + break; case '\015': /* CR */ screen_write_carriagereturn(sctx); - goto count_c0; + break; case '\016': /* SO */ ictx->cell.set = 1; break; @@ -1113,15 +1112,6 @@ input_c0_dispatch(struct input_ctx *ictx) break; } - return (0); - -count_c0: - trigger = options_get_number(&wp->window->options, "c0-change-trigger"); - if (trigger != 0 && ++wp->changes >= trigger) { - wp->flags |= PANE_DROP; - window_pane_timer_start(wp); - } - return (0); } diff --git a/tmux.1 b/tmux.1 index 9debeaf4..79dfa4b4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2886,24 +2886,6 @@ used when the .Ic automatic-rename option is enabled. .Pp -.It Ic c0-change-interval Ar interval -.It Ic c0-change-trigger Ar trigger -These two options configure a simple form of rate limiting for a pane. -If -.Nm -sees more than -.Ar trigger -C0 sequences that modify the screen (for example, carriage returns, linefeeds -or backspaces) in one millisecond, it will stop updating the pane immediately and -instead redraw it entirely every -.Ar interval -milliseconds. -This helps to prevent fast output (such as -.Xr yes 1 ) -overwhelming the terminal. -The default is a trigger of 250 and an interval of 100. -A trigger of zero disables the rate limiting. -.Pp .It Ic clock-mode-colour Ar colour Set clock colour. .Pp diff --git a/tmux.h b/tmux.h index 6d4e29fd..ccbdb763 100644 --- a/tmux.h +++ b/tmux.h @@ -844,10 +844,6 @@ struct window_pane { char tty[TTY_NAME_MAX]; int status; - u_int changes; - struct event changes_timer; - u_int changes_redraw; - int fd; struct bufferevent *event; @@ -2107,7 +2103,6 @@ struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); -void window_pane_timer_start(struct window_pane *); int window_pane_spawn(struct window_pane *, int, char **, const char *, const char *, int, struct environ *, struct termios *, char **); diff --git a/window.c b/window.c index 8366833a..7edfed42 100644 --- a/window.c +++ b/window.c @@ -58,7 +58,6 @@ u_int next_window_pane_id; u_int next_window_id; u_int next_active_point; -void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); @@ -741,9 +740,6 @@ window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); - if (event_initialized(&wp->changes_timer)) - evtimer_del(&wp->changes_timer); - if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); @@ -877,42 +873,6 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, return (0); } -void -window_pane_timer_start(struct window_pane *wp) -{ - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = 1000; - - evtimer_del(&wp->changes_timer); - evtimer_set(&wp->changes_timer, window_pane_timer_callback, wp); - evtimer_add(&wp->changes_timer, &tv); -} - -void -window_pane_timer_callback(unused int fd, unused short events, void *data) -{ - struct window_pane *wp = data; - struct window *w = wp->window; - u_int interval, trigger; - - interval = options_get_number(&w->options, "c0-change-interval"); - trigger = options_get_number(&w->options, "c0-change-trigger"); - - if (wp->changes_redraw++ == interval) { - wp->flags |= PANE_REDRAW; - wp->changes_redraw = 0; - } - - if (trigger == 0 || wp->changes < trigger) { - wp->flags |= PANE_REDRAW; - wp->flags &= ~PANE_DROP; - } else - window_pane_timer_start(wp); - wp->changes = 0; -} - void window_pane_read_callback(unused struct bufferevent *bufev, void *data) { From b833fabeb2334c44406bd54ff7cc804d20c546f6 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 11 May 2015 10:58:22 +0000 Subject: [PATCH 152/703] Left the c0-* options behind in the table. --- options-table.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/options-table.c b/options-table.c index 21758543..9869e14f 100644 --- a/options-table.c +++ b/options-table.c @@ -485,20 +485,6 @@ const struct options_table_entry window_options_table[] = { "#{?pane_dead,[dead],}" }, - { .name = "c0-change-trigger", - .type = OPTIONS_TABLE_NUMBER, - .default_num = 250, - .minimum = 0, - .maximum = USHRT_MAX - }, - - { .name = "c0-change-interval", - .type = OPTIONS_TABLE_NUMBER, - .default_num = 100, - .minimum = 1, - .maximum = USHRT_MAX - }, - { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, .default_num = 4 From e958db09a72c2308d3edc8384be02714eb068ae7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 15:27:46 +0000 Subject: [PATCH 153/703] Add bell-action "other" to pass through bells in all windows except the current, suggested by Jan ONDREJ. --- options-table.c | 2 +- server-window.c | 11 ++++++++--- tmux.1 | 8 +++++--- tmux.h | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/options-table.c b/options-table.c index 9869e14f..44d36dca 100644 --- a/options-table.c +++ b/options-table.c @@ -49,7 +49,7 @@ const char *options_table_status_position_list[] = { "top", "bottom", NULL }; const char *options_table_bell_action_list[] = { - "none", "any", "current", NULL + "none", "any", "current", "other", NULL }; /* Server options. */ diff --git a/server-window.c b/server-window.c index 4385dd90..1b3938d3 100644 --- a/server-window.c +++ b/server-window.c @@ -77,13 +77,18 @@ server_window_check_bell(struct session *s, struct winlink *wl) if (c->session != s || c->flags & CLIENT_CONTROL) continue; if (!visual) { - if (c->session->curw->window == w || action == BELL_ANY) + if ((action == BELL_CURRENT && + c->session->curw->window == w) || + (action == BELL_OTHER && + c->session->curw->window != w) || + action == BELL_ANY) tty_bell(&c->tty); continue; } - if (c->session->curw->window == w) + if (action == BELL_CURRENT && c->session->curw->window == w) status_message_set(c, "Bell in current window"); - else if (action == BELL_ANY) + else if (action == BELL_ANY || (action == BELL_OTHER && + c->session->curw->window != w)) status_message_set(c, "Bell in window %d", wl->idx); } diff --git a/tmux.1 b/tmux.1 index 79dfa4b4..7d9c0dc8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2375,16 +2375,18 @@ Set the base index from which an unused index should be searched when a new window is created. The default is zero. .It Xo Ic bell-action -.Op Ic any | none | current +.Op Ic any | none | current | other .Xc Set action on window bell. .Ic any means a bell in any window linked to a session causes a bell in the current window of that session, .Ic none -means all bells are ignored and +means all bells are ignored, .Ic current -means only bells in windows other than the current window are ignored. +means only bells in windows other than the current window are ignored and +.Ic other +means bells in the current window are ignored but not those in other windows. .It Xo Ic bell-on-alert .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index ccbdb763..f7c44b20 100644 --- a/tmux.h +++ b/tmux.h @@ -75,6 +75,7 @@ extern char **environ; #define BELL_NONE 0 #define BELL_ANY 1 #define BELL_CURRENT 2 +#define BELL_OTHER 3 /* Special key codes. */ #define KEYC_NONE 0xfff From ec34439f9cddcbde78d5fa2c628e454d2df5dd53 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 15:29:29 +0000 Subject: [PATCH 154/703] Add a session_alerts format which is a list of all the alerts in the current session in symbolic form (something like "0!,4~,5!"). Use this in the default set-titles-string. Prompted by a request from Jan ONDREJ. --- format.c | 20 ++++++++++++++++++++ options-table.c | 2 +- tmux.1 | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 39351fc3..db6f9231 100644 --- a/format.c +++ b/format.c @@ -494,6 +494,8 @@ format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; time_t t; + struct winlink *wl; + char alerts[256], tmp[16]; ft->s = s; @@ -518,6 +520,24 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); + + *alerts = '\0'; + RB_FOREACH (wl, winlinks, &s->windows) { + if ((wl->flags & WINLINK_ALERTFLAGS) == 0) + continue; + snprintf(tmp, sizeof tmp, "%u", wl->idx); + + if (*alerts != '\0') + strlcat(alerts, ",", sizeof alerts); + strlcat(alerts, tmp, sizeof alerts); + if (wl->flags & WINLINK_ACTIVITY) + strlcat(alerts, "#", sizeof alerts); + if (wl->flags & WINLINK_BELL) + strlcat(alerts, "!", sizeof alerts); + if (wl->flags & WINLINK_SILENCE) + strlcat(alerts, "~", sizeof alerts); + } + format_add(ft, "session_alerts", "%s", alerts); } /* Set default format keys for a client. */ diff --git a/options-table.c b/options-table.c index 44d36dca..4ad45d37 100644 --- a/options-table.c +++ b/options-table.c @@ -296,7 +296,7 @@ const struct options_table_entry session_options_table[] = { { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, - .default_str = "#S:#I:#W - \"#T\"" + .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" }, { .name = "status", diff --git a/tmux.1 b/tmux.1 index 7d9c0dc8..f5a0aaa3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3304,6 +3304,7 @@ The following variables are available, where appropriate: .It Li "pane_width" Ta "" Ta "Width of pane" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_activity" Ta "" Ta "Integer time of session last activity" .It Li "session_activity_string" Ta "" Ta "String time of session last activity" From 37ae8a9e0fc2cd7ab2387a9a762a39dc0ff6723c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 19:36:08 +0000 Subject: [PATCH 155/703] Tidy blank lines when outputting server info. --- cmd-show-messages.c | 48 +++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 028b4bb0..2a04bd93 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -47,11 +47,11 @@ const struct cmd_entry cmd_server_info_entry = { cmd_show_messages_exec }; -void cmd_show_messages_server(struct cmd_q *); -void cmd_show_messages_terminals(struct cmd_q *); -void cmd_show_messages_jobs(struct cmd_q *); +int cmd_show_messages_server(struct cmd_q *); +int cmd_show_messages_terminals(struct cmd_q *, int); +int cmd_show_messages_jobs(struct cmd_q *, int); -void +int cmd_show_messages_server(struct cmd_q *cmdq) { char *tim; @@ -63,10 +63,12 @@ cmd_show_messages_server(struct cmd_q *cmdq) cmdq_print(cmdq, "socket path %s", socket_path); cmdq_print(cmdq, "debug level %d", debug_level); cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); + + return (1); } -void -cmd_show_messages_terminals(struct cmd_q *cmdq) +int +cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { struct tty_term *term; const struct tty_term_code_entry *ent; @@ -76,8 +78,11 @@ cmd_show_messages_terminals(struct cmd_q *cmdq) n = 0; LIST_FOREACH(term, &tty_terms, entry) { - cmdq_print(cmdq, - "Terminal %u: %s [references=%u, flags=0x%x]:", + if (blank) { + cmdq_print(cmdq, "%s", ""); + blank = 0; + } + cmdq_print(cmdq, "Terminal %u: %s [references=%u, flags=0x%x]:", n, term->name, term->references, term->flags); n++; for (i = 0; i < NTTYCODE; i++) { @@ -106,21 +111,26 @@ cmd_show_messages_terminals(struct cmd_q *cmdq) } } } + return (n != 0); } -void -cmd_show_messages_jobs(struct cmd_q *cmdq) +int +cmd_show_messages_jobs(struct cmd_q *cmdq, int blank) { struct job *job; u_int n; n = 0; LIST_FOREACH(job, &all_jobs, lentry) { - cmdq_print(cmdq, - "Job %u: %s [fd=%d, pid=%d, status=%d]", + if (blank) { + cmdq_print(cmdq, "%s", ""); + blank = 0; + } + cmdq_print(cmdq, "Job %u: %s [fd=%d, pid=%d, status=%d]", n, job->cmd, job->fd, job->pid, job->status); n++; } + return (n != 0); } enum cmd_retval @@ -130,23 +140,19 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c; struct message_entry *msg; char *tim; - int done; + int done, blank; - done = 0; + done = blank = 0; if (args_has(args, 'I') || self->entry == &cmd_server_info_entry) { - cmd_show_messages_server(cmdq); + blank = cmd_show_messages_server(cmdq); done = 1; } if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { - if (done) - cmdq_print(cmdq, "%s", ""); - cmd_show_messages_terminals(cmdq); + blank = cmd_show_messages_terminals(cmdq, blank); done = 1; } if (args_has(args, 'J') || self->entry == &cmd_server_info_entry) { - if (done) - cmdq_print(cmdq, "%s", ""); - cmd_show_messages_jobs(cmdq); + cmd_show_messages_jobs(cmdq, blank); done = 1; } if (done) From 3f4ee98162cd5bb7000f93fec0e631e123b1281d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 12 May 2015 22:40:38 +0000 Subject: [PATCH 156/703] To replace c0-*, add a high watermark to the pty event, and also backoff when the any of the ttys the pane is going to write to has buffered enough data. --- tmux.h | 16 ++++++++++++++-- tty.c | 26 +++++++++++++++++--------- window.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 79 insertions(+), 19 deletions(-) diff --git a/tmux.h b/tmux.h index f7c44b20..bc47a268 100644 --- a/tmux.h +++ b/tmux.h @@ -56,6 +56,16 @@ extern char **environ; */ #define UTF8_SIZE 9 +/* + * READ_SIZE is the maximum size of data to hold from a pty (the event high + * watermark). READ_BACKOFF is the amount of data waiting to be output to a tty + * before pty reads will be backed off. READ_TIME is how long to back off + * before the next read (in microseconds) if a tty is above READ_BACKOFF. + */ +#define READ_SIZE 1024 +#define READ_BACKOFF 512 +#define READ_TIME 100 + /* Fatal errors. */ #define fatal(msg) log_fatal("%s: %s", __func__, msg); #define fatalx(msg) log_fatalx("%s: %s", __func__, msg); @@ -847,6 +857,7 @@ struct window_pane { int fd; struct bufferevent *event; + struct event timer; struct input_ctx *ictx; @@ -1595,8 +1606,9 @@ void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); -void tty_write( - void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); +void tty_write(void (*)(struct tty *, const struct tty_ctx *), + struct tty_ctx *); +int tty_client_ready(struct client *, struct window_pane *wp); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); void tty_cmd_cell(struct tty *, const struct tty_ctx *); void tty_cmd_clearendofline(struct tty *, const struct tty_ctx *); diff --git a/tty.c b/tty.c index a9f49c9b..a58ca937 100644 --- a/tty.c +++ b/tty.c @@ -723,9 +723,23 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, tty_update_mode(tty, tty->mode, s); } +int +tty_client_ready(struct client *c, struct window_pane *wp) +{ + if (c->session == NULL || c->tty.term == NULL) + return (0); + if (c->flags & CLIENT_SUSPENDED) + return (0); + if (c->tty.flags & TTY_FREEZE) + return (0); + if (c->session->curw->window != wp->window) + return (0); + return (1); +} + void -tty_write( - void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) +tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), + struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct client *c; @@ -740,13 +754,7 @@ tty_write( return; TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || c->tty.term == NULL) - continue; - if (c->flags & CLIENT_SUSPENDED) - continue; - if (c->tty.flags & TTY_FREEZE) - continue; - if (c->session->curw->window != wp->window) + if (!tty_client_ready(c, wp)) continue; ctx->xoff = wp->xoff; diff --git a/window.c b/window.c index 7edfed42..bc17656d 100644 --- a/window.c +++ b/window.c @@ -58,6 +58,7 @@ u_int next_window_pane_id; u_int next_window_id; u_int next_active_point; +void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); @@ -740,6 +741,9 @@ window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); + if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); @@ -867,28 +871,53 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); + + bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); free(cmd); return (0); } +void +window_pane_timer_callback(unused int fd, unused short events, void *data) +{ + window_pane_read_callback(NULL, data); +} + void window_pane_read_callback(unused struct bufferevent *bufev, void *data) { - struct window_pane *wp = data; - char *new_data; - size_t new_size; + struct window_pane *wp = data; + struct evbuffer *evb = wp->event->input; + char *new_data; + size_t new_size, available; + struct client *c; + struct timeval tv; - new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; + if (event_initialized(&wp->timer)) + evtimer_del(&wp->timer); + + log_debug("%%%u has %zu bytes", wp->id, EVBUFFER_LENGTH(evb)); + + TAILQ_FOREACH(c, &clients, entry) { + if (!tty_client_ready(c, wp)) + continue; + + available = EVBUFFER_LENGTH(c->tty.event->output); + if (available > READ_BACKOFF) + goto start_timer; + } + + new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(wp->event->input); + new_data = EVBUFFER_DATA(evb); bufferevent_write(wp->pipe_event, new_data, new_size); } input_parse(wp); - wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + wp->pipe_off = EVBUFFER_LENGTH(evb); /* * If we get here, we're not outputting anymore, so set the silence @@ -897,11 +926,22 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) wp->window->flags |= WINDOW_SILENCE; if (gettimeofday(&wp->window->silence_timer, NULL) != 0) fatal("gettimeofday failed."); + return; + +start_timer: + log_debug("%%%u backing off (%s %zu > %d)", wp->id, c->ttyname, + available, READ_BACKOFF); + + tv.tv_sec = 0; + tv.tv_usec = READ_TIME; + + evtimer_set(&wp->timer, window_pane_timer_callback, wp); + evtimer_add(&wp->timer, &tv); } void -window_pane_error_callback( - unused struct bufferevent *bufev, unused short what, void *data) +window_pane_error_callback(unused struct bufferevent *bufev, unused short what, + void *data) { struct window_pane *wp = data; From beb0c01c27de2cc9d42684571e905446b76bc79d Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 17 May 2015 14:34:11 +0100 Subject: [PATCH 157/703] Hook repo to Travis-CI From now on, all pushes to master will result in tmux compiling against a linux-based distribution (Debian). This will make it easier for automatic merges between OpenBSD and portable to be tested, without the need for so much manual syncing. Any build failures will be reported to me, and fixed accordingly. --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..7aa698f0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: c +matrix: + include: + - compiler: gcc + - compiler: clang + env: CFLAGS="-g -O2" +before_install: + - sudo apt-get update -qq + - sudo apt-get -y install debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential +script: (CFLAGS= ./autogen.sh) && configure --enable-debug && make From 35d21be19ae0069b8cc09a0701251c19aeae77ab Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 17 May 2015 14:39:04 +0100 Subject: [PATCH 158/703] TRAVIS-CI: correct path to configure Specify path to ./configure --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7aa698f0..a1d7e427 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,4 @@ matrix: before_install: - sudo apt-get update -qq - sudo apt-get -y install debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential -script: (CFLAGS= ./autogen.sh) && configure --enable-debug && make +script: (CFLAGS= ./autogen.sh) && ./configure --enable-debug && make From 4123d69b51c1806fe37dde7f2bd28ac916cb1407 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 17 May 2015 14:52:58 +0100 Subject: [PATCH 159/703] README.md: github-specific readme This is the same as the current README, but allows for markdown to be used. We could switch this over to using the README file at some point. --- README.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..d3b53829 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +Welcome to tmux! +================ + +[![Build Status](https://travis-ci.org/ThomasAdam/tmux.svg?branch=master)](https://travis-ci.org/ThomasAdam/tmux) + +tmux is a "terminal multiplexer", it enables a number of terminals (or windows) +to be accessed and controlled from a single terminal. tmux is intended to be a +simple, modern, BSD-licensed alternative to programs such as GNU screen. + +This release runs on FreeBSD, NetBSD, Linux and OS X and may still run on +Solaris and AIX. + +tmux depends on libevent 2.x. Download it from: + + http://www.monkey.org/~provos/libevent/ + +To build tmux from a release tarball, do: + + $ ./configure && make + $ sudo make install + +To get and build the latest from version control: + + $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux + $ cd tmux + $ sh autogen.sh + $ ./configure && make + +For more information see https://sourceforge.net/scm/?type=git&group_id=200378 +and http://git-scm.com. Patches should be sent by email to the mailing list at +tmux-users@lists.sourceforge.net. + +For documentation on using tmux, see the tmux.1 manpage. It can be viewed from +the source tree with: + + $ nroff -mdoc tmux.1|less + +Some common questions are answered in the FAQ file and a more extensive (but +slightly out of date) guide is available in the OpenBSD FAQ at +http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO +file and some example configurations and a Vim syntax file are in the examples +directory. + +For debugging, running tmux with -v or -vv will generate server and client log +files in the current directory. + +tmux mailing lists are available. Visit: + + https://sourceforge.net/mail/?group_id=200378 + +Bug reports, feature suggestions and especially code contributions are most +welcome. Please send by email to: + + tmux-users@lists.sourceforge.net + +This file and the CHANGES, FAQ, TODO, and SYNCING files are licensed under +the ISC license. Files under examples/ remain copyright their authors unless +otherwise stated in the file but permission has been received to distribute +them with tmux. All other files have a license and copyright notice at their +start. From 2c53b23d5968da2e796ead6ed9f8ff3c33b8bbfb Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 May 2015 08:48:37 +0000 Subject: [PATCH 160/703] In terminfo, sometimes cvvis implies cnorm and sometimes it doesn't, so don't assume it does. Fixes missing cursor with emacs-in-tmux-in-tmux. --- tty.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tty.c b/tty.c index a58ca937..63380c29 100644 --- a/tty.c +++ b/tty.c @@ -507,14 +507,17 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; - if (changed & (MODE_CURSOR|MODE_BLINKING)) { - if (mode & MODE_CURSOR) { - if (mode & MODE_BLINKING && - tty_term_has(tty->term, TTYC_CVVIS)) - tty_putcode(tty, TTYC_CVVIS); - else - tty_putcode(tty, TTYC_CNORM); - } else + if (changed & MODE_BLINKING) { + if (tty_term_has(tty->term, TTYC_CVVIS)) + tty_putcode(tty, TTYC_CVVIS); + else + tty_putcode(tty, TTYC_CNORM); + changed |= MODE_CURSOR; + } + if (changed & MODE_CURSOR) { + if (mode & MODE_CURSOR) + tty_putcode(tty, TTYC_CNORM); + else tty_putcode(tty, TTYC_CIVIS); } if (s != NULL && tty->cstyle != s->cstyle) { From 7140cce7f31eb2135491fdc62645b4753d941520 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 20 May 2015 06:39:02 +0000 Subject: [PATCH 161/703] Return empty string if format is empty rather than attempting to allocate zero bytes. --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index db6f9231..eff1668d 100644 --- a/format.c +++ b/format.c @@ -335,7 +335,7 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) size_t tmplen; struct tm *tm; - if (fmt == NULL) + if (fmt == NULL || *fmt == '\0') return (xstrdup("")); tm = localtime(&t); From 379400cfa69f8df9ac13b070c60d5f8a282ddf6e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 27 May 2015 13:28:04 +0000 Subject: [PATCH 162/703] Move the jobs output cache into the formats code so that #() work more generally (for example, again working in set-titles-string). --- cmd-refresh-client.c | 5 +- format.c | 192 ++++++++++++++++++++++++++++++++--- server-client.c | 6 +- server.c | 2 + status.c | 236 ++----------------------------------------- tmux.1 | 32 +++--- tmux.h | 2 + 7 files changed, 206 insertions(+), 269 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index f693872c..b6d5d624 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -65,10 +65,9 @@ cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) } if (tty_set_size(&c->tty, w, h)) recalculate_sizes(); - } else if (args_has(args, 'S')) { - status_update_jobs(c); + } else if (args_has(args, 'S')) server_status_client(c); - } else + else server_redraw_client(c); return (CMD_RETURN_NORMAL); diff --git a/format.c b/format.c index eff1668d..4fe1e924 100644 --- a/format.c +++ b/format.c @@ -35,6 +35,9 @@ * string. */ +void format_job_callback(struct job *); +const char *format_job_get(struct format_tree *, const char *); + int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); char *format_time_string(time_t); @@ -46,6 +49,32 @@ void format_defaults_client(struct format_tree *, struct client *); void format_defaults_winlink(struct format_tree *, struct session *, struct winlink *); +/* Entry in format job tree. */ +struct format_job { + const char *cmd; + + time_t last; + char *out; + + struct job *job; + int status; + + RB_ENTRY(format_job) entry; +}; + +/* Format job tree. */ +int format_job_cmp(struct format_job *, struct format_job *); +RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); +RB_PROTOTYPE(format_job_tree, format_job, entry, format_job_cmp); +RB_GENERATE(format_job_tree, format_job, entry, format_job_cmp); + +/* Format job tree comparison function. */ +int +format_job_cmp(struct format_job *fj1, struct format_job *fj2) +{ + return (strcmp(fj1->cmd, fj2->cmd)); +} + /* Entry in format tree. */ struct format_entry { char *key; @@ -54,22 +83,22 @@ struct format_entry { RB_ENTRY(format_entry) entry; }; -/* Tree of format entries. */ +/* Format entry tree. */ struct format_tree { struct window *w; struct session *s; - RB_HEAD(format_rb_tree, format_entry) tree; + int status; + + RB_HEAD(format_entry_tree, format_entry) tree; }; +int format_entry_cmp(struct format_entry *, struct format_entry *); +RB_PROTOTYPE(format_entry_tree, format_entry, entry, format_entry_cmp); +RB_GENERATE(format_entry_tree, format_entry, entry, format_entry_cmp); -/* Format key-value replacement entry. */ -int format_cmp(struct format_entry *, struct format_entry *); -RB_PROTOTYPE(format_rb_tree, format_entry, entry, format_cmp); -RB_GENERATE(format_rb_tree, format_entry, entry, format_cmp); - -/* Format tree comparison function. */ +/* Format entry tree comparison function. */ int -format_cmp(struct format_entry *fe1, struct format_entry *fe2) +format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2) { return (strcmp(fe1->key, fe2->key)); } @@ -134,15 +163,118 @@ const char *format_lower[] = { NULL /* z */ }; +/* Format job callback. */ +void +format_job_callback(struct job *job) +{ + struct format_job *fj = job->data; + char *line, *buf; + size_t len; + struct client *c; + + fj->job = NULL; + free(fj->out); + + if (WIFEXITED(job->status) && WEXITSTATUS(job->status) != 0) { + xasprintf(&fj->out, "<'%s' exited with %d>", fj->cmd, + WEXITSTATUS(job->status)); + return; + } + if (WIFSIGNALED(job->status)) { + xasprintf(&fj->out, "<'%s' got signal %d>", fj->cmd, + WTERMSIG(job->status)); + return; + } + + buf = NULL; + if ((line = evbuffer_readline(job->event->input)) == NULL) { + len = EVBUFFER_LENGTH(job->event->input); + buf = xmalloc(len + 1); + if (len != 0) + memcpy(buf, EVBUFFER_DATA(job->event->input), len); + buf[len] = '\0'; + } else + buf = line; + fj->out = buf; + + if (fj->status) { + TAILQ_FOREACH(c, &clients, entry) + server_status_client(c); + fj->status = 0; + } +} + +/* Find a job. */ +const char * +format_job_get(struct format_tree *ft, const char *cmd) +{ + struct format_job fj0, *fj; + + fj0.cmd = cmd; + if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) + { + fj = xcalloc(1, sizeof *fj); + fj->cmd = xstrdup(cmd); + fj->status = ft->status; + + xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); + + RB_INSERT(format_job_tree, &format_jobs, fj); + } + + if (fj->job == NULL && fj->last != time(NULL)) { + fj->job = job_run(fj->cmd, NULL, -1, format_job_callback, + NULL, fj); + if (fj->job == NULL) { + free(fj->out); + xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); + } + } + fj->last = time(NULL); + + return (fj->out); +} + +/* Remove old jobs. */ +void +format_clean(void) +{ + struct format_job *fj, *fj1; + time_t now; + + now = time(NULL); + RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) { + if (fj->last > now || now - fj->last < 3600) + continue; + RB_REMOVE(format_job_tree, &format_jobs, fj); + + if (fj->job != NULL) + job_free(fj->job); + + free((void*)fj->cmd); + free(fj->out); + + free(fj); + } +} + /* Create a new tree. */ struct format_tree * format_create(void) +{ + return (format_create_status(0)); +} + +/* Create a new tree for the status line. */ +struct format_tree * +format_create_status(int status) { struct format_tree *ft; - char host[HOST_NAME_MAX+1], *ptr; + char host[HOST_NAME_MAX + 1], *ptr; ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); + ft->status = status; if (gethostname(host, sizeof host) == 0) { format_add(ft, "host", "%s", host); @@ -160,8 +292,8 @@ format_free(struct format_tree *ft) { struct format_entry *fe, *fe1; - RB_FOREACH_SAFE(fe, format_rb_tree, &ft->tree, fe1) { - RB_REMOVE(format_rb_tree, &ft->tree, fe); + RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) { + RB_REMOVE(format_entry_tree, &ft->tree, fe); free(fe->value); free(fe->key); free(fe); @@ -185,7 +317,7 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) xvasprintf(&fe->value, fmt, ap); va_end(ap); - fe_now = RB_INSERT(format_rb_tree, &ft->tree, fe); + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { free(fe_now->value); fe_now->value = fe->value; @@ -224,7 +356,7 @@ format_find(struct format_tree *ft, const char *key) } fe_find.key = (char *) key; - fe = RB_FIND(format_rb_tree, &ft->tree, &fe_find); + fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe == NULL) return (NULL); return (fe->value); @@ -358,9 +490,9 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf; + char *buf, *tmp; const char *ptr, *s; - size_t off, len, n; + size_t off, len, n, slen; int ch, brackets; if (fmt == NULL) @@ -383,6 +515,34 @@ format_expand(struct format_tree *ft, const char *fmt) ch = (u_char) *fmt++; switch (ch) { + case '(': + brackets = 1; + for (ptr = fmt; *ptr != '\0'; ptr++) { + if (*ptr == '(') + brackets++; + if (*ptr == ')' && --brackets == 0) + break; + } + if (*ptr != ')' || brackets != 0) + break; + n = ptr - fmt; + + tmp = xmalloc(n + 1); + memcpy(tmp, fmt, n); + tmp[n] = '\0'; + + s = format_job_get(ft, tmp); + slen = strlen(s); + + while (len - off < slen + 1) { + buf = xreallocarray(buf, 2, len); + len *= 2; + } + memcpy(buf + off, s, slen); + off += slen; + + fmt += n + 1; + continue; case '{': brackets = 1; for (ptr = fmt; *ptr != '\0'; ptr++) { diff --git a/server-client.c b/server-client.c index dd6eda1d..36408e91 100644 --- a/server-client.c +++ b/server-client.c @@ -157,8 +157,6 @@ server_client_lost(struct client *c) if (c->stderr_data != c->stdout_data) evbuffer_free(c->stderr_data); - status_free_jobs(&c->status_new); - status_free_jobs(&c->status_old); screen_free(&c->status); free(c->title); @@ -269,10 +267,8 @@ server_client_status_timer(void) interval = options_get_number(&s->options, "status-interval"); difference = tv.tv_sec - c->status_timer.tv_sec; - if (interval != 0 && difference >= interval) { - status_update_jobs(c); + if (interval != 0 && difference >= interval) c->flags |= CLIENT_STATUS; - } } } diff --git a/server.c b/server.c index ba161c4b..a5cd50a7 100644 --- a/server.c +++ b/server.c @@ -485,6 +485,8 @@ server_second_callback(unused int fd, unused short events, unused void *arg) server_client_status_timer(); + format_clean(); + evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; diff --git a/status.c b/status.c index 9bd5111c..0ffc0427 100644 --- a/status.c +++ b/status.c @@ -33,13 +33,10 @@ char *status_redraw_get_left(struct client *, time_t, int, struct grid_cell *, size_t *); char *status_redraw_get_right(struct client *, time_t, int, struct grid_cell *, size_t *); -char *status_find_job(struct client *, char **); -void status_job_free(void *); -void status_job_callback(struct job *); char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); -void status_replace1(struct client *, char **, char **, char *, size_t); +void status_replace1(char **, char **, char *, size_t); void status_message_callback(int, short, void *); const char *status_prompt_up_history(u_int *); @@ -368,246 +365,25 @@ out: return (1); } -/* Replace a single special sequence (prefixed by #). */ -void -status_replace1(struct client *c, char **iptr, char **optr, char *out, - size_t outsize) -{ - char ch, tmp[256], *ptr, *endptr; - size_t ptrlen; - long limit; - - errno = 0; - limit = strtol(*iptr, &endptr, 10); - if ((limit == 0 && errno != EINVAL) || - (limit == LONG_MIN && errno != ERANGE) || - (limit == LONG_MAX && errno != ERANGE) || - limit != 0) - *iptr = endptr; - if (limit <= 0) - limit = LONG_MAX; - - switch (*(*iptr)++) { - case '(': - if ((ptr = status_find_job(c, iptr)) == NULL) - return; - goto do_replace; - case '[': - /* - * Embedded style, handled at display time. Leave present and - * skip input until ]. - */ - ch = ']'; - goto skip_to; - case '{': - ptr = (char *) "#{"; - goto do_replace; - default: - xsnprintf(tmp, sizeof tmp, "#%c", *(*iptr - 1)); - ptr = tmp; - goto do_replace; - } - - return; - -do_replace: - ptrlen = strlen(ptr); - if ((size_t) limit < ptrlen) - ptrlen = limit; - - if (*optr + ptrlen >= out + outsize - 1) - return; - while (ptrlen > 0 && *ptr != '\0') { - *(*optr)++ = *ptr++; - ptrlen--; - } - - return; - -skip_to: - *(*optr)++ = '#'; - - (*iptr)--; /* include ch */ - while (**iptr != ch && **iptr != '\0') { - if (*optr >= out + outsize - 1) - break; - *(*optr)++ = *(*iptr)++; - } -} - /* Replace special sequences in fmt. */ char * status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) { - static char out[BUFSIZ]; - char in[BUFSIZ], ch, *iptr, *optr, *expanded; - size_t len; struct format_tree *ft; + char *expanded; if (fmt == NULL) return (xstrdup("")); - len = strftime(in, sizeof in, fmt, localtime(&t)); - in[len] = '\0'; - - iptr = in; - optr = out; - - while (*iptr != '\0') { - if (optr >= out + (sizeof out) - 1) - break; - ch = *iptr++; - - if (ch != '#' || *iptr == '\0') { - *optr++ = ch; - continue; - } - status_replace1(c, &iptr, &optr, out, sizeof out); - } - *optr = '\0'; - - ft = format_create(); + ft = format_create_status(1); format_defaults(ft, c, NULL, wl, NULL); - expanded = format_expand(ft, out); + + expanded = format_expand_time(ft, fmt, t); + format_free(ft); return (expanded); } -/* Figure out job name and get its result, starting it off if necessary. */ -char * -status_find_job(struct client *c, char **iptr) -{ - struct status_out *so, so_find; - char *cmd; - int lastesc; - size_t len; - - if (**iptr == '\0') - return (NULL); - if (**iptr == ')') { /* no command given */ - (*iptr)++; - return (NULL); - } - - cmd = xmalloc(strlen(*iptr) + 1); - len = 0; - - lastesc = 0; - for (; **iptr != '\0'; (*iptr)++) { - if (!lastesc && **iptr == ')') - break; /* unescaped ) is the end */ - if (!lastesc && **iptr == '\\') { - lastesc = 1; - continue; /* skip \ if not escaped */ - } - lastesc = 0; - cmd[len++] = **iptr; - } - if (**iptr == '\0') /* no terminating ) */ { - free(cmd); - return (NULL); - } - (*iptr)++; /* skip final ) */ - cmd[len] = '\0'; - - /* First try in the new tree. */ - so_find.cmd = cmd; - so = RB_FIND(status_out_tree, &c->status_new, &so_find); - if (so != NULL && so->out != NULL) { - free(cmd); - return (so->out); - } - - /* If not found at all, start the job and add to the tree. */ - if (so == NULL) { - job_run(cmd, NULL, -1, status_job_callback, status_job_free, c); - c->references++; - - so = xmalloc(sizeof *so); - so->cmd = xstrdup(cmd); - so->out = NULL; - RB_INSERT(status_out_tree, &c->status_new, so); - } - - /* Lookup in the old tree. */ - so_find.cmd = cmd; - so = RB_FIND(status_out_tree, &c->status_old, &so_find); - free(cmd); - if (so != NULL) - return (so->out); - return (NULL); -} - -/* Free job tree. */ -void -status_free_jobs(struct status_out_tree *sotree) -{ - struct status_out *so, *so_next; - - so_next = RB_MIN(status_out_tree, sotree); - while (so_next != NULL) { - so = so_next; - so_next = RB_NEXT(status_out_tree, sotree, so); - - RB_REMOVE(status_out_tree, sotree, so); - free(so->out); - free(so->cmd); - free(so); - } -} - -/* Update jobs on status interval. */ -void -status_update_jobs(struct client *c) -{ - /* Free the old tree. */ - status_free_jobs(&c->status_old); - - /* Move the new to old. */ - memcpy(&c->status_old, &c->status_new, sizeof c->status_old); - RB_INIT(&c->status_new); -} - -/* Free status job. */ -void -status_job_free(void *data) -{ - struct client *c = data; - - c->references--; -} - -/* Job has finished: save its result. */ -void -status_job_callback(struct job *job) -{ - struct client *c = job->data; - struct status_out *so, so_find; - char *line, *buf; - size_t len; - - if (c->flags & CLIENT_DEAD) - return; - - so_find.cmd = job->cmd; - so = RB_FIND(status_out_tree, &c->status_new, &so_find); - if (so == NULL || so->out != NULL) - return; - - buf = NULL; - if ((line = evbuffer_readline(job->event->input)) == NULL) { - len = EVBUFFER_LENGTH(job->event->input); - buf = xmalloc(len + 1); - if (len != 0) - memcpy(buf, EVBUFFER_DATA(job->event->input), len); - buf[len] = '\0'; - } else - buf = line; - - so->out = buf; - server_status_client(c); -} - /* Return winlink status line entry and adjust gc as necessary. */ char * status_print(struct client *c, struct winlink *wl, time_t t, diff --git a/tmux.1 b/tmux.1 index f5a0aaa3..1233d1be 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2662,25 +2662,10 @@ will be expanded. It may also contain any of the following special character sequences: .Bl -column "Character pair" "Replaced with" -offset indent .It Sy "Character pair" Ta Sy "Replaced with" -.It Li "#(shell-command)" Ta "First line of the command's output" .It Li "#[attributes]" Ta "Colour or attribute change" .It Li "##" Ta "A literal" Ql # .El .Pp -The #(shell-command) form executes -.Ql shell-command -and inserts the first line of its output. -Note that shell commands are only executed once at the interval specified by -the -.Ic status-interval -option: if the status line is redrawn in the meantime, the previous result is -used. -Shell commands are executed with the -.Nm -global environment set (see the -.Sx ENVIRONMENT -section). -.Pp For details on how the names and titles can be set see the .Sx "NAMES AND TITLES" section. @@ -3245,6 +3230,23 @@ a number and a colon, so .Ql #{=10:pane_title} will include at most the first 10 characters of the pane title. .Pp +In addition, the first line of a shell command's output may be inserted using +.Ql #() . +For example, +.Ql #(uptime) +will insert the system's uptime. +When constructing formats, +.Nm +does not wait for +.Ql #() +commands to finish; instead, the previous result from running the same command is used, +or a placeholder if the command has not been run before. +Commands are executed with the +.Nm +global environment set (see the +.Sx ENVIRONMENT +section). +.Pp The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" diff --git a/tmux.h b/tmux.h index bc47a268..78e64170 100644 --- a/tmux.h +++ b/tmux.h @@ -1475,7 +1475,9 @@ void cfg_show_causes(struct session *); /* format.c */ struct format_tree; +void format_clean(void); struct format_tree *format_create(void); +struct format_tree *format_create_status(int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); From a55e569af513deb99258354822ee75cbdce83619 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 May 2015 23:02:27 +0000 Subject: [PATCH 163/703] Use RB_MIN to get the lowest index for the current window when creating grouped sessions, rather than using RB_ROOT. --- cmd-new-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 199e82c2..9c767489 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -254,7 +254,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (groupwith != NULL) { session_group_add(groupwith, s); session_group_synchronize_to(s); - session_select(s, RB_ROOT(&s->windows)->idx); + session_select(s, RB_MIN(winlinks, &s->windows)->idx); } /* From 74c755f2aba6402f9ec51fc6b4b61560884af46d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 May 2015 23:12:38 +0000 Subject: [PATCH 164/703] Expand formats again inside #(), and free the temporaries. --- format.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 4fe1e924..a1165de6 100644 --- a/format.c +++ b/format.c @@ -490,7 +490,7 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf, *tmp; + char *buf, *tmp, *cmd; const char *ptr, *s; size_t off, len, n, slen; int ch, brackets; @@ -530,10 +530,14 @@ format_expand(struct format_tree *ft, const char *fmt) tmp = xmalloc(n + 1); memcpy(tmp, fmt, n); tmp[n] = '\0'; + cmd = format_expand(ft, tmp); - s = format_job_get(ft, tmp); + s = format_job_get(ft, cmd); slen = strlen(s); + free(cmd); + free(tmp); + while (len - off < slen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; From 2a8c2648f0375994715b2fea9de144b15c9588a2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 May 2015 23:26:52 +0000 Subject: [PATCH 165/703] Don't use special strings if #() commands fail, just remove the format (as if the command produced nothing). Makes constructions that can fail like '#(test whatever && echo foo)' work as they did before. --- format.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/format.c b/format.c index a1165de6..72d6bfc9 100644 --- a/format.c +++ b/format.c @@ -175,17 +175,6 @@ format_job_callback(struct job *job) fj->job = NULL; free(fj->out); - if (WIFEXITED(job->status) && WEXITSTATUS(job->status) != 0) { - xasprintf(&fj->out, "<'%s' exited with %d>", fj->cmd, - WEXITSTATUS(job->status)); - return; - } - if (WIFSIGNALED(job->status)) { - xasprintf(&fj->out, "<'%s' got signal %d>", fj->cmd, - WTERMSIG(job->status)); - return; - } - buf = NULL; if ((line = evbuffer_readline(job->event->input)) == NULL) { len = EVBUFFER_LENGTH(job->event->input); From 7e067cb9dc122ac3296af116c7028d7aa5f4322e Mon Sep 17 00:00:00 2001 From: deraadt Date: Sun, 31 May 2015 23:27:06 +0000 Subject: [PATCH 166/703] does not need syslog.h --- server.c | 1 - 1 file changed, 1 deletion(-) diff --git a/server.c b/server.c index a5cd50a7..6438fe6d 100644 --- a/server.c +++ b/server.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include From 58b50fb543168128f1eef30ad9908cf27ee91f41 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2015 09:20:19 +0000 Subject: [PATCH 167/703] Clear signal handlers before event_reinit as apparently it can otherwise cause libevent to go strange. --- server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.c b/server.c index 6438fe6d..b53648f0 100644 --- a/server.c +++ b/server.c @@ -129,9 +129,9 @@ server_start(int lockfd, char *lockfile) fatal("daemon failed"); /* event_init() was called in our parent, need to reinit. */ + clear_signals(0); if (event_reinit(ev_base) != 0) fatal("event_reinit failed"); - clear_signals(0); logfile("server"); log_debug("server started, pid %ld", (long) getpid()); From a3c617249527d96fa8c12e7470ebebec58fde00e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Jun 2015 13:59:57 +0000 Subject: [PATCH 168/703] Missing t at end of response, from Vincent Bernat. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 886d561d..6be5d906 100644 --- a/input.c +++ b/input.c @@ -1599,7 +1599,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx) return; break; case 18: - input_reply(ictx, "\033[8;%u;%u", wp->sy, wp->sx); + input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); From 3821ca4917e9f508ec09a24f43dfc9c29066fa20 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 2 Jun 2015 15:16:13 +0100 Subject: [PATCH 169/703] Update TODO. --- TODO | 42 ++---------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/TODO b/TODO index 34b6cfe3..93d11f4f 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,6 @@ * allow multiple targets: fnmatch for -t/-c, for example detach all clients with -t* * add -c for new-session like new-window - * attach should take a pane and select it as well as attaching * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions @@ -19,10 +18,9 @@ * way to set socket path from config file - format improvements: - * last bits of status_replace into formats? * option to quote format (#{session_name:quoted}) * formats need conditions for >0 (for #P) - * some way to pad # stuff with spaces, #!2T maybe + * some way to pad # stuff with spaces * last window update time and format for it * formats to show if a window is linked into multiple sessions, into multiple attached sessions, and is the active window in multiple @@ -43,36 +41,14 @@ - improve mouse support: * bind commands to mouse in different areas? - * more fine-grained options? * commands executed when clicking on a pattern (URL) - * mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets - the flag on w/o checking the others before calling tty_update_mode) - * mouse can be like normal key bindings? - - {button-{1,2,3},wheel-{up,down}}-{status,pane,border} and - drag-{start,end}-{status,pane,border} plus the modifiers - - resize and copy can be special cases - once you call something - like copy-mode -M or resize-pane -M to start the drag, it tracks - mouse until you call -m to stop the drag. or just keep drags - entirely special? - - what happens with stuff that wants mouse inside? especially for - pane clicks which need to run command AND pass event through - (like mouse-select-pane). maybe just a flag to say whether it - always runs or only if pane hasn't taken mouse? or it could be - eg bind Button1Pane "select-pane -t=; send-keys -Mt=' - - also need a) some way to tell commands bound to key which - window or pane the mouse key binding applies to (maybe a new - special char in target, or pass targets through formats?) b) a - way to bind repeat count to mode keys so that wheel up/down can - do multiple lines c) send-keys -M to pass a mouse event through? - - what does the mouse->KEYC_* conversion and find-the-pane bit? - server_client_handle_key? - hooks! - warts on current naming: * display-time but message-fg/bg/attr * list-* vs show-* - * split-window -> split-pane?? + * split-window -> split-pane? - better UTF-8 support: * window names and titles @@ -82,7 +58,6 @@ - copy/paste improvements: * incremental searching - * append to buffer * paste w/o trailing whitespace * command to toggle selection not to move it in copy-mode * regex searching @@ -107,8 +82,6 @@ - terminfo bits * use a better termcap internally instead of screen, perhaps xterm * use screen-256color when started on 256 colour terminal? - * need a tmux terminfo entry to document the extensions we are using in - upstream terminfo - code cleanup * instead of separate window and session options, just one master @@ -134,12 +107,6 @@ tell when in the config file - then we use cmdq->c if we need a client w/o a session else cmd_current_client * optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx - * cmd_find_* could be much simpler - parse everything the same, only - difference is what to choose when not given a ":" or "." (such as a - plain "0" could be session, window or pane). So just cmd_find_target - with a type (session, window, or pane) - * instead of all the obscure =, ^ -t codes, we should support something - simpler and easier like -t:mouse:, -t:first: - miscellaneous * way to keep a job running just read its last line of output for #() @@ -151,9 +118,4 @@ jobs and run-shell and lock command all start with slightly different environments * multiline status line? - * bind commands to key sequences -- make it so ALL keys go through a - table, first an implicit table in which C-b is the only default - binding to a command that says "next key from $othertable" and so - on. means -n can go away as well * customizable command aliases - * any remaining clients in wait-for should be woken when server exits From 1c3e1bae41bcdc0d5c590bb2f95b987a42254de7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:26:25 +0100 Subject: [PATCH 170/703] Remove $Id$. --- autogen.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index 300b54db..cbf6df1f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,5 +1,4 @@ #!/bin/sh -# $Id$ if [ "x$(uname)" = "xOpenBSD" ]; then [ -z "$AUTOMAKE_VERSION" ] && export AUTOMAKE_VERSION=1.10 From 09bcbc57da3dde96830d62d64866657a950227f7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:28:26 +0100 Subject: [PATCH 171/703] $Id$ -> $OpenBSD$. --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index be79e273..7b538ddb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $Id$ +.\" $OpenBSD$ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" From 11ae6d16e534add51c28a665e42196b694e92515 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:35:44 +0100 Subject: [PATCH 172/703] $Id$ -> $OpenBSD$. --- array.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/array.h b/array.h index 2c70c310..671bea42 100644 --- a/array.h +++ b/array.h @@ -1,4 +1,4 @@ -/* $Id$ */ +/* $OpenBSD$ */ /* * Copyright (c) 2006 Nicholas Marriott From dfd72f525009b5140c3ac41fd011787c98ebea08 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:42:36 +0100 Subject: [PATCH 173/703] -$Id$. --- compat.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/compat.h b/compat.h index 5f3ff8fc..2c6f1c67 100644 --- a/compat.h +++ b/compat.h @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 2007 Nicholas Marriott * From fc2fb0eb9504ecfbaa162ffa6db173be38031363 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 3 Jun 2015 18:57:35 +0100 Subject: [PATCH 174/703] Update mailing list addresses. --- FAQ | 6 +++--- README | 19 ++++++++++--------- README.md | 27 +++++++++++++++------------ www/index.html.in | 4 ++-- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/FAQ b/FAQ index 2ca0a596..5913ba59 100644 --- a/FAQ +++ b/FAQ @@ -108,8 +108,8 @@ Check the latest version of tmux from Git to see if the problem is still reproducible. Sometimes the length of time between releases means a lot of fixes can be sitting in Git and the problem might already be fixed. -Please send bug reports by email to nicm@users.sourceforge.net or -tmux-users@lists.sourceforge.net. Please include as much of the following +Please send bug reports by email to nicholas.marriott@gmail.com or +tmux-users@googlegroups.com. Please include as much of the following information as possible: - the version of tmux you are running; @@ -123,7 +123,7 @@ information as possible: * Why doesn't tmux do $x? -Please send feature requests by email to nicm@users.sourceforge.net. +Please send feature requests by email to tmux-users@googlegroups.com. * Why do you use the screen terminal description inside tmux? It sucks. diff --git a/README b/README index fad67b71..a5898a65 100644 --- a/README +++ b/README @@ -4,8 +4,7 @@ tmux is a "terminal multiplexer", it enables a number of terminals (or windows) to be accessed and controlled from a single terminal. tmux is intended to be a simple, modern, BSD-licensed alternative to programs such as GNU screen. -This release runs on OpenBSD, FreeBSD, NetBSD, Linux and OS X and may still -run on Solaris and AIX (although they haven't been tested in a while). +This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. tmux depends on libevent 2.x. Download it from: @@ -25,7 +24,7 @@ To get and build the latest from version control: For more information see https://sourceforge.net/scm/?type=git&group_id=200378 and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@lists.sourceforge.net. +tmux-users@googlegroups.com. For documentation on using tmux, see the tmux.1 manpage. It can be viewed from the source tree with: @@ -41,20 +40,22 @@ directory. For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. -tmux mailing lists are available. Visit: +tmux mailing lists are available. For general discussion and bug reports: - https://sourceforge.net/mail/?group_id=200378 + https://groups.google.com/forum/#!forum/tmux-users + +And for Git commit emails: + + https://groups.google.com/forum/#!forum/tmux-git Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: - tmux-users@lists.sourceforge.net + tmux-users@googlegroups.com This file and the CHANGES, FAQ and TODO files are licensed under the ISC license. Files under examples/ remain copyright their authors unless otherwise stated in the file but permission has been received to distribute them with tmux. All other files have a license and copyright notice at their start. --- Nicholas Marriott - -$Id$ +-- Nicholas Marriott diff --git a/README.md b/README.md index d3b53829..eab63eaf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ Welcome to tmux! -================ [![Build Status](https://travis-ci.org/ThomasAdam/tmux.svg?branch=master)](https://travis-ci.org/ThomasAdam/tmux) @@ -7,8 +6,7 @@ tmux is a "terminal multiplexer", it enables a number of terminals (or windows) to be accessed and controlled from a single terminal. tmux is intended to be a simple, modern, BSD-licensed alternative to programs such as GNU screen. -This release runs on FreeBSD, NetBSD, Linux and OS X and may still run on -Solaris and AIX. +This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. tmux depends on libevent 2.x. Download it from: @@ -28,7 +26,7 @@ To get and build the latest from version control: For more information see https://sourceforge.net/scm/?type=git&group_id=200378 and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@lists.sourceforge.net. +tmux-users@googlegroups.com. For documentation on using tmux, see the tmux.1 manpage. It can be viewed from the source tree with: @@ -44,17 +42,22 @@ directory. For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. -tmux mailing lists are available. Visit: +tmux mailing lists are available. For general discussion and bug reports: - https://sourceforge.net/mail/?group_id=200378 + https://groups.google.com/forum/#!forum/tmux-users + +And for Git commit emails: + + https://groups.google.com/forum/#!forum/tmux-git Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: - tmux-users@lists.sourceforge.net + tmux-users@googlegroups.com -This file and the CHANGES, FAQ, TODO, and SYNCING files are licensed under -the ISC license. Files under examples/ remain copyright their authors unless -otherwise stated in the file but permission has been received to distribute -them with tmux. All other files have a license and copyright notice at their -start. +This file and the CHANGES, FAQ and TODO files are licensed under the ISC +license. Files under examples/ remain copyright their authors unless otherwise +stated in the file but permission has been received to distribute them with +tmux. All other files have a license and copyright notice at their start. + +-- Nicholas Marriott diff --git a/www/index.html.in b/www/index.html.in index 72f5f12e..771e01aa 100644 --- a/www/index.html.in +++ b/www/index.html.in @@ -17,7 +17,7 @@
  • FAQ
  • Source Code
  • SourceForge Page
  • -
  • Mailing List
  • +
  • Mailing List
  • IRC Channel
  • @@ -44,7 +44,7 @@ and ncurses.

    For support contact the -tmux-users@lists.sf.net +tmux-users@googlegroups.com mailing list or IRC channel #tmux on freenode.

    From 89131c3e907e14eb3c3d6c34291bb289a16c6f1c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 00:38:01 +0100 Subject: [PATCH 175/703] No $Id$. --- CHANGES | 2 -- FAQ | 2 -- 2 files changed, 4 deletions(-) diff --git a/CHANGES b/CHANGES index 6e0d8f80..cf695936 100644 --- a/CHANGES +++ b/CHANGES @@ -1924,5 +1924,3 @@ The list of older changes is below. emacs) that don't require scrolling regions (ESC[r) mostly work fine (including mutt, emacs). No status bar yet and no key remapping or other customisation. - -$Id$ diff --git a/FAQ b/FAQ index 5913ba59..c7d3d15a 100644 --- a/FAQ +++ b/FAQ @@ -461,5 +461,3 @@ Or from inside tmux by detaching individual clients with C-b D or all using: C-b : attach -d - -$Id$ From d2b35e19cdd61d163d26c4babccc1550e72a9623 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 00:44:22 +0100 Subject: [PATCH 176/703] No more SF. --- www/index.html.in | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/www/index.html.in b/www/index.html.in index 771e01aa..78b48b6c 100644 --- a/www/index.html.in +++ b/www/index.html.in @@ -11,12 +11,11 @@
    tmux @@ -31,13 +30,13 @@ background) and reattach them to a different terminal. And do a lot more. See the manual.

    -

    +

    Download tmux %%RELEASE%% -(changelog) or - +(changelog) or + get the development version. tmux is hosted on -SourceForge +GitHub and needs libevent and From 32bc8f4dd4a2cfa814ebf96691f2723d5e43971a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 01:21:41 +0100 Subject: [PATCH 177/703] HTML bits are now elsewhere. --- Makefile.am | 19 ------------- configure.ac | 4 +-- www/favicon.ico | Bin 6518 -> 0 bytes www/images/tmux1.png | Bin 15972 -> 0 bytes www/images/tmux2.png | Bin 108143 -> 0 bytes www/images/tmux3.png | Bin 97986 -> 0 bytes www/images/tmux4.png | Bin 328784 -> 0 bytes www/index.html.in | 64 ------------------------------------------- www/logo.png | Bin 2701 -> 0 bytes www/main.css | 55 ------------------------------------- 10 files changed, 1 insertion(+), 141 deletions(-) delete mode 100644 www/favicon.ico delete mode 100644 www/images/tmux1.png delete mode 100644 www/images/tmux2.png delete mode 100644 www/images/tmux3.png delete mode 100644 www/images/tmux4.png delete mode 100644 www/index.html.in delete mode 100644 www/logo.png delete mode 100644 www/main.css diff --git a/Makefile.am b/Makefile.am index 1b306a01..29294d87 100644 --- a/Makefile.am +++ b/Makefile.am @@ -248,22 +248,3 @@ install-exec-hook: $(mkdir_p) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ $(DESTDIR)$(mandir)/man1/tmux.1 - -# Update SF web site. -upload-index.html: update-index.html - scp www/index.html www/main.css www/images/*.png \ - www/logo.png www/favicon.ico \ - ${USER},tmux@web.sf.net:/home/groups/t/tm/tmux/htdocs - rm -f www/index.html www/images/small-* - -update-index.html: - (cd www/images && \ - rm -f small-* && \ - for i in *.png; do \ - convert "$$i" \ - -resize '150x' -resize 'x100<' \ - -gravity center -crop 150x100+0+0 +repage \ - "small-$$i"; \ - done \ - ) - sed "s/%%RELEASE%%/${RELEASE}/g" www/index.html.in >www/index.html diff --git a/configure.ac b/configure.ac index cccd656b..0dd1151c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,9 +1,7 @@ # $Id$ -# Miscellaneous autofoo bullshit. +# Miscellaneous bits. AC_INIT(tmux, 2.1) -RELEASE=2.0 -AC_SUBST(RELEASE) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) diff --git a/www/favicon.ico b/www/favicon.ico deleted file mode 100644 index 6e5398a7ab2be493892911b021e80015865f396d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6518 zcmeHLL2BDT6rCEpEZ89=!J@Vxw8Vg&=d3kZBn{zFX1@J5wh(C>_wp= zWD_Ts$fEVCn@}9LrRsl@C%B5nnab75BY6Drpa1@wc^bypj6GmERwyuvCfmw0_JlEJ zn$yp#jJ>4X7UigfDxy3E#wZ_u3a7cZ>z|50jgz(D(dYFL7#lRMoRvag9G?fLaF*Mv z?ORvYQT5wSuljZS&>C&`tWoX2`cmswuOA<8egrT^4s)nMEjYEVb;Z~Cgg@kCXO-VL zD^nDmYrpIVZr*(bVC+!tcAjd%3AvsdaQ|=EPwijc3qP=*+wXvIOu2X+C$8(>1?T*S z?-Sp)cL9u%OR7!E34Xm^-vbumH=E7Zp63OQ<9tWoIOgetOY48%Znp#Uz&hfP@lg+M zqtP%aexUe9>}NJ+#m71qSwHw#=Y9T>ufOAaPm#ndO%zBn8z12;D8&y50>pS?n#sIjDY(zeCQ5MdMf>mPI*uoR{F-IGBAQnK^l?P zMh9zwcBXps_9ON^uO`E}a^E}Pb{UQBd&{5G!s##PY+US7@AO7`Sq|q0i3vJOu MN9}-iVEG*Q1FWUhEdT%j diff --git a/www/images/tmux1.png b/www/images/tmux1.png deleted file mode 100644 index a55f86b2d3c47f09b2321e05793ee155c1ac93fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15972 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

    2&aoSo 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

    PaX+#8Y-*USR}qnrR_!{Yrq0qmk{FfiLls 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

  • 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%0A_MGMJx1 z#yArpK?UzCtprF(qetUwd0ul6baz6D>Ld{+NGkI(2XZ0X4j;m`sgb|@2oSBxTd*`Wd$N~#>AnnIEbY{X_hg`_%?J@lpP4JRt}FwDH3_K zi+t$4PQDP(L9WO9=yP2bvv9j1FMk;XqnY^m^sM1P+P!k~9>GZcDP@pL-U>ox1y*|l z>Uge2a<`*Vt({RZc1AsOU^OglJug!PSuTLk3O@1<8}29btwa$%=(?}4J9tcKD+4&jB4+#4TF?C-VeNKcBAL;~K;=>q{mfY=B=C=DeXUa9 z4B*Zexl&y==>%n#cdY^77|VJNXX7(uuL32h!?!ekC(t(j@1!bFLX=v2$)6WUfLI6d z<-E{liKgvL(w&_@Ga>)`|Nj@#oj-mX3joyrAaD9(sPDHH=w`sOQd zBnC7H^l6^`W0Jo|wcCDwi(j7~|9=b`vyYz3!Ki`ISEzq&EDy8p@Rj3{TJIVfh}cbE zSV7pk0SBayx_vntdHIh4uFv*8fWTtsT;u8bbE*D6t-R!Wvu&FX$N43&tU~Fbn~SsG z&Rn}cH_4i$=^P+WB&Vy;=Xzl>77j7l0-jbs4!_G`vwIPGWjO51QDnGgj8{w`5lIZ! zTXDWP`f4HFw7}jW6#ca$DvU0LZGk|oa~hQ^@2aYdhQ1XR^Uz3vjls)MIRvL4Q`7;M zJf|`^R_xTj(bRWLwowDj2?B=gaw6_(!!T*RcbRrJ)O;{U8oT35zOiVhi=45B#)pih zjbuvjr=i1PbcQeV0_=_$Yu||T;BBZbx?rd|6E{US(Y!*kyc{_pDh?>e{};g<>fp#3zNNdkkUxS!w~wGEvT2%=iF zl6^irxJ7Z(&)5E(1PAAgb<5F!mP2I7=+&INzyUU)_u_-jpLoqq#NIp{OmoQS)6 zsmYgqcwB|n9|@!1ZBN(u62^X&rfp@7ScpH|Qqp%QsR_jeOm8Q zM2JupX|E3|_bdN)JopOjyp?W@Uu8CoV17Sl;2S7Pg3YlpCje~6=I#S9@j{f!1$4tz zei$A`C%{kDJ3EK^*!hec61miX*m(wVE@Z9Xx28}0!7ORqEi^qo1-tM6Xliw^k<1(~c+XEjtUEOpZ`V`O;<@ z1SB(~3M%wcO)Wu}uC=l1JV{_Q)f?rED+&Mt%aPMbJAVkqDtt(d-L{w7a@O9+lFKx< zFuN%kPKhTZ+|X$C3>SIXJRCWT|46r+Y&JBw{YgyW{5^(H@ZO8-IMuZ;Amu*~P#mq~ zNUE$(gP7~RkwF!)O1N3-!n;X9FgXOZp zlCw`JT8>i)VjM4ek0q4-DtoIhO-K%6U>y!IzX_2{1+s_|BX2c)^dE*>!mUFgQO*Zd z(yb4wG^lX#jER-t1;_Mbm6HQUZ47iJ`;eOnh?Tz#fcv`fjt=-hsM69=0P&K?q75l} z6vYS5vyuMSgbE@4Oe)Q*nO^&XY5k!$?SL094OzO z8#Rmf`55r0J_m~BPk?*&eAL@q!B$u!Y*kD4u4&ajE(Q+!XKju1-DYH-RVr%*{tw3i z;7Rzhb1`fNoBzj;e}5p=0Zo?Soo>F9a^MSMnFhgD|8bWwbJxo_m1`cq##bL?-Gu`G zp5>oEsE7b=w9!?y2<#VMQyCjCJk9+dZTkT*TY6VdzxvAiv8PJKIe2ec_pdktAP-P49E3J? z7)qI31X#EH>CHVw!^7l>Q9H(5r3W|^O747~vY?MoCs-%tMAi%5Z9H%I5s5xO<1jL{ z@8oQOkAJ$H7)fn0oM?(GRpF1*UN4#da-H(`KGRi=%rpiaNYZi0qYLinKqZi|a=woV zmq;Di5ZAf@>$xc}NB4J7SlP`>w!Py33v!&oi{Kw$Zh#)!fBwz0x4zOqdkU~JUHkeK zAv3IS!|E=IV1!KLsP(jz?)Di%OIX~CgnU;6I8AXPb*<3-+l?!A@t+46+pQ1QhXqD1 z>fvjSYaxXMFF=nfTnB-e|>mwu)mPbA7v1UZd`l&d<}iOCpiJZBx~=yM!R zTRlOZcBCL!QX%8SV<4;C5mA!4&&ch?xUlb zlaseLfgf@khjtDuE{#aaWUZ$Gq<>Vp_DGA5cK+coE?SHPvCg$187_qr#_At6v(Fv% z8(e}$sJlyJJDnXRjF2z)8oBNbz-qXe$IBbe*l8=lmX3gxbeihtieF9w7^9-^;mD$; zqbCIL>|htA(Y=9p>&SOib);T7EoG10KJuH)?DuN!_q+n^1$p;tZ4VY|=S=$6kq;a07OL7Ts=HO6o%O6}HBzKyZaKI!P-kkF$|;8`WLrwS9(lsN#9| z@r$(*ulgUE2Y_PS6JqdL=f*zLFTWfwy#kynO!#bLK`^G+D9O|vqFezoT9t~sLr1{F7JnChFY8s0^;Q)EbvzQb<=i&5;{pa^x$H%ZJ?|j$@Lr>q z(xgq73+S!Z?L^sBzjMuXUO;le;2Jpks+Ici4L0CiNL~wq`y-8IOSV=}U^P^u>3C0W zztm?T0m#(`w2oG4QDw}L=E}Eg2)5rU!8x>oyk|Vu$Tp?HTbPjT!g-;l0|j)~CV=6^ zsaoeR*p2eX7%+;#4t8oW>;}e3myCHVa&Dbqws9iNnoF0L6_=Lf1X3kNost_2UkwRT zpnGWYP)p#rl%@=PlVR{p{R#wsAqn zF2#9_wAuS79=i)YAHSc5J@G=oj5ml2Q~g)q4;1!^o$^sie!LD;{=KHFx?E%66T%OU z_(JzC(`&#W4|Ddv%DdmRBb**adX|Cgn77*J{uU^mvhTr zGKNgM6a6gVQUjn?skc>v5C3MX0s7t;?8Zl%>;1RFc-T!zZwxrh=vPjf3EJ9)`R(IS z$~DoaKlkSH-MVCfWb-T=PlOMWpyNH-rDU=w?IOGzp!fo;F{2?6I*x{hGYb*h; z!pM{lgvnTx_fl&P$^G$i4%jgY(!OJg)>bDmN^8^n*c}kZ01r>m3p#VwX{G_W;?=oB z-|j{o6q!B?xh6KLrook7L*VePBx|2`Ur_vXLCIJ~op)iIby%WLu@EJBU?|54BhXj* z)D#p<49}5|(KRU_KndBKu z30rhzl)EgyGDZh4T$|()sC`-)5=2ywydEbaD3mg(FWrDPf>=C^E8X8&O9)^7g4aJG zt9Rb^J8Yiv681zZ6^LyjV!^qlTCG<*ol=!&-9f~Zkn*dGSW5_ql`tY6pQO(fCg0xb z+iMEqLX3n=)&`kJA$0&gH3=D7s>(FSsE|9nwmg8M6|h{@@*z{D_!-sUPcFGTr!P9H z>iUj9^c`A5~#H2@m>M4j79NKfRqnE5oX-_LCUK#so=7o|undtI;TV$;EMAQk^UCSGLY+j4wf-`|Sbz3`9fO9s?V6X^u&ZuS_#Nu&!fAQ+ zMo&cQ_QPd*Akd}gyym&@#+RSvUJ)uufjm->vlMF&FC!l29E<=R#a{#EW3Jbz6Vv6? zx2W{|#sVnVEnJ~sr_wS{)#=wr678*wbF5bB7S>WK&L1lBce<@q(x2CJAWS()sbo}E7KyVOBU*t0mXIbMU}-h?rTzh2kx%Kq zh0x7#YgyjuwAjzQsZA24%J@b#3f8aVQW3oV19jRyBeCQ9QeP!+9-f$YRrnpj$UMDP zZch{J1e~l>aQ*Pm!kRleQr@DKloZoJN*dge?{cx2 z9VnAI97X^Kzqp3GE_D95R&)^(CxU!gE_O9Ztvh6B9Ty6ii-DVa09O{P(iXXNUBZ*B zYdR{}>B7FyMl7DVo+5}=o)c^yEJ78iupVO{i=k0rWrr($_1XTW=5EJv62r$#!!K9T z-BT??l_6%=cJ_bPn^p3PYqrfXUC!+{(8ry{&GB4)>JBdks{D5FSP6M4a6SNZFKxcA zwh(?gx+cUWD6{!vla1od2>nsvj6NOx$r&neXpEPpbEE8E!R%Kl31o_=Twr6DY3xk{ zLEwP1M`}cMoYGA__6Ugx3swSlo|r5^sej<=Ffq*j?%Sy!PPcQT+w9MCni}fO7F>!( zps36^lh+FVm4lQg)qHkE@#cYgOYKV~z43U#R0-~om%78Cz{O8pU+%yBBl`znSbrWG zK?ck`Xqx!>@r4JD3R0 z4nF0rj1~n0z>gq_U4*pFO6pZ}X31v+2bh zj}_MXUte<7M;Z9AeM$1YJA!ZWu<#n8)cPoaXsF=8_Z~kCxV$ki`}|s>X5$5~HjqlZ zO`^0mp}aO9goMBGHu*dlzSs@n^!VL{HCEfTO$uE2?>fBuIMDgov-qCR`KjhQ!|xwA zb?*io5@vLLWHh8Gmr8VanYa0%b}I&}e)rQcX?f%uOZd2fnsE8Tgy@q-I8$xq;|l9t$eW%2nYrG z!?SjY|2rwT`cB$*X_@y0ORoAp7RHW}$oZZSb3&pk;_L2>60n}x=*a<0$YSsef$kvV z;!T~~>1OLJNUaR(CrlWuadNJQ*uX<()bA{5?S?@k3mzk2+L*37e8-7QSXG!1?3+u9 z93?sM$)+-;I&jW^ZqInn8S^eTQMOTfyrNr=VxTE04mhun@n&Iea8!yC_RYE1i+)aO zom_Bzpuno%x$%>-@jXD5S_;p~rj-^}6Su|G@p+uVB6EJCrvFzYL=P|{1#u-G7Wu0k z90C^EeMFqJ6Mnw%c#(44kxfVH9L|)o4sMy+gbANRkN*|YtxC-M-3`8bP6=$j%}-{T z48#M>`9a$~3|n;R=&9cB*C$G{&l!FEEY5VwxU<@;DD+K*rbJ`+2-XVWs`x9Eyc-252IiZ%R5w- z*JIwX2}Fa+o9d9IILwJnzdG*)XP+%`f?o>W9S4WF=I}mmwDVSF5Liq%PZ?S7c(IG3uD-Z|-TU09UqlPixRF3GX{G})#oO%hFIUI!3GYnAa}O3igCQc@U^zY zS^GS+@QA7X#Xz+2M!$;YBeGI{%9W2hFOpIft-U_|CePZa>i~K4@*mzdE=ghBKqt*1 ziQOu~w>B>J+Ra&71CWwX>;9dV3Y!b&T*7vysDZdZkGQ+1&d0QR?LB6m%2|(- z6cBZ~Od{DvAvwBDYe7e8y$u3?SoO6rsOU2V54%5~cQ49E2cvC-vFJCM!90NbVCXZ_ zm!}tp75i>(Do^{rA0`tSMC5h`iR4lCS-1k`!|ZBX`y%TX?~js~sfNC>;2ls-eZKYXE+ceYG(ZT4`$buihk!}gvac76)~5<~k&x-Hj(9jdO%BK@r| zve}nXxdrGr>Z>0&D=}#cu@hCBZ|3KjvG3&~)4HHakxH_`h%?v;Zn~7|nxotI9WrAW;QlU){(%o@q7LajP_pS_ z%{^RIZ@IbA%@}P=CFbC}qqMUi?8WjueGNIsEWX5(P4x^OLc|0?lUiX}%E-0Toc@?; zX-dV+*2OVNX^zN!>M#4RO1u)2BX|}=o_b;r@E3AD<_Nn+Jz>s`L5QnMafGo7f7&$r z?@v1TUB<6ZLBwGwVbJ~*>?>}oW7B>%*X9-u*O_UVBNrPE9eI2b5?=R5;$&@F=Gv>K z9W+0bD;XoMj@P*FnPPqc(e5e3;Hp*!vA@943BH8RkD)%}S@5Qa9DKe@MjdTtp1cyoVXcj?;- z`z;JLhjW{kIJTQ>1KP54=7rbVs%*TNJH^zN6wV_{2V1@PDCWW;Q^Ik1pq>X6-t(!O zx#RvEhnPz9-lNXBMW)h}EAsgML~KAg<>4Bk(MIn|ms3{RPv>4cpVZkhRR6tKK!?d9 zRB!7a`|j7YeF7?0g7aE+RqE|Z8)fr+%L+CVVj~kGvBQ>ti-`{ri*ARONsO1n?~z(J zKRc;M(E0wBUR@P@gjZY4q+>u9rDo9saaKm|mD7{@kQ?wS^1-83xKOHI0@;*)aXJe% z6*lD2J5UZc>t1i<-nioiF0qvCYJs&kD;^f@fJo1Bf5BWHjA%}&6u3YtEiX9cFji~bs15t@m^y#W2dy-j#+pEYW?#CEF#L}R znQIPUNmztl3)A{nfku~-93$>-6tOj_o};q#TdZil%|Q)}_m6-XeFmHE`K>Hsr!O8? zMgJ4{3kdeV!s737_@bY0O-b^DO|3$ye?`hxH$uq*FUC=>K<1xrWSXL|7quk zTgrxxu{}>9M#OGDYSjVasv6!-N%GYiOzR|2N#;O|zELWE*|ox(1CW|*4rJMA(wKNu zlW+;9y-!*Bh@9z#Dh|9HCdO>jU!4S?4{x_FI{z>cd)Ew#=|P`6k%4>%)Q75)36@Hc z`6S&G*6nLd`qK6y6hJN%S`|)TAe^ zUVl%ZpC{}I-r0rPlDc!#4+I=eIk-i>*#~dztimb!qA`gQ(wa#TnGlZdkkI`PDch~6 zdS~efk3cC$;nv`dnt4sxR3P^PLl2ql`bL`{)7d<>)n-9S`IV<=*#3tjJI_>xD$|i$ zmvepjR%GS>C`J5QCx6|8fC4msR_N}iprKrpYgDv$^4T8-aKG$oP*PnIWYJZ6>~SH~ zW;hR*NjlznFK%tj8!uEe0qH5uKzb^0$+1T2!265QIg&i1jvyYlBd4Jf|2ny*sVm6Ubx?y5@PMNB6BlndcvkZwWXg+jAJ^Kv{AINvp~6XU-~1ER55(idBMod~sN z;dWfLoj4e7D(~lOiQc7Cu=&qsMfaC)je{1I1Y|KJi>^H*Ko4F=Z;jhdd(CD?sCHAb zaTFbWZ5T(J@i5$QRL4fijb9C0M#(G7%b8kTQsBPM%VCuAr;l&)M{PRL?K*pzCYkGt z%zOQ>V?ni0{CC>U#lffcn+n?&f9pMfKo(%!`rT-lZ2mRsmp`P}|EhnN*eTO%r#1&z zFuC6VktB+3o6_$uI2d<4qu(&FVQuODxY)lZmRnU_LpKw42C+SNb0I^WWCqIBUuD1{ zgipDEG2TnkVEf}KzqPfeg8x&%u}3UnP$JkvBiZny^_gDYVdrZ|665l^s2*&4jnSsA z64S8oaH*2o`+Q%-3Vv&R4@&x^h;#%F3ZZ)4VqZHZ?2lCda(sKImiGn^=E~^4#)%1$ z#H9G_(rYEsaYhd|cT!aWk_51ms%~=GXavrn#~EW<-OhPSB?b3Ie~n?{PZjOIjU`9s zJ?!oL{U0W1nkfXT29of~_Hb$)P2cA_lkwNTO1eMylReW);^bzB-V{)V#;9!LgWWBR zH^sjG@<-xy#k^_c#_H{VzUa3WsBqo!Wvd&U;usvNMXdabkXT?jrV*;_dr(mh`}-i= z|B*A#R~1vjve!KMTsdMlRr~%Shojiui#dJ2LUYmEv#A!F%Vyy?p^8p_vUzSmb-=}6 zf7>`cjYSMKvG8nG4>$T+Yg0SyFSr*FsD6V2(7bk5+vd63w=6ZgT5PZRw0p=~z?RD8 z`9O%+%{5ZNA#(qxm#`Ga+a}$I95PJrXjZLU^56>|cLm426p%0znf~@!a zW?1<1%=I~chUC2_n<-}WLcJAC zGtBrs)BS$$?;HBhtCt=#&+~cCd7t-rpZEDfeBrAT;(D}&r;1S$S*zZ$XY&U;aB@HI z>-p9SkqHsO8I9^Rde`Xe*OJJ##> zKG=fLH1i9tM>D5H(fz%`_!r5Woz|MHx;ZY7#KX1qe=k^3{M*P^jW_!>0NBme+Ft%A zl+{@BWl=$iExw1*PkZN`j}js1V0s1^Z%%Yh-TkdV;pCFljOz8xb4&YPAW!myD&olLUJ1#dO*s)xgS_%HWgYmp&e1wH-EzP0mP zhXudD?W>ep+uknD5PGhDJKVW*L9fR`+rP||RM{5Xwt5Tj>Z_Bs7|&cYvW+%7b3l2!LWSl^0PoRtXY9>Y z11pT{d2&iRD4p1QZEUl9>K_OYG8h7Zae%@!I~Bau<-_0gn{qdh!||+xA0=YP?r-*o z0e!PKCsWg^X)XreT5AR5<)r&X3h`x?o3<3Vw!Q_*-iz9sU+TpE-9n;QvU{JY)BJ+f z-xiPWF4>$ENctHweYL+soRji17kuwnA=~QRFbOW2bCU4Ni>*;xgQ&O(II%@EwZ-qA~3w$Tb)?K-bWq2+<_bYO$*|V;T@vjm#B$(l9mKF%|i+Q+C;ow>U?H~ z#G+?pz1OBUrKX^6a^E=VC5aKO2z~f2*|A#RSS|aFdw`&?3CYIT?h&E4=;^jidkleB z4(uWZw$Jy)3_GKCkwM{9{KN};Q%i!jwOO%k+VZ%D##~hLt#j1P&IiyF*ZoPEl+M@I z)>5T`ddD`qe^b=NwN+G-ld%^rt?UV>eWYogyZ^8G{l9tc=MOGN09@#*YWnYv+h1PK z{maf=+~as`ZPlizrLOZo^mR?~V%usG*L;84R0k9MeUvh`Tf!kzhfmGL`bGX`-(}U5 zb3{e+y&jH`>U({g|HqN)UrbRKwu^K2-b`q+V?Q=5dCH88P?4`R%>*{PU`--G0v<7f zD6fP#z}^bof|yf#1A?h-ri!7Erj|?_)M0r`(QpK0F{Xl#D!FT)3GZ~0o)ivDzZ-SV zkY`23D{g92s;5ri@oDbyfN25aFMJmBJ|1=$U2{X)S!|^dz-Rkn+R;>3uj_hxlqR5! zw{`l^q7?mgrP>?Gx877kx>VFr%grx3_|Ksoo7Ajw$SWp}wi)*SJhNik{N2fm=(vC;hmYA%^X=J#M9@0_{bg4)^R_Fs2ZVO<^OQ zgyF+%E;?~(SMz4t1rGN?2$U^rA6WHoPAFEiXwT`+rQ|xPKC^07FUXTtD=p@(?^Puk z7)JVrsum+(-PpYMD$D=hV{I>zM|yRirQF@$YSHoyxLx5PKTe0A4Tvm*7%v$hQ48G? z6DAZR`hIZs)Ce^urQQ_KlZ}PgM z&u|hHx+~jyQ{;JN=mV$D9`hboeSg2BhQ_XT_Bl=N9$uqH9C-8_^l5ntK%c?sqvMUf_XZ*&K;(8-?D&b_#M4>X ztpKMi&mRz1ouj|W!{4^6<~65Dvh4n{M$~os+NQrUaAww=dps#Idb6$v5KTqo_6O`- z(3X^{O>+~29Ew7&*_b<9KM(KW6SCR+Yqb3@LK~2A>l6H9r{Z7w1Kg#bPF~1v7bwgg zkyZ9}jl|4Jm^p9C3Dte)^OhVKy6{#!9 zxX7R|_zYo{74jR92>%H;a}wFs3{>c-0UidbxcDKeZ`!gZVKo+(G)yD;8m(ot+thYc z&ddHrUZVN4BxWu!PCH)wO3=ue{+iSS!a1iFcG%-Lh%avM zu`5vhb0;@HeXF9J+>EOQyWK^*yJWrGnnrJWwE0~NtkdI!Kku&5uX82wHa+x$hTwJTIeh%YMc2Y-~1Q_pX@ z6iO!m)><`rQA1(O{up91b3z>6NiIQgAAR?0hWPLIt^4aIu~QC8l#T<%6`xZPHX^^E zR8o|FqqiE0=Nu`5qw68)jt2O1OSal=-~P81DBRAzO+hKwR=$rO zExCZ~#`U{s7Ygt=9;kVg5UmFq*WvH^d6rK0eV?2s2c@2GD*KwT*}`l92Km(dLq&IS zsAwrAWki6#!sacEEsx1RN$BmZ*eB^VfvW@VDVqX$uB|g?IZZTM^#{M92KWyEO|(~n zssH0s`9FZnKatZ!W28Jb6Jy1%)oe8|(kc(gB7L@re=s%>1O6QlpMAnPN1p1AK8aWb zl{Lp5xsZf>4d@-=LL#6;@SGk*G;QAPzQLh%Aj!e-B%Ay`>djoUWS=5u{!NqNV-pUQ z_@pO0lR+oj??|bL1Eo{*>C~@qjC#KH#K@ADQCx?K;F#!9ba?m=vN2t?eZ*>WAsCfY zfiHD_1$zp&-|O68iE6TcRbxtO7qf|~RL|M@FsQeI@XkeiFp1vvSNEWBj=E?0S9{|7 z-zXm5a}*D5=KVa;ON#?MuhOS*=~RYm`CJd({v);mWn)-zRC@C`E^-=3z^p*Wi6WEu z6;QZnx}x%!E-R{h^S5L1Nb!V*^}IjC6n1X>MV>*GGrQ++|%+g&nd!3S?4d}h{Trb)I$aE9I1Bz0%3@PL2H_3+|aVmdonC3l31@2KqX)tG1z>`_n#g`&~a)J?gb zI?X++Og7W-_{z-8OjT1*4>tm;XZlpFimS%nA8FyS}?nXFPwyHty;IR9_c2 zAL9f{CR0GgAfNK`e~#$?!~|fw0*4l|aP|7gc9ML^E<;i;UB1mU&vg^SUpmsVIhEea z`A^vNl+`4@&R$5idfS$zOj0HG#7cOz9Iyg!<6lCb`KdCjuQ0!Om9Uh^P^%-~_HWvp zqBH)` zZ_OmtHK#RqeVRyzxqC$-OR-tTff$2pY`I=g*M{X4(;n)I8@!#jfw z`ZnH~ZMy*R#$?=nMY**ih{+Q6l7IY#xM=%N)t^7jkBHYpWHXbgYQL9b?&7pqY^XnO zzGd`>Uf>PyS?Z5za%Gpq*P^vG>0Q(gvJp({Sa(^4+^>@r|Ls*R-}zZ%X!~($jEX8H zL_nIp;V_kd^_rCC<~W6mqeN(y1gIs+H00A|uKFpJQ@-hNlY<{N`*OhX3kEVVq5&R9 z_Gd{l#$k3%<{BEteK z0N`maCk&d(d|cqyf`UoU#Ilqt0&0Rh6*GJ0I>?O*=EYQ^-%Q%^+dUwvnHKHIdG94- zGCpG9t#@G)I*K^hwU?kNwxR3@D6MPR_d57$zO8poMjlL`RL_Xk;=+NYxZiT8SNJ}+ z>;+q*M`u=qON9=py>q%gX-e8T=}Y|~p*EqcRj9XB-})Sbx|4QK|NF+nZVesE1c;m7 zg1iJC5y7(dhA^D3h+?IaNEVLVlU~=<`bhr~sY&F5h`tXrC0M8G-pC+A^#W2QAM8jc z>%2a*u~Fe;_jnGV{FIFCHQ;wm*W%ShQD=C8#iY%kB>dOdGr$bZx6^O8#S1yBNsfJnSBFKn)YZRpP#9o?j|3>QlAhVs=tW4` zgnOBhl^3}Jvbr?VNJsI5VoU7Z-nrjYIIUx$9cl9}C&R`~@CL-qYx2xUoAiTq%d33ES5*tZvI>WzUk^n3Sz~oD5-cy)}A2ue(ih5RG(iuAbnACEmNbB6jTDGoXcg>vK z$<_CMsV6r{i$L&?4&++U9Mo_7pOpOH*bEGld2R#x;LQ5FmCdwa&(PbuH=L%QFrfx} zMJX_V_0{+{P`p?gIPl~OR3~u^C$;oiannz--{+6Vb6c3b#cP+l6WOw&)+HVr!{N^w zvrcO7i=AHxfdONFS({(0=C3-REmQl(E8fFHHwQsKFHvmyrvLw|*8jZ&Lo%E%9Jw4{zB!TUZqD8;FhP2KW~rL*V&> zFpruyEduKD_gX3hvXN`VwQTl0MNhO)j_%_@#rJz3=`w~}>#Z5CJV-{~NGe|Rurh`) z&Kh#+>^o8X5T8ct;Rg1_%>c@;Pj>a zK4x5Ncc6N@$kLPDCsUs!$P(3SP}VYb7tGJhi`N?wmeee z!97!5Ey4?Xl<^v=lC{fm{3x9F5iOOTX>WHUjvtUoPLGsi09%OA$%Vyv%41hhjg|Mp zzZlfbR_cY(W7}2{d+o%?u6r7T=$Y=HN`Pe2)=?esf(gjIN29Rr5ihTzPZoDgk?~2q z^bS1%Djk%aEee`3=`w-hNa<=_jqnG5AM9B&4j#iN1(Ql_*i~=!)@3oApp()Tvvj!<{^*X%TuT1I8JANwo^rMIQaVAiCb*t9|V) zRX=LPK$=QAES-L`-TzIhlz3&A{mkgVS%?R-dA;{ zgV7Z$i_lz)anZ){1?=4LqgzltH}pfqSUmYIJBNMWPd2Mo*l&LFC4|l+oTkF}iO+D1 zEU{)qZe8S(u4YffE`kN70~MU zOMojj0%1qiBeVtXok%MaSK~*?{=0bloFxxo%?bLB09tGW_!SKZX z+d&6#;3!ye)j|O*otf@S^NoFRH!T?gMb^#Buae!C2Lfy#TBFG2rpTfcv}c5iYK4QO z43zFqFh^pcV>7rVryugG74Hs0MKWxHDlBT~YS@&Zb>n1pPJ1j@b#_s;%!qCmSDyE* z@WAbksaqz0%GQaJVRckLS(&11M?aWhu13@I#|CI82OAiL(oLPQ^^M&00@sPtr(4xZ zr|sM?L7UsMMl0vk0^D7Yj(?i$B$v_jCLjaG$Z}RIVsb@0`(oHc!>Yu&6Xvc$%~tVW zx^r(ckN1dn!-knfFh6|d72#UO7wd0bf8}~HGGI%ep{998>w-$%wL7ajJo^8(KMJ>C z$Tt~4n{nYa`2w%Hl#>{ZV1_nIElcQ8Q>7<6QW<%ckuJ;Z4}Ca#(^c6LX+(bP%j|KF zw6tIWyJ)2Rs0kUztsi_*+-dL%Cl5E1u1L4e6@52MK0M&s8YYRgZ*`QwM1%dPqMyn2 zRe?4sZ=2*H6Pd-xKTWa=aX~>Hl~HGKSg)~!!|F|j`MfL5mbm8Nxv>Hj7);v=pYe{7 zy0fSquWix`Qh$ChFc(t*)*>7(^uBzv!ONq_senn-8ZD{{{&34p;92J zCU(&La6_oWCzN;k$KuP{Q|@0|n@*$KRDGAAmp~}w|SK4wzS4SQ-6WLv>z~9wdk}mUL}D2MqI1BJ^O96I%=!$v$#A7zMu9F z_Z*UgVDta7`Vbelk$fDg0rI#S+B}H=z;c(_XxOY;0ngH$O;?4;s2ZVJ>OTa?Yw*F! z|7rpLg$lsr$q4k%cxbhh95ItGI9Qx6=cDuE^2)(5D5W>B*kr%2e35-DV~LoXuYi3FXmVzMeinNiXgS%c(R zT)ga@T2!_MmIkIrENH=S|8sYiXNnvjelvMFFeo%&FJ91$8}WnG-Lps+xv+4jKhWz`QoqI zDp_V!IMawqCSk+^ZWScQkHF^aTs=gWiXc@vmCcoy;WJmC_9*dyG#E*DwUgQoBzfx7 zc&MDZ(Lmxw`^#yQQTbd4QzX*maz6b_3Z+nZTE8+%6x5Q zW#znQ_8+>c0tstAaILmSl50=*zOPBIKD2n(8@(*RP&Jtzk=k`_EkG4r?B?C|Z^ZcL z2Q>puFrJX|=Vtb_lU?h&%O);2kI&<&mFYoC&3$1Q6Ba(N2S%4>IN@1cE6ceEE59YX z^->7^Jz$3|DWL&x6G-{pFdDX>eQGNzpTF{eM2in~MM*NieIg@%YF$PI5G&`T+(7>a zOsxN8)+~5x)|uwLel_Qen+>1IA@k?*PNh4%mQ`yQJ(@;8tT5?)P*nKgdf>$A zB0gT>ln@EZ)5q1O_h5_jxf9UF@WgVIdR3E~+Avq0f<XYT9Mk8 z0xx2h6^eI|wJAp1@0&ubEhF%KUiEzVHh8;76?}vPI<-DxiRA{ouuHv&nM8IS#;J6TWEZtd<$1?<+Zr#H6gI_P5Hd3mrdmv$LlATGlxN<+?>6}VQY2Vi74!tWb zsdCcI3LYh!Ribr>ts`6>BJrdS0#m7&V0>sbn_6h7m8$)f|yg>Du(RS8o$x>g<}A%Fo1-8p=`Gp^pB_LP?8r zQMc!LEOkS#W#6bse*zFe2F*n6UqA zY?TUkzZd;5zG3|<_wxahxBnCkn*kl)75~rnN~RDN<Kt4ozZX|)J@me ziv`&;Z$>0p>nxy{RK&YChp(B%tCPp?6q`fTV&MR;jZ*Ipnh&lD z%KA(ELf)rMzoEm=M!-}r-|Jo}s>5Gx&8rPfe;`O&vn|%XL|IE2LkZmD zHWP8WyXCYlNaKH`1zMEs5?fbFa!yU%&b{D;xcvbo={=j5mbnIg50 z;Sfze*S7p_1!-Ddy=eEO@%?pst{+Ej?e*X3!Gh8m^L~i!voI=BEjmZ zl6@G?Ek1`}W5Y__U$cj8U-7-9h!&CSv7K1oP-s1fwcZRZG{u(y>7vQOwg{0iZGpKOZVu)XU2v zmu61;#%Id{YRylR^W@`9x)wVJ_55tZ(h-rCC`iIe*VIDxlGLp+0h?uT@$$v%v7%*g zwTsB2y|VQ@{6JU&D>um-1E42y_w)53s4kR)FS2h)Bv4`*z5JN-ys>lbZ5O@Vcq zNon?;k2tQ%-+R7yeEq=wI_b5ZU2fiF2Lz1&GQI+VZbVT@w zE-?Y8M_=s7)z3Mos=sJ;$#YuS5Yy(&s=sVL=%JW4BGY;aExv}xPTTYfgTnRw<2%=Z z+O>V{6_jm=9II`mp1h85Q$1lSd4Gn$%?O^lMxQBE4ka2ph(u=?!F!7^dR%QaS(Yz* zf*q_(@$$&Lb9oXpX=>)XwnpEUtg9(9d2rSzA-FM0v*#TZYq^|Uuwd#pod=uyTsT@g zI)DL|BIi@gPMQN+Q0=z))}%F!MDVTJBslW|uk%FIhIt@g4hmk=3}Exo7NE=QmYU#d zzs#ZRr|rvpONvVukN@aWwKj%~Di$d0!Md@`$gxLn&&o}DyXIlkywxRcvHYknXxFH> z*!NYN4;9XMhW#kc1KgR+kiBzu$-Mmhhk5{$9lFsbLp@Rj5rhF_@e_=zA)-CCW)4Wp=xj-MnAf4P zgy`}X8&`mRX;jElPyw0a*JPU|C8ZrupCIAkvUIB73PmA0CU`K;`O?zFjO|I`cl(tA z`uhw*YyK+iG`;o}Zb`K&0w9Baq|?8;b06$hcqC{Y3T)mWqsbL_uobnbxKk@7>@NX! zkR0{Rp@`R z`s0@E9NkYc&`*m)GsAc5p^0-vn1D9AU`noeL?4xS;YdJ9ATm(YANKZFiD)t4s=(Ug z{l`9a{km3a!=U@V4Rm%uMqLD;-BYK@Iwfw3vLO3?R)AWV+0I;gdgSAM#C>q9{EPDU zXG>a;=gAGvRH<;k*-EJfRhqI{F1b~~%bSgPD_lgL{fs#hy1q*u@C2w{Wfh*&Z9d7n zzxUDgia*CZ_w+vNTgObb({n{va$T(2@E|+xa9a9vJ2Ss4nPFWI|Yr|;TCLQXA_<#m5AfP zT1aiog;u1@?TLz_GU==`525@S6;btcO_ePS4ny_r*x4+%-i` z{mqwX2w@&4Mfj(kO6P1xoT+KoVw0W=B~XO|L_TXWB-C!9E)P&>L5y@J^;9SM33vin za?-;;(mDM4@s4;y~6xWSzRZ$l}}fq|+u$ z^FL4vF|sjvr-Iu~r`ZQLEuC68oCG8XVU${k=)0qXapRT8Mf!IxfzoCW@j%R?NQJs4 z3ZuHEH7a~MWwtw$Tq$zO=}g#pT!~Ez0EO9FACg!-6@ISJD45lH>8=`t6Ektq7YrOp z{N7UF<@W>ns$X&irFVr{X~HQ}ZL>M}3&Rt7ufY)5HT>u>MHT zNO3Z&jW+6ZGWsp1SU(SZ{L?kPsj*NR|7upyS+zW}Owas$td45uou{=Iqy?F+)IPtk zV9lET9|DUp){CUt%K@hI#(2Cu!~c;N; zq@-RYHB9X>RemHUj+ZsOr*OtxGHX;$)ZksR+j~SNibKW@r+Q1<=MOlek!6!Mb#8AU zsN2j&EmejATB4Aw&dMCtMvlunzklyNGdSlLR+EQ1i+4l3S1JI@5rmQ59k)_Cd{w;N z9Sw|;j;<*2fSEYGU2aIWwF0CMVtkHLmp??~u%1{OJ>2jk&Xhcss_!g_fw;64iS-V$X&EuO%nd$d- zorg3{dh}iGci6n~*Y=1v>`3G>b!8_L|E`%cYqJKUfHU$+a>mtQ1YjcQ8&tp41VW=R z43qXYyRKIg(e}0;)6KqU^}IZ2MDj{w&4rK%wv}e?CJ*puM=qBQbmWK?Q6@Px=qT); z#>LR9BZ_NtG%sfLy9&TqXzaT7jUn8&)qF;YV4m==1mDe{_8#g%u2)W<21Zkwk|J}1 z*!#XX?WF>B>jY#*Vc|T`h|bx*nm-3#7i>jO-PwW|d#PxkT)=qX0W?T#Oa(VK=68W_ z4W-sTn+-vzY#uhZ0j^nXf?pUQ?U=9pL=nvB=DpM#S}7 zBd(RBwo-J)7zv-$Ux!&lD9z3|m7#Rl!>%aWr$Fv~8tom-L)8}Q%s`y4`+DU=T{TDD z5tm@DZb|jiiFyxyAtkEa+PtVX%xor{bVr4Zs!;o0ykc=>=LgoKk7eec6|I*Z7fS+^CbVq8 zDc?74xc#k_tNluXH2nR*$TceBp0!!TT0;A)?|3tB;yi{l3q!$Eez`y$AOA7HSs?&a zSd;or&Cuy$61eS?jZ$C^G)!=ZG@--_PV!uou>afrTCRdQL?F%At5B1%1SE0DY0;GG z1c}bVfreBZcYE6r($h3Ss~(VG2#rS}yIyVhODfSi&gxxPUmDo2H(0axp`wf{X~sfC zq(g4;9ndtYki72FV@6Ezzu8xs|5qx$h)1>lp4~SS?zfEOYpg}gs1f5!qr-a?zaU^+ z%nZns(GxhdFK%5Xd2F(uQ&q1us?S4L{DQZ41lkrGMN2?bnU5O@Du$}`17onL>;eeI zG3XDw*9w+Uuy>SHp2WjenFWC$mIgeHuJ+J6;S-p$gA+?ak! zyq4<3IF@MRA2ML^@DQ?&5gEW2%?gB3=WfrJkBTM*`a3Z{rh%zeqVMH!Pdb0hGL#dL znXiT`^MoU#j{gfL(JFnM4R64GwfSA7uX&APSE8(qDG z)%4V$nheG@4o9v^YI%)c0>V~6u{i*rvr>R@AtU4?Y(~j=bO6F*)kM^rS*2W)_`o7=Mj$ear>m*}5*146}N&O7oF)?dhP=+h} z!lOn}iVS_1y@Ii@W$%8i>*VRSDEzrTMQSTuI_Of9;CEkB!-E7XWVAZ%#VX)*%;Y4h zB~&#C8R{D3M8#i0+#dA@?j8X1^UA8howy zFtV3u1JxqJ{Ped=0oNHY)NnU*TyHIhXhSmaudGT?Z{A?8;S>5#n`emA=FvXj>V5zC z>tj!92CWQd^LHL6@>EOQdZV#Br#SOhmivf-s7Y^*a*@1!75b=M zQ0)S4bRX5C#iysTLQ!lTVX>^db;lRY>9-&H4qWmBKp^a~0}DbdYe&p~&-DK_4a^?g z%EuCWCfD)i)VlZhKOOd=lUsXvUbo1_m=`NQR4#E+U6ZHHN$|EP)huVxYaRQ7T{}si ztN)m8ukE|T>+|Zu(flL7B5Y3TbqnU8T3mWk)(!79WG*&$(w%R*9of(p5SA=q197QM z9`B}9)_f3+oaLS#*;zh&RYdXOFF|n&;QIu~6hwQny$IKbrq^0NRV(F0)xF)ywzHy2 zt~b^!IV%sybr@Eaw9l5zk_$Cw=RDn5{ewkkavuv3(S+iQgNk$z8C496M1FIX_3?4> zKm%&6S$Vv?8rs6k$`5MLuLZ@OO1E$EB^Xl%ERY0jzye{la?D_gG!HIzFm*7~&2}cC zd&}Z$HSUYV_&c75^d+em99GXoj^B*yN2=W>hr-$i$m%0y?$g$2Y4z31{6u1O>b(5Z z>%Tq#fbu!dDO1}doBgvz7IfQlaA}yN+{D1PWuPEui<2T%BpB2K5as+3=!8?e1l?VW zwKJKoH>y3<#jqBY%A_~x73hgC*(o>(?`HtjDosn(o7kX3>wQNjH>N6IyemnSf&&X+ zmD7<>(3sq%0fdXn{pA^#uO?H5Gs5haP#VuGnP2ZU@aV%iIsixcS6|0!=YxG?&x0lR zoYe}d_L5N?iL~<5nH}^jS`h5M>3w+2tgrgip|OZXjkzQ`j|8+sGK?zbpoS<5sxPNs zja-?U*W}$9e#}KVs@6;C&V|*bBV&C9urVyrLloFQL2qLQdpls?M^-+445GSe3-#Hbb5M-N6k6LWM)}sIwglna|>~`}hH&`@=HzOupe-s52=Q(69 z)yLD^vX-qd$*sr+GfypVGSnHVFqx+^gN?X#+N8S{v{nR@Seq_#uRaGEy|aP%cH>y0 z2)rx$bgkkH{%*M4w`ywSGDb`MWp7-Owx1H2uyygXCNB`@m!(-D;m>j0eh0v5v&JB2IV4l*mD6UD3#@N6>yZ**!9~REigHah{JyYF+xo| zQsW4HAuph%76$z%6Fy>G zuYao-on11dsU|Vqf^*4#x*AQXa1`s&W=Yb`@zu9ntISo3Xe@LWDxvq{? z+$6t@8S>SQ0`bblIP1|=d{_ekibk}Ir_B6$YfAntR1M*t^+bgh0jBq$CAjC>C0j<# z*O`)HEmrjp2~Ul|ee;?hqUa~}L-ADVl2o3%+*5kl@MnPJQZE0nU*xl3c}v#a;&Vvvy?RvV|jf3~WtL=rFJQfw+%pR%7_ ziVHfFTt772K@W6}mymIJCocfL&RsfO*;e8>KVYL7RewLeTc{lF0t8!i_hSTNPJ7PV z98?oRAnQSBuC3Q#v6kx9;2h#v{D88z6~E|MNKdd5{E;d0uxPikvUfbv9h#ONOzo)j zOwntd#TJh`{+*3D`d=-;o?|4f^LkE|tl=Ler^D8jjavZUTp$c}ZV~fDt+k6%D!&S4 zCUjjBgwe(+tIyl=GlPuJ- z63+(o{-HM;I{c+}AzneKz07nq6kpQ5INtZycbl}PV>ZSL^*=ZS z0(T8WIF=znpwYV?oFOK|A0Y?P+TLZ+IXT^yeK z+zX22TKH{tlckR+uq>phc6pT#uR2uQ^3atXT~iJ-{@~}df5Uu?&wr~Im6u@!_u5WA%In-WiBK5&c$LF% zf}E^*&XQyK7-(H1y!=$rkWEmjlI|r-T?-0*FM2K7OW3AE=7CMoE5Fuyo;B}DxBfQR zMmq-Un;h}ax$+nKK?>1XQQ#ZRVRUlog$Zy2*)S24q#1WhpMA4;ptVwbyRPhY#C5Vp zuJ~M%YI|-lNzB1x+M3*Wk^(9OS%|;Vb7Mae5w&_rc_DP0)+e1>56u)ZQWfGk&(X5;f_Cpx>>l^b6iEW^w_Am4N zGYudCOamYun)s2W^;QW}y+_vqoz)A8JO5Vs@p79Qoq=AW&#jd$YCv8atSg>@#vzBz zYz5d%P>L$#GF;&ic22Jt@Ps2SC`zt~KZYRKWJFBdTUUda4a1_d23wbSU-P{%=XHil ze!|yuYtpqhx4XFl%=;{2xG>98nPE1; zZxrXV16AN~!AG>g0(z_pb~G32_<|;lvT*0+(=##YG5l82?u)pkM0?RgWr{Mg$o8sX zEUfvrLC#K|bk8ZP>RdLhc&PtD?+jZT>g`ixa(2UNPQa;gWyhBWrdh&>P)o9R%qsnl zDEOl?Db_b5rPi{q*VR14N*65nUQrW+sZpzl+N-e&?S4zs?%$GDrzdanWPW6Co`yv069a~Ybt$& zveAD7Z$5LUQ7HrsttxJ3RC+@()y3X;^lH-1iGDI=dPPcoeUcdp-pfM>)IXH}!T=)g zmld$z(yRM}vW0>|vmf;-a}^iE{*nyujecn2COPu0$cCTKqSafTWu?Nq~_XFxm-g(V!mK7TUBk~AHPH_)+@WNfM5XJXtg6L#yH*{hTaX@&U6p)2*Lt6> z7jlaFvV-hzNt_&g6M_ncr+D@O<{NJ&Fa>g-<75{}06+W;0aZ0YcpxvQamFDi1-&Z| zKqiVQRKMPVhE-dai~#!}ys?o3;al9BZ?1e;{&)9gAFt=yXKl9w{q)_MiGvyW^cy+5 zMf9JG?i)4C#=jo(9R-Zk+YM7=<~sj+(DBIb=3A$`mv+kz((55!^bZiT2`OopeRoEc zB`>$=ZoMr_w`=tl&VivyYJIh!k`JRD-iy$Ur|<9?leJ7%G$=7=*ZfV8mwKamlePvS z=mH{T2fN|J6LQ@%e<8FsjPky>7#fm0SshA1y~3C zOwv|6nDWR$lfG|&{1{m30;2+!$-q*VN3fB*DSE2Fl3A1V$Ta#{i^J&N-W7nYkL)cm z(bIwDN?N42Vc0-bzE8wds0V%mf&sLNqhFh@KHLD!{ZSkr6UH$qyb4TLb;;TOb%Jz+ zGif*9NNjmEt+)%VeGZqU0w1&uipc!g2Df7B0i^=dn*Wz(qf;rAw;r^xQpB^D&@R=MBI)sCktx_Qp7Ec zAfrO{nslD9iVcE^<%EAEKJ6TdkHfj~#4V4lPwX@0j4s%Ut`)=pILb_IW5*tB%E7V9 zj}~_<06SlW07qD{{TRlI!S$@Wgv@pmm?7VWm!1nKx%sYmZF}r}B!>v7`b9XusJL3goPFa6MGI8%2|Qb1Qn8M@gCHPNE!bO24MnMcrCdxOUw zE?V@;=Vp&O-VG8P8;WTxRc%)P&+ryc3)b;b98T|&kp4ul3;J!P!%BN7vwL zn11g$f0gL}Ty+?Ud?tI?e4i;0+# zxMnAYJVr7rSn)Tta!V;R!>cc1qGiJ1r0pO^OGFfGeeu>BFPiO(co^H|tj#Zh*`wwVj=3AkR?6u`GO?pNm9MInhG9p#6F`dfc|ccbw~M`Tuwh%oc&7%SGXRDZ7L+p6ZJchgZQPVE zrhF}l10%R@Y^s?uAH(3czoq*LAGr-I^19|nrN;m(r(>{` zvNUj%?_y~|x#iv46sY8#V~12*tIvT2YL-{g;Eh&GR}PpSzf$|YSL%gl_<`%4MPECL zdVpCmp`Wv2!cG*L72>b@LarBJ2{F3_t-9t+>MFf z)4{7Zy1X4sn@B&-APLo`4u*JAWmI5J#(h=oBlToDAX{OikBTkdV>7=bpHnrWFMF!4 z+2`VR@JaJh!GC8@s5Z}*VAHm4Xd;P9$_H>)CD``ZCZ>LrSCb=Q=wzFq`LHm}=#x5~ zUH%wR{k9Rq%Src>Wa4!ac-K(J&og_^h{+r;sV5H^-rIbw2zP$>$1y zNDitf*xk|u3=k?y7}Mh@U{7Gm5P0ywrdGsMn?jR1Bu24^%cQb!Anr1lPl*Sy*aMw* zkCMrga1Z~%X^L$$9&i0eF?Ikz)T$Be1d~psEP-T}?oO*i0N5r$7h2fd$T$`Ck=!ra z*9B%^qZ=@a7Wr^>$HHI1h%4th$$ala%un3hmIgvXUycJddzV1;U;A5}Xb*@23@Wak z-?j_<=a3bLI|{8E$yl=!695p za<$i_L~jvJCHrSTz+{HYUGFJAr2#*Rmr)K5&dQgfN;(5AN95cta8jyu#U5pr+x~>7RGlkk-3Xk5mu0Rfq}u zK|MJ``X;+`5*Tm!8Lg?Jr9#zD8@#66mApP7stHxgyLEi^4S+qBQE^4?VQk<72lZTvtD9 zt*(AWc(&s)E0jS5#*aNJs(Pc%_psNJaP&sSDzIPChg(IjfEMKYq%Zsa4hwpbic&GC zEVTyI{}pX~n|T#RvNC*U^`WC9>J*f0fPKn|(Yb}$&t0_5AOg(lgw7tSGB!ll1_$Ou z=^81O@>~a3_F1g#TOm~-N=r~G2L4KRTed~!J}c~sdYW`b?CP3L8GHEjMd{KE^IQd$ ziefix%7s1t{&hrcvk+{mF!7)xM7wK>sY=hxEq&Mg#}kUIOh^%hG5$ScckK` zI_@|f1A}GdV!nA8{5aE*w+VJj{wN;c`_hDw!!v4UeFNP>Op2H%EpEGY=mMEavQMDgEjm z(q{p9(69%_ZNh3|pbv-}QnIN6Q zi0?;g4E#ms15Y!rZd{2H@ctJ>4D^sfAZ&Sv&$52>e-W`vR})nPFi*HRQ6UgOw@CaE zOxCCIW4@YbeY=NJ>P1{fQv>G7*sh#*t#&PAR;CKX3P{%ujZ#s|SzTvwVEh?mw7jbp zviF}rs(^yKu7O*VgpX@j1OXDQ9eE; z@5vi&>+uuUR=)ilCp7&83>PcVKuxh#`7Bear)-gnUi1q z1seSh7j86zd>nT`X<26%G%Mh*^+ETazg`!%2e6^@ozuC)kK>JU1|nv(G!QX=01@-y z&xomy<*CT90#}R*i6j;k6=VZ~8#*)1ZB(GJm#$9R*VpEIbOrIYq&C^a(S#-!)3L@0Cb5HJTr$)7z)y_QAfw{ zxaHxyT{n33Ok9PpNbn|%|=gJ2pW}kB{yj6$;51%_V)?km&R|Ep7<&Nt4*5G0d;Fz{O zZT=nfMa9>`w-g~nrm=Fg_4=NleyGcP!Y{sQ&ZX|QN17^p%k%F30LLV#j$t}t*#)Iv zMVV`Fy+M`lMn69e%->R+n$@H7;BK4KDY;QD%wg`g2lNP*m_sj}4!LL>bzW?`6A8b7 z$0N`kXOneyJUsv z-1U^ZUz=!sZ)R-1geUL(HCAl)--mdSlNH+Ev{jYgYZ+Wv5ap$c zV*jjWtOUn*l6;v<+14{Nc_wMh75(&>$kgbI#o4=->a$}Ss3DZ{^%}7K%hHcOY*D%N zw}hM>RzZ}Nv3&nvv|)Eyd_gBq8FTgOCvj&3YuVg0j(~;2VP_u`GwFKQltglkZG}dW zj!f4}xcGy_P?YT%;+b3=%@dK`QFgy5{G9xz-4%G_VQXS+@?40Ka=MG z0;UQ_Q!hqf2jY9@BRt!1ODFTx^Bn%XxRCIPufjc&O@B`LzrLwWI)HV?E8@d%s{kqxOdR?1|&Y6V5 z(i7n^w2X;kI^tKq)0>{*Pn*+VPQ;LZvl_TnR-+IE8zHADLox1p#o;sXr)rA5^E5ShG zat=sbpqUqBN72elFCQ%Z%0V6QPUvm)MSs+}eY70<2vXjq{n%nys{iNw#H{aoPUfkc z>(b=U_)h8(q3xnol*L>3@I>tLd)=AdX(Gl?2k1({fC62ue51jZXlX}9;htkqbw=!7 z06`Ab@`rr%Ic81OcLZgF3K?TGHxF1D^&hJ<8K6V8(@tm^VC>%UNuMl6| zn;E_HhA0sDQF!ycSA{xD?A5%dj_p)~mMz|Cagz4WQ8we2)Q={k*;6Z-5rc;QzsH#Q z7V_?nY1vYs%;tZY=NQptXAT=Sb<6hBYC(uEt;iF#L!Te2U{ozh3-TMDJv8U-;p8DQzO zS1m)!>&dWc?B$AC|A%M*Siumg8K%*Mr`0@n9X>bIHl|{WcW$bO z>I+IA#=IpPU(|*(aJzuz&F_3>`mz(6+4YKi`+%jdeO=H5JZfu{Bd@`RKb?+UZ^Aph z8rKak(>+(?I_;tkV*G{$P0K7TBapG2)9Yy~KZ@^+Q$xiL*AHR^T!?`kQ5!r!^P1LS zI&;}PZg%%_1wG+4XsHORaz3Ke!f24CTl%+tW{ivSBABN@;@jVRDtWz;PNc~WGl3l} zO{*GrBO^TF-WM%Wl{}AE`@I2x96pg=l7omi zXs%x&@WheKM9+_Ws>EUQJmjk-&AI0KQt2;9ieaS*aq|QtE3+X>p9`~f7exdWNNZ5D z^D4jd(~*jOEi#881YddOQ~=9U>a60zt=xL_COsToAMq;9iYVf$#5iEKm_fu9bw^3CSY`ehY^x+-@ZPE?gE@)eCKJ?YOj(h4H$ z@*1+CXfw{%D7y_Z=*#=dhC|1ip7lK|zTBy2_HEj+y?dD2q=oH7pzrHjrMWGJk@F3g zmKSJhWjMJGfZ088cRT{su%!3?-3MyzSXA?HVGsP8rcFyHxV%n3&xjkOV3&1}DK@9>KmdxMaKophs10qu(0NqC=|)PP`-N9U)!wI*G||yTl=R*l(SfH(q*FgI}I`wxH17 zfj-mDjM|F_g!ygCwd*T8##}Yfr#{O9GXP9640ZIoyOn}&gomRPe7*wbI+;(Z`_F7|-4w5oiL<}-G#d_`^>1mb zY6~7OS3TzQ2kzn?_*zK;^SXvaC30;|_>*j;!Mn$a@oBUa&9a_Mz-pL2UrL;U@H&Pa zgyq`cU2GH>X!w~L&incaXCJ`Z{db(EKbU)Kw(pPXhUH;xd*S-w&c*Y<<;4th8(2LK zWQ*|#re-4nvO)=B=J$<)yDLpc2#c}*sq@Sz3i5*{kJ(XQ*r6^zs)-#wiZrjTI<@3b zz=}Ctz2_QslXhcK5UN41Qg>1q`Ol@ih-4jGSoU&SbtEH@-{eM? z=0qN=dyBH|^C``a=x~RZFB*4`>kI73D9#K^Iz~#vMdV0Bu^Esh-hYXEY#;%cZPF^U z?Nl3R_EAumdEu;gilmiws?ue*mUHSyQpr@VZ`gEyMIf6szBIj%C`yokF`Ko}=S|Xz z0MsMT!?&~^Z~1oIjUmm*9^+)ZCFI-H?*!emd?gP-e$+mSZS^2qJk^n{m^oadG^hnLfb`=>+d< z=`z21S*A-WA6k`ISijg_?}aSH!j}Y*Zjq22l|a{-H3vDtCM%=Mc>Nd@oi&2 zqX$ho5vmp0{KB+Ry-^wkDx=4aLAyXnPHC!>UOW6TR%TY%s_IykMq)c66cO)cLDFb4 zX;MmzNLk{ZK*tuYx*y(e73k+6|F2B`Q%SW=m+oH-48=8M z8!RfR{Wl-&;wIpuZIGWx}xxa<(#9+^*&wPOUInDKAIHYNj)Y0gpN;aL%yllEpSa( zVpKQQD%aik@E^E()yqsXZTf1i30wrto8)lx)=T2Xe>rctKq>gler$e8g5)GVfEv_#^MTO4HWe zWAD0kp$?oOLrttj4Z~`dw_x}SMyjq0JI2`ASCv3SMGky~mg12aYji?Tt^xF%hJVtt zrSj_Yr4bp;f=?_VAP8b0URi*i!)#ONYcvoGTJhbsO@waX^oN1M;7ymLJF~sXbsBUl zC9^nq|68B>{-vff+Lf=vemjgGz0MtaaZY{b$hFE6uMCVA6ST5QDwUUgS)EJD_9EjpDOUzQbN0$bU+Ul#ao6QWU&9Vp zxTP#Ihiq`@8Sk$c_W5IgafWP7{7t|(*V+AGoG}bgA7oRiLIRO^9N!DOB!v0bi$TR7 zfbO5UWFHruU)26LCib<&>)p}a`X~9~@jAzLuOfLM9#dJONwie$m{^$@zc^QmyD?iX z9Wm18%Rw!D-O~ZxTb1gf?{Z6?G4reA1Tq}RH9l#XJ%BgI-4}2~=9sh`0gBAYg01q- zL~Eedc-UtWJ6iYk1u%VVtY9fnSlLnw4Uw5oPcI`pWKhCZE(d3h0(o#bgri%8v-c8D z`Dpa`PK$lxJ0&*{SN)Mk7lERF-4QA!FROy{<@NXAD)@B5%@xqWo#;DthnBq z>6w=7celf)6NPr<{>JG4hlk$5e>}|KxO#j_fGwGyT>Rf&kgJl77B7@FK|n+Y%mHPT zs! z1Svr|up)6jq5T3nf?PYk1K7Uku@g`yN~R zDo5c#Lo_BAw=?a_zLwC@$D}}pcIyag|HIhf!o!7z$HkwNrzGAL!@gD%C1ImDh zkcIatZn0S0(F1t`*W#0)Rh_YkbHihwclifaC6p_#T zU|}#thf!Bb-|oE>Ui0+qoMu0KMO|OmE4N1S$PBj^lF68bKF6Momr{o!5wMurU1A$4 z$blTdJz%qriUc-$;9nT3m;C^^NS;{F?|wyCIL{JBi?EB=$D?-|@&aM zv9Es?Ud$21>KU$_#w&}LuqgIZSI`kN%~!;3C}rlQXxKdYLsYPJP)oYhb)7y@q36Jp zJt;}H26krKUwTd#@p68lL7BAQQ%{RNohrwQ5adI(mvlpd*ZvW0{$1F(cP? zqK~?^8f|URox)-3|NY)NH5CqlV+=DL|zLs8YSs z1gB@@H9TVkQ);2lmzQkyNsn9u(E9j0HBCW? zn0LJx(s*FG1a5K$Fv(aGk{!}7H1}0HscUM^gEo`*vjAafzU?O5Yi|7a|9nyzLzEOJcXDPV0p3omFWp6 zR#Sr(O=k2)ZY~ikm^575eimjqkiB7S^Ur5eX$8Yn)7{+0KW>F){sc4;BB_7N&c$6D z4cyn=8C_PWj0qNrvEU5@ORLwzl{caJj-&-n{G6Y z2<(b#nPt(WSwDCAesKD~=8OmB*Hmp7D=%@q4@(up7s`yP29ESQUGhcfK;Y)az8T}t zMxsUUQ2m#+uY$G>1~hWR6y|kkbf{!xB^Mg6NmwY1XhOyRB`jNA{RG0^APVHrpVeX* zBmPK%?!ukwhxSP5G56A-^e@IZz*`0UZDF+5!XBJ45Mu+Gjy&)3PqmS9E+#2(xv-Sd zvfL*qlSTvxAnXeab6kJ0#4l@40UqG~NTe1ctBLecBz2QP>Iq`ub%bcqP;g(*DhN`j z%LoN^7S)z=0(Dn>ZRg&UZLsOUvqZfI%r&q2?0_wxggo>`OwNA1`W{vs=D)6gWzsn|Es7$G+6TY;p^YAMI^&N?w;4K z#S*)>HpWfR--W}2cG4Qu>RA0D%a=tlli;!k zJht?m0eu&J$E?kQI!ubXqO&}xM@k*>!vX_DK;Si7j#iJla%U)nS&8xcYwm+{_d+&* zWhJl>zjXw|s}7I@OTRV0zqn$;%e|3)7{N4h`#I66mzJG8cy-SWBBZk(D;&qeQQma1FM<1 zt+H)hFs0WSrVqcBxaRV1bUZv$3IEq(?1H^;EA#4pS}#V}4W`Z{REDg1K{NHh>YBK? zfh@n~cXSlU!j7WX$hv~v5e0@JVP`kz>8}BUfYQPQZ~)fKskLvLw6s<@?{CSgA;R%) z^*VwQfNg8;aQ}bWWBv2edzu0R?s`b!!kQ1_%u(e3o8t$@qT;?t$^h0H8Gk1JkMdcP)8e1%1mR9kFm;x=K4=zERA zK8IVtY#;39t$;caf1AF&*~u5;TIG-Kg%XJk9-F1PdEKeLb6pV;}qRJ>c+T$@kiqtNMUY-igGlxDG@5*@KejL&~YZ`0(zAk^PVU+!XnnqMvdNhhh z>$MXj{a0oRe1Lf4TfESrb7sSqPK8d_R9mx~kwe|dG_8Yg?eT*JMz1QAS?AF{M6MSx zHsm4s<>i+waFW<+%4peHw(?B5wdJq!q+j5tLwTF&aO~bs&f0d}X>}%;&_da%c!)30 zc&>$Pqt9@cRCpP}kZ+<9hQGqNFX}R=hdkqo&}Qw?Equ98-6+KAUgui@y%R=o+Ap`^ zH}BZ^BnRJ>`P%n%v`CL{zL)PSs&AS2R)$vUY~a+EXnsl)t( zk@mN#*NNKWfu#vq{_bj7J*MA2vOg~;ILSsu)zx~Owj(oOb@la8#UGf%P)L&f>F;!c zn%-PCXF`$WoqJ3oa5>j3Z{AEVn2^r$bH^)ck~OhXQrzh(ryQwJJt1X$b@|!kH&eyW zqC0i^T(8*ig9f(;mb4zJPBJiZ>MkJt^fj?B-iVat_C2w425!tj?Qz7^MrJG@jJ(wt zkl=?S8|qBudd1K*YoEhn8|MD@JIW=677dLpgjMnQf7wtP#;Sms{?BubUTYYWeifg% ze6!@IM!VXwcfkk4{tg;PLCy9+@&o_cyloJ2s99lXg*)<_+_v7OKtH+7Pf=T5eGO~W zS%I4j7Y>qFs-=vQA4I!7L&!`zhD;BAmnUhB`!PYCds`{!H=xVX2<&oluUa8m)l7L;cEU&+wKw5???qnY27AgwWadAt=kh2KW8i1wtDT%6s_M$%-nnS$=K`yv%5!HxjJc!gn2vSNpq%8i1F15mrp(c7NiZDABay*p8FOYo|08C zPGsFUZ39uYYq_D95SL*I9O$CS%P=+lr|*`ro;Y}3Mq`ZO!A2MowR5x<&0a+;cZpoyMa~Kv&+`}=9yyNXj8i3jD7usSr6(w1 z*7}5UXl#(+7rQ|M%hfBNK{81Aa``=-ci>qc>g{+;szYeB`pUuS!JEHc7RJ=m2WJN6 z^}Gh*Df!b>-Vrs(iSQ?O@6HVX8KuN?Dsis9ZN_(h8D?H6Mq4@YB*r=hAhpc*8F>QIQVMPL4Vl(^O64vh-%Jk gmHB_)T*KcGlN(O&^)A$12Y!qVFP|^?*Y*4V1y$-BIsgCw diff --git a/www/images/tmux4.png b/www/images/tmux4.png deleted file mode 100644 index c2c279ef1ddf880d378f9645b0b7f916763e3541..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 328784 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 z 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 - - - - tmux - - - -
    - -
    - -

    tmux is a terminal multiplexer

    - -

    What is a terminal multiplexer? It lets you switch easily between -several programs in one terminal, detach them (they keep running in the -background) and reattach them to a different terminal. And do a lot more. See - -the manual.

    - -

    -Download tmux %%RELEASE%% -(changelog) or - -get the development version. -tmux is hosted on -GitHub -and needs -libevent -and -ncurses.

    - -

    For support contact the -tmux-users@googlegroups.com -mailing list or IRC channel -#tmux on freenode.

    - -

    There are some programs to use with tmux - -on GitHub and a -book on tmux.

    - -
    - Screenshot - Screenshot - Screenshot - Screenshot -
    -
    -
    - - diff --git a/www/logo.png b/www/logo.png deleted file mode 100644 index a13e3d7f3b1618a98c8c513b33ff96af521a7698..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2701 zcmV;83Uc*{P)Pxdi^5MSZA3#036Qo)p;VO8CiY!O(-s*y0SORoQ4`dkJNxeJIBJZO zIQIE=r{Ao-d$W6M$1%S1-DyX%=Djy>{@#A~W_ISSDM4y(ZvKQMNe_c;qLfy0*=$mG zcQ@trR;*aTW>cTnVT=tUJErUUy*)iWZ_TzHuU`IGK$-;DQGjdJ2LaezO^J_zj|HGV zx_tTakIz-RUOV$=0X$3TVLa5p{0+gqXFewUJ>p*KNejsWJ`A=gj|Y75V|#(f!K%iT zIVUW?Bk|T~g~@w)?kym}Tg-Y7mhIu@^VqUI_hM>aDyGSJbIDH&cp%Hq=TcOo@<3Kp zk260fl`=KvCIal=W1}Nm3La$BPkjzcw-X<{=F?Q7I{~OE z4|h4}d_JkG`sD`2>|N<<*a9FO0ic(fGPfMce|D4LUwY^$pOabH|>4V4=Yst zwq2IVl}ZbCi}S;3kxHfX_1vp_Aj`B@rsReCvS4%L%lK7)IkV7BLW6-)dM`>Smbb^? zy>IIm@-&v=zlh!A2eLJIFvvZxYpM&Wo1kM`W+`FY2UhnT z;}vE&ao4u6bxz3a0+U7t&eh2+dfF0WkCd*HN|Fm-~wITJR5+%-M{;#o~-Um(Hf(My7k!r8{0Wik?pzFyy`rG>lh5izt{{~>ci98Ij85?Qj6#$(Huu*{Pws>3gw?aKm#<1Tx z5|q@xquR1`8T%CIc>~$tjeYOcb*bN%B)Y>K6YOw9UAS>Wq9wB5lo5D%nUd%EzSfdq zPseELR?SKU(Dc%zA6=hlR zWM=T9*ik{#I@ty<#DhAgO}fT*$uiv{Dly8wkyzdPswi{Px;Fm0Bn2!nqhzVY%olMd z8P`JECW%Qslir+zzox9L^1&f8ba${zy#)ZF^)1D`DTExM_a9XS_ zV)HZ(6^rdZ8$Hh7sw@GB`1{c(Mc$203?}|Fd3I+MW2rUvlWw}Xl=Zjvy$)bKF7^q2 zGv1cJKqQK{$#lYb-*N5a*ga`Un#zhN`yk=h>7h+f1?3g_Z9 z9>}IOtygGTfChX&#-_j8aV3BU8$8R{ zx44=ScSe{kssy@s)OM<$6`SF7QGyKd_HWoHN*__eN`qaNXB>VRGZ_&c^pL3MlSxLX zw^(k_2!d29)%^g0EwC^{PY?tzZvdF6t!;SGuMfBOS!ZSEMus=T(?x}9M3%@?ST>C- zC{tdXAS2{}IYFKq7tYLo7?A_`@WY;#99&Y%C3XZ67QYf@LpKi%Ax>@sHfiazGVUq;zI_Cq z)6^|Wknz|qO#DrAl03(r;R-|!9LCo={rD>9)nG_HhCCYZl_y|?eI=6IChF&$o}niL z6#wKHTa&M*j=Vd>||9G2-`GObxuWiHbXlRnfZ_>9L zzswS=7+*ytllom2FAxZU>c*ICQ7QoRN_fuinrcM}GGlTqwH`(lUjPg-)+BsgL{|cJ z*C4l+P#D69el-n~^L)mhEcI+AQ0P*4w3kXKiQ}7s^A+V)r)5e0GxuH`F_UvSimRa} z051Jj=rTz}#BMh1Bb1@P9XwiA5ZTE1ndlFo^O{Xa$o)#EdL5S)Ey&_+@#DJAn&Hv@ z;h4NDi$KAZt_67})B$f;}N5#q#B;~OBe_KN_jjFn?Q_o_t;GH!(a_UOCuqtUB%U0aKX@(XS-Je$*? zQ=kW7{CDw(xPAR=G^MSz87_~RSX=L2e9ZEB4AFY1uEXUQ+Pc0GVdEPK5A(i23Y18X zLWM7rJSNWggYE9ZSNA+#Y}%Aqoru7;cS6aki!Dk`MOo!eif;+u2U9t13-eS6APnP7 zrfQzL&--qx4ycPN{DePP>?i4!0Nz5lw*;l^U-&NmMUv9?#&3*|I40tJi=E0NI;zc{ z1<2GxaBZt*-$eFm!s8smCcY(mJ$NnW!U7V}Q~V>;@mzFvQGFn!7fTo)o0kgQ0#dv^ z{ua~5o3J;*Yk%h3Sgz;C$6$;_AHO!XI`($1`}g{rdkg#@KXEj>2Gf_l00000NkvXX Hu0mjf@);+v diff --git a/www/main.css b/www/main.css deleted file mode 100644 index cacdd438..00000000 --- a/www/main.css +++ /dev/null @@ -1,55 +0,0 @@ -html { - max-width: 1000px; - margin-left: auto; - margin-right: auto; -} -body { - font-family: Sans-Serif; - font-size: 10pt; - background-color: white; -} -#body-wrapper { - overflow: auto; -} -#upper-left-title { - font-size:xx-large; - margin-top: 0; -} -#left-menu li { - list-style: none; - margin-top: 1em; -} -.menu-headings { - border-top: 1px solid black; - border-bottom: 1px solid black; - font-weight: bold; - padding: 0.5em; -} -#left-menu-container { - padding-right: 0.5em; - margin-top: 0.5em; - margin-bottom: 0.5em; - margin-right: 0.5em; - border-right: 3px solid black; - text-align: right; - width: 12em; - float: left; -} -#main-content-wrapper { - margin-left: 2em; - margin-right: 4em; -} -#main-content-wrapper li { - list-style: disc; - margin-left: 12em -} -#screenshots { - text-align: center; - margin: 1em; - margin-left: 10em; -} -#screenshots img { - text-align: center; - margin: 0.5em; -} - From 3ed03df23f7d99f53f953784a0738ff16242cff4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 01:26:03 +0100 Subject: [PATCH 178/703] Remove this file. --- README.md | 63 ------------------------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index eab63eaf..00000000 --- a/README.md +++ /dev/null @@ -1,63 +0,0 @@ -Welcome to tmux! - -[![Build Status](https://travis-ci.org/ThomasAdam/tmux.svg?branch=master)](https://travis-ci.org/ThomasAdam/tmux) - -tmux is a "terminal multiplexer", it enables a number of terminals (or windows) -to be accessed and controlled from a single terminal. tmux is intended to be a -simple, modern, BSD-licensed alternative to programs such as GNU screen. - -This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. - -tmux depends on libevent 2.x. Download it from: - - http://www.monkey.org/~provos/libevent/ - -To build tmux from a release tarball, do: - - $ ./configure && make - $ sudo make install - -To get and build the latest from version control: - - $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux - $ cd tmux - $ sh autogen.sh - $ ./configure && make - -For more information see https://sourceforge.net/scm/?type=git&group_id=200378 -and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@googlegroups.com. - -For documentation on using tmux, see the tmux.1 manpage. It can be viewed from -the source tree with: - - $ nroff -mdoc tmux.1|less - -Some common questions are answered in the FAQ file and a more extensive (but -slightly out of date) guide is available in the OpenBSD FAQ at -http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO -file and some example configurations and a Vim syntax file are in the examples -directory. - -For debugging, running tmux with -v or -vv will generate server and client log -files in the current directory. - -tmux mailing lists are available. For general discussion and bug reports: - - https://groups.google.com/forum/#!forum/tmux-users - -And for Git commit emails: - - https://groups.google.com/forum/#!forum/tmux-git - -Bug reports, feature suggestions and especially code contributions are most -welcome. Please send by email to: - - tmux-users@googlegroups.com - -This file and the CHANGES, FAQ and TODO files are licensed under the ISC -license. Files under examples/ remain copyright their authors unless otherwise -stated in the file but permission has been received to distribute them with -tmux. All other files have a license and copyright notice at their start. - --- Nicholas Marriott From dbc5d7b331027c3f44d1fe41ce0480cfd6cd1d72 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 01:27:47 +0100 Subject: [PATCH 179/703] Fix clone URL. --- README | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README b/README index a5898a65..7859ed71 100644 --- a/README +++ b/README @@ -17,14 +17,13 @@ To build tmux from a release tarball, do: To get and build the latest from version control: - $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux + $ git clone https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make -For more information see https://sourceforge.net/scm/?type=git&group_id=200378 -and http://git-scm.com. Patches should be sent by email to the mailing list at -tmux-users@googlegroups.com. +For more information see http://git-scm.com. Patches should be sent by email to +the mailing list at tmux-users@googlegroups.com. For documentation on using tmux, see the tmux.1 manpage. It can be viewed from the source tree with: From c0a790453c639847ae71bb344ba2d35fd8a00c7a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:23:40 +0100 Subject: [PATCH 180/703] Add a couple of presentations I wrote a few years ago. One for the ill-fated AsiaBSDCon in 2011 (canceled due to Fukushima) and the other for LinuxTag 11 in Berlin. --- presentations/tmux_asiabsdcon11.odt | Bin 0 -> 35714 bytes presentations/tmux_asiabsdcon11.pdf | Bin 0 -> 112246 bytes presentations/tmux_linuxtag_2011.odp | Bin 0 -> 15220 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 presentations/tmux_asiabsdcon11.odt create mode 100644 presentations/tmux_asiabsdcon11.pdf create mode 100644 presentations/tmux_linuxtag_2011.odp diff --git a/presentations/tmux_asiabsdcon11.odt b/presentations/tmux_asiabsdcon11.odt new file mode 100644 index 0000000000000000000000000000000000000000..ac9f0934e92bc045aef91c2c692924923d55e816 GIT binary patch literal 35714 zcmbq&byQo?mvwM=cPU;x6nA$h6sNemyK8Zm;FKaoi@UdIa47EX?gb|8{AR8B{+;h; z<-Ob^`|PvNeG7@I0t_rJ0DuSpwCgDf8w_xw(gOg1zi)^MU~g@2?&j@eZtUb_XKiZi zX6@*}=HXz*>S*k0?aJ!tWbR<*XzFfn?%>AiX71&t`u~7U6a}5@!2ke%-z*R?Rcli> zcNcS4HV*dxy*U4~`FjVl^GS0^usS(dMyV>xpdk|@L-Nq%WF^%gcgnwSL}!( z0009}Rs19c*#ZDTkQX2*=kAg~1(Z1MK5 ze+Vr|H0aF&{ATg?PY#G$5P2YaKm>qv3~3S46Ql_UYY0sUF9;DxWe79`8v+K&hJ-`> z5Cdd^L7=yPQbIa}6o%}oh#U}>kYtD+5CI?^Lt2E8hG0QhLuf*H zL5M&qL!cqp5HLtKBpl+082+BamcQrY|9`bis}Mp^@Z}`MK6&MsZqNe=rSW2!2sPN% z@Bn~O4M8X_jE#%Gb>yU!C9B1aLjL^w5f2$o7GKUvG$=;l-id`9GCw(je1+O)(IaY6Swd%0KR z81VX_3AL|5IU8YAotp>cSAF>#k4h&)uyOsaK7kfLotdUMKEm zh1J!FA(D#!Ez|N7UfeI0KC9F8*BoNY?)abkWha9R-1k*NtRkEr#sh@VPM+&1O!c&9 zPxgV`CxiJC&3xC!a~baiT(}D!PWabK=XqinbUWKRJG;9bS3PwD*t+*KL3=5kWAse|a+)iK_$iMH2(uepk7M1xrKdzb9~!$U#9{ZjhgG!DSidiQ zCtM@Bv7u;3fK=FuYCILh`)95KJg*pYH5$tr!Z?F!AQgNX{zYlS$&c1J)Err_!B2@a z3$SA#nh5nO!P9nI3VfeAu6wb)7pG9@La06p4@g{%l&nw(a&RLz{5S7S7 z4}$LDeGk&dp`8@E+Z1-{m+b6bUm%f!cxN4qFZn`6xDOP@&; z>uOQDC`y+gc1vMUl+v>M9;F>!-#hmK!$0JaNkBawi66TK%f2FjO`iJI#Qh8?PmGh_ z96thKGXv_Ey5n9B`9T1vtjOHon!2;(BVa*)*9=N7d&dc1u2TI=KI@MG4!KKvnmjJ# z$ILp`A|vgB$EJ*-dJ zvv{tvn+>3}HYPU@68h>btP9P*%AY!>PY>pKT2rJq;JP|&S{_fTdU==c0CP&(S|7MG z!;+f&&htLOug$vFb!Y80NoD>-17l zsLdzVE9;$_ro6Zp2J!qxpA-H(D^}P*$EM}CM~jR&^=}PKV84XqmT3?H4v|Ql#E-Xv z&cJn=KF~uoMMqauh<2HK0mcijIB#nDL>KkLSE-G`Ybp7JT^7?EtGvPGgv7@ z{N&pKo8TXz?Bn&a%N=^DH+h zJ!$FKc@^7blyl~$+CHRI@v`1%lz}o^aBc@~pLrejB0ucC+4HsDrJz2hZ?uNfNN{HV z`r4)r%(!yGUp)_0G=^!PjgQrah1SIvZ@({BUSx zpSNABdTsRfJ@uhuZpilBU!cL$6c^fR)Z8>2i*e%;tJ7Ij%vtVFUy#W;AT zshBBZ$L3fvTJ_WYbEH>YJdSl}`p)$FQDx32A+R4FXoV4`T>C}pGyggKx?Vlt7;dL~f|iUoPz67Hi)bi4C?5%RV(V}l1SjX`-^YS0ihLvNYu%tl7-U)lI>Pa@+ISdgDNi7j9v3nu0bAj zN?C{4xY$QtbFX#`5!e%dl=IkIJBaGtYwZ^z?P@OR;`Lu(*S zT{vo7>bDt6Hq=Mel3;g7iNx6_M7Dn=}-a_R?X(?g2- zRGRnafn#Iqn#emJL3^RnPV)IMmebo7a((NXL3d*XU!iI^Ey(1R7N!jnZ2QvgeDpDz z8s@jl6Wv)165{WCtZD{le!Kop#BuN62!g*6x=zLa14j23?AGWn7_!d%*6o4K|E*l! z|FPWpjgQmW?@)sTitnK9F^p|qf8Kt#k(TxjT~x!B)82!!pi1iI!Fs9Rzh21GrZjf$ zy-E(+TDFTg>!L|1CUA8yHXh4jtEnd-A@kv%DbkWOcAmUShI|0)GUjf0kHUG3ZLDX1 z%3>?2_y3`EQv5}E`G@ihK|x-nn84Q~xDRCT)3^ylZFrM-7ReK?Yk1p^o-BT$&HIdJ?!h@FlFv!Tg2nhR^z#Muo2> zF$-?`^kALfV|U|WV4Z2t2|X5IbrA|&yJ0-7R}uPR-)$*bc02q5bIq+@#jBxI#!qi{ z9qbJCz3`lW2ApCh4SX`ZLt{Rj#j1PwbDZ5hlHXz!YS$i4ri5QN)%?KI#J;>pH$I(5 zU+Apd(4f_Dw&#(3(&pHu_XRa$O5q!m0Hy~}qMZh;wlvO-Ap^b+jRsL7fe|gw>0FSIYJ{JelYUZRVga@9DnA5q}()#`4T~CVxyE>x0OC zZC+q2tKWAgLA&iYGHe07g?d*eUyEUm!Th0QS?YIVX^_7qp$0#vz7I_39|Bcj)b3&r zt>a|@6ZXN?nmJk{U(+>nRPu#wVOj@%dT#$PMsm6@NYX94=L{pbp${fG5q%hZ4wwvbF5#vgo1j3o5z>8r@?7H}J03 zHiErXNJ>);ctId1ps1zdLPk1e5Wh&qk7C^oQlXciucuPnaRR*>TDX zWtyIbf@$R3lX!M$c=Tah6AWZlk)e?LJ(7)a`g^Lw8)D(h+Glq>0uJkXtGD~-p*ZG= zq?TT1V$jwk@=z>o7kOW+b$GilCwL$VVJ5^^hsrcc9B{!41xl*yb0FaAQCJQ-wY z=4EV+T_ZfZxmAi)b$oa96e57sB30ez7$nxGaumL4WF5;U*it(F<1BjcINzZ|;ds>k zc_N%gB_5L>oE<6y&ekF>n)g4rpd>5OB0xqnqnI_dHK`@}4gjC%8 z{2oErts!BrXNmXX&!*sKG8?V;*N0Ad`+eE_82g7us_l?QBCiGi*0;&4MsKW0@Wi)Md~No5{4Ykr!J&vn%?aLB$(pAy z&e}s?vCF(USk=LlYllXF$UL zi22I{kWlsIsG1Oo<#U27n`hFi$ow#gvRP3lTA+;$zzsBHV+kIK!zC3 zwf-9B<^CDMCpI_?zx!dIJWNuFW|7&N4qgvFSY_B&-O5z=SM>k+*2z!rwD%eLGdV}!}?8fgH63Q0i*9Z@>H)7_U;O{C-`6#Y=B7K%O znJP>*Nqo1xJ1Lu8*Mk?aP3Nx(g9+;wyOTIOJ~@D74SUpvR;J~^IZJamn4I>~omWIkkT)*TtYG<9^P zw&ML-K+b>rdqwA+r?A_nguQVE=l#dffCE&0SIfAS$4L(1ECIG(x%(JUUx6-TxgT8i zC1n0sops=P=7~^#QU3YC^Y>6LKlMSY(>@$^2zZbb%k6L4rO3ZD0z?c>cQ4hd73;%` z0?WSK7nCpr5WirV!P)Gl*hIJh8z#Bg(M5qzt9m4Y5W!i8s8`}+W`AgPk^luip{!+b z2coS3rRvaZu=s7=o&*B&A#dl>!Z~!3Xh}S5dBzJlKelYWq5X+xuquUmuF3kT^C!Hmv*?v z+Os=+gMZ`)qd$Mo@TK5Ck*OGmNWasP*I$0k5qYa$c;t6Lm^WIcTt9b?(QqZ$(@B5` zO)R)aSz!AqkAG)>_{aJ%l}KdT{`HrK*32uvgP?qcQvre8YRx=^mV0O_F&HxJPi$|( zZnBA*c@Um(CIRpXe0%%Eiz+e?f<%xBhD}^>$UNx3dp`8aqwfzO__9xQgTuq@GbE&x>$JOcTtXpJy^W+jBLGE%qFy+!D74<{fC@$gJdrDJeH#~EG8KIt z?(E5@_q_UGgnbZxQRh2{{|Cufx;E!W(j<+*)Nx!eIGcxpKs=Rp+j$7&R(&m2f3$;GgH{*ykC-Hu6QNy52J>vTULHvl*wpahmAahyjjLEKths*A+ zpKNs0sA9d6T^#rWu!zFg>iu^m1Jidw2qgo|7a5b@h*pay#pVDeR3nrAeDf!0*4u){ z&7LPJCk5ZQTooxZ75wr+E2`uG;sMxix!b>}?^zlMms3>0+pstx^a4;p_NGT*3u?e( zrt%5omo!n%Nhm?&ExbC-(Fa!XhqD|;WxS`x!T4iI+Y>451}KWE4T1F{dTw@eWFy3x z!SDv5&B4NUIwQOO#!zq0H=-VVgh`Ph+Ey=xFjNC2{CEK+0b32JOHTlfT6gzXawt3; zsD^2BuIWbD3oJ}sC%K+x$FtxzO{jxx04mxX0yDYdRcYEq*VDy^u`mJH)2T3JnE~i7 zVw6pyAIPvGcQs21riu4?W&t3%3DYX1rhTXXK*!U}o{7ArPV9v*n>AS7c%~;E~gtSK*uV z*Mu>t6*gRfU*ff{*L!*fx&2>DMSUniiKp=2plriZrTl(YZZXsPs%#_G(J?okLUZ`C zHwpwZhmE~HZBIxeUcn4Y4^y3l{I-KfwcEA}7Mt6W{|yKF2#V)4P1<^(xI`5%r0w@A$zM@HK0@D1NzzYEF6L{qj2010^CUbZuXDR+wSGi52N6%6?}9<{G5bu-k9FDFtCa zi4k(K1z(Xi1u|V>%uq2y5fVyV65z|~-fgcXi0Q&8_EdeND$rVpJPD=uR~7qKq!iaz zSSvB7G#B%ptfVGa<%DiIt~J0HGbRRU*){ zk7$LkL#6%{hyAtv69bwf%76&CU(XT0UwApO0P=I0FW9vk3=b4nn<)7G5=6@+r)~7^ z@%!{9Lo#yesWoM{t4+H~vI9^aZN54sLB> zNvP^2I=RxG4*3m~c}jN?7_R7YV|4OU;$CH3fR#~C3B745$I9$AJyZmVYzV(t#tmH1 z$~LchAa>D%`4|l5_CSU3x6ZhNF{b2qAqxpkDX_D6g*$LZNJ%Zd4Z%{Nf+DyvhM!l) zDf>S#c{S5gh{aaq0P(*MjVvD2zALbNW~bG^fBPc#@bxtEZW2|ojCNrgrNP%JMaj@Z z&!4^fb7Iuj;87yPA6dnHX7-nTexR~|t=P#6Q3L_{`>QyI+0HROoo*?g6-#6xe;k>fiZS_~8kTi2N*z|o9h`@K%IS_7)8OVeD49Pk$C%?h{vf#iH#X zQTZjNVTE2bRj^E964!12dExsv6ywNO*Rm40t&8^i^CK*U$x94TWGn}n6StaltZkw` ze=%Q0n&O|r1q5u;Y=*z#m8EkhC)IL&e&oJEH-=&@0Xesb0)Nbiy+NN5L3`Heb{}-u zU95Y+241IgB2%x&WL&g!vtyCTTCe+iS<1ezZR!F>i}NElRpqmxK974kQSf+t1;B;v z*t@(Nl8%BgIeXsuf#_e;h1YYc0Gk|i3H#Okm*6j#chaHQ3}z>xiZGvvm#c3yei%=T ztD5+(D@`FD(U&i_u_Pw!Xi&~q?YMqVPF(GjJ?lzS2@i|TWESN5wDN&G9Grt>uMtGP zNKvpt1Z4}Hg|_aZRo^Zr{zQwnhHBS#rQbVj3Z&6!rg+g_NA%o6k0_So zf)*2e>iu+O4KeZ_HC?6L)#ZjB2H3oh1S5vtLui%yNgDcNW?wkD4rnE}<;O)p_M=`h zD&lLQQuhHN@jVDiQO`w`u}*qnq__(PWlf}g?XJ-4dUQ5Ad{R?RvQ_{dmE?P`Eknvy zK-?(n%P@^Wl`Fb#V>4C5bAz?)YG;G%7l}Qdckl)tixLAkFr(0WS0Oj;FlGH{dfICu z7${YwgBtix=54XD^d5u?ilaapuZwzw?6L1EUA>d}e%Ku@9LkCy-fp)5`<=>tGqbC# z7ZcxT6nma(Vl=6&-UO!5+JTl% zxlylO*2Q(A-W(7(LYLkc7Jp=VA?j}iGjd|h?CC#fH&`pFOT1jBB`|zgiZH)hzA|(l zIxBcj>$=k6Y&B6c$LRV>Yl=N4Nzxf|Weq(rGT{E&+Y{WKA+ayYKK<4d_h#`&)-qgA zn>Fe|+54vg=|0x_;mw@Hw?0i@qw@9Rc^=IQAn_^vJ*T#r_a`273Ow?*gcvC2REOr7 zv&*-qEW8O7=*|?K+V)7ix6oK~N}FkhCELgHATw^Tf{t+|4?k(zh=+0b4|TrMY;rni^4F520U4c{2}dqX zWoHvZyMKftb`NGW3;-0gwfa>iC%`glci41lz{Oyt!Fq|GeFSO zbV~m*o}l=d6xC5FOAi#eIDpMbnPyG^=y%P!`^xj9iSYxd!-rj_o6askgEF~_d$)mT z{6Uy=t$Sr$<)!4*66qu8{+4v<+uP3Y7@sdQt`k*dkQ?Sy(h68Rn*FD%7|~Km4!H&? zv@tDD9KR4Pk{N<4e-SE4wn4h|78Xj;_w7OgzvwQ*yC4-QOj4)L1|~6$;>*Fx=aP{~ zHqI`Rx!zHEAzruWJ@Zw^b_;#+d-;Ttn@`o4)NV&!SD54Q?B)ln?~y+*I+1@($w7Ck zPx?Z7H5lyF)V!9%`n0#9MCYc)Pu?9A1z;wns=JbiHwij}aiQujxX7pF|GN9#8aX2H z{8EjkbFVYLxUWF|Av6_t_#w11^p#STYRqBuh}I)#R_aBweRbUfQzw#4vZHfZD8-YT zuPEUP+uZ><+*gM!$@=U!^yrk%aPyrGfOZ!-gb$OApluRBTi ze2dxF*F(MHZXzYx8VBX_Bz)*2Z^jQ#){+L}h_5l6xJ}6JqW!sb-jxn;3js!%e1((e zH#JM7rikR|?;kdZXUAkqO2&W@Lq;K z^5>ql1B&?(!{S1)a{%S>(ha;91}~ z1GTSNefM{k0r|ye`q{U^!oRI4hCeyR@EzkGpKz@&3UjE3PE|?8;&bT!)NlX7c-m3L zY+~bbV8v1ND;6J^pam}5==bDoogA1`1TCLZy~F4p4Vjdcz&ONq!o5Nh=~z9DvYB%A zI_1LTYzN8beA=xu?LFKGrb;QUx=KncOx371*}CbQ%;&XssIYNeb}LBB&4lLG(N>=M zSj-l+&Xg}HrR9(AH^ccV?x|Gd4(~O8DI8AfqG#UN0#kGmdpJ2lJ5+olnrR~ro4QWb zIVW&!cw@lnNcUagppI!bz-c~JU1~_yIPIc#>9b|q(dqEJCfwwg;l^uZQIh zZQ8B$%CMh}ENJLc4F!@hI)nz!iYy3dqvLNzp-g|M1sBLR-fw)lta89`EuVz~F+9M- z<+?J-4hvMj2QmU$>HPG=W6bF~<-_bM^ix1^=p0vt=6*&OyxmaHKlLlFZdduZ>uf2% zbTj1sQd1=BNXk?4(Q$_6x?}=c$wK{{S^^}22ZyX%)P4YKCk3mPY6c0yBs@pLB4BOob zl&-~%Q;t-Ud{m)B5wi^Bl7Jt6WWcX?x33zT-}4P5N#G)DW9s zj>bJV^wnhMJ}(q;hWcG<`6T=OvNa`##9oO`l3x6R<=lcj3yyB@(`Te6B`%W3CMFbVGQT z+lCs6L1kVZaRXbI$bF^g=p2xhyZI$*Oe>jJ@tWP;4N9^k{;a@tp48pX-!%t*;)<6Nvf~R zDyrMU*0>5+=+KlGrixM4P}HtHJg&NGoh`1lMMx8@xcItcEk#=f`*dVXcldEq&e9V{ z(1GDaED|ee7G9S02~XPXw2?xK-=K2mt)}6^?IQT0{05J?4OjovTgC5Cz z=erws>S(C`qoQ#PbotH3rL%ak8>iQn1RN-yw?h%#cOqj~hWv2~OD(|qv1QlqM$-K{ z8Ci1<9cMonSUjWdWlyOWkv)X=m|i(@UaidCo2-gO$Ce=QnxLglT%bDyfJsyueOD3X z+?l4K&$3cDy`di&#K+^E1aPtU4}@R zGgh>`OYpv4QX)gRcQIREc%Y%ZKf~CYbDc{B_0o8r8%5G_XSrlg!wWMub>Mg!DEY~m zWLji~+&UO1IfXJIgmdlQV!v3t3Ri3@6ZmZM+`zS@1N2|4YbA zq&MHhwh{Z1B-9pdLP)PsrmJ9fC`_srJeC=i$f+Xr>Px9RpJc=Hi#>tU-litIPpOuK z3zAx(%JWcz9>1vpBaNG05h>5ca~tNz=cI$8p`H~MPYgabiy=nAq19%oxgVRj7(9Y1Sy&ta6xZb9XZT)RKakpH%W5y_{i)E%+zH*v) zFhhtrn-^wR4E8yNm5kBr?DYh*PgQr)5uz1(5`PAcP#47|@8We;3^0%T-#lGrC3t3` zb55&$ZC1yFeQl1om+svps&ZG<`}M4jx{)Q_9a((U(ax=&RDpUELlB z{EOAkwvp0H&Vi>JUV8KMR;}oP&F;=>AiP-vpc@aYO|XcU_bi(>jTnh;T#eE z#%6+7LLF*D(dS8YHm}d|o6J_%ybil}Om<I?2|fIs9-YCuhI!oefftcUpayVUjBX`h|t zwYQek8yS+1c5fWjyHe+lGY*g=@wK=37~jb^isVrGg44gtWFv`hGOC{b=2#`%;b@vz zFYGoSePCLLAFV2#q;h${XP}rLMjqG41+gkz4Y{eS|MFU}(XUo$`xq2<*10&tXgx0N zp}!$2k_qkRr83KXcG80tMDPu%$EK$=w(h}})aW*bllJ}9mq++E{Cs;!dkOML7<=%K z0fAL5yIO}sTWm(qF zDA6>kwAt{%rWG^ev{8d%)AWrrQS^9Kv$Hb;=7*IZjWC;Ia$HF8&>tP7k5Z4Dc*=pl zcV%W;^O&AT+^S3cS?ev+j=$$PG58m#%k#SFrJLJq7KDf5qpOJVrlNZS_4>tntsR?J zM-d~(RtPmm?dDad?G5MX><=k^I){6;iId(7_+IDd@RVKdum`(7;8(8`1b(r4Do*H_ z6g|MxLDT6CO8qd@3TS%?ZX#}&E2xTi-$49StfJtF963(DY1HTg(^)*(Cx#L!Lq|Fr z{K%tc8L`jLP=bz$d+nDO;D;BwNK?Uy$*Z!?v9Kj$*XCaH) z!Hk8eV^Ry&D5=A=nUEB#c>V&M?T~iEqx8yl?wv+i`RVr}%Ywq6?P?C2bLI&$G>X^5 z_>2)HXYf4xe#K%s9{eO1Q!VYRTbI(&r!D>IWjZ0?)#UgP4D{sX;2zaaw_#AiBlg=S~=9z8mTu?*XveVv+jk(}tfXMo+HUE@XmShi1WOy4!w+ za$LOm^crm`KX_SI78Jz$xgmBZh@4N;bN~t$w{yx2JIJ66#fBFjCm)}vB${k9|H5oH zdSYSt(?T`BkLRfoD=M2oe6Te;wWO5pkX=*UX%7s#meug=bP&2gG>kZjX zS)W(PrW-KvgK6S>bRL}r&QtbGQlMo>t2bQbEd~RYmwkaDx?S%$^yU(0ZSM#MweLxb za)WTZ3&*erGTXX|J3y7PKMpazYL}kr)_XvaW>Y@M&9!hYH&mQp-V5geMA5Gf{yxSR z6(!u~6%I7+l2+3Ws$I~J0%))>(aV_+pXzoDu>!Q90>9Eta&kJe)^Ov)n)#>heCVgx zb6OyBuM7xus_)cXr+0t)VLb!2_=Byh$n#nnQ>3rY|J>_GU8caw_6g6rp!BhZk?^9MXJf*H+v?VeYU*>{zc{qC(Kb)T^6Gv5f&xjd6ZE-6&`WtH03chmH z$)|2l;V55vT(oT*h6U}7%Cv8}{4+u`)hN&(h{tb9CaWHr!{AzxBx?6a7xCZ!%0HMT z_-U&^PebJG7>Iot-FP>u$%$r#h+?V2ZrqdwkG$vD^?W$k{jz&HZ%vLsOsNpoICdF& ziSdCkT1E0H>*=Q;G7TnH=AIS0qzKk~uQQmRQ}}Twp^W~jOI+7Fn#)iez@zerSy_g7 z{p6-X#RG{O)b~8Y$i(HmL5GSuU#QlD9xyWzzDQyq@|wLQei)?3P{$170DNX^{&rQX ziBbNUiANmEl((?l0^=@`kZ@r}@%BdHTx;*tDe6y*J6aie|EFks_Rs8^=rpYo$5K)I zzhGlD{qA3LH{X%Go$xV84RIPZgeX5mT=6gz$>F7|ipK6&eCKRgD5&RPO_V#~lxHBem$3+=I;-yd(D(MMZR>hPi>)Xhhj95jPMkj zTXj=WKOHUKkLACKsjLOuC!}9;;{~0+jv`dUZ-y<}quf0ACWzsO>kcTU^X&wB6?<<7-yv* z?F)rzY*bTps`wR`>gRZ=N%+=m_Qstayfs3@qCp6GYiQ)KV2Yoj{7VOoeTo}`i$&ZC z(_}?GsGBkN??3TJ6;&vSI~i7wi+V2zmUh0L!uJIo_t4Z+xN5iL@E^1B4U0fqcy})9 zr;>c~LFl+e@>SGFZknxYp>ApHwdcIjPcU_?f{NC$Co0Z61Tv1X%Ld z@vo@oG+8hXo}~KPxMU$vb(GBf!aD%w>1ZtZ%rxLH&~a!6YK6TOK0eE30CbiBJydF=Eiiv48?N(G$5ccP@D82IV=z^o=R7M3*hl=x>BD`>Sc zISB{TOD4H&rFY1C@o1q?|lwQ zZBkqftkqDKU(Ss3H9KHN@A0`jKt^B^H=ao=v;m$GOb~LN3Irl8uUYRBabDLt6Vb)z zA>G5PN21joWqqUdHr8*zAENFLr>p=znxItxwEM`izuK;O8c?}RKzyc*@_bQ1{n3-~>bCsOg0sC@tvcQaIij6c_lPhr+`=;#eL||y~ndu}@-lZTh!m%5W>(9nu z$&z{e7MnXve?V@As_eSeXT==xu$9@boQ8Qn6hTD((kw2jK;v-w#&DH=SRXfthJO$*m6l){JWVc8Aq|}&Pfp;tFb1joCFeV_nrGzv96-pc@XHgIm6j#z| zzMXo6?UHQzb>3_u1><9A(NJwvsdt8AvWf18sm+g42Ty}*sEVV+H22r4ET`SX3$~5z z2qu&X?ZlZo-6B^GMsF95$24=s?d|TPqc&*I`)DYspWNeHR8rNo$hw=4S^c~9Eq(|# z9rR8r9ydA>B{;CQrrJ0w_EiM5&Kl{df>=h6&co#!d6COqzV{**9C@}O6l~$kh-t9$ zE#QCBqv(MytM#h3&%WF2_O>cfpY*vbnBxjJk{Y zt%4Tqr?AXdu#CB9bGaIIxcM(OWlF=eX`6Yc!8<={LKNxw{BOq`;i?$&1Og}fQ|s%h zU8`%F@`W8OCAz>1z{~u+ zBqd2HD+Ov90nS`qf;SNv2djcs)|0V|p(D#dXu5kaq1ggo%6=aFG9Cz|IdF77Mfd9#SXp4A z2qk%}&<$})h^b!J(zA2oZ7}$aZ|lj;lBI96ZeTmf!DBzo)i`G1Tx)RlxVdHW0-mw6 zwQh5AuJu5GEjMR{414W%?5ycSqlV{cqB6_pTDQdc@1bL6Jh>q>4y_%RLJRAAHOxAp z#A+!^y?@Ty#_~FM^cVy$N1RRFbPCX4 zbQX6gFq!70IH9!iry-wno0(x&JEfg7fJ1AX*$F>xfcHbhgE_G%-Ja{ko8|0Oi;+v@ zNI$04m|j zHxhStRn)>$iW^y7$|t-wOAebtPVDPPpF^(LlF65@%xcr3B8;(X7+SqD%_P#&+v`Ec ztbS-Og-AmR;$8ca8(u`0jwRvVhA(#3joE2*z>Pkb*WvlO!b!3GuPoWe3Urm zkTN(y;m{|lAk#%e)R-NHxgGV6mQ?7b2EHf}LhATqlOAJD!p_87FeASo_Qu$lD45TQ zE43Jn+Sg7sGYe6prIE|+A|fUEQC~E7qv~sBEut?ixCG@G!8I0ukDGvcdP@0Y-p_Eo z|M`x>zw0On3an83vt^1mbr)NF-!SJq6vk-ys6bY$?l6jxIz9+iw#1Dk@;q&*#Zo@N zC=%jkTh>AkaPbaK)^j zE#sbxVg}XkhjCN1&J1hvD|z;J#=Z+p&b5DNdak29A26_^=dN7aWMSrDGALs%neTk= zdKqHWYPUw=>+Brj`*h;Y<8qfHvX?Yt4|=Iu+LRg^rf)wRZAf#cwiL_Sg&W~deDZ3g zD1EB)_>@*Nku9OoK6mJL_f?^h7v0=9{*R5Q&a>jdQwrABEsgqC9PYFCq$W3c%go{V z=8u!*WQJv=k9-u>a1y9@*lJx;NL}lY{rTZ`nfHJGW2I9Pf?8@$|R~} z37sGL+}qIR0=~qDm*{uQ;B)1MW*+ARwSMHQ(ge&=f!&pR^%vX8vQ#g4*+|Jj zzbE&z$)j}XNZY_L6=}lxk*kf!k`w*+50fD_#qrho(|Kk#@M!`cFy)A)HkD2^7;p*_ zy9+UK7opAq4Qh)b=w}LIwY>GS!DHCiv=`n^~of-3apt&fmZHo{I5C1uF@U>deT3oL5^=g7pJ&+qUvnx^9JT2do z@lzXAlBeT$Dh595Eu;S6tzG$&9YoZL`z%58k!qJuO{qv0#XobbS@YdSxXKSt*pbj8 zKXCZ*UhMHJ+>-{gzHG0AS*BjjE$);p-^|d$Dm@c~&&7TGbO%SHbY>QOCVr#99m}q4 z1yhi*I!ZqMbKnyYABgZg%2AvtFF>#2(6Ln;oR!7(^S5LIHo@=~U4=X&!6$#c%JX>e;GtVe>A1-sGl|w(PP;SI) z*pu+ckR}@gvuDUEj$hg8wGO?FlwE7*K;Tj)b(mxfZuI~H9LBk{ygdQ=mzTU`2a2?> z5uE1>O!l6vJf6b++@-sW+6~652dTpb>BNc)F(QpI~Tlx;s$WX`A#RkAZt#d5@jo9#y- zCX*^FGD4B31o5Uv6E&Qofsy6cF2ejyH`UK+_X;>y@o|1P!WTMSjK~6m_ew5cEgW|klYw7vC3PnQn zQb7H=-C(h2W)n-s67TSYCQR{g1fuByIXJ#E&+l|fZ!I~?K@;Q@9y5a z?|uGX^9;pwSN*!WXAVmx`^_C2$~lu_PUk$|Kdesbnjt zosrcma*S;znc2yr%#7bVP+Ej06d7T1!ek|CeN*$D^t3D?NB&6-J6ourXWVF7zhZEH z8}COXFC2g4E_jy5>`S73k1d|^0n{{6X$DE6FATHRs0rXbhy3#8rF={t&y~v6e?d{U zj$m(3Vcx0doZaMX-LKbT0^s*oArzpIbe?a8fPLwFA4o-87`j6Zu$yzVma4vw)e z#e0Gx;H@smb>-B&xFD7#>tPB7L-xeh@@me(yhTcQx7~gWGq@)pQ`9168*+~y+ozi# zw~TT1ryB)dFe8i2VGD9ITX>7SwrbPG2d|bXl;<3ei?jsEq_K4%g!CFdRBBFD>QR)w zbA&hjaaiv8_^CW@G^&WT{!6Z817nKnA;fJ>=HS{O%xR|64G9s=^?PM#mn%wO1S4}z zvW&@GV^BlKOU$jp-+<{3!UDk4Q_}kr|L`kG(-O4Hn@Rlr=o7 z!lZ);O_{}X+m#>dbJquJYrtDb2iMKe<(6_>}C5Ky*itcu7kS}s}2^>_0Fh!`lcM8Q-aDMB@ z_y*-y%a$F*VNS#bC?hmNxD6vS!FC@?hy?cf_`{F&^QwmRPle$`Cf}yH5Z%f8#t$K& z;*jiZ^o%=fRH|x^{#KB0F2i?(P zG7d*?Uu2}!?;jCS+`o@<@2WEx!HUwkE7})fN$hYk`qcRVJXR%d1DWi{ylfp2cvXi8 z*xt9(2oXaPK3koX&7Kr1?tvn10;`#XOV^IJf%caS>`MpspLUjTx6U zqd!ERAHYyv-ifjK-tn2|06Co_MY`8kju8H+)t7urRBspjct7^aqIq4n(ot?=t^TzYCghxhgQBNo44^+HYo z68|#z)VsS2rQRWY{yS=-d(MW%Y`fvC_*t65a%y!2$}0R`i;`9xE*kPIzMR{ovN85O ze|d@@Yzv+dKOg7KXX`wQKhU@6Koo&O=1Jf6@YmQ*y>Mnx1w;H0={!K%7L2-24aQqzLKQi(;b}(&EErCVH~T zuDE*8Nm5Z41hdZ+Faqf@m=2N?H*lr74!>hB?WceVB~IL%o+vCtfF@A!r$JNyZZEPwfxZl817&uGt?KvWBa~-_4%79blRL5Hc69?nKAN$iQ((M*xV7>v*C+m zQ!8VNdcrrG2tNsWw%uHMAe-H7CR z@6Y_T3t=a!|0Gx@NGEk;k@qc)woUUHlR#InGRB z-XHP($wyua0y4Y?453F12uPO`5H9Oic&>j7`~w3B{9|R{ZtLtsV`N}tZenDtZ%lq> zs;>{ZVXUto2f^Tmfll1$8;KPuub`D$iWkceg*X}p=5S@eSi~O;hwz3C0a@=_MZyR` zk^>ySLxDj8wF2Ubt@Cg*Tmov5L4bhX`m#2#u`o4pbfR@IHZ6*)vk9O_7IE$Bb1;4K z*9ZqQHcxD|?Ct>*lJ9}PPm6!@(g~l*hf|rGzL{&i9lqb>ygHs>Kb%74M*K7xSylVf zO6zQd?HrUZQQ_yIC=Z?-bs1U3)h%IgUI(mBKJnOz7pL9EiN8Dk-#0?si9rP{ZSjbgk9M$kO{ zYVI0}$xfawj$t;pDV5HktKhQ;ogD61WM1a69-#NmyzVRs=eEeMX0LZ?iC!rKoEwtv z8CaZ(4o%V;%OqcQny$wUo_{cUK>YU$@fPpQ(aGJ)#PL@ECY+B61%v0GyupeRF9}q{ zK?=cGbxWQ?x0JKKmSrZ?lp#hEdv-uiSuS+*UV;Q8Ugdka>2E!5G!%a_ePCIW`MTBh zbR_fv>$ZiwfVxvleeAlg$09aJ!J|d%>2dE=#`b)n=H<+!d0h;+3$|e+aifuO?2uuw zNPx3shX&$1jjV`AMjj)jty1E~MKiQb19pjXi5A9VvdnEvBRwwYxAWLM#kefOg*ufN}>EYg4n@c`#@d!#md~FCokRC zNk%F-s)c>>P?#ujrPFNg5M@|M3R=*7X$T?ACj}^u$#7~G!~I>a zz^^{XcX;YAFX03>7dNSt5O5at%B&94-jiKaY(Id}PXfe7;aZ;j&#R#a@bP;P5 ztLp7+(Kfv$ys5j&*PeKZXuV@Vtb*A?$7;H^#~LhH?`$c*v=4+FqpJ+UD`1Mw_u)s> z4NB$To!gv~C7SK%5fgu}Rqri$5r%xZNimL*wF?Sn!RYd7-_%nyd&}dN1ll6rp_%J2PpKSZGxNvS&k1pu~J84I% z_yz9O|HYA!7cl}SF*J}#5Oc>!V{@B?Sz_Kj2&_A^cy7uA>4bai;ZQ1_8{yQyFVu}9 z;Q2@3t4`zdg6`{rj-5Dql6xfX1Z}WFfJnW_3`k^naa17h2cfOVcPD(+OL)M9-|K*n zmvUrt$gAu$MNtqFnsj{#oDV~CulUVs#MVtj#F4;2^q^Q{LPI5X&moriF&aHEjP?5u z9n?gfaHr?T&@c)GDJC=*#+ppP!p!OFCE2I1(c|Y4WIpN$_bOWuy;t7Q%z(CzrPe~z zfD=zHh^T}WpBF{b(2A|BqOG^#ueWiEo5Q2Btk>2tcE{JqrmgpkrPf2z$Yq<`k>2zz zr6J@cptha?5sE3Ix>DEQuX75*eGkKvTs>vvmp3$n;z3P+rQRkO0~U-crM6$!Gi7uD z&4Y^WL9LSq;iT2pkqjG*s|9H2oiaLt=3%WAvX*=&6jMcYwFtm+4#LHP;b{h71>_CQ zqj*@+Uo8T#{vndXaEhy6;s={@0r@#hvtE^(pPG>zi% zuUzOJ*Z@_uL)+#z!C`o?0DoV>wPk|tV>z{gPciK~z~t&p?VxhgXPrA0G2y08y0y(< zz)PEP>l{QvcqquFU|sWv>>OOE+p_&W+j(QeC)N6%2)^ah9RfMxfPHWP($T- zCO~(3baBDEzvHE&`yHaQqh@UEF*oJb+1(xQ>Y|I5?leI6`@+tS*VNc!TSo`Hhr3=% z$}K?XBJ2#pR4O>JROMLrm6qtjeNk3({`C93-PjLH~@ltvq+hW>=O z+K4&mju)1U#X9)t18aJG7i{9CHFJ?C)^LF>v-=h7_B2~M_XGIFK9&rw7pVJH6lqMC zANPZ3g4jAgu2;cGL3^xBYr55Fkk#m%u@bGZQZ;FY13`uZ%TgkQ2~>75HSj9vR1+A` zPoVu}AfThyw$WGpYdy7dyvpYz%IAQj*UvOhrv>b+1@CZbrI*+gl5(-(^}ZlOUQv>M za{6(jzVW>FBS%3#3tGFnvgv6k`Nd`a<#zt_7&p(XPQ_4WUD9?U&39GYgMLu}1zb`irk)^GXN zVxOL==)ac?5%;Pff+^z~p5dl3n-3xYC-0~S%h_VgGh5lR20}jQ&3G1`CCAoN)_+}u zQCO-=KOMH+;G_i8TNS0Tlb>7EP|23 zmb*8E4UhMM?>!jai`AWP--c&Ceya8adAt7{1CbZMMwrKY`%%v~T4eGE<@NZ3y14&A zSO27ff7X}&NlCo=Joz_`c&Cjv=G*m+cr`|PSloMuJnQ5AK3)6AU@V~|CY9cvm5f6G zDBA5GlK*H5l>zy>Te*cKPc49VMD3_D@gMkoPq3uA)bvqX=^ikxIL7 zbSvf$ij?pN6-q|hHQLZ7-4o(T;Q(~ED#o{~_YWawoXK3vDrXlv?38@k(%Bu9n}7}t$+n8^XFixs~GR(7<85gUl? zy$$Vg($6?o2~Dh7gNUE2}s3C*2JNU-`vThsk6|^$mNnuFl)kILkf&F$yYNnjAilcue^+n zQQSE?#^y-wyq|{or!r*514c70%WUAdjmBG3Wg5}lu2TVem@}M-GwvC(P8)8>m;m(2 zALv0E&x}|Sj_^av;6S?!L+we)e(1OiZ1wg3zFqLWd#jxPk=*2zM*-aGU zz@y8`hgpxYd>ri~nP=UItx$(pNekl=UBKrPg%1NUt`S0h7e>g`mfGhl;}O(DA}E`R zwf4dWF02jDU0;$U^sF>FCsuVf3P395719_GBQ0k z=3>lQJ0y7?EYdCkvIkuoyUK=y!HJehGPJsi72!y_=#P;~%A(yLBx$&6)U_gK`k`>d zkz_T5VzXw-@psQ|Z82}`RW|#cA|n#xCLt1A3XRfBBjqJUb?H946Xkf^VLlwy1&6v@ z_I$>p6N6!V@Lm>@sbVw)vnH zrH;G5p18@rKL15GyLDObx?V4;;s{!q~ry- zVG%WCiB1Mh=2pn;i{(}3FaNg~Emk7soQ%$LUpR`}Kc54-7NTalgQ$}_? z&iN#B9(l%aA3pWa#s=3*mrb`hL?4&!mYyP_#MubMm^qDI9HhCN8NbvPNG-DzDB5MD z>Ls7J?(12&Wb~q2ZoHm^QTL8vZXO(3_lahUtTY;)fu)d)p{nRporP_y^*4)`MMwTp zGOOzBKoqv8h-7Inmbb-F_vn4m4?L4`@}|I!8_+RC8o0ykVogX6FeQ*ecOC5i21GpjmOE4 z7O(D>-f5V6G7?zo5?B+G%iMYr*~g-K^@2PDJY9z%CwNaM^LwsfoO}(H`JN5Vim4^< zIg~fG78je-qpTk#2XCk=JmTg$(z#mvSeyzpgxJ0=#C~cDUQIDK&%GRD+3v%1VEH)w ztRsY5Ng&q{+_BEG-H&+zsCv~Ay3*6~Ri5p0_S&d4cR_tfKt*m`ynkgR->6aUDn9u?YXxWBV*ecdAdER+B4%vJD z_bBQOka^6JlHvtR&sy<#BiIY}^oF*fX~$<`SWPK2K? zA(@2~X08)*nb}RjxFyi`pbNIDWc)g~5o_zNyIN+t%b8->Cx_>GOIEg<+?H0>@NDn` z*YjRq-Rin^+NnQTU$<9EiFbyTH%n!d9!;iEdWc7S5J7b*;NKh~ryt5&n$p(Ag(+Ys zjUrzvGuOt2n1jG8+YigGe*%%8%3W)-MohJAyon^i6FBzO77Pg{SluIsSpu%So>_(t zJ9FeYK0aKA`%p0cgPi%$r{%hjG!^bhcx#5)`$ue$|1jeRzeytBdI$kytDg;o0>Uv- zP}2oK`4yHTjN+;F68?l-X<Cl#p*ftJqlE2xTA|JYR;<%|1>y-{TiSyRR}za) ztu7D!c`yg!ZQvcPg=?;)7Ar^aA&kOt%n*R{PFK+XzXpzzGk-C+qg>uISvKH`%! z!ic|`SLzSj5tJpAD7qVtjrh& zR1ZcTUPn`kwxG9ey}rx1rjF{6)Xot{y`0K8)vYLda4Bi+|Kf?xo1sW6$;xgnx8Q~c z4SisO+hw@Dwf-T%rM;h2dZYf!LN}>parChphu%l3G^Mh0hg`&nnU`+$L^=&!o*QP1 zq#AIZMxao)x$6~0grn5Zwl#`x)b2NPN?YUvb*|`I(QMF3&W>5|v%asuZ*Sf(YBhh7 zd;kLKp8D&}+aD4A8OyZRao+&}y&b@c zHU`!vj+{&R-q$%j+2hg)zy{Om5J8Y!HkZ9gM)+a zS1aM4T^SqwhtJO0!Ri;6u@Rk#mC0M~0YF9pS_Zn`yl)!*zbf^{`*)RWZEgS4(c2jQ z7~-$)0O<*s>HawX-IJMvv9Z;E`2RKl0521r8J)3#lL3v3g^4Q>A^xB9__qmXB*gy( z!0F^*VB=_N>tOvWzrug;08UrPLe0vIkMotvYT6P>NGDI?$&DlOoI zkoWhsdK(lk_phNDIhgzcr2(j7!plg{Ku^QKNW)04OwY*4z`;q+%uV+%eB8!HoWBJa zXy_RMGRh1LoQxctEcD!TzZ?JP!Nl0Y32@8M7&|-s>WNpLL0FlILxzc2=}+wc#qm!; zBX=Xf+fH64R>uFq|7XJ=+-4>=CV&oX9eCvcb0PQZru;P{OtehIDmE5w`2VcevvaVu zG%<3bV`8GWW?&~4bhfZErr}^=Vg0k0zX0Dx>tx|%Wx}g$Zi4S*?d*o{Wa40LVPjy0 zZ|!X5WMOAz;%4ITYv6zI{h`u-J`#>jfRQ^oSs3B}noB1GLn{*+BU@)1Cmuq;r|MUw zg|&g%|Ejb#d{gm%vfBNPlkHD&I|By;GY11Z^WV%2OiX_^x!O7y|E^_XX8c`kWDXE9 z0u1DL8zVg%11ljNFZZ7}G3T3Jf1UkjG{36<`wQx~Tm#DH&en!D1{PM1bWXqje9PI9 zm8znQ3^#kCP}trsWC%F|jF<=qB_QdbuOFJnC3b*~Stu+R8;c$bXHbY$XtaNaBoy*R z`&9GxoY(V+i*Aqm_}O9J{VofqqNG4na9BJvC`cqfue$QJ=JiXThkwxP)jIOs?ne+* zRIk|Ji;UYBcQI6mvk8}(%aV3@RbZoO!ELzFIuV=bhW#aP;>nk5YzVXVECcnaa6-BjBf?-g%}3QSWpPV)|( zN{(_p!`pX1x7t3NhW8-2ff9^Oq^*60!w`T!PR^#Ro4Vy`sD`?9>w^!jPGbC;N5HaI zRWKeewHop@?Gvp56(kzGyV_-}*A)ocrHeV4e<#vv)`QiIe3;zIs7 zq?Xsxwu6sb0;JLY*YuxQ5hf5u`J2(7PiQ_M-jV^{Ya)N(8YzN|JEjQAc$K#D7!ZSQ z+r3mP_u#|gDh$9Rg^hN%;H%7NfnBf2Yn>4K9@Bt3?6pybZJSFyTrwJVtnn1ytCm60 zz@*FO>+tfzvh;;co!15|Gz`Zo1x{Js4QQ9MF!q!c8f=GDg=bB-)ng>3sK!)KYy9?>#g> zlY|_gpX%6q#q&w67ju=3AZ}^9@Q$&dEQC{SWXg2XtZA_<5~UGL2Ys7cdxfV>ZFx5| zIz6+I9IDE%3*T{bqF>3`Jh9)!YAQ&@h(|i@CRlu-KaIV5PSODrE!OV-)r9NbI^o^G z3IZ@$SL#NiG!CrW#|vJ{YNmeb%q!)W!5yzL5RMk9X{d49 zLNcGG!DV(lS8EDkPTHjV_|OE=Zc7l`1$f}wT1h}e8Fo6%1$*nV5v{)H!ccx3w^LaI zwjRm4oa8Mgh^DOh9&d&tu6Xf^r#({+(Ma_w7oD6xd|GDNAL&_RitPh_Z*0w<&D8$25t~ zyC&7P$5@U+2Jw46>n(>F(?IB%IO;py-rd?>}?*)8!!Am14#JNE)dwZ$vK z+IZL+oxQAhYRi1tO-QiQd4IntaV}7<9m{}uNwLFm8jME}0lYk;1l5gd1+P4z#-smo_0?-mq(vex^ooCFjEW>$tW$;z~4nkxm zk~q4`4qtc<+ zXrDLMbEH8#C!mDp2k}4q9Zw-z2nJehz3b0TF*i{xgrpyO)~JNgqX3A7?q`VTVI|==K#CF`|80OT;!=FzU#Z6MRUfE&!XwT^(&? z;TzaN+~&f5{NQ5D9JQ?wZYpQ>V>~H*r8dH| z*tl%o%w{0mG>hcP97W3Zf{iE*HmLj|v8qms?_a2W&%A4=!b600-yQF8CQ6*cY?FDi za~WVe-lM+AUNIsg-I)j~42JJJa4HGRrL1{o0&mltgFaoC^UTdo=HBYWG@La` zU!><(hCtq*Lt%M)uR+vin0!?b&^4GA)LN>UJ^(8kR8kLeiWc!ugOX=TZ^b1M)DCFG z)vh18z1knrl_a4|($lqyXGeyY#}>{~hA~(jI z;2T8AkzXNm1di)Nixb!P$Ba6l*z{Ol%5#B*BjVaH!YQtvCSrFyjaEHI^fl7LkGOaBqZ@m9gdcIVCv`aV?Izs@O$Ww{F&}QQu)NSO2EH>#lk)wMQ`_I&HkMt5z%91O%Q0u4P#!qviG>d70*fCq%fk;k&2ArDOF43 zD61xY@1q%mp99lPO_gIi=L?c%UcMFX*Vq!3b;iuzg;F#@`Y@}~d|9dIQTSq%T7*7n zmhR@xJ{vx?CZ`cYXk={d08f!kt(+}%c zWNA_no^c)*MDAsNCu2D*F6`?6F4am4sSa&Nupq93TKTirv(nKTa=|h;EaW?wM1|$- zP=q}_iZHX)513?1e4r5w*eCb;SxQ_y`tcswr*dWB^49H>Q7}F=_T)K7A*y$eB#^}) zX(qRWHG0~b)_1ULP)AVf(w4YrG4 zwf*QI;SO?JWNlgwcAgbc?eINza?!`eU>rjo9^SLg;Su3wniKT7!JN<8G7?&7lTPVd z{HAEeYg#8Cm+zdg;jC6H@up`E6zN2-`yhr=rQ0cm}*Y zr_BkurzQ54h+fB(tR{oa6)_BS>WYf3{Q1*WJsrwc=2i;b?C*2MM`*pR~N*w~0qV+1lI&vj%O&ey;} zm#Ih*xvjCWdE@pc`YkS=DkQ0k8!k4tvYurrgr9SWX|`ke{e>n=MxI5EsS(=RO+GzJ zUJX2@#4eb}RauWA{Myn(s@Ru$jOFA;-HNM< zIgDxF;}h9+aJA{Zv{moI_?abm;sQ~g1?q`*e!sr5Iku);BmqlEhO8!C_@a5p&()HZ z?w&Hh1C$5d9w=Zg3-*LcO7!rx#nLJFR^i)DHR<7}Q?o2cj|Qx;X zSrUAlrx_mGyPl73jwOyyu#>&ifE##PDSdPg2W0(KJ)kfo9oh&rDE3p#*3*pH)-XDb=HUv9dX{joH0NU6fKqx- zNtVP`FsRoKCr~aL@Y#k_^QIlaub(jkZG?=|Rze?#XS>iKVUlvH(ujOq;1i(BRBgCI zzO#E*j#;*hSXibLXtI+K^1JWeR(-8N{5CfzS`%^)|O2w8#)P&wV5!3072E&hhqN<(w zAd{oT%*bvru;jo9cReH4`MoIO%MT4)&Btl1f)PORU>I1u^5Vnwd72l%dR*FFKZmBS zMA06ic7dq(nof|gywj-Rx#ErDq`S8hkqwcPK4s~N}vqK`Tj(wf+`-HC6|&3GKzi-MPU$C^sFz-kbjQ>~vC z<$=w?tQ_dTJQ8T~6d0+&WJz#GO77o92^EM2U$2IiOVYdmKdRt;&2!ei^rMH#7)2I( z{On|B{3!y|a|>hYQ#wqanoNyUtly9yu9}}sl6}hLn9bA3u%#i@yXr6BCsSAT+QS-i zf^da&_`R5?_W0GDjkYz_q12rhQVPb>4iG48Cyz64hD}V7?xDxy%PKt|KJGVJ_|f2v z8it*f6BVTDptbr9GM!|ll3WsU&kD6Jo%y;L55CF=*nYcwPX{`Pt&YRa)=p1B`PJiF zhp2FI43Vo{Or)FCB-EpU+=)WzeB;Pe?%kR9!VUV1Sh%VKUre&a@VdR#9SO{J37NyS z(i)ooca8M4G0_oJCaN~2WM1em;rsX)G9twSS^KN|r?i55vB+&fQo4CSqqXM4kF zG7+I}29Bt&gfM!ef*B&Mzh{{lkKRtNUZ%K;cSf92fer zv7l7Q;+3p)r?h`9+T{iF_c5fL7a{*U%aMMw;jj;+S4vy4SnzBgH}x$|X@-S|qGZjF zq~1@O$eKn7q9XL_l@-BIir1V77EaAsM1c=C%l5;@0rq^q!Yja&Z)YGHDwaE-A0mjK zfrL>p!OvdLWLa(=w{=A%(Ob)$Wxn>@zPuLEo3s=5m)0s zg0qFosig@#>0*Psa}~^J)?Ax=QbvD;8jXWgPWGLL2>j`>KGwHf#h=9%NX*?&D0H%E zgL2I|lMC)2CkG~P(GYcan!BG_%D`y z00i$teF(}~k65#O=RbZ@rz7CSUrNixz$AHcU@1LBAs2f!ic7lWC8{$Bk5f`qJKy1> z)_g{Eo&L&W+vx^vLrBg!Q=&$vqOBgS?^)b^8llw4pr#4$Sb&3n9TsPFF>WdvjNW$P4}XRUCIK zE~7KCHag77rAgMPJ)IbX)|zL&YpC8AnFoqBQCd>quM%cDi)y~+oiIJ5j7PKOJRM@d1)rTJ zuFl~Oz_>IFKv*x+1eHG8pCC%ltPl#;<9~DPM4Q6#1;n;Ix<*c+wfN)?BD+(CohQh% z#^Pn#O%q2@21)8v@wIPi{@jvga(d4A zu1xWAk!2t5gc_%x@n&{LtTLlb8&_aXzPWGzX{}Eb#FzUgM&JvCkkKl*KOZWE`uzMe zX1E?C*uLWDyPs+$Qzqw(b&4LOltpVGbb%p{Qxdr0BYYTOXzyASUxECKKEJF;Wk}Ds z81MG5EUz}~U@jsVI_chJ8N?U+Y#AM%`LAN(#-Fi_tyt~~nR(`gsaxB>^K_R2e$B`x zOVSHTJGb}xIu5#f-h`Epb7<1z?-#D+T_9Fir-}kOzP-v6Ipib$t<1`A*cPw zfEwcgKGUVTNZ;X>w9+WMsz=DB9vUQwsx)hq{>dNbUVlwWjy|gHVE4-D_F+RjNDp_POR5{2Wf z7Y{1_eA*w^V-YYWB;Lw8Ya}n<9#BrZ=d0aenUtqe7b-jD|7HG!AyVkD3W@n<9|}jd zkqdqw^jh7a55^3?l(C2c*qpZndC8D9hwOI7>AQo+reg{hH8}i>bOa*;29NIc)=KH3 z5MS|=g-1~rCL)hXdG$k=Ytq9RfiPY-x~fg#cJy?g8qD|{+*grskIC@K#GXYaAm$SBo$-}h$)>ih0Tbkv z`uhvciSX*V0NWtPVB1=B^HjW)CFP{^3+0}Q9(9Z8g6htjF1|rWDiQ1lF}D6@;iao2 zFJ{>5b1mJW0?e?Sk^GyOFT@6f=Sj+(3P`ukBRC24KN1vzecla(VR95ZY5q*=T*LZ- znunQ!a?Z}baOV{eiO7%oQ*V7g%ppnZ*h>qu_L<&h`BLLUK(|xGXY{)5;C?m7L*>jH z`hxe^d<;6mu4gf(tz0Nxx^fWAbKs2mAH$w*7U$45r!9|YVE%tmd4kcdud7poI@4r%4-hdRw zA0tFtsk(PN>gNwz+AsG*S_%ceNOeZwy*0Vhyhl&o;{aL~oFXJd3nAf6TjHlIbe$9f zzb@P-I`@=}_>4>6*7||thWJjG6Q<97rz>NaUe6d*#L)%!eH-mZjhKDiY9mpN_8fIe z2v^^y2J$r$+BeSEKtI_cy!*O%s4gAG77)qZ=6mgvbn*HMMn>-fnz*CnPftAGJtIG@ zP_5bs_sI9Q5}K@@a^(wHq-X>j|MWosEL;Eq+z)=0xBTWpwl<~~W`8bQIWp227#RW9 z6Mro-(HS{AIQ*Fe`M2A|?-c2OBK#uQ{{jP8crpVdId?QPaG?9Gz#mJof0uk)M){u# zm|EEaR_XpPDa?P7va_{wwgaq!I{zQN{hOSVt*w=vfsKjP{~`BpTE2Dl|9@<6z5U-( zc>glCU#n)0bb=O6)&_Qt|A(UgHo&)KIp@D!hX3aG;%MUZwu1S~;i6M>#bH?-!E3$7 z?qIE#{XD8Z>jOLSmqmHst>q3_MbHLZnF>=;@E2a554F1daB<9!+x4_-JYF1LZ3EYP zBE-?U0v6F1gB3pP=h6J&=nfp6=h>nsTT-W&CwdF)X=gx~1mF_yX0Sif2jfnQUA4_d z9p6NykT3BUx7vuK`q`xB`Y{Y&e~kJp$EIkqloYgau``%~fy8Fnbnp3M@Ny1XZc*j! z?5rYnT8#%QFMD?-ud?*bR)ye)Xq*=T6RC=ehr>l#^7AQ9Rix)uy3(o3NyzoNZH*dq zJ>SqOzp#@s* zqvh(%eiK&%!y9Nm3bNa@5YuM0v!QPd4=Nm+>%MYkj}z57df8cM3A~w`o3<#xVd_qGrBVi=&)~=qEdtcJDYXm%}CPg_f<5!Xo(VApyH`1te)%_utL`VZL=wgRN5H_)GN`1qqP&gRo~#F3e1OJ56z9t1Z!^f$}|O<#uj0V9KlLG5sZ)k2=Gj&ibjnJ4XVsV^=yP&BebOFw}lsbrSP}mwZTP zT9Px?k7v%ThGPVhI@WJ*RU!5H0!f79R#r15-_)#NZDfm5>>6gqH$cGj$Ucf`A?a2W z6bMD0c^C90U|CC7y8pg}G)0=GbJ;JyOSn#>E91a~$ygkp#MgHyC|ETgdGi#9X&kpZ zNa2eyTmn}Q2{PM+kUWuks@cGg?Bl4{gM#;C&+MpUWU!;lx?b4`vNvgl7VdCHsOk{P z)q$jtCQo1VRi)r+9Vit9GN`Oejq`-6l+&X!-pjro3~xB`0D^hFI5z22=|lB!}X#XyPvKEG^w5#}QU zB5|LE*iDgeg5~fj4;Y6{Iw)x7I%vy=ZrQzcv)k&<*AO+KK}<-!M}IJSApyIio_Q`$ z;Y*(BV7X-0+K04^CC~&YBpI;d0Y6C)M=7$6vuQywLTC50AJ)e`cg1~~_TIM@v`@~8UTP2V6J!HfxcXlLowg(Y$Q&m_^oSYwdp z9uOkEktpWviq)PIaoInpP!^A~I;aJ*-sMR8&*b%{w&3~HU?dV>thg2V=j z8cD{x8zf)Z3FT^;IAc^vJwAK3+-E}JoH&qvDqfB_+9w&T z-fU;ii|D4Qo1t|-A?0MAMrf%-YNs^0r}CKLl>O!z0K0>BqeEH(Rm49zifb%zm2XV= z%%RxL`u?X(+7GLeA8@Cn4pX)Ow)(`ON)$SLEk#iw0op#Sh9I*0|>6p###*~2-h+E~M zeQSMCZdo3TOZ~K%X7Flvk{BnIBA>z;KG?P~;KE5ZmSMH4%hHNi9H90-upw$a%ka`^ zNw4)Tk)%=G>I0bS`zDCBgl8SQsy^FoGQPDcvjm-sA-E5? z2rw$oHv`kf=RZyPGQss=%R zp8yqd!D#1TilmEecCLi(mo+0el|q5Tuv9E_7S5Nm)zbL(6tZ?L9)cFkUfEQc%n3yLs~iYIr7&LeGJzwiBB>cLu1h3 zqFAlUxo~&DQ0p!^FD>EU)RQW0Ftd9KdhJAY z2j1$Jee=2+l7Yp7lFaL!ZPG2@e|Bzd(WZ*Nh5{OnQ?Rjdr4-N*qX5?P4<{rVK{F(Ala)RaMYt%~i z<8WrS$SK*Y>dOt~a_6s2X|uh%7U{r%3k&sjae+aQfslXgL2Cy%gz645!n_$_Z^zrV zw0{2|EJ*JonGHuTmLG@QvYy{NJ|le=)}Y0{!16^_wyNS0VEH-=P0A z&Hs5r{v`&`+v{R zU#9*4D+%x%6|d-`&WTKaDLz0e}4e|^V7Qx t!1?=EgWtE_Z^^&^cYAw4{Z+yP|I6b_UJ4BSjSUI#0|t2NnFYU{{eKppoJ;@! literal 0 HcmV?d00001 diff --git a/presentations/tmux_asiabsdcon11.pdf b/presentations/tmux_asiabsdcon11.pdf new file mode 100644 index 0000000000000000000000000000000000000000..76cd9b25f09d51654bfe934ce67a942a2491cf73 GIT binary patch literal 112246 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 literal 0 HcmV?d00001 From 9d80ad16f921c9a78255841394e88fa97fb1291c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:25:39 +0100 Subject: [PATCH 181/703] Remove old tools. --- tools/check-compat.sh | 5 ----- tools/fuzz.c | 31 ------------------------------- tools/putty-utf8.sh | 1 - 3 files changed, 37 deletions(-) delete mode 100644 tools/check-compat.sh delete mode 100644 tools/fuzz.c delete mode 100644 tools/putty-utf8.sh diff --git a/tools/check-compat.sh b/tools/check-compat.sh deleted file mode 100644 index b7603a80..00000000 --- a/tools/check-compat.sh +++ /dev/null @@ -1,5 +0,0 @@ -# $Id$ - -grep "#include" compat.h|while read line; do - grep "$line" *.[ch] compat/*.[ch] -done diff --git a/tools/fuzz.c b/tools/fuzz.c deleted file mode 100644 index 39a2a4db..00000000 --- a/tools/fuzz.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - -#include -#include -#include -#include - -int -main(void) -{ - time_t t; - int i; - - setvbuf(stdout, NULL, _IONBF, 0); - - t = time(NULL); - srandom((u_int) t); - - for (;;) { - putchar('\033'); - - for (i = 0; i < random() % 25; i++) { - if (i > 22) - putchar(';'); - else - putchar(random() % 256); - } - - /* usleep(100); */ - } -} diff --git a/tools/putty-utf8.sh b/tools/putty-utf8.sh deleted file mode 100644 index 73cb5fb1..00000000 --- a/tools/putty-utf8.sh +++ /dev/null @@ -1 +0,0 @@ -echo -ne \\033%G\\033[?47h\\033%G\\033[?47l From 1df39aa9620b9bb3fc36a52cf9a2765095be561e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:26:50 +0100 Subject: [PATCH 182/703] I don't think we should carry around scripts. I'm not too sure about examples/ at all, nobody is maintaining it... --- examples/tmux_backup.sh | 81 ----------------------------------------- 1 file changed, 81 deletions(-) delete mode 100644 examples/tmux_backup.sh diff --git a/examples/tmux_backup.sh b/examples/tmux_backup.sh deleted file mode 100644 index bc0bf370..00000000 --- a/examples/tmux_backup.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash -# -# By Victor Orlikowski. Public domain. -# -# This script maintains snapshots of each pane's -# history buffer, for each tmux session you are running. -# -# It is intended to be run by cron, on whatever interval works -# for you. - -# Maximum number of snapshots to keep. -max_backups=12 -# Names of sessions you may wish to exclude from snapshotting, -# space separated. -ignore_sessions="" -# The directory into which you want your snapshots placed. -# The default is probably "good enough." -backup_dir=~/.tmux_backup/snapshot - -######################################################################## - -# Rotate previous backups. -i=${max_backups} -while [[ ${i} != 0 ]] ; do -if [ -d ${backup_dir}.${i} ] ; then - if [[ ${i} = ${max_backups} ]] ; then - rm -r ${backup_dir}.${i} - else - mv ${backup_dir}.${i} ${backup_dir}.$((${i}+1)) - fi -fi -i=$((${i}-1)) -done - -if [ -d ${backup_dir} ] ; then - mv ${backup_dir} ${backup_dir}.1 -fi - -## Dump hardcopy from all windows in all available tmux sessions. -unset TMUX -for session in $(tmux list-sessions | cut -d' ' -f1 | sed -e 's/:$//') ; do - for ignore_session in ${ignore_sessions} ; do - if [ ${session} = ${ignore_session} ] ; then - continue 2 - fi - done - - # Session name can contain the colon character (":"). - # This can screw up addressing of windows within tmux, since - # target windows are specified as target-session:target-window. - # - # We use uuidgen to create a "safe" temporary session name, - # which we then use to create a "detached" session that "links" - # to the "real" session that we want to back up. - tmpsession=$(uuidgen) - tmux new-session -d -s "$tmpsession" -t "$session" - HISTSIZE=$(tmux show-options -g -t "$tmpsession" | grep "history-limit" | awk '{print $2}') - for win in $(tmux list-windows -t "$tmpsession" | grep -v "^\s" | cut -d' ' -f1 | sed -e 's/:$//'); do - session_dir=$(echo "$session" | sed -e 's/ /_/g' | sed -e 's%/%|%g') - win_spec="$tmpsession":"$win" - - if [ ! -d ${backup_dir}/${session_dir}/${win} ] ; then - mkdir -p ${backup_dir}/${session_dir}/${win} - fi - - for pane in $(tmux list-panes -t "$win_spec" | cut -d' ' -f1 | sed -e 's/:$//'); do - pane_path=${backup_dir}/${session_dir}/${win}/${pane} - pane_spec="$win_spec"."$pane" - - tmux capture-pane -t "$pane_spec" -S -${HISTSIZE} - tmux save-buffer ${pane_path} - - if [ ! -s ${pane_path} ] ; then - sleep 1 - rm ${pane_path} - fi - done - done - tmux kill-session -t "$tmpsession" - -done From 0b22d574e0adc8f27b31fea9e278731ded06ac70 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:46:49 +0100 Subject: [PATCH 183/703] Update FAQ for new behaviour. --- FAQ | 51 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/FAQ b/FAQ index c7d3d15a..2227730f 100644 --- a/FAQ +++ b/FAQ @@ -352,42 +352,33 @@ lock(1) or vlock(1)) by using the following: bind x set lock-command '/usr/bin/vlock' \; lock-client \; set lock-command 'tput civis && read -s -n1' -* vim displays reverse video instead of italics, while less displays italics - (or just regular text) instead of reverse. What's wrong? +* I don't see italics! Or less and vim show italics and reverse the wrong way round! -Screen's terminfo description lacks italics mode and has standout mode in its -place, but using the same escape sequence that urxvt uses for italics. This -means applications (like vim) looking for italics will not find it and might -turn to reverse in its place, while applications (like less) asking for -standout will end up with italics instead of reverse. To make applications -aware that tmux supports italics and to use a proper escape sequence for -standout, you'll need to create a new terminfo file with modified sgr, smso, -rmso, sitm and ritm entries: +GNU screen does not support italics and the "screen" terminfo description uses +the italics escape sequence incorrectly. - $ mkdir $HOME/.terminfo/ - $ screen_terminfo="screen" - $ infocmp "$screen_terminfo" | sed \ - -e 's/^screen[^|]*|[^,]*,/screen-it|screen with italics support,/' \ - -e 's/%?%p1%t;3%/%?%p1%t;7%/' \ - -e 's/smso=[^,]*,/smso=\\E[7m,/' \ - -e 's/rmso=[^,]*,/rmso=\\E[27m,/' \ - -e '$s/$/ sitm=\\E[3m, ritm=\\E[23m,/' > /tmp/screen.terminfo - $ tic /tmp/screen.terminfo +If default-terminal is set to "screen" or matches "screen-*", tmux will behave +like screen and italics will be disabled. + +To enable italics, create a new terminfo entry called "tmux" (some platforms +may already have this, you can check with "infocmp tmux"): + + $ cat < Date: Thu, 4 Jun 2015 08:47:23 +0100 Subject: [PATCH 184/703] Note version this happened. --- FAQ | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FAQ b/FAQ index 2227730f..21e6167d 100644 --- a/FAQ +++ b/FAQ @@ -357,8 +357,8 @@ bind x set lock-command '/usr/bin/vlock' \; lock-client \; set lock-command 'tpu GNU screen does not support italics and the "screen" terminfo description uses the italics escape sequence incorrectly. -If default-terminal is set to "screen" or matches "screen-*", tmux will behave -like screen and italics will be disabled. +As of tmux 2.1, if default-terminal is set to "screen" or matches "screen-*", +tmux will behave like screen and italics will be disabled. To enable italics, create a new terminfo entry called "tmux" (some platforms may already have this, you can check with "infocmp tmux"): From 75061cb45d68ad3a7a75839a2f658813e697c55e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 08:50:20 +0100 Subject: [PATCH 185/703] I no longer need to care about GCC 3. --- Makefile.am | 12 ++---------- configure.ac | 13 ------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/Makefile.am b/Makefile.am index 29294d87..0aec7ae4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,8 +22,7 @@ if IS_GLIBC CFLAGS += -D_GNU_SOURCE endif -# Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly -# different flags. +# Set flags for gcc. if IS_GCC CFLAGS += -std=gnu99 -O2 if IS_DEBUG @@ -32,17 +31,10 @@ CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align -CFLAGS += -Wdeclaration-after-statement +CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign CPPFLAGS += -DDEBUG endif -if IS_GCC4 CPPFLAGS += -iquote. -if IS_DEBUG -CFLAGS += -Wno-pointer-sign -endif -else -CPPFLAGS += -I. -I- -endif endif # Set flags for Solaris. diff --git a/configure.ac b/configure.ac index 0dd1151c..cd3ab5a3 100644 --- a/configure.ac +++ b/configure.ac @@ -44,19 +44,6 @@ fi # Is this gcc? AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) -AC_MSG_CHECKING(for gcc that whines about -I) -AC_EGREP_CPP( - yes, - [ - #if __GNUC__ > 3 - yes - #endif - ], - found_gcc4=yes, - found_gcc4=no -) -AM_CONDITIONAL(IS_GCC4, test "x$found_gcc4" = xyes) -AC_MSG_RESULT($found_gcc4) # Is this Sun CC? AC_EGREP_CPP( From 8fcac1b79449b09466320fb823775e4a24ba19dc Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 4 Jun 2015 09:26:35 +0100 Subject: [PATCH 186/703] SYNCING: Update for GH Explain the release process now that we're using GH. --- SYNCING | 66 +++++++++++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/SYNCING b/SYNCING index 218685c3..9ef3ef06 100644 --- a/SYNCING +++ b/SYNCING @@ -1,10 +1,10 @@ Preamble ======== -Tmux on SourceForge has two git repositories [1] "tmux-code" and "tmux-openbsd". +Tmux portable relies on repositories "tmux" and "tmux-openbsd". Here's a description of them: -* "tmux-code" is the portable version, the one which contains code for other +* "tmux" is the portable version, the one which contains code for other operating systems, and autotools, etc., which isn't found or needed in the OpenBSD base system. @@ -18,7 +18,7 @@ repository will take at least that long to appear in this git repository. OpenBSD code). It is assumed that the person doing the sync has read/write access to the -tmux-code repository on SourceForge already. +tmux repository on SourceForge already. If you've never used git before, git tracks meta-data about the committer and the author, as part of a commit, hence: @@ -37,11 +37,11 @@ this information has ever been set before. Cloning repositories ==================== -This involves having both tmux-code and tmux-openbsd cloned, as in: +This involves having both tmux and tmux-openbsd cloned, as in: % cd /some/where/useful -% git clone ssh://${USER}@git.code.sf.net/p/tmux/tmux -% git clone ssh://${USER}@git.code.sf.net/p/tmux/tmux-openbsd +% git clone https://github.com/tmux/tmux.git +% git clone https://github.com/ThomasAdam/tmux-openbsd.git Note that you do not need additional checkouts to manage the sync -- an existing clone of either repositories will suffice. So if you already have @@ -50,56 +50,56 @@ these checkouts existing, skip that. Adding in git-remotes ===================== -Because the portable "tmux-code" git repository and the "tmux-openbsd" +Because the portable "tmux" git repository and the "tmux-openbsd" repository do not inherently share any history between each other, the history has been faked between them. This "faking of history" is something which has to be told to git for the purposes of comparing the "tmux" and "tmux-openbsd" repositories for syncing. To do this, we must reference the -clone of the "tmux-openbsd" repository from the "tmux-code" repository, as +clone of the "tmux-openbsd" repository from the "tmux" repository, as shown by the following command: -% cd /path/to/tmux-code +% cd /path/to/tmux % git remote add obsd-tmux file:///path/to/tmux-openbsd So that now, the remote "obsd-tmux" can be used to reference branches and commits from the "tmux-openbsd" repository, but from the context of the -portable "tmux-code" repository, which makes sense because it's the "tmux" +portable "tmux" repository, which makes sense because it's the "tmux" repository which will have the updates applied to them. Fetching updates ================ To ensure the latest commits from "tmux-openbsd" can be found from within -"tmux-code", we have to ensure the "master" branch from "tmux-openbsd" is -up-to-date first, and then reference that update in "tmux-code", as in: +"tmux", we have to ensure the "master" branch from "tmux-openbsd" is +up-to-date first, and then reference that update in "tmux", as in: % cd /path/to/tmux-openbsd % git checkout master % git pull -Then back in "tmux-code": +Then back in "tmux": -% cd /path/to/tmux-code -% git fetch obsd-tmux-code +% cd /path/to/tmux +% git fetch obsd-tmux Creating the necessary branches =============================== -Now that "tmux-code" can see commits and branches from "tmux-openbsd" by way +Now that "tmux" can see commits and branches from "tmux-openbsd" by way of the remote name "obsd-tmux", we can now create the master branch from -"tmux-openbsd" in the "tmux-code" repository: +"tmux-openbsd" in the "tmux" repository: % git checkout -b obsd-master obsd-tmux/master Adding in the fake history points ================================= -To tie both the "master" branch from "tmux-code" and the "obsd-master" +To tie both the "master" branch from "tmux" and the "obsd-master" branch from "tmux-openbsd" together, the fake history points added to the -"tmux-code" repository need to be added. To do this, we must add an +"tmux" repository need to be added. To do this, we must add an additional refspec line, as in: -% cd /path/to/tmux-code +% cd /path/to/tmux % git config --add remote.origin.fetch '+refs/replace/*:refs/replace/*' % git fetch origin @@ -110,7 +110,7 @@ Make sure the "master" branch is checked out: % git checkout master -The following will show commits on OpenBSD not yet synched with "tmux-code": +The following will show commits on OpenBSD not yet synched with "tmux": % git log master..obsd-master @@ -162,22 +162,18 @@ Release tmux for next version % git push 1.X -4. Build the tarball with make dist. Now that it's using autoconf there - shouldn't be any weird files (such as the original and rejection files - from patch(1)) but it doesn't hurt taking a quick look at it. +4. Build the tarball with 'make dist'. -5. Split the release changes into a new file. This should be named - tmux-$VERSION-readme to make sourceforge show it automagically in specific - parts of the project page. +5. Check the tarball. If it's good, go here to select the tag just pushed: -6. Upload the tarball and the above file. Make the tarball the default - download by selecting all operating systems under the file details. + https://github.com/tmux/tmux/tags -7. Run make update-index.html upload-index.html to replace %%VERSION%%. + Click the "Add release notes", upload the tarball and add a link in the + description field to the CHANGES file. -8. Bump version in configure.ac and uncomment "found_debug=yes" to create - a debug build by default. +7. Clone the tmux.github.io repository, and change the RELEASE version in + the Makefile. Commit it, and run 'make' to replace %%VERSION%%. Push + the result out. -9. Update freshmeat. - -[1] https://sourceforge.net/p/tmux/_list/git +8. Bump version in tmu/tmux.git configure.ac and uncomment "found_debug=yes" to + create a debug build by default. From 2c29b3e82cd2e6dcedaa14fc7620b389be2c56a9 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Thu, 4 Jun 2015 10:35:40 +0100 Subject: [PATCH 187/703] SYNCING: Few tweaks --- SYNCING | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/SYNCING b/SYNCING index 9ef3ef06..52591af7 100644 --- a/SYNCING +++ b/SYNCING @@ -17,9 +17,6 @@ repository will take at least that long to appear in this git repository. (It might take longer, depending on the CVS mirror used to import the OpenBSD code). -It is assumed that the person doing the sync has read/write access to the -tmux repository on SourceForge already. - If you've never used git before, git tracks meta-data about the committer and the author, as part of a commit, hence: @@ -154,13 +151,13 @@ Release tmux for next version 3. Tag with: - % git tag -a 1.X + % git tag -a 2.X - Where "1.X" is the next version. + Where "2.X" is the next version. Push the tag out with: - % git push 1.X + % git push 2.X 4. Build the tarball with 'make dist'. From b67db455a87618ef438609f2caef667573e8744b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 10:37:39 +0100 Subject: [PATCH 188/703] Update TODO with some items from old SF tickets. --- TODO | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO b/TODO index 93d11f4f..4a3ec3cd 100644 --- a/TODO +++ b/TODO @@ -32,6 +32,9 @@ * flag to choose-* for sort order * choose mode would be better per client than per window? * two choices (first one then second, for swap-pane and join-pane) + * choose modes should ditch the key bindings and just have fixed keys, and + be more customized to their purpose (d to delete a buffer for choose-buffer, + a preview of buffer contents, etc) - improve monitor-*: * straighten out rules for multiple clients @@ -55,6 +58,7 @@ * message display * prompt input * multibyte key input + * searching in copy mode - copy/paste improvements: * incremental searching @@ -119,3 +123,4 @@ environments * multiline status line? * customizable command aliases + * automatic pane logging From a3edfd9e84f4f8e9b74425cdc4b00fe9cb3d0ba6 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2015 09:42:29 +0000 Subject: [PATCH 189/703] teminal -> terminal, from Corey Farwell. --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 6be5d906..41c0bc85 100644 --- a/input.c +++ b/input.c @@ -43,7 +43,7 @@ * pretty stupid but not supporting it is more trouble than it is worth. * * - Special handling for ESC inside a DCS to allow arbitrary byte sequences to - * be passed to the underlying teminal(s). + * be passed to the underlying terminals. */ /* Input parser cell. */ From d058e963fda8a4847cd572e5fb62cd851fdec69a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 11:36:44 +0100 Subject: [PATCH 190/703] Update mailmap. --- .mailmap | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index a7a401ff..3b43fa18 100644 --- a/.mailmap +++ b/.mailmap @@ -1,24 +1,34 @@ Bob Beck beck Igor Sobrado sobrado +Ingo Schwarze schwarze Jacek Masiulaniec jacekm +Jason McIntyre jmc Jason McIntyre jcm Joel Sing jsing +Jonathan Gray jsg +Kenneth R Westerback krw Marc Espie espie Matthew Dempsky matthew Matthias Kilian kili Matthieu Herrb matthieu Miod Vallat miod -Nicholas Marriott nicm -Nicholas Marriott no_author - +Nicholas Marriott Nicholas Marriott +Nicholas Marriott nicm +Nicholas Marriott no_author Okan Demirmen okan Philip Guenther guenther Pierre-Yves Ritschard pyr Ray Lai ray Ryan McBride mcbride +Sebastian Benoit benno Stefan Sperling stsp Stuart Henderson sthen Ted Unangst tedu -Theo Deraadt deraadt +Theo de Raadt deraadt +Theo de Raadt Theo Deraadt Thomas Adam Thomas +Thomas Adam n6tadam +Thomas Adam Thomas Adam +Tobias Stoeckmann tobias +Todd C Miller millert William Yodlowsky william From 1de74e27e535b3e294c265974973a7743c8374a0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Jun 2015 11:40:27 +0100 Subject: [PATCH 191/703] Spaces -> tabs. --- .mailmap | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.mailmap b/.mailmap index 3b43fa18..a9b07251 100644 --- a/.mailmap +++ b/.mailmap @@ -1,34 +1,34 @@ -Bob Beck beck -Igor Sobrado sobrado +Bob Beck beck +Igor Sobrado sobrado Ingo Schwarze schwarze -Jacek Masiulaniec jacekm +Jacek Masiulaniec jacekm Jason McIntyre jmc -Jason McIntyre jcm +Jason McIntyre jcm Joel Sing jsing Jonathan Gray jsg Kenneth R Westerback krw -Marc Espie espie -Matthew Dempsky matthew -Matthias Kilian kili -Matthieu Herrb matthieu -Miod Vallat miod -Nicholas Marriott Nicholas Marriott -Nicholas Marriott nicm -Nicholas Marriott no_author -Okan Demirmen okan +Marc Espie espie +Matthew Dempsky matthew +Matthias Kilian kili +Matthieu Herrb matthieu +Miod Vallat miod +Nicholas Marriott Nicholas Marriott +Nicholas Marriott nicm +Nicholas Marriott no_author +Okan Demirmen okan Philip Guenther guenther -Pierre-Yves Ritschard pyr -Ray Lai ray +Pierre-Yves Ritschard pyr +Ray Lai ray Ryan McBride mcbride Sebastian Benoit benno -Stefan Sperling stsp -Stuart Henderson sthen -Ted Unangst tedu -Theo de Raadt deraadt -Theo de Raadt Theo Deraadt +Stefan Sperling stsp +Stuart Henderson sthen +Ted Unangst tedu +Theo de Raadt deraadt +Theo de Raadt Theo Deraadt Thomas Adam Thomas Thomas Adam n6tadam Thomas Adam Thomas Adam Tobias Stoeckmann tobias Todd C Miller millert -William Yodlowsky william +William Yodlowsky william From a863834574ec02b87ff0e7245ef31f0d4543ab34 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2015 11:43:51 +0000 Subject: [PATCH 192/703] Add support for a single "marked pane". There is one marked pane in the server at a time; it may be toggled or cleared with select-pane -m and -M (the border is highlighted). A new target '~' or '{marked}' specifies the marked pane to commands and it is the default target for the swap-pane and join-pane -s flag (this makes them much simpler to use - mark the source pane and then change to the target pane to run swapp or joinp). --- cmd-find.c | 65 +++++++++++++++++++++++++++++++-- cmd-join-pane.c | 2 +- cmd-select-pane.c | 31 +++++++++++++--- cmd-swap-pane.c | 8 +++-- cmd-swap-window.c | 2 +- key-bindings.c | 3 ++ screen-redraw.c | 44 +++++++++++++++-------- server.c | 92 ++++++++++++++++++++++++++++++++++++++++------- tmux.1 | 71 ++++++++++++++++++++++++++++++++++-- tmux.h | 13 +++++++ window.c | 5 +++ 11 files changed, 296 insertions(+), 40 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 98f1e187..2b862f95 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -29,6 +29,7 @@ #define CMD_FIND_PREFER_UNATTACHED 0x1 #define CMD_FIND_QUIET 0x2 #define CMD_FIND_WINDOW_INDEX 0x4 +#define CMD_FIND_DEFAULT_MARKED 0x8 enum cmd_find_type { CMD_FIND_PANE, @@ -759,7 +760,14 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* Find current state. */ cmd_find_clear_state(¤t, cmdq, flags); - if (cmd_find_current_session(¤t) != 0) { + if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { + current.s = marked_session; + current.wl = marked_winlink; + current.idx = current.wl->idx; + current.w = current.wl->window; + current.wp = marked_window_pane; + } + if (current.s == NULL && cmd_find_current_session(¤t) != 0) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no current session"); goto error; @@ -798,9 +806,24 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, } return (&fs); } - copy = xstrdup(target); + + /* Marked target is a plain ~ or {marked}. */ + if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) { + if (!server_check_marked()) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no marked target"); + goto error; + } + fs.s = marked_session; + fs.wl = marked_winlink; + fs.idx = fs.wl->idx; + fs.w = fs.wl->window; + fs.wp = marked_window_pane; + return (&fs); + } /* Find separators if they exist. */ + copy = xstrdup(target); colon = strchr(copy, ':'); if (colon != NULL) *colon++ = '\0'; @@ -1053,6 +1076,24 @@ cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) return (fs->wl); } +/* Find the target window, defaulting to marked rather than current. */ +struct winlink * +cmd_find_window_marked(struct cmd_q *cmdq, const char *target, + struct session **sp) +{ + struct cmd_find_state *fs; + int flags = CMD_FIND_DEFAULT_MARKED; + + fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + if (sp != NULL) + *sp = fs->s; + return (fs->wl); +} + /* Find the target pane and report an error and return NULL. */ struct winlink * cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, @@ -1072,6 +1113,26 @@ cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, return (fs->wl); } +/* Find the target pane, defaulting to marked rather than current. */ +struct winlink * +cmd_find_pane_marked(struct cmd_q *cmdq, const char *target, + struct session **sp, struct window_pane **wpp) +{ + struct cmd_find_state *fs; + int flags = CMD_FIND_DEFAULT_MARKED; + + fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + if (sp != NULL) + *sp = fs->s; + if (wpp != NULL) + *wpp = fs->wp; + return (fs->wl); +} + /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 943cdce4..b995b674 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -75,7 +75,7 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) dst_idx = dst_wl->idx; server_unzoom_window(dst_w); - src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index f237e8fd..5ea4bdc3 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -28,8 +28,8 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", - "DdegLlP:Rt:U", 0, 0, - "[-DdegLlRU] [-P style] " CMD_TARGET_PANE_USAGE, + "DdegLlMmP:Rt:U", 0, 0, + "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_exec }; @@ -47,7 +47,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; - struct window_pane *wp; + struct session *s; + struct window_pane *wp, *lastwp, *markedwp; const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { @@ -74,9 +75,31 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); + if (args_has(args, 'm') || args_has(args, 'M')) { + if (args_has(args, 'm') && !window_pane_visible(wp)) + return (CMD_RETURN_NORMAL); + lastwp = marked_window_pane; + + if (args_has(args, 'M') || server_is_marked(s, wl, wp)) + server_clear_marked(); + else + server_set_marked(s, wl, wp); + markedwp = marked_window_pane; + + if (lastwp != NULL) { + server_redraw_window_borders(lastwp->window); + server_status_window(lastwp->window); + } + if (markedwp != NULL) { + server_redraw_window_borders(markedwp->window); + server_status_window(markedwp->window); + } + return (CMD_RETURN_NORMAL); + } + if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if (args_has(args, 'P')) { style = args_get(args, 'P'); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 918a2e4f..c4751c36 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -58,18 +58,22 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); + src_wl = dst_wl; } else if (args_has(self->args, 'U')) { src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); + src_wl = dst_wl; } else { - src_wl = cmd_find_pane(cmdq, NULL, NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, NULL, NULL, + &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; } } else { - src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, + &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 655b910c..51682e37 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -47,7 +47,7 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; target_src = args_get(args, 's'); - if ((wl_src = cmd_find_window(cmdq, target_src, &src)) == NULL) + if ((wl_src = cmd_find_window_marked(cmdq, target_src, &src)) == NULL) return (CMD_RETURN_ERROR); target_dst = args_get(args, 't'); if ((wl_dst = cmd_find_window(cmdq, target_dst, &dst)) == NULL) diff --git a/key-bindings.c b/key-bindings.c index a3cb0bea..ab4751f9 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -177,6 +177,7 @@ key_bindings_init(void) "bind ? list-keys", "bind D choose-client", "bind L switch-client -l", + "bind M select-pane -M", "bind [ copy-mode", "bind ] paste-buffer", "bind c new-window", @@ -184,6 +185,7 @@ key_bindings_init(void) "bind f command-prompt \"find-window '%%'\"", "bind i display-message", "bind l last-window", + "bind m select-pane -m", "bind n next-window", "bind o select-pane -t:.+", "bind p previous-window", @@ -222,6 +224,7 @@ key_bindings_init(void) "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", + "bind -n MouseDown3Pane select-pane -mt=", }; u_int i; struct cmd_list *cmdlist; diff --git a/screen-redraw.c b/screen-redraw.c index babc74a9..799f5c55 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -26,8 +26,8 @@ int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); int screen_redraw_cell_border(struct client *, u_int, u_int); int screen_redraw_check_cell(struct client *, u_int, u_int, struct window_pane **); -int screen_redraw_check_active(u_int, u_int, int, struct window *, - struct window_pane *); +int screen_redraw_check_is(u_int, u_int, int, struct window *, + struct window_pane *, struct window_pane *); void screen_redraw_draw_borders(struct client *, int, u_int); void screen_redraw_draw_panes(struct client *, u_int); @@ -175,13 +175,13 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, return (CELL_OUTSIDE); } -/* Check active pane indicator. */ +/* Check if the border of a particular pane. */ int -screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, - struct window_pane *wp) +screen_redraw_check_is(u_int px, u_int py, int type, struct window *w, + struct window_pane *wantwp, struct window_pane *wp) { /* Is this off the active pane border? */ - if (screen_redraw_cell_border1(w->active, px, py) != 1) + if (screen_redraw_cell_border1(wantwp, px, py) != 1) return (0); /* If there are more than two panes, that's enough. */ @@ -196,7 +196,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, if (wp->xoff == 0 && wp->sx == w->sx) { /* This can either be the top pane or the bottom pane. */ if (wp->yoff == 0) { /* top pane */ - if (wp == w->active) + if (wp == wantwp) return (px <= wp->sx / 2); return (px > wp->sx / 2); } @@ -207,7 +207,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, if (wp->yoff == 0 && wp->sy == w->sy) { /* This can either be the left pane or the right pane. */ if (wp->xoff == 0) { /* left pane */ - if (wp == w->active) + if (wp == wantwp) return (py <= wp->sy / 2); return (py > wp->sy / 2); } @@ -274,13 +274,15 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) void screen_redraw_draw_borders(struct client *c, int status, u_int top) { - struct window *w = c->session->curw->window; + struct session *s = c->session; + struct window *w = s->curw->window; struct options *oo = &w->options; struct tty *tty = &c->tty; struct window_pane *wp; - struct grid_cell active_gc, other_gc, msg_gc; + struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; + struct grid_cell msg_gc; u_int i, j, type, msgx = 0, msgy = 0; - int small, flags; + int active, small, flags; char msg[256]; const char *tmp; size_t msglen = 0; @@ -314,15 +316,29 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) style_apply(&active_gc, oo, "pane-active-border-style"); active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; + memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); + m_other_gc.attr ^= GRID_ATTR_REVERSE; + memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); + m_active_gc.attr ^= GRID_ATTR_REVERSE; + for (j = 0; j < tty->sy - status; j++) { for (i = 0; i < tty->sx; i++) { type = screen_redraw_check_cell(c, i, j, &wp); if (type == CELL_INSIDE) continue; - if (type == CELL_OUTSIDE && - small && i > msgx && j == msgy) + if (type == CELL_OUTSIDE && small && + i > msgx && j == msgy) continue; - if (screen_redraw_check_active(i, j, type, w, wp)) + active = screen_redraw_check_is(i, j, type, w, + w->active, wp); + if (server_is_marked(s, s->curw, marked_window_pane) && + screen_redraw_check_is(i, j, type, w, + marked_window_pane, wp)) { + if (active) + tty_attributes(tty, &m_active_gc, NULL); + else + tty_attributes(tty, &m_other_gc, NULL); + } else if (active) tty_attributes(tty, &active_gc, NULL); else tty_attributes(tty, &other_gc, NULL); diff --git a/server.c b/server.c index b53648f0..52fdb0c3 100644 --- a/server.c +++ b/server.c @@ -50,19 +50,85 @@ int server_shutdown; struct event server_ev_accept; struct event server_ev_second; -int server_create_socket(void); -void server_loop(void); -int server_should_shutdown(void); -void server_send_shutdown(void); -void server_clean_dead(void); -void server_accept_callback(int, short, void *); -void server_signal_callback(int, short, void *); -void server_child_signal(void); -void server_child_exited(pid_t, int); -void server_child_stopped(pid_t, int); -void server_second_callback(int, short, void *); -void server_lock_server(void); -void server_lock_sessions(void); +struct session *marked_session; +struct winlink *marked_winlink; +struct window *marked_window; +struct window_pane *marked_window_pane; +struct layout_cell *marked_layout_cell; + +int server_create_socket(void); +void server_loop(void); +int server_should_shutdown(void); +void server_send_shutdown(void); +void server_clean_dead(void); +void server_accept_callback(int, short, void *); +void server_signal_callback(int, short, void *); +void server_child_signal(void); +void server_child_exited(pid_t, int); +void server_child_stopped(pid_t, int); +void server_second_callback(int, short, void *); +void server_lock_server(void); +void server_lock_sessions(void); + +/* Set marked pane. */ +void +server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) +{ + marked_session = s; + marked_winlink = wl; + marked_window = wl->window; + marked_window_pane = wp; + marked_layout_cell = wp->layout_cell; +} + +/* Clear marked pane. */ +void +server_clear_marked(void) +{ + marked_session = NULL; + marked_winlink = NULL; + marked_window = NULL; + marked_window_pane = NULL; + marked_layout_cell = NULL; +} + +/* Is this the marked pane? */ +int +server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) +{ + if (s == NULL || wl == NULL || wp == NULL) + return (0); + if (marked_session != s || marked_winlink != wl) + return (0); + if (marked_window_pane != wp) + return (0); + return (server_check_marked()); +} + +/* Check if the marked pane is still valid. */ +int +server_check_marked(void) +{ + struct winlink *wl; + + if (marked_window_pane == NULL) + return (0); + if (marked_layout_cell != marked_window_pane->layout_cell) + return (0); + + if (!session_alive(marked_session)) + return (0); + RB_FOREACH(wl, winlinks, &marked_session->windows) { + if (wl->window == marked_window && wl == marked_winlink) + break; + } + if (wl == NULL) + return (0); + + if (!window_has_pane(marked_window, marked_window_pane)) + return (0); + return (window_pane_visible(marked_window_pane)); +} /* Create server socket. */ int diff --git a/tmux.1 b/tmux.1 index 1233d1be..015e5ee7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -298,6 +298,12 @@ Change to the previous window. Briefly display pane indexes. .It r Force redraw of the attached client. +.It m +Mark the current pane (see +.Ic select-pane +.Fl m ) . +.It M +Clear the marked pane. .It s Select a new session for the attached client interactively. .It t @@ -351,6 +357,8 @@ This section contains a list of the commands supported by .Nm . Most commands accept the optional .Fl t +(and sometimes +.Fl s ) argument with one of .Ar target-client , .Ar target-session @@ -451,7 +459,6 @@ Each has a single-character alternative form. .It Li "{last}" Ta "!" Ta "The last (previously current) window" .It Li "{next}" Ta "+" Ta "The next window by number" .It Li "{previous}" Ta "-" Ta "The previous window by number" -.It Li "{mouse}" Ta "=" Ta "The window where the mouse event happened" .El .Pp .Ar target-pane @@ -481,7 +488,6 @@ The following special tokens are available for the pane index: .It Li "{down}" Ta "" Ta "The pane below the active pane" .It Li "{left}" Ta "" Ta "The pane to the left of the active pane" .It Li "{right}" Ta "" Ta "The pane to the right of the active pane" -.It Li "{mouse}" Ta "=" Ta "The pane where the mouse event happened" .El .Pp The tokens @@ -493,6 +499,27 @@ may be followed by an offset, for example: select-window -t:+2 .Ed .Pp +In addition, +.Em target-session , +.Em target-window +or +.Em target-pane +may consist entirely of the token +.Ql {mouse} +(alternative form +.Ql = ) +to specify the most recent mouse event +(see the +.Sx MOUSE SUPPORT +section) +or +.Ql {marked} +(alternative form +.Ql ~ ) +to specify the marked pane (see +.Ic select-pane +.Fl m ) . +.Pp Sessions, window and panes are each numbered with a unique ID; session IDs are prefixed with a .Ql $ , @@ -1440,6 +1467,13 @@ option causes .Ar src-pane to be joined to left of or above .Ar dst-pane . +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. .It Xo Ic kill-pane .Op Fl a .Op Fl t Ar target-pane @@ -1795,7 +1829,7 @@ commands. .Fl o applies the last set layout if possible (undoes the most recent layout change). .It Xo Ic select-pane -.Op Fl DdegLlRU +.Op Fl DdegLlMmRU .Op Fl P Ar style .Op Fl t Ar target-pane .Xc @@ -1823,6 +1857,20 @@ enables or .Fl d disables input to the pane. .Pp +.Fl m +and +.Fl M +are used to set and clear the +.Em marked pane . +There is one marked pane at a time, setting a new marked pane clears the last. +The marked pane is the default target for +.Fl s +to +.Ic join-pane , +.Ic swap-pane +and +.Ic swap-window . +.Pp Each pane has a style: by default the .Ic window-style and @@ -1911,6 +1959,13 @@ swaps with the next pane (after it numerically). instructs .Nm not to change the active pane. +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. .It Xo Ic swap-window .Op Fl d .Op Fl s Ar src-window @@ -1922,6 +1977,15 @@ This is similar to except the source and destination windows are swapped. It is an error if no window exists at .Ar src-window . +.Pp +Like +.Ic swap-pane , +if +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the window containing the marked pane is used rather than the current window. .It Xo Ic unlink-window .Op Fl k .Op Fl t Ar target-window @@ -3488,6 +3552,7 @@ The flag is one of the following symbols appended to the window name: .It Li "#" Ta "Window is monitored and activity has been detected." .It Li "!" Ta "A bell has occurred in the window." .It Li "~" Ta "The window has been silent for the monitor-silence interval." +.It Li "M" Ta "The window contains the marked pane." .It Li "Z" Ta "The window's active pane is zoomed." .El .Pp diff --git a/tmux.h b/tmux.h index 78e64170..ae1682af 100644 --- a/tmux.h +++ b/tmux.h @@ -1688,8 +1688,12 @@ struct session *cmd_find_current(struct cmd_q *); struct session *cmd_find_session(struct cmd_q *, const char *, int); struct winlink *cmd_find_window(struct cmd_q *, const char *, struct session **); +struct winlink *cmd_find_window_marked(struct cmd_q *, const char *, + struct session **); struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); +struct winlink *cmd_find_pane_marked(struct cmd_q *, const char *, + struct session **, struct window_pane **); struct client *cmd_find_client(struct cmd_q *, const char *, int); int cmd_find_index(struct cmd_q *, const char *, struct session **); @@ -1850,6 +1854,15 @@ const char *key_string_lookup_key(int); /* server.c */ extern struct clients clients; extern struct clients dead_clients; +extern struct session *marked_session; +extern struct winlink *marked_winlink; +extern struct window_pane *marked_window_pane; +void server_set_marked(struct session *, struct winlink *, + struct window_pane *); +void server_clear_marked(void); +int server_is_marked(struct session *, struct winlink *, + struct window_pane *); +int server_check_marked(void); int server_start(int, char *); void server_update_socket(void); void server_add_accept(int); diff --git a/window.c b/window.c index bc17656d..a1d6792b 100644 --- a/window.c +++ b/window.c @@ -543,6 +543,9 @@ window_add_pane(struct window *w, u_int hlimit) void window_lost_pane(struct window *w, struct window_pane *wp) { + if (wp == marked_window_pane) + server_clear_marked(); + if (wp == w->active) { w->active = w->last; w->last = NULL; @@ -661,6 +664,8 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; + if (server_check_marked() && wl == marked_winlink) + flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; flags[pos] = '\0'; From 4a6c06d6a981d05c5ab3dbc7a365ff740fbb7feb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2015 14:29:33 +0000 Subject: [PATCH 193/703] Make unsetting a global option restore it to the default. Diff lying around for a while, I have forgotten who suggested it :-/. --- cmd-set-option.c | 19 ++++++++++++++----- tmux.1 | 12 +++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 83d31b93..77c7d7c2 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -266,16 +266,25 @@ cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, { struct args *args = self->args; - if (args_has(args, 'g')) { - cmdq_error(cmdq, "can't unset global option: %s", oe->name); - return (-1); - } if (value != NULL) { cmdq_error(cmdq, "value passed to unset option: %s", oe->name); return (-1); } - options_remove(oo, oe->name); + if (args_has(args, 'g') || oo == &global_options) { + switch (oe->type) { + case OPTIONS_TABLE_STRING: + options_set_string(oo, oe->name, "%s", oe->default_str); + break; + case OPTIONS_TABLE_STYLE: + options_set_style(oo, oe->name, oe->default_str, 0); + break; + default: + options_set_number(oo, oe->name, oe->default_num); + break; + } + } else + options_remove(oo, oe->name); return (0); } diff --git a/tmux.1 b/tmux.1 index 015e5ee7..e479afbe 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2273,21 +2273,19 @@ command), a server option with .Fl s , otherwise a session option. -.Pp If .Fl g -is specified, the global session or window option is set. +is given, the global session or window option is set. The .Fl u flag unsets an option, so a session inherits the option from the global -options. -It is not possible to unset a global option. +options (or with +.Fl g , +restores a global option to the default). .Pp The .Fl o -flag prevents setting an option that is already set. -.Pp -The +flag prevents setting an option that is already set and .Fl q flag suppresses errors about unknown options. .Pp From dc0d34e13792ff14b5c02fac01af013dfe111c83 Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 4 Jun 2015 20:34:22 +0000 Subject: [PATCH 194/703] tweak SYNOPSIS and usage(); --- tmux.1 | 2 +- tmux.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index e479afbe..fbf0e180 100644 --- a/tmux.1 +++ b/tmux.1 @@ -23,7 +23,7 @@ .Sh SYNOPSIS .Nm tmux .Bk -words -.Op Fl 2lCuv +.Op Fl 2Cluv .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name diff --git a/tmux.c b/tmux.c index 77a39248..878180a7 100644 --- a/tmux.c +++ b/tmux.c @@ -58,7 +58,7 @@ __dead void usage(void) { fprintf(stderr, - "usage: %s [-2lquv] [-c shell-command] [-f file] [-L socket-name]\n" + "usage: %s [-2Cluv] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [command [flags]]\n", __progname); exit(1); From 6b2129696fbefe44d0adce1f2a11b4ae477cd07e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Jun 2015 23:27:51 +0000 Subject: [PATCH 195/703] Move the nested check from client to server and compare the client tty name to all the pane pty names instead of comparing socket paths. This means that "new -d" will work without unsetting $TMUX. --- client.c | 16 +--------------- cmd-attach-session.c | 7 ++++++- cmd-new-session.c | 19 ++++++++++++------- server-client.c | 21 +++++++++++++++++++++ tmux.h | 4 ++-- 5 files changed, 42 insertions(+), 25 deletions(-) diff --git a/client.c b/client.c index e2ffa546..48109613 100644 --- a/client.c +++ b/client.c @@ -222,7 +222,7 @@ client_main(int argc, char **argv, int flags) cmdflags = CMD_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; - cmdflags = CMD_STARTSERVER|CMD_CANTNEST; + cmdflags = CMD_STARTSERVER; } else { msg = MSG_COMMAND; @@ -240,24 +240,10 @@ client_main(int argc, char **argv, int flags) TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) cmdflags |= CMD_STARTSERVER; - if (cmd->entry->flags & CMD_CANTNEST) - cmdflags |= CMD_CANTNEST; } cmd_list_free(cmdlist); } - /* - * Check if this could be a nested session, if the command can't nest: - * if the socket path matches $TMUX, this is probably the same server. - */ - if (shell_cmd == NULL && environ_path != NULL && - (cmdflags & CMD_CANTNEST) && - strcmp(socket_path, environ_path) == 0) { - fprintf(stderr, "sessions should be nested with care, " - "unset $TMUX to force\n"); - return (1); - } - /* Set process title, log and signals now this is the client. */ setproctitle("client (%s)", socket_path); logfile("client"); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 46d568b4..f889019c 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", "c:drt:", 0, 0, "[-dr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - CMD_CANTNEST|CMD_STARTSERVER, + CMD_STARTSERVER, cmd_attach_session_exec }; @@ -81,6 +81,11 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); + if (server_client_check_nested(cmdq->client)) { + cmdq_error(cmdq, "sessions should be nested with care, " + "unset $TMUX to force"); + return (CMD_RETURN_ERROR); + } if (wl != NULL) { if (wp != NULL) diff --git a/cmd-new-session.c b/cmd-new-session.c index 9c767489..0fb849e2 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_new_session_entry = { "[-AdDP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", - CMD_STARTSERVER|CMD_CANTNEST, + CMD_STARTSERVER, cmd_new_session_exec }; @@ -145,15 +145,20 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } /* - * Save the termios settings, part of which is used for new windows in - * this session. + * If this is a new client, check for nesting and save the termios + * settings (part of which is used for new windows in this session). * - * This is read again with tcgetattr() rather than using tty.tio as if - * detached, tty_open won't be called. Because of this, it must be done - * before opening the terminal as that calls tcsetattr() to prepare for - * tmux taking over. + * tcgetattr() is used rather than using tty.tio since if the client is + * detached, tty_open won't be called. It must be done before opening + * the terminal as that calls tcsetattr() to prepare for tmux taking + * over. */ if (!detached && !already_attached && c->tty.fd != -1) { + if (server_client_check_nested(cmdq->client)) { + cmdq_error(cmdq, "sessions should be nested with care, " + "unset $TMUX to force"); + return (CMD_RETURN_ERROR); + } if (tcgetattr(c->tty.fd, &tio) != 0) fatal("tcgetattr failed"); tiop = &tio; diff --git a/server-client.c b/server-client.c index 36408e91..01c8b313 100644 --- a/server-client.c +++ b/server-client.c @@ -46,6 +46,27 @@ void server_client_msg_command(struct client *, struct imsg *); void server_client_msg_identify(struct client *, struct imsg *); void server_client_msg_shell(struct client *); +/* Check if this client is inside this server. */ +int +server_client_check_nested(struct client *c) +{ + struct environ_entry *envent; + struct window_pane *wp; + + if (c->tty.path == NULL) + return (0); + + envent = environ_find(&c->environ, "TMUX"); + if (envent == NULL || *envent->value == '\0') + return (0); + + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + if (strcmp(wp->tty, c->tty.path) == 0) + return (1); + } + return (0); +} + /* Set client key table. */ void server_client_key_table(struct client *c, const char *name) diff --git a/tmux.h b/tmux.h index ae1682af..8406955b 100644 --- a/tmux.h +++ b/tmux.h @@ -1374,8 +1374,7 @@ struct cmd_entry { const char *usage; #define CMD_STARTSERVER 0x1 -#define CMD_CANTNEST 0x2 -#define CMD_READONLY 0x4 +#define CMD_READONLY 0x2 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); @@ -1868,6 +1867,7 @@ void server_update_socket(void); void server_add_accept(int); /* server-client.c */ +int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, int); void server_client_create(int); int server_client_open(struct client *, char **); From 4219939c10980051a6d272853c96b6e1931639b1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 08:14:16 +0000 Subject: [PATCH 196/703] Make it so that if a window or session target is prefixed with an =, only an exact name or index match is accepted, no special character, prefix match, or fnmatch. --- cmd-find.c | 92 ++++++++++++++++++++++++++++++++------------------ key-bindings.c | 20 +++++------ tmux.1 | 11 ++++++ 3 files changed, 80 insertions(+), 43 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 2b862f95..7b4d4c3e 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -30,6 +30,8 @@ #define CMD_FIND_QUIET 0x2 #define CMD_FIND_WINDOW_INDEX 0x4 #define CMD_FIND_DEFAULT_MARKED 0x8 +#define CMD_FIND_EXACT_SESSION 0x10 +#define CMD_FIND_EXACT_WINDOW 0x20 enum cmd_find_type { CMD_FIND_PANE, @@ -381,6 +383,10 @@ cmd_find_get_session(struct cmd_find_state *fs, const char *session) if (fs->s != NULL) return (0); + /* Stop now if exact only. */ + if (fs->flags & CMD_FIND_EXACT_SESSION) + return (-1); + /* Otherwise look for prefix. */ s = NULL; RB_FOREACH(s_loop, sessions, &sessions) { @@ -455,10 +461,11 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) { struct winlink *wl; const char *errstr; - int idx, n; + int idx, n, exact; struct session *s; log_debug("%s: %s", __func__, window); + exact = (fs->flags & CMD_FIND_EXACT_WINDOW); /* Check for window ids starting with @. */ if (*window == '@') { @@ -469,7 +476,7 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) } /* Try as an offset. */ - if (window[0] == '+' || window[0] == '-') { + if (!exact && window[0] == '+' || window[0] == '-') { if (window[1] != '\0') n = strtonum(window + 1, 1, INT_MAX, NULL); else @@ -499,40 +506,44 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) } /* Try special characters. */ - if (strcmp(window, "!") == 0) { - fs->wl = TAILQ_FIRST(&fs->s->lastw); - if (fs->wl == NULL) - return (-1); - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - return (0); - } else if (strcmp(window, "^") == 0) { - fs->wl = RB_MIN(winlinks, &fs->s->windows); - if (fs->wl == NULL) - return (-1); - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - return (0); - } else if (strcmp(window, "$") == 0) { - fs->wl = RB_MAX(winlinks, &fs->s->windows); - if (fs->wl == NULL) - return (-1); - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - return (0); + if (!exact) { + if (strcmp(window, "!") == 0) { + fs->wl = TAILQ_FIRST(&fs->s->lastw); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } else if (strcmp(window, "^") == 0) { + fs->wl = RB_MIN(winlinks, &fs->s->windows); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } else if (strcmp(window, "$") == 0) { + fs->wl = RB_MAX(winlinks, &fs->s->windows); + if (fs->wl == NULL) + return (-1); + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + return (0); + } } /* First see if this is a valid window index in this session. */ - idx = strtonum(window, 0, INT_MAX, &errstr); - if (errstr == NULL) { - if (fs->flags & CMD_FIND_WINDOW_INDEX) { - fs->idx = idx; - return (0); - } - fs->wl = winlink_find_by_index(&fs->s->windows, idx); - if (fs->wl != NULL) { - fs->w = fs->wl->window; - return (0); + if (window[0] != '+' && window[0] != '-') { + idx = strtonum(window, 0, INT_MAX, &errstr); + if (errstr == NULL) { + if (fs->flags & CMD_FIND_WINDOW_INDEX) { + fs->idx = idx; + return (0); + } + fs->wl = winlink_find_by_index(&fs->s->windows, idx); + if (fs->wl != NULL) { + fs->w = fs->wl->window; + return (0); + } } } @@ -551,6 +562,11 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) return (0); } + + /* Stop now if exact only. */ + if (exact) + return (-1); + /* Try as the start of a window name, error if multiple. */ fs->wl = NULL; RB_FOREACH(wl, winlinks, &fs->s->windows) { @@ -868,6 +884,16 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, } } + /* Set exact match flags. */ + if (session != NULL && *session == '=') { + session++; + fs.flags |= CMD_FIND_EXACT_SESSION; + } + if (window != NULL && *window == '=') { + window++; + fs.flags |= CMD_FIND_EXACT_WINDOW; + } + /* Empty is the same as NULL. */ if (session != NULL && *session == '\0') session = NULL; diff --git a/key-bindings.c b/key-bindings.c index ab4751f9..3dc3a054 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -161,16 +161,16 @@ key_bindings_init(void) "bind , command-prompt -I'#W' \"rename-window '%%'\"", "bind - delete-buffer", "bind . command-prompt \"move-window -t '%%'\"", - "bind 0 select-window -t:0", - "bind 1 select-window -t:1", - "bind 2 select-window -t:2", - "bind 3 select-window -t:3", - "bind 4 select-window -t:4", - "bind 5 select-window -t:5", - "bind 6 select-window -t:6", - "bind 7 select-window -t:7", - "bind 8 select-window -t:8", - "bind 9 select-window -t:9", + "bind 0 select-window -t:=0", + "bind 1 select-window -t:=1", + "bind 2 select-window -t:=2", + "bind 3 select-window -t:=3", + "bind 4 select-window -t:=4", + "bind 5 select-window -t:=5", + "bind 6 select-window -t:=6", + "bind 7 select-window -t:=7", + "bind 8 select-window -t:=8", + "bind 9 select-window -t:=9", "bind : command-prompt", "bind \\; last-pane", "bind = choose-buffer", diff --git a/tmux.1 b/tmux.1 index fbf0e180..7ad33530 100644 --- a/tmux.1 +++ b/tmux.1 @@ -404,6 +404,14 @@ An pattern which is matched against the session name. .El .Pp +If the session name is prefixed with a +.Ql = : , +only an exact match is accepted (so +.Ql =mysess +will only match exactly +.Ql mysess , +not +.Ql mysession ) . If a single session is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no @@ -440,6 +448,9 @@ As an pattern matched against the window name. .El .Pp +Like sessions, a +.Ql = +prefix will do an exact match only. An empty window name specifies the next unused index if appropriate (for example the .Ic new-window From 2f586905fc7dc4a3f65a1bca1ad94e28ffc3508d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 09:09:08 +0000 Subject: [PATCH 197/703] Fix a warning. --- cmd-find.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index 7b4d4c3e..5e0b1dbb 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -476,7 +476,7 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) } /* Try as an offset. */ - if (!exact && window[0] == '+' || window[0] == '-') { + if (!exact && (window[0] == '+' || window[0] == '-')) { if (window[1] != '\0') n = strtonum(window + 1, 1, INT_MAX, NULL); else From f7598b8a267eeab926ed0a934f36595077aab605 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Jun 2015 12:44:15 +0100 Subject: [PATCH 198/703] Only need *.ch in compat. --- Makefile.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 0aec7ae4..99163c2c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,12 +6,11 @@ CLEANFILES = tmux.1.mdoc tmux.1.man # Distribution tarball options. EXTRA_DIST = \ - CHANGES FAQ README TODO COPYING examples compat \ + CHANGES FAQ README TODO COPYING examples compat/*.[ch] \ array.h compat.h tmux.h osdep-*.c mdoc2man.awk tmux.1 dist-hook: make clean grep "^#found_debug=" configure - find $(distdir) -name .svn -type d|xargs rm -Rf # Preprocessor flags. CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" From b0782df8a64f744b7c067e6f918ce5217ea09e57 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 15:10:13 +0000 Subject: [PATCH 199/703] Do not use the key variable uninitialized (in a debug log statement), reported by jungleboogie0 at gmail dot com. --- tty-keys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tty-keys.c b/tty-keys.c index 1df22d4c..75e06526 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -501,6 +501,7 @@ tty_keys_next(struct tty *tty) case -1: /* no, or not valid */ break; case -2: /* yes, but we don't care. */ + key = KEYC_MOUSE; goto discard_key; case 1: /* partial */ goto partial_key; From 8c93b768e4864be330c3d6a7962892135224f0f4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 18:01:12 +0000 Subject: [PATCH 200/703] Instead of putting dead clients on a list and checking it every loop, use event_once to queue a callback to deal with them. Also dead clients with references would never actually be freed because the wrap-up functions (the callback for stdin, or status_prompt_clear) would never be called. So call them in server_client_lost. --- cmd-confirm-before.c | 2 +- cmd-load-buffer.c | 2 +- server-client.c | 37 ++++++++++++++++++++++++++++++++++--- server.c | 11 ----------- tmux.h | 1 + 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 5e4816ed..e6104574 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -117,7 +117,7 @@ cmd_confirm_before_free(void *data) struct cmd_confirm_before_data *cdata = data; struct client *c = cdata->client; - c->references--; + server_client_deref(c); free(cdata->cmd); free(cdata); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 3a26db39..8f653929 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -132,7 +132,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) return; c->stdin_callback = NULL; - c->references--; + server_client_deref(c); if (c->flags & CLIENT_DEAD) return; diff --git a/server-client.c b/server-client.c index 01c8b313..b10c1e13 100644 --- a/server-client.c +++ b/server-client.c @@ -31,6 +31,7 @@ #include "tmux.h" void server_client_key_table(struct client *, const char *); +void server_client_free(int, short, void *); void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); int server_client_check_mouse(struct client *); @@ -85,7 +86,7 @@ server_client_create(int fd) setblocking(fd, 0); c = xcalloc(1, sizeof *c); - c->references = 0; + c->references = 1; imsg_init(&c->ibuf, fd); server_update_event(c); @@ -161,6 +162,14 @@ server_client_lost(struct client *c) { struct message_entry *msg, *msg1; + c->flags |= CLIENT_DEAD; + + status_prompt_clear(c); + status_message_clear(c); + + if (c->stdin_callback != NULL) + c->stdin_callback(c, 1, c->stdin_callback_data); + TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %d", c->ibuf.fd); @@ -213,8 +222,7 @@ server_client_lost(struct client *c) if (event_initialized(&c->event)) event_del(&c->event); - TAILQ_INSERT_TAIL(&dead_clients, c, entry); - c->flags |= CLIENT_DEAD; + server_client_deref(c); server_add_accept(0); /* may be more file descriptors now */ @@ -223,6 +231,29 @@ server_client_lost(struct client *c) server_update_socket(); } +/* Remove reference from a client. */ +void +server_client_deref(struct client *c) +{ + log_debug("deref client %d (%d references)", c->ibuf.fd, c->references); + + c->references--; + if (c->references == 0) + event_once(-1, EV_TIMEOUT, server_client_free, c, NULL); +} + +/* Free dead client. */ +void +server_client_free(unused int fd, unused short events, void *arg) +{ + struct client *c = arg; + + log_debug("free client %d (%d references)", c->ibuf.fd, c->references); + + if (c->references == 0) + free(c); +} + /* Process a single client event. */ void server_client_callback(int fd, short events, void *data) diff --git a/server.c b/server.c index 52fdb0c3..cc5a63f6 100644 --- a/server.c +++ b/server.c @@ -41,9 +41,7 @@ * Main server functions. */ -/* Client list. */ struct clients clients; -struct clients dead_clients; int server_fd; int server_shutdown; @@ -205,7 +203,6 @@ server_start(int lockfd, char *lockfile) RB_INIT(&windows); RB_INIT(&all_window_panes); TAILQ_INIT(&clients); - TAILQ_INIT(&dead_clients); RB_INIT(&sessions); RB_INIT(&dead_sessions); TAILQ_INIT(&session_groups); @@ -325,7 +322,6 @@ void server_clean_dead(void) { struct session *s, *s1; - struct client *c, *c1; RB_FOREACH_SAFE(s, sessions, &dead_sessions, s1) { if (s->references != 0) @@ -334,13 +330,6 @@ server_clean_dead(void) free(s->name); free(s); } - - TAILQ_FOREACH_SAFE(c, &dead_clients, entry, c1) { - if (c->references != 0) - continue; - TAILQ_REMOVE(&dead_clients, c, entry); - free(c); - } } /* Update socket execute permissions based on whether sessions are attached. */ diff --git a/tmux.h b/tmux.h index 8406955b..fa36fe6f 100644 --- a/tmux.h +++ b/tmux.h @@ -1871,6 +1871,7 @@ int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, int); void server_client_create(int); int server_client_open(struct client *, char **); +void server_client_deref(struct client *); void server_client_lost(struct client *); void server_client_callback(int, short, void *); void server_client_status_timer(void); From 10e90ae01f53a67a1b7c3a2c498cefb73c6a23b4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 18:06:30 +0000 Subject: [PATCH 201/703] Change deref to the more sensible unref, and add a couple I missed before. --- cfg.c | 2 +- cmd-confirm-before.c | 2 +- cmd-load-buffer.c | 2 +- notify.c | 2 +- server-client.c | 6 +++--- tmux.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cfg.c b/cfg.c index ff43976e..37474094 100644 --- a/cfg.c +++ b/cfg.c @@ -108,7 +108,7 @@ cfg_default_done(unused struct cmd_q *cmdq) */ if (!TAILQ_EMPTY(&cfg_client->cmdq->queue)) cmdq_continue(cfg_client->cmdq); - cfg_client->references--; + server_client_unref(cfg_client); cfg_client = NULL; } } diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index e6104574..248515cd 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -117,7 +117,7 @@ cmd_confirm_before_free(void *data) struct cmd_confirm_before_data *cdata = data; struct client *c = cdata->client; - server_client_deref(c); + server_client_unref(c); free(cdata->cmd); free(cdata); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 8f653929..897807d0 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -132,7 +132,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) return; c->stdin_callback = NULL; - server_client_deref(c); + server_client_unref(c); if (c->flags & CLIENT_DEAD) return; diff --git a/notify.c b/notify.c index 19bf17e8..8dfbd6ba 100644 --- a/notify.c +++ b/notify.c @@ -121,7 +121,7 @@ notify_drain(void) } if (ne->client != NULL) - ne->client->references--; + server_client_unref(ne->client); if (ne->session != NULL) ne->session->references--; if (ne->window != NULL) diff --git a/server-client.c b/server-client.c index b10c1e13..8739b5ab 100644 --- a/server-client.c +++ b/server-client.c @@ -222,7 +222,7 @@ server_client_lost(struct client *c) if (event_initialized(&c->event)) event_del(&c->event); - server_client_deref(c); + server_client_unref(c); server_add_accept(0); /* may be more file descriptors now */ @@ -233,9 +233,9 @@ server_client_lost(struct client *c) /* Remove reference from a client. */ void -server_client_deref(struct client *c) +server_client_unref(struct client *c) { - log_debug("deref client %d (%d references)", c->ibuf.fd, c->references); + log_debug("unref client %d (%d references)", c->ibuf.fd, c->references); c->references--; if (c->references == 0) diff --git a/tmux.h b/tmux.h index fa36fe6f..7068eb44 100644 --- a/tmux.h +++ b/tmux.h @@ -1871,7 +1871,7 @@ int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, int); void server_client_create(int); int server_client_open(struct client *, char **); -void server_client_deref(struct client *); +void server_client_unref(struct client *); void server_client_lost(struct client *); void server_client_callback(int, short, void *); void server_client_status_timer(void); From 641a9cd3f591b0ace3ae9947ebe6ab889b641eed Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 18:18:32 +0000 Subject: [PATCH 202/703] Similarly, for sessions use a callback to free rather than checking every loop. --- notify.c | 2 +- server.c | 19 ------------------- session.c | 31 +++++++++++++++++++++++++++---- tmux.h | 2 +- window-choose.c | 6 +++--- 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/notify.c b/notify.c index 8dfbd6ba..b51a5e59 100644 --- a/notify.c +++ b/notify.c @@ -123,7 +123,7 @@ notify_drain(void) if (ne->client != NULL) server_client_unref(ne->client); if (ne->session != NULL) - ne->session->references--; + session_unref(ne->session); if (ne->window != NULL) window_remove_ref(ne->window); diff --git a/server.c b/server.c index cc5a63f6..001a404c 100644 --- a/server.c +++ b/server.c @@ -58,7 +58,6 @@ int server_create_socket(void); void server_loop(void); int server_should_shutdown(void); void server_send_shutdown(void); -void server_clean_dead(void); void server_accept_callback(int, short, void *); void server_signal_callback(int, short, void *); void server_child_signal(void); @@ -204,7 +203,6 @@ server_start(int lockfd, char *lockfile) RB_INIT(&all_window_panes); TAILQ_INIT(&clients); RB_INIT(&sessions); - RB_INIT(&dead_sessions); TAILQ_INIT(&session_groups); mode_key_init_trees(); key_bindings_init(); @@ -264,8 +262,6 @@ server_loop(void) server_window_loop(); server_client_loop(); - - server_clean_dead(); } } @@ -317,21 +313,6 @@ server_send_shutdown(void) session_destroy(s); } -/* Free dead, unreferenced clients and sessions. */ -void -server_clean_dead(void) -{ - struct session *s, *s1; - - RB_FOREACH_SAFE(s, sessions, &dead_sessions, s1) { - if (s->references != 0) - continue; - RB_REMOVE(sessions, &dead_sessions, s); - free(s->name); - free(s); - } -} - /* Update socket execute permissions based on whether sessions are attached. */ void server_update_socket(void) diff --git a/session.c b/session.c index 907bdee9..5061083a 100644 --- a/session.c +++ b/session.c @@ -27,12 +27,12 @@ #include "tmux.h" -/* Global session list. */ struct sessions sessions; -struct sessions dead_sessions; u_int next_session_id; struct session_groups session_groups; +void session_free(int, short, void *); + struct winlink *session_next_alert(struct winlink *); struct winlink *session_previous_alert(struct winlink *); @@ -109,7 +109,7 @@ session_create(const char *name, int argc, char **argv, const char *path, struct winlink *wl; s = xmalloc(sizeof *s); - s->references = 0; + s->references = 1; s->flags = 0; if (gettimeofday(&s->creation_time, NULL) != 0) @@ -164,6 +164,29 @@ session_create(const char *name, int argc, char **argv, const char *path, return (s); } +/* Remove a reference from a session. */ +void +session_unref(struct session *s) +{ + log_debug("session %s has %d references", s->name, s->references); + + s->references--; + if (s->references == 0) + event_once(-1, EV_TIMEOUT, session_free, s, NULL); +} + +/* Free session. */ +void +session_free(unused int fd, unused short events, void *arg) +{ + struct session *s = arg; + + log_debug("sesson %s freed (%d references)", s->name, s->references); + + if (s->references == 0) + free(s); +} + /* Destroy a session. */ void session_destroy(struct session *s) @@ -191,7 +214,7 @@ session_destroy(struct session *s) close(s->cwd); - RB_INSERT(sessions, &dead_sessions, s); + session_unref(s); } /* Check a session name is valid: not empty and no colons or periods. */ diff --git a/tmux.h b/tmux.h index 7068eb44..699ee81e 100644 --- a/tmux.h +++ b/tmux.h @@ -2259,7 +2259,6 @@ void control_notify_session_close(struct session *); /* session.c */ extern struct sessions sessions; -extern struct sessions dead_sessions; extern struct session_groups session_groups; int session_cmp(struct session *, struct session *); RB_PROTOTYPE(sessions, session, entry, session_cmp); @@ -2271,6 +2270,7 @@ struct session *session_create(const char *, int, char **, const char *, int, struct environ *, struct termios *, int, u_int, u_int, char **); void session_destroy(struct session *); +void session_unref(struct session *); int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); diff --git a/window-choose.c b/window-choose.c index 2af56e23..c71fea3d 100644 --- a/window-choose.c +++ b/window-choose.c @@ -209,11 +209,11 @@ window_choose_data_create(int type, struct client *c, struct session *s) void window_choose_data_free(struct window_choose_data *wcd) { - wcd->start_client->references--; - wcd->start_session->references--; + server_client_unref(wcd->start_client); + session_unref(wcd->start_session); if (wcd->tree_session != NULL) - wcd->tree_session->references--; + session_unref(wcd->tree_session); free(wcd->ft_template); format_free(wcd->ft); From 1cb073d48efa74b2e593a2beb8f56fbda6f9076a Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 22:01:17 +0000 Subject: [PATCH 203/703] Use fixed colour tables rather than generated and do a quick search for exact match before doing the distance comparison. --- colour.c | 377 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 292 insertions(+), 85 deletions(-) diff --git a/colour.c b/colour.c index 82f8533a..5929e982 100644 --- a/colour.c +++ b/colour.c @@ -29,91 +29,306 @@ * of the 256 colour palette. */ -/* An RGB colour. */ struct colour_rgb { + u_char i; u_char r; u_char g; u_char b; }; -/* 256 colour RGB table, generated on first use. */ -struct colour_rgb *colour_rgb_256; +const struct colour_rgb colour_from_256[] = { + { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f }, + { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf }, + { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff }, + { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f }, + { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf }, + { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff }, + { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f }, + { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf }, + { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff }, + { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f }, + { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf }, + { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff }, + { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f }, + { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf }, + { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff }, + { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f }, + { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf }, + { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff }, + { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f }, + { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf }, + { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff }, + { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f }, + { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf }, + { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff }, + { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f }, + { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf }, + { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff }, + { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f }, + { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf }, + { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff }, + { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f }, + { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf }, + { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff }, + { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f }, + { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf }, + { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff }, + { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f }, + { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf }, + { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff }, + { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f }, + { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf }, + { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff }, + { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f }, + { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf }, + { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff }, + { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f }, + { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf }, + { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff }, + { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f }, + { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf }, + { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff }, + { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f }, + { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf }, + { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff }, + { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f }, + { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf }, + { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff }, + { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f }, + { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf }, + { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff }, + { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f }, + { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf }, + { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff }, + { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f }, + { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf }, + { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff }, + { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f }, + { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf }, + { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff }, + { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f }, + { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf }, + { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff }, + { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f }, + { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf }, + { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff }, + { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f }, + { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf }, + { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff }, + { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f }, + { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf }, + { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff }, + { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f }, + { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf }, + { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff }, + { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f }, + { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf }, + { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff }, + { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f }, + { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf }, + { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff }, + { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f }, + { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf }, + { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff }, + { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f }, + { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf }, + { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff }, + { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f }, + { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf }, + { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff }, + { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f }, + { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf }, + { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff }, + { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f }, + { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf }, + { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff }, + { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f }, + { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf }, + { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, + { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 }, + { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 }, + { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a }, + { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e }, + { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 }, + { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 }, + { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a }, + { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e }, + { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 }, + { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 }, + { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda }, + { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee }, +}; +const struct colour_rgb colour_to_256[] = { + { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f }, + { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf }, + { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff }, + { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f }, + { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf }, + { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff }, + { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f }, + { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf }, + { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff }, + { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f }, + { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf }, + { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff }, + { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f }, + { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf }, + { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff }, + { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f }, + { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf }, + { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff }, + { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 }, + { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 }, + { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a }, + { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e }, + { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 }, + { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 }, + { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 }, + { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 }, + { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 }, + { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 }, + { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 }, + { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 }, + { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 }, + { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 }, + { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 }, + { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 }, + { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 }, + { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 }, + { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 }, + { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 }, + { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 }, + { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 }, + { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 }, + { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 }, + { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 }, + { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 }, + { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 }, + { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 }, + { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 }, + { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 }, + { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 }, + { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 }, + { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 }, + { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 }, + { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 }, + { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 }, + { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 }, + { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 }, + { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 }, + { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 }, + { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 }, + { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 }, + { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a }, + { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e }, + { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 }, + { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 }, + { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 }, + { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 }, + { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 }, + { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 }, + { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 }, + { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 }, + { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 }, + { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 }, + { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 }, + { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 }, + { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 }, + { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 }, + { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 }, + { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 }, + { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 }, + { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 }, + { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 }, + { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 }, + { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 }, + { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 }, + { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 }, + { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 }, + { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 }, + { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 }, + { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 }, + { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 }, + { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 }, + { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 }, + { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 }, + { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 }, + { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 }, + { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 }, + { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 }, + { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 }, + { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 }, + { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 }, + { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda }, + { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee }, + { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f }, + { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf }, + { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff }, + { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f }, + { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf }, + { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff }, + { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f }, + { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf }, + { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff }, + { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f }, + { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf }, + { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff }, + { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f }, + { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf }, + { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff }, + { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f }, + { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf }, + { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, +}; -void colour_rgb_generate256(void); -u_int colour_rgb_distance(struct colour_rgb *, struct colour_rgb *); +int colour_rgb_cmp(const void *, const void *); int colour_rgb_find(struct colour_rgb *); -/* Generate 256 colour RGB table. */ -void -colour_rgb_generate256(void) +/* Compare function for bsearch(). */ +int +colour_rgb_cmp(const void *lhs0, const void *rhs0) { - struct colour_rgb *rgb; - u_int i, r, g, b; + const struct colour_rgb *lhs = lhs0, *rhs = rhs0; - /* - * Allocate the table. The first 16 colours are often changed by users - * and terminals so don't include them. - */ - colour_rgb_256 = xcalloc(240, sizeof *colour_rgb_256); + if (lhs->r < rhs->r) + return (-1); + if (lhs->r > rhs->r) + return (1); - /* Add the colours first. */ - r = g = b = 0; - for (i = 240; i > 24; i--) { - rgb = &colour_rgb_256[240 - i]; + if (lhs->g < rhs->g) + return (-1); + if (lhs->g > rhs->g) + return (1); - if (r != 0) - rgb->r = (r * 40) + 55; - if (g != 0) - rgb->g = (g * 40) + 55; - if (b != 0) - rgb->b = (b * 40) + 55; + if (lhs->b < rhs->b) + return (-1); + if (lhs->b > rhs->b) + return (1); - b++; - if (b > 5) { - b = 0; - g++; - } - if (g > 5) { - g = 0; - r++; - } - } - - /* Then add the greys. */ - for (i = 24; i > 0; i--) { - rgb = &colour_rgb_256[240 - i]; - - rgb->r = 8 + (24 - i) * 10; - rgb->g = 8 + (24 - i) * 10; - rgb->b = 8 + (24 - i) * 10; - } -} - -/* Get colour RGB distance. */ -u_int -colour_rgb_distance(struct colour_rgb *rgb1, struct colour_rgb *rgb2) -{ - int r, g, b; - - r = rgb1->r - rgb2->r; - g = rgb1->g - rgb2->g; - b = rgb1->b - rgb2->b; - return (r * r + g * g + b * b); + return (0); } /* Work out the nearest colour from the 256 colour set. */ int colour_rgb_find(struct colour_rgb *rgb) { - u_int distance, lowest, colour, i; + struct colour_rgb *found; + u_int distance, lowest, colour, i; + int r, g, b; - if (colour_rgb_256 == NULL) - colour_rgb_generate256(); + found = bsearch(rgb, colour_to_256, nitems(colour_to_256), + sizeof colour_to_256[0], colour_rgb_cmp); + if (found != NULL) + return (16 + found->i); colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { - distance = colour_rgb_distance(&colour_rgb_256[i], rgb); + r = colour_from_256[i].r - rgb->r; + g = colour_from_256[i].g - rgb->g; + b = colour_from_256[i].b - rgb->b; + + distance = r * r + g * g + b * b; if (distance < lowest) { lowest = distance; colour = 16 + i; @@ -217,47 +432,39 @@ colour_fromstring(const char *s) return (n | 0x100); } - if (strcasecmp(s, "black") == 0 || (s[0] == '0' && s[1] == '\0')) + if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0) return (0); - if (strcasecmp(s, "red") == 0 || (s[0] == '1' && s[1] == '\0')) + if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0) return (1); - if (strcasecmp(s, "green") == 0 || (s[0] == '2' && s[1] == '\0')) + if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0) return (2); - if (strcasecmp(s, "yellow") == 0 || (s[0] == '3' && s[1] == '\0')) + if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0) return (3); - if (strcasecmp(s, "blue") == 0 || (s[0] == '4' && s[1] == '\0')) + if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0) return (4); - if (strcasecmp(s, "magenta") == 0 || (s[0] == '5' && s[1] == '\0')) + if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0) return (5); - if (strcasecmp(s, "cyan") == 0 || (s[0] == '6' && s[1] == '\0')) + if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0) return (6); - if (strcasecmp(s, "white") == 0 || (s[0] == '7' && s[1] == '\0')) + if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0) return (7); - if (strcasecmp(s, "default") == 0 || (s[0] == '8' && s[1] == '\0')) + if (strcasecmp(s, "default") == 0 || strcmp(s, "8") == 0) return (8); - if (strcasecmp(s, "brightblack") == 0 || - (s[0] == '9' && s[1] == '0' && s[2] == '\0')) + if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0) return (90); - if (strcasecmp(s, "brightred") == 0 || - (s[0] == '9' && s[1] == '1' && s[2] == '\0')) + if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0) return (91); - if (strcasecmp(s, "brightgreen") == 0 || - (s[0] == '9' && s[1] == '2' && s[2] == '\0')) + if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0) return (92); - if (strcasecmp(s, "brightyellow") == 0 || - (s[0] == '9' && s[1] == '3' && s[2] == '\0')) + if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0) return (93); - if (strcasecmp(s, "brightblue") == 0 || - (s[0] == '9' && s[1] == '4' && s[2] == '\0')) + if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0) return (94); - if (strcasecmp(s, "brightmagenta") == 0 || - (s[0] == '9' && s[1] == '5' && s[2] == '\0')) + if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0) return (95); - if (strcasecmp(s, "brightcyan") == 0 || - (s[0] == '9' && s[1] == '6' && s[2] == '\0')) + if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0) return (96); - if (strcasecmp(s, "brightwhite") == 0 || - (s[0] == '9' && s[1] == '7' && s[2] == '\0')) + if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) return (97); return (-1); } From 55b96a5bd5786f162b258c58627deb8e829cabd7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 22:33:39 +0000 Subject: [PATCH 204/703] Handle the RGB colour escape sequence (\033[38;2;;;m and 48;2) like xterm(1) does, by mapping to the nearest in the 256 colour palette. --- colour.c | 34 ++++++++++----------- input.c | 91 ++++++++++++++++++++++++++++++++++++++++++-------------- tmux.h | 1 + 3 files changed, 85 insertions(+), 41 deletions(-) diff --git a/colour.c b/colour.c index 5929e982..ac4b72fa 100644 --- a/colour.c +++ b/colour.c @@ -281,12 +281,11 @@ const struct colour_rgb colour_to_256[] = { { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, }; -int colour_rgb_cmp(const void *, const void *); -int colour_rgb_find(struct colour_rgb *); +int colour_cmp_rgb(const void *, const void *); /* Compare function for bsearch(). */ int -colour_rgb_cmp(const void *lhs0, const void *rhs0) +colour_cmp_rgb(const void *lhs0, const void *rhs0) { const struct colour_rgb *lhs = lhs0, *rhs = rhs0; @@ -310,23 +309,22 @@ colour_rgb_cmp(const void *lhs0, const void *rhs0) /* Work out the nearest colour from the 256 colour set. */ int -colour_rgb_find(struct colour_rgb *rgb) +colour_find_rgb(u_char r, u_char g, u_char b) { - struct colour_rgb *found; - u_int distance, lowest, colour, i; - int r, g, b; + struct colour_rgb rgb = { .r = r, .g = g, .b = b }, *found; + u_int distance, lowest, colour, i; - found = bsearch(rgb, colour_to_256, nitems(colour_to_256), - sizeof colour_to_256[0], colour_rgb_cmp); + found = bsearch(&rgb, colour_to_256, nitems(colour_to_256), + sizeof colour_to_256[0], colour_cmp_rgb); if (found != NULL) return (16 + found->i); colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { - r = colour_from_256[i].r - rgb->r; - g = colour_from_256[i].g - rgb->g; - b = colour_from_256[i].b - rgb->b; + r = colour_from_256[i].r - rgb.r; + g = colour_from_256[i].g - rgb.g; + b = colour_from_256[i].b - rgb.b; distance = r * r + g * g + b * b; if (distance < lowest) { @@ -409,20 +407,20 @@ colour_tostring(int c) int colour_fromstring(const char *s) { - const char *errstr; - const char *cp; - struct colour_rgb rgb; - int n; + const char *errstr; + const char *cp; + int n; + u_char r, g, b; if (*s == '#' && strlen(s) == 7) { for (cp = s + 1; isxdigit((u_char) *cp); cp++) ; if (*cp != '\0') return (-1); - n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &rgb.r, &rgb.g, &rgb.b); + n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b); if (n != 3) return (-1); - return (colour_rgb_find(&rgb) | 0x100); + return (colour_find_rgb(r, g, b) | 0x100); } if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { diff --git a/input.c b/input.c index 41c0bc85..7a371c62 100644 --- a/input.c +++ b/input.c @@ -126,6 +126,8 @@ void input_csi_dispatch_rm_private(struct input_ctx *); void input_csi_dispatch_sm(struct input_ctx *); void input_csi_dispatch_sm_private(struct input_ctx *); void input_csi_dispatch_winops(struct input_ctx *); +void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *); +void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); void input_csi_dispatch_sgr(struct input_ctx *); int input_dcs_dispatch(struct input_ctx *); int input_utf8_open(struct input_ctx *); @@ -1609,13 +1611,71 @@ input_csi_dispatch_winops(struct input_ctx *ictx) } } +/* Handle CSI SGR for 256 colours. */ +void +input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) +{ + struct grid_cell *gc = &ictx->cell.cell; + int c; + + (*i)++; + c = input_get(ictx, *i, 0, -1); + if (c == -1) { + if (fgbg == 38) { + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = 8; + } else if (fgbg == 48) { + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = 8; + } + } else { + if (fgbg == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = c; + } else if (fgbg == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = c; + } + } +} + +/* Handle CSI SGR for RGB colours. */ +void +input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) +{ + struct grid_cell *gc = &ictx->cell.cell; + int c, r, g, b; + + (*i)++; + r = input_get(ictx, *i, 0, -1); + if (r == -1 || r > 255) + return; + (*i)++; + g = input_get(ictx, *i, 0, -1); + if (g == -1 || g > 255) + return; + (*i)++; + b = input_get(ictx, *i, 0, -1); + if (b == -1 || b > 255) + return; + + c = colour_find_rgb(r, g, b); + if (fgbg == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = c; + } else if (fgbg == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = c; + } +} + /* Handle CSI SGR. */ void input_csi_dispatch_sgr(struct input_ctx *ictx) { struct grid_cell *gc = &ictx->cell.cell; u_int i; - int n, m; + int n; if (ictx->param_list_len == 0) { memcpy(gc, &grid_default_cell, sizeof *gc); @@ -1627,28 +1687,13 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) if (n == 38 || n == 48) { i++; - if (input_get(ictx, i, 0, -1) != 5) - continue; - - i++; - m = input_get(ictx, i, 0, -1); - if (m == -1) { - if (n == 38) { - gc->flags &= ~GRID_FLAG_FG256; - gc->fg = 8; - } else if (n == 48) { - gc->flags &= ~GRID_FLAG_BG256; - gc->bg = 8; - } - - } else { - if (n == 38) { - gc->flags |= GRID_FLAG_FG256; - gc->fg = m; - } else if (n == 48) { - gc->flags |= GRID_FLAG_BG256; - gc->bg = m; - } + switch (input_get(ictx, i, 0, -1)) { + case 2: + input_csi_dispatch_sgr_rgb(ictx, n, &i); + break; + case 5: + input_csi_dispatch_sgr_256(ictx, n, &i); + break; } continue; } diff --git a/tmux.h b/tmux.h index 699ee81e..4ddf5c4a 100644 --- a/tmux.h +++ b/tmux.h @@ -1953,6 +1953,7 @@ char *xterm_keys_lookup(int); int xterm_keys_find(const char *, size_t, size_t *, int *); /* colour.c */ +int colour_find_rgb(u_char, u_char, u_char); void colour_set_fg(struct grid_cell *, int); void colour_set_bg(struct grid_cell *, int); const char *colour_tostring(int); From ed6c036ee3192029e9d0a60e8f9bb2a4ccfb99bf Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Jun 2015 22:50:27 +0000 Subject: [PATCH 205/703] Use ints for the calculations rather than u_char, they could end up signed. --- colour.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/colour.c b/colour.c index ac4b72fa..a56ddce9 100644 --- a/colour.c +++ b/colour.c @@ -313,6 +313,7 @@ colour_find_rgb(u_char r, u_char g, u_char b) { struct colour_rgb rgb = { .r = r, .g = g, .b = b }, *found; u_int distance, lowest, colour, i; + int dr, dg, db; found = bsearch(&rgb, colour_to_256, nitems(colour_to_256), sizeof colour_to_256[0], colour_cmp_rgb); @@ -322,11 +323,11 @@ colour_find_rgb(u_char r, u_char g, u_char b) colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { - r = colour_from_256[i].r - rgb.r; - g = colour_from_256[i].g - rgb.g; - b = colour_from_256[i].b - rgb.b; + dr = (int)colour_from_256[i].r - r; + dg = (int)colour_from_256[i].g - g; + db = (int)colour_from_256[i].b - b; - distance = r * r + g * g + b * b; + distance = dr * dr + dg * dg + db * db; if (distance < lowest) { lowest = distance; colour = 16 + i; From a5c55e439383202547e442f6afc0c8c664687728 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 7 Jun 2015 08:36:03 +0100 Subject: [PATCH 206/703] Update TODO. --- TODO | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO b/TODO index 4a3ec3cd..ce7b1f15 100644 --- a/TODO +++ b/TODO @@ -6,8 +6,6 @@ * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions - * exact match operator for targets (or break the substring match - and require eg x* instead of just x) - make command sequences more usable * don't require space after ; From c4e811e51936edab66803a7b9e099ac135e6e19b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 7 Jun 2015 21:39:39 +0000 Subject: [PATCH 207/703] Add -E flag when attaching or switching client to bypass update-environment, from Steven Lu. --- cmd-attach-session.c | 50 ++++++++++++++++++++++++-------------------- cmd-new-session.c | 13 +++++++----- cmd-switch-client.c | 6 +++--- tmux.1 | 25 +++++++++++++++++++--- tmux.h | 2 +- 5 files changed, 61 insertions(+), 35 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index f889019c..b2a2f8c5 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -34,18 +34,18 @@ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", - "c:drt:", 0, 0, - "[-dr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + "c:dErt:", 0, 0, + "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, CMD_STARTSERVER, cmd_attach_session_exec }; enum cmd_retval cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, - const char *cflag) + const char *cflag, int Eflag) { struct session *s; - struct client *c; + struct client *c = cmdq->client, *c_loop; struct winlink *wl = NULL; struct window *w = NULL; struct window_pane *wp = NULL; @@ -79,9 +79,9 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, wl = winlink_find_by_window(&s->windows, w); } - if (cmdq->client == NULL) + if (c == NULL) return (CMD_RETURN_NORMAL); - if (server_client_check_nested(cmdq->client)) { + if (server_client_check_nested(c)) { cmdq_error(cmdq, "sessions should be nested with care, " "unset $TMUX to force"); return (CMD_RETURN_ERROR); @@ -93,18 +93,18 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, session_set_current(s, wl); } - if (cmdq->client->session != NULL) { + if (c->session != NULL) { if (dflag) { /* * Can't use server_write_session in case attaching to * the same session as currently attached to. */ - TAILQ_FOREACH(c, &clients, entry) { - if (c->session != s || c == cmdq->client) + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session != s || c == c) continue; server_write_client(c, MSG_DETACH, - c->session->name, - strlen(c->session->name) + 1); + c_loop->session->name, + strlen(c_loop->session->name) + 1); } } @@ -126,13 +126,13 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, s->cwd = fd; } - cmdq->client->session = s; - notify_attached_session_changed(cmdq->client); + c->session = s; + notify_attached_session_changed(c); session_update_activity(s); - server_redraw_client(cmdq->client); + server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { - if (server_client_open(cmdq->client, &cause) != 0) { + if (server_client_open(c, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); @@ -157,23 +157,26 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (rflag) - cmdq->client->flags |= CLIENT_READONLY; + c->flags |= CLIENT_READONLY; if (dflag) { server_write_session(s, MSG_DETACH, s->name, strlen(s->name) + 1); } - update = options_get_string(&s->options, "update-environment"); - environ_update(update, &cmdq->client->environ, &s->environ); + if (!Eflag) { + update = options_get_string(&s->options, + "update-environment"); + environ_update(update, &c->environ, &s->environ); + } - cmdq->client->session = s; - notify_attached_session_changed(cmdq->client); + c->session = s; + notify_attached_session_changed(c); session_update_activity(s); - server_redraw_client(cmdq->client); + server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; - server_write_ready(cmdq->client); + server_write_ready(c); cmdq->client_exit = 0; } recalculate_sizes(); @@ -188,5 +191,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; return (cmd_attach_session(cmdq, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'))); + args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'), + args_has(args, 'E'))); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 0fb849e2..1533e5f0 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -37,8 +37,8 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { "new-session", "new", - "Ac:dDF:n:Ps:t:x:y:", 0, -1, - "[-AdDP] [-c start-directory] [-F format] [-n window-name] " + "Ac:dDEF:n:Ps:t:x:y:", 0, -1, + "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", CMD_STARTSERVER, @@ -91,7 +91,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (session_find(newname) != NULL) { if (args_has(args, 'A')) { return (cmd_attach_session(cmdq, newname, - args_has(args, 'D'), 0, NULL)); + args_has(args, 'D'), 0, NULL, + args_has(args, 'E'))); } cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); @@ -230,9 +231,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Construct the environment. */ environ_init(&env); - update = options_get_string(&global_s_options, "update-environment"); - if (c != NULL) + if (c != NULL && !args_has(args, 'E')) { + update = options_get_string(&global_s_options, + "update-environment"); environ_update(update, &c->environ, &env); + } /* Create the new session. */ idx = -1 - options_get_number(&global_s_options, "base-index"); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 369fc917..23751d73 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { "switch-client", "switchc", - "lc:npt:rT:", 0, 0, - "[-lnpr] [-c target-client] [-t target-session] [-T key-table]", + "lc:Enpt:rT:", 0, 0, + "[-Elnpr] [-c target-client] [-t target-session] [-T key-table]", CMD_READONLY, cmd_switch_client_exec }; @@ -119,7 +119,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } } - if (c != NULL && s != c->session) { + if (c != NULL && s != c->session && !args_has(args, 'E')) { update = options_get_string(&s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } diff --git a/tmux.1 b/tmux.1 index 7ad33530..16e979ad 100644 --- a/tmux.1 +++ b/tmux.1 @@ -670,7 +670,7 @@ section. The following commands are available to manage clients and sessions: .Bl -tag -width Ds .It Xo Ic attach-session -.Op Fl dr +.Op Fl dEr .Op Fl c Ar working-directory .Op Fl t Ar target-session .Xc @@ -709,6 +709,12 @@ session. .Fl c will set the session working directory (used for new windows) to .Ar working-directory . +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. .It Xo Ic detach-client .Op Fl P .Op Fl a @@ -783,7 +789,7 @@ command. Lock all clients attached to .Ar target-session . .It Xo Ic new-session -.Op Fl AdDP +.Op Fl AdDEP .Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name @@ -858,6 +864,13 @@ By default, it uses the format .Ql #{session_name}: but a different format may be specified with .Fl F . +.Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. +.Ic update-environment . .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client @@ -912,7 +925,7 @@ Suspend a client by sending .Dv SIGTSTP (tty stop). .It Xo Ic switch-client -.Op Fl lnpr +.Op Fl Elnpr .Op Fl c Ar target-client .Op Fl t Ar target-session .Op Fl T Ar key-table @@ -934,6 +947,12 @@ toggles whether a client is read-only (see the .Ic attach-session command). .Pp +If +.Fl E +is used, +.Ic update-environment +option will not be applied. +.Pp .Fl T sets the client's key table; the next key from the client will be interpreted from .Ar key-table . diff --git a/tmux.h b/tmux.h index 4ddf5c4a..265fdc40 100644 --- a/tmux.h +++ b/tmux.h @@ -1801,7 +1801,7 @@ extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, - const char *); + const char *, int); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); From 02a848d77c7f41acecccd58d7819a562eef7c704 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 8 Jun 2015 09:46:14 +0100 Subject: [PATCH 208/703] It isn't supposed to... --- TODO | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO b/TODO index ce7b1f15..6f8d732c 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ - command bits and pieces: - * why doesn't command-prompt work if made read-only? * allow multiple targets: fnmatch for -t/-c, for example detach all clients with -t* * add -c for new-session like new-window From a412dd616f07167610e3290e1cfc0e40eb9ec664 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 9 Jun 2015 07:07:06 +0000 Subject: [PATCH 209/703] Fix loop comparison broken in last commit, from Thomas Adam. --- cmd-attach-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index b2a2f8c5..cb58d4e2 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -100,7 +100,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, * the same session as currently attached to. */ TAILQ_FOREACH(c_loop, &clients, entry) { - if (c_loop->session != s || c == c) + if (c_loop->session != s || c == c_loop) continue; server_write_client(c, MSG_DETACH, c_loop->session->name, From bbc0898060a02515461cbd90d7af35bf91d9cb3d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 10 Jun 2015 12:56:04 +0000 Subject: [PATCH 210/703] wp->tty is a char [] not a char * so it can't be NULL. From Thomas Adam. --- format.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/format.c b/format.c index 72d6bfc9..eaa979da 100644 --- a/format.c +++ b/format.c @@ -872,8 +872,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_synchronized", "%d", !!options_get_number(&wp->window->options, "synchronize-panes")); - if (wp->tty != NULL) - format_add(ft, "pane_tty", "%s", wp->tty); + format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); if ((cmd = cmd_stringify_argv(wp->argc, wp->argv)) != NULL) { format_add(ft, "pane_start_command", "%s", cmd); From dca084e703ce54c500e4af7ddc1e0c2b65d4d403 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 10 Jun 2015 15:39:23 +0100 Subject: [PATCH 211/703] Don't leak dotfd if fchdir fails. From ettl dot martin78 at gmail dot com. --- compat/openat.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compat/openat.c b/compat/openat.c index 5cd9e551..6b04eedc 100644 --- a/compat/openat.c +++ b/compat/openat.c @@ -40,8 +40,12 @@ openat(int fd, const char *path, int flags, ...) dotfd = open(".", O_RDONLY); if (dotfd == -1) return (-1); - if (fchdir(fd) != 0) + if (fchdir(fd) != 0) { + saved_errno = errno; + close(dotfd); + errno = saved_errno; return (-1); + } } retval = open(path, flags, mode); From 29c29e771767b037f2929b889bb0de2b0b6ee138 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 14 Jun 2015 10:07:44 +0000 Subject: [PATCH 212/703] Add a format for client PID (client_pid) and server PID (pid). Diff for client_pid from Thomas Adam. --- client.c | 8 ++++++-- format.c | 2 ++ server-client.c | 6 ++++++ tmux.1 | 2 ++ tmux.h | 2 ++ 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 48109613..4b7bbb1f 100644 --- a/client.c +++ b/client.c @@ -348,9 +348,10 @@ client_main(int argc, char **argv, int flags) void client_send_identify(int flags) { - const char *s; + const char *s; char **ss; - int fd; + int fd; + pid_t pid; client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); @@ -370,6 +371,9 @@ client_send_identify(int flags) fatal("dup failed"); client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + pid = getpid(); + client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); + for (ss = environ; *ss != NULL; ss++) client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1); diff --git a/format.c b/format.c index eaa979da..49cf7506 100644 --- a/format.c +++ b/format.c @@ -271,6 +271,7 @@ format_create_status(int status) *ptr = '\0'; format_add(ft, "host_short", "%s", host); } + format_add(ft, "pid", "%ld", (long) getpid()); return (ft); } @@ -703,6 +704,7 @@ format_defaults_client(struct format_tree *ft, struct client *c) if (ft->s == NULL) ft->s = c->session; + format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); if (c->tty.path != NULL) diff --git a/server-client.c b/server-client.c index 8739b5ab..9beef4bc 100644 --- a/server-client.c +++ b/server-client.c @@ -1044,6 +1044,7 @@ server_client_msg_dispatch(struct client *c) case MSG_IDENTIFY_CWD: case MSG_IDENTIFY_STDIN: case MSG_IDENTIFY_ENVIRON: + case MSG_IDENTIFY_CLIENTPID: case MSG_IDENTIFY_DONE: server_client_msg_identify(c, &imsg); break; @@ -1218,6 +1219,11 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (strchr(data, '=') != NULL) environ_put(&c->environ, data); break; + case MSG_IDENTIFY_CLIENTPID: + if (datalen != sizeof c->pid) + fatalx("bad MSG_IDENTIFY_CLIENTPID size"); + memcpy(&c->pid, data, sizeof c->pid); + break; default: break; } diff --git a/tmux.1 b/tmux.1 index 16e979ad..70b43a7a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3353,6 +3353,7 @@ The following variables are available, where appropriate: .It Li "client_created_string" Ta "" Ta "String time client created" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" +.It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" @@ -3396,6 +3397,7 @@ The following variables are available, where appropriate: .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" +.It Li "pid" Ta "" Ta "Server PID" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" diff --git a/tmux.h b/tmux.h index 265fdc40..dfd6bf49 100644 --- a/tmux.h +++ b/tmux.h @@ -425,6 +425,7 @@ enum msgtype { MSG_IDENTIFY_STDIN, MSG_IDENTIFY_ENVIRON, MSG_IDENTIFY_DONE, + MSG_IDENTIFY_CLIENTPID, MSG_COMMAND = 200, MSG_DETACH, @@ -1206,6 +1207,7 @@ RB_HEAD(status_out_tree, status_out); struct client { struct imsgbuf ibuf; + pid_t pid; int fd; struct event event; int retval; From d96ab3401960ab4a7c9434dfda1ebdc5204873e0 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 15 Jun 2015 10:58:01 +0000 Subject: [PATCH 213/703] Add window_activity format, from Thomas Adam based on a diff originally from propos6 at gmail dot com. --- format.c | 5 +++++ input.c | 4 ++++ tmux.1 | 2 ++ tmux.h | 1 + window.c | 3 +++ 5 files changed, 15 insertions(+) diff --git a/format.c b/format.c index 49cf7506..67bce925 100644 --- a/format.c +++ b/format.c @@ -749,6 +749,7 @@ void format_defaults_window(struct format_tree *ft, struct window *w) { char *layout; + time_t t; ft->w = w; @@ -757,6 +758,10 @@ format_defaults_window(struct format_tree *ft, struct window *w) else layout = layout_dump(w->layout_root); + t = w->activity_time.tv_sec; + format_add(ft, "window_activity", "%lld", (long long) t); + format_add(ft, "window_activity_string", "%s", format_time_string(t)); + format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); diff --git a/input.c b/input.c index 7a371c62..d1ff17fe 100644 --- a/input.c +++ b/input.c @@ -20,6 +20,7 @@ #include #include +#include #include "tmux.h" @@ -849,6 +850,9 @@ input_parse(struct window_pane *wp) wp->window->flags |= WINDOW_ACTIVITY; wp->window->flags &= ~WINDOW_SILENCE; + if (gettimeofday(&wp->window->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + /* * Open the screen. Use NULL wp if there is a mode set as don't want to * update the tty. diff --git a/tmux.1 b/tmux.1 index 70b43a7a..400378fc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3414,6 +3414,8 @@ The following variables are available, where appropriate: .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "window_activity" Ta "" Ta "Integer time of window last activity" +.It Li "window_activity_string" Ta "" Ta "String time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" diff --git a/tmux.h b/tmux.h index dfd6bf49..da91d4db 100644 --- a/tmux.h +++ b/tmux.h @@ -892,6 +892,7 @@ struct window { char *name; struct event name_timer; struct timeval silence_timer; + struct timeval activity_time; struct window_pane *active; struct window_pane *last; diff --git a/window.c b/window.c index a1d6792b..4639944e 100644 --- a/window.c +++ b/window.c @@ -295,6 +295,9 @@ window_create1(u_int sx, u_int sy) w->sx = sx; w->sy = sy; + if (gettimeofday(&w->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + options_init(&w->options, &global_w_options); if (options_get_number(&w->options, "automatic-rename")) queue_window_name(w); From 021cdbe1c0111951a1f63b09b41c9e3db3793db5 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jun 2015 16:44:49 +0000 Subject: [PATCH 214/703] Use an explicit job state instead of avoid closing our side of the socketpair and setting it to -1 to mark when the other side is closed. This avoids closing it while the libevent bufferevent still has it (it could try to add it to the polled set which some mechanisms don't like). Fixes part a problem reported by Bruno Sutic. --- job.c | 13 ++++++++----- tmux.h | 6 ++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/job.c b/job.c index 5675eec3..0b348eda 100644 --- a/job.c +++ b/job.c @@ -100,6 +100,8 @@ job_run(const char *cmd, struct session *s, int cwd, close(out[1]); job = xmalloc(sizeof *job); + job->state = JOB_RUNNING; + job->cmd = xstrdup(cmd); job->pid = pid; job->status = 0; @@ -167,14 +169,13 @@ job_callback(unused struct bufferevent *bufev, unused short events, void *data) log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); - if (job->pid == -1) { + if (job->state == JOB_DEAD) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); } else { bufferevent_disable(job->event, EV_READ); - close(job->fd); - job->fd = -1; + job->state = JOB_CLOSED; } } @@ -186,10 +187,12 @@ job_died(struct job *job, int status) job->status = status; - if (job->fd == -1) { + if (job->state == JOB_CLOSED) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); - } else + } else { job->pid = -1; + job->state = JOB_DEAD; + } } diff --git a/tmux.h b/tmux.h index da91d4db..ff0b4c84 100644 --- a/tmux.h +++ b/tmux.h @@ -714,6 +714,12 @@ struct options { /* Scheduled job. */ struct job { + enum { + JOB_RUNNING, + JOB_DEAD, + JOB_CLOSED + } state; + char *cmd; pid_t pid; int status; From 0ff335961eec019d776f19bd8c26cce7cde0effa Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jun 2015 16:50:28 +0000 Subject: [PATCH 215/703] Move the shuffle code from new-window -a into a function and add a -a flag for move-window too. From Thomas Adam. --- cmd-move-window.c | 13 +++++++++++-- cmd-new-window.c | 18 ++---------------- tmux.1 | 8 ++++++-- tmux.h | 1 + window.c | 25 +++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/cmd-move-window.c b/cmd-move-window.c index e765b625..b15df4f6 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -30,7 +30,7 @@ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { "move-window", "movew", - "dkrs:t:", 0, 0, + "adkrs:t:", 0, 0, "[-dkr] " CMD_SRCDST_WINDOW_USAGE, 0, cmd_move_window_exec @@ -38,7 +38,7 @@ const struct cmd_entry cmd_move_window_entry = { const struct cmd_entry cmd_link_window_entry = { "link-window", "linkw", - "dks:t:", 0, 0, + "adks:t:", 0, 0, "[-dk] " CMD_SRCDST_WINDOW_USAGE, 0, cmd_move_window_exec @@ -72,6 +72,15 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); sflag = args_has(self->args, 's'); + + if (args_has(self->args, 'a')) { + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) + return (CMD_RETURN_ERROR); + if ((idx = winlink_shuffle_up(s, s->curw)) == -1) + return (CMD_RETURN_ERROR); + } + if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(cmdq, "can't link window: %s", cause); diff --git a/cmd-new-window.c b/cmd-new-window.c index c05a0ce8..9cead449 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -51,7 +51,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl; const char *cmd, *path, *template; char **argv, *cause, *cp; - int argc, idx, last, detached, cwd, fd = -1; + int argc, idx, detached, cwd, fd = -1; struct format_tree *ft; struct environ_entry *envent; @@ -59,24 +59,10 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); - idx = wl->idx + 1; - - /* Find the next free index. */ - for (last = idx; last < INT_MAX; last++) { - if (winlink_find_by_index(&s->windows, last) == NULL) - break; - } - if (last == INT_MAX) { + if ((idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } - - /* Move everything from last - 1 to idx up a bit. */ - for (; last > idx; last--) { - wl = winlink_find_by_index(&s->windows, last - 1); - server_link_window(s, wl, s, last, 0, 0, NULL); - server_unlink_window(s, wl); - } } else { idx = cmd_find_index(cmdq, args_get(args, 't'), &s); if (idx == -2) diff --git a/tmux.1 b/tmux.1 index 400378fc..fb014898 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1544,7 +1544,7 @@ If no .Ar target-session is specified, select the last window of the current session. .It Xo Ic link-window -.Op Fl dk +.Op Fl adk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc @@ -1558,6 +1558,10 @@ If is specified and no such window exists, the .Ar src-window is linked there. +With +.Fl a , +the window is moved to the next index up (following windows +are moved if necessary). If .Fl k is given and @@ -1623,7 +1627,7 @@ and .Ar dst-pane may belong to the same window. .It Xo Ic move-window -.Op Fl rdk +.Op Fl ardk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc diff --git a/tmux.h b/tmux.h index ff0b4c84..663832e5 100644 --- a/tmux.h +++ b/tmux.h @@ -2166,6 +2166,7 @@ struct window_pane *window_pane_find_right(struct window_pane *); void window_set_name(struct window *, const char *); void window_remove_ref(struct window *); void winlink_clear_flags(struct winlink *); +int winlink_shuffle_up(struct session *, struct winlink *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); diff --git a/window.c b/window.c index 4639944e..82c10606 100644 --- a/window.c +++ b/window.c @@ -1378,3 +1378,28 @@ winlink_clear_flags(struct winlink *wl) } } } + +int +winlink_shuffle_up(struct session *s, struct winlink *wl) +{ + int idx, last; + + idx = wl->idx + 1; + + /* Find the next free index. */ + for (last = idx; last < INT_MAX; last++) { + if (winlink_find_by_index(&s->windows, last) == NULL) + break; + } + if (last == INT_MAX) + return (-1); + + /* Move everything from last - 1 to idx up a bit. */ + for (; last > idx; last--) { + wl = winlink_find_by_index(&s->windows, last - 1); + server_link_window(s, wl, s, last, 0, 0, NULL); + server_unlink_window(s, wl); + } + + return (idx); +} From 84f0622c852761a2d6688e944bc01a03af78c52a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jun 2015 17:02:15 +0000 Subject: [PATCH 216/703] Break cmdq_continue inner loop into a helper function. --- cmd-queue.c | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 61b14147..af987f59 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,6 +25,8 @@ #include "tmux.h" +enum cmd_retval cmdq_continue_one(struct cmd_q *); + /* Create new command queue. */ struct cmd_q * cmdq_new(struct client *c) @@ -160,14 +162,39 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) item->mouse.valid = 0; } +/* Process one command. */ +enum cmd_retval +cmdq_continue_one(struct cmd_q *cmdq) +{ + struct cmd *cmd = cmdq->cmd; + enum cmd_retval retval; + char tmp[1024]; + int flags = !!(cmd->flags & CMD_CONTROL); + + cmd_print(cmd, tmp, sizeof tmp); + log_debug("cmdq %p: %s", cmdq, tmp); + + cmdq->time = time(NULL); + cmdq->number++; + + cmdq_guard(cmdq, "begin", flags); + + retval = cmd->entry->exec(cmd, cmdq); + + if (retval == CMD_RETURN_ERROR) + cmdq_guard(cmdq, "error", flags); + else + cmdq_guard(cmdq, "end", flags); + return (retval); +} + /* Continue processing command queue. Returns 1 if finishes empty. */ int cmdq_continue(struct cmd_q *cmdq) { struct cmd_q_item *next; enum cmd_retval retval; - int empty, flags; - char s[1024]; + int empty; cmdq->references++; notify_disable(); @@ -184,23 +211,7 @@ cmdq_continue(struct cmd_q *cmdq) do { while (cmdq->cmd != NULL) { - cmd_print(cmdq->cmd, s, sizeof s); - log_debug("cmdq %p: %s (client %d)", cmdq, s, - cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); - - cmdq->time = time(NULL); - cmdq->number++; - - flags = !!(cmdq->cmd->flags & CMD_CONTROL); - cmdq_guard(cmdq, "begin", flags); - - retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); - - if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error", flags); - else - cmdq_guard(cmdq, "end", flags); - + retval = cmdq_continue_one(cmdq); if (retval == CMD_RETURN_ERROR) break; if (retval == CMD_RETURN_WAIT) @@ -209,7 +220,6 @@ cmdq_continue(struct cmd_q *cmdq) cmdq_flush(cmdq); goto empty; } - cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); } next = TAILQ_NEXT(cmdq->item, qentry); From 85120b37eaa33f572f8f4c12a050c431a6efba3e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 17 Jun 2015 19:56:08 +0000 Subject: [PATCH 217/703] Change break-pane to take target and source panes (-t and -s) in line with other commands, from Thomas Adam. --- cmd-break-pane.c | 19 +++++++++++++------ tmux.1 | 8 +++++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index a1da0a3a..c7af7865 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -32,8 +32,8 @@ enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { "break-pane", "breakp", - "dPF:t:", 0, 0, - "[-dP] [-F format] " CMD_TARGET_PANE_USAGE, + "dPF:s:t:", 0, 0, + "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, 0, cmd_break_pane_exec }; @@ -48,13 +48,19 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; char *name; char *cause; - int base_idx; + int idx; struct format_tree *ft; const char *template; char *cp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 's'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); + if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) + return (CMD_RETURN_ERROR); + if (idx != -1 && winlink_find_by_index(&s->windows, idx) != NULL) { + cmdq_error(cmdq, "index %d already in use", idx); + return (CMD_RETURN_ERROR); + } if (window_count_panes(wl->window) == 1) { cmdq_error(cmdq, "can't break with only one pane"); @@ -76,8 +82,9 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) free(name); layout_init(w, wp); - base_idx = options_get_number(&s->options, "base-index"); - wl = session_attach(s, w, -1 - base_idx, &cause); /* can't fail */ + if (idx == -1) + idx = -1 - options_get_number(&s->options, "base-index"); + wl = session_attach(s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) session_select(s, wl->idx); diff --git a/tmux.1 b/tmux.1 index fb014898..033330b7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1233,12 +1233,14 @@ Commands related to windows and panes are as follows: .It Xo Ic break-pane .Op Fl dP .Op Fl F Ar format -.Op Fl t Ar target-pane +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane .Xc .D1 (alias: Ic breakp ) Break -.Ar target-pane -off from its containing window to make it the only pane in a new window. +.Ar src-pane +off from its containing window to make it the only pane in +.Ar dst-window . If .Fl d is given, the new window does not become the current window. From 164ba041c9301d8c804a9f76da8ac62a8de286de Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Jun 2015 23:53:56 +0000 Subject: [PATCH 218/703] Remove a stray : and tweak paragraph. --- tmux.1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 033330b7..f0dd569a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -404,14 +404,15 @@ An pattern which is matched against the session name. .El .Pp -If the session name is prefixed with a -.Ql = : , +If the session name is prefixed with an +.Ql = , only an exact match is accepted (so .Ql =mysess will only match exactly .Ql mysess , not .Ql mysession ) . +.Pp If a single session is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no From b43b13faf9ba620c3150bfe459fe6b38e58f6079 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Jun 2015 23:55:24 +0000 Subject: [PATCH 219/703] Use xsnprintf. --- format.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 67bce925..8a52b0e1 100644 --- a/format.c +++ b/format.c @@ -338,7 +338,7 @@ format_find(struct format_tree *ft, const char *key) case OPTIONS_STRING: return (o->str); case OPTIONS_NUMBER: - snprintf(s, sizeof s, "%lld", o->num); + xsnprintf(s, sizeof s, "%lld", o->num); return (s); case OPTIONS_STYLE: return (style_tostring(&o->style)); @@ -679,7 +679,7 @@ format_defaults_session(struct format_tree *ft, struct session *s) RB_FOREACH (wl, winlinks, &s->windows) { if ((wl->flags & WINLINK_ALERTFLAGS) == 0) continue; - snprintf(tmp, sizeof tmp, "%u", wl->idx); + xsnprintf(tmp, sizeof tmp, "%u", wl->idx); if (*alerts != '\0') strlcat(alerts, ",", sizeof alerts); From f557c7d8ca48c79136e4997b2cba0a08ee039257 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 18 Jun 2015 23:56:01 +0000 Subject: [PATCH 220/703] Use the SRCDST define for usage. --- cmd-join-pane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index b995b674..cc6432a4 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -36,7 +36,7 @@ enum cmd_retval join_pane(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_join_pane_entry = { "join-pane", "joinp", "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", + "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, 0, cmd_join_pane_exec }; @@ -44,7 +44,7 @@ const struct cmd_entry cmd_join_pane_entry = { const struct cmd_entry cmd_move_pane_entry = { "move-pane", "movep", "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", + "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, 0, cmd_join_pane_exec }; From 8abcea18a24dea24d6049fefa31c877133489092 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 20 Jun 2015 08:43:55 +0100 Subject: [PATCH 221/703] Remove monitor-content options which have been removed, from Guy Hughes. --- examples/tmux.vim | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index 4d64514a..a6bf74e2 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -222,7 +222,6 @@ syn keyword tmuxOptsSet \ update-environment \ visual-activity \ visual-bell - \ visual-content \ visual-silence \ word-separators @@ -243,7 +242,6 @@ syn keyword tmuxOptsSetw \ mode-mouse \ mode-style \ monitor-activity - \ monitor-content \ monitor-silence \ other-pane-height \ other-pane-width @@ -253,7 +251,6 @@ syn keyword tmuxOptsSetw \ utf8 \ window-status-activity-style \ window-status-bell-style - \ window-status-content-style \ window-status-current-format \ window-status-current-style \ window-status-format From 78723af99f52251aec11d586a507e34b5d0fcaf6 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 28 Jun 2015 12:01:19 +0100 Subject: [PATCH 222/703] README: Clarify SYNCING is under the ISC --- README | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README b/README index 7859ed71..acf15632 100644 --- a/README +++ b/README @@ -52,9 +52,10 @@ welcome. Please send by email to: tmux-users@googlegroups.com -This file and the CHANGES, FAQ and TODO files are licensed under the ISC -license. Files under examples/ remain copyright their authors unless otherwise -stated in the file but permission has been received to distribute them with -tmux. All other files have a license and copyright notice at their start. +This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under +the ISC license. Files under examples/ remain copyright their authors unless +otherwise stated in the file but permission has been received to distribute +them with tmux. All other files have a license and copyright notice at their +start. -- Nicholas Marriott From b298478435c2e32b788566d1fab93dee1b71ff60 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 6 Jul 2015 14:24:57 +0000 Subject: [PATCH 223/703] Update environment with -E when attach-session used on an already attached session or switch-client used on the current session. From Cam Hutchison. --- cmd-attach-session.c | 6 ++++++ cmd-switch-client.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index cb58d4e2..e7cde0f0 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -126,6 +126,12 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, s->cwd = fd; } + if (!Eflag) { + update = options_get_string(&s->options, + "update-environment"); + environ_update(update, &c->environ, &s->environ); + } + c->session = s; notify_attached_session_changed(c); session_update_activity(s); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 23751d73..4bac540d 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -119,7 +119,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } } - if (c != NULL && s != c->session && !args_has(args, 'E')) { + if (c != NULL && !args_has(args, 'E')) { update = options_get_string(&s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } From 235e0bd65a49b469d661a9831067e3f745fbbaad Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 12 Jul 2015 19:46:58 +0100 Subject: [PATCH 224/703] Update imsg*.[ch] from OpenBSD, including bzero->memset. --- compat.h | 5 ----- compat/imsg-buffer.c | 11 ++++++----- compat/imsg.c | 17 ++++++++--------- configure.ac | 1 - 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compat.h b/compat.h index 2c6f1c67..8666a1d8 100644 --- a/compat.h +++ b/compat.h @@ -185,11 +185,6 @@ typedef uint64_t u_int64_t; #define flock(fd, op) (0) #endif -#ifndef HAVE_BZERO -#undef bzero -#define bzero(buf, len) memset(buf, 0, len); -#endif - #ifndef HAVE_CLOSEFROM /* closefrom.c */ void closefrom(int); diff --git a/compat/imsg-buffer.c b/compat/imsg-buffer.c index 6756de0a..241c4f4d 100644 --- a/compat/imsg-buffer.c +++ b/compat/imsg-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg-buffer.c,v 1.4 2014/06/30 00:25:17 deraadt Exp $ */ +/* $OpenBSD: imsg-buffer.c,v 1.7 2015/07/12 18:40:49 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -74,7 +74,7 @@ ibuf_realloc(struct ibuf *buf, size_t len) /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { - errno = ENOMEM; + errno = ERANGE; return (-1); } @@ -149,7 +149,7 @@ ibuf_write(struct msgbuf *msgbuf) unsigned int i = 0; ssize_t n; - bzero(&iov, sizeof(iov)); + memset(&iov, 0, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; @@ -233,8 +233,9 @@ msgbuf_write(struct msgbuf *msgbuf) char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; - bzero(&iov, sizeof(iov)); - bzero(&msg, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; diff --git a/compat/imsg.c b/compat/imsg.c index 9db26ad6..982ee069 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imsg.c,v 1.6 2014/06/30 00:26:22 deraadt Exp $ */ +/* $OpenBSD: imsg.c,v 1.9 2015/07/12 18:40:49 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -34,12 +34,10 @@ int imsg_get_fd(struct imsgbuf *); int available_fds(unsigned int); -/* TA: 2014-09-08: Note that the original code calls getdtablecount() which is - * OpenBSD specific. Until such time that it's ported elsewhere from - * , I've mimicked what OpenSMTPD are doing, by using available_fds() - * instead. +/* + * The original code calls getdtablecount() which is OpenBSD specific. Use + * available_fds() from OpenSMTPD instead. */ - int available_fds(unsigned int n) { @@ -68,7 +66,7 @@ void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); - bzero(&ibuf->r, sizeof(ibuf->r)); + memset(&ibuf->r, 0, sizeof(ibuf->r)); ibuf->fd = fd; ibuf->w.fd = fd; ibuf->pid = getpid(); @@ -89,7 +87,8 @@ imsg_read(struct imsgbuf *ibuf) int fd; struct imsg_fd *ifd; - bzero(&msg, sizeof(msg)); + memset(&msg, 0, sizeof(msg)); + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; @@ -317,7 +316,7 @@ int imsg_flush(struct imsgbuf *ibuf) { while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) < 0) + if (msgbuf_write(&ibuf->w) <= 0) return (-1); return (0); } diff --git a/configure.ac b/configure.ac index cd3ab5a3..24aded53 100644 --- a/configure.ac +++ b/configure.ac @@ -99,7 +99,6 @@ AC_CHECK_HEADERS( # Check for some functions that are replaced or omitted. AC_CHECK_FUNCS( [ \ - bzero \ dirfd \ flock \ setproctitle \ From 73f9f0334ce639e2f05cb0646af2c81881859a2e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 13 Jul 2015 14:19:50 +0100 Subject: [PATCH 225/703] Check for flock in libbsd for AIX, and remove some getopt.h includes. From J Raynor. --- arguments.c | 1 - configure.ac | 3 +++ tmux.c | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arguments.c b/arguments.c index 05ff97eb..ca6cc760 100644 --- a/arguments.c +++ b/arguments.c @@ -18,7 +18,6 @@ #include -#include #include #include #include diff --git a/configure.ac b/configure.ac index 24aded53..de336fef 100644 --- a/configure.ac +++ b/configure.ac @@ -96,6 +96,9 @@ AC_CHECK_HEADERS( ] ) +# Look for library needed for flock. +AC_SEARCH_LIBS(flock, bsd) + # Check for some functions that are replaced or omitted. AC_CHECK_FUNCS( [ \ diff --git a/tmux.c b/tmux.c index 62f8a808..39f344c0 100644 --- a/tmux.c +++ b/tmux.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include From cc768d77ec89c43e5770588b36d584d5d18104d3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 13:28:50 +0000 Subject: [PATCH 226/703] Revert to marking lines as wrapped on newlines, fixes problems with capturep -J. --- screen-write.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/screen-write.c b/screen-write.c index e38c9f53..37e2b548 100644 --- a/screen-write.c +++ b/screen-write.c @@ -795,6 +795,8 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) gl = &s->grid->linedata[s->grid->hsize + s->cy]; if (wrapped) gl->flags |= GRID_LINE_WRAPPED; + else + gl->flags &= ~GRID_LINE_WRAPPED; if (s->cy == s->rlower) grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); From 6308c48efd7a80dad5701721f76b9aafb9b814f8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 13:36:29 +0000 Subject: [PATCH 227/703] Add a -s flag to show-environment to output Bourne shell commands a la ssh-agent. Mostly from Cam Hutchison with some changes by me. --- cmd-show-environment.c | 68 ++++++++++++++++++++++++++++++++---------- tmux.1 | 5 +++- 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 7737752f..a61cf3f4 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -27,16 +27,61 @@ * Show environment. */ -enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); + +char *cmd_show_environment_escape(struct environ_entry *); +void cmd_show_environment_print(struct cmd *, struct cmd_q *, + struct environ_entry *); const struct cmd_entry cmd_show_environment_entry = { "show-environment", "showenv", - "gt:", 0, 1, - "[-g] " CMD_TARGET_SESSION_USAGE " [name]", + "gst:", 0, 1, + "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", 0, cmd_show_environment_exec }; +char * +cmd_show_environment_escape(struct environ_entry *envent) +{ + const char *value = envent->value; + char c, *out, *ret; + + out = ret = xmalloc(strlen(value) * 2 + 1); /* at most twice the size */ + while ((c = *value++) != '\0') { + /* POSIX interprets $ ` " and \ in double quotes. */ + if (c == '$' || c == '`' || c == '"' || c == '\\') + *out++ = '\\'; + *out++ = c; + } + *out = '\0'; + + return ret; +} + +void +cmd_show_environment_print(struct cmd *self, struct cmd_q *cmdq, + struct environ_entry *envent) +{ + char *escaped; + + if (!args_has(self->args, 's')) { + if (envent->value != NULL) + cmdq_print(cmdq, "%s=%s", envent->name, envent->value); + else + cmdq_print(cmdq, "-%s", envent->name); + return; + } + + if (envent->value != NULL) { + escaped = cmd_show_environment_escape(envent); + cmdq_print(cmdq, "%s=\"%s\"; export %s;", envent->name, escaped, + envent->name); + free(escaped); + } else + cmdq_print(cmdq, "unset %s;", envent->name); +} + enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -48,7 +93,8 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'g')) env = &global_environ; else { - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) return (CMD_RETURN_ERROR); env = &s->environ; } @@ -59,19 +105,11 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "unknown variable: %s", args->argv[0]); return (CMD_RETURN_ERROR); } - if (envent->value != NULL) - cmdq_print(cmdq, "%s=%s", envent->name, envent->value); - else - cmdq_print(cmdq, "-%s", envent->name); + cmd_show_environment_print(self, cmdq, envent); return (CMD_RETURN_NORMAL); } - RB_FOREACH(envent, environ, env) { - if (envent->value != NULL) - cmdq_print(cmdq, "%s=%s", envent->name, envent->value); - else - cmdq_print(cmdq, "-%s", envent->name); - } - + RB_FOREACH(envent, environ, env) + cmd_show_environment_print(self, cmdq, envent); return (CMD_RETURN_NORMAL); } diff --git a/tmux.1 b/tmux.1 index f0dd569a..db5f314e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3542,7 +3542,7 @@ flag unsets a variable. indicates the variable is to be removed from the environment before starting a new process. .It Xo Ic show-environment -.Op Fl g +.Op Fl gs .Op Fl t Ar target-session .Op Ar variable .Xc @@ -3556,6 +3556,9 @@ If is omitted, all variables are shown. Variables removed from the environment are prefixed with .Ql - . +If +.Fl s +is used, the output is formatted as a set of Bourne shell commands. .El .Sh STATUS LINE .Nm From 81069f66f96dd83025fc6f2990619eb861199e10 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 15:37:26 +0000 Subject: [PATCH 228/703] Add a format to show if client is a control client. From Bruno Sutic. --- format.c | 2 ++ tmux.1 | 1 + 2 files changed, 3 insertions(+) diff --git a/format.c b/format.c index 8a52b0e1..aff34abb 100644 --- a/format.c +++ b/format.c @@ -711,6 +711,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_tty", "%s", c->tty.path); if (c->tty.termname != NULL) format_add(ft, "client_termname", "%s", c->tty.termname); + format_add(ft, "client_control_mode", "%d", + !!(c->flags & CLIENT_CONTROL)); t = c->creation_time.tv_sec; format_add(ft, "client_created", "%lld", (long long) t); diff --git a/tmux.1 b/tmux.1 index db5f314e..b4c608cb 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3358,6 +3358,7 @@ The following variables are available, where appropriate: .It Li "client_activity_string" Ta "" Ta "String time client last had activity" .It Li "client_created" Ta "" Ta "Integer time client created" .It Li "client_created_string" Ta "" Ta "String time client created" +.It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" .It Li "client_pid" Ta "" Ta "PID of client process" From c7374c31c4ba176e94825e8d734b5abe8a6879b1 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 15:49:31 +0000 Subject: [PATCH 229/703] Initialize cwd fd to -1 so that we don't close fd 0 if the client is destroyed before it is changed. Also allow ttyname() to fail. Fixes problems when running out of file descriptors reported by Bruno Sutic. --- server-client.c | 5 +++-- tmux.h | 2 +- tty.c | 9 +++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/server-client.c b/server-client.c index 9beef4bc..4c03de91 100644 --- a/server-client.c +++ b/server-client.c @@ -96,6 +96,8 @@ server_client_create(int fd) environ_init(&c->environ); + c->cwd = -1; + c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; @@ -1253,12 +1255,11 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (c->fd == -1) return; - if (!isatty(c->fd)) { + if (tty_init(&c->tty, c, c->fd, c->term) != 0) { close(c->fd); c->fd = -1; return; } - tty_init(&c->tty, c, c->fd, c->term); if (c->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; if (c->flags & CLIENT_256COLOURS) diff --git a/tmux.h b/tmux.h index 663832e5..e082aeab 100644 --- a/tmux.h +++ b/tmux.h @@ -1600,7 +1600,7 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); -void tty_init(struct tty *, struct client *, int, char *); +int tty_init(struct tty *, struct client *, int, char *); int tty_resize(struct tty *); int tty_set_size(struct tty *, u_int, u_int); void tty_set_class(struct tty *, u_int); diff --git a/tty.c b/tty.c index 63380c29..ffb36cb1 100644 --- a/tty.c +++ b/tty.c @@ -59,11 +59,14 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) -void +int tty_init(struct tty *tty, struct client *c, int fd, char *term) { char *path; + if (!isatty(fd)) + return (-1); + memset(tty, 0, sizeof *tty); tty->log_fd = -1; @@ -75,13 +78,15 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->client = c; if ((path = ttyname(fd)) == NULL) - fatalx("ttyname failed"); + return (-1); tty->path = xstrdup(path); tty->cstyle = 0; tty->ccolour = xstrdup(""); tty->flags = 0; tty->term_flags = 0; + + return (0); } int From e45d624df288d914a1628d373ff245b03f7d600b Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 15:51:31 +0000 Subject: [PATCH 230/703] Fix line endings. --- server-client.c | 6 +++--- tmux.h | 2 +- tty.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/server-client.c b/server-client.c index 4c03de91..09348b05 100644 --- a/server-client.c +++ b/server-client.c @@ -96,8 +96,8 @@ server_client_create(int fd) environ_init(&c->environ); - c->cwd = -1; - + c->cwd = -1; + c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; @@ -1255,7 +1255,7 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (c->fd == -1) return; - if (tty_init(&c->tty, c, c->fd, c->term) != 0) { + if (tty_init(&c->tty, c, c->fd, c->term) != 0) { close(c->fd); c->fd = -1; return; diff --git a/tmux.h b/tmux.h index e082aeab..3aceff9d 100644 --- a/tmux.h +++ b/tmux.h @@ -1600,7 +1600,7 @@ void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); -int tty_init(struct tty *, struct client *, int, char *); +int tty_init(struct tty *, struct client *, int, char *); int tty_resize(struct tty *); int tty_set_size(struct tty *, u_int, u_int); void tty_set_class(struct tty *, u_int); diff --git a/tty.c b/tty.c index ffb36cb1..374fb8c6 100644 --- a/tty.c +++ b/tty.c @@ -59,14 +59,14 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) -int +int tty_init(struct tty *tty, struct client *c, int fd, char *term) { char *path; - if (!isatty(fd)) - return (-1); - + if (!isatty(fd)) + return (-1); + memset(tty, 0, sizeof *tty); tty->log_fd = -1; @@ -78,15 +78,15 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->client = c; if ((path = ttyname(fd)) == NULL) - return (-1); + return (-1); tty->path = xstrdup(path); tty->cstyle = 0; tty->ccolour = xstrdup(""); tty->flags = 0; tty->term_flags = 0; - - return (0); + + return (0); } int From 4e637b1b610f93d474a60daa4ab78afef66b29bc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 18:10:26 +0000 Subject: [PATCH 231/703] Ignore environment variables that are too long to send to the server. --- client.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 4b7bbb1f..63432926 100644 --- a/client.c +++ b/client.c @@ -350,6 +350,7 @@ client_send_identify(int flags) { const char *s; char **ss; + size_t sslen; int fd; pid_t pid; @@ -374,8 +375,11 @@ client_send_identify(int flags) pid = getpid(); client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); - for (ss = environ; *ss != NULL; ss++) - client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1); + for (ss = environ; *ss != NULL; ss++) { + sslen = strlen(*ss) + 1; + if (sslen <= MAX_IMSGSIZE - IMSG_HEADER_SIZE) + client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); + } client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); From 8dcea2cc14448494d25a6a68618ae7088bef1b95 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2015 18:45:18 +0000 Subject: [PATCH 232/703] Reset G0/G1 state when resetting everything else with send-keys -R. --- input.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/input.c b/input.c index d1ff17fe..095816c3 100644 --- a/input.c +++ b/input.c @@ -801,10 +801,7 @@ input_reset(struct window_pane *wp) { struct input_ctx *ictx = wp->ictx; - memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); - memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); - ictx->old_cx = 0; - ictx->old_cy = 0; + input_reset_cell(ictx); if (wp->mode == NULL) screen_write_start(&ictx->ctx, wp, &wp->base); From bed3069fd746741286e624126774f98ed51fbbdf Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 14 Jul 2015 08:14:35 +0100 Subject: [PATCH 233/703] Add _LINUX_SOURCE_COMPAT on AIX. --- Makefile.am | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile.am b/Makefile.am index 99163c2c..3173ba99 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,6 +50,11 @@ if IS_SUNCC CFLAGS += -erroff=E_EMPTY_DECLARATION endif +# Set _LINUX_SOURCE_COMPAT for AIX for mallocing 0 bytes +if IS_AIX +DEFS += -D_LINUX_SOURCE_COMPAT=1 +endif + # List of sources. dist_tmux_SOURCES = \ arguments.c \ From 8c96e2a6d941bf5438b587c3d4a5ee1c97f9aa14 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 15 Jul 2015 08:46:35 +0100 Subject: [PATCH 234/703] Implement osdep_get_name and osdep_get_cwd for AIX, from J Raynor. --- compat/forkpty-aix.c | 4 +++ osdep-aix.c | 64 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/compat/forkpty-aix.c b/compat/forkpty-aix.c index fd558eb8..6894aa44 100644 --- a/compat/forkpty-aix.c +++ b/compat/forkpty-aix.c @@ -40,6 +40,10 @@ forkpty(int *master, unused char *name, struct termios *tio, struct winsize *ws) if ((path = ttyname(*master)) == NULL) goto out; + + if (name != NULL) + strlcpy(name, path, TTY_NAME_MAX); + if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; diff --git a/osdep-aix.c b/osdep-aix.c index 24051ab8..0a3d12e4 100644 --- a/osdep-aix.c +++ b/osdep-aix.c @@ -16,21 +16,75 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include +#include -#include +#include +#include +#include #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(unused int fd, char *tty) { - return (NULL); + struct psinfo p; + char *path; + ssize_t bytes; + int f, ttyfd, retval; + pid_t pgrp; + + if ((ttyfd = open(tty, O_RDONLY|O_NOCTTY)) == -1) + return (NULL); + + retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); + close(ttyfd); + if (retval == -1) + return (NULL); + + xasprintf(&path, "/proc/%u/psinfo", (u_int) pgrp); + f = open(path, O_RDONLY); + free(path); + if (f < 0) + return (NULL); + + bytes = read(f, &p, sizeof(p)); + close(f); + if (bytes != sizeof(p)) + return (NULL); + + return (xstrdup(p.pr_fname)); } char * -osdep_get_cwd(unused int fd) +osdep_get_cwd(int fd) { + static char target[MAXPATHLEN + 1]; + char *path; + const char *ttypath; + ssize_t n; + pid_t pgrp; + int len, retval, ttyfd; + + if ((ttypath = ptsname(fd)) == NULL) + return (NULL); + if ((ttyfd = open(ttypath, O_RDONLY|O_NOCTTY)) == -1) + return (NULL); + + retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); + close(ttyfd); + if (retval == -1) + return (NULL); + + xasprintf(&path, "/proc/%u/cwd", (u_int) pgrp); + n = readlink(path, target, MAXPATHLEN); + free(path); + if (n > 0) { + target[n] = '\0'; + if ((len = strlen(target)) > 1 && target[len - 1] == '/') + target[len - 1] = '\0'; + return (target); + } return (NULL); } From 3192178f15c9bcc88021ceb74189173d562e7694 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Jul 2015 06:53:47 +0000 Subject: [PATCH 235/703] Initialize client fd to -1 as well, from Bobby Powers. --- server-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-client.c b/server-client.c index 09348b05..c2c7070f 100644 --- a/server-client.c +++ b/server-client.c @@ -96,6 +96,7 @@ server_client_create(int fd) environ_init(&c->environ); + c->fd = -1; c->cwd = -1; c->cmdq = cmdq_new(c); From bad8d0fd200c7718a8f51f52b9dd2f23c74d996f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 17 Jul 2015 13:09:07 +0000 Subject: [PATCH 236/703] Do not call window_unzoom from window_destroy because it will try to add a notification which will get confused because the reference count is already zero and end up back in window_destroy and a double free. Instead, just destroy the layouts directly. Noticed by Thomas Adam. --- window.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/window.c b/window.c index 82c10606..6351eff5 100644 --- a/window.c +++ b/window.c @@ -341,12 +341,12 @@ window_create(const char *name, int argc, char **argv, const char *path, void window_destroy(struct window *w) { - window_unzoom(w); - RB_REMOVE(windows, &windows, w); if (w->layout_root != NULL) - layout_free(w); + layout_free_cell(w->layout_root); + if (w->saved_layout_root != NULL) + layout_free_cell(w->saved_layout_root); free(w->old_layout); if (event_initialized(&w->name_timer)) From 96dcbe217bda0b065df8079e33d14a7192e14c46 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 19 Jul 2015 08:07:55 +0100 Subject: [PATCH 237/703] Update tmux.vim from Ben Boeckel. --- examples/tmux.vim | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index a6bf74e2..dcb58246 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -191,9 +191,7 @@ syn keyword tmuxOptsSet \ message-command-style \ message-limit \ message-style - \ mouse-resize-pane - \ mouse-select-pane - \ mouse-select-window + \ mouse \ mouse-utf8 \ pane-active-border-style \ pane-border-style @@ -230,8 +228,6 @@ syn keyword tmuxOptsSetw \ allow-rename \ alternate-screen \ automatic-rename - \ c0-change-interval - \ c0-change-trigger \ clock-mode-colour \ clock-mode-style \ force-height @@ -239,7 +235,6 @@ syn keyword tmuxOptsSetw \ main-pane-height \ main-pane-width \ mode-keys - \ mode-mouse \ mode-style \ monitor-activity \ monitor-silence @@ -249,6 +244,7 @@ syn keyword tmuxOptsSetw \ remain-on-exit \ synchronize-panes \ utf8 + \ window-active-style \ window-status-activity-style \ window-status-bell-style \ window-status-current-format @@ -257,6 +253,7 @@ syn keyword tmuxOptsSetw \ window-status-last-style \ window-status-separator \ window-status-style + \ window-style \ wrap-search \ xterm-keys From d4ce210713cc907fc69497c51cf9b64d2f414ca4 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Jul 2015 10:34:11 +0000 Subject: [PATCH 238/703] Correct the tsl/fsl sequence to ]0 not ]2 (from Marcel Korpel). While here, Xr xterm and remove some advice about elinks that is better elsewhere. --- tmux.1 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tmux.1 b/tmux.1 index b4c608cb..69bde40a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2709,12 +2709,10 @@ and .Xr terminfo 5 entries if they exist. .Nm -automatically sets these to the \ee]2;...\e007 sequence if -the terminal appears to be an xterm. +automatically sets these to the \ee]0;...\e007 sequence if +the terminal appears to be +.Xr xterm 1 . This option is off by default. -Note that elinks -will only attempt to set the window title if the STY environment -variable is set. .It Ic set-titles-string Ar string String used to set the window title if .Ic set-titles From 92af3766ecc456bd8ade8fe5746d27f4557fdaa8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 20 Jul 2015 15:50:04 +0000 Subject: [PATCH 239/703] Add an option (history-file) for a file to save/restore command prompt history, from Olof-Joachim Frahm. --- options-table.c | 5 +++ server.c | 2 ++ status.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ tmux.1 | 4 +++ tmux.c | 28 ++++++++++----- tmux.h | 3 ++ 6 files changed, 124 insertions(+), 9 deletions(-) diff --git a/options-table.c b/options-table.c index 4ad45d37..7f75d22c 100644 --- a/options-table.c +++ b/options-table.c @@ -83,6 +83,11 @@ const struct options_table_entry server_options_table[] = { .default_num = 0 }, + { .name = "history-file", + .type = OPTIONS_TABLE_STRING, + .default_str = NULL + }, + { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, diff --git a/server.c b/server.c index 001a404c..d1c0bb3b 100644 --- a/server.c +++ b/server.c @@ -240,6 +240,7 @@ server_start(int lockfd, char *lockfile) cfg_add_cause("%s: %s", cfg_file, cause); } cmdq_continue(cfg_cmd_q); + status_prompt_load_history(); server_add_accept(0); @@ -250,6 +251,7 @@ server_start(int lockfd, char *lockfile) set_signals(server_signal_callback); server_loop(); + status_prompt_save_history(); exit(0); } diff --git a/status.c b/status.c index 0ffc0427..96ff8994 100644 --- a/status.c +++ b/status.c @@ -47,11 +47,102 @@ const char **status_prompt_complete_list(u_int *, const char *); char *status_prompt_complete_prefix(const char **, u_int); char *status_prompt_complete(struct session *, const char *); +char *status_prompt_find_history_file(void); + /* Status prompt history. */ #define PROMPT_HISTORY 100 char **status_prompt_hlist; u_int status_prompt_hsize; +/* Find the history file to load/save from/to. */ +char * +status_prompt_find_history_file(void) +{ + const char *home, *history_file; + char *path; + + history_file = options_get_string(&global_options, "history-file"); + if (*history_file == '\0') + return (NULL); + if (*history_file == '/') + return (xstrdup(history_file)); + + if (history_file[0] != '~' || history_file[1] != '/') + return (NULL); + if ((home = find_home()) == NULL) + return (NULL); + xasprintf(&path, "%s%s", home, history_file + 1); + return (path); +} + +/* Load status prompt history from file. */ +void +status_prompt_load_history(void) +{ + FILE *f; + char *history_file, *line, *tmp; + size_t length; + + if ((history_file = status_prompt_find_history_file()) == NULL) + return; + log_debug("loading history from %s", history_file); + + f = fopen(history_file, "r"); + if (f == NULL) { + log_debug("%s: %s", history_file, strerror(errno)); + free(history_file); + return; + } + free(history_file); + + for (;;) { + if ((line = fgetln(f, &length)) == NULL) + break; + + if (length > 0) { + if (line[length - 1] == '\n') { + line[length - 1] = '\0'; + status_prompt_add_history(line); + } else { + tmp = xmalloc(length + 1); + memcpy(tmp, line, length); + tmp[length] = '\0'; + status_prompt_add_history(tmp); + free(tmp); + } + } + } + fclose(f); +} + +/* Save status prompt history to file. */ +void +status_prompt_save_history(void) +{ + FILE *f; + u_int i; + char *history_file; + + if ((history_file = status_prompt_find_history_file()) == NULL) + return; + log_debug("saving history to %s", history_file); + + f = fopen(history_file, "w"); + if (f == NULL) { + log_debug("%s: %s", history_file, strerror(errno)); + free(history_file); + return; + } + free(history_file); + + for (i = 0; i < status_prompt_hsize; i++) { + fputs(status_prompt_hlist[i], f); + fputc('\n', f); + } + fclose(f); + +} + /* Status output tree. */ RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); diff --git a/tmux.1 b/tmux.1 index 69bde40a..7320df4f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2396,6 +2396,10 @@ passed through to applications running in .Nm . Attached clients should be detached and attached again after changing this option. +.It Ic history-file Ar path +If not empty, a file to which +.Nm +will write command prompt history on exit and load it from on start. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for each client. diff --git a/tmux.c b/tmux.c index 878180a7..a3481461 100644 --- a/tmux.c +++ b/tmux.c @@ -198,10 +198,27 @@ shell_exec(const char *shell, const char *shellcmd) fatal("execl failed"); } +const char* +find_home(void) +{ + struct passwd *pw; + const char *home; + + home = getenv("HOME"); + if (home == NULL || *home == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + home = pw->pw_dir; + else + home = NULL; + } + + return home; +} + int main(int argc, char **argv) { - struct passwd *pw; char *s, *path, *label, **var, tmp[PATH_MAX]; char in[256]; const char *home; @@ -320,14 +337,7 @@ main(int argc, char **argv) /* Locate the configuration file. */ if (cfg_file == NULL) { - home = getenv("HOME"); - if (home == NULL || *home == '\0') { - pw = getpwuid(getuid()); - if (pw != NULL) - home = pw->pw_dir; - else - home = NULL; - } + home = find_home(); if (home != NULL) { xasprintf(&cfg_file, "%s/.tmux.conf", home); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { diff --git a/tmux.h b/tmux.h index 3aceff9d..7edc4636 100644 --- a/tmux.h +++ b/tmux.h @@ -1469,6 +1469,7 @@ int checkshell(const char *); int areshell(const char *); void setblocking(int, int); __dead void shell_exec(const char *, const char *); +const char *find_home(void); /* cfg.c */ extern struct cmd_q *cfg_cmd_q; @@ -1943,6 +1944,8 @@ void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); void status_prompt_key(struct client *, int); void status_prompt_update(struct client *, const char *, const char *); +void status_prompt_load_history(void); +void status_prompt_save_history(void); /* resize.c */ void recalculate_sizes(void); From 669059aa19ec831e9a8afc008c978ea7806692e3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 24 Jul 2015 09:06:15 +0100 Subject: [PATCH 240/703] Fix a warning, from Kosta Zertsekel. --- compat/getopt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compat/getopt.c b/compat/getopt.c index 03ad9e80..9cd3b3d2 100644 --- a/compat/getopt.c +++ b/compat/getopt.c @@ -94,7 +94,7 @@ BSDgetopt(int nargc, char *const *nargv, const char *ostr) } else { /* need an argument */ if (*place) /* no white space */ - BSDoptarg = place; + BSDoptarg = (char *)place; else if (nargc <= ++BSDoptind) { /* no arg */ place = EMSG; if (*ostr == ':') From d33adc4fd0f7657ede3178c9b1f33c2d5df3c524 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jul 2015 08:45:45 +0000 Subject: [PATCH 241/703] Make -q suppress ambiguous option warnings too, from Cam Hutchison. --- cmd-set-option.c | 7 +++++-- tmux.1 | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 77c7d7c2..761cee93 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -110,8 +110,11 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Find the option entry, try each table. */ table = oe = NULL; if (options_table_find(optstr, &table, &oe) != 0) { - cmdq_error(cmdq, "ambiguous option: %s", optstr); - return (CMD_RETURN_ERROR); + if (!args_has(args, 'q')) { + cmdq_error(cmdq, "ambiguous option: %s", optstr); + return (CMD_RETURN_ERROR); + } + return (CMD_RETURN_NORMAL); } if (oe == NULL) { if (!args_has(args, 'q')) { diff --git a/tmux.1 b/tmux.1 index 7320df4f..a4422057 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2322,9 +2322,9 @@ restores a global option to the default). .Pp The .Fl o -flag prevents setting an option that is already set and +flag prevents setting an option that is already set and the .Fl q -flag suppresses errors about unknown options. +flag suppresses errors about unknown or ambiguous options. .Pp With .Fl a , From b254115acd54513cd4b5858e31afc7980e93246c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 28 Jul 2015 15:18:10 +0000 Subject: [PATCH 242/703] Tidy up the way terminals are described and move some structs out of tmux.h. --- cmd-show-messages.c | 34 +--- status.c | 1 - tmux.h | 32 +-- tty-term.c | 477 +++++++++++++++++++++++++------------------- 4 files changed, 277 insertions(+), 267 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 2a04bd93..92ac5cc2 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -70,11 +70,8 @@ cmd_show_messages_server(struct cmd_q *cmdq) int cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { - struct tty_term *term; - const struct tty_term_code_entry *ent; - struct tty_code *code; - u_int i, n; - char out[80]; + struct tty_term *term; + u_int i, n; n = 0; LIST_FOREACH(term, &tty_terms, entry) { @@ -85,31 +82,8 @@ cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) cmdq_print(cmdq, "Terminal %u: %s [references=%u, flags=0x%x]:", n, term->name, term->references, term->flags); n++; - for (i = 0; i < NTTYCODE; i++) { - ent = &tty_term_codes[i]; - code = &term->codes[ent->code]; - switch (code->type) { - case TTYCODE_NONE: - cmdq_print(cmdq, "%4u: %s: [missing]", - ent->code, ent->name); - break; - case TTYCODE_STRING: - strnvis(out, code->value.string, sizeof out, - VIS_OCTAL|VIS_TAB|VIS_NL); - cmdq_print(cmdq, "%4u: %s: (string) %s", - ent->code, ent->name, out); - break; - case TTYCODE_NUMBER: - cmdq_print(cmdq, "%4u: %s: (number) %d", - ent->code, ent->name, code->value.number); - break; - case TTYCODE_FLAG: - cmdq_print(cmdq, "%4u: %s: (flag) %s", - ent->code, ent->name, - code->value.flag ? "true" : "false"); - break; - } - } + for (i = 0; i < tty_term_ncodes(); i++) + cmdq_print(cmdq, "%s", tty_term_describe(term, i)); } return (n != 0); } diff --git a/status.c b/status.c index 96ff8994..16f4fa70 100644 --- a/status.c +++ b/status.c @@ -36,7 +36,6 @@ char *status_redraw_get_right(struct client *, time_t, int, char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); -void status_replace1(char **, char **, char *, size_t); void status_message_callback(int, short, void *); const char *status_prompt_up_history(u_int *); diff --git a/tmux.h b/tmux.h index 7edc4636..b3456922 100644 --- a/tmux.h +++ b/tmux.h @@ -387,32 +387,6 @@ enum tty_code_code { TTYC_XENL, /* eat_newline_glitch, xn */ TTYC_XT, /* xterm(1)-compatible title, XT */ }; -#define NTTYCODE (TTYC_XT + 1) - -/* Termcap types. */ -enum tty_code_type { - TTYCODE_NONE = 0, - TTYCODE_STRING, - TTYCODE_NUMBER, - TTYCODE_FLAG, -}; - -/* Termcap code. */ -struct tty_code { - enum tty_code_type type; - union { - char *string; - int number; - int flag; - } value; -}; - -/* Entry in terminal code table. */ -struct tty_term_code_entry { - enum tty_code_code code; - enum tty_code_type type; - const char *name; -}; /* Message codes. */ enum msgtype { @@ -1096,13 +1070,14 @@ struct tty_key { struct tty_key *next; }; +struct tty_code; struct tty_term { char *name; u_int references; char acs[UCHAR_MAX + 1][2]; - struct tty_code codes[NTTYCODE]; + struct tty_code *codes; #define TERM_256COLOURS 0x1 #define TERM_EARLYWRAP 0x2 @@ -1643,7 +1618,7 @@ void tty_bell(struct tty *); /* tty-term.c */ extern struct tty_terms tty_terms; -extern const struct tty_term_code_entry tty_term_codes[NTTYCODE]; +u_int tty_term_ncodes(void); struct tty_term *tty_term_find(char *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); @@ -1657,6 +1632,7 @@ const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const void *, const void *); int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code); +const char *tty_term_describe(struct tty_term *, enum tty_code_code); /* tty-acs.c */ const char *tty_acs_get(struct tty *, u_char); diff --git a/tty-term.c b/tty-term.c index f6f6b444..14339de1 100644 --- a/tty-term.c +++ b/tty-term.c @@ -32,210 +32,237 @@ char *tty_term_strip(const char *); struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); -const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { - { TTYC_ACSC, TTYCODE_STRING, "acsc" }, - { TTYC_AX, TTYCODE_FLAG, "AX" }, - { TTYC_BCE, TTYCODE_FLAG, "bce" }, - { TTYC_BEL, TTYCODE_STRING, "bel" }, - { TTYC_BLINK, TTYCODE_STRING, "blink" }, - { TTYC_BOLD, TTYCODE_STRING, "bold" }, - { TTYC_CIVIS, TTYCODE_STRING, "civis" }, - { TTYC_CLEAR, TTYCODE_STRING, "clear" }, - { TTYC_CNORM, TTYCODE_STRING, "cnorm" }, - { TTYC_COLORS, TTYCODE_NUMBER, "colors" }, - { TTYC_CR, TTYCODE_STRING, "Cr" }, - { TTYC_CS, TTYCODE_STRING, "Cs" }, - { TTYC_CSR, TTYCODE_STRING, "csr" }, - { TTYC_CUB, TTYCODE_STRING, "cub" }, - { TTYC_CUB1, TTYCODE_STRING, "cub1" }, - { TTYC_CUD, TTYCODE_STRING, "cud" }, - { TTYC_CUD1, TTYCODE_STRING, "cud1" }, - { TTYC_CUF, TTYCODE_STRING, "cuf" }, - { TTYC_CUF1, TTYCODE_STRING, "cuf1" }, - { TTYC_CUP, TTYCODE_STRING, "cup" }, - { TTYC_CUU, TTYCODE_STRING, "cuu" }, - { TTYC_CUU1, TTYCODE_STRING, "cuu1" }, - { TTYC_CVVIS, TTYCODE_STRING, "cvvis" }, - { TTYC_DCH, TTYCODE_STRING, "dch" }, - { TTYC_DCH1, TTYCODE_STRING, "dch1" }, - { TTYC_DIM, TTYCODE_STRING, "dim" }, - { TTYC_DL, TTYCODE_STRING, "dl" }, - { TTYC_DL1, TTYCODE_STRING, "dl1" }, - { TTYC_E3, TTYCODE_STRING, "E3" }, - { TTYC_ECH, TTYCODE_STRING, "ech" }, - { TTYC_EL, TTYCODE_STRING, "el" }, - { TTYC_EL1, TTYCODE_STRING, "el1" }, - { TTYC_ENACS, TTYCODE_STRING, "enacs" }, - { TTYC_FSL, TTYCODE_STRING, "fsl" }, - { TTYC_HOME, TTYCODE_STRING, "home" }, - { TTYC_HPA, TTYCODE_STRING, "hpa" }, - { TTYC_ICH, TTYCODE_STRING, "ich" }, - { TTYC_ICH1, TTYCODE_STRING, "ich1" }, - { TTYC_IL, TTYCODE_STRING, "il" }, - { TTYC_IL1, TTYCODE_STRING, "il1" }, - { TTYC_INVIS, TTYCODE_STRING, "invis" }, - { TTYC_IS1, TTYCODE_STRING, "is1" }, - { TTYC_IS2, TTYCODE_STRING, "is2" }, - { TTYC_IS3, TTYCODE_STRING, "is3" }, - { TTYC_KCBT, TTYCODE_STRING, "kcbt" }, - { TTYC_KCUB1, TTYCODE_STRING, "kcub1" }, - { TTYC_KCUD1, TTYCODE_STRING, "kcud1" }, - { TTYC_KCUF1, TTYCODE_STRING, "kcuf1" }, - { TTYC_KCUU1, TTYCODE_STRING, "kcuu1" }, - { TTYC_KDC2, TTYCODE_STRING, "kDC" }, - { TTYC_KDC3, TTYCODE_STRING, "kDC3" }, - { TTYC_KDC4, TTYCODE_STRING, "kDC4" }, - { TTYC_KDC5, TTYCODE_STRING, "kDC5" }, - { TTYC_KDC6, TTYCODE_STRING, "kDC6" }, - { TTYC_KDC7, TTYCODE_STRING, "kDC7" }, - { TTYC_KDCH1, TTYCODE_STRING, "kdch1" }, - { TTYC_KDN2, TTYCODE_STRING, "kDN" }, - { TTYC_KDN3, TTYCODE_STRING, "kDN3" }, - { TTYC_KDN4, TTYCODE_STRING, "kDN4" }, - { TTYC_KDN5, TTYCODE_STRING, "kDN5" }, - { TTYC_KDN6, TTYCODE_STRING, "kDN6" }, - { TTYC_KDN7, TTYCODE_STRING, "kDN7" }, - { TTYC_KEND, TTYCODE_STRING, "kend" }, - { TTYC_KEND2, TTYCODE_STRING, "kEND" }, - { TTYC_KEND3, TTYCODE_STRING, "kEND3" }, - { TTYC_KEND4, TTYCODE_STRING, "kEND4" }, - { TTYC_KEND5, TTYCODE_STRING, "kEND5" }, - { TTYC_KEND6, TTYCODE_STRING, "kEND6" }, - { TTYC_KEND7, TTYCODE_STRING, "kEND7" }, - { TTYC_KF1, TTYCODE_STRING, "kf1" }, - { TTYC_KF10, TTYCODE_STRING, "kf10" }, - { TTYC_KF11, TTYCODE_STRING, "kf11" }, - { TTYC_KF12, TTYCODE_STRING, "kf12" }, - { TTYC_KF13, TTYCODE_STRING, "kf13" }, - { TTYC_KF14, TTYCODE_STRING, "kf14" }, - { TTYC_KF15, TTYCODE_STRING, "kf15" }, - { TTYC_KF16, TTYCODE_STRING, "kf16" }, - { TTYC_KF17, TTYCODE_STRING, "kf17" }, - { TTYC_KF18, TTYCODE_STRING, "kf18" }, - { TTYC_KF19, TTYCODE_STRING, "kf19" }, - { TTYC_KF2, TTYCODE_STRING, "kf2" }, - { TTYC_KF20, TTYCODE_STRING, "kf20" }, - { TTYC_KF21, TTYCODE_STRING, "kf21" }, - { TTYC_KF22, TTYCODE_STRING, "kf22" }, - { TTYC_KF23, TTYCODE_STRING, "kf23" }, - { TTYC_KF24, TTYCODE_STRING, "kf24" }, - { TTYC_KF25, TTYCODE_STRING, "kf25" }, - { TTYC_KF26, TTYCODE_STRING, "kf26" }, - { TTYC_KF27, TTYCODE_STRING, "kf27" }, - { TTYC_KF28, TTYCODE_STRING, "kf28" }, - { TTYC_KF29, TTYCODE_STRING, "kf29" }, - { TTYC_KF3, TTYCODE_STRING, "kf3" }, - { TTYC_KF30, TTYCODE_STRING, "kf30" }, - { TTYC_KF31, TTYCODE_STRING, "kf31" }, - { TTYC_KF32, TTYCODE_STRING, "kf32" }, - { TTYC_KF33, TTYCODE_STRING, "kf33" }, - { TTYC_KF34, TTYCODE_STRING, "kf34" }, - { TTYC_KF35, TTYCODE_STRING, "kf35" }, - { TTYC_KF36, TTYCODE_STRING, "kf36" }, - { TTYC_KF37, TTYCODE_STRING, "kf37" }, - { TTYC_KF38, TTYCODE_STRING, "kf38" }, - { TTYC_KF39, TTYCODE_STRING, "kf39" }, - { TTYC_KF4, TTYCODE_STRING, "kf4" }, - { TTYC_KF40, TTYCODE_STRING, "kf40" }, - { TTYC_KF41, TTYCODE_STRING, "kf41" }, - { TTYC_KF42, TTYCODE_STRING, "kf42" }, - { TTYC_KF43, TTYCODE_STRING, "kf43" }, - { TTYC_KF44, TTYCODE_STRING, "kf44" }, - { TTYC_KF45, TTYCODE_STRING, "kf45" }, - { TTYC_KF46, TTYCODE_STRING, "kf46" }, - { TTYC_KF47, TTYCODE_STRING, "kf47" }, - { TTYC_KF48, TTYCODE_STRING, "kf48" }, - { TTYC_KF49, TTYCODE_STRING, "kf49" }, - { TTYC_KF5, TTYCODE_STRING, "kf5" }, - { TTYC_KF50, TTYCODE_STRING, "kf50" }, - { TTYC_KF51, TTYCODE_STRING, "kf51" }, - { TTYC_KF52, TTYCODE_STRING, "kf52" }, - { TTYC_KF53, TTYCODE_STRING, "kf53" }, - { TTYC_KF54, TTYCODE_STRING, "kf54" }, - { TTYC_KF55, TTYCODE_STRING, "kf55" }, - { TTYC_KF56, TTYCODE_STRING, "kf56" }, - { TTYC_KF57, TTYCODE_STRING, "kf57" }, - { TTYC_KF58, TTYCODE_STRING, "kf58" }, - { TTYC_KF59, TTYCODE_STRING, "kf59" }, - { TTYC_KF6, TTYCODE_STRING, "kf6" }, - { TTYC_KF60, TTYCODE_STRING, "kf60" }, - { TTYC_KF61, TTYCODE_STRING, "kf61" }, - { TTYC_KF62, TTYCODE_STRING, "kf62" }, - { TTYC_KF63, TTYCODE_STRING, "kf63" }, - { TTYC_KF7, TTYCODE_STRING, "kf7" }, - { TTYC_KF8, TTYCODE_STRING, "kf8" }, - { TTYC_KF9, TTYCODE_STRING, "kf9" }, - { TTYC_KHOM2, TTYCODE_STRING, "kHOM" }, - { TTYC_KHOM3, TTYCODE_STRING, "kHOM3" }, - { TTYC_KHOM4, TTYCODE_STRING, "kHOM4" }, - { TTYC_KHOM5, TTYCODE_STRING, "kHOM5" }, - { TTYC_KHOM6, TTYCODE_STRING, "kHOM6" }, - { TTYC_KHOM7, TTYCODE_STRING, "kHOM7" }, - { TTYC_KHOME, TTYCODE_STRING, "khome" }, - { TTYC_KIC2, TTYCODE_STRING, "kIC" }, - { TTYC_KIC3, TTYCODE_STRING, "kIC3" }, - { TTYC_KIC4, TTYCODE_STRING, "kIC4" }, - { TTYC_KIC5, TTYCODE_STRING, "kIC5" }, - { TTYC_KIC6, TTYCODE_STRING, "kIC6" }, - { TTYC_KIC7, TTYCODE_STRING, "kIC7" }, - { TTYC_KICH1, TTYCODE_STRING, "kich1" }, - { TTYC_KLFT2, TTYCODE_STRING, "kLFT" }, - { TTYC_KLFT3, TTYCODE_STRING, "kLFT3" }, - { TTYC_KLFT4, TTYCODE_STRING, "kLFT4" }, - { TTYC_KLFT5, TTYCODE_STRING, "kLFT5" }, - { TTYC_KLFT6, TTYCODE_STRING, "kLFT6" }, - { TTYC_KLFT7, TTYCODE_STRING, "kLFT7" }, - { TTYC_KMOUS, TTYCODE_STRING, "kmous" }, - { TTYC_KNP, TTYCODE_STRING, "knp" }, - { TTYC_KNXT2, TTYCODE_STRING, "kNXT" }, - { TTYC_KNXT3, TTYCODE_STRING, "kNXT3" }, - { TTYC_KNXT4, TTYCODE_STRING, "kNXT4" }, - { TTYC_KNXT5, TTYCODE_STRING, "kNXT5" }, - { TTYC_KNXT6, TTYCODE_STRING, "kNXT6" }, - { TTYC_KNXT7, TTYCODE_STRING, "kNXT7" }, - { TTYC_KPP, TTYCODE_STRING, "kpp" }, - { TTYC_KPRV2, TTYCODE_STRING, "kPRV" }, - { TTYC_KPRV3, TTYCODE_STRING, "kPRV3" }, - { TTYC_KPRV4, TTYCODE_STRING, "kPRV4" }, - { TTYC_KPRV5, TTYCODE_STRING, "kPRV5" }, - { TTYC_KPRV6, TTYCODE_STRING, "kPRV6" }, - { TTYC_KPRV7, TTYCODE_STRING, "kPRV7" }, - { TTYC_KRIT2, TTYCODE_STRING, "kRIT" }, - { TTYC_KRIT3, TTYCODE_STRING, "kRIT3" }, - { TTYC_KRIT4, TTYCODE_STRING, "kRIT4" }, - { TTYC_KRIT5, TTYCODE_STRING, "kRIT5" }, - { TTYC_KRIT6, TTYCODE_STRING, "kRIT6" }, - { TTYC_KRIT7, TTYCODE_STRING, "kRIT7" }, - { TTYC_KUP2, TTYCODE_STRING, "kUP" }, - { TTYC_KUP3, TTYCODE_STRING, "kUP3" }, - { TTYC_KUP4, TTYCODE_STRING, "kUP4" }, - { TTYC_KUP5, TTYCODE_STRING, "kUP5" }, - { TTYC_KUP6, TTYCODE_STRING, "kUP6" }, - { TTYC_KUP7, TTYCODE_STRING, "kUP7" }, - { TTYC_MS, TTYCODE_STRING, "Ms" }, - { TTYC_OP, TTYCODE_STRING, "op" }, - { TTYC_REV, TTYCODE_STRING, "rev" }, - { TTYC_RI, TTYCODE_STRING, "ri" }, - { TTYC_RMACS, TTYCODE_STRING, "rmacs" }, - { TTYC_RMCUP, TTYCODE_STRING, "rmcup" }, - { TTYC_RMKX, TTYCODE_STRING, "rmkx" }, - { TTYC_SE, TTYCODE_STRING, "Se" }, - { TTYC_SETAB, TTYCODE_STRING, "setab" }, - { TTYC_SETAF, TTYCODE_STRING, "setaf" }, - { TTYC_SGR0, TTYCODE_STRING, "sgr0" }, - { TTYC_SITM, TTYCODE_STRING, "sitm" }, - { TTYC_SMACS, TTYCODE_STRING, "smacs" }, - { TTYC_SMCUP, TTYCODE_STRING, "smcup" }, - { TTYC_SMKX, TTYCODE_STRING, "smkx" }, - { TTYC_SMSO, TTYCODE_STRING, "smso" }, - { TTYC_SMUL, TTYCODE_STRING, "smul" }, - { TTYC_SS, TTYCODE_STRING, "Ss" }, - { TTYC_TSL, TTYCODE_STRING, "tsl" }, - { TTYC_VPA, TTYCODE_STRING, "vpa" }, - { TTYC_XENL, TTYCODE_FLAG, "xenl" }, - { TTYC_XT, TTYCODE_FLAG, "XT" }, +enum tty_code_type { + TTYCODE_NONE = 0, + TTYCODE_STRING, + TTYCODE_NUMBER, + TTYCODE_FLAG, }; +struct tty_code { + enum tty_code_type type; + union { + char *string; + int number; + int flag; + } value; +}; + +struct tty_term_code_entry { + enum tty_code_type type; + const char *name; +}; + +const struct tty_term_code_entry tty_term_codes[] = { + [TTYC_ACSC] = { TTYCODE_STRING, "acsc" }, + [TTYC_AX] = { TTYCODE_FLAG, "AX" }, + [TTYC_BCE] = { TTYCODE_FLAG, "bce" }, + [TTYC_BEL] = { TTYCODE_STRING, "bel" }, + [TTYC_BLINK] = { TTYCODE_STRING, "blink" }, + [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, + [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, + [TTYC_CLEAR] = { TTYCODE_STRING, "clear" }, + [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, + [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, + [TTYC_CR] = { TTYCODE_STRING, "Cr" }, + [TTYC_CS] = { TTYCODE_STRING, "Cs" }, + [TTYC_CSR] = { TTYCODE_STRING, "csr" }, + [TTYC_CUB] = { TTYCODE_STRING, "cub" }, + [TTYC_CUB1] = { TTYCODE_STRING, "cub1" }, + [TTYC_CUD] = { TTYCODE_STRING, "cud" }, + [TTYC_CUD1] = { TTYCODE_STRING, "cud1" }, + [TTYC_CUF] = { TTYCODE_STRING, "cuf" }, + [TTYC_CUF1] = { TTYCODE_STRING, "cuf1" }, + [TTYC_CUP] = { TTYCODE_STRING, "cup" }, + [TTYC_CUU] = { TTYCODE_STRING, "cuu" }, + [TTYC_CUU1] = { TTYCODE_STRING, "cuu1" }, + [TTYC_CVVIS] = { TTYCODE_STRING, "cvvis" }, + [TTYC_DCH] = { TTYCODE_STRING, "dch" }, + [TTYC_DCH1] = { TTYCODE_STRING, "dch1" }, + [TTYC_DIM] = { TTYCODE_STRING, "dim" }, + [TTYC_DL] = { TTYCODE_STRING, "dl" }, + [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, + [TTYC_E3] = { TTYCODE_STRING, "E3" }, + [TTYC_ECH] = { TTYCODE_STRING, "ech" }, + [TTYC_EL] = { TTYCODE_STRING, "el" }, + [TTYC_EL1] = { TTYCODE_STRING, "el1" }, + [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, + [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, + [TTYC_HOME] = { TTYCODE_STRING, "home" }, + [TTYC_HPA] = { TTYCODE_STRING, "hpa" }, + [TTYC_ICH] = { TTYCODE_STRING, "ich" }, + [TTYC_ICH1] = { TTYCODE_STRING, "ich1" }, + [TTYC_IL] = { TTYCODE_STRING, "il" }, + [TTYC_IL1] = { TTYCODE_STRING, "il1" }, + [TTYC_INVIS] = { TTYCODE_STRING, "invis" }, + [TTYC_IS1] = { TTYCODE_STRING, "is1" }, + [TTYC_IS2] = { TTYCODE_STRING, "is2" }, + [TTYC_IS3] = { TTYCODE_STRING, "is3" }, + [TTYC_KCBT] = { TTYCODE_STRING, "kcbt" }, + [TTYC_KCUB1] = { TTYCODE_STRING, "kcub1" }, + [TTYC_KCUD1] = { TTYCODE_STRING, "kcud1" }, + [TTYC_KCUF1] = { TTYCODE_STRING, "kcuf1" }, + [TTYC_KCUU1] = { TTYCODE_STRING, "kcuu1" }, + [TTYC_KDC2] = { TTYCODE_STRING, "kDC" }, + [TTYC_KDC3] = { TTYCODE_STRING, "kDC3" }, + [TTYC_KDC4] = { TTYCODE_STRING, "kDC4" }, + [TTYC_KDC5] = { TTYCODE_STRING, "kDC5" }, + [TTYC_KDC6] = { TTYCODE_STRING, "kDC6" }, + [TTYC_KDC7] = { TTYCODE_STRING, "kDC7" }, + [TTYC_KDCH1] = { TTYCODE_STRING, "kdch1" }, + [TTYC_KDN2] = { TTYCODE_STRING, "kDN" }, + [TTYC_KDN3] = { TTYCODE_STRING, "kDN3" }, + [TTYC_KDN4] = { TTYCODE_STRING, "kDN4" }, + [TTYC_KDN5] = { TTYCODE_STRING, "kDN5" }, + [TTYC_KDN6] = { TTYCODE_STRING, "kDN6" }, + [TTYC_KDN7] = { TTYCODE_STRING, "kDN7" }, + [TTYC_KEND] = { TTYCODE_STRING, "kend" }, + [TTYC_KEND2] = { TTYCODE_STRING, "kEND" }, + [TTYC_KEND3] = { TTYCODE_STRING, "kEND3" }, + [TTYC_KEND4] = { TTYCODE_STRING, "kEND4" }, + [TTYC_KEND5] = { TTYCODE_STRING, "kEND5" }, + [TTYC_KEND6] = { TTYCODE_STRING, "kEND6" }, + [TTYC_KEND7] = { TTYCODE_STRING, "kEND7" }, + [TTYC_KF1] = { TTYCODE_STRING, "kf1" }, + [TTYC_KF10] = { TTYCODE_STRING, "kf10" }, + [TTYC_KF11] = { TTYCODE_STRING, "kf11" }, + [TTYC_KF12] = { TTYCODE_STRING, "kf12" }, + [TTYC_KF13] = { TTYCODE_STRING, "kf13" }, + [TTYC_KF14] = { TTYCODE_STRING, "kf14" }, + [TTYC_KF15] = { TTYCODE_STRING, "kf15" }, + [TTYC_KF16] = { TTYCODE_STRING, "kf16" }, + [TTYC_KF17] = { TTYCODE_STRING, "kf17" }, + [TTYC_KF18] = { TTYCODE_STRING, "kf18" }, + [TTYC_KF19] = { TTYCODE_STRING, "kf19" }, + [TTYC_KF2] = { TTYCODE_STRING, "kf2" }, + [TTYC_KF20] = { TTYCODE_STRING, "kf20" }, + [TTYC_KF21] = { TTYCODE_STRING, "kf21" }, + [TTYC_KF22] = { TTYCODE_STRING, "kf22" }, + [TTYC_KF23] = { TTYCODE_STRING, "kf23" }, + [TTYC_KF24] = { TTYCODE_STRING, "kf24" }, + [TTYC_KF25] = { TTYCODE_STRING, "kf25" }, + [TTYC_KF26] = { TTYCODE_STRING, "kf26" }, + [TTYC_KF27] = { TTYCODE_STRING, "kf27" }, + [TTYC_KF28] = { TTYCODE_STRING, "kf28" }, + [TTYC_KF29] = { TTYCODE_STRING, "kf29" }, + [TTYC_KF3] = { TTYCODE_STRING, "kf3" }, + [TTYC_KF30] = { TTYCODE_STRING, "kf30" }, + [TTYC_KF31] = { TTYCODE_STRING, "kf31" }, + [TTYC_KF32] = { TTYCODE_STRING, "kf32" }, + [TTYC_KF33] = { TTYCODE_STRING, "kf33" }, + [TTYC_KF34] = { TTYCODE_STRING, "kf34" }, + [TTYC_KF35] = { TTYCODE_STRING, "kf35" }, + [TTYC_KF36] = { TTYCODE_STRING, "kf36" }, + [TTYC_KF37] = { TTYCODE_STRING, "kf37" }, + [TTYC_KF38] = { TTYCODE_STRING, "kf38" }, + [TTYC_KF39] = { TTYCODE_STRING, "kf39" }, + [TTYC_KF4] = { TTYCODE_STRING, "kf4" }, + [TTYC_KF40] = { TTYCODE_STRING, "kf40" }, + [TTYC_KF41] = { TTYCODE_STRING, "kf41" }, + [TTYC_KF42] = { TTYCODE_STRING, "kf42" }, + [TTYC_KF43] = { TTYCODE_STRING, "kf43" }, + [TTYC_KF44] = { TTYCODE_STRING, "kf44" }, + [TTYC_KF45] = { TTYCODE_STRING, "kf45" }, + [TTYC_KF46] = { TTYCODE_STRING, "kf46" }, + [TTYC_KF47] = { TTYCODE_STRING, "kf47" }, + [TTYC_KF48] = { TTYCODE_STRING, "kf48" }, + [TTYC_KF49] = { TTYCODE_STRING, "kf49" }, + [TTYC_KF5] = { TTYCODE_STRING, "kf5" }, + [TTYC_KF50] = { TTYCODE_STRING, "kf50" }, + [TTYC_KF51] = { TTYCODE_STRING, "kf51" }, + [TTYC_KF52] = { TTYCODE_STRING, "kf52" }, + [TTYC_KF53] = { TTYCODE_STRING, "kf53" }, + [TTYC_KF54] = { TTYCODE_STRING, "kf54" }, + [TTYC_KF55] = { TTYCODE_STRING, "kf55" }, + [TTYC_KF56] = { TTYCODE_STRING, "kf56" }, + [TTYC_KF57] = { TTYCODE_STRING, "kf57" }, + [TTYC_KF58] = { TTYCODE_STRING, "kf58" }, + [TTYC_KF59] = { TTYCODE_STRING, "kf59" }, + [TTYC_KF6] = { TTYCODE_STRING, "kf6" }, + [TTYC_KF60] = { TTYCODE_STRING, "kf60" }, + [TTYC_KF61] = { TTYCODE_STRING, "kf61" }, + [TTYC_KF62] = { TTYCODE_STRING, "kf62" }, + [TTYC_KF63] = { TTYCODE_STRING, "kf63" }, + [TTYC_KF7] = { TTYCODE_STRING, "kf7" }, + [TTYC_KF8] = { TTYCODE_STRING, "kf8" }, + [TTYC_KF9] = { TTYCODE_STRING, "kf9" }, + [TTYC_KHOM2] = { TTYCODE_STRING, "kHOM" }, + [TTYC_KHOM3] = { TTYCODE_STRING, "kHOM3" }, + [TTYC_KHOM4] = { TTYCODE_STRING, "kHOM4" }, + [TTYC_KHOM5] = { TTYCODE_STRING, "kHOM5" }, + [TTYC_KHOM6] = { TTYCODE_STRING, "kHOM6" }, + [TTYC_KHOM7] = { TTYCODE_STRING, "kHOM7" }, + [TTYC_KHOME] = { TTYCODE_STRING, "khome" }, + [TTYC_KIC2] = { TTYCODE_STRING, "kIC" }, + [TTYC_KIC3] = { TTYCODE_STRING, "kIC3" }, + [TTYC_KIC4] = { TTYCODE_STRING, "kIC4" }, + [TTYC_KIC5] = { TTYCODE_STRING, "kIC5" }, + [TTYC_KIC6] = { TTYCODE_STRING, "kIC6" }, + [TTYC_KIC7] = { TTYCODE_STRING, "kIC7" }, + [TTYC_KICH1] = { TTYCODE_STRING, "kich1" }, + [TTYC_KLFT2] = { TTYCODE_STRING, "kLFT" }, + [TTYC_KLFT3] = { TTYCODE_STRING, "kLFT3" }, + [TTYC_KLFT4] = { TTYCODE_STRING, "kLFT4" }, + [TTYC_KLFT5] = { TTYCODE_STRING, "kLFT5" }, + [TTYC_KLFT6] = { TTYCODE_STRING, "kLFT6" }, + [TTYC_KLFT7] = { TTYCODE_STRING, "kLFT7" }, + [TTYC_KMOUS] = { TTYCODE_STRING, "kmous" }, + [TTYC_KNP] = { TTYCODE_STRING, "knp" }, + [TTYC_KNXT2] = { TTYCODE_STRING, "kNXT" }, + [TTYC_KNXT3] = { TTYCODE_STRING, "kNXT3" }, + [TTYC_KNXT4] = { TTYCODE_STRING, "kNXT4" }, + [TTYC_KNXT5] = { TTYCODE_STRING, "kNXT5" }, + [TTYC_KNXT6] = { TTYCODE_STRING, "kNXT6" }, + [TTYC_KNXT7] = { TTYCODE_STRING, "kNXT7" }, + [TTYC_KPP] = { TTYCODE_STRING, "kpp" }, + [TTYC_KPRV2] = { TTYCODE_STRING, "kPRV" }, + [TTYC_KPRV3] = { TTYCODE_STRING, "kPRV3" }, + [TTYC_KPRV4] = { TTYCODE_STRING, "kPRV4" }, + [TTYC_KPRV5] = { TTYCODE_STRING, "kPRV5" }, + [TTYC_KPRV6] = { TTYCODE_STRING, "kPRV6" }, + [TTYC_KPRV7] = { TTYCODE_STRING, "kPRV7" }, + [TTYC_KRIT2] = { TTYCODE_STRING, "kRIT" }, + [TTYC_KRIT3] = { TTYCODE_STRING, "kRIT3" }, + [TTYC_KRIT4] = { TTYCODE_STRING, "kRIT4" }, + [TTYC_KRIT5] = { TTYCODE_STRING, "kRIT5" }, + [TTYC_KRIT6] = { TTYCODE_STRING, "kRIT6" }, + [TTYC_KRIT7] = { TTYCODE_STRING, "kRIT7" }, + [TTYC_KUP2] = { TTYCODE_STRING, "kUP" }, + [TTYC_KUP3] = { TTYCODE_STRING, "kUP3" }, + [TTYC_KUP4] = { TTYCODE_STRING, "kUP4" }, + [TTYC_KUP5] = { TTYCODE_STRING, "kUP5" }, + [TTYC_KUP6] = { TTYCODE_STRING, "kUP6" }, + [TTYC_KUP7] = { TTYCODE_STRING, "kUP7" }, + [TTYC_MS] = { TTYCODE_STRING, "Ms" }, + [TTYC_OP] = { TTYCODE_STRING, "op" }, + [TTYC_REV] = { TTYCODE_STRING, "rev" }, + [TTYC_RI] = { TTYCODE_STRING, "ri" }, + [TTYC_RMACS] = { TTYCODE_STRING, "rmacs" }, + [TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" }, + [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, + [TTYC_SE] = { TTYCODE_STRING, "Se" }, + [TTYC_SETAB] = { TTYCODE_STRING, "setab" }, + [TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, + [TTYC_SGR0] = { TTYCODE_STRING, "sgr0" }, + [TTYC_SITM] = { TTYCODE_STRING, "sitm" }, + [TTYC_SMACS] = { TTYCODE_STRING, "smacs" }, + [TTYC_SMCUP] = { TTYCODE_STRING, "smcup" }, + [TTYC_SMKX] = { TTYCODE_STRING, "smkx" }, + [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, + [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, + [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, + [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, + [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, + [TTYC_XT] = { TTYCODE_FLAG, "XT" }, +}; + +u_int +tty_term_ncodes(void) +{ + return (nitems(tty_term_codes)); +} + char * tty_term_strip(const char *s) { @@ -309,11 +336,11 @@ tty_term_override(struct tty_term *term, const char *overrides) log_debug("%s override: %s %s", term->name, entstr, removeflag ? "@" : val); - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; if (strcmp(entstr, ent->name) != 0) continue; - code = &term->codes[ent->code]; + code = &term->codes[i]; if (removeflag) { code->type = TTYCODE_NONE; @@ -372,7 +399,7 @@ tty_term_find(char *name, int fd, char **cause) term->name = xstrdup(name); term->references = 1; term->flags = 0; - memset(term->codes, 0, sizeof term->codes); + term->codes = xcalloc (tty_term_ncodes(), sizeof *term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); /* Set up curses terminal. */ @@ -397,10 +424,10 @@ tty_term_find(char *name, int fd, char **cause) } /* Fill in codes. */ - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; - code = &term->codes[ent->code]; + code = &term->codes[i]; code->type = TTYCODE_NONE; switch (ent->type) { case TTYCODE_NONE: @@ -507,10 +534,12 @@ tty_term_free(struct tty_term *term) LIST_REMOVE(term, entry); - for (i = 0; i < NTTYCODE; i++) { + for (i = 0; i < tty_term_ncodes(); i++) { if (term->codes[i].type == TTYCODE_STRING) free(term->codes[i].value.string); } + free(term->codes); + free(term->name); free(term); } @@ -576,3 +605,35 @@ tty_term_flag(struct tty_term *term, enum tty_code_code code) log_fatalx("not a flag: %d", code); return (term->codes[code].value.flag); } + +const char * +tty_term_describe(struct tty_term *term, enum tty_code_code code) +{ + static char s[256]; + char out[128]; + + switch (term->codes[code].type) { + case TTYCODE_NONE: + xsnprintf(s, sizeof s, "%4u: %s: [missing]", + code, tty_term_codes[code].name); + break; + case TTYCODE_STRING: + strnvis(out, term->codes[code].value.string, sizeof out, + VIS_OCTAL|VIS_TAB|VIS_NL); + xsnprintf(s, sizeof s, "%4u: %s: (string) %s", + code, tty_term_codes[code].name, + out); + break; + case TTYCODE_NUMBER: + xsnprintf(s, sizeof s, "%4u: %s: (number) %d", + code, tty_term_codes[code].name, + term->codes[code].value.number); + break; + case TTYCODE_FLAG: + xsnprintf(s, sizeof s, "%4u: %s: (flag) %s", + code, tty_term_codes[code].name, + term->codes[code].value.flag ? "true" : "false"); + break; + } + return (s); +} From 5ec3621101e13e9032d1871f02883757b18d51ac Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 29 Jul 2015 11:56:02 +0000 Subject: [PATCH 243/703] status_out and associated data structures are no longer used. --- server-client.c | 2 -- status.c | 10 ---------- tmux.h | 15 --------------- 3 files changed, 27 deletions(-) diff --git a/server-client.c b/server-client.c index c2c7070f..f33018e4 100644 --- a/server-client.c +++ b/server-client.c @@ -115,8 +115,6 @@ server_client_create(int fd) c->tty.sy = 24; screen_init(&c->status, c->tty.sx, 1, 0); - RB_INIT(&c->status_new); - RB_INIT(&c->status_old); c->message_string = NULL; TAILQ_INIT(&c->message_log); diff --git a/status.c b/status.c index 16f4fa70..d9501f02 100644 --- a/status.c +++ b/status.c @@ -142,16 +142,6 @@ status_prompt_save_history(void) } -/* Status output tree. */ -RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); - -/* Output tree comparison function. */ -int -status_out_cmp(struct status_out *so1, struct status_out *so2) -{ - return (strcmp(so1->cmd, so2->cmd)); -} - /* Get screen line of status line. -1 means off. */ int status_at_line(struct client *c) diff --git a/tmux.h b/tmux.h index b3456922..0f9cb26e 100644 --- a/tmux.h +++ b/tmux.h @@ -1176,15 +1176,6 @@ struct message_entry { TAILQ_ENTRY(message_entry) entry; }; -/* Status output data from a job. */ -struct status_out { - char *cmd; - char *out; - - RB_ENTRY(status_out) entry; -}; -RB_HEAD(status_out_tree, status_out); - /* Client connection. */ struct client { struct imsgbuf ibuf; @@ -1215,8 +1206,6 @@ struct client { struct event repeat_timer; - struct status_out_tree status_old; - struct status_out_tree status_new; struct timeval status_timer; struct screen status; @@ -1904,11 +1893,7 @@ int server_set_stdin_callback(struct client *, void (*)(struct client *, void server_unzoom_window(struct window *); /* status.c */ -int status_out_cmp(struct status_out *, struct status_out *); -RB_PROTOTYPE(status_out_tree, status_out, entry, status_out_cmp); int status_at_line(struct client *); -void status_free_jobs(struct status_out_tree *); -void status_update_jobs(struct client *); struct window *status_get_window_at(struct client *, u_int); int status_redraw(struct client *); void printflike(2, 3) status_message_set(struct client *, const char *, ...); From 736d8350e930c37b12b9e78d92def3b701040667 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 7 Aug 2015 15:06:17 +0100 Subject: [PATCH 244/703] +history-file, from Ben Boeckel. --- examples/tmux.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/tmux.vim b/examples/tmux.vim index dcb58246..0e9476ec 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -184,6 +184,7 @@ syn keyword tmuxOptsSet \ escape-time \ exit-unattached \ focus-events + \ history-file \ history-limit \ lock-after-time \ lock-command From 13b7fd82c12ff2a0812d2b378a49011c12028796 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 12 Aug 2015 08:55:20 +0000 Subject: [PATCH 245/703] Rename left/right/up/down relative to active pane to add -of suffix (left-of/right-of/etc) to remove conflict with left/right meaning leftmost or rightmost pane. From Ben Boeckel. --- cmd-find.c | 8 ++++---- tmux.1 | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 5e0b1dbb..4adfe681 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -102,10 +102,10 @@ const char *cmd_find_pane_table[][2] = { { "{top-right}", "top-right" }, { "{bottom-left}", "bottom-left" }, { "{bottom-right}", "bottom-right" }, - { "{up}", "{up}" }, - { "{down}", "{down}" }, - { "{left}", "{left}" }, - { "{right}", "{right}" }, + { "{up-of}", "{up}" }, + { "{down-of}", "{down}" }, + { "{left-of}", "{left}" }, + { "{right-up}", "{right}" }, { NULL, NULL } }; diff --git a/tmux.1 b/tmux.1 index a4422057..19f2af36 100644 --- a/tmux.1 +++ b/tmux.1 @@ -496,10 +496,10 @@ The following special tokens are available for the pane index: .It Li "{top-right}" Ta "" Ta "The top-right pane" .It Li "{bottom-left}" Ta "" Ta "The bottom-left pane" .It Li "{bottom-right}" Ta "" Ta "The bottom-right pane" -.It Li "{up}" Ta "" Ta "The pane above the active pane" -.It Li "{down}" Ta "" Ta "The pane below the active pane" -.It Li "{left}" Ta "" Ta "The pane to the left of the active pane" -.It Li "{right}" Ta "" Ta "The pane to the right of the active pane" +.It Li "{up-of}" Ta "" Ta "The pane above the active pane" +.It Li "{down-of}" Ta "" Ta "The pane below the active pane" +.It Li "{left-of}" Ta "" Ta "The pane to the left of the active pane" +.It Li "{right-of}" Ta "" Ta "The pane to the right of the active pane" .El .Pp The tokens From 46aa92420a4d3af7d273155aabb646d4bc91bb01 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 13 Aug 2015 15:02:23 +0000 Subject: [PATCH 246/703] right-up should be right-of, also rename the values too. --- cmd-find.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 4adfe681..87dacfb4 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -102,10 +102,10 @@ const char *cmd_find_pane_table[][2] = { { "{top-right}", "top-right" }, { "{bottom-left}", "bottom-left" }, { "{bottom-right}", "bottom-right" }, - { "{up-of}", "{up}" }, - { "{down-of}", "{down}" }, - { "{left-of}", "{left}" }, - { "{right-up}", "{right}" }, + { "{up-of}", "{up-of}" }, + { "{down-of}", "{down-of}" }, + { "{left-of}", "{left-of}" }, + { "{right-of}", "{right-of}" }, { NULL, NULL } }; @@ -699,22 +699,22 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) return (-1); fs->wp = fs->w->last; return (0); - } else if (strcmp(pane, "{up}") == 0) { + } else if (strcmp(pane, "{up-of}") == 0) { fs->wp = window_pane_find_up(fs->w->active); if (fs->wp == NULL) return (-1); return (0); - } else if (strcmp(pane, "{down}") == 0) { + } else if (strcmp(pane, "{down-of}") == 0) { fs->wp = window_pane_find_down(fs->w->active); if (fs->wp == NULL) return (-1); return (0); - } else if (strcmp(pane, "{left}") == 0) { + } else if (strcmp(pane, "{left-of}") == 0) { fs->wp = window_pane_find_left(fs->w->active); if (fs->wp == NULL) return (-1); return (0); - } else if (strcmp(pane, "{right}") == 0) { + } else if (strcmp(pane, "{right-of}") == 0) { fs->wp = window_pane_find_right(fs->w->active); if (fs->wp == NULL) return (-1); From f5357ed940bd62fefdcca9791a41ca762db42599 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 15 Aug 2015 09:53:19 +0100 Subject: [PATCH 247/703] Handle \ at EOL from Daniel Hahler. --- examples/tmux.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index 0e9476ec..6f85db5b 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -266,7 +266,9 @@ syn match tmuxOptions /\s-\a\+/ display syn match tmuxVariable /\w\+=/ display syn match tmuxVariableExpansion /\${\=\w\+}\=/ display -syn region tmuxComment start=/#/ end=/$/ contains=tmuxTodo display oneline +" Comments can span multiple lines, when the newline is escaped +" (with a single) backslash at the end. +syn region tmuxComment start=/#/ skip=/\\\@ Date: Sun, 16 Aug 2015 08:57:34 +0000 Subject: [PATCH 248/703] Come out of copy mode when history is cleared. --- cmd-clear-history.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd-clear-history.c b/cmd-clear-history.c index e1342880..88dbbcf7 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -45,6 +45,9 @@ cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); gd = wp->base.grid; + if (wp->mode == &window_copy_mode) + window_pane_reset_mode(wp); + grid_move_lines(gd, 0, gd->hsize, gd->sy); gd->hsize = 0; From 3be116bb6ef88196294f62749c51c49064c25576 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 17 Aug 2015 11:58:27 -0400 Subject: [PATCH 249/703] -rdynamic only for gcc Closes #56 --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index fe520a01..ac5ff3eb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,11 +23,11 @@ endif CFLAGS += -Wno-unused-parameter -Wno-unused-variable CFLAGS += -Ilibssh/include/ -Imsgpack/src -CFLAGS += -rdynamic # for stack traces # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. if IS_GCC +CFLAGS += -rdynamic # for stack traces CFLAGS += -std=gnu99 if IS_DEBUG CFLAGS += -O0 -g From 3219e0314e3d1d39a57db330faa5693ce0264244 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 24 Aug 2015 22:49:13 +0000 Subject: [PATCH 250/703] In grid_duplicate_lines, if the line is empty (cellsize == 0) then clear the destination celldata pointer rather than leaving a stale copy of the source pointer (which may later be freed). Fixes a crash found by Kuang-che Wu. --- grid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 7e086143..c339cdc5 100644 --- a/grid.c +++ b/grid.c @@ -652,7 +652,8 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, srcl->cellsize, sizeof *dstl->celldata); memcpy(dstl->celldata, srcl->celldata, srcl->cellsize * sizeof *dstl->celldata); - } + } else + dstl->celldata = NULL; sy++; dy++; From 2ffbd5b5f05dded1564ba32a6a00b0b417439b2f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 25 Aug 2015 15:00:05 +0000 Subject: [PATCH 251/703] When searching for tabs, start from screen width, fixes out-of-bounds read found by Kuang-che Wu. --- input.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index 095816c3..7a334810 100644 --- a/input.c +++ b/input.c @@ -1199,6 +1199,7 @@ input_csi_dispatch(struct input_ctx *ictx) struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; + u_int cx; if (ictx->flags & INPUT_DISCARD) return (0); @@ -1217,12 +1218,16 @@ input_csi_dispatch(struct input_ctx *ictx) switch (entry->type) { case INPUT_CSI_CBT: /* Find the previous tab point, n times. */ + cx = s->cx; + if (cx > screen_size_x(s) - 1) + cx = screen_size_x(s) - 1; n = input_get(ictx, 0, 1, 1); - while (s->cx > 0 && n-- > 0) { + while (cx > 0 && n-- > 0) { do - s->cx--; - while (s->cx > 0 && !bit_test(s->tabs, s->cx)); + cx--; + while (cx > 0 && !bit_test(s->tabs, cx)); } + s->cx = cx; break; case INPUT_CSI_CUB: screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); From fc58e44f89b876aa051f849361bc215c1f965696 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 07:49:24 +0000 Subject: [PATCH 252/703] Only do the automatic-rename dance if the pane has changed (seen output, or new active pane). --- input.c | 2 ++ names.c | 4 ++++ tmux.h | 1 + window.c | 1 + 4 files changed, 8 insertions(+) diff --git a/input.c b/input.c index 7a334810..7ec35c88 100644 --- a/input.c +++ b/input.c @@ -844,6 +844,8 @@ input_parse(struct window_pane *wp) if (EVBUFFER_LENGTH(evb) == 0) return; + wp->flags |= PANE_CHANGED; + wp->window->flags |= WINDOW_ACTIVITY; wp->window->flags &= ~WINDOW_SILENCE; diff --git a/names.c b/names.c index 1ceb83c0..0a3af903 100644 --- a/names.c +++ b/names.c @@ -50,6 +50,10 @@ window_name_callback(unused int fd, unused short events, void *data) if (w->active == NULL) return; + if (~w->active->flags & PANE_CHANGED) + return; + w->active->flags &= ~PANE_CHANGED; + if (!options_get_number(&w->options, "automatic-rename")) { if (event_initialized(&w->name_timer)) event_del(&w->name_timer); diff --git a/tmux.h b/tmux.h index 0f9cb26e..2d0ccede 100644 --- a/tmux.h +++ b/tmux.h @@ -826,6 +826,7 @@ struct window_pane { #define PANE_RESIZE 0x8 #define PANE_FOCUSPUSH 0x10 #define PANE_INPUTOFF 0x20 +#define PANE_CHANGED 0x40 int argc; char **argv; diff --git a/window.c b/window.c index 6351eff5..395574f7 100644 --- a/window.c +++ b/window.c @@ -412,6 +412,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp) return (1); } w->active->active_point = next_active_point++; + w->active->flags |= PANE_CHANGED; return (1); } From 25faca41eb30bc7cba95da0103bfa6fdda4d9a8b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 07:55:43 +0000 Subject: [PATCH 253/703] Error messages should not have a trailing period. --- server-window.c | 2 +- window.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server-window.c b/server-window.c index 1b3938d3..c96c2602 100644 --- a/server-window.c +++ b/server-window.c @@ -147,7 +147,7 @@ server_window_check_silence(struct session *s, struct winlink *wl) * from this window. */ if (gettimeofday(&w->silence_timer, NULL) != 0) - fatal("gettimeofday failed."); + fatal("gettimeofday failed"); return (0); } diff --git a/window.c b/window.c index 395574f7..77c7a633 100644 --- a/window.c +++ b/window.c @@ -934,7 +934,7 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) */ wp->window->flags |= WINDOW_SILENCE; if (gettimeofday(&wp->window->silence_timer, NULL) != 0) - fatal("gettimeofday failed."); + fatal("gettimeofday failed"); return; start_timer: From ee9f708500c2a6987b7a3c0aba2ac5e88812d8aa Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 10:06:52 +0000 Subject: [PATCH 254/703] Allow environment variables in #{}. --- format.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index aff34abb..429f6185 100644 --- a/format.c +++ b/format.c @@ -322,6 +322,7 @@ format_find(struct format_tree *ft, const char *key) { struct format_entry *fe, fe_find; struct options_entry *o; + struct environ_entry *envent; static char s[16]; o = options_find(&global_options, key); @@ -347,9 +348,18 @@ format_find(struct format_tree *ft, const char *key) fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); - if (fe == NULL) - return (NULL); - return (fe->value); + if (fe != NULL) + return (fe->value); + + envent = NULL; + if (ft->s != NULL) + envent = environ_find(&ft->s->environ, key); + if (envent == NULL) + envent = environ_find(&global_environ, key); + if (envent != NULL) + return (envent->value); + + return (NULL); } /* @@ -371,7 +381,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, copy[keylen] = '\0'; /* Is there a length limit or whatnot? */ - if (!islower((u_char) *copy) && *copy != '@' && *copy != '?') { + if (!isalpha((u_char) *copy) && *copy != '@' && *copy != '?') { while (*copy != ':' && *copy != '\0') { switch (*copy) { case '=': From b6618b631b783f652ca06c9669b20e40ef30e336 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 11:38:27 +0000 Subject: [PATCH 255/703] Move format job cleanup onto its own timer. --- format.c | 17 ++++++++++++++++- server.c | 2 -- tmux.h | 1 - 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index 429f6185..09b16e76 100644 --- a/format.c +++ b/format.c @@ -37,6 +37,7 @@ void format_job_callback(struct job *); const char *format_job_get(struct format_tree *, const char *); +void format_job_timer(int, short, void *); int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); @@ -63,6 +64,7 @@ struct format_job { }; /* Format job tree. */ +struct event format_job_event; int format_job_cmp(struct format_job *, struct format_job *); RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); RB_PROTOTYPE(format_job_tree, format_job, entry, format_job_cmp); @@ -191,6 +193,8 @@ format_job_callback(struct job *job) server_status_client(c); fj->status = 0; } + + log_debug("%s: %s: %s", __func__, fj->cmd, fj->out); } /* Find a job. */ @@ -226,10 +230,11 @@ format_job_get(struct format_tree *ft, const char *cmd) /* Remove old jobs. */ void -format_clean(void) +format_job_timer(unused int fd, unused short events, unused void *arg) { struct format_job *fj, *fj1; time_t now; + struct timeval tv = { .tv_sec = 60 }; now = time(NULL); RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) { @@ -237,6 +242,8 @@ format_clean(void) continue; RB_REMOVE(format_job_tree, &format_jobs, fj); + log_debug("%s: %s", __func__, fj->cmd); + if (fj->job != NULL) job_free(fj->job); @@ -245,6 +252,9 @@ format_clean(void) free(fj); } + + evtimer_del(&format_job_event); + evtimer_add(&format_job_event, &tv); } /* Create a new tree. */ @@ -261,6 +271,11 @@ format_create_status(int status) struct format_tree *ft; char host[HOST_NAME_MAX + 1], *ptr; + if (!event_initialized(&format_job_event)) { + evtimer_set(&format_job_event, format_job_timer, NULL); + format_job_timer(-1, 0, NULL); + } + ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); ft->status = status; diff --git a/server.c b/server.c index d1c0bb3b..c4de510e 100644 --- a/server.c +++ b/server.c @@ -522,8 +522,6 @@ server_second_callback(unused int fd, unused short events, unused void *arg) server_client_status_timer(); - format_clean(); - evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; diff --git a/tmux.h b/tmux.h index 2d0ccede..5859cc53 100644 --- a/tmux.h +++ b/tmux.h @@ -1449,7 +1449,6 @@ void cfg_show_causes(struct session *); /* format.c */ struct format_tree; -void format_clean(void); struct format_tree *format_create(void); struct format_tree *format_create_status(int); void format_free(struct format_tree *); From 18d4802a7bcfc08948dccfd9084144b043ac591b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 12:15:54 +0000 Subject: [PATCH 256/703] Log time with message. --- log.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/log.c b/log.c index 0f42f66f..9a53775e 100644 --- a/log.c +++ b/log.c @@ -67,11 +67,13 @@ void log_vwrite(const char *msg, va_list ap) { char *fmt; + time_t t; if (log_file == NULL) return; - if (asprintf(&fmt, "%s\n", msg) == -1) + t = time(NULL); + if (asprintf(&fmt, "%lld %s\n", (long long)t, msg) == -1) exit(1); if (vfprintf(log_file, fmt, ap) == -1) exit(1); From 75d10058a41d8e95dda126d460a119a9037e9345 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 12:16:28 +0000 Subject: [PATCH 257/703] Run status update on a per-client timer at status-interval. --- cmd-attach-session.c | 2 ++ cmd-new-session.c | 1 + cmd-set-option.c | 5 +++- cmd-switch-client.c | 1 + format.c | 2 +- server-client.c | 38 ++---------------------------- server-fn.c | 1 + server.c | 2 -- status.c | 56 ++++++++++++++++++++++++++++++++++++++++---- tmux.h | 4 +++- 10 files changed, 67 insertions(+), 45 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index e7cde0f0..9f357d6e 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -133,6 +133,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s); server_redraw_client(c); @@ -177,6 +178,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s); server_redraw_client(c); diff --git a/cmd-new-session.c b/cmd-new-session.c index 1533e5f0..2125ee88 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -275,6 +275,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) else if (c->session != NULL) c->last_session = c->session; c->session = s; + status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s); server_redraw_client(c); diff --git a/cmd-set-option.c b/cmd-set-option.c index 761cee93..260961cb 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -176,7 +176,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - /* Start or stop timers when automatic-rename changed. */ + /* Start or stop timers when name or status options changed. */ if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) @@ -185,6 +185,9 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) evtimer_del(&w->name_timer); } } + if (strcmp(oe->name, "status") == 0 || + strcmp(oe->name, "status-interval") == 0) + status_timer_start_all(); /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 4bac540d..6a79c7b7 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -127,6 +127,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session != NULL) c->last_session = c->session; c->session = s; + status_timer_start(c); session_update_activity(s); recalculate_sizes(); diff --git a/format.c b/format.c index 09b16e76..39843674 100644 --- a/format.c +++ b/format.c @@ -247,7 +247,7 @@ format_job_timer(unused int fd, unused short events, unused void *arg) if (fj->job != NULL) job_free(fj->job); - free((void*)fj->cmd); + free((void *)fj->cmd); free(fj->out); free(fj); diff --git a/server-client.c b/server-client.c index f33018e4..82cec36c 100644 --- a/server-client.c +++ b/server-client.c @@ -188,6 +188,8 @@ server_client_lost(struct client *c) if (c->stderr_data != c->stdout_data) evbuffer_free(c->stderr_data); + if (event_initialized(&c->status_timer)) + evtimer_del(&c->status_timer); screen_free(&c->status); free(c->title); @@ -289,42 +291,6 @@ client_lost: server_client_lost(c); } -/* Handle client status timer. */ -void -server_client_status_timer(void) -{ - struct client *c; - struct session *s; - struct timeval tv; - int interval; - time_t difference; - - if (gettimeofday(&tv, NULL) != 0) - fatal("gettimeofday failed"); - - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL) - continue; - if (c->message_string != NULL || c->prompt_string != NULL) { - /* - * Don't need timed redraw for messages/prompts so bail - * now. The status timer isn't reset when they are - * redrawn anyway. - */ - continue; - } - s = c->session; - - if (!options_get_number(&s->options, "status")) - continue; - interval = options_get_number(&s->options, "status-interval"); - - difference = tv.tv_sec - c->status_timer.tv_sec; - if (interval != 0 && difference >= interval) - c->flags |= CLIENT_STATUS; - } -} - /* Check for mouse keys. */ int server_client_check_mouse(struct client *c) diff --git a/server-fn.c b/server-fn.c index 0e6e4d46..469dd82d 100644 --- a/server-fn.c +++ b/server-fn.c @@ -419,6 +419,7 @@ server_destroy_session(struct session *s) } else { c->last_session = NULL; c->session = s_new; + status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s_new); server_redraw_client(c); diff --git a/server.c b/server.c index c4de510e..cfd2dd34 100644 --- a/server.c +++ b/server.c @@ -520,8 +520,6 @@ server_second_callback(unused int fd, unused short events, unused void *arg) } } - server_client_status_timer(); - evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; diff --git a/status.c b/status.c index d9501f02..93cee2b1 100644 --- a/status.c +++ b/status.c @@ -37,6 +37,7 @@ char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); void status_message_callback(int, short, void *); +void status_timer_callback(int, short, void *); const char *status_prompt_up_history(u_int *); const char *status_prompt_down_history(u_int *); @@ -142,6 +143,55 @@ status_prompt_save_history(void) } +/* Status timer callback. */ +void +status_timer_callback(unused int fd, unused short events, void *arg) +{ + struct client *c = arg; + struct session *s = c->session; + struct timeval tv; + + evtimer_del(&c->status_timer); + + if (s == NULL) + return; + + if (c->message_string == NULL && c->prompt_string == NULL) + c->flags |= CLIENT_STATUS; + + timerclear(&tv); + tv.tv_sec = options_get_number(&s->options, "status-interval"); + + if (tv.tv_sec != 0) + evtimer_add(&c->status_timer, &tv); + log_debug("client %d, status interval %d", c->ibuf.fd, (int)tv.tv_sec); +} + +/* Start status timer for client. */ +void +status_timer_start(struct client *c) +{ + struct session *s = c->session; + + if (event_initialized(&c->status_timer)) + evtimer_del(&c->status_timer); + else + evtimer_set(&c->status_timer, status_timer_callback, c); + + if (s != NULL && options_get_number(&s->options, "status")) + status_timer_callback(-1, 0, c); +} + +/* Start status timer for all clients. */ +void +status_timer_start_all(void) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) + status_timer_start(c); +} + /* Get screen line of status line. -1 means off. */ int status_at_line(struct client *c) @@ -244,10 +294,8 @@ status_redraw(struct client *c) left = right = NULL; larrow = rarrow = 0; - /* Update status timer. */ - if (gettimeofday(&c->status_timer, NULL) != 0) - fatal("gettimeofday failed"); - t = c->status_timer.tv_sec; + /* Store current time. */ + t = time(NULL); /* Set up default colour. */ style_apply(&stdgc, &s->options, "status-style"); diff --git a/tmux.h b/tmux.h index 5859cc53..6b82e79c 100644 --- a/tmux.h +++ b/tmux.h @@ -1207,7 +1207,7 @@ struct client { struct event repeat_timer; - struct timeval status_timer; + struct event status_timer; struct screen status; #define CLIENT_TERMINAL 0x1 @@ -1893,6 +1893,8 @@ int server_set_stdin_callback(struct client *, void (*)(struct client *, void server_unzoom_window(struct window *); /* status.c */ +void status_timer_start(struct client *); +void status_timer_start_all(void); int status_at_line(struct client *); struct window *status_get_window_at(struct client *, u_int); int status_redraw(struct client *); From 6419f665237d4280966c298a38836b53746b8dcc Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 12:25:42 +0000 Subject: [PATCH 258/703] Give clock mode its own timer. --- server.c | 9 --------- tmux.h | 1 - window-choose.c | 1 - window-clock.c | 50 +++++++++++++++++++++++++++++-------------------- window-copy.c | 1 - 5 files changed, 30 insertions(+), 32 deletions(-) diff --git a/server.c b/server.c index cfd2dd34..d39e3f56 100644 --- a/server.c +++ b/server.c @@ -504,8 +504,6 @@ server_child_stopped(pid_t pid, int status) void server_second_callback(unused int fd, unused short events, unused void *arg) { - struct window *w; - struct window_pane *wp; struct timeval tv; if (options_get_number(&global_s_options, "lock-server")) @@ -513,13 +511,6 @@ server_second_callback(unused int fd, unused short events, unused void *arg) else server_lock_sessions(); - RB_FOREACH(w, windows, &windows) { - TAILQ_FOREACH(wp, &w->panes, entry) { - if (wp->mode != NULL && wp->mode->timer != NULL) - wp->mode->timer(wp); - } - } - evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; diff --git a/tmux.h b/tmux.h index 6b82e79c..9fae33bd 100644 --- a/tmux.h +++ b/tmux.h @@ -777,7 +777,6 @@ struct window_mode { void (*resize)(struct window_pane *, u_int, u_int); void (*key)(struct window_pane *, struct client *, struct session *, int, struct mouse_event *); - void (*timer)(struct window_pane *); }; /* Structures for choose mode. */ diff --git a/window-choose.c b/window-choose.c index c71fea3d..37baf0d7 100644 --- a/window-choose.c +++ b/window-choose.c @@ -57,7 +57,6 @@ const struct window_mode window_choose_mode = { window_choose_free, window_choose_resize, window_choose_key, - NULL, }; struct window_choose_mode_item { diff --git a/window-clock.c b/window-clock.c index 3cabd9e9..4a497891 100644 --- a/window-clock.c +++ b/window-clock.c @@ -29,8 +29,8 @@ void window_clock_free(struct window_pane *); void window_clock_resize(struct window_pane *, u_int, u_int); void window_clock_key(struct window_pane *, struct client *, struct session *, int, struct mouse_event *); -void window_clock_timer(struct window_pane *); +void window_clock_timer_callback(int, short, void *); void window_clock_draw_screen(struct window_pane *); const struct window_mode window_clock_mode = { @@ -38,12 +38,12 @@ const struct window_mode window_clock_mode = { window_clock_free, window_clock_resize, window_clock_key, - window_clock_timer, }; struct window_clock_mode_data { struct screen screen; time_t tim; + struct event timer; }; const char window_clock_table[14][5][5] = { @@ -119,15 +119,42 @@ const char window_clock_table[14][5][5] = { { 1,0,0,0,1 } }, }; +void +window_clock_timer_callback(unused int fd, unused short events, void *arg) +{ + struct window_pane *wp = arg; + struct window_clock_mode_data *data = wp->modedata; + struct tm now, then; + time_t t; + struct timeval tv = { .tv_sec = 1 }; + + evtimer_del(&data->timer); + evtimer_add(&data->timer, &tv); + + t = time(NULL); + gmtime_r(&t, &now); + gmtime_r(&data->tim, &then); + if (now.tm_min == then.tm_min) + return; + data->tim = t; + + window_clock_draw_screen(wp); + server_redraw_window(wp->window); +} + struct screen * window_clock_init(struct window_pane *wp) { struct window_clock_mode_data *data; struct screen *s; + struct timeval tv = { .tv_sec = 1 }; wp->modedata = data = xmalloc(sizeof *data); data->tim = time(NULL); + evtimer_set(&data->timer, window_clock_timer_callback, wp); + evtimer_add(&data->timer, &tv); + s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; @@ -142,6 +169,7 @@ window_clock_free(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; + evtimer_del(&data->timer); screen_free(&data->screen); free(data); } @@ -163,24 +191,6 @@ window_clock_key(struct window_pane *wp, unused struct client *c, window_pane_reset_mode(wp); } -void -window_clock_timer(struct window_pane *wp) -{ - struct window_clock_mode_data *data = wp->modedata; - struct tm now, then; - time_t t; - - t = time(NULL); - gmtime_r(&t, &now); - gmtime_r(&data->tim, &then); - if (now.tm_min == then.tm_min) - return; - data->tim = t; - - window_clock_draw_screen(wp); - server_redraw_window(wp->window); -} - void window_clock_draw_screen(struct window_pane *wp) { diff --git a/window-copy.c b/window-copy.c index 7d5bae4c..850b437a 100644 --- a/window-copy.c +++ b/window-copy.c @@ -91,7 +91,6 @@ const struct window_mode window_copy_mode = { window_copy_free, window_copy_resize, window_copy_key, - NULL, }; enum window_copy_input_type { From 675def039652e69d8fd5f229eff2128116e1d328 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 12:31:55 +0000 Subject: [PATCH 259/703] Remove the lock-server option which is a bit redundant, it isn't that different without it. --- options-table.c | 5 ----- server.c | 27 +-------------------------- tmux.1 | 17 +---------------- 3 files changed, 2 insertions(+), 47 deletions(-) diff --git a/options-table.c b/options-table.c index 7f75d22c..b9431483 100644 --- a/options-table.c +++ b/options-table.c @@ -206,11 +206,6 @@ const struct options_table_entry session_options_table[] = { .default_str = "lock -np" }, - { .name = "lock-server", - .type = OPTIONS_TABLE_FLAG, - .default_num = 1 - }, - { .name = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0, diff --git a/server.c b/server.c index d39e3f56..f9b0dc52 100644 --- a/server.c +++ b/server.c @@ -64,7 +64,6 @@ void server_child_signal(void); void server_child_exited(pid_t, int); void server_child_stopped(pid_t, int); void server_second_callback(int, short, void *); -void server_lock_server(void); void server_lock_sessions(void); /* Set marked pane. */ @@ -506,10 +505,7 @@ server_second_callback(unused int fd, unused short events, unused void *arg) { struct timeval tv; - if (options_get_number(&global_s_options, "lock-server")) - server_lock_server(); - else - server_lock_sessions(); + server_lock_sessions(); evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); @@ -517,27 +513,6 @@ server_second_callback(unused int fd, unused short events, unused void *arg) evtimer_add(&server_ev_second, &tv); } -/* Lock the server if ALL sessions have hit the time limit. */ -void -server_lock_server(void) -{ - struct session *s; - int timeout; - time_t t; - - t = time(NULL); - RB_FOREACH(s, sessions, &sessions) { - if (s->flags & SESSION_UNATTACHED) - continue; - timeout = options_get_number(&s->options, "lock-after-time"); - if (timeout <= 0 || t <= s->activity_time.tv_sec + timeout) - return; /* not timed out */ - } - - server_lock(); - recalculate_sizes(); -} - /* Lock any sessions which have timed out. */ void server_lock_sessions(void) diff --git a/tmux.1 b/tmux.1 index 19f2af36..8e07fc2a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2561,9 +2561,7 @@ Lock the session (like the .Ic lock-session command) after .Ar number -seconds of inactivity, or the entire server (all sessions) if the -.Ic lock-server -option is set. +seconds of inactivity. The default is not to lock (set to 0). .It Ic lock-command Ar shell-command Command to run when locking each client. @@ -2571,19 +2569,6 @@ The default is to run .Xr lock 1 with .Fl np . -.It Xo Ic lock-server -.Op Ic on | off -.Xc -If this option is -.Ic on -(the default), -instead of each session locking individually as each has been -idle for -.Ic lock-after-time , -the entire server will lock after -.Em all -sessions would have locked. -This has no effect as a session option; it must be set as a global option. .It Ic message-command-style Ar style Set status line message command style, where .Ar style From 57cc4d45d52e0af562172ac4044f311e1bb00c64 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:01:03 +0000 Subject: [PATCH 260/703] Make session_update_activity more useful and use it in more places. --- cmd-attach-session.c | 4 ++-- cmd-new-session.c | 2 +- cmd-switch-client.c | 2 +- server-client.c | 10 +++++----- server-fn.c | 2 +- session.c | 21 +++++++++++++-------- tmux.h | 2 +- 7 files changed, 24 insertions(+), 19 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 9f357d6e..356bd4aa 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -135,7 +135,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, c->session = s; status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s); + session_update_activity(s, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { @@ -180,7 +180,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, c->session = s; status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s); + session_update_activity(s, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; diff --git a/cmd-new-session.c b/cmd-new-session.c index 2125ee88..fa4f1553 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -277,7 +277,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) c->session = s; status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s); + session_update_activity(s, NULL); server_redraw_client(c); } recalculate_sizes(); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 6a79c7b7..8bc8a3c5 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -128,7 +128,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) c->last_session = c->session; c->session = s; status_timer_start(c); - session_update_activity(s); + session_update_activity(s, NULL); recalculate_sizes(); server_check_unattached(); diff --git a/server-client.c b/server-client.c index 82cec36c..da24e222 100644 --- a/server-client.c +++ b/server-client.c @@ -561,9 +561,7 @@ server_client_handle_key(struct client *c, int key) /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); - memcpy(&s->last_activity_time, &s->activity_time, - sizeof s->last_activity_time); - memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); + session_update_activity(s, &c->activity_time); /* Number keys jump to pane in identify mode. */ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { @@ -981,6 +979,7 @@ server_client_msg_dispatch(struct client *c) struct msg_stdin_data stdindata; const char *data; ssize_t n, datalen; + struct session *s; if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) return (-1); @@ -1064,11 +1063,12 @@ server_client_msg_dispatch(struct client *c) if (c->tty.fd == -1) /* exited in the meantime */ break; + s = c->session; if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday"); - if (c->session != NULL) - session_update_activity(c->session); + if (s != NULL) + session_update_activity(s, &c->activity_time); tty_start_tty(&c->tty); server_redraw_client(c); diff --git a/server-fn.c b/server-fn.c index 469dd82d..cddd762f 100644 --- a/server-fn.c +++ b/server-fn.c @@ -421,7 +421,7 @@ server_destroy_session(struct session *s) c->session = s_new; status_timer_start(c); notify_attached_session_changed(c); - session_update_activity(s_new); + session_update_activity(s_new, NULL); server_redraw_client(c); } } diff --git a/session.c b/session.c index 5061083a..7572ac21 100644 --- a/session.c +++ b/session.c @@ -112,10 +112,6 @@ session_create(const char *name, int argc, char **argv, const char *path, s->references = 1; s->flags = 0; - if (gettimeofday(&s->creation_time, NULL) != 0) - fatal("gettimeofday failed"); - session_update_activity(s); - s->cwd = dup(cwd); s->curw = NULL; @@ -133,6 +129,10 @@ session_create(const char *name, int argc, char **argv, const char *path, memcpy(s->tio, tio, sizeof *s->tio); } + if (gettimeofday(&s->creation_time, NULL) != 0) + fatal("gettimeofday failed"); + session_update_activity(s, &s->creation_time); + s->sx = sx; s->sy = sy; @@ -224,12 +224,17 @@ session_check_name(const char *name) return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); } -/* Update session active time. */ +/* Update activity time. */ void -session_update_activity(struct session *s) +session_update_activity(struct session *s, struct timeval *from) { - if (gettimeofday(&s->activity_time, NULL) != 0) - fatal("gettimeofday"); + struct timeval *last = &s->last_activity_time; + + memcpy(last, &s->activity_time, sizeof *last); + if (from == NULL) + gettimeofday(&s->activity_time, NULL); + else + memcpy(&s->activity_time, from, sizeof s->activity_time); } /* Find the next usable session. */ diff --git a/tmux.h b/tmux.h index 9fae33bd..cc38dab8 100644 --- a/tmux.h +++ b/tmux.h @@ -2248,7 +2248,7 @@ struct session *session_create(const char *, int, char **, const char *, void session_destroy(struct session *); void session_unref(struct session *); int session_check_name(const char *); -void session_update_activity(struct session *); +void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); struct winlink *session_new(struct session *, const char *, int, char **, From f6a0f8730efab9c52eaccbb62b5b1d27e8be7949 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:12:20 +0000 Subject: [PATCH 261/703] Per-session timers for locking, and remove the global one-second timer. --- cmd-set-option.c | 2 +- server.c | 45 ++------------------------------------------- session.c | 40 +++++++++++++++++++++++++++++++++++++++- tmux.h | 2 ++ 4 files changed, 44 insertions(+), 45 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 260961cb..56ca91e0 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -176,7 +176,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - /* Start or stop timers when name or status options changed. */ + /* Start or stop timers if necessary. */ if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) diff --git a/server.c b/server.c index f9b0dc52..0e7733c8 100644 --- a/server.c +++ b/server.c @@ -46,7 +46,6 @@ struct clients clients; int server_fd; int server_shutdown; struct event server_ev_accept; -struct event server_ev_second; struct session *marked_session; struct winlink *marked_winlink; @@ -163,9 +162,8 @@ server_create_socket(void) int server_start(int lockfd, char *lockfile) { - int pair[2]; - struct timeval tv; - char *cause; + int pair[2]; + char *cause; /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) @@ -243,11 +241,6 @@ server_start(int lockfd, char *lockfile) server_add_accept(0); - memset(&tv, 0, sizeof tv); - tv.tv_sec = 1; - evtimer_set(&server_ev_second, server_second_callback, NULL); - evtimer_add(&server_ev_second, &tv); - set_signals(server_signal_callback); server_loop(); status_prompt_save_history(); @@ -498,37 +491,3 @@ server_child_stopped(pid_t pid, int status) } } } - -/* Handle once-per-second timer events. */ -void -server_second_callback(unused int fd, unused short events, unused void *arg) -{ - struct timeval tv; - - server_lock_sessions(); - - evtimer_del(&server_ev_second); - memset(&tv, 0, sizeof tv); - tv.tv_sec = 1; - evtimer_add(&server_ev_second, &tv); -} - -/* Lock any sessions which have timed out. */ -void -server_lock_sessions(void) -{ - struct session *s; - int timeout; - time_t t; - - t = time(NULL); - RB_FOREACH(s, sessions, &sessions) { - if (s->flags & SESSION_UNATTACHED) - continue; - timeout = options_get_number(&s->options, "lock-after-time"); - if (timeout > 0 && t > s->activity_time.tv_sec + timeout) { - server_lock_session(s); - recalculate_sizes(); - } - } -} diff --git a/session.c b/session.c index 7572ac21..041adf2d 100644 --- a/session.c +++ b/session.c @@ -33,6 +33,8 @@ struct session_groups session_groups; void session_free(int, short, void *); +void session_lock_timer(int, short, void *); + struct winlink *session_next_alert(struct winlink *); struct winlink *session_previous_alert(struct winlink *); @@ -108,7 +110,7 @@ session_create(const char *name, int argc, char **argv, const char *path, struct session *s; struct winlink *wl; - s = xmalloc(sizeof *s); + s = xcalloc(1, sizeof *s); s->references = 1; s->flags = 0; @@ -149,6 +151,10 @@ session_create(const char *name, int argc, char **argv, const char *path, } RB_INSERT(sessions, &sessions, s); + if (gettimeofday(&s->creation_time, NULL) != 0) + fatal("gettimeofday failed"); + session_update_activity(s, &s->creation_time); + if (argc >= 0) { wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); if (wl == NULL) { @@ -200,6 +206,9 @@ session_destroy(struct session *s) free(s->tio); + if (event_initialized(&s->lock_timer)) + event_del(&s->lock_timer); + session_group_remove(s); environ_free(&s->environ); options_free(&s->options); @@ -224,17 +233,46 @@ session_check_name(const char *name) return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); } +/* Lock session if it has timed out. */ +void +session_lock_timer(unused int fd, unused short events, void *arg) +{ + struct session *s = arg; + + if (s->flags & SESSION_UNATTACHED) + return; + + log_debug("session %s locked, activity time %lld", s->name, + (long long)s->activity_time.tv_sec); + + server_lock_session(s); + recalculate_sizes(); +} + /* Update activity time. */ void session_update_activity(struct session *s, struct timeval *from) { struct timeval *last = &s->last_activity_time; + struct timeval tv; memcpy(last, &s->activity_time, sizeof *last); if (from == NULL) gettimeofday(&s->activity_time, NULL); else memcpy(&s->activity_time, from, sizeof s->activity_time); + + if (evtimer_initialized(&s->lock_timer)) + evtimer_del(&s->lock_timer); + else + evtimer_set(&s->lock_timer, session_lock_timer, s); + + if (~s->flags & SESSION_UNATTACHED) { + timerclear(&tv); + tv.tv_sec = options_get_number(&s->options, "lock-after-time"); + if (tv.tv_sec != 0) + evtimer_add(&s->lock_timer, &tv); + } } /* Find the next usable session. */ diff --git a/tmux.h b/tmux.h index cc38dab8..6ac0b578 100644 --- a/tmux.h +++ b/tmux.h @@ -993,6 +993,8 @@ struct session { struct timeval activity_time; struct timeval last_activity_time; + struct event lock_timer; + u_int sx; u_int sy; From ed2a486f46010559926f49b2f95fc3ceb61fe105 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:16:03 +0000 Subject: [PATCH 262/703] Don't leak name when freeing session, from Kuang-che Wu. --- session.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/session.c b/session.c index 041adf2d..c4e64e27 100644 --- a/session.c +++ b/session.c @@ -189,8 +189,10 @@ session_free(unused int fd, unused short events, void *arg) log_debug("sesson %s freed (%d references)", s->name, s->references); - if (s->references == 0) + if (s->references == 0) { + free(s->name); free(s); + } } /* Destroy a session. */ From f957db81d9c1c17468e57729f32aa67ffd3e23e0 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:21:25 +0000 Subject: [PATCH 263/703] Remove unused prototypes. --- server.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/server.c b/server.c index 0e7733c8..d36dda12 100644 --- a/server.c +++ b/server.c @@ -62,8 +62,6 @@ void server_signal_callback(int, short, void *); void server_child_signal(void); void server_child_exited(pid_t, int); void server_child_stopped(pid_t, int); -void server_second_callback(int, short, void *); -void server_lock_sessions(void); /* Set marked pane. */ void From b0940bdf5460ec3324254b5df68b5386513641b2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 13:26:41 +0000 Subject: [PATCH 264/703] Check changed flag after restarting timer. --- names.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/names.c b/names.c index 0a3af903..aa0673ea 100644 --- a/names.c +++ b/names.c @@ -50,10 +50,6 @@ window_name_callback(unused int fd, unused short events, void *data) if (w->active == NULL) return; - if (~w->active->flags & PANE_CHANGED) - return; - w->active->flags &= ~PANE_CHANGED; - if (!options_get_number(&w->options, "automatic-rename")) { if (event_initialized(&w->name_timer)) event_del(&w->name_timer); @@ -61,6 +57,10 @@ window_name_callback(unused int fd, unused short events, void *data) } queue_window_name(w); + if (~w->active->flags & PANE_CHANGED) + return; + w->active->flags &= ~PANE_CHANGED; + name = format_window_name(w); if (strcmp(name, w->name) != 0) { window_set_name(w, name); From e2100c5f5f9c71185e2a3c410ebdd37f52d701a7 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 15:51:48 +0000 Subject: [PATCH 265/703] We now only checking for name changes when the active pane has changed, but that can only happen when we have already been woken up by a read event, so there is no need for a timer, we can just check the changed flag on the end of that read event (we already loop over the windows to check for bells etc anyway). --- cmd-set-option.c | 4 +--- names.c | 32 +++++++------------------------- server-window.c | 1 + tmux.h | 3 +-- window.c | 5 ----- 5 files changed, 10 insertions(+), 35 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 56ca91e0..631a4d0e 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -180,9 +180,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); - else if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); + w->active->flags |= PANE_CHANGED; } } if (strcmp(oe->name, "status") == 0 || diff --git a/names.c b/names.c index aa0673ea..00bc88a2 100644 --- a/names.c +++ b/names.c @@ -25,37 +25,16 @@ #include "tmux.h" -void window_name_callback(unused int, unused short, void *); - void -queue_window_name(struct window *w) +check_window_name(struct window *w) { - struct timeval tv; - - tv.tv_sec = 0; - tv.tv_usec = NAME_INTERVAL * 1000L; - - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); - evtimer_set(&w->name_timer, window_name_callback, w); - evtimer_add(&w->name_timer, &tv); -} - -void -window_name_callback(unused int fd, unused short events, void *data) -{ - struct window *w = data; - char *name; + char *name; if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) { - if (event_initialized(&w->name_timer)) - event_del(&w->name_timer); + if (!options_get_number(&w->options, "automatic-rename")) return; - } - queue_window_name(w); if (~w->active->flags & PANE_CHANGED) return; @@ -63,9 +42,12 @@ window_name_callback(unused int fd, unused short events, void *data) name = format_window_name(w); if (strcmp(name, w->name) != 0) { + log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); server_status_window(w); - } + } else + log_debug("@%u name not changed (still %s)", w->id, w->name); + free(name); } diff --git a/server-window.c b/server-window.c index c96c2602..be1dde2a 100644 --- a/server-window.c +++ b/server-window.c @@ -49,6 +49,7 @@ server_window_loop(void) server_status_session(s); } } + check_window_name(w); } } diff --git a/tmux.h b/tmux.h index 6ac0b578..395d3292 100644 --- a/tmux.h +++ b/tmux.h @@ -870,7 +870,6 @@ RB_HEAD(window_pane_tree, window_pane); struct window { u_int id; char *name; - struct event name_timer; struct timeval silence_timer; struct timeval activity_time; @@ -2209,7 +2208,7 @@ void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void queue_window_name(struct window *); +void check_window_name(struct window *); char *default_window_name(struct window *); char *format_window_name(struct window *); char *parse_window_name(const char *); diff --git a/window.c b/window.c index 77c7a633..bfd13b1c 100644 --- a/window.c +++ b/window.c @@ -299,8 +299,6 @@ window_create1(u_int sx, u_int sy) fatal("gettimeofday failed"); options_init(&w->options, &global_w_options); - if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); w->references = 0; @@ -349,9 +347,6 @@ window_destroy(struct window *w) layout_free_cell(w->saved_layout_root); free(w->old_layout); - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); - options_free(&w->options); window_destroy_panes(w); From 55b8d7456155de4e5d44136a140a599e2a2a755b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 16:10:46 +0000 Subject: [PATCH 266/703] Revert previous; we do need a timer, until I have a better idea. We can't do the name check every loop, because that is too expensive, and we can't make sure it only happens infrequently because we have no idea when the next change will happen. --- cmd-set-option.c | 4 +++- names.c | 32 +++++++++++++++++++++++++------- server-window.c | 1 - tmux.h | 3 ++- window.c | 5 +++++ 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 631a4d0e..56ca91e0 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -180,7 +180,9 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) - w->active->flags |= PANE_CHANGED; + queue_window_name(w); + else if (event_initialized(&w->name_timer)) + evtimer_del(&w->name_timer); } } if (strcmp(oe->name, "status") == 0 || diff --git a/names.c b/names.c index 00bc88a2..aa0673ea 100644 --- a/names.c +++ b/names.c @@ -25,16 +25,37 @@ #include "tmux.h" +void window_name_callback(unused int, unused short, void *); + void -check_window_name(struct window *w) +queue_window_name(struct window *w) { - char *name; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = NAME_INTERVAL * 1000L; + + if (event_initialized(&w->name_timer)) + evtimer_del(&w->name_timer); + evtimer_set(&w->name_timer, window_name_callback, w); + evtimer_add(&w->name_timer, &tv); +} + +void +window_name_callback(unused int fd, unused short events, void *data) +{ + struct window *w = data; + char *name; if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) + if (!options_get_number(&w->options, "automatic-rename")) { + if (event_initialized(&w->name_timer)) + event_del(&w->name_timer); return; + } + queue_window_name(w); if (~w->active->flags & PANE_CHANGED) return; @@ -42,12 +63,9 @@ check_window_name(struct window *w) name = format_window_name(w); if (strcmp(name, w->name) != 0) { - log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); server_status_window(w); - } else - log_debug("@%u name not changed (still %s)", w->id, w->name); - + } free(name); } diff --git a/server-window.c b/server-window.c index be1dde2a..c96c2602 100644 --- a/server-window.c +++ b/server-window.c @@ -49,7 +49,6 @@ server_window_loop(void) server_status_session(s); } } - check_window_name(w); } } diff --git a/tmux.h b/tmux.h index 395d3292..6ac0b578 100644 --- a/tmux.h +++ b/tmux.h @@ -870,6 +870,7 @@ RB_HEAD(window_pane_tree, window_pane); struct window { u_int id; char *name; + struct event name_timer; struct timeval silence_timer; struct timeval activity_time; @@ -2208,7 +2209,7 @@ void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void check_window_name(struct window *); +void queue_window_name(struct window *); char *default_window_name(struct window *); char *format_window_name(struct window *); char *parse_window_name(const char *); diff --git a/window.c b/window.c index bfd13b1c..77c7a633 100644 --- a/window.c +++ b/window.c @@ -299,6 +299,8 @@ window_create1(u_int sx, u_int sy) fatal("gettimeofday failed"); options_init(&w->options, &global_w_options); + if (options_get_number(&w->options, "automatic-rename")) + queue_window_name(w); w->references = 0; @@ -347,6 +349,9 @@ window_destroy(struct window *w) layout_free_cell(w->saved_layout_root); free(w->old_layout); + if (event_initialized(&w->name_timer)) + evtimer_del(&w->name_timer); + options_free(&w->options); window_destroy_panes(w); From 983ebb26898beaf66383c5f5aff56cb4c0f80ca4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 16:46:40 +0000 Subject: [PATCH 267/703] Allow formats to be specified as functions (in the code) so they are only evaluated on demand rather than each time a format tree is constructed. Use this for expensive formats like pane_current_command. --- format.c | 175 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 126 insertions(+), 49 deletions(-) diff --git a/format.c b/format.c index 39843674..a885bb85 100644 --- a/format.c +++ b/format.c @@ -35,14 +35,23 @@ * string. */ +struct format_entry; +typedef void (*format_cb)(struct format_tree *, struct format_entry *); + void format_job_callback(struct job *); const char *format_job_get(struct format_tree *, const char *); void format_job_timer(int, short, void *); +void format_cb_host(struct format_tree *, struct format_entry *); +void format_cb_host_short(struct format_tree *, struct format_entry *); +void format_cb_pid(struct format_tree *, struct format_entry *); +void format_cb_start_command(struct format_tree *, struct format_entry *); +void format_cb_current_command(struct format_tree *, struct format_entry *); + +void format_add_cb(struct format_tree *, const char *, format_cb); int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); char *format_time_string(time_t); -char *format_get_command(struct window_pane *); void format_defaults_pane_tabs(struct format_tree *, struct window_pane *); void format_defaults_session(struct format_tree *, struct session *); @@ -79,18 +88,19 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) /* Entry in format tree. */ struct format_entry { - char *key; - char *value; - - RB_ENTRY(format_entry) entry; + char *key; + char *value; + format_cb cb; + RB_ENTRY(format_entry) entry; }; /* Format entry tree. */ struct format_tree { - struct window *w; - struct session *s; + struct window *w; + struct session *s; + struct window_pane *wp; - int status; + int status; RB_HEAD(format_entry_tree, format_entry) tree; }; @@ -257,6 +267,75 @@ format_job_timer(unused int fd, unused short events, unused void *arg) evtimer_add(&format_job_event, &tv); } +/* Callback for host. */ +void +format_cb_host(unused struct format_tree *ft, struct format_entry *fe) +{ + char host[HOST_NAME_MAX + 1]; + + if (gethostname(host, sizeof host) != 0) + fe->value = xstrdup(""); + else + fe->value = xstrdup(host); +} + +/* Callback for host_short. */ +void +format_cb_host_short(unused struct format_tree *ft, struct format_entry *fe) +{ + char host[HOST_NAME_MAX + 1], *cp; + + if (gethostname(host, sizeof host) != 0) + fe->value = xstrdup(""); + else { + if ((cp = strchr(host, '.')) != NULL) + *cp = '\0'; + fe->value = xstrdup(host); + } +} + +/* Callback for pid. */ +void +format_cb_pid(unused struct format_tree *ft, struct format_entry *fe) +{ + xasprintf(&fe->value, "%ld", (long)getpid()); +} + +/* Callback for pane_start_command. */ +void +format_cb_start_command(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + + if (wp == NULL) + return; + + fe->value = cmd_stringify_argv(wp->argc, wp->argv); +} + +/* Callback for pane_current_command. */ +void +format_cb_current_command(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + char *cmd; + + if (wp == NULL) + return; + + cmd = get_proc_name(wp->fd, wp->tty); + if (cmd == NULL || *cmd == '\0') { + free(cmd); + cmd = cmd_stringify_argv(wp->argc, wp->argv); + if (cmd == NULL || *cmd == '\0') { + free(cmd); + cmd = xstrdup(wp->shell); + } + } + fe->value = parse_window_name(cmd); + free(cmd); +} + /* Create a new tree. */ struct format_tree * format_create(void) @@ -269,7 +348,6 @@ struct format_tree * format_create_status(int status) { struct format_tree *ft; - char host[HOST_NAME_MAX + 1], *ptr; if (!event_initialized(&format_job_event)) { evtimer_set(&format_job_event, format_job_timer, NULL); @@ -280,13 +358,9 @@ format_create_status(int status) RB_INIT(&ft->tree); ft->status = status; - if (gethostname(host, sizeof host) == 0) { - format_add(ft, "host", "%s", host); - if ((ptr = strchr(host, '.')) != NULL) - *ptr = '\0'; - format_add(ft, "host_short", "%s", host); - } - format_add(ft, "pid", "%ld", (long) getpid()); + format_add_cb(ft, "host", format_cb_host); + format_add_cb(ft, "host_short", format_cb_host_short); + format_add_cb(ft, "pid", format_cb_pid); return (ft); } @@ -318,17 +392,42 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); + if (fe_now != NULL) { + free(fe->key); + free(fe); + free(fe_now->value); + fe = fe_now; + } + + fe->cb = NULL; + va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); +} + +/* Add a key and function. */ +void +format_add_cb(struct format_tree *ft, const char *key, format_cb cb) +{ + struct format_entry *fe; + struct format_entry *fe_now; + + fe = xmalloc(sizeof *fe); + fe->key = xstrdup(key); fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { - free(fe_now->value); - fe_now->value = fe->value; free(fe->key); free(fe); + free(fe_now->value); + fe = fe_now; } + + fe->cb = cb; + + fe->value = NULL; } /* Find a format entry. */ @@ -363,8 +462,11 @@ format_find(struct format_tree *ft, const char *key) fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); - if (fe != NULL) + if (fe != NULL) { + if (fe->value == NULL && fe->cb != NULL) + fe->cb(ft, fe); return (fe->value); + } envent = NULL; if (ft->s != NULL) @@ -506,7 +608,7 @@ char * format_expand(struct format_tree *ft, const char *fmt) { char *buf, *tmp, *cmd; - const char *ptr, *s; + const char *ptr, *s, *saved = fmt; size_t off, len, n, slen; int ch, brackets; @@ -610,29 +712,10 @@ format_expand(struct format_tree *ft, const char *fmt) } buf[off] = '\0'; + log_debug("format '%s' -> '%s'", saved, buf); return (buf); } -/* Get command name for format. */ -char * -format_get_command(struct window_pane *wp) -{ - char *cmd, *out; - - cmd = get_proc_name(wp->fd, wp->tty); - if (cmd == NULL || *cmd == '\0') { - free(cmd); - cmd = cmd_stringify_argv(wp->argc, wp->argv); - if (cmd == NULL || *cmd == '\0') { - free(cmd); - cmd = xstrdup(wp->shell); - } - } - out = parse_window_name(cmd); - free(cmd); - return (out); -} - /* Get time as a string. */ char * format_time_string(time_t t) @@ -863,11 +946,11 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) struct grid_line *gl; unsigned long long size; u_int i, idx; - char *cmd; int status; if (ft->w == NULL) ft->w = wp->window; + ft->wp = wp; size = 0; for (i = 0; i < gd->hsize; i++) { @@ -908,14 +991,8 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); - if ((cmd = cmd_stringify_argv(wp->argc, wp->argv)) != NULL) { - format_add(ft, "pane_start_command", "%s", cmd); - free(cmd); - } - if ((cmd = format_get_command(wp)) != NULL) { - format_add(ft, "pane_current_command", "%s", cmd); - free(cmd); - } + format_add_cb(ft, "pane_start_command", format_cb_start_command); + format_add_cb(ft, "pane_current_command", format_cb_current_command); format_add(ft, "cursor_x", "%u", wp->base.cx); format_add(ft, "cursor_y", "%u", wp->base.cy); From 5f122af55662a2af36f20493446dbbadfd24f7af Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 17:01:42 +0000 Subject: [PATCH 268/703] Make a few more expensive (ish) formats functions instead of inline. --- format.c | 169 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 106 insertions(+), 63 deletions(-) diff --git a/format.c b/format.c index a885bb85..d86e529d 100644 --- a/format.c +++ b/format.c @@ -45,8 +45,12 @@ void format_job_timer(int, short, void *); void format_cb_host(struct format_tree *, struct format_entry *); void format_cb_host_short(struct format_tree *, struct format_entry *); void format_cb_pid(struct format_tree *, struct format_entry *); +void format_cb_session_alerts(struct format_tree *, struct format_entry *); +void format_cb_window_layout(struct format_tree *, struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); +void format_cb_history_bytes(struct format_tree *, struct format_entry *); +void format_cb_pane_tabs(struct format_tree *, struct format_entry *); void format_add_cb(struct format_tree *, const char *, format_cb); int format_replace(struct format_tree *, const char *, size_t, char **, @@ -301,6 +305,51 @@ format_cb_pid(unused struct format_tree *ft, struct format_entry *fe) xasprintf(&fe->value, "%ld", (long)getpid()); } +/* Callback for session_alerts. */ +void +format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) +{ + struct session *s = ft->s; + struct winlink *wl; + char alerts[256], tmp[16]; + + if (s == NULL) + return; + + *alerts = '\0'; + RB_FOREACH(wl, winlinks, &s->windows) { + if ((wl->flags & WINLINK_ALERTFLAGS) == 0) + continue; + xsnprintf(tmp, sizeof tmp, "%u", wl->idx); + + if (*alerts != '\0') + strlcat(alerts, ",", sizeof alerts); + strlcat(alerts, tmp, sizeof alerts); + if (wl->flags & WINLINK_ACTIVITY) + strlcat(alerts, "#", sizeof alerts); + if (wl->flags & WINLINK_BELL) + strlcat(alerts, "!", sizeof alerts); + if (wl->flags & WINLINK_SILENCE) + strlcat(alerts, "~", sizeof alerts); + } + fe->value = xstrdup(alerts); +} + +/* Callback for window_layout. */ +void +format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) +{ + struct window *w = ft->w; + + if (w == NULL) + return; + + if (w->saved_layout_root != NULL) + fe->value = layout_dump(w->saved_layout_root); + else + fe->value = layout_dump(w->layout_root); +} + /* Callback for pane_start_command. */ void format_cb_start_command(struct format_tree *ft, struct format_entry *fe) @@ -336,6 +385,56 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) free(cmd); } +/* Callback for history_bytes. */ +void +format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct grid *gd; + struct grid_line *gl; + unsigned long long size; + u_int i; + + if (wp == NULL) + return; + gd = wp->base.grid; + + size = 0; + for (i = 0; i < gd->hsize; i++) { + gl = &gd->linedata[i]; + size += gl->cellsize * sizeof *gl->celldata; + } + size += gd->hsize * sizeof *gd->linedata; + + xasprintf(&fe->value, "%llu", size); +} + +/* Callback for pane_tabs. */ +void +format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + struct evbuffer *buffer; + u_int i; + int size; + + if (wp == NULL) + return; + + buffer = evbuffer_new(); + for (i = 0; i < wp->base.grid->sx; i++) { + if (!bit_test(wp->base.tabs, i)) + continue; + + if (EVBUFFER_LENGTH(buffer) > 0) + evbuffer_add(buffer, ",", 1); + evbuffer_add_printf(buffer, "%u", i); + } + size = EVBUFFER_LENGTH(buffer); + xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); + evbuffer_free(buffer); +} + /* Create a new tree. */ struct format_tree * format_create(void) @@ -756,8 +855,6 @@ format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; time_t t; - struct winlink *wl; - char alerts[256], tmp[16]; ft->s = s; @@ -783,23 +880,7 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); - *alerts = '\0'; - RB_FOREACH (wl, winlinks, &s->windows) { - if ((wl->flags & WINLINK_ALERTFLAGS) == 0) - continue; - xsnprintf(tmp, sizeof tmp, "%u", wl->idx); - - if (*alerts != '\0') - strlcat(alerts, ",", sizeof alerts); - strlcat(alerts, tmp, sizeof alerts); - if (wl->flags & WINLINK_ACTIVITY) - strlcat(alerts, "#", sizeof alerts); - if (wl->flags & WINLINK_BELL) - strlcat(alerts, "!", sizeof alerts); - if (wl->flags & WINLINK_SILENCE) - strlcat(alerts, "~", sizeof alerts); - } - format_add(ft, "session_alerts", "%s", alerts); + format_add_cb(ft, "session_alerts", format_cb_session_alerts); } /* Set default format keys for a client. */ @@ -858,16 +939,10 @@ format_defaults_client(struct format_tree *ft, struct client *c) void format_defaults_window(struct format_tree *ft, struct window *w) { - char *layout; time_t t; ft->w = w; - if (w->saved_layout_root != NULL) - layout = layout_dump(w->saved_layout_root); - else - layout = layout_dump(w->layout_root); - t = w->activity_time.tv_sec; format_add(ft, "window_activity", "%lld", (long long) t); format_add(ft, "window_activity_string", "%s", format_time_string(t)); @@ -876,12 +951,10 @@ format_defaults_window(struct format_tree *ft, struct window *w) format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_height", "%u", w->sy); - format_add(ft, "window_layout", "%s", layout); + format_add_cb(ft, "window_layout", format_cb_window_layout); format_add(ft, "window_panes", "%u", window_count_panes(w)); format_add(ft, "window_zoomed_flag", "%d", !!(w->flags & WINDOW_ZOOMED)); - - free(layout); } /* Set default format keys for a winlink. */ @@ -916,51 +989,21 @@ format_defaults_winlink(struct format_tree *ft, struct session *s, free(flags); } -/* Add window pane tabs. */ -void -format_defaults_pane_tabs(struct format_tree *ft, struct window_pane *wp) -{ - struct evbuffer *buffer; - u_int i; - - buffer = evbuffer_new(); - for (i = 0; i < wp->base.grid->sx; i++) { - if (!bit_test(wp->base.tabs, i)) - continue; - - if (EVBUFFER_LENGTH(buffer) > 0) - evbuffer_add(buffer, ",", 1); - evbuffer_add_printf(buffer, "%u", i); - } - - format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer), - EVBUFFER_DATA(buffer)); - evbuffer_free(buffer); -} - /* Set default format keys for a window pane. */ void format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { - struct grid *gd = wp->base.grid; - struct grid_line *gl; - unsigned long long size; - u_int i, idx; - int status; + struct grid *gd = wp->base.grid; + u_int idx; + int status; if (ft->w == NULL) ft->w = wp->window; ft->wp = wp; - size = 0; - for (i = 0; i < gd->hsize; i++) { - gl = &gd->linedata[i]; - size += gl->cellsize * sizeof *gl->celldata; - } - size += gd->hsize * sizeof *gd->linedata; format_add(ft, "history_size", "%u", gd->hsize); format_add(ft, "history_limit", "%u", gd->hlimit); - format_add(ft, "history_bytes", "%llu", size); + format_add_cb(ft, "history_bytes", format_cb_history_bytes); if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); @@ -1023,7 +1066,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "mouse_utf8_flag", "%d", !!(wp->base.mode & MODE_MOUSE_UTF8)); - format_defaults_pane_tabs(ft, wp); + format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); } /* Set default format keys for paste buffer. */ From d9b313332186b0c99ec6a9b7585db8188bb35cfc Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Aug 2015 17:11:12 +0000 Subject: [PATCH 269/703] Only set default title to hostname on screens that are being used for a window pane, no point in calling gethostname() for temporary screens. --- screen.c | 8 +------- window.c | 4 ++++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/screen.c b/screen.c index 1b841eed..f487cc67 100644 --- a/screen.c +++ b/screen.c @@ -31,14 +31,8 @@ void screen_resize_y(struct screen *, u_int); void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) { - char host[HOST_NAME_MAX+1]; - s->grid = grid_create(sx, sy, hlimit); - - if (gethostname(host, sizeof(host)) == 0) - s->title = xstrdup(host); - else - s->title = xstrdup(""); + s->title = xstrdup(""); s->cstyle = 0; s->ccolour = xstrdup(""); diff --git a/window.c b/window.c index 77c7a633..ff3ccfb8 100644 --- a/window.c +++ b/window.c @@ -704,6 +704,7 @@ struct window_pane * window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) { struct window_pane *wp; + char host[HOST_NAME_MAX + 1]; wp = xcalloc(1, sizeof *wp); wp->window = w; @@ -740,6 +741,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; + if (gethostname(host, sizeof host) == 0) + screen_set_title(&wp->base, host); + input_init(wp); return (wp); From 73bd8160768c5411bf96c8e2326c36f19cdd45c5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 00:24:44 +0000 Subject: [PATCH 270/703] Microseconds in log time. --- log.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/log.c b/log.c index 9a53775e..fa26eb4c 100644 --- a/log.c +++ b/log.c @@ -66,14 +66,15 @@ log_close(void) void log_vwrite(const char *msg, va_list ap) { - char *fmt; - time_t t; + char *fmt; + struct timeval tv; if (log_file == NULL) return; - t = time(NULL); - if (asprintf(&fmt, "%lld %s\n", (long long)t, msg) == -1) + gettimeofday(&tv, NULL); + if (asprintf(&fmt, "%lld.%06d %s\n", (long long)tv.tv_sec, + (int)tv.tv_usec, msg) == -1) exit(1); if (vfprintf(log_file, fmt, ap) == -1) exit(1); From b7861f34bae7a9e00446e0a8cf2f38e220c41c79 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 00:29:15 +0000 Subject: [PATCH 271/703] Better take on reducing the name timer. Again check for name changes in the main loop after events that may have changed the pane, but do so at most once every 500 millis. If the pane changed too soon, use a timer to ensure that a check happens later. --- cmd-set-option.c | 4 +-- names.c | 63 +++++++++++++++++++++++++++++++++++------------- server-window.c | 1 + tmux.h | 11 ++++++--- window.c | 6 ++--- 5 files changed, 57 insertions(+), 28 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 56ca91e0..631a4d0e 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -180,9 +180,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); - else if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); + w->active->flags |= PANE_CHANGED; } } if (strcmp(oe->name, "status") == 0 || diff --git a/names.c b/names.c index aa0673ea..e880c577 100644 --- a/names.c +++ b/names.c @@ -25,47 +25,76 @@ #include "tmux.h" -void window_name_callback(unused int, unused short, void *); +void name_time_callback(int, short, void *); +int name_time_expired(struct window *, struct timeval *); void -queue_window_name(struct window *w) +name_time_callback(unused int fd, unused short events, void *arg) { - struct timeval tv; + struct window *w = arg; - tv.tv_sec = 0; - tv.tv_usec = NAME_INTERVAL * 1000L; + /* The event loop will call check_window_name for us on the way out. */ + log_debug("@%u name timer expired", w->id); +} - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); - evtimer_set(&w->name_timer, window_name_callback, w); - evtimer_add(&w->name_timer, &tv); +int +name_time_expired(struct window *w, struct timeval *tv) +{ + struct timeval offset; + + timersub(tv, &w->name_time, &offset); + if (offset.tv_sec != 0 || offset.tv_usec > NAME_INTERVAL) + return (0); + return (NAME_INTERVAL - offset.tv_usec); } void -window_name_callback(unused int fd, unused short events, void *data) +check_window_name(struct window *w) { - struct window *w = data; + struct timeval tv, next; char *name; + int left; if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) { - if (event_initialized(&w->name_timer)) - event_del(&w->name_timer); + if (!options_get_number(&w->options, "automatic-rename")) + return; + + if (~w->active->flags & PANE_CHANGED) { + log_debug("@%u active pane not changed", w->id); return; } - queue_window_name(w); + log_debug("@%u active pane changed", w->id); - if (~w->active->flags & PANE_CHANGED) + gettimeofday(&tv, NULL); + left = name_time_expired(w, &tv); + if (left != 0) { + if (!event_initialized(&w->name_event)) + evtimer_set(&w->name_event, name_time_callback, w); + if (!evtimer_pending(&w->name_event, NULL)) { + log_debug("@%u name timer queued (%d left)", w->id, left); + timerclear(&next); + next.tv_usec = left; + event_add(&w->name_event, &next); + } else + log_debug("@%u name timer already queued (%d left)", w->id, left); return; + } + memcpy(&w->name_time, &tv, sizeof w->name_time); + if (event_initialized(&w->name_event)) + evtimer_del(&w->name_event); + w->active->flags &= ~PANE_CHANGED; name = format_window_name(w); if (strcmp(name, w->name) != 0) { + log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); server_status_window(w); - } + } else + log_debug("@%u name not changed (still %s)", w->id, w->name); + free(name); } diff --git a/server-window.c b/server-window.c index c96c2602..be1dde2a 100644 --- a/server-window.c +++ b/server-window.c @@ -49,6 +49,7 @@ server_window_loop(void) server_status_session(s); } } + check_window_name(w); } } diff --git a/tmux.h b/tmux.h index 6ac0b578..5ae7225f 100644 --- a/tmux.h +++ b/tmux.h @@ -47,8 +47,8 @@ extern char **environ; */ #define PANE_MINIMUM 2 -/* Automatic name refresh interval, in milliseconds. */ -#define NAME_INTERVAL 500 +/* Automatic name refresh interval, in microseconds. Must be < 1 second. */ +#define NAME_INTERVAL 500000 /* * UTF-8 data size. This must be big enough to hold combined characters as well @@ -869,8 +869,11 @@ RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ struct window { u_int id; + char *name; - struct event name_timer; + struct event name_event; + struct timeval name_time; + struct timeval silence_timer; struct timeval activity_time; @@ -2209,7 +2212,7 @@ void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ -void queue_window_name(struct window *); +void check_window_name(struct window *); char *default_window_name(struct window *); char *format_window_name(struct window *); char *parse_window_name(const char *); diff --git a/window.c b/window.c index ff3ccfb8..f58d80ea 100644 --- a/window.c +++ b/window.c @@ -299,8 +299,6 @@ window_create1(u_int sx, u_int sy) fatal("gettimeofday failed"); options_init(&w->options, &global_w_options); - if (options_get_number(&w->options, "automatic-rename")) - queue_window_name(w); w->references = 0; @@ -349,8 +347,8 @@ window_destroy(struct window *w) layout_free_cell(w->saved_layout_root); free(w->old_layout); - if (event_initialized(&w->name_timer)) - evtimer_del(&w->name_timer); + if (event_initialized(&w->name_event)) + evtimer_del(&w->name_event); options_free(&w->options); From 5267ce8ff41e8c5e69fa5d05da92b61e739c328a Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 00:39:18 +0000 Subject: [PATCH 272/703] Treat entering or leaving a mode as pane changed. --- window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/window.c b/window.c index f58d80ea..c172b68e 100644 --- a/window.c +++ b/window.c @@ -1066,7 +1066,7 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) if ((s = wp->mode->init(wp)) != NULL) wp->screen = s; - wp->flags |= PANE_REDRAW; + wp->flags |= (PANE_REDRAW|PANE_CHANGED); return (0); } @@ -1080,7 +1080,7 @@ window_pane_reset_mode(struct window_pane *wp) wp->mode = NULL; wp->screen = &wp->base; - wp->flags |= PANE_REDRAW; + wp->flags |= (PANE_REDRAW|PANE_CHANGED); } void From b5aaefc727f303276a681d74f091f52a7e859d36 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 08:30:54 +0000 Subject: [PATCH 273/703] Move alerts onto events rather than checking every loop. --- Makefile | 4 +- server-window.c => alerts.c | 168 +++++++++++++++++++++++++----------- cmd-set-option.c | 2 + input.c | 9 +- server.c | 5 +- session.c | 1 + tmux.h | 11 ++- window.c | 23 ++--- 8 files changed, 150 insertions(+), 73 deletions(-) rename server-window.c => alerts.c (53%) diff --git a/Makefile b/Makefile index ac7e6e54..ad893d4b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ # $OpenBSD$ PROG= tmux -SRCS= arguments.c \ +SRCS= alerts.c \ + arguments.c \ attributes.c \ cfg.c \ client.c \ @@ -101,7 +102,6 @@ SRCS= arguments.c \ screen.c \ server-client.c \ server-fn.c \ - server-window.c \ server.c \ session.c \ signal.c \ diff --git a/server-window.c b/alerts.c similarity index 53% rename from server-window.c rename to alerts.c index be1dde2a..cfdd30e3 100644 --- a/server-window.c +++ b/alerts.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2015 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,43 +19,133 @@ #include #include -#include -#include #include "tmux.h" -int server_window_check_bell(struct session *, struct winlink *); -int server_window_check_activity(struct session *, struct winlink *); -int server_window_check_silence(struct session *, struct winlink *); -void ring_bell(struct session *); +int alerts_fired; + +void alerts_timer(int, short, void *); +int alerts_enabled(struct window *, int); +void alerts_callback(int, short, void *); +void alerts_reset(struct window *); + +int alerts_check_bell(struct session *, struct winlink *); +int alerts_check_activity(struct session *, struct winlink *); +int alerts_check_silence(struct session *, struct winlink *); +void alerts_ring_bell(struct session *); -/* Window functions that need to happen every loop. */ void -server_window_loop(void) +alerts_timer(unused int fd, unused short events, void *arg) +{ + struct window *w = arg; + + log_debug("@%u alerts timer expired", w->id); + alerts_reset(w); + alerts_queue(w, WINDOW_SILENCE); +} + +void +alerts_callback(unused int fd, unused short events, unused void *arg) { struct window *w; struct session *s; struct winlink *wl; + int flags, alerts; RB_FOREACH(w, windows, &windows) { RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window != w) continue; + flags = w->flags; - if (server_window_check_bell(s, wl) || - server_window_check_activity(s, wl) || - server_window_check_silence(s, wl)) + alerts = alerts_check_bell(s, wl); + alerts |= alerts_check_activity(s, wl); + alerts |= alerts_check_silence(s, wl); + if (alerts != 0) server_status_session(s); + + log_debug("%s:%d @%u alerts check, alerts %#x, " + "flags %#x", s->name, wl->idx, w->id, + alerts, flags); } } - check_window_name(w); } + alerts_fired = 0; } -/* Check for bell in window. */ int -server_window_check_bell(struct session *s, struct winlink *wl) +alerts_enabled(struct window *w, int flags) +{ + struct session *s; + + if (flags & WINDOW_ACTIVITY) { + if (options_get_number(&w->options, "monitor-activity")) + return (1); + } + if (flags & WINDOW_SILENCE) { + if (options_get_number(&w->options, "monitor-silence") != 0) + return (1); + } + if (~flags & WINDOW_BELL) + return (0); + RB_FOREACH(s, sessions, &sessions) { + if (!session_has(s, w)) + continue; + if (options_get_number(&s->options, "bell-action") != BELL_NONE) + return (1); + } + return (0); +} + +void +alerts_reset_all(void) +{ + struct window *w; + + RB_FOREACH(w, windows, &windows) + alerts_reset(w); +} + +void +alerts_reset(struct window *w) +{ + struct timeval tv; + + w->flags &= ~WINDOW_SILENCE; + event_del(&w->alerts_timer); + + timerclear(&tv); + tv.tv_sec = options_get_number(&w->options, "monitor-silence"); + + log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); + if (tv.tv_sec != 0) + event_add(&w->alerts_timer, &tv); +} + +void +alerts_queue(struct window *w, int flags) +{ + if (!event_initialized(&w->alerts_timer)) + evtimer_set(&w->alerts_timer, alerts_timer, w); + + if (w->flags & flags) + return; + w->flags |= flags; + log_debug("@%u alerts flags added %#x", w->id, flags); + + if (!alerts_fired && alerts_enabled(w, flags)) { + log_debug("alerts check queued (by @%u)", w->id); + event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); + alerts_fired = 1; + } + + if (flags & WINDOW_ACTIVITY) + alerts_reset(w); +} + +int +alerts_check_bell(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; @@ -70,10 +160,11 @@ server_window_check_bell(struct session *s, struct winlink *wl) if (s->curw->window == w) w->flags &= ~WINDOW_BELL; - visual = options_get_number(&s->options, "visual-bell"); action = options_get_number(&s->options, "bell-action"); if (action == BELL_NONE) return (0); + + visual = options_get_number(&s->options, "visual-bell"); TAILQ_FOREACH(c, &clients, entry) { if (c->session != s || c->flags & CLIENT_CONTROL) continue; @@ -93,12 +184,11 @@ server_window_check_bell(struct session *s, struct winlink *wl) status_message_set(c, "Bell in window %d", wl->idx); } - return (1); + return (WINDOW_BELL); } -/* Check for activity in window. */ int -server_window_check_activity(struct session *s, struct winlink *wl) +alerts_check_activity(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; @@ -115,7 +205,7 @@ server_window_check_activity(struct session *s, struct winlink *wl) return (0); if (options_get_number(&s->options, "bell-on-alert")) - ring_bell(s); + alerts_ring_bell(s); wl->flags |= WINLINK_ACTIVITY; if (options_get_number(&s->options, "visual-activity")) { @@ -126,45 +216,28 @@ server_window_check_activity(struct session *s, struct winlink *wl) } } - return (1); + return (WINDOW_ACTIVITY); } -/* Check for silence in window. */ int -server_window_check_silence(struct session *s, struct winlink *wl) +alerts_check_silence(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; - struct timeval timer; - int silence_interval, timer_difference; + + if (s->curw->window == w) + w->flags &= ~WINDOW_SILENCE; if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) return (0); - - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) { - /* - * Reset the timer for this window if we've focused it. We - * don't want the timer tripping as soon as we've switched away - * from this window. - */ - if (gettimeofday(&w->silence_timer, NULL) != 0) - fatal("gettimeofday failed"); - - return (0); - } - - silence_interval = options_get_number(&w->options, "monitor-silence"); - if (silence_interval == 0) + if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) return (0); - if (gettimeofday(&timer, NULL) != 0) - fatal("gettimeofday"); - timer_difference = timer.tv_sec - w->silence_timer.tv_sec; - if (timer_difference <= silence_interval) + if (options_get_number(&w->options, "monitor-silence") == 0) return (0); if (options_get_number(&s->options, "bell-on-alert")) - ring_bell(s); + alerts_ring_bell(s); wl->flags |= WINLINK_SILENCE; if (options_get_number(&s->options, "visual-silence")) { @@ -175,12 +248,11 @@ server_window_check_silence(struct session *s, struct winlink *wl) } } - return (1); + return (WINDOW_SILENCE); } -/* Ring terminal bell. */ void -ring_bell(struct session *s) +alerts_ring_bell(struct session *s) { struct client *c; diff --git a/cmd-set-option.c b/cmd-set-option.c index 631a4d0e..98ab2513 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -186,6 +186,8 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (strcmp(oe->name, "status") == 0 || strcmp(oe->name, "status-interval") == 0) status_timer_start_all(); + if (strcmp(oe->name, "monitor-silence") == 0) + alerts_reset_all(); /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); diff --git a/input.c b/input.c index 7ec35c88..cfe6b4af 100644 --- a/input.c +++ b/input.c @@ -844,14 +844,9 @@ input_parse(struct window_pane *wp) if (EVBUFFER_LENGTH(evb) == 0) return; + window_update_activity(wp->window); wp->flags |= PANE_CHANGED; - wp->window->flags |= WINDOW_ACTIVITY; - wp->window->flags &= ~WINDOW_SILENCE; - - if (gettimeofday(&wp->window->activity_time, NULL) != 0) - fatal("gettimeofday failed"); - /* * Open the screen. Use NULL wp if there is a mode set as don't want to * update the tty. @@ -1081,7 +1076,7 @@ input_c0_dispatch(struct input_ctx *ictx) case '\000': /* NUL */ break; case '\007': /* BEL */ - wp->window->flags |= WINDOW_BELL; + alerts_queue(wp->window, WINDOW_BELL); break; case '\010': /* BS */ screen_write_backspace(sctx); diff --git a/server.c b/server.c index d36dda12..d21acbe9 100644 --- a/server.c +++ b/server.c @@ -249,10 +249,13 @@ server_start(int lockfd, char *lockfile) void server_loop(void) { + struct window *w; + while (!server_should_shutdown()) { event_loop(EVLOOP_ONCE); - server_window_loop(); + RB_FOREACH(w, windows, &windows) + check_window_name(w); server_client_loop(); } } diff --git a/session.c b/session.c index c4e64e27..56986497 100644 --- a/session.c +++ b/session.c @@ -519,6 +519,7 @@ session_set_current(struct session *s, struct winlink *wl) winlink_stack_push(&s->lastw, s->curw); s->curw = wl; winlink_clear_flags(wl); + window_update_activity(wl->window); return (0); } diff --git a/tmux.h b/tmux.h index 5ae7225f..ae305a64 100644 --- a/tmux.h +++ b/tmux.h @@ -874,7 +874,8 @@ struct window { struct event name_event; struct timeval name_time; - struct timeval silence_timer; + struct event alerts_timer; + struct timeval activity_time; struct window_pane *active; @@ -1829,6 +1830,10 @@ void key_bindings_dispatch(struct key_binding *, struct client *, int key_string_lookup_string(const char *); const char *key_string_lookup_key(int); +/* alerts.c */ +void alerts_reset_all(void); +void alerts_queue(struct window *, int); + /* server.c */ extern struct clients clients; extern struct clients dead_clients; @@ -1856,9 +1861,6 @@ void server_client_callback(int, short, void *); void server_client_status_timer(void); void server_client_loop(void); -/* server-window.c */ -void server_window_loop(void); - /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); void server_write_ready(struct client *); @@ -2084,6 +2086,7 @@ void winlink_stack_push(struct winlink_stack *, struct winlink *); void winlink_stack_remove(struct winlink_stack *, struct winlink *); struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); +void window_update_activity(struct window *); struct window *window_create1(u_int, u_int); struct window *window_create(const char *, int, char **, const char *, const char *, int, struct environ *, struct termios *, diff --git a/window.c b/window.c index c172b68e..d8506774 100644 --- a/window.c +++ b/window.c @@ -277,6 +277,13 @@ window_find_by_id(u_int id) return (RB_FIND(windows, &windows, &w)); } +void +window_update_activity(struct window *w) +{ + gettimeofday(&w->activity_time, NULL); + alerts_queue(w, WINDOW_ACTIVITY); +} + struct window * window_create1(u_int sx, u_int sy) { @@ -295,9 +302,6 @@ window_create1(u_int sx, u_int sy) w->sx = sx; w->sy = sy; - if (gettimeofday(&w->activity_time, NULL) != 0) - fatal("gettimeofday failed"); - options_init(&w->options, &global_w_options); w->references = 0; @@ -305,6 +309,8 @@ window_create1(u_int sx, u_int sy) w->id = next_window_id++; RB_INSERT(windows, &windows, w); + window_update_activity(w); + return (w); } @@ -350,6 +356,9 @@ window_destroy(struct window *w) if (event_initialized(&w->name_event)) evtimer_del(&w->name_event); + if (event_initialized(&w->alerts_timer)) + evtimer_del(&w->alerts_timer); + options_free(&w->options); window_destroy_panes(w); @@ -929,14 +938,6 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) input_parse(wp); wp->pipe_off = EVBUFFER_LENGTH(evb); - - /* - * If we get here, we're not outputting anymore, so set the silence - * flag on the window. - */ - wp->window->flags |= WINDOW_SILENCE; - if (gettimeofday(&wp->window->silence_timer, NULL) != 0) - fatal("gettimeofday failed"); return; start_timer: From b9f0571780e7058a70a8cc3b6805b6f1a73061e0 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 08:54:41 +0000 Subject: [PATCH 274/703] We already loop over the windows in server_client_loop, so don't do it again in server_loop just to check names. --- server-client.c | 1 + server.c | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/server-client.c b/server-client.c index da24e222..8e298954 100644 --- a/server-client.c +++ b/server-client.c @@ -717,6 +717,7 @@ server_client_loop(void) } wp->flags &= ~PANE_REDRAW; } + check_window_name(w); } } diff --git a/server.c b/server.c index d21acbe9..9cddecd3 100644 --- a/server.c +++ b/server.c @@ -249,13 +249,11 @@ server_start(int lockfd, char *lockfile) void server_loop(void) { - struct window *w; - while (!server_should_shutdown()) { + log_debug("event dispatch enter"); event_loop(EVLOOP_ONCE); + log_debug("event dispatch exit"); - RB_FOREACH(w, windows, &windows) - check_window_name(w); server_client_loop(); } } From b56958500036970023c7a53264331cd10a5bbed2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 09:25:00 +0000 Subject: [PATCH 275/703] Move struct paste_buffer out of tmux.h. --- cmd-choose-buffer.c | 4 ++-- cmd-paste-buffer.c | 2 +- cmd-save-buffer.c | 21 +++++++++++---------- cmd-set-buffer.c | 39 +++++++++++++++++---------------------- format.c | 6 ++++-- paste.c | 34 ++++++++++++++++++++++++++++++++-- status.c | 16 ++++++++-------- tmux.h | 42 ++++++++++++++++-------------------------- window-copy.c | 32 +++++++++++++++++--------------- 9 files changed, 108 insertions(+), 88 deletions(-) diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index af178976..b4590306 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -65,7 +65,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); utf8flag = options_get_number(&wl->window->options, "utf8"); - if (paste_get_top() == NULL) + if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) @@ -85,7 +85,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cdata->ft_template = xstrdup(template); format_defaults_paste_buffer(cdata->ft, pb, utf8flag); - xasprintf(&action_data, "%s", pb->name); + xasprintf(&action_data, "%s", paste_buffer_name(pb)); cdata->command = cmd_template_replace(action, action_data, 1); free(action_data); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 6d5fb9fd..d4ff93d1 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -58,7 +58,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) bufname = args_get(args, 'b'); if (bufname == NULL) - pb = paste_get_top(); + pb = paste_get_top(NULL); else { pb = paste_get_name(bufname); if (pb == NULL) { diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 62c3989e..4644e689 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -57,14 +57,14 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *bufname; + const char *path, *bufname, *bufdata; char *start, *end, *msg; - size_t size, used, msglen; + size_t size, used, msglen, bufsize; int cwd, fd; FILE *f; if (!args_has(args, 'b')) { - if ((pb = paste_get_top()) == NULL) { + if ((pb = paste_get_top(NULL)) == NULL) { cmdq_error(cmdq, "no buffers"); return (CMD_RETURN_ERROR); } @@ -76,6 +76,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } } + bufdata = paste_buffer_data(pb, &bufsize); if (self->entry == &cmd_show_buffer_entry) path = "-"; @@ -114,7 +115,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_ERROR); } - if (fwrite(pb->data, 1, pb->size, f) != pb->size) { + if (fwrite(bufdata, 1, bufsize, f) != bufsize) { cmdq_error(cmdq, "%s: fwrite error", path); fclose(f); return (CMD_RETURN_ERROR); @@ -124,25 +125,25 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); do_stdout: - evbuffer_add(c->stdout_data, pb->data, pb->size); + evbuffer_add(c->stdout_data, bufdata, bufsize); server_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: - if (pb->size > (INT_MAX / 4) - 1) { + if (bufsize > (INT_MAX / 4) - 1) { cmdq_error(cmdq, "buffer too big"); return (CMD_RETURN_ERROR); } msg = NULL; used = 0; - while (used != pb->size) { - start = pb->data + used; - end = memchr(start, '\n', pb->size - used); + while (used != bufsize) { + start = bufdata + used; + end = memchr(start, '\n', bufsize - used); if (end != NULL) size = end - start; else - size = pb->size - used; + size = bufsize - used; msglen = size * 4 + 1; msg = xrealloc(msg, msglen); diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 0ec362b3..4fa7ca11 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -42,9 +42,9 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; - char *pdata, *cause; - const char *bufname; - size_t psize, newsize; + char *bufdata, *cause; + const char *bufname, *olddata; + size_t bufsize, newsize; bufname = NULL; @@ -58,12 +58,11 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) bufname = args_get(args, 'b'); if (bufname == NULL) { - pb = paste_get_top(); + pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); } - bufname = pb->name; } if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { @@ -79,37 +78,33 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "no data specified"); return (CMD_RETURN_ERROR); } - - psize = 0; - pdata = NULL; - pb = NULL; + bufsize = 0; + bufdata = NULL; + if ((newsize = strlen(args->argv[0])) == 0) return (CMD_RETURN_NORMAL); if (args_has(args, 'b')) { bufname = args_get(args, 'b'); pb = paste_get_name(bufname); - } else if (args_has(args, 'a')) { - pb = paste_get_top(); - if (pb != NULL) - bufname = pb->name; - } + } else if (args_has(args, 'a')) + pb = paste_get_top(&bufname); if (args_has(args, 'a') && pb != NULL) { - psize = pb->size; - pdata = xmalloc(psize); - memcpy(pdata, pb->data, psize); + olddata = paste_buffer_data(pb, &bufsize); + bufdata = xmalloc(bufsize); + memcpy(bufdata, olddata, bufsize); } - pdata = xrealloc(pdata, psize + newsize); - memcpy(pdata + psize, args->argv[0], newsize); - psize += newsize; + bufdata = xrealloc(bufdata, bufsize + newsize); + memcpy(bufdata + bufsize, args->argv[0], newsize); + bufsize += newsize; - if (paste_set(pdata, psize, bufname, &cause) != 0) { + if (paste_set(bufdata, bufsize, bufname, &cause) != 0) { cmdq_error(cmdq, "%s", cause); - free(pdata); + free(bufdata); free(cause); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index d86e529d..ae22a844 100644 --- a/format.c +++ b/format.c @@ -1074,10 +1074,12 @@ void format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, int utf8flag) { + size_t bufsize; char *s; - format_add(ft, "buffer_size", "%zu", pb->size); - format_add(ft, "buffer_name", "%s", pb->name); + paste_buffer_data(pb, &bufsize); + format_add(ft, "buffer_size", "%zu", bufsize); + format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); s = paste_make_sample(pb, utf8flag); format_add(ft, "buffer_sample", "%s", s); diff --git a/paste.c b/paste.c index 2ccc3cd2..923a06cf 100644 --- a/paste.c +++ b/paste.c @@ -30,6 +30,18 @@ * string! */ +struct paste_buffer { + char *data; + size_t size; + + char *name; + int automatic; + u_int order; + + RB_ENTRY(paste_buffer) name_entry; + RB_ENTRY(paste_buffer) time_entry; +}; + u_int paste_next_index; u_int paste_next_order; u_int paste_num_automatic; @@ -60,6 +72,22 @@ paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) return (0); } +/* Get paste buffer name. */ +const char * +paste_buffer_name(struct paste_buffer *pb) +{ + return (pb->name); +} + +/* Get paste buffer data. */ +const char * +paste_buffer_data(struct paste_buffer *pb, size_t *size) +{ + if (size != NULL) + *size = pb->size; + return (pb->data); +} + /* Walk paste buffers by name. */ struct paste_buffer * paste_walk(struct paste_buffer *pb) @@ -71,13 +99,15 @@ paste_walk(struct paste_buffer *pb) /* Get the most recent automatic buffer. */ struct paste_buffer * -paste_get_top(void) +paste_get_top(const char **name) { struct paste_buffer *pb; pb = RB_MIN(paste_time_tree, &paste_by_time); if (pb == NULL) return (NULL); + if (name != NULL) + *name = pb->name; return (pb); } @@ -87,7 +117,7 @@ paste_free_top(void) { struct paste_buffer *pb; - pb = paste_get_top(); + pb = paste_get_top(NULL); if (pb == NULL) return (-1); return (paste_free_name(pb->name)); diff --git a/status.c b/status.c index 93cee2b1..29cb686c 100644 --- a/status.c +++ b/status.c @@ -815,10 +815,9 @@ status_prompt_key(struct client *c, int key) struct options *oo = &sess->options; struct paste_buffer *pb; char *s, *first, *last, word[64], swapc; - const char *histstr; - const char *wsep = NULL; + const char *histstr, *bufdata, *wsep = NULL; u_char ch; - size_t size, n, off, idx; + size_t size, n, off, idx, bufsize; size = strlen(c->prompt_buffer); switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) { @@ -1067,24 +1066,25 @@ status_prompt_key(struct client *c, int key) c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PASTE: - if ((pb = paste_get_top()) == NULL) + if ((pb = paste_get_top(NULL)) == NULL) break; - for (n = 0; n < pb->size; n++) { - ch = (u_char) pb->data[n]; + bufdata = paste_buffer_data(pb, &bufsize); + for (n = 0; n < bufsize; n++) { + ch = (u_char)bufdata[n]; if (ch < 32 || ch == 127) break; } c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1); if (c->prompt_index == size) { - memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + memcpy(c->prompt_buffer + c->prompt_index, bufdata, n); c->prompt_index += n; c->prompt_buffer[c->prompt_index] = '\0'; } else { memmove(c->prompt_buffer + c->prompt_index + n, c->prompt_buffer + c->prompt_index, size + 1 - c->prompt_index); - memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + memcpy(c->prompt_buffer + c->prompt_index, bufdata, n); c->prompt_index += n; } diff --git a/tmux.h b/tmux.h index ae305a64..fc25be95 100644 --- a/tmux.h +++ b/tmux.h @@ -957,19 +957,6 @@ struct layout_cell { TAILQ_ENTRY(layout_cell) entry; }; -/* Paste buffer. */ -struct paste_buffer { - char *data; - size_t size; - - char *name; - int automatic; - u_int order; - - RB_ENTRY(paste_buffer) name_entry; - RB_ENTRY(paste_buffer) time_entry; -}; - /* Environment variable. */ struct environ_entry { char *name; @@ -1452,6 +1439,22 @@ void cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); +/* paste.c */ +struct paste_buffer; +const char *paste_buffer_name(struct paste_buffer *); +const char *paste_buffer_data(struct paste_buffer *, size_t *); +struct paste_buffer *paste_walk(struct paste_buffer *); +struct paste_buffer *paste_get_top(const char **); +struct paste_buffer *paste_get_name(const char *); +int paste_free_top(void); +int paste_free_name(const char *); +void paste_add(char *, size_t); +int paste_rename(const char *, const char *, char **); +int paste_set(char *, size_t, const char *, char **); +char *paste_make_sample(struct paste_buffer *, int); +void paste_send_pane(struct paste_buffer *, struct window_pane *, + const char *, int); + /* format.c */ struct format_tree; struct format_tree *format_create(void); @@ -1636,19 +1639,6 @@ void tty_keys_build(struct tty *); void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); -/* paste.c */ -struct paste_buffer *paste_walk(struct paste_buffer *); -struct paste_buffer *paste_get_top(void); -struct paste_buffer *paste_get_name(const char *); -int paste_free_top(void); -int paste_free_name(const char *); -void paste_add(char *, size_t); -int paste_rename(const char *, const char *, char **); -int paste_set(char *, size_t, const char *, char **); -char *paste_make_sample(struct paste_buffer *, int); -void paste_send_pane(struct paste_buffer *, struct window_pane *, - const char *, int); - /* arguments.c */ int args_cmp(struct args_entry *, struct args_entry *); RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); diff --git a/window-copy.c b/window-copy.c index 850b437a..460dd10b 100644 --- a/window-copy.c +++ b/window-copy.c @@ -782,7 +782,8 @@ window_copy_key_input(struct window_pane *wp, int key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - size_t inputlen, n; + const char *bufdata; + size_t inputlen, n, bufsize; int np; struct paste_buffer *pb; u_char ch; @@ -800,17 +801,18 @@ window_copy_key_input(struct window_pane *wp, int key) *data->inputstr = '\0'; break; case MODEKEYEDIT_PASTE: - if ((pb = paste_get_top()) == NULL) + if ((pb = paste_get_top(NULL)) == NULL) break; - for (n = 0; n < pb->size; n++) { - ch = (u_char) pb->data[n]; + bufdata = paste_buffer_data(pb, &bufsize); + for (n = 0; n < bufsize; n++) { + ch = (u_char)bufdata[n]; if (ch < 32 || ch == 127) break; } inputlen = strlen(data->inputstr); data->inputstr = xrealloc(data->inputstr, inputlen + n + 1); - memcpy(data->inputstr + inputlen, pb->data, n); + memcpy(data->inputstr + inputlen, bufdata, n); data->inputstr[inputlen + n] = '\0'; break; case MODEKEYEDIT_ENTER: @@ -1491,7 +1493,8 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) { char *buf; struct paste_buffer *pb; - size_t len; + const char *bufdata; + size_t len, bufsize; struct screen_write_ctx ctx; buf = window_copy_get_selection(wp, &len); @@ -1504,17 +1507,16 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) screen_write_stop(&ctx); } - if (bufname == NULL || *bufname == '\0') { - pb = paste_get_top(); - if (pb != NULL) - bufname = pb->name; - } else + if (bufname == NULL || *bufname == '\0') + pb = paste_get_top(&bufname); + else pb = paste_get_name(bufname); if (pb != NULL) { - buf = xrealloc(buf, len + pb->size); - memmove(buf + pb->size, buf, len); - memcpy(buf, pb->data, pb->size); - len += pb->size; + bufdata = paste_buffer_data(pb, &bufsize); + buf = xrealloc(buf, len + bufsize); + memmove(buf + bufsize, buf, len); + memcpy(buf, bufdata, bufsize); + len += bufsize; } if (paste_set(buf, len, bufname, NULL) != 0) free(buf); From 373ef850e0a00dfb8180054625a91d046849261e Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 09:36:46 +0000 Subject: [PATCH 276/703] paste_send_pane can be merged into cmd-paste-buffer.c now. --- cmd-paste-buffer.c | 31 +++++++++++++++++++++++++++---- cmd-save-buffer.c | 4 ++-- paste.c | 29 ----------------------------- tmux.h | 2 -- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index d4ff93d1..cd3fc7d8 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -48,7 +48,9 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; struct paste_buffer *pb; - const char *sepstr, *bufname; + const char *sepstr, *bufname, *bufdata, *bufend, *line; + size_t seplen, bufsize; + int bracket = args_has(args, 'p'); if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); @@ -67,7 +69,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) } } - if (pb != NULL) { + if (pb != NULL && ~wp->flags & PANE_INPUTOFF) { sepstr = args_get(args, 's'); if (sepstr == NULL) { if (args_has(args, 'r')) @@ -75,10 +77,31 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else sepstr = "\r"; } - paste_send_pane(pb, wp, sepstr, args_has(args, 'p')); + seplen = strlen(sepstr); + + if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) + bufferevent_write(wp->event, "\033[200~", 6); + + bufdata = paste_buffer_data(pb, &bufsize); + bufend = bufdata + bufsize; + + for (;;) { + line = memchr(bufdata, '\n', bufend - bufdata); + if (line == NULL) + break; + + bufferevent_write(wp->event, bufdata, line - bufdata); + bufferevent_write(wp->event, sepstr, seplen); + + bufdata = line + 1; + } + if (bufdata != bufend) + bufferevent_write(wp->event, bufdata, bufend - bufdata); + + if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) + bufferevent_write(wp->event, "\033[201~", 6); } - /* Delete the buffer if -d. */ if (args_has(args, 'd')) { if (bufname == NULL) paste_free_top(); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 4644e689..5916cd4d 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -57,8 +57,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *bufname, *bufdata; - char *start, *end, *msg; + const char *path, *bufname, *bufdata, *start, *end; + char *msg; size_t size, used, msglen, bufsize; int cwd, fd; FILE *f; diff --git a/paste.c b/paste.c index 923a06cf..e7f752ce 100644 --- a/paste.c +++ b/paste.c @@ -319,32 +319,3 @@ paste_make_sample(struct paste_buffer *pb, int utf8flag) strlcpy(buf + width, "...", 4); return (buf); } - -/* Paste into a window pane, filtering '\n' according to separator. */ -void -paste_send_pane(struct paste_buffer *pb, struct window_pane *wp, - const char *sep, int bracket) -{ - const char *data = pb->data, *end = data + pb->size, *lf; - size_t seplen; - - if (wp->flags & PANE_INPUTOFF) - return; - - if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) - bufferevent_write(wp->event, "\033[200~", 6); - - seplen = strlen(sep); - while ((lf = memchr(data, '\n', end - data)) != NULL) { - if (lf != data) - bufferevent_write(wp->event, data, lf - data); - bufferevent_write(wp->event, sep, seplen); - data = lf + 1; - } - - if (end != data) - bufferevent_write(wp->event, data, end - data); - - if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) - bufferevent_write(wp->event, "\033[201~", 6); -} diff --git a/tmux.h b/tmux.h index fc25be95..822bdc93 100644 --- a/tmux.h +++ b/tmux.h @@ -1452,8 +1452,6 @@ void paste_add(char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); char *paste_make_sample(struct paste_buffer *, int); -void paste_send_pane(struct paste_buffer *, struct window_pane *, - const char *, int); /* format.c */ struct format_tree; From 52bbac506c54cd378fb4493d218027dc36a7fce5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 23:19:52 +0000 Subject: [PATCH 277/703] struct args_entry can go into arguments.c. --- arguments.c | 6 ++++++ tmux.h | 7 +------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arguments.c b/arguments.c index 05ff97eb..54753de3 100644 --- a/arguments.c +++ b/arguments.c @@ -29,6 +29,12 @@ * Manipulate command arguments. */ +struct args_entry { + u_char flag; + char *value; + RB_ENTRY(args_entry) entry; +}; + struct args_entry *args_find(struct args *, u_char); RB_GENERATE(args_tree, args_entry, entry, args_cmp); diff --git a/tmux.h b/tmux.h index 822bdc93..6b462589 100644 --- a/tmux.h +++ b/tmux.h @@ -1257,13 +1257,8 @@ struct client { TAILQ_HEAD(clients, client); /* Parsed arguments structures. */ -struct args_entry { - u_char flag; - char *value; - RB_ENTRY(args_entry) entry; -}; +struct args_entry; RB_HEAD(args_tree, args_entry); - struct args { struct args_tree tree; int argc; From 5047670693e3bc861eb4ae95708c96041b12a759 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 29 Aug 2015 23:55:55 +0000 Subject: [PATCH 278/703] Remove some old prototypes and unused functions. --- layout-set.c | 6 ------ tmux.h | 6 ------ window.c | 10 ---------- 3 files changed, 22 deletions(-) diff --git a/layout-set.c b/layout-set.c index 64b655a0..da94cff2 100644 --- a/layout-set.c +++ b/layout-set.c @@ -44,12 +44,6 @@ const struct { { "tiled", layout_set_tiled }, }; -const char * -layout_set_name(u_int layout) -{ - return (layout_sets[layout].name); -} - int layout_set_lookup(const char *name) { diff --git a/tmux.h b/tmux.h index 6b462589..10b161de 100644 --- a/tmux.h +++ b/tmux.h @@ -1841,7 +1841,6 @@ int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); void server_client_callback(int, short, void *); -void server_client_status_timer(void); void server_client_loop(void); /* server-fn.c */ @@ -1863,7 +1862,6 @@ void server_status_window(struct window *); void server_lock(void); void server_lock_session(struct session *); void server_lock_client(struct client *); -int server_unlock(const char *); void server_kill_window(struct window *); int server_link_window(struct session *, struct winlink *, struct session *, int, int, int, char **); @@ -2013,7 +2011,6 @@ void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); void screen_write_reverseindex(struct screen_write_ctx *); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); void screen_write_linefeed(struct screen_write_ctx *, int); -void screen_write_linefeedscreen(struct screen_write_ctx *, int); void screen_write_carriagereturn(struct screen_write_ctx *); void screen_write_clearendofscreen(struct screen_write_ctx *); void screen_write_clearstartofscreen(struct screen_write_ctx *); @@ -2076,7 +2073,6 @@ struct window *window_create(const char *, int, char **, const char *, u_int, u_int, u_int, char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); -void window_set_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *); @@ -2157,12 +2153,10 @@ char *layout_dump(struct layout_cell *); int layout_parse(struct window *, const char *); /* layout-set.c */ -const char *layout_set_name(u_int); int layout_set_lookup(const char *); u_int layout_set_select(struct window *, u_int); u_int layout_set_next(struct window *); u_int layout_set_previous(struct window *); -void layout_set_active_changed(struct window *); /* window-clock.c */ extern const struct window_mode window_clock_mode; diff --git a/window.c b/window.c index d8506774..e5decdc4 100644 --- a/window.c +++ b/window.c @@ -440,16 +440,6 @@ window_get_active_at(struct window *w, u_int x, u_int y) return (NULL); } -void -window_set_active_at(struct window *w, u_int x, u_int y) -{ - struct window_pane *wp; - - wp = window_get_active_at(w, x, y); - if (wp != NULL && wp != w->active) - window_set_active_pane(w, wp); -} - struct window_pane * window_find_string(struct window *w, const char *s) { From b87dc608d9b5b470926aaf77c5956befdfb7bc7b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Aug 2015 15:43:40 +0000 Subject: [PATCH 279/703] Some style nits and dead assignments. --- cmd-show-environment.c | 2 +- cmd-swap-pane.c | 2 -- server-client.c | 7 ------- tmux.c | 4 ++-- xterm-keys.c | 2 ++ 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/cmd-show-environment.c b/cmd-show-environment.c index a61cf3f4..af24d91b 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -56,7 +56,7 @@ cmd_show_environment_escape(struct environ_entry *envent) } *out = '\0'; - return ret; + return (ret); } void diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index c4751c36..dc2b7246 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -58,12 +58,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); - src_wl = dst_wl; } else if (args_has(self->args, 'U')) { src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); - src_wl = dst_wl; } else { src_wl = cmd_find_pane_marked(cmdq, NULL, NULL, &src_wp); diff --git a/server-client.c b/server-client.c index 8e298954..08058ab4 100644 --- a/server-client.c +++ b/server-client.c @@ -551,13 +551,6 @@ server_client_handle_key(struct client *c, int key) w = s->curw->window; wp = w->active; - /* No session, do nothing. */ - if (c->session == NULL) - return; - s = c->session; - w = c->session->curw->window; - wp = w->active; - /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); diff --git a/tmux.c b/tmux.c index a3481461..e66d24c0 100644 --- a/tmux.c +++ b/tmux.c @@ -198,7 +198,7 @@ shell_exec(const char *shell, const char *shellcmd) fatal("execl failed"); } -const char* +const char * find_home(void) { struct passwd *pw; @@ -213,7 +213,7 @@ find_home(void) home = NULL; } - return home; + return (home); } int diff --git a/xterm-keys.c b/xterm-keys.c index 5ca75d89..10d51c78 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -120,6 +120,8 @@ xterm_keys_match(const char *template, const char *buf, size_t len, size_t pos; int retval; + *modifiers = 0; + if (len == 0) return (0); From 29f2120e5baabeccd8882be843907d3a8b42febf Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 30 Aug 2015 21:47:50 +0100 Subject: [PATCH 280/703] Linux: get_proc_name() -> osdep_get_name() --- format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format.c b/format.c index 5f31fa86..5268a405 100644 --- a/format.c +++ b/format.c @@ -373,7 +373,7 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) if (wp == NULL) return; - cmd = get_proc_name(wp->fd, wp->tty); + cmd = osdep_get_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = cmd_stringify_argv(wp->argc, wp->argv); From dd92b6e83dbd4b5f24ad062c7944ca32add8d0e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Aug 2015 22:19:07 +0000 Subject: [PATCH 281/703] Event base does not need to be global. --- client.c | 10 +++++----- server.c | 4 ++-- tmux.c | 5 +---- tmux.h | 5 ++--- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/client.c b/client.c index 63432926..271568da 100644 --- a/client.c +++ b/client.c @@ -52,7 +52,7 @@ const char *client_exitsession; int client_attached; int client_get_lock(char *); -int client_connect(char *, int); +int client_connect(struct event_base *, char *, int); void client_send_identify(int); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); @@ -96,7 +96,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ int -client_connect(char *path, int start_server) +client_connect(struct event_base *base, char *path, int start_server) { struct sockaddr_un sa; size_t size; @@ -149,7 +149,7 @@ retry: close(lockfd); return (-1); } - fd = server_start(lockfd, lockfile); + fd = server_start(base, lockfd, lockfile); } if (locked) { free(lockfile); @@ -203,7 +203,7 @@ client_exit_message(void) /* Client main loop. */ int -client_main(int argc, char **argv, int flags) +client_main(struct event_base *base, int argc, char **argv, int flags) { struct cmd *cmd; struct cmd_list *cmdlist; @@ -252,7 +252,7 @@ client_main(int argc, char **argv, int flags) set_signals(client_signal); /* Initialize the client socket and start the server. */ - fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); + fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { if (errno == ECONNREFUSED) { fprintf(stderr, "no server running on %s\n", diff --git a/server.c b/server.c index 9cddecd3..8308367d 100644 --- a/server.c +++ b/server.c @@ -158,7 +158,7 @@ server_create_socket(void) /* Fork new server. */ int -server_start(int lockfd, char *lockfile) +server_start(struct event_base *base, int lockfd, char *lockfile) { int pair[2]; char *cause; @@ -188,7 +188,7 @@ server_start(int lockfd, char *lockfile) /* event_init() was called in our parent, need to reinit. */ clear_signals(0); - if (event_reinit(ev_base) != 0) + if (event_reinit(base) != 0) fatal("event_reinit failed"); logfile("server"); diff --git a/tmux.c b/tmux.c index e66d24c0..b984b903 100644 --- a/tmux.c +++ b/tmux.c @@ -41,8 +41,6 @@ struct options global_s_options; /* session options */ struct options global_w_options; /* window options */ struct environ global_environ; -struct event_base *ev_base; - char *cfg_file; char *shell_cmd; int debug_level; @@ -386,6 +384,5 @@ main(int argc, char **argv) setproctitle("%s (%s)", __progname, socket_path); /* Pass control to the client. */ - ev_base = event_init(); - exit(client_main(argc, argv, flags)); + exit(client_main(event_init(), argc, argv, flags)); } diff --git a/tmux.h b/tmux.h index 10b161de..3b67470f 100644 --- a/tmux.h +++ b/tmux.h @@ -1407,7 +1407,6 @@ extern struct options global_options; extern struct options global_s_options; extern struct options global_w_options; extern struct environ global_environ; -extern struct event_base *ev_base; extern char *cfg_file; extern char *shell_cmd; extern int debug_level; @@ -1792,7 +1791,7 @@ int cmd_string_parse(const char *, struct cmd_list **, const char *, void cmd_wait_for_flush(void); /* client.c */ -int client_main(int, char **, int); +int client_main(struct event_base *, int, char **, int); /* key-bindings.c */ RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); @@ -1829,7 +1828,7 @@ void server_clear_marked(void); int server_is_marked(struct session *, struct winlink *, struct window_pane *); int server_check_marked(void); -int server_start(int, char *); +int server_start(struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); From c6e9160c676db48e12e3c38229870d1c020cf1d2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Aug 2015 22:40:25 +0000 Subject: [PATCH 282/703] Login shell can be a client flag, and move the exec code into client.c. --- client.c | 67 ++++++++++++++++++++++++++++++++++++++++++-------------- tmux.c | 36 +++++------------------------- tmux.h | 4 +--- 3 files changed, 57 insertions(+), 50 deletions(-) diff --git a/client.c b/client.c index 271568da..55c5aa56 100644 --- a/client.c +++ b/client.c @@ -33,6 +33,7 @@ #include "tmux.h" +int client_flags; struct imsgbuf client_ibuf; struct event client_event; struct event client_stdin; @@ -51,9 +52,10 @@ enum msgtype client_exittype; const char *client_exitsession; int client_attached; +__dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); -void client_send_identify(int); +void client_send_identify(void); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); @@ -62,7 +64,7 @@ void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); void client_callback(int, short, void *); int client_dispatch_attached(void); -int client_dispatch_wait(void *); +int client_dispatch_wait(void); const char *client_exit_message(void); /* @@ -215,6 +217,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct termios tio, saved_tio; size_t size; + /* Save the flags. */ + client_flags = flags; + /* Set up the initial command. */ cmdflags = 0; if (shell_cmd != NULL) { @@ -266,13 +271,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Create imsg. */ imsg_init(&client_ibuf, fd); - event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); + event_set(&client_event, fd, EV_READ, client_callback, NULL); /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); - if (flags & CLIENT_CONTROLCONTROL) { + if (client_flags & CLIENT_CONTROLCONTROL) { if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno)); @@ -291,7 +296,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(flags); + client_send_identify(); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -326,13 +331,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Print the exit message, if any, and exit. */ if (client_attached) { - if (client_exitreason != CLIENT_EXIT_NONE && !login_shell) + if (client_exitreason != CLIENT_EXIT_NONE) printf("[%s]\n", client_exit_message()); ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); - } else if (flags & CLIENT_CONTROLCONTROL) { + } else if (client_flags & CLIENT_CONTROLCONTROL) { if (client_exitreason != CLIENT_EXIT_NONE) printf("%%exit %s\n", client_exit_message()); else @@ -346,12 +351,12 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Send identify messages to server. */ void -client_send_identify(int flags) +client_send_identify(void) { const char *s; char **ss; size_t sslen; - int fd; + int fd, flags = client_flags; pid_t pid; client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); @@ -421,14 +426,13 @@ client_update_event(void) events = EV_READ; if (client_ibuf.w.queued > 0) events |= EV_WRITE; - event_set( - &client_event, client_ibuf.fd, events, client_callback, shell_cmd); + event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); event_add(&client_event, NULL); } /* Callback to handle signals in the client. */ void -client_signal(int sig, unused short events, unused void *data) +client_signal(int sig, unused short events, unused void *arg) { struct sigaction sigact; int status; @@ -470,7 +474,7 @@ client_signal(int sig, unused short events, unused void *data) /* Callback for client imsg read events. */ void -client_callback(unused int fd, short events, void *data) +client_callback(unused int fd, short events, unused void *arg) { ssize_t n; int retval; @@ -481,7 +485,7 @@ client_callback(unused int fd, short events, void *data) if (client_attached) retval = client_dispatch_attached(); else - retval = client_dispatch_wait(data); + retval = client_dispatch_wait(); if (retval != 0) { event_loopexit(NULL); return; @@ -504,7 +508,7 @@ lost_server: /* Callback for client stdin read events. */ void -client_stdin_callback(unused int fd, unused short events, unused void *data1) +client_stdin_callback(unused int fd, unused short events, unused void *arg) { struct msg_stdin_data data; @@ -536,9 +540,38 @@ client_write(int fd, const char *data, size_t size) } } +/* Run command in shell; used for -c. */ +__dead void +client_exec(const char *shell) +{ + const char *name, *ptr; + char *argv0; + + log_debug("shell %s, command %s", shell, shell_cmd); + + ptr = strrchr(shell, '/'); + if (ptr != NULL && *(ptr + 1) != '\0') + name = ptr + 1; + else + name = shell; + if (client_flags & CLIENT_LOGIN) + xasprintf(&argv0, "-%s", name); + else + xasprintf(&argv0, "%s", name); + setenv("SHELL", shell, 1); + + setblocking(STDIN_FILENO, 1); + setblocking(STDOUT_FILENO, 1); + setblocking(STDERR_FILENO, 1); + closefrom(STDERR_FILENO + 1); + + execl(shell, argv0, "-c", shell_cmd, (char *) NULL); + fatal("execl failed"); +} + /* Dispatch imsgs when in wait state (before MSG_READY). */ int -client_dispatch_wait(void *data0) +client_dispatch_wait(void) { struct imsg imsg; char *data; @@ -614,7 +647,7 @@ client_dispatch_wait(void *data0) fatalx("bad MSG_SHELL string"); clear_signals(0); - shell_exec(data, data0); + client_exec(data); /* NOTREACHED */ case MSG_DETACH: case MSG_DETACHKILL: diff --git a/tmux.c b/tmux.c index b984b903..1ef23915 100644 --- a/tmux.c +++ b/tmux.c @@ -46,7 +46,6 @@ char *shell_cmd; int debug_level; time_t start_time; char socket_path[PATH_MAX]; -int login_shell; char *environ_path; __dead void usage(void); @@ -170,32 +169,6 @@ setblocking(int fd, int state) } } -__dead void -shell_exec(const char *shell, const char *shellcmd) -{ - const char *shellname, *ptr; - char *argv0; - - ptr = strrchr(shell, '/'); - if (ptr != NULL && *(ptr + 1) != '\0') - shellname = ptr + 1; - else - shellname = shell; - if (login_shell) - xasprintf(&argv0, "-%s", shellname); - else - xasprintf(&argv0, "%s", shellname); - setenv("SHELL", shell, 1); - - setblocking(STDIN_FILENO, 1); - setblocking(STDOUT_FILENO, 1); - setblocking(STDERR_FILENO, 1); - closefrom(STDERR_FILENO + 1); - - execl(shell, argv0, "-c", shellcmd, (char *) NULL); - fatal("execl failed"); -} - const char * find_home(void) { @@ -229,9 +202,12 @@ main(int argc, char **argv) setlocale(LC_TIME, ""); - flags = 0; + if (**argv == '-') + flags = CLIENT_LOGIN; + else + flags = 0; + label = path = NULL; - login_shell = (**argv == '-'); while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUv")) != -1) { switch (opt) { case '2': @@ -252,7 +228,7 @@ main(int argc, char **argv) cfg_file = xstrdup(optarg); break; case 'l': - login_shell = 1; + flags |= CLIENT_LOGIN; break; case 'L': free(label); diff --git a/tmux.h b/tmux.h index 3b67470f..528c3e71 100644 --- a/tmux.h +++ b/tmux.h @@ -1203,7 +1203,7 @@ struct client { struct screen status; #define CLIENT_TERMINAL 0x1 -/* 0x2 unused */ +#define CLIENT_LOGIN 0x2 #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 @@ -1412,14 +1412,12 @@ extern char *shell_cmd; extern int debug_level; extern time_t start_time; extern char socket_path[PATH_MAX]; -extern int login_shell; extern char *environ_path; void logfile(const char *); const char *getshell(void); int checkshell(const char *); int areshell(const char *); void setblocking(int, int); -__dead void shell_exec(const char *, const char *); const char *find_home(void); /* cfg.c */ From 6a539c00dfe52f588376d997a75e6c7540db5e14 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 30 Aug 2015 22:56:36 +0000 Subject: [PATCH 283/703] Path from $TMUX does not need to be global anymore. --- tmux.c | 26 +++++++++++++------------- tmux.h | 1 - 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/tmux.c b/tmux.c index 1ef23915..6eb2a301 100644 --- a/tmux.c +++ b/tmux.c @@ -46,7 +46,6 @@ char *shell_cmd; int debug_level; time_t start_time; char socket_path[PATH_MAX]; -char *environ_path; __dead void usage(void); char *makesocketpath(const char *); @@ -191,10 +190,8 @@ int main(int argc, char **argv) { char *s, *path, *label, **var, tmp[PATH_MAX]; - char in[256]; const char *home; - long long pid; - int opt, flags, keys, session; + int opt, flags, keys; #ifdef DEBUG malloc_options = (char *) "AFGJPX"; @@ -321,11 +318,6 @@ main(int argc, char **argv) } } - /* Get path from environment. */ - s = getenv("TMUX"); - if (s != NULL && sscanf(s, "%255[^,],%lld,%d", in, &pid, &session) == 3) - environ_path = xstrdup(in); - /* * Figure out the socket path. If specified on the command-line with -S * or -L, use it, otherwise try $TMUX or assume -L default. @@ -333,8 +325,15 @@ main(int argc, char **argv) if (path == NULL) { /* If no -L, use the environment. */ if (label == NULL) { - if (environ_path != NULL) - path = xstrdup(environ_path); + s = getenv("TMUX"); + if (s != NULL) { + path = xstrdup(s); + path[strcspn (path, ",")] = '\0'; + if (*path == '\0') { + free(path); + label = xstrdup("default"); + } + } else label = xstrdup("default"); } @@ -343,14 +342,15 @@ main(int argc, char **argv) if (label != NULL) { if ((path = makesocketpath(label)) == NULL) { fprintf(stderr, "can't create socket: %s\n", - strerror(errno)); + strerror(errno)); exit(1); } } } free(label); - if (strlcpy(socket_path, path, sizeof socket_path) >= sizeof socket_path) { + if (strlcpy(socket_path, path, sizeof socket_path) >= + sizeof socket_path) { fprintf(stderr, "socket path too long: %s\n", path); exit(1); } diff --git a/tmux.h b/tmux.h index 528c3e71..1bde93cd 100644 --- a/tmux.h +++ b/tmux.h @@ -1412,7 +1412,6 @@ extern char *shell_cmd; extern int debug_level; extern time_t start_time; extern char socket_path[PATH_MAX]; -extern char *environ_path; void logfile(const char *); const char *getshell(void); int checkshell(const char *); From 2c6ea705fd05966c00534623a9b8fa3431df98f5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 31 Aug 2015 19:57:37 +0100 Subject: [PATCH 284/703] Bring back pane_current_path. --- format.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/format.c b/format.c index 5268a405..bbb172af 100644 --- a/format.c +++ b/format.c @@ -50,6 +50,7 @@ void format_cb_session_alerts(struct format_tree *, struct format_entry *); void format_cb_window_layout(struct format_tree *, struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); +void format_cb_current_path(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); void format_cb_pane_tabs(struct format_tree *, struct format_entry *); @@ -386,6 +387,21 @@ format_cb_current_command(struct format_tree *ft, struct format_entry *fe) free(cmd); } +/* Callback for pane_current_path. */ +void +format_cb_current_path(struct format_tree *ft, struct format_entry *fe) +{ + struct window_pane *wp = ft->wp; + char *cwd; + + if (wp == NULL) + return; + + cwd = osdep_get_cwd(wp->fd); + if (cwd != NULL) + fe->value = xstrdup(cwd); +} + /* Callback for history_bytes. */ void format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) @@ -1037,6 +1053,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_pid", "%ld", (long) wp->pid); format_add_cb(ft, "pane_start_command", format_cb_start_command); format_add_cb(ft, "pane_current_command", format_cb_current_command); + format_add_cb(ft, "pane_current_path", format_cb_current_path); format_add(ft, "cursor_x", "%u", wp->base.cx); format_add(ft, "cursor_y", "%u", wp->base.cy); From 2a836bc306c5635329bec2dd35ba9537a552ff9a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 09:48:34 +0000 Subject: [PATCH 285/703] All the cmd_*_entry declarations do not need to be in tmux.h. --- cmd.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tmux.h | 86 --------------------------------------------------------- 2 files changed, 87 insertions(+), 86 deletions(-) diff --git a/cmd.c b/cmd.c index e8ce932d..0c20d656 100644 --- a/cmd.c +++ b/cmd.c @@ -28,6 +28,93 @@ #include "tmux.h" +extern const struct cmd_entry cmd_attach_session_entry; +extern const struct cmd_entry cmd_bind_key_entry; +extern const struct cmd_entry cmd_break_pane_entry; +extern const struct cmd_entry cmd_capture_pane_entry; +extern const struct cmd_entry cmd_choose_buffer_entry; +extern const struct cmd_entry cmd_choose_client_entry; +extern const struct cmd_entry cmd_choose_session_entry; +extern const struct cmd_entry cmd_choose_tree_entry; +extern const struct cmd_entry cmd_choose_window_entry; +extern const struct cmd_entry cmd_clear_history_entry; +extern const struct cmd_entry cmd_clock_mode_entry; +extern const struct cmd_entry cmd_command_prompt_entry; +extern const struct cmd_entry cmd_confirm_before_entry; +extern const struct cmd_entry cmd_copy_mode_entry; +extern const struct cmd_entry cmd_delete_buffer_entry; +extern const struct cmd_entry cmd_detach_client_entry; +extern const struct cmd_entry cmd_display_message_entry; +extern const struct cmd_entry cmd_display_panes_entry; +extern const struct cmd_entry cmd_down_pane_entry; +extern const struct cmd_entry cmd_find_window_entry; +extern const struct cmd_entry cmd_has_session_entry; +extern const struct cmd_entry cmd_if_shell_entry; +extern const struct cmd_entry cmd_join_pane_entry; +extern const struct cmd_entry cmd_kill_pane_entry; +extern const struct cmd_entry cmd_kill_server_entry; +extern const struct cmd_entry cmd_kill_session_entry; +extern const struct cmd_entry cmd_kill_window_entry; +extern const struct cmd_entry cmd_last_pane_entry; +extern const struct cmd_entry cmd_last_window_entry; +extern const struct cmd_entry cmd_link_window_entry; +extern const struct cmd_entry cmd_list_buffers_entry; +extern const struct cmd_entry cmd_list_clients_entry; +extern const struct cmd_entry cmd_list_commands_entry; +extern const struct cmd_entry cmd_list_keys_entry; +extern const struct cmd_entry cmd_list_panes_entry; +extern const struct cmd_entry cmd_list_sessions_entry; +extern const struct cmd_entry cmd_list_windows_entry; +extern const struct cmd_entry cmd_load_buffer_entry; +extern const struct cmd_entry cmd_lock_client_entry; +extern const struct cmd_entry cmd_lock_server_entry; +extern const struct cmd_entry cmd_lock_session_entry; +extern const struct cmd_entry cmd_move_pane_entry; +extern const struct cmd_entry cmd_move_window_entry; +extern const struct cmd_entry cmd_new_session_entry; +extern const struct cmd_entry cmd_new_window_entry; +extern const struct cmd_entry cmd_next_layout_entry; +extern const struct cmd_entry cmd_next_window_entry; +extern const struct cmd_entry cmd_paste_buffer_entry; +extern const struct cmd_entry cmd_pipe_pane_entry; +extern const struct cmd_entry cmd_previous_layout_entry; +extern const struct cmd_entry cmd_previous_window_entry; +extern const struct cmd_entry cmd_refresh_client_entry; +extern const struct cmd_entry cmd_rename_session_entry; +extern const struct cmd_entry cmd_rename_window_entry; +extern const struct cmd_entry cmd_resize_pane_entry; +extern const struct cmd_entry cmd_respawn_pane_entry; +extern const struct cmd_entry cmd_respawn_window_entry; +extern const struct cmd_entry cmd_rotate_window_entry; +extern const struct cmd_entry cmd_run_shell_entry; +extern const struct cmd_entry cmd_save_buffer_entry; +extern const struct cmd_entry cmd_select_layout_entry; +extern const struct cmd_entry cmd_select_pane_entry; +extern const struct cmd_entry cmd_select_window_entry; +extern const struct cmd_entry cmd_send_keys_entry; +extern const struct cmd_entry cmd_send_prefix_entry; +extern const struct cmd_entry cmd_server_info_entry; +extern const struct cmd_entry cmd_set_buffer_entry; +extern const struct cmd_entry cmd_set_environment_entry; +extern const struct cmd_entry cmd_set_option_entry; +extern const struct cmd_entry cmd_set_window_option_entry; +extern const struct cmd_entry cmd_show_buffer_entry; +extern const struct cmd_entry cmd_show_environment_entry; +extern const struct cmd_entry cmd_show_messages_entry; +extern const struct cmd_entry cmd_show_options_entry; +extern const struct cmd_entry cmd_show_window_options_entry; +extern const struct cmd_entry cmd_source_file_entry; +extern const struct cmd_entry cmd_split_window_entry; +extern const struct cmd_entry cmd_start_server_entry; +extern const struct cmd_entry cmd_suspend_client_entry; +extern const struct cmd_entry cmd_swap_pane_entry; +extern const struct cmd_entry cmd_swap_window_entry; +extern const struct cmd_entry cmd_switch_client_entry; +extern const struct cmd_entry cmd_unbind_key_entry; +extern const struct cmd_entry cmd_unlink_window_entry; +extern const struct cmd_entry cmd_up_pane_entry; +extern const struct cmd_entry cmd_wait_for_entry; + const struct cmd_entry *cmd_table[] = { &cmd_attach_session_entry, &cmd_bind_key_entry, diff --git a/tmux.h b/tmux.h index 1bde93cd..4e880028 100644 --- a/tmux.h +++ b/tmux.h @@ -1671,92 +1671,6 @@ struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, struct winlink **); char *cmd_template_replace(const char *, const char *, int); extern const struct cmd_entry *cmd_table[]; -extern const struct cmd_entry cmd_attach_session_entry; -extern const struct cmd_entry cmd_bind_key_entry; -extern const struct cmd_entry cmd_break_pane_entry; -extern const struct cmd_entry cmd_capture_pane_entry; -extern const struct cmd_entry cmd_choose_buffer_entry; -extern const struct cmd_entry cmd_choose_client_entry; -extern const struct cmd_entry cmd_choose_session_entry; -extern const struct cmd_entry cmd_choose_tree_entry; -extern const struct cmd_entry cmd_choose_window_entry; -extern const struct cmd_entry cmd_clear_history_entry; -extern const struct cmd_entry cmd_clock_mode_entry; -extern const struct cmd_entry cmd_command_prompt_entry; -extern const struct cmd_entry cmd_confirm_before_entry; -extern const struct cmd_entry cmd_copy_mode_entry; -extern const struct cmd_entry cmd_delete_buffer_entry; -extern const struct cmd_entry cmd_detach_client_entry; -extern const struct cmd_entry cmd_display_message_entry; -extern const struct cmd_entry cmd_display_panes_entry; -extern const struct cmd_entry cmd_down_pane_entry; -extern const struct cmd_entry cmd_find_window_entry; -extern const struct cmd_entry cmd_has_session_entry; -extern const struct cmd_entry cmd_if_shell_entry; -extern const struct cmd_entry cmd_join_pane_entry; -extern const struct cmd_entry cmd_kill_pane_entry; -extern const struct cmd_entry cmd_kill_server_entry; -extern const struct cmd_entry cmd_kill_session_entry; -extern const struct cmd_entry cmd_kill_window_entry; -extern const struct cmd_entry cmd_last_pane_entry; -extern const struct cmd_entry cmd_last_window_entry; -extern const struct cmd_entry cmd_link_window_entry; -extern const struct cmd_entry cmd_list_buffers_entry; -extern const struct cmd_entry cmd_list_clients_entry; -extern const struct cmd_entry cmd_list_commands_entry; -extern const struct cmd_entry cmd_list_keys_entry; -extern const struct cmd_entry cmd_list_panes_entry; -extern const struct cmd_entry cmd_list_sessions_entry; -extern const struct cmd_entry cmd_list_windows_entry; -extern const struct cmd_entry cmd_load_buffer_entry; -extern const struct cmd_entry cmd_lock_client_entry; -extern const struct cmd_entry cmd_lock_server_entry; -extern const struct cmd_entry cmd_lock_session_entry; -extern const struct cmd_entry cmd_move_pane_entry; -extern const struct cmd_entry cmd_move_window_entry; -extern const struct cmd_entry cmd_new_session_entry; -extern const struct cmd_entry cmd_new_window_entry; -extern const struct cmd_entry cmd_next_layout_entry; -extern const struct cmd_entry cmd_next_window_entry; -extern const struct cmd_entry cmd_paste_buffer_entry; -extern const struct cmd_entry cmd_pipe_pane_entry; -extern const struct cmd_entry cmd_previous_layout_entry; -extern const struct cmd_entry cmd_previous_window_entry; -extern const struct cmd_entry cmd_refresh_client_entry; -extern const struct cmd_entry cmd_rename_session_entry; -extern const struct cmd_entry cmd_rename_window_entry; -extern const struct cmd_entry cmd_resize_pane_entry; -extern const struct cmd_entry cmd_respawn_pane_entry; -extern const struct cmd_entry cmd_respawn_window_entry; -extern const struct cmd_entry cmd_rotate_window_entry; -extern const struct cmd_entry cmd_run_shell_entry; -extern const struct cmd_entry cmd_save_buffer_entry; -extern const struct cmd_entry cmd_select_layout_entry; -extern const struct cmd_entry cmd_select_pane_entry; -extern const struct cmd_entry cmd_select_window_entry; -extern const struct cmd_entry cmd_send_keys_entry; -extern const struct cmd_entry cmd_send_prefix_entry; -extern const struct cmd_entry cmd_server_info_entry; -extern const struct cmd_entry cmd_set_buffer_entry; -extern const struct cmd_entry cmd_set_environment_entry; -extern const struct cmd_entry cmd_set_option_entry; -extern const struct cmd_entry cmd_set_window_option_entry; -extern const struct cmd_entry cmd_show_buffer_entry; -extern const struct cmd_entry cmd_show_environment_entry; -extern const struct cmd_entry cmd_show_messages_entry; -extern const struct cmd_entry cmd_show_options_entry; -extern const struct cmd_entry cmd_show_window_options_entry; -extern const struct cmd_entry cmd_source_file_entry; -extern const struct cmd_entry cmd_split_window_entry; -extern const struct cmd_entry cmd_start_server_entry; -extern const struct cmd_entry cmd_suspend_client_entry; -extern const struct cmd_entry cmd_swap_pane_entry; -extern const struct cmd_entry cmd_swap_window_entry; -extern const struct cmd_entry cmd_switch_client_entry; -extern const struct cmd_entry cmd_unbind_key_entry; -extern const struct cmd_entry cmd_unlink_window_entry; -extern const struct cmd_entry cmd_up_pane_entry; -extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, From 83157c02d6935d3ea4dcf6e39d6a531849a775d6 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 10:01:56 +0000 Subject: [PATCH 286/703] Move initial conf load into cfg.c. --- cfg.c | 43 +++++++++++++++++++++++++++++++++++++------ server.c | 21 ++------------------- tmux.h | 3 +-- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/cfg.c b/cfg.c index 37474094..e15d048d 100644 --- a/cfg.c +++ b/cfg.c @@ -23,16 +23,47 @@ #include #include #include +#include #include #include "tmux.h" -struct cmd_q *cfg_cmd_q; -int cfg_finished; -int cfg_references; -char **cfg_causes; -u_int cfg_ncauses; -struct client *cfg_client; +struct cmd_q *cfg_cmd_q; +int cfg_finished; +int cfg_references; +char **cfg_causes; +u_int cfg_ncauses; +struct client *cfg_client; + +void cfg_default_done(struct cmd_q *); + +void +start_cfg(void) +{ + char *cause = NULL; + + cfg_cmd_q = cmdq_new(NULL); + cfg_cmd_q->emptyfn = cfg_default_done; + + cfg_finished = 0; + cfg_references = 1; + + cfg_client = TAILQ_FIRST(&clients); + if (cfg_client != NULL) + cfg_client->references++; + + if (access(TMUX_CONF, R_OK) == 0) { + if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) + cfg_add_cause("%s: %s", TMUX_CONF, cause); + } else if (errno != ENOENT) + cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); + + if (cfg_file != NULL && load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) + cfg_add_cause("%s: %s", cfg_file, cause); + free(cause); + + cmdq_continue(cfg_cmd_q); +} int load_cfg(const char *path, struct cmd_q *cmdq, char **cause) diff --git a/server.c b/server.c index 8308367d..045daead 100644 --- a/server.c +++ b/server.c @@ -160,8 +160,7 @@ server_create_socket(void) int server_start(struct event_base *base, int lockfd, char *lockfile) { - int pair[2]; - char *cause; + int pair[2]; /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) @@ -217,24 +216,8 @@ server_start(struct event_base *base, int lockfd, char *lockfile) free(lockfile); close(lockfd); - cfg_cmd_q = cmdq_new(NULL); - cfg_cmd_q->emptyfn = cfg_default_done; - cfg_finished = 0; - cfg_references = 1; - cfg_client = TAILQ_FIRST(&clients); - if (cfg_client != NULL) - cfg_client->references++; + start_cfg(); - if (access(TMUX_CONF, R_OK) == 0) { - if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) - cfg_add_cause("%s: %s", TMUX_CONF, cause); - } else if (errno != ENOENT) - cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); - if (cfg_file != NULL) { - if (load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) - cfg_add_cause("%s: %s", cfg_file, cause); - } - cmdq_continue(cfg_cmd_q); status_prompt_load_history(); server_add_accept(0); diff --git a/tmux.h b/tmux.h index 4e880028..ced3cccf 100644 --- a/tmux.h +++ b/tmux.h @@ -1420,12 +1420,11 @@ void setblocking(int, int); const char *find_home(void); /* cfg.c */ -extern struct cmd_q *cfg_cmd_q; extern int cfg_finished; extern int cfg_references; extern struct client *cfg_client; +void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); -void cfg_default_done(struct cmd_q *); void cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); From 952ba846111fb91d9882846a4061955262f34212 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 10:10:59 +0000 Subject: [PATCH 287/703] Work out config file when needed not at startup. --- cfg.c | 18 +++++++++++++++++- tmux.c | 28 ++++++++-------------------- tmux.h | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/cfg.c b/cfg.c index e15d048d..ecb38fc0 100644 --- a/cfg.c +++ b/cfg.c @@ -28,6 +28,7 @@ #include "tmux.h" +char *cfg_file; struct cmd_q *cfg_cmd_q; int cfg_finished; int cfg_references; @@ -37,10 +38,18 @@ struct client *cfg_client; void cfg_default_done(struct cmd_q *); +void +set_cfg_file(const char *path) +{ + free(cfg_file); + cfg_file = xstrdup(path); +} + void start_cfg(void) { - char *cause = NULL; + char *cause = NULL; + const char *home; cfg_cmd_q = cmdq_new(NULL); cfg_cmd_q->emptyfn = cfg_default_done; @@ -58,6 +67,13 @@ start_cfg(void) } else if (errno != ENOENT) cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); + if (cfg_file == NULL && (home = find_home()) != NULL) { + xasprintf(&cfg_file, "%s/.tmux.conf", home); + if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { + free(cfg_file); + cfg_file = NULL; + } + } if (cfg_file != NULL && load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) cfg_add_cause("%s: %s", cfg_file, cause); free(cause); diff --git a/tmux.c b/tmux.c index 6eb2a301..69097bf9 100644 --- a/tmux.c +++ b/tmux.c @@ -41,7 +41,6 @@ struct options global_s_options; /* session options */ struct options global_w_options; /* window options */ struct environ global_environ; -char *cfg_file; char *shell_cmd; int debug_level; time_t start_time; @@ -171,8 +170,11 @@ setblocking(int fd, int state) const char * find_home(void) { - struct passwd *pw; - const char *home; + struct passwd *pw; + static const char *home; + + if (home != NULL) + return (home); home = getenv("HOME"); if (home == NULL || *home == '\0') { @@ -189,9 +191,8 @@ find_home(void) int main(int argc, char **argv) { - char *s, *path, *label, **var, tmp[PATH_MAX]; - const char *home; - int opt, flags, keys; + char *s, *path, *label, **var, tmp[PATH_MAX]; + int opt, flags, keys; #ifdef DEBUG malloc_options = (char *) "AFGJPX"; @@ -221,8 +222,7 @@ main(int argc, char **argv) flags |= CLIENT_CONTROL; break; case 'f': - free(cfg_file); - cfg_file = xstrdup(optarg); + set_cfg_file(optarg); break; case 'l': flags |= CLIENT_LOGIN; @@ -306,18 +306,6 @@ main(int argc, char **argv) options_set_number(&global_w_options, "mode-keys", keys); } - /* Locate the configuration file. */ - if (cfg_file == NULL) { - home = find_home(); - if (home != NULL) { - xasprintf(&cfg_file, "%s/.tmux.conf", home); - if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { - free(cfg_file); - cfg_file = NULL; - } - } - } - /* * Figure out the socket path. If specified on the command-line with -S * or -L, use it, otherwise try $TMUX or assume -L default. diff --git a/tmux.h b/tmux.h index ced3cccf..c2309f8a 100644 --- a/tmux.h +++ b/tmux.h @@ -1407,7 +1407,6 @@ extern struct options global_options; extern struct options global_s_options; extern struct options global_w_options; extern struct environ global_environ; -extern char *cfg_file; extern char *shell_cmd; extern int debug_level; extern time_t start_time; @@ -1425,6 +1424,7 @@ extern int cfg_references; extern struct client *cfg_client; void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); +void set_cfg_file(const char *); void cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); From 69a2d46ee55b5fc62862dde7cf74bc02556e7559 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 11:13:39 +0000 Subject: [PATCH 288/703] Remove dead_clients which is no longer used. --- tmux.h | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.h b/tmux.h index c2309f8a..a71d6c2a 100644 --- a/tmux.h +++ b/tmux.h @@ -1728,7 +1728,6 @@ void alerts_queue(struct window *, int); /* server.c */ extern struct clients clients; -extern struct clients dead_clients; extern struct session *marked_session; extern struct winlink *marked_winlink; extern struct window_pane *marked_window_pane; From fa3d4fab85e4c1f807808640f75f50a41bbb2c5b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 18:50:16 +0000 Subject: [PATCH 289/703] Fix a spelling error, sesson -> session. --- session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.c b/session.c index 56986497..a929cada 100644 --- a/session.c +++ b/session.c @@ -187,7 +187,7 @@ session_free(unused int fd, unused short events, void *arg) { struct session *s = arg; - log_debug("sesson %s freed (%d references)", s->name, s->references); + log_debug("session %s freed (%d references)", s->name, s->references); if (s->references == 0) { free(s->name); From 364a885b0c8b3bc58396775603c7927e1fb19cee Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 19:14:43 +0000 Subject: [PATCH 290/703] Pass logging through vis(3). --- log.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/log.c b/log.c index fa26eb4c..ecb9698a 100644 --- a/log.c +++ b/log.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "tmux.h" @@ -66,19 +67,24 @@ log_close(void) void log_vwrite(const char *msg, va_list ap) { - char *fmt; + char *fmt, *out; struct timeval tv; if (log_file == NULL) return; - gettimeofday(&tv, NULL); - if (asprintf(&fmt, "%lld.%06d %s\n", (long long)tv.tv_sec, - (int)tv.tv_usec, msg) == -1) + if (vasprintf(&fmt, msg, ap) == -1) exit(1); - if (vfprintf(log_file, fmt, ap) == -1) + if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) + exit(1); + + gettimeofday(&tv, NULL); + if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, + (int)tv.tv_usec, out) == -1) exit(1); fflush(log_file); + + free(out); free(fmt); } From 66a2720c56328c186784345664ba283a4ca0748c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 19:16:00 +0000 Subject: [PATCH 291/703] Log the whole new input buffer once rather than each byte. --- input.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index cfe6b4af..25cfe971 100644 --- a/input.c +++ b/input.c @@ -862,10 +862,12 @@ input_parse(struct window_pane *wp) notify_input(wp, evb); off = 0; + log_debug("%s: %s, %zu bytes: %.*s", __func__, ictx->state->name, len, + (int)len, buf); + /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; - log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name); /* Find the transition. */ itr = ictx->state->transitions; @@ -1070,7 +1072,7 @@ input_c0_dispatch(struct input_ctx *ictx) struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; - log_debug("%s: '%c", __func__, ictx->ch); + log_debug("%s: '%c'", __func__, ictx->ch); switch (ictx->ch) { case '\000': /* NUL */ From 93b946ee5076495ff815ad1a1d874341cbc70499 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Sep 2015 19:50:09 +0000 Subject: [PATCH 292/703] Tweak some error messages/comments. --- input.c | 2 +- server-client.c | 2 +- tty-term.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index 25cfe971..1b0c1752 100644 --- a/input.c +++ b/input.c @@ -878,7 +878,7 @@ input_parse(struct window_pane *wp) } if (itr->first == -1 || itr->last == -1) { /* No transition? Eh? */ - fatalx("No transition from state!"); + fatalx("no transition from state"); } /* diff --git a/server-client.c b/server-client.c index 08058ab4..70f5adcb 100644 --- a/server-client.c +++ b/server-client.c @@ -1060,7 +1060,7 @@ server_client_msg_dispatch(struct client *c) s = c->session; if (gettimeofday(&c->activity_time, NULL) != 0) - fatal("gettimeofday"); + fatal("gettimeofday failed"); if (s != NULL) session_update_activity(s, &c->activity_time); diff --git a/tty-term.c b/tty-term.c index 14339de1..f4fd91a8 100644 --- a/tty-term.c +++ b/tty-term.c @@ -560,7 +560,6 @@ tty_term_string(struct tty_term *term, enum tty_code_code code) return (term->codes[code].value.string); } -/* No vtparm. Fucking curses. */ const char * tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) { From 2ebef95994953e49ae84862a65f9ee649a72e200 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 1 Sep 2015 21:08:19 +0100 Subject: [PATCH 293/703] Sync up vis.* for stravis(). --- compat/vis.c | 56 ++++++++++++++++++++++++++++++++++++---------------- compat/vis.h | 10 +++++++++- configure.ac | 14 ++++++------- 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/compat/vis.c b/compat/vis.c index 97eb5271..82b42be9 100644 --- a/compat/vis.c +++ b/compat/vis.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vis.c,v 1.19 2005/09/01 17:15:49 millert Exp $ */ +/* $OpenBSD: vis.c,v 1.24 2015/07/20 01:52:28 millert Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -29,14 +29,17 @@ */ #include -#include +#include #include +#include #include +#include #include "tmux.h" #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') -#define isvisible(c) \ +#define isvisible(c,flag) \ + (((c) == '\\' || (flag & VIS_ALL) == 0) && \ (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ @@ -45,7 +48,7 @@ ((flag & VIS_NL) == 0 && (c) == '\n') || \ ((flag & VIS_SAFE) && ((c) == '\b' || \ (c) == '\007' || (c) == '\r' || \ - isgraph((u_char)(c))))) + isgraph((u_char)(c)))))) /* * vis - visually encode characters @@ -53,10 +56,11 @@ char * vis(char *dst, int c, int flag, int nextc) { - if (isvisible(c)) { - *dst++ = c; - if (c == '\\' && (flag & VIS_NOSLASH) == 0) + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) *dst++ = '\\'; + *dst++ = c; *dst = '\0'; return (dst); } @@ -136,10 +140,10 @@ done: /* * strvis, strnvis, strvisx - visually encode characters from src into dst - * + * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, - * is returned. + * is returned. * * Strnvis will write no more than siz-1 bytes (and will NULL terminate). * The number of bytes needed to fully encode the string is returned. @@ -168,19 +172,18 @@ strnvis(char *dst, const char *src, size_t siz, int flag) i = 0; for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { - if (isvisible(c)) { - i = 1; - *dst++ = c; - if (c == '\\' && (flag & VIS_NOSLASH) == 0) { + if (isvisible(c, flag)) { + if ((c == '"' && (flag & VIS_DQ) != 0) || + (c == '\\' && (flag & VIS_NOSLASH) == 0)) { /* need space for the extra '\\' */ - if (dst < end) - *dst++ = '\\'; - else { - dst--; + if (dst + 1 >= end) { i = 2; break; } + *dst++ = '\\'; } + i = 1; + *dst++ = c; src++; } else { i = vis(tbuf, c, flag, *++src) - tbuf; @@ -203,6 +206,25 @@ strnvis(char *dst, const char *src, size_t siz, int flag) return (dst - start); } +int +stravis(char **outp, const char *src, int flag) +{ + char *buf; + int len, serrno; + + buf = calloc(4, strlen(src) + 1); + if (buf == NULL) + return -1; + len = strvis(buf, src, flag); + serrno = errno; + *outp = realloc(buf, len + 1); + if (*outp == NULL) { + *outp = buf; + errno = serrno; + } + return (len); +} + int strvisx(char *dst, const char *src, size_t len, int flag) { diff --git a/compat/vis.h b/compat/vis.h index d6ff235d..6795139c 100644 --- a/compat/vis.h +++ b/compat/vis.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vis.h,v 1.11 2005/08/09 19:38:31 millert Exp $ */ +/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ /*- @@ -50,6 +50,8 @@ #define VIS_NL 0x10 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ +#define VIS_DQ 0x200 /* backslash-escape double quotes */ +#define VIS_ALL 0x400 /* encode all characters */ /* * other @@ -71,12 +73,18 @@ */ #define UNVIS_END 1 /* no more characters */ +#include + +__BEGIN_DECLS char *vis(char *, int, int, int); int strvis(char *, const char *, int); +int stravis(char **, const char *, int); int strnvis(char *, const char *, size_t, int); int strvisx(char *, const char *, size_t, int); int strunvis(char *, const char *); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t); +__END_DECLS + #endif /* !_VIS_H_ */ diff --git a/configure.ac b/configure.ac index de336fef..9d64a964 100644 --- a/configure.ac +++ b/configure.ac @@ -326,22 +326,22 @@ if test "x$found_strtonum" = xyes; then fi AM_CONDITIONAL(NO_STRTONUM, [test "x$found_strtonum" = xno]) -# Look for strnvis, compat/{vis,unvis}.c used if missing. -AC_CHECK_FUNC(strnvis, found_strnvis=yes, found_strnvis=no) -if test "x$found_strnvis" = xyes; then +# Look for stravis, compat/{vis,unvis}.c used if missing. +AC_CHECK_FUNC(stravis, found_stravis=yes, found_stravis=no) +if test "x$found_stravis" = xyes; then AC_MSG_CHECKING(if strnvis is broken) AC_EGREP_HEADER([strnvis\(char \*, const char \*, size_t, int\)], vis.h, AC_MSG_RESULT(no), - [found_strnvis=no]) - if test "x$found_strnvis" = xno; then + [found_stravis=no]) + if test "x$found_stravis" = xno; then AC_MSG_RESULT(yes) fi fi -if test "x$found_strnvis" = xyes; then +if test "x$found_stravis" = xyes; then AC_DEFINE(HAVE_VIS) fi -AM_CONDITIONAL(NO_VIS, [test "x$found_strnvis" = xno]) +AM_CONDITIONAL(NO_VIS, [test "x$found_stravis" = xno]) # Look for cfmakeraw, compat/cfmakeraw.c used if missing. AC_CHECK_FUNC(cfmakeraw, found_cfmakeraw=yes, found_cfmakeraw=no) From a45164f2e023f436d0c9b0f50580a71f2b4b8f87 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2015 17:12:07 +0000 Subject: [PATCH 294/703] Fix indentation of grid_string_cells_fg. --- grid.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/grid.c b/grid.c index c339cdc5..93fed05e 100644 --- a/grid.c +++ b/grid.c @@ -395,29 +395,29 @@ grid_string_cells_fg(const struct grid_cell *gc, int *values) values[n++] = gc->fg; } else { switch (gc->fg) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - values[n++] = gc->fg + 30; - break; - case 8: - values[n++] = 39; - break; - case 90: - case 91: - case 92: - case 93: - case 94: - case 95: - case 96: - case 97: - values[n++] = gc->fg; - break; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + values[n++] = gc->fg + 30; + break; + case 8: + values[n++] = 39; + break; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + values[n++] = gc->fg; + break; } } return (n); From 8121127606e8c354daa4802177763d2b1f8df81d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2015 17:37:54 +0000 Subject: [PATCH 295/703] We no longer need the terminal service class, so don't bother asking for it. --- tmux.h | 3 --- tty-keys.c | 69 ------------------------------------------------------ tty.c | 9 ------- 3 files changed, 81 deletions(-) diff --git a/tmux.h b/tmux.h index a71d6c2a..9e028194 100644 --- a/tmux.h +++ b/tmux.h @@ -1082,9 +1082,7 @@ LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; - char *path; - u_int class; u_int sx; u_int sy; @@ -1564,7 +1562,6 @@ void tty_putn(struct tty *, const void *, size_t, u_int); int tty_init(struct tty *, struct client *, int, char *); int tty_resize(struct tty *); int tty_set_size(struct tty *, u_int, u_int); -void tty_set_class(struct tty *, u_int); void tty_start_tty(struct tty *); void tty_stop_tty(struct tty *); void tty_set_title(struct tty *, const char *); diff --git a/tty-keys.c b/tty-keys.c index 75e06526..c1de2ab7 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -41,7 +41,6 @@ struct tty_key *tty_keys_find1( struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); void tty_keys_callback(int, short, void *); int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); -int tty_keys_device(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { @@ -482,17 +481,6 @@ tty_keys_next(struct tty *tty) return (0); log_debug("keys are %zu (%.*s)", len, (int) len, buf); - /* Is this device attributes response? */ - switch (tty_keys_device(tty, buf, len, &size)) { - case 0: /* yes */ - key = KEYC_NONE; - goto complete_key; - case -1: /* no, or not valid */ - break; - case 1: /* partial */ - goto partial_key; - } - /* Is this a mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size)) { case 0: /* yes */ @@ -794,60 +782,3 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (0); } - -/* - * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for - * partial. - */ -int -tty_keys_device(struct tty *tty, const char *buf, size_t len, size_t *size) -{ - u_int i, class; - char tmp[64], *endptr; - - /* - * Primary device attributes are \033[?a;b and secondary are - * \033[>a;b;c. - */ - - *size = 0; - - /* First three bytes are always \033[?. */ - if (buf[0] != '\033') - return (-1); - if (len == 1) - return (1); - if (buf[1] != '[') - return (-1); - if (len == 2) - return (1); - if (buf[2] != '>' && buf[2] != '?') - return (-1); - if (len == 3) - return (1); - - /* Copy the rest up to a 'c'. */ - for (i = 0; i < (sizeof tmp) - 1 && buf[3 + i] != 'c'; i++) { - if (3 + i == len) - return (1); - tmp[i] = buf[3 + i]; - } - if (i == (sizeof tmp) - 1) - return (-1); - tmp[i] = '\0'; - *size = 4 + i; - - /* Only primary is of interest. */ - if (buf[2] != '?') - return (0); - - /* Convert service class. */ - class = strtoul(tmp, &endptr, 10); - if (*endptr != ';') - class = 0; - - log_debug("received service class %u", class); - tty_set_class(tty, class); - - return (0); -} diff --git a/tty.c b/tty.c index 374fb8c6..105a24ed 100644 --- a/tty.c +++ b/tty.c @@ -233,7 +233,6 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } - tty_puts(tty, "\033[c"); } tty->cx = UINT_MAX; @@ -253,14 +252,6 @@ tty_start_tty(struct tty *tty) tty->mouse_drag_release = NULL; } -void -tty_set_class(struct tty *tty, u_int class) -{ - if (tty->class != 0) - return; - tty->class = class; -} - void tty_stop_tty(struct tty *tty) { From 38e3baab2a8f46d910ea5104b48d4c71480d4814 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2015 17:43:25 +0000 Subject: [PATCH 296/703] A one line helper function is a little silly. --- alerts.c | 4 ++-- tmux.h | 1 - tty.c | 6 ------ 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/alerts.c b/alerts.c index cfdd30e3..5d52f7ad 100644 --- a/alerts.c +++ b/alerts.c @@ -174,7 +174,7 @@ alerts_check_bell(struct session *s, struct winlink *wl) (action == BELL_OTHER && c->session->curw->window != w) || action == BELL_ANY) - tty_bell(&c->tty); + tty_putcode(&c->tty, TTYC_BEL); continue; } if (action == BELL_CURRENT && c->session->curw->window == w) @@ -258,6 +258,6 @@ alerts_ring_bell(struct session *s) TAILQ_FOREACH(c, &clients, entry) { if (c->session == s && !(c->flags & CLIENT_CONTROL)) - tty_bell(&c->tty); + tty_putcode(&c->tty, TTYC_BEL); } } diff --git a/tmux.h b/tmux.h index 9e028194..0223d857 100644 --- a/tmux.h +++ b/tmux.h @@ -1596,7 +1596,6 @@ void tty_cmd_utf8character(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); -void tty_bell(struct tty *); /* tty-term.c */ extern struct tty_terms tty_terms; diff --git a/tty.c b/tty.c index 105a24ed..c4dfde1d 100644 --- a/tty.c +++ b/tty.c @@ -1712,9 +1712,3 @@ tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) } } } - -void -tty_bell(struct tty *tty) -{ - tty_putcode(tty, TTYC_BEL); -} From 6c10fc659a096d2d3514e23b1300c4bf51a122a4 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Sep 2015 17:52:57 +0000 Subject: [PATCH 297/703] Log pane which received input data. --- input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index 1b0c1752..ab56fc38 100644 --- a/input.c +++ b/input.c @@ -862,8 +862,8 @@ input_parse(struct window_pane *wp) notify_input(wp, evb); off = 0; - log_debug("%s: %s, %zu bytes: %.*s", __func__, ictx->state->name, len, - (int)len, buf); + log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, + ictx->state->name, len, (int)len, buf); /* Parse the input. */ while (off < len) { From 82326dcbe2e833476c2b8961d6b8b5c2cee69c0e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Sep 2015 14:30:23 +0000 Subject: [PATCH 298/703] A couple of style nits. --- format.c | 3 +-- tmux.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/format.c b/format.c index ae22a844..20cf0adb 100644 --- a/format.c +++ b/format.c @@ -218,8 +218,7 @@ format_job_get(struct format_tree *ft, const char *cmd) struct format_job fj0, *fj; fj0.cmd = cmd; - if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) - { + if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) { fj = xcalloc(1, sizeof *fj); fj->cmd = xstrdup(cmd); fj->status = ft->status; diff --git a/tmux.c b/tmux.c index 69097bf9..875e8d72 100644 --- a/tmux.c +++ b/tmux.c @@ -321,8 +321,7 @@ main(int argc, char **argv) free(path); label = xstrdup("default"); } - } - else + } else label = xstrdup("default"); } From aceae73b9a401c0b124a3534079e9c0d998f0dbd Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 4 Sep 2015 12:02:44 +0000 Subject: [PATCH 299/703] Change wait-for to work when the signal comes before the wait, also use some helper functions and add some logging. --- cmd-wait-for.c | 105 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 35 deletions(-) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index a3e85856..0dce438b 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -41,6 +41,7 @@ const struct cmd_entry cmd_wait_for_entry = { struct wait_channel { const char *name; int locked; + int woken; TAILQ_HEAD(, cmd_q) waiters; TAILQ_HEAD(, cmd_q) lockers; @@ -69,8 +70,49 @@ enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, struct wait_channel *); +struct wait_channel *cmd_wait_for_add(const char *); +void cmd_wait_for_remove(struct wait_channel *wc); + +struct wait_channel * +cmd_wait_for_add(const char *name) +{ + struct wait_channel *wc; + + wc = xmalloc(sizeof *wc); + wc->name = xstrdup(name); + + wc->locked = 0; + wc->woken = 0; + + TAILQ_INIT(&wc->waiters); + TAILQ_INIT(&wc->lockers); + + RB_INSERT(wait_channels, &wait_channels, wc); + + log_debug("add wait channel %s", wc->name); + + return (wc); +} + +void +cmd_wait_for_remove(struct wait_channel *wc) +{ + + if (wc->locked) + return; + if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) + return; + + log_debug("remove wait channel %s", wc->name); + + RB_REMOVE(wait_channels, &wait_channels, wc); + + free((void *)wc->name); + free(wc); +} + enum cmd_retval -cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) +cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) { struct args *args = self->args; const char *name = args->argv[0]; @@ -89,15 +131,20 @@ cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, +cmd_wait_for_signal(unused struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { struct cmd_q *wq, *wq1; - if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { - cmdq_error(cmdq, "no waiting clients on %s", name); - return (CMD_RETURN_ERROR); + if (wc == NULL) + wc = cmd_wait_for_add(name); + + if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) { + log_debug("signal wait channel %s, no waiters", wc->name); + wc->woken = 1; + return (CMD_RETURN_NORMAL); } + log_debug("signal wait channel %s, with waiters", wc->name); TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { TAILQ_REMOVE(&wc->waiters, wq, waitentry); @@ -105,12 +152,7 @@ cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, cmdq_continue(wq); } - if (!wc->locked) { - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void *)wc->name); - free(wc); - } - + cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); } @@ -118,19 +160,23 @@ enum cmd_retval cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { - if (cmdq->client == NULL || cmdq->client->session != NULL) { + struct client *c = cmdq->client; + + if (c == NULL || c->session != NULL) { cmdq_error(cmdq, "not able to wait"); return (CMD_RETURN_ERROR); } - if (wc == NULL) { - wc = xmalloc(sizeof *wc); - wc->name = xstrdup(name); - wc->locked = 0; - TAILQ_INIT(&wc->waiters); - TAILQ_INIT(&wc->lockers); - RB_INSERT(wait_channels, &wait_channels, wc); + if (wc == NULL) + wc = cmd_wait_for_add(name); + + if (wc->woken) { + log_debug("wait channel %s already woken (client %d)", wc->name, + c->tty.fd); + cmd_wait_for_remove(wc); + return (CMD_RETURN_NORMAL); } + log_debug("wait channel %s not woken (client %d)", wc->name, c->tty.fd); TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; @@ -147,14 +193,8 @@ cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, return (CMD_RETURN_ERROR); } - if (wc == NULL) { - wc = xmalloc(sizeof *wc); - wc->name = xstrdup(name); - wc->locked = 0; - TAILQ_INIT(&wc->waiters); - TAILQ_INIT(&wc->lockers); - RB_INSERT(wait_channels, &wait_channels, wc); - } + if (wc == NULL) + wc = cmd_wait_for_add(name); if (wc->locked) { TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); @@ -183,11 +223,7 @@ cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, cmdq_continue(wq); } else { wc->locked = 0; - if (TAILQ_EMPTY(&wc->waiters)) { - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void *)wc->name); - free(wc); - } + cmd_wait_for_remove(wc); } return (CMD_RETURN_NORMAL); @@ -210,8 +246,7 @@ cmd_wait_for_flush(void) if (!cmdq_free(wq)) cmdq_continue(wq); } - RB_REMOVE(wait_channels, &wait_channels, wc); - free((void *)wc->name); - free(wc); + wc->locked = 0; + cmd_wait_for_remove(wc); } } From fe536457cca384fdaa5fcf80236404cfaafc38f9 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 6 Sep 2015 21:29:36 +0100 Subject: [PATCH 300/703] Fix includes Let compat/ work out the includes; otherwise works on OpenBSD. --- cfg.c | 1 - log.c | 1 - 2 files changed, 2 deletions(-) diff --git a/cfg.c b/cfg.c index ecb38fc0..9548f582 100644 --- a/cfg.c +++ b/cfg.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "tmux.h" diff --git a/log.c b/log.c index 2acc6f0e..b0b22460 100644 --- a/log.c +++ b/log.c @@ -23,7 +23,6 @@ #include #include #include -#include #include "tmux.h" From 67ee995cc1ec0a9ae2247dae6bdde54211be8c5a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 9 Sep 2015 12:09:21 +0000 Subject: [PATCH 301/703] No need to keep global options around for client which doesn't use them. --- cfg.c | 2 +- client.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cfg.c b/cfg.c index ecb38fc0..2f457e6b 100644 --- a/cfg.c +++ b/cfg.c @@ -98,7 +98,7 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) } found = 0; - while ((buf = fparseln(f, NULL, &line, delim, 0))) { + while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) { log_debug("%s: %s", path, buf); /* Skip empty lines. */ diff --git a/client.c b/client.c index 55c5aa56..d36be86b 100644 --- a/client.c +++ b/client.c @@ -268,6 +268,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } return (1); } + options_free(&global_options); + options_free(&global_s_options); + options_free(&global_w_options); + environ_free(&global_environ); /* Create imsg. */ imsg_init(&client_ibuf, fd); From cfabe30becba6f0c54035a29ee61a6a7f3d0cf60 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Sep 2015 08:58:14 +0000 Subject: [PATCH 302/703] Add session_last_attached time and format, from Sina Siadat. --- cmd-attach-session.c | 2 ++ cmd-new-session.c | 1 + cmd-switch-client.c | 1 + format.c | 7 +++++++ server-fn.c | 1 + tmux.1 | 2 ++ tmux.h | 1 + 7 files changed, 15 insertions(+) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 356bd4aa..a2ae49cb 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -136,6 +136,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { @@ -181,6 +182,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; diff --git a/cmd-new-session.c b/cmd-new-session.c index fa4f1553..7687398e 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -278,6 +278,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); } recalculate_sizes(); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 8bc8a3c5..10171018 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -129,6 +129,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) c->session = s; status_timer_start(c); session_update_activity(s, NULL); + gettimeofday(&s->last_attached_time, NULL); recalculate_sizes(); server_check_unattached(); diff --git a/format.c b/format.c index 20cf0adb..c401fa35 100644 --- a/format.c +++ b/format.c @@ -872,6 +872,13 @@ format_defaults_session(struct format_tree *ft, struct session *s) format_add(ft, "session_created", "%lld", (long long) t); format_add(ft, "session_created_string", "%s", format_time_string(t)); + t = s->last_attached_time.tv_sec; + if (t != 0) { /* zero if never attached */ + format_add(ft, "session_last_attached", "%lld", (long long) t); + format_add(ft, "session_last_attached_string", "%s", + format_time_string(t)); + } + t = s->activity_time.tv_sec; format_add(ft, "session_activity", "%lld", (long long) t); format_add(ft, "session_activity_string", "%s", format_time_string(t)); diff --git a/server-fn.c b/server-fn.c index cddd762f..27908fde 100644 --- a/server-fn.c +++ b/server-fn.c @@ -422,6 +422,7 @@ server_destroy_session(struct session *s) status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s_new, NULL); + gettimeofday(&s_new->last_attached_time, NULL); server_redraw_client(c); } } diff --git a/tmux.1 b/tmux.1 index 8e07fc2a..a2f6cb96 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3401,6 +3401,8 @@ The following variables are available, where appropriate: .It Li "session_activity_string" Ta "" Ta "String time of session last activity" .It Li "session_created" Ta "" Ta "Integer time session created" .It Li "session_created_string" Ta "" Ta "String time session created" +.It Li "session_last_attached" Ta "" Ta "Integer time session last attached" +.It Li "session_last_attached_string" Ta "" Ta "String time session last attached" .It Li "session_group" Ta "" Ta "Number of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_height" Ta "" Ta "Height of session" diff --git a/tmux.h b/tmux.h index 0223d857..8cd83e27 100644 --- a/tmux.h +++ b/tmux.h @@ -981,6 +981,7 @@ struct session { int cwd; struct timeval creation_time; + struct timeval last_attached_time; struct timeval activity_time; struct timeval last_activity_time; From 79e5b6290733567266a584d817fed48e1f2a52c0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Sep 2015 12:41:49 +0100 Subject: [PATCH 303/703] osdep_event_init not event_init. --- tmux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.c b/tmux.c index 86ed6f7e..31ff24a0 100644 --- a/tmux.c +++ b/tmux.c @@ -354,5 +354,5 @@ main(int argc, char **argv) #endif /* Pass control to the client. */ - exit(client_main(event_init(), argc, argv, flags)); + exit(client_main(osdep_event_init(), argc, argv, flags)); } From 66c4ed98d62010421a47bcdb7a4a22a7cbcffff9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 10 Sep 2015 14:59:16 +0100 Subject: [PATCH 304/703] Fix bad merge. --- tmux.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/tmux.h b/tmux.h index 1c643534..61027b98 100644 --- a/tmux.h +++ b/tmux.h @@ -39,9 +39,6 @@ extern char *__progname; extern char **environ; -extern char *__progname; -extern char **environ; - /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" From ef35c9f7659205659d6863058b9a7262b21440a5 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 11 Sep 2015 13:16:35 +0100 Subject: [PATCH 305/703] Add --enable-coverage for gcov. --- Makefile.am | 4 ++++ configure.ac | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index e9a6bd33..982a88d8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,10 @@ CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign CPPFLAGS += -DDEBUG endif +if IS_COVERAGE +CFLAGS += -g -O0 --coverage +LDFLAGS += --coverage +endif CPPFLAGS += -iquote. endif diff --git a/configure.ac b/configure.ac index 9d64a964..f3fabd94 100644 --- a/configure.ac +++ b/configure.ac @@ -23,15 +23,23 @@ AC_PROG_INSTALL # Default tmux.conf goes in /etc not ${prefix}/etc. test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc -# Is this a debug build? +# Is this --enable-debug? found_debug=yes AC_ARG_ENABLE( debug, - AC_HELP_STRING(--enable-debug, create a debug build), + AC_HELP_STRING(--enable-debug, enable debug build flags), found_debug=$enable_debug ) AM_CONDITIONAL(IS_DEBUG, test "x$found_debug" = xyes) +# Is this --enable-coverage? +AC_ARG_ENABLE( + coverage, + AC_HELP_STRING(--enable-coverage, enable coverage build flags), + found_coverage=$enable_coverage +) +AM_CONDITIONAL(IS_COVERAGE, test "x$found_coverage" = xyes) + # Is this a static build? AC_ARG_ENABLE( static, From a3de5dbab1680528d622a5054075e3d8efced795 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Sep 2015 14:41:50 +0000 Subject: [PATCH 306/703] Merge delete-buffer into cmd-set-buffer.c and change the paste buffer API so it has one paste_free() rather than free_top and free_name (everywhere that uses it already has the right pointer). --- Makefile | 1 - cmd-delete-buffer.c | 57 --------------------------------------------- cmd-paste-buffer.c | 8 ++----- cmd-set-buffer.c | 52 ++++++++++++++++++++--------------------- paste.c | 37 ++++++----------------------- tmux.h | 3 +-- 6 files changed, 36 insertions(+), 122 deletions(-) delete mode 100644 cmd-delete-buffer.c diff --git a/Makefile b/Makefile index ad893d4b..653c8d85 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,6 @@ SRCS= alerts.c \ cmd-command-prompt.c \ cmd-confirm-before.c \ cmd-copy-mode.c \ - cmd-delete-buffer.c \ cmd-detach-client.c \ cmd-display-message.c \ cmd-display-panes.c \ diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c deleted file mode 100644 index 42268b78..00000000 --- a/cmd-delete-buffer.c +++ /dev/null @@ -1,57 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include - -#include "tmux.h" - -/* - * Delete a paste buffer. - */ - -enum cmd_retval cmd_delete_buffer_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_delete_buffer_entry = { - "delete-buffer", "deleteb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - cmd_delete_buffer_exec -}; - -enum cmd_retval -cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - const char *bufname; - - if (!args_has(args, 'b')) { - paste_free_top(); - return (CMD_RETURN_NORMAL); - } - bufname = args_get(args, 'b'); - - if (paste_free_name(bufname) != 0) { - cmdq_error(cmdq, "no buffer %s", bufname); - return (CMD_RETURN_ERROR); - } - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index cd3fc7d8..87f09ee6 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -102,12 +102,8 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) bufferevent_write(wp->event, "\033[201~", 6); } - if (args_has(args, 'd')) { - if (bufname == NULL) - paste_free_top(); - else - paste_free_name(bufname); - } + if (pb != NULL && args_has(args, 'd')) + paste_free(pb); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 4fa7ca11..01afa774 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -24,7 +24,7 @@ #include "tmux.h" /* - * Add, set, or append to a paste buffer. + * Add, set, append to or delete a paste buffer. */ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); @@ -37,6 +37,14 @@ const struct cmd_entry cmd_set_buffer_entry = { cmd_set_buffer_exec }; +const struct cmd_entry cmd_delete_buffer_entry = { + "delete-buffer", "deleteb", + "b:", 0, 0, + CMD_BUFFER_USAGE, + 0, + cmd_set_buffer_exec +}; + enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { @@ -46,31 +54,31 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) const char *bufname, *olddata; size_t bufsize, newsize; - bufname = NULL; + bufname = args_get(args, 'b'); + if (bufname == NULL) + pb = paste_get_top(&bufname); + else + pb = paste_get_name(bufname); - if (args_has(args, 'n')) { - if (args->argc > 0) { - cmdq_error(cmdq, "don't provide data with n flag"); + if (self->entry == &cmd_delete_buffer_entry) { + if (pb == NULL) { + cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); } + paste_free(pb); + return (CMD_RETURN_NORMAL); + } - if (args_has(args, 'b')) - bufname = args_get(args, 'b'); - - if (bufname == NULL) { - pb = paste_get_top(&bufname); - if (pb == NULL) { - cmdq_error(cmdq, "no buffer"); - return (CMD_RETURN_ERROR); - } + if (args_has(args, 'n')) { + if (pb == NULL) { + cmdq_error(cmdq, "no buffer"); + return (CMD_RETURN_ERROR); } - if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { cmdq_error(cmdq, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } - return (CMD_RETURN_NORMAL); } @@ -78,19 +86,11 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "no data specified"); return (CMD_RETURN_ERROR); } - pb = NULL; - - bufsize = 0; - bufdata = NULL; - if ((newsize = strlen(args->argv[0])) == 0) return (CMD_RETURN_NORMAL); - if (args_has(args, 'b')) { - bufname = args_get(args, 'b'); - pb = paste_get_name(bufname); - } else if (args_has(args, 'a')) - pb = paste_get_top(&bufname); + bufsize = 0; + bufdata = NULL; if (args_has(args, 'a') && pb != NULL) { olddata = paste_buffer_data(pb, &bufsize); diff --git a/paste.c b/paste.c index e7f752ce..fa58f7c1 100644 --- a/paste.c +++ b/paste.c @@ -111,18 +111,6 @@ paste_get_top(const char **name) return (pb); } -/* Free the most recent buffer. */ -int -paste_free_top(void) -{ - struct paste_buffer *pb; - - pb = paste_get_top(NULL); - if (pb == NULL) - return (-1); - return (paste_free_name(pb->name)); -} - /* Get a paste buffer by name. */ struct paste_buffer * paste_get_name(const char *name) @@ -136,20 +124,10 @@ paste_get_name(const char *name) return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); } -/* Free a paste buffer by name. */ -int -paste_free_name(const char *name) +/* Free a paste buffer. */ +void +paste_free(struct paste_buffer *pb) { - struct paste_buffer *pb, pbfind; - - if (name == NULL || *name == '\0') - return (-1); - - pbfind.name = (char *)name; - pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind); - if (pb == NULL) - return (-1); - RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_time_tree, &paste_by_time, pb); if (pb->automatic) @@ -158,7 +136,6 @@ paste_free_name(const char *name) free(pb->data); free(pb->name); free(pb); - return (0); } /* @@ -179,7 +156,7 @@ paste_add(char *data, size_t size) if (paste_num_automatic < limit) break; if (pb->automatic) - paste_free_name(pb->name); + paste_free(pb); } pb = xmalloc(sizeof *pb); @@ -257,7 +234,7 @@ paste_rename(const char *oldname, const char *newname, char **cause) int paste_set(char *data, size_t size, const char *name, char **cause) { - struct paste_buffer *pb; + struct paste_buffer *pb, *old; if (cause != NULL) *cause = NULL; @@ -288,8 +265,8 @@ paste_set(char *data, size_t size, const char *name, char **cause) pb->automatic = 0; pb->order = paste_next_order++; - if (paste_get_name(name) != NULL) - paste_free_name(name); + if ((old = paste_get_name(name)) != NULL) + paste_free(old); RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); diff --git a/tmux.h b/tmux.h index 8cd83e27..a8cd8532 100644 --- a/tmux.h +++ b/tmux.h @@ -1435,8 +1435,7 @@ const char *paste_buffer_data(struct paste_buffer *, size_t *); struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_get_top(const char **); struct paste_buffer *paste_get_name(const char *); -int paste_free_top(void); -int paste_free_name(const char *); +void paste_free(struct paste_buffer *); void paste_add(char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); From ede0f2f633c2a0c2718004f0527bd5832041e966 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Sep 2015 10:45:55 +0000 Subject: [PATCH 307/703] Set woken flag when flushing so that the channel is freed, while here use the same loop construct for both loops. --- cmd-wait-for.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 0dce438b..e38ea8f1 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -97,7 +97,6 @@ cmd_wait_for_add(const char *name) void cmd_wait_for_remove(struct wait_channel *wc) { - if (wc->locked) return; if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) @@ -241,7 +240,8 @@ cmd_wait_for_flush(void) if (!cmdq_free(wq)) cmdq_continue(wq); } - while ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { + wc->woken = 1; + TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) { TAILQ_REMOVE(&wc->lockers, wq, waitentry); if (!cmdq_free(wq)) cmdq_continue(wq); From 901c2eb20a7066b743c9c6cdde0766737f7c079f Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Sep 2015 13:31:40 +0000 Subject: [PATCH 308/703] Add copy-mode -e to exit copy mode when scrolling off the bottom, useful for quick view of history, from Cam Hutchison. --- cmd-copy-mode.c | 6 +++--- tmux.1 | 12 +++++++++++- tmux.h | 2 +- window-copy.c | 26 ++++++++++++++++++++++++-- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index d729ada6..e04b561b 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -28,8 +28,8 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { "copy-mode", NULL, - "Mt:u", 0, 0, - "[-Mu] " CMD_TARGET_PANE_USAGE, + "Met:u", 0, 0, + "[-Meu] " CMD_TARGET_PANE_USAGE, 0, cmd_copy_mode_exec }; @@ -66,7 +66,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) if (wp->mode != &window_copy_mode) { if (window_pane_set_mode(wp, &window_copy_mode) != 0) return (CMD_RETURN_NORMAL); - window_copy_init_from_pane(wp); + window_copy_init_from_pane(wp, args_has(self->args, 'e')); } if (args_has(args, 'M')) { if (wp->mode != NULL && wp->mode != &window_copy_mode) diff --git a/tmux.1 b/tmux.1 index a2f6cb96..43dce715 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1133,7 +1133,7 @@ The synopsis for the command is: .Bl -tag -width Ds .It Xo Ic copy-mode -.Op Fl Mu +.Op Fl Meu .Op Fl t Ar target-pane .Xc Enter copy mode. @@ -1143,6 +1143,16 @@ option scrolls one page up. .Fl M begins a mouse drag (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . +.Fl e +specifies that scrolling to the bottom of the history (to the visible screen) +should exit copy mode. +While in copy mode, pressing a key other than those used for scrolling will +disable this behaviour. +This is intended to allow fast scrolling through a pane's history, for +example with: +.Bd -literal -offset indent +bind PageUp copy-mode -eu +.Ed .El .Pp Each window displayed by diff --git a/tmux.h b/tmux.h index a8cd8532..080d670d 100644 --- a/tmux.h +++ b/tmux.h @@ -2068,7 +2068,7 @@ extern const char window_clock_table[14][5][5]; /* window-copy.c */ extern const struct window_mode window_copy_mode; -void window_copy_init_from_pane(struct window_pane *); +void window_copy_init_from_pane(struct window_pane *, u_int); void window_copy_init_for_output(struct window_pane *); void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); diff --git a/window-copy.c b/window-copy.c index 460dd10b..d18c0424 100644 --- a/window-copy.c +++ b/window-copy.c @@ -135,7 +135,8 @@ struct window_copy_mode_data { u_int selx; u_int sely; - u_int rectflag; /* are we in rectangle copy mode? */ + int rectflag; /* in rectangle copy mode? */ + int scroll_exit; /* exit on scroll to end? */ u_int cx; u_int cy; @@ -175,6 +176,7 @@ window_copy_init(struct window_pane *wp) data->backing_written = 0; data->rectflag = 0; + data->scroll_exit = 0; data->inputtype = WINDOW_COPY_OFF; data->inputprompt = NULL; @@ -206,7 +208,7 @@ window_copy_init(struct window_pane *wp) } void -window_copy_init_from_pane(struct window_pane *wp) +window_copy_init_from_pane(struct window_pane *wp, u_int scroll_exit) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -219,6 +221,7 @@ window_copy_init_from_pane(struct window_pane *wp) data->backing = &wp->base; data->cx = data->backing->cx; data->cy = data->backing->cy; + data->scroll_exit = scroll_exit; s->cx = data->cx; s->cy = data->cy; @@ -419,6 +422,13 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, } cmd = mode_key_lookup(&data->mdata, key, &arg); + if (cmd != MODEKEYCOPY_PREVIOUSPAGE && + cmd != MODEKEYCOPY_NEXTPAGE && + cmd != MODEKEYCOPY_SCROLLUP && + cmd != MODEKEYCOPY_SCROLLDOWN && + cmd != MODEKEYCOPY_HALFPAGEUP && + cmd != MODEKEYCOPY_HALFPAGEDOWN) + data->scroll_exit = 0; switch (cmd) { case MODEKEYCOPY_APPENDSELECTION: if (sess != NULL) { @@ -461,6 +471,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, case MODEKEYCOPY_SCROLLDOWN: for (; np != 0; np--) window_copy_cursor_down(wp, 1); + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } break; case MODEKEYCOPY_PREVIOUSPAGE: for (; np != 0; np--) @@ -476,6 +490,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, else data->oy -= n; } + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; @@ -498,6 +516,10 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, else data->oy -= n; } + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; From 16efa8483888e326aed2c05a01b63b45a2b118ef Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 10:25:52 +0000 Subject: [PATCH 309/703] Make refresh-client force update of jobs, from Sina Siadat. --- cmd-refresh-client.c | 7 +++++-- format.c | 18 +++++++++++------- server-client.c | 3 ++- status.c | 5 ++++- tmux.h | 5 ++++- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index b6d5d624..5a45ec25 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -65,10 +65,13 @@ cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) } if (tty_set_size(&c->tty, w, h)) recalculate_sizes(); - } else if (args_has(args, 'S')) + } else if (args_has(args, 'S')) { + c->flags |= CLIENT_STATUSFORCE; server_status_client(c); - else + } else { + c->flags |= CLIENT_STATUSFORCE; server_redraw_client(c); + } return (CMD_RETURN_NORMAL); } diff --git a/format.c b/format.c index c401fa35..2fae8da0 100644 --- a/format.c +++ b/format.c @@ -104,7 +104,7 @@ struct format_tree { struct session *s; struct window_pane *wp; - int status; + int flags; RB_HEAD(format_entry_tree, format_entry) tree; }; @@ -216,27 +216,31 @@ const char * format_job_get(struct format_tree *ft, const char *cmd) { struct format_job fj0, *fj; + time_t t; fj0.cmd = cmd; if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) { fj = xcalloc(1, sizeof *fj); fj->cmd = xstrdup(cmd); - fj->status = ft->status; xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); RB_INSERT(format_job_tree, &format_jobs, fj); } - if (fj->job == NULL && fj->last != time(NULL)) { + t = time(NULL); + if (fj->job == NULL && ((ft->flags & FORMAT_FORCE) || fj->last != t)) { fj->job = job_run(fj->cmd, NULL, -1, format_job_callback, NULL, fj); if (fj->job == NULL) { free(fj->out); xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); } + fj->last = t; } - fj->last = time(NULL); + + if (ft->flags & FORMAT_STATUS) + fj->status = 1; return (fj->out); } @@ -438,12 +442,12 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) struct format_tree * format_create(void) { - return (format_create_status(0)); + return (format_create_flags(0)); } /* Create a new tree for the status line. */ struct format_tree * -format_create_status(int status) +format_create_flags(int flags) { struct format_tree *ft; @@ -454,7 +458,7 @@ format_create_status(int status) ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); - ft->status = status; + ft->flags = flags; format_add_cb(ft, "host", format_cb_host); format_add_cb(ft, "host_short", format_cb_host_short); diff --git a/server-client.c b/server-client.c index 70f5adcb..6669bf05 100644 --- a/server-client.c +++ b/server-client.c @@ -937,7 +937,8 @@ server_client_check_redraw(struct client *c) tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; tty_update_mode(tty, tty->mode, NULL); - c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS); + c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS| + CLIENT_STATUSFORCE); } /* Set client title. */ diff --git a/status.c b/status.c index 29cb686c..7a1d2818 100644 --- a/status.c +++ b/status.c @@ -503,7 +503,10 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) if (fmt == NULL) return (xstrdup("")); - ft = format_create_status(1); + if (c->flags & CLIENT_STATUSFORCE) + ft = format_create_flags(FORMAT_STATUS|FORMAT_FORCE); + else + ft = format_create_flags(FORMAT_STATUS); format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand_time(ft, fmt, t); diff --git a/tmux.h b/tmux.h index 080d670d..e5523418 100644 --- a/tmux.h +++ b/tmux.h @@ -1220,6 +1220,7 @@ struct client { #define CLIENT_UTF8 0x10000 #define CLIENT_256COLOURS 0x20000 #define CLIENT_IDENTIFIED 0x40000 +#define CLIENT_STATUSFORCE 0x80000 int flags; struct key_table *keytable; @@ -1442,9 +1443,11 @@ int paste_set(char *, size_t, const char *, char **); char *paste_make_sample(struct paste_buffer *, int); /* format.c */ +#define FORMAT_STATUS 0x1 +#define FORMAT_FORCE 0x2 struct format_tree; struct format_tree *format_create(void); -struct format_tree *format_create_status(int); +struct format_tree *format_create_flags(int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); From af16ce6ad9170e6a48e79e3af696f60daa2bae1d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 11:34:50 +0000 Subject: [PATCH 310/703] When the active pane changes, redraw panes if the style has changed. From Cam Hutchison. --- cmd-select-pane.c | 28 +++++++++++++++++----------- style.c | 12 ++++++++++++ tmux.h | 4 ++++ window.c | 24 ++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 5ea4bdc3..e76587cc 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -47,6 +47,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; + struct window *w; struct session *s; struct window_pane *wp, *lastwp, *markedwp; const char *style; @@ -55,21 +56,24 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); + w = wl->window; - if (wl->window->last == NULL) { + if (w->last == NULL) { cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } if (args_has(self->args, 'e')) - wl->window->last->flags &= ~PANE_INPUTOFF; + w->last->flags &= ~PANE_INPUTOFF; else if (args_has(self->args, 'd')) - wl->window->last->flags |= PANE_INPUTOFF; + w->last->flags |= PANE_INPUTOFF; else { - server_unzoom_window(wl->window); - window_set_active_pane(wl->window, wl->window->last); - server_status_window(wl->window); - server_redraw_window_borders(wl->window); + server_unzoom_window(w); + window_redraw_active_switch(w, w->last); + if (window_set_active_pane(w, w->last)) { + server_status_window(w); + server_redraw_window_borders(w); + } } return (CMD_RETURN_NORMAL); @@ -77,6 +81,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); + w = wl->window; if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) @@ -135,16 +140,17 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (wp == wl->window->active) + if (wp == w->active) return (CMD_RETURN_NORMAL); server_unzoom_window(wp->window); if (!window_pane_visible(wp)) { cmdq_error(cmdq, "pane not visible"); return (CMD_RETURN_ERROR); } - if (window_set_active_pane(wl->window, wp)) { - server_status_window(wl->window); - server_redraw_window_borders(wl->window); + window_redraw_active_switch(w, wp); + if (window_set_active_pane(w, wp)) { + server_status_window(w); + server_redraw_window_borders(w); } return (CMD_RETURN_NORMAL); diff --git a/style.c b/style.c index 9fafdd1d..c00b0fee 100644 --- a/style.c +++ b/style.c @@ -252,3 +252,15 @@ style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) if (gcp->attr != 0) gc->attr |= gcp->attr; } + +/* Check if two styles are the same. */ +int +style_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + return gc1->fg == gc2->fg && + gc1->bg == gc2->bg && + (gc1->flags & ~GRID_FLAG_PADDING) == + (gc2->flags & ~GRID_FLAG_PADDING) && + (gc1->attr & ~GRID_ATTR_CHARSET) == + (gc2->attr & ~GRID_ATTR_CHARSET); +} diff --git a/tmux.h b/tmux.h index e5523418..5b1f0f33 100644 --- a/tmux.h +++ b/tmux.h @@ -1983,6 +1983,8 @@ struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *); +void window_redraw_active_switch(struct window *, + struct window_pane *); struct window_pane *window_add_pane(struct window *, u_int); void window_resize(struct window *, u_int, u_int); int window_zoom(struct window_pane *); @@ -2210,5 +2212,7 @@ void style_apply(struct grid_cell *, struct options *, const char *); void style_apply_update(struct grid_cell *, struct options *, const char *); +int style_equal(const struct grid_cell *, + const struct grid_cell *); #endif /* TMUX_H */ diff --git a/window.c b/window.c index e5decdc4..9e77adcd 100644 --- a/window.c +++ b/window.c @@ -423,6 +423,30 @@ window_set_active_pane(struct window *w, struct window_pane *wp) return (1); } +void +window_redraw_active_switch(struct window *w, struct window_pane *wp) +{ + const struct grid_cell *agc, *wgc; + + if (wp == w->active) + return; + + /* + * If window-style and window-active-style are the same, we don't need + * to redraw panes when switching active panes. Otherwise, if the + * active or inactive pane do not have a custom style, they will need + * to be redrawn. + */ + agc = options_get_style(&w->options, "window-active-style"); + wgc = options_get_style(&w->options, "window-style"); + if (style_equal(agc, wgc)) + return; + if (style_equal(&grid_default_cell, &w->active->colgc)) + w->active->flags |= PANE_REDRAW; + if (style_equal(&grid_default_cell, &wp->colgc)) + wp->flags |= PANE_REDRAW; +} + struct window_pane * window_get_active_at(struct window *w, u_int x, u_int y) { From 8da6de3e663ce23f434b6c90b49a95849f309063 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 11:57:22 +0000 Subject: [PATCH 311/703] Style nit, int for flags not u_int. --- tmux.h | 2 +- window-copy.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tmux.h b/tmux.h index 5b1f0f33..6839be84 100644 --- a/tmux.h +++ b/tmux.h @@ -2073,7 +2073,7 @@ extern const char window_clock_table[14][5][5]; /* window-copy.c */ extern const struct window_mode window_copy_mode; -void window_copy_init_from_pane(struct window_pane *, u_int); +void window_copy_init_from_pane(struct window_pane *, int); void window_copy_init_for_output(struct window_pane *); void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); diff --git a/window-copy.c b/window-copy.c index d18c0424..c7d360de 100644 --- a/window-copy.c +++ b/window-copy.c @@ -208,7 +208,7 @@ window_copy_init(struct window_pane *wp) } void -window_copy_init_from_pane(struct window_pane *wp, u_int scroll_exit) +window_copy_init_from_pane(struct window_pane *wp, int scroll_exit) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; From 216ddf3da5798fe3e7246310ebe0b77e591ee34e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 12:12:24 +0000 Subject: [PATCH 312/703] Move tzset() from log_open to main. --- log.c | 2 -- tmux.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/log.c b/log.c index ecb9698a..8c2fdb07 100644 --- a/log.c +++ b/log.c @@ -48,8 +48,6 @@ log_open(const char *path) setvbuf(log_file, NULL, _IOLBF, 0); event_set_log_callback(log_event_cb); - - tzset(); } /* Close logging. */ diff --git a/tmux.c b/tmux.c index 875e8d72..0b8e6a91 100644 --- a/tmux.c +++ b/tmux.c @@ -199,6 +199,7 @@ main(int argc, char **argv) #endif setlocale(LC_TIME, ""); + tzset(); if (**argv == '-') flags = CLIENT_LOGIN; From 62bb6e37e0bdc2de2a20f7adc38993d2ccfd8c11 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 12:52:22 +0000 Subject: [PATCH 313/703] Should add buffer if no -b. --- cmd-set-buffer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 01afa774..e7f7627e 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -56,11 +56,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) bufname = args_get(args, 'b'); if (bufname == NULL) - pb = paste_get_top(&bufname); + pb = NULL; else pb = paste_get_name(bufname); if (self->entry == &cmd_delete_buffer_entry) { + if (pb == NULL) + pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); @@ -70,6 +72,8 @@ cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 'n')) { + if (pb == NULL) + pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); From 16ee4de5df3c31e362ed095e3feae1b504ddf1c5 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Sep 2015 13:22:02 +0000 Subject: [PATCH 314/703] Remove some extra blank lines. --- cmd-find.c | 1 - cmd-set-option.c | 1 - paste.c | 1 - screen-write.c | 1 - window-clock.c | 1 - 5 files changed, 5 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 87dacfb4..ec883cbd 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -562,7 +562,6 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) return (0); } - /* Stop now if exact only. */ if (exact) return (-1); diff --git a/cmd-set-option.c b/cmd-set-option.c index 98ab2513..e0b07edb 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -263,7 +263,6 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, return (CMD_RETURN_NORMAL); } - /* Unset an option. */ int cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, diff --git a/paste.c b/paste.c index fa58f7c1..44f88d0b 100644 --- a/paste.c +++ b/paste.c @@ -254,7 +254,6 @@ paste_set(char *data, size_t size, const char *name, char **cause) return (-1); } - pb = xmalloc(sizeof *pb); pb->name = xstrdup(name); diff --git a/screen-write.c b/screen-write.c index 37e2b548..f80048b6 100644 --- a/screen-write.c +++ b/screen-write.c @@ -46,7 +46,6 @@ screen_write_stop(unused struct screen_write_ctx *ctx) { } - /* Reset screen state. */ void screen_write_reset(struct screen_write_ctx *ctx) diff --git a/window-clock.c b/window-clock.c index 4a497891..5bc546a9 100644 --- a/window-clock.c +++ b/window-clock.c @@ -233,7 +233,6 @@ window_clock_draw_screen(struct window_pane *wp) screen_write_puts(&ctx, &gc, "%s", tim); } - screen_write_stop(&ctx); return; } From d47789620b76a8a9b08b3f999211e25fcf8ee25f Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Mon, 14 Sep 2015 14:39:51 +0100 Subject: [PATCH 315/703] Add missing --- tmux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.c b/tmux.c index 94d97f92..5514a9af 100644 --- a/tmux.c +++ b/tmux.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "tmux.h" From 166aa97f75d01dababc94c78b8ca1192479166a7 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 14 Sep 2015 15:59:21 +0100 Subject: [PATCH 316/703] No more $Id$. --- Makefile.am | 2 +- configure.ac | 2 +- tools/cmp-cvs.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 696ccb2b..8b39ccfc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -# $Id$ +# Makefile.am # Obvious program stuff. bin_PROGRAMS = tmux diff --git a/configure.ac b/configure.ac index f3fabd94..7874e2a7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -# $Id$ +# configure.ac # Miscellaneous bits. AC_INIT(tmux, 2.1) diff --git a/tools/cmp-cvs.sh b/tools/cmp-cvs.sh index 104ded6b..5429d769 100644 --- a/tools/cmp-cvs.sh +++ b/tools/cmp-cvs.sh @@ -1,4 +1,4 @@ -# $Id$ +#!/bin/ksh rm diff.out touch diff.out From 54bd7612868341c47bd3bd237c9598d5860b3ef3 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 15 Sep 2015 13:57:46 +0100 Subject: [PATCH 317/703] Add BCE to TODO. --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index 6f8d732c..f2bb4ce1 100644 --- a/TODO +++ b/TODO @@ -121,3 +121,5 @@ * multiline status line? * customizable command aliases * automatic pane logging + * BCE? We are halfway there (output side is done for pane backgrounds), + just need to change how screen/grid handles erase From a4b4b299875d833019cb829ca1bee41d71724f37 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:24:54 +0000 Subject: [PATCH 318/703] Rename cmd_q dead flag to a general flags bitmask (will be more flags later). --- cmd-if-shell.c | 2 +- cmd-queue.c | 9 ++++++--- cmd-run-shell.c | 2 +- server-client.c | 2 +- tmux.h | 3 ++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a307bd2f..0271fdea 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -143,7 +143,7 @@ cmd_if_shell_callback(struct job *job) struct cmd_list *cmdlist; char *cause, *cmd; - if (cmdq->dead) + if (cmdq->flags & CMD_Q_DEAD) return; if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) diff --git a/cmd-queue.c b/cmd-queue.c index af987f59..217045fa 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -35,7 +35,7 @@ cmdq_new(struct client *c) cmdq = xcalloc(1, sizeof *cmdq); cmdq->references = 1; - cmdq->dead = 0; + cmdq->flags = 0; cmdq->client = c; cmdq->client_exit = -1; @@ -51,8 +51,11 @@ cmdq_new(struct client *c) int cmdq_free(struct cmd_q *cmdq) { - if (--cmdq->references != 0) - return (cmdq->dead); + if (--cmdq->references != 0) { + if (cmdq->flags & CMD_Q_DEAD) + return (1); + return (0); + } cmdq_flush(cmdq); free(cmdq); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 134cbeba..17ac0f76 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -131,7 +131,7 @@ cmd_run_shell_callback(struct job *job) int retcode; u_int lines; - if (cmdq->dead) + if (cmdq->flags & CMD_Q_DEAD) return; cmd = cdata->cmd; diff --git a/server-client.c b/server-client.c index 6669bf05..10768839 100644 --- a/server-client.c +++ b/server-client.c @@ -214,7 +214,7 @@ server_client_lost(struct client *c) free(c->prompt_string); free(c->prompt_buffer); - c->cmdq->dead = 1; + c->cmdq->flags |= CMD_Q_DEAD; cmdq_free(c->cmdq); c->cmdq = NULL; diff --git a/tmux.h b/tmux.h index 6839be84..3fd057e9 100644 --- a/tmux.h +++ b/tmux.h @@ -1305,7 +1305,8 @@ TAILQ_HEAD(cmd_q_items, cmd_q_item); /* Command queue. */ struct cmd_q { int references; - int dead; + int flags; +#define CMD_Q_DEAD 0x1 struct client *client; int client_exit; From d1b73be6e18a9576f4dc3bac73d7e1cc1a135938 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:29:30 +0000 Subject: [PATCH 319/703] Hoist some common code out of both branches of an if/else. --- cmd-attach-session.c | 54 +++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a2ae49cb..a7ef1cd9 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -93,6 +93,24 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, session_set_current(s, wl); } + if (cflag != NULL) { + ft = format_create(); + format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, + NULL, NULL); + cp = format_expand(ft, cflag); + format_free(ft); + + fd = open(cp, O_RDONLY|O_DIRECTORY); + free(cp); + if (fd == -1) { + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } + close(s->cwd); + s->cwd = fd; + } + if (c->session != NULL) { if (dflag) { /* @@ -108,24 +126,6 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } } - if (cflag != NULL) { - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, - NULL, NULL); - cp = format_expand(ft, cflag); - format_free(ft); - - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - close(s->cwd); - s->cwd = fd; - } - if (!Eflag) { update = options_get_string(&s->options, "update-environment"); @@ -146,24 +146,6 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, return (CMD_RETURN_ERROR); } - if (cflag != NULL) { - ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, - NULL, NULL); - cp = format_expand(ft, cflag); - format_free(ft); - - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - close(s->cwd); - s->cwd = fd; - } - if (rflag) c->flags |= CLIENT_READONLY; From ecb257f0efad988660b1a3f27bc9e641275cd1f2 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:40:05 +0000 Subject: [PATCH 320/703] A few minor style nits. --- cmd-list-panes.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/cmd-list-panes.c b/cmd-list-panes.c index bd34344f..0af391c5 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -19,7 +19,6 @@ #include #include -#include #include "tmux.h" @@ -30,10 +29,10 @@ enum cmd_retval cmd_list_panes_exec(struct cmd *, struct cmd_q *); void cmd_list_panes_server(struct cmd *, struct cmd_q *); -void cmd_list_panes_session( - struct cmd *, struct session *, struct cmd_q *, int); -void cmd_list_panes_window(struct cmd *, - struct session *, struct winlink *, struct cmd_q *, int); +void cmd_list_panes_session(struct cmd *, struct session *, struct cmd_q *, + int); +void cmd_list_panes_window(struct cmd *, struct session *, struct winlink *, + struct cmd_q *, int); const struct cmd_entry cmd_list_panes_entry = { "list-panes", "lsp", @@ -77,8 +76,8 @@ cmd_list_panes_server(struct cmd *self, struct cmd_q *cmdq) } void -cmd_list_panes_session( - struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) +cmd_list_panes_session(struct cmd *self, struct session *s, struct cmd_q *cmdq, + int type) { struct winlink *wl; @@ -87,8 +86,8 @@ cmd_list_panes_session( } void -cmd_list_panes_window(struct cmd *self, - struct session *s, struct winlink *wl, struct cmd_q *cmdq, int type) +cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, + struct cmd_q *cmdq, int type) { struct args *args = self->args; struct window_pane *wp; @@ -115,9 +114,9 @@ cmd_list_panes_window(struct cmd *self, "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; case 2: - template = "#{session_name}:#{window_index}.#{pane_index}: " - "[#{pane_width}x#{pane_height}] [history " - "#{history_size}/#{history_limit}, " + template = "#{session_name}:#{window_index}." + "#{pane_index}: [#{pane_width}x#{pane_height}] " + "[history #{history_size}/#{history_limit}, " "#{history_bytes} bytes] #{pane_id}" "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; From 232a0ffc34bd1c4c3dbb006369b8c87b9ee55c9a Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:40:27 +0000 Subject: [PATCH 321/703] Give some variables less silly names. --- cmd-kill-session.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd-kill-session.c b/cmd-kill-session.c index d7e2a219..74843eb6 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -41,16 +41,16 @@ enum cmd_retval cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s, *s2, *s3; + struct session *s, *sloop, *stmp; if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'a')) { - RB_FOREACH_SAFE(s2, sessions, &sessions, s3) { - if (s != s2) { - server_destroy_session(s2); - session_destroy(s2); + RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { + if (sloop != s) { + server_destroy_session(sloop); + session_destroy(sloop); } } } else { From c1d0b6a6eed2fd393ea30d257ce147e8f6b5e6c8 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Sep 2015 22:41:00 +0000 Subject: [PATCH 322/703] Log when cmdq_continue is called. --- cmd-queue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd-queue.c b/cmd-queue.c index 217045fa..1fe34dce 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -195,6 +195,7 @@ cmdq_continue_one(struct cmd_q *cmdq) int cmdq_continue(struct cmd_q *cmdq) { + struct client *c = cmdq->client; struct cmd_q_item *next; enum cmd_retval retval; int empty; @@ -202,6 +203,9 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->references++; notify_disable(); + log_debug("continuing cmdq %p: flags=%#x, client=%d", cmdq, cmdq->flags, + c != NULL ? c->ibuf.fd : -1); + empty = TAILQ_EMPTY(&cmdq->queue); if (empty) goto empty; From 8b5d5dca9f8fcb7832c48baeef10a0320776b30c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Sep 2015 14:11:55 +0000 Subject: [PATCH 323/703] Redraw both src and dst sessions in break-pane. --- cmd-break-pane.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index c7af7865..2aa5c5b7 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -43,7 +43,8 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; - struct session *s; + struct session *src_s; + struct session *dst_s; struct window_pane *wp; struct window *w; char *name; @@ -53,28 +54,28 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) const char *template; char *cp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 's'), &s, &wp)) == NULL) + wl = cmd_find_pane(cmdq, args_get(args, 's'), &src_s, &wp); + if (wl == NULL) return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) + if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst_s)) == -2) return (CMD_RETURN_ERROR); - if (idx != -1 && winlink_find_by_index(&s->windows, idx) != NULL) { + if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { cmdq_error(cmdq, "index %d already in use", idx); return (CMD_RETURN_ERROR); } + w = wl->window; - if (window_count_panes(wl->window) == 1) { + if (window_count_panes(w) == 1) { cmdq_error(cmdq, "can't break with only one pane"); return (CMD_RETURN_ERROR); } - - w = wl->window; server_unzoom_window(w); TAILQ_REMOVE(&w->panes, wp, entry); window_lost_pane(w, wp); layout_close_pane(wp); - w = wp->window = window_create1(s->sx, s->sy); + w = wp->window = window_create1(dst_s->sx, dst_s->sy); TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; name = default_window_name(w); @@ -83,20 +84,25 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) layout_init(w, wp); if (idx == -1) - idx = -1 - options_get_number(&s->options, "base-index"); - wl = session_attach(s, w, idx, &cause); /* can't fail */ + idx = -1 - options_get_number(&dst_s->options, "base-index"); + wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) - session_select(s, wl->idx); + session_select(dst_s, wl->idx); - server_redraw_session(s); - server_status_session_group(s); + server_redraw_session(src_s); + if (src_s != dst_s) + server_redraw_session(dst_s); + server_status_session_group(src_s); + if (src_s != dst_s) + server_status_session_group(dst_s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; ft = format_create(); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, wp); + format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, + wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); From 6b709e655e52a302f537d6995affdbaf4e8ef0f1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 18 Sep 2015 09:55:22 +0000 Subject: [PATCH 324/703] -l should apply to the new not the old pane with -b, from "MadMaverick9" on GitHub. --- layout.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/layout.c b/layout.c index bb1bbf8d..266d1f39 100644 --- a/layout.c +++ b/layout.c @@ -686,6 +686,8 @@ layout_split_pane( case LAYOUT_LEFTRIGHT: if (size < 0) size2 = ((sx + 1) / 2) - 1; + else if (insert_before) + size2 = sx - size - 1; else size2 = size; if (size2 < PANE_MINIMUM) @@ -699,6 +701,8 @@ layout_split_pane( case LAYOUT_TOPBOTTOM: if (size < 0) size2 = ((sy + 1) / 2) - 1; + else if (insert_before) + size2 = sy - size - 1; else size2 = size; if (size2 < PANE_MINIMUM) From d5f223a3fec0b0ccf69f17e3515a20c79ec28e6e Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 21 Sep 2015 09:34:52 +0000 Subject: [PATCH 325/703] Reset the alerts timer always on activity, from Thomas Adam. --- alerts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/alerts.c b/alerts.c index 5d52f7ad..806e565b 100644 --- a/alerts.c +++ b/alerts.c @@ -126,6 +126,9 @@ alerts_reset(struct window *w) void alerts_queue(struct window *w, int flags) { + if (w->flags & WINDOW_ACTIVITY) + alerts_reset(w); + if (!event_initialized(&w->alerts_timer)) evtimer_set(&w->alerts_timer, alerts_timer, w); @@ -139,9 +142,6 @@ alerts_queue(struct window *w, int flags) event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); alerts_fired = 1; } - - if (flags & WINDOW_ACTIVITY) - alerts_reset(w); } int From dc66795e353e1d84c23cb87f4120480a152b43d9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 22 Sep 2015 21:56:16 +0000 Subject: [PATCH 326/703] Don't update last session when the session is unchanged, from Sina Siadat. --- cmd-switch-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 10171018..3a72886a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -124,7 +124,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) environ_update(update, &c->environ, &s->environ); } - if (c->session != NULL) + if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; status_timer_start(c); From 1caebaa49a0a418b21724c3651e993224bfc12eb Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 23 Sep 2015 14:26:53 +0100 Subject: [PATCH 327/703] Add to TODO. --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index f2bb4ce1..78d566a4 100644 --- a/TODO +++ b/TODO @@ -62,6 +62,7 @@ * paste w/o trailing whitespace * command to toggle selection not to move it in copy-mode * regex searching + * copy-pipe should have -x as well - layout stuff * way to tag a layout as a number/name From ddb2d1221b3a824114e7c456251c4cf983b0b330 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Sep 2015 07:02:18 +0000 Subject: [PATCH 328/703] Assign flag not number for flag types (we got away with it so far because that are a union). From Filipe Brandenburger. --- tty-term.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-term.c b/tty-term.c index f4fd91a8..76626ddf 100644 --- a/tty-term.c +++ b/tty-term.c @@ -451,7 +451,7 @@ tty_term_find(char *name, int fd, char **cause) if (n == -1) break; code->type = TTYCODE_FLAG; - code->value.number = n; + code->value.flag = n; break; } } From 69ea6b9373c0cc992932499b89330c26e27d6510 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Sep 2015 12:03:58 +0000 Subject: [PATCH 329/703] Do not leak log file descriptor. --- log.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/log.c b/log.c index 8c2fdb07..711b9b8a 100644 --- a/log.c +++ b/log.c @@ -42,6 +42,9 @@ log_event_cb(unused int severity, const char *msg) void log_open(const char *path) { + if (log_file != NULL) + fclose(log_file); + log_file = fopen(path, "w"); if (log_file == NULL) return; From 2a62917444783e62ea5b8c13069aecb77b47ff07 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 24 Sep 2015 12:06:20 +0000 Subject: [PATCH 330/703] Don't leak fd and path on failure. --- client.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client.c b/client.c index d36be86b..da7bb314 100644 --- a/client.c +++ b/client.c @@ -153,15 +153,19 @@ retry: } fd = server_start(base, lockfd, lockfile); } + if (locked) { free(lockfile); close(lockfd); } - setblocking(fd, 0); return (fd); failed: + if (locked) { + free(lockfile); + close(lockfd); + } close(fd); return (-1); } From 28f23f18e9d79405a60348c4f7aeded33da9135b Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Sep 2015 15:53:07 +0000 Subject: [PATCH 331/703] Free the history when it is cleared, based on a diff from Carlo Cannas. --- cmd-clear-history.c | 4 +--- grid.c | 20 ++++++++++++++++---- tmux.h | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cmd-clear-history.c b/cmd-clear-history.c index 88dbbcf7..63e9d548 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -47,9 +47,7 @@ cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) if (wp->mode == &window_copy_mode) window_pane_reset_mode(wp); - - grid_move_lines(gd, 0, gd->hsize, gd->sy); - gd->hsize = 0; + grid_clear_history(gd); return (CMD_RETURN_NORMAL); } diff --git a/grid.c b/grid.c index 93fed05e..99dafab2 100644 --- a/grid.c +++ b/grid.c @@ -170,6 +170,18 @@ grid_scroll_history(struct grid *gd) gd->hsize++; } +/* Clear the history. */ +void +grid_clear_history(struct grid *gd) +{ + grid_clear_lines(gd, 0, gd->hsize); + grid_move_lines(gd, 0, gd->hsize, gd->sy); + + gd->hsize = 0; + gd->linedata = xreallocarray(gd->linedata, gd->sy, + sizeof *gd->linedata); +} + /* Scroll a region up, moving the top line into the history. */ void grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) @@ -344,8 +356,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) grid_clear_lines(gd, yy, 1); } - memmove( - &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); + memmove(&gd->linedata[dy], &gd->linedata[py], + ny * (sizeof *gd->linedata)); /* Wipe any lines that have been moved (without freeing them). */ for (yy = py; yy < py + ny; yy++) { @@ -371,8 +383,8 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) grid_expand_line(gd, py, px + nx); grid_expand_line(gd, py, dx + nx); - memmove( - &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); + memmove(&gl->celldata[dx], &gl->celldata[px], + nx * sizeof *gl->celldata); /* Wipe any cells that have been moved. */ for (xx = px; xx < px + nx; xx++) { diff --git a/tmux.h b/tmux.h index 3fd057e9..5bf9b39b 100644 --- a/tmux.h +++ b/tmux.h @@ -1842,6 +1842,7 @@ int grid_compare(struct grid *, struct grid *); void grid_collect_history(struct grid *); void grid_scroll_history(struct grid *); void grid_scroll_history_region(struct grid *, u_int, u_int); +void grid_clear_history(struct grid *); void grid_expand_line(struct grid *, u_int, u_int); const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); const struct grid_line *grid_peek_line(struct grid *, u_int); From 03d7dba5d86afa797eac49e75d37554590ef66c3 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Sep 2015 23:30:12 +0000 Subject: [PATCH 332/703] If the terminal has colors=256, only try to use setaf/setab if they exist, reported by Filipe Brandenburger. --- tty.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tty.c b/tty.c index c4dfde1d..7be952c8 100644 --- a/tty.c +++ b/tty.c @@ -1648,14 +1648,19 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) char s[32]; /* - * If the terminfo entry has 256 colours, assume that setaf and setab - * work correctly. + * If the terminfo entry has 256 colours and setaf and setab exist, + * assume that they work correctly. */ if (tty->term->flags & TERM_256COLOURS) { - if (*type == '3') + if (*type == '3') { + if (!tty_term_has(tty->term, TTYC_SETAF)) + goto fallback; tty_putcode1(tty, TTYC_SETAF, colour); - else + } else { + if (!tty_term_has(tty->term, TTYC_SETAB)) + goto fallback; tty_putcode1(tty, TTYC_SETAB, colour); + } return (0); } @@ -1663,13 +1668,15 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) * If the user has specified -2 to the client, setaf and setab may not * work, so send the usual sequence. */ - if (tty->term_flags & TERM_256COLOURS) { - xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); - tty_puts(tty, s); - return (0); - } + if (tty->term_flags & TERM_256COLOURS) + goto fallback; return (-1); + +fallback: + xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); + tty_puts(tty, s); + return (0); } void From 695a591f8e9bce3fe3dd22a23a337f0fa548b543 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 25 Sep 2015 23:30:24 +0000 Subject: [PATCH 333/703] Adding colors=256 to *256color* was always pretty stupid and now it won't work (without adding setaf@:setab@ too). --- options-table.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/options-table.c b/options-table.c index b9431483..cfec138d 100644 --- a/options-table.c +++ b/options-table.c @@ -107,8 +107,7 @@ const struct options_table_entry server_options_table[] = { { .name = "terminal-overrides", .type = OPTIONS_TABLE_STRING, - .default_str = "*256col*:colors=256" - ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" + .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" }, From 7340d5adfdc8cc6d845a373f3e0d59bfd10a45d1 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 7 Oct 2015 09:52:58 +0000 Subject: [PATCH 334/703] Couple of memory leaks in error paths, from Frederik Vanderstraeten. --- cmd-capture-pane.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index a348e155..bd9ecebb 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -196,6 +196,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (c == NULL || (c->session != NULL && !(c->flags & CLIENT_CONTROL))) { cmdq_error(cmdq, "can't write to stdout"); + free(buf); return (CMD_RETURN_ERROR); } evbuffer_add(c->stdout_data, buf, len); @@ -210,11 +211,12 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (paste_set(buf, len, bufname, &cause) != 0) { cmdq_error(cmdq, "%s", cause); - free(buf); free(cause); + free(buf); return (CMD_RETURN_ERROR); } } + free(buf); return (CMD_RETURN_NORMAL); } From 241fd72f754388c140036eb1b826a07700f5be3b Mon Sep 17 00:00:00 2001 From: guenther Date: Sun, 11 Oct 2015 00:26:23 +0000 Subject: [PATCH 335/703] Userspace doesn't need to use SUN_LEN(): connect() and bind() must accept sizeof(struct sockaddr_un), so do the simple, portable thing ok beck@ deraadt@ --- client.c | 2 +- server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index da7bb314..2eb48a49 100644 --- a/client.c +++ b/client.c @@ -119,7 +119,7 @@ retry: fatal("socket failed"); log_debug("trying connect"); - if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { + if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; diff --git a/server.c b/server.c index 045daead..fd77ff38 100644 --- a/server.c +++ b/server.c @@ -145,7 +145,7 @@ server_create_socket(void) return (-1); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); - if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) return (-1); umask(mask); From f199fb6a2b53c99fb92de3db5a7e03ac73fb0db4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Oct 2015 09:24:25 +0100 Subject: [PATCH 336/703] Fix available_fds when there is no AF_INET, reported by Mathieu Arnold. --- compat/imsg.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compat/imsg.c b/compat/imsg.c index 982ee069..6c9bee68 100644 --- a/compat/imsg.c +++ b/compat/imsg.c @@ -51,8 +51,12 @@ available_fds(unsigned int n) for (i = 0; i < n; i++) { fds[i] = -1; if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - ret = 1; - break; + if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) + fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); + if (fds[i] < 0) { + ret = 1; + break; + } } } From c06c14fb299549adeb32c5021032374686e5d682 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 15 Oct 2015 21:42:17 +0100 Subject: [PATCH 337/703] Some header fixes. --- job.c | 2 +- log.c | 2 +- screen.c | 1 - tmux.c | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/job.c b/job.c index ff21b3b6..dc11c4c8 100644 --- a/job.c +++ b/job.c @@ -20,10 +20,10 @@ #include #include +#include #include #include #include -#include #include "tmux.h" diff --git a/log.c b/log.c index c622d894..adb65702 100644 --- a/log.c +++ b/log.c @@ -17,12 +17,12 @@ */ #include -#include #include #include #include #include +#include #include "tmux.h" diff --git a/screen.c b/screen.c index 01314330..f487cc67 100644 --- a/screen.c +++ b/screen.c @@ -18,7 +18,6 @@ #include -#include #include #include #include diff --git a/tmux.c b/tmux.c index 5514a9af..dff7952c 100644 --- a/tmux.c +++ b/tmux.c @@ -26,8 +26,8 @@ #include #include #include -#include #include +#include #include "tmux.h" From cf89abb01367c9f0d7e848ef0058465e8eddf09c Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 16 Oct 2015 07:43:29 +0000 Subject: [PATCH 338/703] Don't free after calling paste_set but do after evbuffer_add, from Theo Buehler. --- cmd-capture-pane.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index bd9ecebb..8958a12d 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -200,11 +200,11 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } evbuffer_add(c->stdout_data, buf, len); + free(buf); if (args_has(args, 'P') && len > 0) evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); } else { - bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); @@ -217,6 +217,5 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) } } - free(buf); return (CMD_RETURN_NORMAL); } From e0527d773144fef667addb5b4f24045c7d8aaa99 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 17 Oct 2015 18:48:22 +0100 Subject: [PATCH 339/703] time.h is not needed now tzset() is not in log.c. --- log.c | 1 - 1 file changed, 1 deletion(-) diff --git a/log.c b/log.c index adb65702..536af173 100644 --- a/log.c +++ b/log.c @@ -22,7 +22,6 @@ #include #include #include -#include #include "tmux.h" From 9c601ebde81a3965541161499ebce0b5e5f1122f Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 17 Oct 2015 18:30:43 +0000 Subject: [PATCH 340/703] Add pledge "stdio unix sendfd proc exec tty" to tmux client process, "sendfd" is dropped after first message from the server. --- client.c | 53 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/client.c b/client.c index 2eb48a49..c3803ed5 100644 --- a/client.c +++ b/client.c @@ -55,7 +55,7 @@ int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); -void client_send_identify(void); +void client_send_identify(const char *, int); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); @@ -214,7 +214,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct cmd *cmd; struct cmd_list *cmdlist; struct msg_command_data *data; - int cmdflags, fd, i; + int cmdflags, fd, i, cwd; + const char* ttynam; pid_t ppid; enum msgtype msg; char *cause; @@ -272,6 +273,26 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } return (1); } + + /* Save these before pledge(). */ + if ((cwd = open(".", O_RDONLY)) == -1) + cwd = open("/", O_RDONLY); + if ((ttynam = ttyname(STDIN_FILENO)) == NULL) + ttynam = ""; + + /* + * Drop privileges for client. "proc exec" is needed for -c and for + * locking (which uses system(3)). + * + * "tty" is needed to restore termios(4) and also for some reason -CC + * does not work properly without it (input is not recognised). + * + * "sendfd" is dropped later in client_dispatch_wait(). + */ + if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) + fatal("pledge failed"); + + /* Free stuff that is not used in the client. */ options_free(&global_options); options_free(&global_s_options); options_free(&global_w_options); @@ -304,7 +325,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(); + client_send_identify(ttynam, cwd); /* closes cwd */ /* Send first command. */ if (msg == MSG_COMMAND) { @@ -359,7 +380,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Send identify messages to server. */ void -client_send_identify(void) +client_send_identify(const char *ttynam, int cwd) { const char *s; char **ss; @@ -373,13 +394,8 @@ client_send_identify(void) s = ""; client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - if ((s = ttyname(STDIN_FILENO)) == NULL) - s = ""; - client_write_one(MSG_IDENTIFY_TTYNAME, -1, s, strlen(s) + 1); - - if ((fd = open(".", O_RDONLY)) == -1) - fd = open("/", O_RDONLY); - client_write_one(MSG_IDENTIFY_CWD, fd, NULL, 0); + client_write_one(MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); + client_write_one(MSG_IDENTIFY_CWD, cwd, NULL, 0); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); @@ -395,8 +411,6 @@ client_send_identify(void) } client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); - - client_update_event(); } /* Helper to send one message. */ @@ -587,6 +601,19 @@ client_dispatch_wait(void) struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; + static int pledge_applied; + + /* + * "sendfd" is no longer required once all of the identify messages + * have been sent. We know the server won't send us anything until that + * point (because we don't ask it to), so we can drop "sendfd" once we + * get the first message from the server. + */ + if (!pledge_applied) { + if (pledge("stdio unix proc exec tty", NULL) != 0) + fatal("pledge failed"); + pledge_applied = 1; + }; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) From 6c3ade76dfae9d9fe6f838a11dd23cd7109505de Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 17 Oct 2015 20:16:12 +0100 Subject: [PATCH 341/703] __OpenBSD__ around pledge(). --- client.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client.c b/client.c index 612d4202..1e7492d9 100644 --- a/client.c +++ b/client.c @@ -276,6 +276,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; +#ifdef __OpenBSD__ /* * Drop privileges for client. "proc exec" is needed for -c and for * locking (which uses system(3)). @@ -287,6 +288,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) */ if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) fatal("pledge failed"); +#endif /* Free stuff that is not used in the client. */ options_free(&global_options); @@ -605,6 +607,7 @@ client_dispatch_wait(void) struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; +#ifdef __OpenBSD__ static int pledge_applied; /* @@ -618,6 +621,7 @@ client_dispatch_wait(void) fatal("pledge failed"); pledge_applied = 1; }; +#endif for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) From 310f0a960ca64fa3809545badc629c0c166c6cd2 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 18 Oct 2015 18:10:43 +0100 Subject: [PATCH 342/703] Update for 2.1 release. --- CHANGES | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index cf695936..db7be25b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,54 @@ +CHANGES FROM 2.0 to 2.1 18 October 2015 + +Incompatible Changes +==================== + +* Mouse-mode has been rewritten. There's now no longer options for: + - mouse-resize-pane + - mouse-select-pane + - mouse-select-window + - mode-mouse + + Instead there is just one option: 'mouse' which turns on mouse support + entirely. +* 'default-terminal' is now a session option. Furthermore, if this is set + to 'screen-*' then emulate what screen does. If italics are wanted, this + can be set to 'tmux' but this is still new and not necessarily supported + on all platforms with older ncurses installs. +* The c0-* options for rate-limiting have been removed. Instead, a backoff + approach is used. + +Normal Changes +============== + +* New formats: + - session_activity + - window_linked + - window_activity_format + - session_alerts + - session_last_attached + - client_pid + - pid +* 'copy-selection', 'append-selection', 'start-named-buffer' now understand + an '-x' flag to prevent it exiting copying mode. +* 'select-pane' now understands '-P' to set window/pane background colours. +* 'renumber-windows' now understands windows which are unlinked. +* 'bind' now understands multiple key tables. Allows for key-chaining. +* 'select-layout' understands '-o' to undo the last layout change. +* The environment is updated when switching sessions as well as attaching. +* 'select-pane' now understands '-M' for marking a pane. This marked pane + can then be used with commands which understand src-pane specifiers + automatically. +* If a session/window target is prefixed with '=' then only an exact match + is considered. +* 'move-window' understands '-a'. +* 'update-environment' understands '-E' when attach-session is used on an + already attached client. +* 'show-environment' understands '-s' to output Bourne-compatible commands. +* New option: 'history-file' to save/restore command prompt history. +* Copy mode is exited if the history is cleared whilst in copy-mode. +* 'copy-mode' learned '-e' to exit copy-mode when scrolling to end. + CHANGES FROM 1.9a to 2.0 6 March 2015 Incompatible Changes diff --git a/configure.ac b/configure.ac index 7874e2a7..93aae20d 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ AC_PROG_INSTALL test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc # Is this --enable-debug? -found_debug=yes +#found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, enable debug build flags), From 7c78b2b756a207896406a6e6a857e8cedb851c7e Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 18 Oct 2015 18:19:08 +0100 Subject: [PATCH 343/703] Start working on tmux 2.2 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 93aae20d..a2b9e054 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # configure.ac # Miscellaneous bits. -AC_INIT(tmux, 2.1) +AC_INIT(tmux, 2.2) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) @@ -24,7 +24,7 @@ AC_PROG_INSTALL test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc # Is this --enable-debug? -#found_debug=yes +found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, enable debug build flags), From 174a2ad731055f97838290226d656813143620ca Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 18 Oct 2015 20:42:42 +0000 Subject: [PATCH 344/703] Pass current directory as a string rather than a file descriptor because pledge doesn't let us pass directory file descriptors. --- client.c | 18 +++++++++--------- server-client.c | 7 ++++--- tmux.h | 3 ++- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/client.c b/client.c index c3803ed5..73d871bc 100644 --- a/client.c +++ b/client.c @@ -55,7 +55,7 @@ int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); -void client_send_identify(const char *, int); +void client_send_identify(const char *, const char *); int client_write_one(enum msgtype, int, const void *, size_t); int client_write_server(enum msgtype, const void *, size_t); void client_update_event(void); @@ -214,11 +214,11 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct cmd *cmd; struct cmd_list *cmdlist; struct msg_command_data *data; - int cmdflags, fd, i, cwd; - const char* ttynam; + int cmdflags, fd, i; + const char *ttynam, *cwd; pid_t ppid; enum msgtype msg; - char *cause; + char *cause, path[PATH_MAX]; struct termios tio, saved_tio; size_t size; @@ -275,8 +275,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Save these before pledge(). */ - if ((cwd = open(".", O_RDONLY)) == -1) - cwd = open("/", O_RDONLY); + if ((cwd = getcwd(path, sizeof path)) == NULL) + cwd = "/"; if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; @@ -325,7 +325,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } /* Send identify messages. */ - client_send_identify(ttynam, cwd); /* closes cwd */ + client_send_identify(ttynam, cwd); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -380,7 +380,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Send identify messages to server. */ void -client_send_identify(const char *ttynam, int cwd) +client_send_identify(const char *ttynam, const char *cwd) { const char *s; char **ss; @@ -395,7 +395,7 @@ client_send_identify(const char *ttynam, int cwd) client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); client_write_one(MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); - client_write_one(MSG_IDENTIFY_CWD, cwd, NULL, 0); + client_write_one(MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); diff --git a/server-client.c b/server-client.c index 10768839..52ced4d4 100644 --- a/server-client.c +++ b/server-client.c @@ -1166,9 +1166,10 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) c->ttyname = xstrdup(data); break; case MSG_IDENTIFY_CWD: - if (datalen != 0) - fatalx("bad MSG_IDENTIFY_CWD size"); - c->cwd = imsg->fd; + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_IDENTIFY_CWD string"); + if ((c->cwd = open(data, O_RDONLY)) == -1) + c->cwd = open("/", O_RDONLY); break; case MSG_IDENTIFY_STDIN: if (datalen != 0) diff --git a/tmux.h b/tmux.h index 5bf9b39b..72495100 100644 --- a/tmux.h +++ b/tmux.h @@ -395,11 +395,12 @@ enum msgtype { MSG_IDENTIFY_FLAGS = 100, MSG_IDENTIFY_TERM, MSG_IDENTIFY_TTYNAME, - MSG_IDENTIFY_CWD, + MSG_IDENTIFY_OLDCWD, /* unused */ MSG_IDENTIFY_STDIN, MSG_IDENTIFY_ENVIRON, MSG_IDENTIFY_DONE, MSG_IDENTIFY_CLIENTPID, + MSG_IDENTIFY_CWD, MSG_COMMAND = 200, MSG_DETACH, From 8c8cddbe022af5ece9949804cacffbee6b8972fa Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 20 Oct 2015 14:19:27 +0000 Subject: [PATCH 345/703] The table could change when retrying so don't save it at start of server_client_handle_key. --- server-client.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server-client.c b/server-client.c index 52ced4d4..320389af 100644 --- a/server-client.c +++ b/server-client.c @@ -541,7 +541,7 @@ server_client_handle_key(struct client *c, int key) struct window *w; struct window_pane *wp; struct timeval tv; - struct key_table *table = c->keytable; + struct key_table *table; struct key_binding bd_find, *bd; int xtimeout; @@ -607,7 +607,7 @@ server_client_handle_key(struct client *c, int key) retry: /* Try to see if there is a key binding in the current table. */ bd_find.key = key; - bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); + bd = RB_FIND(key_bindings, &c->keytable->key_bindings, &bd_find); if (bd != NULL) { /* * Key was matched in this table. If currently repeating but a @@ -625,6 +625,7 @@ retry: * Take a reference to this table to make sure the key binding * doesn't disappear. */ + table = c->keytable; table->references++; /* From 076034345afe0dbfef3fa1a8116a64dc7d990b51 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 20 Oct 2015 21:12:08 +0000 Subject: [PATCH 346/703] Use client pointer not file descriptor in logging. --- cmd-queue.c | 4 ++-- cmd-wait-for.c | 5 ++--- server-client.c | 10 +++++----- server-fn.c | 2 +- status.c | 2 +- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index 1fe34dce..ff8c69cb 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -203,8 +203,8 @@ cmdq_continue(struct cmd_q *cmdq) cmdq->references++; notify_disable(); - log_debug("continuing cmdq %p: flags=%#x, client=%d", cmdq, cmdq->flags, - c != NULL ? c->ibuf.fd : -1); + log_debug("continuing cmdq %p: flags %#x, client %p", cmdq, cmdq->flags, + c); empty = TAILQ_EMPTY(&cmdq->queue); if (empty) diff --git a/cmd-wait-for.c b/cmd-wait-for.c index e38ea8f1..79e0b55e 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -170,12 +170,11 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, wc = cmd_wait_for_add(name); if (wc->woken) { - log_debug("wait channel %s already woken (client %d)", wc->name, - c->tty.fd); + log_debug("wait channel %s already woken (%p)", wc->name, c); cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); } - log_debug("wait channel %s not woken (client %d)", wc->name, c->tty.fd); + log_debug("wait channel %s not woken (%p)", wc->name, c); TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; diff --git a/server-client.c b/server-client.c index 320389af..41ecc141 100644 --- a/server-client.c +++ b/server-client.c @@ -131,7 +131,7 @@ server_client_create(int fd) evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); TAILQ_INSERT_TAIL(&clients, c, entry); - log_debug("new client %d", fd); + log_debug("new client %p", c); } /* Open client terminal if needed. */ @@ -172,7 +172,7 @@ server_client_lost(struct client *c) c->stdin_callback(c, 1, c->stdin_callback_data); TAILQ_REMOVE(&clients, c, entry); - log_debug("lost client %d", c->ibuf.fd); + log_debug("lost client %p", c); /* * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called @@ -238,7 +238,7 @@ server_client_lost(struct client *c) void server_client_unref(struct client *c) { - log_debug("unref client %d (%d references)", c->ibuf.fd, c->references); + log_debug("unref client %p (%d references)", c, c->references); c->references--; if (c->references == 0) @@ -251,7 +251,7 @@ server_client_free(unused int fd, unused short events, void *arg) { struct client *c = arg; - log_debug("free client %d (%d references)", c->ibuf.fd, c->references); + log_debug("free client %p (%d references)", c, c->references); if (c->references == 0) free(c); @@ -998,7 +998,7 @@ server_client_msg_dispatch(struct client *c) continue; } - log_debug("got %u from client %d", imsg.hdr.type, c->ibuf.fd); + log_debug("got %u from client %p", imsg.hdr.type, c); switch (imsg.hdr.type) { case MSG_IDENTIFY_FLAGS: case MSG_IDENTIFY_TERM: diff --git a/server-fn.c b/server-fn.c index 27908fde..a31a3772 100644 --- a/server-fn.c +++ b/server-fn.c @@ -64,7 +64,7 @@ server_write_client(struct client *c, enum msgtype type, const void *buf, if (c->flags & CLIENT_BAD) return (-1); - log_debug("writing %d to client %d", type, c->ibuf.fd); + log_debug("writing %d to client %p", type, c); error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, (void *) buf, len); if (error == 1) diff --git a/status.c b/status.c index 7a1d2818..e17c1f5d 100644 --- a/status.c +++ b/status.c @@ -164,7 +164,7 @@ status_timer_callback(unused int fd, unused short events, void *arg) if (tv.tv_sec != 0) evtimer_add(&c->status_timer, &tv); - log_debug("client %d, status interval %d", c->ibuf.fd, (int)tv.tv_sec); + log_debug("client %p, status interval %d", c, (int)tv.tv_sec); } /* Start status timer for client. */ From ddbc4a0f6ca0c6486a038ce91971212785d44744 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 21 Oct 2015 11:13:47 +0000 Subject: [PATCH 347/703] By popular demand add a default binding for mouse wheel up to scroll into history (if the mouse is, on of course). --- key-bindings.c | 1 + 1 file changed, 1 insertion(+) diff --git a/key-bindings.c b/key-bindings.c index 3dc3a054..cc1a1c6e 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -225,6 +225,7 @@ key_bindings_init(void) "bind -n MouseDown1Status select-window -t=", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n MouseDown3Pane select-pane -mt=", + "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -e\"'", }; u_int i; struct cmd_list *cmdlist; From 60ca29df64a96412ad6f78a294e0c68b01bed3d6 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 21 Oct 2015 13:14:36 +0000 Subject: [PATCH 348/703] client_key_table was missing. --- tmux.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.1 b/tmux.1 index 43dce715..1e85a898 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3357,6 +3357,7 @@ The following variables are available, where appropriate: .It Li "client_created_string" Ta "" Ta "String time client created" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_height" Ta "" Ta "Height of client" +.It Li "client_key_table" Ta "" Ta "Current key table" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" .It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" From 31fd071faa55638bfaa337d4b62c08e34a24c8f8 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 10:46:24 +0000 Subject: [PATCH 349/703] Rename shutdown to exit. --- server.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/server.c b/server.c index fd77ff38..2aed3dbb 100644 --- a/server.c +++ b/server.c @@ -44,7 +44,7 @@ struct clients clients; int server_fd; -int server_shutdown; +int server_exit; struct event server_ev_accept; struct session *marked_session; @@ -55,8 +55,8 @@ struct layout_cell *marked_layout_cell; int server_create_socket(void); void server_loop(void); -int server_should_shutdown(void); -void server_send_shutdown(void); +int server_should_exit(void); +void server_send_exit(void); void server_accept_callback(int, short, void *); void server_signal_callback(int, short, void *); void server_child_signal(void); @@ -232,7 +232,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) void server_loop(void) { - while (!server_should_shutdown()) { + while (!server_should_exit()) { log_debug("event dispatch enter"); event_loop(EVLOOP_ONCE); log_debug("event dispatch exit"); @@ -243,7 +243,7 @@ server_loop(void) /* Check if the server should exit (no more clients or sessions). */ int -server_should_shutdown(void) +server_should_exit(void) { struct client *c; @@ -268,9 +268,9 @@ server_should_shutdown(void) return (1); } -/* Shutdown the server by killing all clients and windows. */ +/* Exit the server by killing all clients and windows. */ void -server_send_shutdown(void) +server_send_exit(void) { struct client *c, *c1; struct session *s, *s1; @@ -281,7 +281,7 @@ server_send_shutdown(void) if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) server_client_lost(c); else - server_write_client(c, MSG_SHUTDOWN, NULL, 0); + server_write_client(c, MSG_EXIT, NULL, 0); c->session = NULL; } @@ -348,7 +348,7 @@ server_accept_callback(int fd, short events, unused void *data) } fatal("accept failed"); } - if (server_shutdown) { + if (server_exit) { close(newfd); return; } @@ -386,8 +386,8 @@ server_signal_callback(int sig, unused short events, unused void *data) switch (sig) { case SIGTERM: - server_shutdown = 1; - server_send_shutdown(); + server_exit = 1; + server_send_exit(); break; case SIGCHLD: server_child_signal(); From 515dfea4b79a09513d1ede5dbb8a89c3e18b8393 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 10:48:30 +0000 Subject: [PATCH 350/703] This should not be changed. --- server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.c b/server.c index 2aed3dbb..ff38a353 100644 --- a/server.c +++ b/server.c @@ -281,7 +281,7 @@ server_send_exit(void) if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) server_client_lost(c); else - server_write_client(c, MSG_EXIT, NULL, 0); + server_write_client(c, MSG_SHUTDOWN, NULL, 0); c->session = NULL; } From c2c2d44c72a6e693c22bb48d54b0b337a568b70b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 11:00:51 +0000 Subject: [PATCH 351/703] Log identify messages. --- server-client.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server-client.c b/server-client.c index 41ecc141..435e8b1a 100644 --- a/server-client.c +++ b/server-client.c @@ -1155,38 +1155,45 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) fatalx("bad MSG_IDENTIFY_FLAGS size"); memcpy(&flags, data, sizeof flags); c->flags |= flags; + log_debug("client %p IDENTIFY_FLAGS %#x", c, flags); break; case MSG_IDENTIFY_TERM: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); c->term = xstrdup(data); + log_debug("client %p IDENTIFY_TERM %s", c, data); break; case MSG_IDENTIFY_TTYNAME: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TTYNAME string"); c->ttyname = xstrdup(data); + log_debug("client %p IDENTIFY_TTYNAME %s", c, data); break; case MSG_IDENTIFY_CWD: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_CWD string"); if ((c->cwd = open(data, O_RDONLY)) == -1) c->cwd = open("/", O_RDONLY); + log_debug("client %p IDENTIFY_CWD %s", c, data); break; case MSG_IDENTIFY_STDIN: if (datalen != 0) fatalx("bad MSG_IDENTIFY_STDIN size"); c->fd = imsg->fd; + log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd); break; case MSG_IDENTIFY_ENVIRON: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) environ_put(&c->environ, data); + log_debug("client %p IDENTIFY_ENVIRON %s", c, data); break; case MSG_IDENTIFY_CLIENTPID: if (datalen != sizeof c->pid) fatalx("bad MSG_IDENTIFY_CLIENTPID size"); memcpy(&c->pid, data, sizeof c->pid); + log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid); break; default: break; From a05c27a7e1c4d43709817d6746a510f16c960b4b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 11:19:31 +0000 Subject: [PATCH 352/703] Unzoom before -LRUD, reported by Andy Weidenbaum. --- cmd-select-pane.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e76587cc..7986e98c 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -120,14 +120,19 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (args_has(self->args, 'L')) + if (args_has(self->args, 'L')) { + server_unzoom_window(wp->window); wp = window_pane_find_left(wp); - else if (args_has(self->args, 'R')) + } else if (args_has(self->args, 'R')) { + server_unzoom_window(wp->window); wp = window_pane_find_right(wp); - else if (args_has(self->args, 'U')) + } else if (args_has(self->args, 'U')) { + server_unzoom_window(wp->window); wp = window_pane_find_up(wp); - else if (args_has(self->args, 'D')) + } else if (args_has(self->args, 'D')) { + server_unzoom_window(wp->window); wp = window_pane_find_down(wp); + } if (wp == NULL) return (CMD_RETURN_NORMAL); From 3ebcf25149d75977ea97e9d4f786e0508d1a0d5e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 22 Oct 2015 11:23:00 +0000 Subject: [PATCH 353/703] If the pane is still on all_window_panes but not actually connected to window or session (which can happen if it is killed during a command sequence and something else has a reference), fall back to the best effort. Fixes "tmux killw\; detach" for Rudis Muiznieks. --- cmd-find.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index ec883cbd..376d9041 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -255,24 +255,35 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) wp = NULL; /* Not running in a pane. We know nothing. Find the best session. */ - if (wp == NULL) { - fs->s = cmd_find_best_session(NULL, 0, fs->flags); - if (fs->s == NULL) - return (-1); - fs->wl = fs->s->curw; - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - fs->wp = fs->w->active; - return (0); - } + if (wp == NULL) + goto unknown_pane; /* We now know the window and pane. */ fs->w = wp->window; fs->wp = wp; /* Find the best session and winlink. */ - if (cmd_find_best_session_with_window(fs) != 0) + if (cmd_find_best_session_with_window(fs) != 0) { + if (wp != NULL) { + /* + * The window may have been destroyed but the pane + * still on all_window_panes due to something else + * holding a reference. + */ + goto unknown_pane; + } return (-1); + } + return (0); + +unknown_pane: + fs->s = cmd_find_best_session(NULL, 0, fs->flags); + if (fs->s == NULL) + return (-1); + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; return (0); } From 63a3fd3c0fa41fec6cbc15dbd2bf08ceb3ccb208 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 15:52:54 +0000 Subject: [PATCH 354/703] Use tty_term_flag not _has for XT, and make -2 force direct use of 256-colour escape sequences (so setaf/setab can be bypassed if needed). --- tty.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tty.c b/tty.c index 7be952c8..5d23ee82 100644 --- a/tty.c +++ b/tty.c @@ -228,7 +228,7 @@ tty_start_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) { + if (tty_term_flag(tty->term, TTYC_XT)) { if (options_get_number(&global_options, "focus-events")) { tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); @@ -293,7 +293,7 @@ tty_stop_tty(struct tty *tty) if (tty_term_has(tty->term, TTYC_KMOUS)) tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); - if (tty_term_has(tty->term, TTYC_XT)) { + if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { tty->flags &= ~TTY_FOCUS; tty_raw(tty, "\033[?1004l"); @@ -1647,6 +1647,13 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) { char s[32]; + /* + * If the user has specified -2 to the client, setaf and setab may not + * work (or they may not want to use them), so send the usual sequence. + */ + if (tty->term_flags & TERM_256COLOURS) + goto fallback; + /* * If the terminfo entry has 256 colours and setaf and setab exist, * assume that they work correctly. @@ -1664,13 +1671,6 @@ tty_try_256(struct tty *tty, u_char colour, const char *type) return (0); } - /* - * If the user has specified -2 to the client, setaf and setab may not - * work, so send the usual sequence. - */ - if (tty->term_flags & TERM_256COLOURS) - goto fallback; - return (-1); fallback: From 14da99940806dd63c2180ad238886fcb6735db04 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 16:02:21 +0000 Subject: [PATCH 355/703] Format for scroll position, from Jorge Morante. --- format.c | 6 +++++- tmux.1 | 1 + tmux.h | 1 + window-copy.c | 10 ++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index 2fae8da0..1d80fbf7 100644 --- a/format.c +++ b/format.c @@ -1005,7 +1005,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { struct grid *gd = wp->base.grid; u_int idx; - int status; + int status, scroll_position; if (ft->w == NULL) ft->w = wp->window; @@ -1052,6 +1052,10 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); + scroll_position = window_copy_scroll_position(wp); + if (scroll_position != -1) + format_add(ft, "scroll_position", "%d", scroll_position); + format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); diff --git a/tmux.1 b/tmux.1 index 1e85a898..7744785a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3406,6 +3406,7 @@ The following variables are available, where appropriate: .It Li "pid" Ta "" Ta "Server PID" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" +.It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_activity" Ta "" Ta "Integer time of session last activity" diff --git a/tmux.h b/tmux.h index 72495100..528c22c7 100644 --- a/tmux.h +++ b/tmux.h @@ -2082,6 +2082,7 @@ void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_pageup(struct window_pane *); void window_copy_start_drag(struct client *, struct mouse_event *); +int window_copy_scroll_position(struct window_pane *); /* window-choose.c */ extern const struct window_mode window_choose_mode; diff --git a/window-copy.c b/window-copy.c index c7d360de..f81a2a16 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2219,6 +2219,16 @@ window_copy_scroll_down(struct window_pane *wp, u_int ny) screen_write_stop(&ctx); } +int +window_copy_scroll_position(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + if (wp->mode != &window_copy_mode) + return (-1); + return (data->oy); +} + void window_copy_rectangle_toggle(struct window_pane *wp) { From 5383b047d1dd0d71f724d8f9cac4e45994506d35 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 16:07:29 +0000 Subject: [PATCH 356/703] tmux can call pledge() in main with large set and then reduce it slightly in the server to "stdio rpath wpath cpath fattr unix recvfd proc exec tty ps". --- server.c | 4 ++++ tmux.c | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/server.c b/server.c index ff38a353..7ce1c99d 100644 --- a/server.c +++ b/server.c @@ -178,6 +178,10 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); + if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " + "ps", NULL) != 0) + fatal("pledge failed"); + /* * Must daemonise before loading configuration as the PID changes so * $TMUX would be wrong for sessions created in the config file. diff --git a/tmux.c b/tmux.c index 0b8e6a91..b2530285 100644 --- a/tmux.c +++ b/tmux.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -254,6 +255,10 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); + if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " + "proc exec tty ps", NULL) != 0) + err(1, "pledge"); + if (!(flags & CLIENT_UTF8)) { /* * If the user has set whichever of LC_ALL, LC_CTYPE or LANG From 1a4ddfa8a7c699e48980dd8fa70024ffe0b67f57 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 16:29:07 +0000 Subject: [PATCH 357/703] If $TMUX is set, and we are unsure about the session, use it. --- cmd-find.c | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index 376d9041..4173e202 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "tmux.h" @@ -51,6 +52,7 @@ struct cmd_find_state { int idx; }; +struct session *cmd_find_try_TMUX(struct client *, struct window *); int cmd_find_client_better(struct client *, struct client *); struct client *cmd_find_best_client(struct client **, u_int); int cmd_find_session_better(struct session *, struct session *, @@ -109,6 +111,33 @@ const char *cmd_find_pane_table[][2] = { { NULL, NULL } }; +/* Get session from TMUX if present. */ +struct session * +cmd_find_try_TMUX(struct client *c, struct window *w) +{ + struct environ_entry *envent; + char tmp[256]; + long long pid; + u_int session; + struct session *s; + + envent = environ_find(&c->environ, "TMUX"); + if (envent == NULL) + return (NULL); + + if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3) + return (NULL); + if (pid != getpid()) + return (NULL); + log_debug("client %d TMUX is %s (session @%u)", c->ibuf.fd, + envent->value, session); + + s = session_find_by_id(session); + if (s == NULL || (w != NULL && !session_has(s, w))) + return (NULL); + return (s); +} + /* Is this client better? */ int cmd_find_client_better(struct client *c, struct client *than) @@ -192,6 +221,12 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) u_int ssize; struct session *s; + if (fs->cmdq->client != NULL) { + fs->s = cmd_find_try_TMUX(fs->cmdq->client, fs->w); + if (fs->s != NULL) + return (cmd_find_best_winlink_with_window(fs)); + } + ssize = 0; RB_FOREACH(s, sessions, &sessions) { if (!session_has(s, fs->w)) @@ -277,7 +312,9 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) return (0); unknown_pane: - fs->s = cmd_find_best_session(NULL, 0, fs->flags); + fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL); + if (fs->s == NULL) + fs->s = cmd_find_best_session(NULL, 0, fs->flags); if (fs->s == NULL) return (-1); fs->wl = fs->s->curw; From 26a55ddcf99b9631af28d8273dac2bafa968adff Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 16:30:15 +0000 Subject: [PATCH 358/703] Remove some unnecessary checks before free(). --- cmd-new-session.c | 2 +- cmd-new-window.c | 2 +- cmd-split-window.c | 2 +- tty.c | 6 ++---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index 7687398e..b3bf3c74 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -133,7 +133,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) strerror(errno)); return (CMD_RETURN_ERROR); } - } else if (cp != NULL) + } else free(cp); cwd = fd; } else if (c != NULL && c->session == NULL) diff --git a/cmd-new-window.c b/cmd-new-window.c index 9cead449..893fe6e2 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -107,7 +107,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) strerror(errno)); return (CMD_RETURN_ERROR); } - } else if (cp != NULL) + } else free(cp); cwd = fd; } else if (cmdq->client != NULL && cmdq->client->session == NULL) diff --git a/cmd-split-window.c b/cmd-split-window.c index 4e85a0f3..f397113f 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -101,7 +101,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) strerror(errno)); return (CMD_RETURN_ERROR); } - } else if (cp != NULL) + } else free(cp); cwd = fd; } else if (cmdq->client != NULL && cmdq->client->session == NULL) diff --git a/tty.c b/tty.c index 5d23ee82..3d6f29ca 100644 --- a/tty.c +++ b/tty.c @@ -338,10 +338,8 @@ tty_free(struct tty *tty) tty_close(tty); free(tty->ccolour); - if (tty->path != NULL) - free(tty->path); - if (tty->termname != NULL) - free(tty->termname); + free(tty->path); + free(tty->termname); } void From 2e2b8a95bd13dab848a61113c9974c9ac936f9d6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 23 Oct 2015 23:46:36 +0000 Subject: [PATCH 359/703] Pasting mouse escape sequences is unlikely, so skip them when working out whether the user is pasting. --- server-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 435e8b1a..dbff9212 100644 --- a/server-client.c +++ b/server-client.c @@ -598,7 +598,7 @@ server_client_handle_key(struct client *c, int key) m->valid = 0; /* Treat everything as a regular key when pasting is detected. */ - if (server_client_assume_paste(s)) { + if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) { if (!(c->flags & CLIENT_READONLY)) window_pane_key(wp, c, s, key, m); return; From 3034a71488ce5a76105b5777848dd8578a7aa376 Mon Sep 17 00:00:00 2001 From: deraadt Date: Sun, 25 Oct 2015 07:48:16 +0000 Subject: [PATCH 360/703] Let's see if anyone screams about not being able to specify $TMPDIR for their tmux sockets. (Over the years, I have seen $TMPDIR set up worse than /tmp many times, and don't know how this practice infected other parts of the system. Nothing uses tmpdir(3), nor a huge-temporary-file program like sort.) ok nicm --- tmux.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tmux.c b/tmux.c index b2530285..8b3d0bfe 100644 --- a/tmux.c +++ b/tmux.c @@ -128,8 +128,6 @@ makesocketpath(const char *label) uid = getuid(); if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); - else if ((s = getenv("TMPDIR")) != NULL && *s != '\0') - xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); else xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); From 3faa51a0caebb56746988ae7f5e9e9f649dc42b6 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 25 Oct 2015 08:59:26 +0000 Subject: [PATCH 361/703] Pass output from jobs through format_expand() so they are expanded again (this was the previous behaviour). --- format.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/format.c b/format.c index 1d80fbf7..f5f036c2 100644 --- a/format.c +++ b/format.c @@ -39,7 +39,7 @@ struct format_entry; typedef void (*format_cb)(struct format_tree *, struct format_entry *); void format_job_callback(struct job *); -const char *format_job_get(struct format_tree *, const char *); +char *format_job_get(struct format_tree *, const char *); void format_job_timer(int, short, void *); void format_cb_host(struct format_tree *, struct format_entry *); @@ -212,7 +212,7 @@ format_job_callback(struct job *job) } /* Find a job. */ -const char * +char * format_job_get(struct format_tree *ft, const char *cmd) { struct format_job fj0, *fj; @@ -242,7 +242,7 @@ format_job_get(struct format_tree *ft, const char *cmd) if (ft->flags & FORMAT_STATUS) fj->status = 1; - return (fj->out); + return (format_expand(ft, fj->out)); } /* Remove old jobs. */ @@ -709,9 +709,9 @@ format_expand_time(struct format_tree *ft, const char *fmt, time_t t) char * format_expand(struct format_tree *ft, const char *fmt) { - char *buf, *tmp, *cmd; + char *buf, *tmp, *cmd, *out; const char *ptr, *s, *saved = fmt; - size_t off, len, n, slen; + size_t off, len, n, outlen; int ch, brackets; if (fmt == NULL) @@ -751,18 +751,20 @@ format_expand(struct format_tree *ft, const char *fmt) tmp[n] = '\0'; cmd = format_expand(ft, tmp); - s = format_job_get(ft, cmd); - slen = strlen(s); + out = format_job_get(ft, cmd); + outlen = strlen(out); free(cmd); free(tmp); - while (len - off < slen + 1) { + while (len - off < outlen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; } - memcpy(buf + off, s, slen); - off += slen; + memcpy(buf + off, out, outlen); + off += outlen; + + free(out); fmt += n + 1; continue; From 91f53d590bba4282754a36632e04ddc97170f7ae Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 25 Oct 2015 09:31:07 +0000 Subject: [PATCH 362/703] __OpenBSD__ around pledge() --- server.c | 3 ++- tmux.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server.c b/server.c index 139505d4..7dcad380 100644 --- a/server.c +++ b/server.c @@ -177,10 +177,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); +#ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); - +#endif /* * Must daemonise before loading configuration as the PID changes so * $TMUX would be wrong for sessions created in the config file. diff --git a/tmux.c b/tmux.c index b8373ab7..18da1abe 100644 --- a/tmux.c +++ b/tmux.c @@ -259,9 +259,11 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); +#ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " "proc exec tty ps", NULL) != 0) err(1, "pledge"); +#endif if (!(flags & CLIENT_UTF8)) { /* From 7930cb54c0cd6105899ba51be767697e1210aec2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 25 Oct 2015 09:31:46 +0000 Subject: [PATCH 363/703] ifdef __OpenBSD__ around pledge(). --- server.c | 2 ++ tmux.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/server.c b/server.c index 139505d4..d3be5cc1 100644 --- a/server.c +++ b/server.c @@ -177,9 +177,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); +#ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); +#endif /* * Must daemonise before loading configuration as the PID changes so diff --git a/tmux.c b/tmux.c index b8373ab7..18da1abe 100644 --- a/tmux.c +++ b/tmux.c @@ -259,9 +259,11 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); +#ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " "proc exec tty ps", NULL) != 0) err(1, "pledge"); +#endif if (!(flags & CLIENT_UTF8)) { /* From ad437f13d55b62d9a635fb85ec67c25c10ab7d22 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 25 Oct 2015 09:34:56 +0000 Subject: [PATCH 364/703] Add missing headers for getpid() --- cmd-find.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-find.c b/cmd-find.c index 3d129875..ad03445d 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "tmux.h" From c14fb5b633e63cc5f20d1f67fe071e4d4404e48e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 25 Oct 2015 09:38:08 +0000 Subject: [PATCH 365/703] -sys/types.h --- cmd-find.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index ad03445d..01aaeb1f 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "tmux.h" From e65306d8e7bd6db99bd0746cd16a21d2c066b8db Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 25 Oct 2015 22:29:17 +0000 Subject: [PATCH 366/703] Extend the modifiers allowed before formats: as well as the existing #{=10:...} length limit, add #{t:...} to convert a time_t format to a string, #{b:...} for basename and #{d:...} for dirname. Remove all the foo_string time formats as they can now be replaced by "t:", for example #{window_activity_string} becomes #{t:window_activity}. --- cmd-choose-client.c | 2 +- cmd-list-sessions.c | 2 +- format.c | 243 +++++++++++++++++++++++++++----------------- tmux.1 | 26 +++-- tmux.h | 1 - 5 files changed, 168 insertions(+), 106 deletions(-) diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 93ac28a8..93a141ee 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -31,7 +31,7 @@ "#{client_tty}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \ - "(last used #{client_activity_string})" + "(last used #{t:client_activity})" enum cmd_retval cmd_choose_client_exec(struct cmd *, struct cmd_q *); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 8ad55d03..49ef9467 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -30,7 +30,7 @@ #define LIST_SESSIONS_TEMPLATE \ "#{session_name}: #{session_windows} windows " \ - "(created #{session_created_string}) " \ + "(created #{t:session_created}) " \ "[#{session_width}x#{session_height}]" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ diff --git a/format.c b/format.c index f5f036c2..7ff11b59 100644 --- a/format.c +++ b/format.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -52,7 +53,9 @@ void format_cb_current_command(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); void format_cb_pane_tabs(struct format_tree *, struct format_entry *); +char *format_find(struct format_tree *, const char *, int); void format_add_cb(struct format_tree *, const char *, format_cb); +void format_add_tv(struct format_tree *, const char *, struct timeval *); int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); char *format_time_string(time_t); @@ -90,10 +93,16 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) return (strcmp(fj1->cmd, fj2->cmd)); } +/* Format modifiers. */ +#define FORMAT_TIMESTRING 0x1 +#define FORMAT_BASENAME 0x2 +#define FORMAT_DIRNAME 0x4 + /* Entry in format tree. */ struct format_entry { char *key; char *value; + time_t t; format_cb cb; RB_ENTRY(format_entry) entry; }; @@ -503,12 +512,37 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...) } fe->cb = NULL; + fe->t = 0; va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); } +/* Add a key and time. */ +void +format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) +{ + struct format_entry *fe; + struct format_entry *fe_now; + + fe = xmalloc(sizeof *fe); + fe->key = xstrdup(key); + + fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); + if (fe_now != NULL) { + free(fe->key); + free(fe); + free(fe_now->value); + fe = fe_now; + } + + fe->cb = NULL; + fe->t = tv->tv_sec; + + fe->value = NULL; +} + /* Add a key and function. */ void format_add_cb(struct format_tree *ft, const char *key, format_cb cb) @@ -528,57 +562,99 @@ format_add_cb(struct format_tree *ft, const char *key, format_cb cb) } fe->cb = cb; + fe->t = 0; fe->value = NULL; } /* Find a format entry. */ -const char * -format_find(struct format_tree *ft, const char *key) +char * +format_find(struct format_tree *ft, const char *key, int modifiers) { struct format_entry *fe, fe_find; struct options_entry *o; struct environ_entry *envent; - static char s[16]; + static char s[64]; + const char *found; + char *copy, *saved; - o = options_find(&global_options, key); - if (o == NULL && ft->w != NULL) - o = options_find(&ft->w->options, key); - if (o == NULL) - o = options_find(&global_w_options, key); - if (o == NULL && ft->s != NULL) - o = options_find(&ft->s->options, key); - if (o == NULL) - o = options_find(&global_s_options, key); - if (o != NULL) { - switch (o->type) { - case OPTIONS_STRING: - return (o->str); - case OPTIONS_NUMBER: - xsnprintf(s, sizeof s, "%lld", o->num); - return (s); - case OPTIONS_STYLE: - return (style_tostring(&o->style)); + found = NULL; + + if (~modifiers & FORMAT_TIMESTRING) { + o = options_find(&global_options, key); + if (o == NULL && ft->w != NULL) + o = options_find(&ft->w->options, key); + if (o == NULL) + o = options_find(&global_w_options, key); + if (o == NULL && ft->s != NULL) + o = options_find(&ft->s->options, key); + if (o == NULL) + o = options_find(&global_s_options, key); + if (o != NULL) { + switch (o->type) { + case OPTIONS_STRING: + found = o->str; + goto found; + case OPTIONS_NUMBER: + xsnprintf(s, sizeof s, "%lld", o->num); + found = s; + goto found; + case OPTIONS_STYLE: + found = style_tostring(&o->style); + goto found; + } } } fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { + if (modifiers & FORMAT_TIMESTRING) { + if (fe->t == 0) + return (NULL); + ctime_r(&fe->t, s); + s[strcspn(s, "\n")] = '\0'; + found = s; + goto found; + } + if (fe->t != 0) { + xsnprintf(s, sizeof s, "%lld", (long long)fe->t); + found = s; + goto found; + } if (fe->value == NULL && fe->cb != NULL) fe->cb(ft, fe); - return (fe->value); + found = fe->value; + goto found; } - envent = NULL; - if (ft->s != NULL) - envent = environ_find(&ft->s->environ, key); - if (envent == NULL) - envent = environ_find(&global_environ, key); - if (envent != NULL) - return (envent->value); + if (~modifiers & FORMAT_TIMESTRING) { + envent = NULL; + if (ft->s != NULL) + envent = environ_find(&ft->s->environ, key); + if (envent == NULL) + envent = environ_find(&global_environ, key); + if (envent != NULL) { + found = envent->value; + goto found; + } + } return (NULL); + +found: + copy = xstrdup(found); + if (modifiers & FORMAT_BASENAME) { + saved = copy; + copy = xstrdup(basename(saved)); + free(saved); + } + if (modifiers & FORMAT_DIRNAME) { + saved = copy; + copy = xstrdup(dirname(saved)); + free(saved); + } + return (copy); } /* @@ -589,10 +665,10 @@ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - char *copy, *copy0, *endptr, *ptr, *saved, *trimmed; - const char *value; + char *copy, *copy0, *endptr, *ptr, *saved, *trimmed, *value; size_t valuelen; u_long limit = 0; + int modifiers = 0; /* Make a copy of the key. */ copy0 = copy = xmalloc(keylen + 1); @@ -600,24 +676,34 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, copy[keylen] = '\0'; /* Is there a length limit or whatnot? */ - if (!isalpha((u_char) *copy) && *copy != '@' && *copy != '?') { - while (*copy != ':' && *copy != '\0') { - switch (*copy) { - case '=': - errno = 0; - limit = strtoul(copy + 1, &endptr, 10); - if (errno == ERANGE && limit == ULONG_MAX) - goto fail; - copy = endptr; - break; - default: - copy++; - break; - } - } - if (*copy != ':') - goto fail; - copy++; + switch (copy[0]) { + case '=': + errno = 0; + limit = strtoul(copy + 1, &endptr, 10); + if (errno == ERANGE && limit == ULONG_MAX) + break; + if (*endptr != ':') + break; + copy = endptr + 1; + break; + case 'b': + if (copy[1] != ':') + break; + modifiers |= FORMAT_BASENAME; + copy += 2; + break; + case 'd': + if (copy[1] != ':') + break; + modifiers |= FORMAT_DIRNAME; + copy += 2; + break; + case 't': + if (copy[1] != ':') + break; + modifiers |= FORMAT_TIMESTRING; + copy += 2; + break; } /* @@ -630,7 +716,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; *ptr = '\0'; - value = format_find(ft, copy + 1); + value = saved = format_find(ft, copy + 1, modifiers); if (value != NULL && *value != '\0' && (value[0] != '0' || value[1] != '\0')) { value = ptr + 1; @@ -644,13 +730,13 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; value = ptr + 1; } - saved = format_expand(ft, value); - value = saved; + value = format_expand(ft, value); + free(saved); + saved = value; } else { - value = format_find(ft, copy); + saved = value = format_find(ft, copy, modifiers); if (value == NULL) - value = ""; - saved = NULL; + saved = value = xstrdup(""); } /* Truncate the value if needed. */ @@ -820,18 +906,6 @@ format_expand(struct format_tree *ft, const char *fmt) return (buf); } -/* Get time as a string. */ -char * -format_time_string(time_t t) -{ - char *tim; - - tim = ctime(&t); - *strchr(tim, '\n') = '\0'; - - return (tim); -} - /* Set defaults for any of arguments that are not NULL. */ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, @@ -859,7 +933,6 @@ void format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; - time_t t; ft->s = s; @@ -874,20 +947,9 @@ format_defaults_session(struct format_tree *ft, struct session *s) if (sg != NULL) format_add(ft, "session_group", "%u", session_group_index(sg)); - t = s->creation_time.tv_sec; - format_add(ft, "session_created", "%lld", (long long) t); - format_add(ft, "session_created_string", "%s", format_time_string(t)); - - t = s->last_attached_time.tv_sec; - if (t != 0) { /* zero if never attached */ - format_add(ft, "session_last_attached", "%lld", (long long) t); - format_add(ft, "session_last_attached_string", "%s", - format_time_string(t)); - } - - t = s->activity_time.tv_sec; - format_add(ft, "session_activity", "%lld", (long long) t); - format_add(ft, "session_activity_string", "%s", format_time_string(t)); + format_add_tv(ft, "session_created", &s->creation_time); + format_add_tv(ft, "session_last_attached", &s->last_attached_time); + format_add_tv(ft, "session_activity", &s->activity_time); format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); @@ -900,7 +962,6 @@ void format_defaults_client(struct format_tree *ft, struct client *c) { struct session *s; - time_t t; if (ft->s == NULL) ft->s = c->session; @@ -915,13 +976,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); - t = c->creation_time.tv_sec; - format_add(ft, "client_created", "%lld", (long long) t); - format_add(ft, "client_created_string", "%s", format_time_string(t)); - - t = c->activity_time.tv_sec; - format_add(ft, "client_activity", "%lld", (long long) t); - format_add(ft, "client_activity_string", "%s", format_time_string(t)); + format_add_tv(ft, "client_created", &c->creation_time); + format_add_tv(ft, "client_activity", &c->activity_time); if (strcmp(c->keytable->name, "root") == 0) format_add(ft, "client_prefix", "%d", 0); @@ -951,14 +1007,9 @@ format_defaults_client(struct format_tree *ft, struct client *c) void format_defaults_window(struct format_tree *ft, struct window *w) { - time_t t; - ft->w = w; - t = w->activity_time.tv_sec; - format_add(ft, "window_activity", "%lld", (long long) t); - format_add(ft, "window_activity_string", "%s", format_time_string(t)); - + format_add_tv(ft, "window_activity", &w->activity_time); format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); diff --git a/tmux.1 b/tmux.1 index 7744785a..39172f34 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3319,12 +3319,31 @@ if is enabled, or .Ql no if not. +.Pp A limit may be placed on the length of the resultant string by prefixing it by an .Ql = , a number and a colon, so .Ql #{=10:pane_title} will include at most the first 10 characters of the pane title. +Prefixing a time variable with +.Ql t: +will convert it to a string, so if +.Ql #{window_activity} +gives +.Ql 1445765102, +.Ql #{t:window_activity} +gives +.Ql Sun Oct 25 09:25:02 2015 . +The +.Ql b: +and +.Ql d: +prefixes are +.Xr basename 3 +and +.Xr dirname 3 +of the variable respectively. .Pp In addition, the first line of a shell command's output may be inserted using .Ql #() . @@ -3352,9 +3371,7 @@ The following variables are available, where appropriate: .It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" .It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" .It Li "client_activity" Ta "" Ta "Integer time client last had activity" -.It Li "client_activity_string" Ta "" Ta "String time client last had activity" .It Li "client_created" Ta "" Ta "Integer time client created" -.It Li "client_created_string" Ta "" Ta "String time client created" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_key_table" Ta "" Ta "Current key table" @@ -3410,11 +3427,8 @@ The following variables are available, where appropriate: .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_activity" Ta "" Ta "Integer time of session last activity" -.It Li "session_activity_string" Ta "" Ta "String time of session last activity" .It Li "session_created" Ta "" Ta "Integer time session created" -.It Li "session_created_string" Ta "" Ta "String time session created" .It Li "session_last_attached" Ta "" Ta "Integer time session last attached" -.It Li "session_last_attached_string" Ta "" Ta "String time session last attached" .It Li "session_group" Ta "" Ta "Number of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_height" Ta "" Ta "Height of session" @@ -3424,9 +3438,7 @@ The following variables are available, where appropriate: .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "window_activity" Ta "" Ta "Integer time of window last activity" -.It Li "window_activity_string" Ta "" Ta "String time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" -.It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" .It Li "window_find_matches" Ta "" Ta "Matched data from the find-window" .It Li "window_flags" Ta "#F" Ta "Window flags" diff --git a/tmux.h b/tmux.h index 528c22c7..97238865 100644 --- a/tmux.h +++ b/tmux.h @@ -1453,7 +1453,6 @@ struct format_tree *format_create_flags(int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); -const char *format_find(struct format_tree *, const char *); char *format_expand_time(struct format_tree *, const char *, time_t); char *format_expand(struct format_tree *, const char *); void format_defaults(struct format_tree *, struct client *, From c582f7d177ed14d7d1bbe5ecdd76d9732910fbbf Mon Sep 17 00:00:00 2001 From: jmc Date: Mon, 26 Oct 2015 00:15:37 +0000 Subject: [PATCH 367/703] space before punctuation; --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 39172f34..a9f3cdc9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3331,7 +3331,7 @@ Prefixing a time variable with will convert it to a string, so if .Ql #{window_activity} gives -.Ql 1445765102, +.Ql 1445765102 , .Ql #{t:window_activity} gives .Ql Sun Oct 25 09:25:02 2015 . From a22fe33aa00d4e29e8d71b58b8f728d83e5d23f3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2015 17:17:06 +0000 Subject: [PATCH 368/703] Some extra logging of where keys are actually going. --- input-keys.c | 5 +++-- server-client.c | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index d812a906..6e49dd3b 100644 --- a/input-keys.c +++ b/input-keys.c @@ -145,7 +145,8 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) char *out; u_char ch; - log_debug("writing key 0x%x (%s)", key, key_string_lookup_key(key)); + log_debug("writing key 0x%x (%s) to %%%u", key, + key_string_lookup_key(key), wp->id); /* If this is a mouse key, pass off to mouse function. */ if (KEYC_IS_MOUSE(key)) { @@ -251,6 +252,6 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) buf[len++] = x + 33; buf[len++] = y + 33; } - log_debug("writing mouse %.*s", (int)len, buf); + log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); bufferevent_write(wp->event, buf, len); } diff --git a/server-client.c b/server-client.c index dbff9212..24f0250a 100644 --- a/server-client.c +++ b/server-client.c @@ -369,6 +369,8 @@ server_client_check_mouse(struct client *c) wp = window_get_active_at(s->curw->window, x, y); if (wp != NULL) where = PANE; + log_debug("mouse at %u,%u is on pane %%%u", x, y, + wp->id); } if (where == NOWHERE) return (KEYC_NONE); From b85be36d1cb96e0328b3f1dc9388288989d3b8e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2015 22:03:04 +0000 Subject: [PATCH 369/703] Handle unknown keys more gracefully, return a string instead of NULL. --- cmd-list-keys.c | 8 -------- key-string.c | 6 ++++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index e2d5dc52..d26486bd 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -77,8 +77,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) continue; RB_FOREACH(bd, key_bindings, &table->key_bindings) { key = key_string_lookup_key(bd->key); - if (key == NULL) - continue; if (bd->can_repeat) repeat = 1; @@ -97,8 +95,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) continue; RB_FOREACH(bd, key_bindings, &table->key_bindings) { key = key_string_lookup_key(bd->key); - if (key == NULL) - continue; if (!repeat) r = ""; @@ -140,8 +136,6 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) any_mode = 0; RB_FOREACH(mbind, mode_key_tree, mtab->tree) { key = key_string_lookup_key(mbind->key); - if (key == NULL) - continue; if (mbind->mode != 0) any_mode = 1; @@ -153,8 +147,6 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) RB_FOREACH(mbind, mode_key_tree, mtab->tree) { key = key_string_lookup_key(mbind->key); - if (key == NULL) - continue; mode = ""; if (mbind->mode != 0) diff --git a/key-string.c b/key-string.c index b6474c4f..88cd2a32 100644 --- a/key-string.c +++ b/key-string.c @@ -238,8 +238,10 @@ key_string_lookup_key(int key) } /* Invalid keys are errors. */ - if (key == 127 || key > 255) - return (NULL); + if (key == 127 || key > 255) { + snprintf(out, sizeof out, "", key); + return (out); + } /* Check for standard or control key. */ if (key >= 0 && key <= 32) { From 380a1ea8ef8c4d47eda53214f0e283bdad857a6d Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2015 23:06:18 +0000 Subject: [PATCH 370/703] Default bindings for mouse wheel on status line to change window (like we had before), from Patrick Palka. --- key-bindings.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/key-bindings.c b/key-bindings.c index cc1a1c6e..2d739e84 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -223,6 +223,8 @@ key_bindings_init(void) "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", + "bind -n WheelDownStatus next-window", + "bind -n WheelUpStatus previous-window", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n MouseDown3Pane select-pane -mt=", "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -e\"'", From 640c6fdd5fd859cffecf461647440a437d25f879 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 26 Oct 2015 23:16:18 +0000 Subject: [PATCH 371/703] If a mouse event has no key binding, pass it through to the pane it happened in, not the active pane like normal key presses. Fixes problems seen by Enrico Ghirardi. --- server-client.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/server-client.c b/server-client.c index 24f0250a..0e657ae8 100644 --- a/server-client.c +++ b/server-client.c @@ -551,7 +551,6 @@ server_client_handle_key(struct client *c, int key) if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; w = s->curw->window; - wp = w->active; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) @@ -592,19 +591,14 @@ server_client_handle_key(struct client *c, int key) m->valid = 1; m->key = key; - if (!options_get_number(&s->options, "mouse")) { - window_pane_key(wp, c, s, key, m); - return; - } + if (!options_get_number(&s->options, "mouse")) + goto forward; } else m->valid = 0; /* Treat everything as a regular key when pasting is detected. */ - if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) { - if (!(c->flags & CLIENT_READONLY)) - window_pane_key(wp, c, s, key, m); - return; - } + if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) + goto forward; retry: /* Try to see if there is a key binding in the current table. */ @@ -680,7 +674,17 @@ retry: key == options_get_number(&s->options, "prefix2")) { server_client_key_table(c, "prefix"); server_status_client(c); - } else if (!(c->flags & CLIENT_READONLY)) + return; + } + +forward: + if (c->flags & CLIENT_READONLY) + return; + if (KEYC_IS_MOUSE(key)) + wp = cmd_mouse_pane(m, NULL, NULL); + else + wp = w->active; + if (wp != NULL) window_pane_key(wp, c, s, key, m); } From 3fc001d0a2e2456fb266f689964fe116d58c2227 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 09:15:21 +0000 Subject: [PATCH 372/703] Use copy-mode -et= in WheelUpPane binding, from Patrick Palka. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index 2d739e84..e12dd62c 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -227,7 +227,7 @@ key_bindings_init(void) "bind -n WheelUpStatus previous-window", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n MouseDown3Pane select-pane -mt=", - "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -e\"'", + "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", }; u_int i; struct cmd_list *cmdlist; From 17c2c4219df2a8fec4c2f46f718a3fbbbfebe50a Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 09:18:06 +0000 Subject: [PATCH 373/703] The format callback may not always succeed, so we need to check for NULL. From Patrick Palka. --- format.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/format.c b/format.c index 7ff11b59..0d7d7c20 100644 --- a/format.c +++ b/format.c @@ -643,6 +643,8 @@ format_find(struct format_tree *ft, const char *key, int modifiers) return (NULL); found: + if (found == NULL) + return (NULL); copy = xstrdup(found); if (modifiers & FORMAT_BASENAME) { saved = copy; From 9952201ca72b42819d64a9174fa7b5b898215668 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 09:28:31 +0000 Subject: [PATCH 374/703] Count brackets in #{?...} so that nested conditional formats work, from Daniel De Graaf. --- format.c | 34 ++++++++++++++++++++-------------- screen.c | 2 -- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/format.c b/format.c index 0d7d7c20..285bfadb 100644 --- a/format.c +++ b/format.c @@ -670,7 +670,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, char *copy, *copy0, *endptr, *ptr, *saved, *trimmed, *value; size_t valuelen; u_long limit = 0; - int modifiers = 0; + int modifiers = 0, brackets; /* Make a copy of the key. */ copy0 = copy = xmalloc(keylen + 1); @@ -718,20 +718,26 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, goto fail; *ptr = '\0'; - value = saved = format_find(ft, copy + 1, modifiers); - if (value != NULL && *value != '\0' && - (value[0] != '0' || value[1] != '\0')) { - value = ptr + 1; - ptr = strchr(value, ','); - if (ptr == NULL) - goto fail; - *ptr = '\0'; - } else { - ptr = strchr(ptr + 1, ','); - if (ptr == NULL) - goto fail; - value = ptr + 1; + value = ptr + 1; + saved = format_find(ft, copy + 1, modifiers); + + brackets = 0; + for (ptr = ptr + 1; *ptr != '\0'; ptr++) { + if (*ptr == '{') + brackets++; + if (*ptr == '}') + brackets--; + if (*ptr == ',' && brackets == 0) + break; } + if (*ptr == '\0') + goto fail; + + if (saved != NULL && *saved != '\0' && + (saved[0] != '0' || saved[1] != '\0')) { + *ptr = '\0'; + } else + value = ptr + 1; value = format_expand(ft, value); free(saved); saved = value; diff --git a/screen.c b/screen.c index f487cc67..5551bb93 100644 --- a/screen.c +++ b/screen.c @@ -194,8 +194,6 @@ screen_resize_y(struct screen *s, u_int sy) * Now just increase the history size, if possible, to take * over the lines which are left. If history is off, delete * lines from the top. - * - * XXX Should apply history limit? */ available = s->cy; if (gd->flags & GRID_HISTORY) From 07b0ea03c33893bd2b104db5ea4e1397f92e0477 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 13:23:24 +0000 Subject: [PATCH 375/703] Break the common process set up, event loop and imsg dispatch code between server and client out into a separate internal API. This will make it easier to add another process. --- Makefile | 1 + client.c | 509 ++++++++++++++++++------------------------- cmd-attach-session.c | 18 +- cmd-detach-client.c | 13 +- cmd-find.c | 14 +- cmd-new-session.c | 7 +- proc.c | 249 +++++++++++++++++++++ server-client.c | 266 ++++++++++------------ server-fn.c | 64 +----- server.c | 72 ++---- signal.c | 14 +- tmux.c | 4 +- tmux.h | 31 ++- 13 files changed, 648 insertions(+), 614 deletions(-) create mode 100644 proc.c diff --git a/Makefile b/Makefile index 653c8d85..89c8c5c8 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,7 @@ SRCS= alerts.c \ options-table.c \ options.c \ paste.c \ + proc.c \ procname.c \ resize.c \ screen-redraw.c \ diff --git a/client.c b/client.c index 73d871bc..606feefe 100644 --- a/client.c +++ b/client.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -33,10 +34,10 @@ #include "tmux.h" -int client_flags; -struct imsgbuf client_ibuf; -struct event client_event; -struct event client_stdin; +struct tmuxproc *client_proc; +struct tmuxpeer *client_peer; +int client_flags; +struct event client_stdin; enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, @@ -47,24 +48,21 @@ enum { CLIENT_EXIT_EXITED, CLIENT_EXIT_SERVER_EXITED, } client_exitreason = CLIENT_EXIT_NONE; -int client_exitval; -enum msgtype client_exittype; -const char *client_exitsession; -int client_attached; +int client_exitval; +enum msgtype client_exittype; +const char *client_exitsession; +int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); void client_send_identify(const char *, const char *); -int client_write_one(enum msgtype, int, const void *, size_t); -int client_write_server(enum msgtype, const void *, size_t); -void client_update_event(void); -void client_signal(int, short, void *); void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); -void client_callback(int, short, void *); -int client_dispatch_attached(void); -int client_dispatch_wait(void); +void client_signal(int); +void client_dispatch(struct imsg *, void *); +void client_dispatch_attached(struct imsg *); +void client_dispatch_wait(struct imsg *); const char *client_exit_message(void); /* @@ -222,6 +220,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct termios tio, saved_tio; size_t size; + /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ + signal(SIGCHLD, SIG_IGN); + /* Save the flags. */ client_flags = flags; @@ -254,13 +255,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags) cmd_list_free(cmdlist); } - /* Set process title, log and signals now this is the client. */ - setproctitle("client (%s)", socket_path); - logfile("client"); - - /* Establish signal handlers. */ - set_signals(client_signal); - /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { @@ -274,6 +268,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags) return (1); } + /* Build process state. */ + client_proc = proc_start("client", base, 0, client_signal); + client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); + /* Save these before pledge(). */ if ((cwd = getcwd(path, sizeof path)) == NULL) cwd = "/"; @@ -298,10 +296,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags) options_free(&global_w_options); environ_free(&global_environ); - /* Create imsg. */ - imsg_init(&client_ibuf, fd); - event_set(&client_event, fd, EV_READ, client_callback, NULL); - /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, @@ -345,18 +339,17 @@ client_main(struct event_base *base, int argc, char **argv, int flags) size += sizeof *data; /* Send the command. */ - if (client_write_server(msg, data, size) != 0) { + if (proc_send(client_peer, msg, -1, data, size) != 0) { fprintf(stderr, "failed to send command\n"); free(data); return (1); } free(data); } else if (msg == MSG_SHELL) - client_write_server(msg, NULL, 0); + proc_send(client_peer, msg, -1, NULL, 0); - /* Set the event and dispatch. */ - client_update_event(); - event_dispatch(); + /* Start main loop. */ + proc_loop(client_proc, NULL); /* Print the exit message, if any, and exit. */ if (client_attached) { @@ -388,144 +381,29 @@ client_send_identify(const char *ttynam, const char *cwd) int fd, flags = client_flags; pid_t pid; - client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); + proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); if ((s = getenv("TERM")) == NULL) s = ""; - client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); + proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - client_write_one(MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); - client_write_one(MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); + proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); + proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); - client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); pid = getpid(); - client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); + proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); for (ss = environ; *ss != NULL; ss++) { sslen = strlen(*ss) + 1; if (sslen <= MAX_IMSGSIZE - IMSG_HEADER_SIZE) - client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); + proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } - client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); -} - -/* Helper to send one message. */ -int -client_write_one(enum msgtype type, int fd, const void *buf, size_t len) -{ - int retval; - - retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd, - (void *)buf, len); - if (retval != 1) - return (-1); - return (0); -} - -/* Write a message to the server without a file descriptor. */ -int -client_write_server(enum msgtype type, const void *buf, size_t len) -{ - int retval; - - retval = client_write_one(type, -1, buf, len); - if (retval == 0) - client_update_event(); - return (retval); -} - -/* Update client event based on whether it needs to read or read and write. */ -void -client_update_event(void) -{ - short events; - - event_del(&client_event); - events = EV_READ; - if (client_ibuf.w.queued > 0) - events |= EV_WRITE; - event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); - event_add(&client_event, NULL); -} - -/* Callback to handle signals in the client. */ -void -client_signal(int sig, unused short events, unused void *arg) -{ - struct sigaction sigact; - int status; - - if (sig == SIGCHLD) - waitpid(WAIT_ANY, &status, WNOHANG); - else if (!client_attached) { - if (sig == SIGTERM) - event_loopexit(NULL); - } else { - switch (sig) { - case SIGHUP: - client_exitreason = CLIENT_EXIT_LOST_TTY; - client_exitval = 1; - client_write_server(MSG_EXITING, NULL, 0); - break; - case SIGTERM: - client_exitreason = CLIENT_EXIT_TERMINATED; - client_exitval = 1; - client_write_server(MSG_EXITING, NULL, 0); - break; - case SIGWINCH: - client_write_server(MSG_RESIZE, NULL, 0); - break; - case SIGCONT: - memset(&sigact, 0, sizeof sigact); - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_RESTART; - sigact.sa_handler = SIG_IGN; - if (sigaction(SIGTSTP, &sigact, NULL) != 0) - fatal("sigaction failed"); - client_write_server(MSG_WAKEUP, NULL, 0); - break; - } - } - - client_update_event(); -} - -/* Callback for client imsg read events. */ -void -client_callback(unused int fd, short events, unused void *arg) -{ - ssize_t n; - int retval; - - if (events & EV_READ) { - if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) - goto lost_server; - if (client_attached) - retval = client_dispatch_attached(); - else - retval = client_dispatch_wait(); - if (retval != 0) { - event_loopexit(NULL); - return; - } - } - - if (events & EV_WRITE) { - if (msgbuf_write(&client_ibuf.w) <= 0 && errno != EAGAIN) - goto lost_server; - } - - client_update_event(); - return; - -lost_server: - client_exitreason = CLIENT_EXIT_LOST_SERVER; - client_exitval = 1; - event_loopexit(NULL); + proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } /* Callback for client stdin read events. */ @@ -538,10 +416,9 @@ client_stdin_callback(unused int fd, unused short events, unused void *arg) if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) return; - client_write_server(MSG_STDIN, &data, sizeof data); + proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); if (data.size <= 0) event_del(&client_stdin); - client_update_event(); } /* Force write to file descriptor. */ @@ -591,13 +468,65 @@ client_exec(const char *shell) fatal("execl failed"); } -/* Dispatch imsgs when in wait state (before MSG_READY). */ -int -client_dispatch_wait(void) +/* Callback to handle signals in the client. */ +void +client_signal(int sig) +{ + struct sigaction sigact; + int status; + + if (sig == SIGCHLD) + waitpid(WAIT_ANY, &status, WNOHANG); + else if (!client_attached) { + if (sig == SIGTERM) + proc_exit(client_proc); + } else { + switch (sig) { + case SIGHUP: + client_exitreason = CLIENT_EXIT_LOST_TTY; + client_exitval = 1; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case SIGTERM: + client_exitreason = CLIENT_EXIT_TERMINATED; + client_exitval = 1; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case SIGWINCH: + proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); + break; + case SIGCONT: + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_IGN; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); + break; + } + } +} + +/* Callback for client read events. */ +void +client_dispatch(struct imsg *imsg, unused void *arg) +{ + if (imsg == NULL) { + client_exitreason = CLIENT_EXIT_LOST_SERVER; + client_exitval = 1; + } else if (client_attached) + client_dispatch_attached(imsg); + else + client_dispatch_wait(imsg); +} + +/* Dispatch imsgs when in wait state (before MSG_READY). */ +void +client_dispatch_wait(struct imsg *imsg) { - struct imsg imsg; char *data; - ssize_t n, datalen; + ssize_t datalen; struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; @@ -615,163 +544,141 @@ client_dispatch_wait(void) pledge_applied = 1; }; - for (;;) { - if ((n = imsg_get(&client_ibuf, &imsg)) == -1) - fatalx("imsg_get failed"); - if (n == 0) - return (0); + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - - log_debug("got %u from server", imsg.hdr.type); - switch (imsg.hdr.type) { - case MSG_EXIT: - case MSG_SHUTDOWN: - if (datalen != sizeof retval && datalen != 0) - fatalx("bad MSG_EXIT size"); - if (datalen == sizeof retval) { - memcpy(&retval, data, sizeof retval); - client_exitval = retval; - } - imsg_free(&imsg); - return (-1); - case MSG_READY: - if (datalen != 0) - fatalx("bad MSG_READY size"); - - event_del(&client_stdin); - client_attached = 1; - client_write_server(MSG_RESIZE, NULL, 0); - break; - case MSG_STDIN: - if (datalen != 0) - fatalx("bad MSG_STDIN size"); - - event_add(&client_stdin, NULL); - break; - case MSG_STDOUT: - if (datalen != sizeof stdoutdata) - fatalx("bad MSG_STDOUT size"); - memcpy(&stdoutdata, data, sizeof stdoutdata); - - client_write(STDOUT_FILENO, stdoutdata.data, - stdoutdata.size); - break; - case MSG_STDERR: - if (datalen != sizeof stderrdata) - fatalx("bad MSG_STDERR size"); - memcpy(&stderrdata, data, sizeof stderrdata); - - client_write(STDERR_FILENO, stderrdata.data, - stderrdata.size); - break; - case MSG_VERSION: - if (datalen != 0) - fatalx("bad MSG_VERSION size"); - - fprintf(stderr, "protocol version mismatch " - "(client %d, server %u)\n", PROTOCOL_VERSION, - imsg.hdr.peerid); - client_exitval = 1; - - imsg_free(&imsg); - return (-1); - case MSG_SHELL: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_SHELL string"); - - clear_signals(0); - client_exec(data); - /* NOTREACHED */ - case MSG_DETACH: - case MSG_DETACHKILL: - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXITED: - imsg_free(&imsg); - return (-1); + switch (imsg->hdr.type) { + case MSG_EXIT: + case MSG_SHUTDOWN: + if (datalen != sizeof retval && datalen != 0) + fatalx("bad MSG_EXIT size"); + if (datalen == sizeof retval) { + memcpy(&retval, data, sizeof retval); + client_exitval = retval; } + proc_exit(client_proc); + break; + case MSG_READY: + if (datalen != 0) + fatalx("bad MSG_READY size"); - imsg_free(&imsg); + event_del(&client_stdin); + client_attached = 1; + proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); + break; + case MSG_STDIN: + if (datalen != 0) + fatalx("bad MSG_STDIN size"); + + event_add(&client_stdin, NULL); + break; + case MSG_STDOUT: + if (datalen != sizeof stdoutdata) + fatalx("bad MSG_STDOUT size"); + memcpy(&stdoutdata, data, sizeof stdoutdata); + + client_write(STDOUT_FILENO, stdoutdata.data, + stdoutdata.size); + break; + case MSG_STDERR: + if (datalen != sizeof stderrdata) + fatalx("bad MSG_STDERR size"); + memcpy(&stderrdata, data, sizeof stderrdata); + + client_write(STDERR_FILENO, stderrdata.data, + stderrdata.size); + break; + case MSG_VERSION: + if (datalen != 0) + fatalx("bad MSG_VERSION size"); + + fprintf(stderr, "protocol version mismatch " + "(client %d, server %u)\n", PROTOCOL_VERSION, + imsg->hdr.peerid); + client_exitval = 1; + proc_exit(client_proc); + break; + case MSG_SHELL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_SHELL string"); + + clear_signals(0); + client_exec(data); + /* NOTREACHED */ + case MSG_DETACH: + case MSG_DETACHKILL: + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case MSG_EXITED: + proc_exit(client_proc); + break; } } /* Dispatch imsgs in attached state (after MSG_READY). */ -int -client_dispatch_attached(void) +void +client_dispatch_attached(struct imsg *imsg) { - struct imsg imsg; struct sigaction sigact; char *data; - ssize_t n, datalen; + ssize_t datalen; - for (;;) { - if ((n = imsg_get(&client_ibuf, &imsg)) == -1) - fatalx("imsg_get failed"); - if (n == 0) - return (0); + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + switch (imsg->hdr.type) { + case MSG_DETACH: + case MSG_DETACHKILL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_DETACH string"); - log_debug("got %u from server", imsg.hdr.type); - switch (imsg.hdr.type) { - case MSG_DETACH: - case MSG_DETACHKILL: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_DETACH string"); + client_exitsession = xstrdup(data); + client_exittype = imsg->hdr.type; + if (imsg->hdr.type == MSG_DETACHKILL) + client_exitreason = CLIENT_EXIT_DETACHED_HUP; + else + client_exitreason = CLIENT_EXIT_DETACHED; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case MSG_EXIT: + if (datalen != 0 && datalen != sizeof (int)) + fatalx("bad MSG_EXIT size"); - client_exitsession = xstrdup(data); - client_exittype = imsg.hdr.type; - if (imsg.hdr.type == MSG_DETACHKILL) - client_exitreason = CLIENT_EXIT_DETACHED_HUP; - else - client_exitreason = CLIENT_EXIT_DETACHED; - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXIT: - if (datalen != 0 && datalen != sizeof (int)) - fatalx("bad MSG_EXIT size"); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + client_exitreason = CLIENT_EXIT_EXITED; + break; + case MSG_EXITED: + if (datalen != 0) + fatalx("bad MSG_EXITED size"); - client_write_server(MSG_EXITING, NULL, 0); - client_exitreason = CLIENT_EXIT_EXITED; - break; - case MSG_EXITED: - if (datalen != 0) - fatalx("bad MSG_EXITED size"); + proc_exit(client_proc); + break; + case MSG_SHUTDOWN: + if (datalen != 0) + fatalx("bad MSG_SHUTDOWN size"); - imsg_free(&imsg); - return (-1); - case MSG_SHUTDOWN: - if (datalen != 0) - fatalx("bad MSG_SHUTDOWN size"); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + client_exitreason = CLIENT_EXIT_SERVER_EXITED; + client_exitval = 1; + break; + case MSG_SUSPEND: + if (datalen != 0) + fatalx("bad MSG_SUSPEND size"); - client_write_server(MSG_EXITING, NULL, 0); - client_exitreason = CLIENT_EXIT_SERVER_EXITED; - client_exitval = 1; - break; - case MSG_SUSPEND: - if (datalen != 0) - fatalx("bad MSG_SUSPEND size"); + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_DFL; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + kill(getpid(), SIGTSTP); + break; + case MSG_LOCK: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_LOCK string"); - memset(&sigact, 0, sizeof sigact); - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_RESTART; - sigact.sa_handler = SIG_DFL; - if (sigaction(SIGTSTP, &sigact, NULL) != 0) - fatal("sigaction failed"); - kill(getpid(), SIGTSTP); - break; - case MSG_LOCK: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_LOCK string"); - - system(data); - client_write_server(MSG_UNLOCK, NULL, 0); - break; - } - - imsg_free(&imsg); + system(data); + proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); + break; } } diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a7ef1cd9..4e390323 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -113,16 +113,10 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (c->session != NULL) { if (dflag) { - /* - * Can't use server_write_session in case attaching to - * the same session as currently attached to. - */ TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_write_client(c, MSG_DETACH, - c_loop->session->name, - strlen(c_loop->session->name) + 1); + proc_send_s(c->peer, MSG_DETACH, s->name); } } @@ -150,8 +144,11 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, c->flags |= CLIENT_READONLY; if (dflag) { - server_write_session(s, MSG_DETACH, s->name, - strlen(s->name) + 1); + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session != s || c == c_loop) + continue; + proc_send_s(c->peer, MSG_DETACH, s->name); + } } if (!Eflag) { @@ -168,7 +165,8 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; - server_write_ready(c); + if (~c->flags & CLIENT_CONTROL) + proc_send(c->peer, MSG_READY, -1, NULL, 0); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 4bae9997..813ac032 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -57,7 +57,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); tty_stop_tty(&c->tty); c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_SUSPEND, NULL, 0); + proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); return (CMD_RETURN_NORMAL); } @@ -74,9 +74,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session != s) continue; - server_write_client(cloop, msgtype, - cloop->session->name, - strlen(cloop->session->name) + 1); + proc_send_s(cloop->peer, msgtype, cloop->session->name); } return (CMD_RETURN_STOP); } @@ -89,14 +87,11 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session == NULL || cloop == c) continue; - server_write_client(cloop, msgtype, - cloop->session->name, - strlen(cloop->session->name) + 1); + proc_send_s(cloop->peer, msgtype, cloop->session->name); } return (CMD_RETURN_NORMAL); } - server_write_client(c, msgtype, c->session->name, - strlen(c->session->name) + 1); + proc_send_s(c->peer, msgtype, c->session->name); return (CMD_RETURN_STOP); } diff --git a/cmd-find.c b/cmd-find.c index 4173e202..fbc06fb7 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -129,8 +129,8 @@ cmd_find_try_TMUX(struct client *c, struct window *w) return (NULL); if (pid != getpid()) return (NULL); - log_debug("client %d TMUX is %s (session @%u)", c->ibuf.fd, - envent->value, session); + log_debug("client %p TMUX is %s (session @%u)", c, envent->value, + session); s = session_find_by_id(session); if (s == NULL || (w != NULL && !session_has(s, w))) @@ -333,6 +333,8 @@ cmd_find_current_session(struct cmd_find_state *fs) { /* If we know the current client, use it. */ if (fs->cmdq->client != NULL) { + log_debug("%s: have client %p%s", __func__, fs->cmdq->client, + fs->cmdq->client->session == NULL ? "" : " (with session)"); if (fs->cmdq->client->session == NULL) return (cmd_find_current_session_with_client(fs)); fs->s = fs->cmdq->client->session; @@ -365,8 +367,11 @@ cmd_find_current_client(struct cmd_q *cmdq) u_int csize; /* If the queue client has a session, use it. */ - if (cmdq->client != NULL && cmdq->client->session != NULL) + if (cmdq->client != NULL && cmdq->client->session != NULL) { + log_debug("%s: using cmdq %p client %p", __func__, cmdq, + cmdq->client); return (cmdq->client); + } /* Otherwise find the current session. */ cmd_find_clear_state(¤t, cmdq, 0); @@ -375,6 +380,7 @@ cmd_find_current_client(struct cmd_q *cmdq) /* If it is attached, find the best of it's clients. */ s = current.s; + log_debug("%s: current session $%u %s", __func__, s->id, s->name); if (~s->flags & SESSION_UNATTACHED) { csize = 0; TAILQ_FOREACH(c, &clients, entry) { @@ -1220,6 +1226,7 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) c = cmd_find_current_client(cmdq); if (c == NULL && !quiet) cmdq_error(cmdq, "no current client"); + log_debug("%s: no target, return %p", __func__, c); return (c); } copy = xstrdup(target); @@ -1251,6 +1258,7 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) cmdq_error(cmdq, "can't find client %s", copy); free(copy); + log_debug("%s: target %s, return %p", __func__, target, c); return (c); } diff --git a/cmd-new-session.c b/cmd-new-session.c index b3bf3c74..f1a6167a 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -270,9 +270,10 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) * taking this session and needs to get MSG_READY and stay around. */ if (!detached) { - if (!already_attached) - server_write_ready(c); - else if (c->session != NULL) + if (!already_attached) { + if (~c->flags & CLIENT_CONTROL) + proc_send(c->peer, MSG_READY, -1, NULL, 0); + } else if (c->session != NULL) c->last_session = c->session; c->session = s; status_timer_start(c); diff --git a/proc.c b/proc.c new file mode 100644 index 00000000..413b2125 --- /dev/null +++ b/proc.c @@ -0,0 +1,249 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +struct tmuxproc { + const char *name; + int exit; + + void (*signalcb)(int); +}; + +struct tmuxpeer { + struct tmuxproc *parent; + + struct imsgbuf ibuf; + struct event event; + + int flags; +#define PEER_BAD 0x1 + + void (*dispatchcb)(struct imsg *, void *); + void *arg; +}; + +static void proc_update_event(struct tmuxpeer *); + +static void +proc_event_cb(unused int fd, short events, void *arg) +{ + struct tmuxpeer *peer = arg; + ssize_t n; + struct imsg imsg; + int v; + + if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { + if ((n = imsg_read(&peer->ibuf)) == -1 || n == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + for (;;) { + if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { + peer->dispatchcb(NULL, peer->arg); + return; + } + if (n == 0) + break; + log_debug("peer %p message %d", peer, imsg.hdr.type); + + v = imsg.hdr.peerid; + if (imsg.hdr.type != MSG_VERSION && + v != PROTOCOL_VERSION) { + log_debug("peer %p bad version %d", peer, v); + + proc_send(peer, MSG_VERSION, -1, NULL, 0); + peer->flags |= PEER_BAD; + + if (imsg.fd != -1) + close(imsg.fd); + imsg_free(&imsg); + break; + } + + peer->dispatchcb(&imsg, peer->arg); + imsg_free(&imsg); + } + } + + if (events & EV_WRITE) { + if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { + peer->dispatchcb(NULL, peer->arg); + return; + } + } + + if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + + proc_update_event(peer); +} + +static void +proc_signal_cb(int signo, unused short events, void *arg) +{ + struct tmuxproc *tp = arg; + + tp->signalcb(signo); +} + +static void +proc_update_event(struct tmuxpeer *peer) +{ + short events; + + event_del(&peer->event); + + events = EV_READ; + if (peer->ibuf.w.queued > 0) + events |= EV_WRITE; + event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); + + event_add(&peer->event, NULL); +} + +int +proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, + size_t len) +{ + struct imsgbuf *ibuf = &peer->ibuf; + void *vp = (void *)buf; + int retval; + + if (peer->flags & PEER_BAD) + return (-1); + log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len); + + retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); + if (retval != 1) + return (-1); + proc_update_event(peer); + return (0); +} + +int +proc_send_s(struct tmuxpeer *peer, enum msgtype type, const char *s) +{ + return (proc_send(peer, type, -1, s, strlen(s) + 1)); +} + +struct tmuxproc * +proc_start(const char *name, struct event_base *base, int forkflag, + void (*signalcb)(int)) +{ + struct tmuxproc *tp; + + if (forkflag) { + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + return (NULL); + } + if (daemon(1, 0) != 0) + fatal("daemon failed"); + + clear_signals(0); + if (event_reinit(base) != 0) + fatalx("event_reinit failed"); + } + + logfile(name); + setproctitle("%s (%s)", name, socket_path); + + log_debug("%s started (%ld): socket %s, protocol %d", name, + (long)getpid(), socket_path, PROTOCOL_VERSION); + + tp = xcalloc(1, sizeof *tp); + tp->name = xstrdup(name); + + tp->signalcb = signalcb; + set_signals(proc_signal_cb, tp); + + return (tp); +} + +void +proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) +{ + log_debug("%s loop enter", tp->name); + do + event_loop(EVLOOP_ONCE); + while (!tp->exit && (loopcb == NULL || !loopcb ())); + log_debug("%s loop exit", tp->name); +} + +void +proc_exit(struct tmuxproc *tp) +{ + tp->exit = 1; +} + +struct tmuxpeer * +proc_add_peer(struct tmuxproc *tp, int fd, + void (*dispatchcb)(struct imsg *, void *), void *arg) +{ + struct tmuxpeer *peer; + + peer = xcalloc(1, sizeof *peer); + peer->parent = tp; + + peer->dispatchcb = dispatchcb; + peer->arg = arg; + + imsg_init(&peer->ibuf, fd); + event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); + + log_debug("add peer %p: %d (%p)", peer, fd, arg); + + proc_update_event(peer); + return (peer); +} + +void +proc_remove_peer(struct tmuxpeer *peer) +{ + log_debug("remove peer %p", peer); + + event_del(&peer->event); + imsg_clear(&peer->ibuf); + + close(peer->ibuf.fd); + free(peer); +} + +void +proc_kill_peer(struct tmuxpeer *peer) +{ + peer->flags |= PEER_BAD; +} diff --git a/server-client.c b/server-client.c index 0e657ae8..13f905b2 100644 --- a/server-client.c +++ b/server-client.c @@ -18,10 +18,12 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -42,10 +44,10 @@ void server_client_set_title(struct client *); void server_client_reset_state(struct client *); int server_client_assume_paste(struct session *); -int server_client_msg_dispatch(struct client *); -void server_client_msg_command(struct client *, struct imsg *); -void server_client_msg_identify(struct client *, struct imsg *); -void server_client_msg_shell(struct client *); +void server_client_dispatch(struct imsg *, void *); +void server_client_dispatch_command(struct client *, struct imsg *); +void server_client_dispatch_identify(struct client *, struct imsg *); +void server_client_dispatch_shell(struct client *); /* Check if this client is inside this server. */ int @@ -87,8 +89,7 @@ server_client_create(int fd) c = xcalloc(1, sizeof *c); c->references = 1; - imsg_init(&c->ibuf, fd); - server_update_event(c); + c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c); if (gettimeofday(&c->creation_time, NULL) != 0) fatal("gettimeofday failed"); @@ -220,10 +221,8 @@ server_client_lost(struct client *c) environ_free(&c->environ); - close(c->ibuf.fd); - imsg_clear(&c->ibuf); - if (event_initialized(&c->event)) - event_del(&c->event); + proc_remove_peer(c->peer); + c->peer = NULL; server_client_unref(c); @@ -257,40 +256,6 @@ server_client_free(unused int fd, unused short events, void *arg) free(c); } -/* Process a single client event. */ -void -server_client_callback(int fd, short events, void *data) -{ - struct client *c = data; - - if (c->flags & CLIENT_DEAD) - return; - - if (fd == c->ibuf.fd) { - if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) <= 0 && - errno != EAGAIN) - goto client_lost; - - if (c->flags & CLIENT_BAD) { - if (c->ibuf.w.queued == 0) - goto client_lost; - return; - } - - if (events & EV_READ && server_client_msg_dispatch(c) != 0) - goto client_lost; - } - - server_push_stdout(c); - server_push_stderr(c); - - server_update_event(c); - return; - -client_lost: - server_client_lost(c); -} - /* Check for mouse keys. */ int server_client_check_mouse(struct client *c) @@ -880,7 +845,7 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; - server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval); + proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); c->flags &= ~CLIENT_EXIT; } @@ -974,123 +939,112 @@ server_client_set_title(struct client *c) } /* Dispatch message from client. */ -int -server_client_msg_dispatch(struct client *c) +void +server_client_dispatch(struct imsg *imsg, void *arg) { - struct imsg imsg; + struct client *c = arg; struct msg_stdin_data stdindata; const char *data; - ssize_t n, datalen; + ssize_t datalen; struct session *s; - if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) - return (-1); + if (c->flags & CLIENT_DEAD) + return; - for (;;) { - if ((n = imsg_get(&c->ibuf, &imsg)) == -1) - return (-1); - if (n == 0) - return (0); - - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - - if (imsg.hdr.peerid != PROTOCOL_VERSION) { - server_write_client(c, MSG_VERSION, NULL, 0); - c->flags |= CLIENT_BAD; - if (imsg.fd != -1) - close(imsg.fd); - imsg_free(&imsg); - continue; - } - - log_debug("got %u from client %p", imsg.hdr.type, c); - switch (imsg.hdr.type) { - case MSG_IDENTIFY_FLAGS: - case MSG_IDENTIFY_TERM: - case MSG_IDENTIFY_TTYNAME: - case MSG_IDENTIFY_CWD: - case MSG_IDENTIFY_STDIN: - case MSG_IDENTIFY_ENVIRON: - case MSG_IDENTIFY_CLIENTPID: - case MSG_IDENTIFY_DONE: - server_client_msg_identify(c, &imsg); - break; - case MSG_COMMAND: - server_client_msg_command(c, &imsg); - break; - case MSG_STDIN: - if (datalen != sizeof stdindata) - fatalx("bad MSG_STDIN size"); - memcpy(&stdindata, data, sizeof stdindata); - - if (c->stdin_callback == NULL) - break; - if (stdindata.size <= 0) - c->stdin_closed = 1; - else { - evbuffer_add(c->stdin_data, stdindata.data, - stdindata.size); - } - c->stdin_callback(c, c->stdin_closed, - c->stdin_callback_data); - break; - case MSG_RESIZE: - if (datalen != 0) - fatalx("bad MSG_RESIZE size"); - - if (c->flags & CLIENT_CONTROL) - break; - if (tty_resize(&c->tty)) { - recalculate_sizes(); - server_redraw_client(c); - } - break; - case MSG_EXITING: - if (datalen != 0) - fatalx("bad MSG_EXITING size"); - - c->session = NULL; - tty_close(&c->tty); - server_write_client(c, MSG_EXITED, NULL, 0); - break; - case MSG_WAKEUP: - case MSG_UNLOCK: - if (datalen != 0) - fatalx("bad MSG_WAKEUP size"); - - if (!(c->flags & CLIENT_SUSPENDED)) - break; - c->flags &= ~CLIENT_SUSPENDED; - - if (c->tty.fd == -1) /* exited in the meantime */ - break; - s = c->session; - - if (gettimeofday(&c->activity_time, NULL) != 0) - fatal("gettimeofday failed"); - if (s != NULL) - session_update_activity(s, &c->activity_time); - - tty_start_tty(&c->tty); - server_redraw_client(c); - recalculate_sizes(); - break; - case MSG_SHELL: - if (datalen != 0) - fatalx("bad MSG_SHELL size"); - - server_client_msg_shell(c); - break; - } - - imsg_free(&imsg); + if (imsg == NULL) { + server_client_lost(c); + return; } + + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + switch (imsg->hdr.type) { + case MSG_IDENTIFY_FLAGS: + case MSG_IDENTIFY_TERM: + case MSG_IDENTIFY_TTYNAME: + case MSG_IDENTIFY_CWD: + case MSG_IDENTIFY_STDIN: + case MSG_IDENTIFY_ENVIRON: + case MSG_IDENTIFY_CLIENTPID: + case MSG_IDENTIFY_DONE: + server_client_dispatch_identify(c, imsg); + break; + case MSG_COMMAND: + server_client_dispatch_command(c, imsg); + break; + case MSG_STDIN: + if (datalen != sizeof stdindata) + fatalx("bad MSG_STDIN size"); + memcpy(&stdindata, data, sizeof stdindata); + + if (c->stdin_callback == NULL) + break; + if (stdindata.size <= 0) + c->stdin_closed = 1; + else { + evbuffer_add(c->stdin_data, stdindata.data, + stdindata.size); + } + c->stdin_callback(c, c->stdin_closed, + c->stdin_callback_data); + break; + case MSG_RESIZE: + if (datalen != 0) + fatalx("bad MSG_RESIZE size"); + + if (c->flags & CLIENT_CONTROL) + break; + if (tty_resize(&c->tty)) { + recalculate_sizes(); + server_redraw_client(c); + } + break; + case MSG_EXITING: + if (datalen != 0) + fatalx("bad MSG_EXITING size"); + + c->session = NULL; + tty_close(&c->tty); + proc_send(c->peer, MSG_EXITED, -1, NULL, 0); + break; + case MSG_WAKEUP: + case MSG_UNLOCK: + if (datalen != 0) + fatalx("bad MSG_WAKEUP size"); + + if (!(c->flags & CLIENT_SUSPENDED)) + break; + c->flags &= ~CLIENT_SUSPENDED; + + if (c->tty.fd == -1) /* exited in the meantime */ + break; + s = c->session; + + if (gettimeofday(&c->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + if (s != NULL) + session_update_activity(s, &c->activity_time); + + tty_start_tty(&c->tty); + server_redraw_client(c); + recalculate_sizes(); + break; + case MSG_SHELL: + if (datalen != 0) + fatalx("bad MSG_SHELL size"); + + server_client_dispatch_shell(c); + break; + } + + server_push_stdout(c); + server_push_stderr(c); } /* Handle command message. */ void -server_client_msg_command(struct client *c, struct imsg *imsg) +server_client_dispatch_command(struct client *c, struct imsg *imsg) { struct msg_command_data data; char *buf; @@ -1143,7 +1097,7 @@ error: /* Handle identify message. */ void -server_client_msg_identify(struct client *c, struct imsg *imsg) +server_client_dispatch_identify(struct client *c, struct imsg *imsg) { const char *data; size_t datalen; @@ -1217,7 +1171,7 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) if (c->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); - server_write_client(c, MSG_STDIN, NULL, 0); + proc_send(c->peer, MSG_STDIN, -1, NULL, 0); c->tty.fd = -1; c->tty.log_fd = -1; @@ -1248,14 +1202,14 @@ server_client_msg_identify(struct client *c, struct imsg *imsg) /* Handle shell message. */ void -server_client_msg_shell(struct client *c) +server_client_dispatch_shell(struct client *c) { const char *shell; shell = options_get_string(&global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1); + proc_send_s(c->peer, MSG_SHELL, shell); - c->flags |= CLIENT_BAD; /* it will die after exec */ + proc_kill_peer(c->peer); } diff --git a/server-fn.c b/server-fn.c index a31a3772..095535ab 100644 --- a/server-fn.c +++ b/server-fn.c @@ -17,7 +17,10 @@ */ #include +#include +#include +#include #include #include #include @@ -47,43 +50,6 @@ server_fill_environ(struct session *s, struct environ *env) environ_set(env, "TMUX", var); } -void -server_write_ready(struct client *c) -{ - if (c->flags & CLIENT_CONTROL) - return; - server_write_client(c, MSG_READY, NULL, 0); -} - -int -server_write_client(struct client *c, enum msgtype type, const void *buf, - size_t len) -{ - struct imsgbuf *ibuf = &c->ibuf; - int error; - - if (c->flags & CLIENT_BAD) - return (-1); - log_debug("writing %d to client %p", type, c); - error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, - (void *) buf, len); - if (error == 1) - server_update_event(c); - return (error == 1 ? 0 : -1); -} - -void -server_write_session(struct session *s, enum msgtype type, const void *buf, - size_t len) -{ - struct client *c; - - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == s) - server_write_client(c, type, buf, len); - } -} - void server_redraw_client(struct client *c) { @@ -227,7 +193,7 @@ server_lock_client(struct client *c) tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_LOCK, cmd, strlen(cmd) + 1); + proc_send_s(c->peer, MSG_LOCK, cmd); } void @@ -484,22 +450,6 @@ server_callback_identify(unused int fd, unused short events, void *data) server_clear_identify(c); } -void -server_update_event(struct client *c) -{ - short events; - - events = 0; - if (!(c->flags & CLIENT_BAD)) - events |= EV_READ; - if (c->ibuf.w.queued > 0) - events |= EV_WRITE; - if (event_initialized(&c->event)) - event_del(&c->event); - event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); - event_add(&c->event, NULL); -} - /* Push stdout to client if possible. */ void server_push_stdout(struct client *c) @@ -516,7 +466,7 @@ server_push_stdout(struct client *c) memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); data.size = size; - if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0) + if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) == 0) evbuffer_drain(c->stdout_data, size); } @@ -540,7 +490,7 @@ server_push_stderr(struct client *c) memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); data.size = size; - if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0) + if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) == 0) evbuffer_drain(c->stderr_data, size); } @@ -570,7 +520,7 @@ server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, if (c->stdin_closed) c->stdin_callback(c, 1, c->stdin_callback_data); - server_write_client(c, MSG_STDIN, NULL, 0); + proc_send(c->peer, MSG_STDIN, -1, NULL, 0); return (0); } diff --git a/server.c b/server.c index 7ce1c99d..a36e9a73 100644 --- a/server.c +++ b/server.c @@ -43,6 +43,7 @@ struct clients clients; +struct tmuxproc *server_proc; int server_fd; int server_exit; struct event server_ev_accept; @@ -54,11 +55,11 @@ struct window_pane *marked_window_pane; struct layout_cell *marked_layout_cell; int server_create_socket(void); -void server_loop(void); +int server_loop(void); int server_should_exit(void); void server_send_exit(void); -void server_accept_callback(int, short, void *); -void server_signal_callback(int, short, void *); +void server_accept(int, short, void *); +void server_signal(int); void server_child_signal(void); void server_child_exited(pid_t, int); void server_child_stopped(pid_t, int); @@ -162,17 +163,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) { int pair[2]; - /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); - log_debug("starting server"); - switch (fork()) { - case -1: - fatal("fork failed"); - case 0: - break; - default: + server_proc = proc_start("server", base, 1, server_signal); + if (server_proc == NULL) { close(pair[1]); return (pair[0]); } @@ -182,21 +177,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) "ps", NULL) != 0) fatal("pledge failed"); - /* - * Must daemonise before loading configuration as the PID changes so - * $TMUX would be wrong for sessions created in the config file. - */ - if (daemon(1, 0) != 0) - fatal("daemon failed"); - - /* event_init() was called in our parent, need to reinit. */ - clear_signals(0); - if (event_reinit(base) != 0) - fatal("event_reinit failed"); - - logfile("server"); - log_debug("server started, pid %ld", (long) getpid()); - RB_INIT(&windows); RB_INIT(&all_window_panes); TAILQ_INIT(&clients); @@ -207,8 +187,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) utf8_build(); start_time = time(NULL); - log_debug("socket path %s", socket_path); - setproctitle("server (%s)", socket_path); server_fd = server_create_socket(); if (server_fd == -1) @@ -226,31 +204,19 @@ server_start(struct event_base *base, int lockfd, char *lockfile) server_add_accept(0); - set_signals(server_signal_callback); - server_loop(); + proc_loop(server_proc, server_loop); status_prompt_save_history(); exit(0); } -/* Main server loop. */ -void +/* Server loop callback. */ +int server_loop(void) -{ - while (!server_should_exit()) { - log_debug("event dispatch enter"); - event_loop(EVLOOP_ONCE); - log_debug("event dispatch exit"); - - server_client_loop(); - } -} - -/* Check if the server should exit (no more clients or sessions). */ -int -server_should_exit(void) { struct client *c; + server_client_loop(); + if (!options_get_number(&global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); @@ -282,10 +248,10 @@ server_send_exit(void) cmd_wait_for_flush(); TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { - if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) + if (c->flags & CLIENT_SUSPENDED) server_client_lost(c); else - server_write_client(c, MSG_SHUTDOWN, NULL, 0); + proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); c->session = NULL; } @@ -331,7 +297,7 @@ server_update_socket(void) /* Callback for server socket. */ void -server_accept_callback(int fd, short events, unused void *data) +server_accept(int fd, short events, unused void *data) { struct sockaddr_storage sa; socklen_t slen = sizeof sa; @@ -372,19 +338,19 @@ server_add_accept(int timeout) event_del(&server_ev_accept); if (timeout == 0) { - event_set(&server_ev_accept, - server_fd, EV_READ, server_accept_callback, NULL); + event_set(&server_ev_accept, server_fd, EV_READ, server_accept, + NULL); event_add(&server_ev_accept, NULL); } else { - event_set(&server_ev_accept, - server_fd, EV_TIMEOUT, server_accept_callback, NULL); + event_set(&server_ev_accept, server_fd, EV_TIMEOUT, + server_accept, NULL); event_add(&server_ev_accept, &tv); } } /* Signal handler. */ void -server_signal_callback(int sig, unused short events, unused void *data) +server_signal(int sig) { int fd; diff --git a/signal.c b/signal.c index 7e6268a7..9a4d58c2 100644 --- a/signal.c +++ b/signal.c @@ -32,7 +32,7 @@ struct event ev_sigusr1; struct event ev_sigwinch; void -set_signals(void(*handler)(int, short, unused void *)) +set_signals(void (*handler)(int, short, void *), void *arg) { struct sigaction sigact; @@ -49,17 +49,17 @@ set_signals(void(*handler)(int, short, unused void *)) if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); - signal_set(&ev_sighup, SIGHUP, handler, NULL); + signal_set(&ev_sighup, SIGHUP, handler, arg); signal_add(&ev_sighup, NULL); - signal_set(&ev_sigchld, SIGCHLD, handler, NULL); + signal_set(&ev_sigchld, SIGCHLD, handler, arg); signal_add(&ev_sigchld, NULL); - signal_set(&ev_sigcont, SIGCONT, handler, NULL); + signal_set(&ev_sigcont, SIGCONT, handler, arg); signal_add(&ev_sigcont, NULL); - signal_set(&ev_sigterm, SIGTERM, handler, NULL); + signal_set(&ev_sigterm, SIGTERM, handler, arg); signal_add(&ev_sigterm, NULL); - signal_set(&ev_sigusr1, SIGUSR1, handler, NULL); + signal_set(&ev_sigusr1, SIGUSR1, handler, arg); signal_add(&ev_sigusr1, NULL); - signal_set(&ev_sigwinch, SIGWINCH, handler, NULL); + signal_set(&ev_sigwinch, SIGWINCH, handler, arg); signal_add(&ev_sigwinch, NULL); } diff --git a/tmux.c b/tmux.c index 8b3d0bfe..01e73f5d 100644 --- a/tmux.c +++ b/tmux.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "tmux.h" @@ -347,9 +348,6 @@ main(int argc, char **argv) } free(path); - /* Set process title. */ - setproctitle("%s (%s)", __progname, socket_path); - /* Pass control to the client. */ exit(client_main(event_init(), argc, argv, flags)); } diff --git a/tmux.h b/tmux.h index 97238865..c4b12402 100644 --- a/tmux.h +++ b/tmux.h @@ -24,11 +24,9 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -1170,8 +1168,10 @@ struct message_entry { }; /* Client connection. */ +struct tmuxproc; +struct tmuxpeer; struct client { - struct imsgbuf ibuf; + struct tmuxpeer *peer; pid_t pid; int fd; @@ -1209,7 +1209,7 @@ struct client { #define CLIENT_STATUS 0x10 #define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 -#define CLIENT_BAD 0x80 +/* 0x80 unused */ #define CLIENT_IDENTIFY 0x100 #define CLIENT_DEAD 0x200 #define CLIENT_BORDERS 0x400 @@ -1420,6 +1420,19 @@ int areshell(const char *); void setblocking(int, int); const char *find_home(void); +/* proc.c */ +struct imsg; +int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t); +int proc_send_s(struct tmuxpeer *, enum msgtype, const char *); +struct tmuxproc *proc_start(const char *, struct event_base *, int, + void (*)(int)); +void proc_loop(struct tmuxproc *, int (*)(void)); +void proc_exit(struct tmuxproc *); +struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, + void (*)(struct imsg *, void *), void *); +void proc_remove_peer(struct tmuxpeer *); +void proc_kill_peer(struct tmuxpeer *); + /* cfg.c */ extern int cfg_finished; extern int cfg_references; @@ -1727,6 +1740,7 @@ void alerts_reset_all(void); void alerts_queue(struct window *, int); /* server.c */ +extern struct tmuxproc *server_proc; extern struct clients clients; extern struct session *marked_session; extern struct winlink *marked_winlink; @@ -1748,16 +1762,10 @@ void server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); -void server_client_callback(int, short, void *); void server_client_loop(void); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); -void server_write_ready(struct client *); -int server_write_client(struct client *, enum msgtype, const void *, - size_t); -void server_write_session(struct session *, enum msgtype, const void *, - size_t); void server_redraw_client(struct client *); void server_status_client(struct client *); void server_redraw_session(struct session *); @@ -1780,7 +1788,6 @@ void server_destroy_session(struct session *); void server_check_unattached(void); void server_set_identify(struct client *); void server_clear_identify(struct client *); -void server_update_event(struct client *); void server_push_stdout(struct client *); void server_push_stderr(struct client *); int server_set_stdin_callback(struct client *, void (*)(struct client *, @@ -2110,7 +2117,7 @@ char *format_window_name(struct window *); char *parse_window_name(const char *); /* signal.c */ -void set_signals(void(*)(int, short, void *)); +void set_signals(void(*)(int, short, void *), void *); void clear_signals(int); /* control.c */ From 67c3a014b960b8c1d7931d3c99f570610b1d4d3f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 14:51:35 +0000 Subject: [PATCH 376/703] No more TMPDIR. --- tmux.1 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index a9f3cdc9..1fb99252 100644 --- a/tmux.1 +++ b/tmux.1 @@ -143,11 +143,10 @@ session created, and continues to process the rest of the configuration file. .It Fl L Ar socket-name .Nm stores the server socket in a directory under -.Ev TMUX_TMPDIR , -.Ev TMPDIR -if it is unset, or +.Ev TMUX_TMPDIR +or .Pa /tmp -if both are unset. +if it is unset. The default socket is named .Em default . This option allows a different socket name to be specified, allowing several From 44657bf932b068aff5ce1019a4e8a2e7b00b5321 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 27 Oct 2015 15:58:42 +0000 Subject: [PATCH 377/703] Move struct options into options.c. --- alerts.c | 24 +++++++++---------- client.c | 6 ++--- cmd-attach-session.c | 4 ++-- cmd-break-pane.c | 2 +- cmd-choose-buffer.c | 2 +- cmd-move-window.c | 2 +- cmd-new-session.c | 10 ++++---- cmd-new-window.c | 4 ++-- cmd-rename-window.c | 2 +- cmd-send-keys.c | 4 ++-- cmd-set-option.c | 24 +++++++++---------- cmd-show-options.c | 14 ++++++----- cmd-split-window.c | 6 ++--- cmd-switch-client.c | 2 +- format.c | 12 +++++----- input-keys.c | 2 +- input.c | 6 ++--- layout-set.c | 8 +++---- names.c | 4 ++-- options.c | 28 ++++++++++++++++++++-- paste.c | 2 +- resize.c | 8 +++---- screen-redraw.c | 6 ++--- server-client.c | 20 ++++++++-------- server-fn.c | 16 ++++++------- server.c | 2 +- session.c | 19 ++++++++------- status.c | 56 ++++++++++++++++++++++---------------------- tmux.c | 31 ++++++++++++------------ tmux.h | 22 +++++++---------- tty-keys.c | 2 +- tty-term.c | 2 +- tty.c | 8 +++---- window-choose.c | 6 ++--- window-clock.c | 4 ++-- window-copy.c | 34 +++++++++++++-------------- window.c | 20 ++++++++-------- 37 files changed, 223 insertions(+), 201 deletions(-) diff --git a/alerts.c b/alerts.c index 806e565b..f1477030 100644 --- a/alerts.c +++ b/alerts.c @@ -80,11 +80,11 @@ alerts_enabled(struct window *w, int flags) struct session *s; if (flags & WINDOW_ACTIVITY) { - if (options_get_number(&w->options, "monitor-activity")) + if (options_get_number(w->options, "monitor-activity")) return (1); } if (flags & WINDOW_SILENCE) { - if (options_get_number(&w->options, "monitor-silence") != 0) + if (options_get_number(w->options, "monitor-silence") != 0) return (1); } if (~flags & WINDOW_BELL) @@ -92,7 +92,7 @@ alerts_enabled(struct window *w, int flags) RB_FOREACH(s, sessions, &sessions) { if (!session_has(s, w)) continue; - if (options_get_number(&s->options, "bell-action") != BELL_NONE) + if (options_get_number(s->options, "bell-action") != BELL_NONE) return (1); } return (0); @@ -116,7 +116,7 @@ alerts_reset(struct window *w) event_del(&w->alerts_timer); timerclear(&tv); - tv.tv_sec = options_get_number(&w->options, "monitor-silence"); + tv.tv_sec = options_get_number(w->options, "monitor-silence"); log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); if (tv.tv_sec != 0) @@ -160,11 +160,11 @@ alerts_check_bell(struct session *s, struct winlink *wl) if (s->curw->window == w) w->flags &= ~WINDOW_BELL; - action = options_get_number(&s->options, "bell-action"); + action = options_get_number(s->options, "bell-action"); if (action == BELL_NONE) return (0); - visual = options_get_number(&s->options, "visual-bell"); + visual = options_get_number(s->options, "visual-bell"); TAILQ_FOREACH(c, &clients, entry) { if (c->session != s || c->flags & CLIENT_CONTROL) continue; @@ -201,14 +201,14 @@ alerts_check_activity(struct session *s, struct winlink *wl) if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) return (0); - if (!options_get_number(&w->options, "monitor-activity")) + if (!options_get_number(w->options, "monitor-activity")) return (0); - if (options_get_number(&s->options, "bell-on-alert")) + if (options_get_number(s->options, "bell-on-alert")) alerts_ring_bell(s); wl->flags |= WINLINK_ACTIVITY; - if (options_get_number(&s->options, "visual-activity")) { + if (options_get_number(s->options, "visual-activity")) { TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; @@ -233,14 +233,14 @@ alerts_check_silence(struct session *s, struct winlink *wl) if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) return (0); - if (options_get_number(&w->options, "monitor-silence") == 0) + if (options_get_number(w->options, "monitor-silence") == 0) return (0); - if (options_get_number(&s->options, "bell-on-alert")) + if (options_get_number(s->options, "bell-on-alert")) alerts_ring_bell(s); wl->flags |= WINLINK_SILENCE; - if (options_get_number(&s->options, "visual-silence")) { + if (options_get_number(s->options, "visual-silence")) { TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; diff --git a/client.c b/client.c index 606feefe..aa453538 100644 --- a/client.c +++ b/client.c @@ -291,9 +291,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) fatal("pledge failed"); /* Free stuff that is not used in the client. */ - options_free(&global_options); - options_free(&global_s_options); - options_free(&global_w_options); + options_free(global_options); + options_free(global_s_options); + options_free(global_w_options); environ_free(&global_environ); /* Create stdin handler. */ diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 4e390323..a3623ec4 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -121,7 +121,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (!Eflag) { - update = options_get_string(&s->options, + update = options_get_string(s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } @@ -152,7 +152,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (!Eflag) { - update = options_get_string(&s->options, + update = options_get_string(s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 2aa5c5b7..39179cc7 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -84,7 +84,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) layout_init(w, wp); if (idx == -1) - idx = -1 - options_get_number(&dst_s->options, "base-index"); + idx = -1 - options_get_number(dst_s->options, "base-index"); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) session_select(dst_s, wl->idx); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index b4590306..e790de6b 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -63,7 +63,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); - utf8flag = options_get_number(&wl->window->options, "utf8"); + utf8flag = options_get_number(wl->window->options, "utf8"); if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); diff --git a/cmd-move-window.c b/cmd-move-window.c index b15df4f6..1bd46ab2 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -95,7 +95,7 @@ cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) * session already has the correct winlink id to us, either * automatically or specified by -s. */ - if (!sflag && options_get_number(&src->options, "renumber-windows")) + if (!sflag && options_get_number(src->options, "renumber-windows")) session_renumber_windows(src); recalculate_sizes(); diff --git a/cmd-new-session.c b/cmd-new-session.c index f1a6167a..48828e54 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -197,7 +197,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) goto error; } } - if (sy > 0 && options_get_number(&global_s_options, "status")) + if (sy > 0 && options_get_number(global_s_options, "status")) sy--; if (sx == 0) sx = 1; @@ -211,7 +211,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) argc = args->argc; argv = args->argv; } else if (target == NULL) { - cmd = options_get_string(&global_s_options, "default-command"); + cmd = options_get_string(global_s_options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = &cmd; @@ -232,13 +232,13 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Construct the environment. */ environ_init(&env); if (c != NULL && !args_has(args, 'E')) { - update = options_get_string(&global_s_options, + update = options_get_string(global_s_options, "update-environment"); environ_update(update, &c->environ, &env); } /* Create the new session. */ - idx = -1 - options_get_number(&global_s_options, "base-index"); + idx = -1 - options_get_number(global_s_options, "base-index"); s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, sy, &cause); if (s == NULL) { @@ -252,7 +252,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (argc >= 0 && args_has(args, 'n')) { w = s->curw->window; window_set_name(w, args_get(args, 'n')); - options_set_number(&w->options, "automatic-rename", 0); + options_set_number(w->options, "automatic-rename", 0); } /* diff --git a/cmd-new-window.c b/cmd-new-window.c index 893fe6e2..a3712d76 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -71,7 +71,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) detached = args_has(args, 'd'); if (args->argc == 0) { - cmd = options_get_string(&s->options, "default-command"); + cmd = options_get_string(s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; @@ -136,7 +136,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) } if (idx == -1) - idx = -1 - options_get_number(&s->options, "base-index"); + idx = -1 - options_get_number(s->options, "base-index"); wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, &cause); if (wl == NULL) { diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 2f677a48..bc85d96b 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -47,7 +47,7 @@ cmd_rename_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); window_set_name(wl->window, args->argv[0]); - options_set_number(&wl->window->options, "automatic-rename", 0); + options_set_number(wl->window->options, "automatic-rename", 0); server_status_window(wl->window); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index b1f8d672..dd796d60 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -70,9 +70,9 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) if (self->entry == &cmd_send_prefix_entry) { if (args_has(args, '2')) - key = options_get_number(&s->options, "prefix2"); + key = options_get_number(s->options, "prefix2"); else - key = options_get_number(&s->options, "prefix"); + key = options_get_number(s->options, "prefix"); window_pane_key(wp, NULL, s, key, NULL); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-option.c b/cmd-set-option.c index e0b07edb..6762e6ad 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -126,10 +126,10 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Work out the tree from the table. */ if (table == server_options_table) - oo = &global_options; + oo = global_options; else if (table == window_options_table) { if (args_has(self->args, 'g')) - oo = &global_w_options; + oo = global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) { @@ -139,11 +139,11 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) 'g')) ? " need target window or -g" : ""); return (CMD_RETURN_ERROR); } - oo = &wl->window->options; + oo = wl->window->options; } } else if (table == session_options_table) { if (args_has(self->args, 'g')) - oo = &global_s_options; + oo = global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) { @@ -153,7 +153,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) 'g')) ? " need target session or -g" : ""); return (CMD_RETURN_ERROR); } - oo = &s->options; + oo = s->options; } } else { cmdq_error(cmdq, "unknown table"); @@ -179,7 +179,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) /* Start or stop timers if necessary. */ if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { - if (options_get_number(&w->options, "automatic-rename")) + if (options_get_number(w->options, "automatic-rename")) w->active->flags |= PANE_CHANGED; } } @@ -210,25 +210,25 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, struct options *oo; if (args_has(args, 's')) - oo = &global_options; + oo = global_options; else if (args_has(self->args, 'w') || self->entry == &cmd_set_window_option_entry) { if (args_has(self->args, 'g')) - oo = &global_w_options; + oo = global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); - oo = &wl->window->options; + oo = wl->window->options; } } else { if (args_has(self->args, 'g')) - oo = &global_s_options; + oo = global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); - oo = &s->options; + oo = s->options; } } @@ -276,7 +276,7 @@ cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, return (-1); } - if (args_has(args, 'g') || oo == &global_options) { + if (args_has(args, 'g') || oo == global_options) { switch (oe->type) { case OPTIONS_TABLE_STRING: options_set_string(oo, oe->name, "%s", oe->default_str); diff --git a/cmd-show-options.c b/cmd-show-options.c index a5011e72..e99d665f 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -61,28 +61,28 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) int quiet; if (args_has(self->args, 's')) { - oo = &global_options; + oo = global_options; table = server_options_table; } else if (args_has(self->args, 'w') || self->entry == &cmd_show_window_options_entry) { table = window_options_table; if (args_has(self->args, 'g')) - oo = &global_w_options; + oo = global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); - oo = &wl->window->options; + oo = wl->window->options; } } else { table = session_options_table; if (args_has(self->args, 'g')) - oo = &global_s_options; + oo = global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); - oo = &s->options; + oo = s->options; } } @@ -151,13 +151,15 @@ cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, struct options_entry *o; const char *optval; - RB_FOREACH(o, options_tree, &oo->tree) { + o = options_first(oo); + while (o != NULL) { if (*o->name == '@') { if (args_has(self->args, 'v')) cmdq_print(cmdq, "%s", o->str); else cmdq_print(cmdq, "%s \"%s\"", o->name, o->str); } + o = options_next(o); } for (oe = table; oe->name != NULL; oe++) { diff --git a/cmd-split-window.c b/cmd-split-window.c index f397113f..60b04d77 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -73,7 +73,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) server_fill_environ(s, &env); if (args->argc == 0) { - cmd = options_get_string(&s->options, "default-command"); + cmd = options_get_string(s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; @@ -135,9 +135,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) else size = (wp->sx * percentage) / 100; } - hlimit = options_get_number(&s->options, "history-limit"); + hlimit = options_get_number(s->options, "history-limit"); - shell = options_get_string(&s->options, "default-shell"); + shell = options_get_string(s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 3a72886a..5a8c8b08 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -120,7 +120,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } if (c != NULL && !args_has(args, 'E')) { - update = options_get_string(&s->options, "update-environment"); + update = options_get_string(s->options, "update-environment"); environ_update(update, &c->environ, &s->environ); } diff --git a/format.c b/format.c index 285bfadb..0add71e5 100644 --- a/format.c +++ b/format.c @@ -581,15 +581,15 @@ format_find(struct format_tree *ft, const char *key, int modifiers) found = NULL; if (~modifiers & FORMAT_TIMESTRING) { - o = options_find(&global_options, key); + o = options_find(global_options, key); if (o == NULL && ft->w != NULL) - o = options_find(&ft->w->options, key); + o = options_find(ft->w->options, key); if (o == NULL) - o = options_find(&global_w_options, key); + o = options_find(global_w_options, key); if (o == NULL && ft->s != NULL) - o = options_find(&ft->s->options, key); + o = options_find(ft->s->options, key); if (o == NULL) - o = options_find(&global_s_options, key); + o = options_find(global_s_options, key); if (o != NULL) { switch (o->type) { case OPTIONS_STRING: @@ -1101,7 +1101,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); format_add(ft, "pane_synchronized", "%d", - !!options_get_number(&wp->window->options, "synchronize-panes")); + !!options_get_number(wp->window->options, "synchronize-panes")); format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); diff --git a/input-keys.c b/input-keys.c index 6e49dd3b..579c0f39 100644 --- a/input-keys.c +++ b/input-keys.c @@ -171,7 +171,7 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) * Then try to look this up as an xterm key, if the flag to output them * is set. */ - if (options_get_number(&wp->window->options, "xterm-keys")) { + if (options_get_number(wp->window->options, "xterm-keys")) { if ((out = xterm_keys_lookup(key)) != NULL) { bufferevent_write(wp->event, out, strlen(out)); free(out); diff --git a/input.c b/input.c index ab56fc38..3a02b0ce 100644 --- a/input.c +++ b/input.c @@ -1907,12 +1907,12 @@ input_exit_rename(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; - if (!options_get_number(&ictx->wp->window->options, "allow-rename")) + if (!options_get_number(ictx->wp->window->options, "allow-rename")) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); window_set_name(ictx->wp->window, ictx->input_buf); - options_set_number(&ictx->wp->window->options, "automatic-rename", 0); + options_set_number(ictx->wp->window->options, "automatic-rename", 0); server_status_window(ictx->wp->window); } @@ -1921,7 +1921,7 @@ input_exit_rename(struct input_ctx *ictx) int input_utf8_open(struct input_ctx *ictx) { - if (!options_get_number(&ictx->wp->window->options, "utf8")) { + if (!options_get_number(ictx->wp->window->options, "utf8")) { /* Print, and do not switch state. */ input_print(ictx); return (-1); diff --git a/layout-set.c b/layout-set.c index da94cff2..852ec0f6 100644 --- a/layout-set.c +++ b/layout-set.c @@ -245,10 +245,10 @@ layout_set_main_h(struct window *w) width = (w->sx - (n - 1)) / columns; /* Get the main pane height and add one for separator line. */ - mainheight = options_get_number(&w->options, "main-pane-height") + 1; + mainheight = options_get_number(w->options, "main-pane-height") + 1; /* Get the optional other pane height and add one for separator line. */ - otherheight = options_get_number(&w->options, "other-pane-height") + 1; + otherheight = options_get_number(w->options, "other-pane-height") + 1; /* * If an other pane height was specified, honour it so long as it @@ -366,10 +366,10 @@ layout_set_main_v(struct window *w) height = (w->sy - (n - 1)) / rows; /* Get the main pane width and add one for separator line. */ - mainwidth = options_get_number(&w->options, "main-pane-width") + 1; + mainwidth = options_get_number(w->options, "main-pane-width") + 1; /* Get the optional other pane width and add one for separator line. */ - otherwidth = options_get_number(&w->options, "other-pane-width") + 1; + otherwidth = options_get_number(w->options, "other-pane-width") + 1; /* * If an other pane width was specified, honour it so long as it diff --git a/names.c b/names.c index e880c577..0e806ca0 100644 --- a/names.c +++ b/names.c @@ -58,7 +58,7 @@ check_window_name(struct window *w) if (w->active == NULL) return; - if (!options_get_number(&w->options, "automatic-rename")) + if (!options_get_number(w->options, "automatic-rename")) return; if (~w->active->flags & PANE_CHANGED) { @@ -122,7 +122,7 @@ format_window_name(struct window *w) format_defaults_window(ft, w); format_defaults_pane(ft, w->active); - fmt = options_get_string(&w->options, "automatic-rename-format"); + fmt = options_get_string(w->options, "automatic-rename-format"); name = format_expand(ft, fmt); format_free(ft); diff --git a/options.c b/options.c index 030cfd51..487918fd 100644 --- a/options.c +++ b/options.c @@ -29,6 +29,13 @@ * a red-black tree. */ +struct options { + RB_HEAD(options_tree, options_entry) tree; + struct options *parent; +}; + +int options_cmp(struct options_entry *, struct options_entry *); +RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); RB_GENERATE(options_tree, options_entry, entry, options_cmp); int @@ -37,11 +44,15 @@ options_cmp(struct options_entry *o1, struct options_entry *o2) return (strcmp(o1->name, o2->name)); } -void -options_init(struct options *oo, struct options *parent) +struct options * +options_create(struct options *parent) { + struct options *oo; + + oo = xcalloc(1, sizeof *oo); RB_INIT(&oo->tree); oo->parent = parent; + return (oo); } void @@ -57,6 +68,19 @@ options_free(struct options *oo) free(o->str); free(o); } + free(oo); +} + +struct options_entry * +options_first(struct options *oo) +{ + return (RB_MIN(options_tree, &oo->tree)); +} + +struct options_entry * +options_next(struct options_entry *o) +{ + return (RB_NEXT(options_tree, &oo->tree, o)); } struct options_entry * diff --git a/paste.c b/paste.c index 44f88d0b..ad3b56b5 100644 --- a/paste.c +++ b/paste.c @@ -151,7 +151,7 @@ paste_add(char *data, size_t size) if (size == 0) return; - limit = options_get_number(&global_options, "buffer-limit"); + limit = options_get_number(global_options, "buffer-limit"); RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { if (paste_num_automatic < limit) break; diff --git a/resize.c b/resize.c index 3606bfeb..c7805e05 100644 --- a/resize.c +++ b/resize.c @@ -53,7 +53,7 @@ recalculate_sizes(void) int flag, has_status, is_zoomed, forced; RB_FOREACH(s, sessions, &sessions) { - has_status = options_get_number(&s->options, "status"); + has_status = options_get_number(s->options, "status"); s->attached = 0; ssx = ssy = UINT_MAX; @@ -94,7 +94,7 @@ recalculate_sizes(void) RB_FOREACH(w, windows, &windows) { if (w->active == NULL) continue; - flag = options_get_number(&w->options, "aggressive-resize"); + flag = options_get_number(w->options, "aggressive-resize"); ssx = ssy = UINT_MAX; RB_FOREACH(s, sessions, &sessions) { @@ -115,12 +115,12 @@ recalculate_sizes(void) continue; forced = 0; - limit = options_get_number(&w->options, "force-width"); + limit = options_get_number(w->options, "force-width"); if (limit >= PANE_MINIMUM && ssx > limit) { ssx = limit; forced |= WINDOW_FORCEWIDTH; } - limit = options_get_number(&w->options, "force-height"); + limit = options_get_number(w->options, "force-height"); if (limit >= PANE_MINIMUM && ssy > limit) { ssy = limit; forced |= WINDOW_FORCEHEIGHT; diff --git a/screen-redraw.c b/screen-redraw.c index 799f5c55..fd4536d0 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -222,7 +222,7 @@ void screen_redraw_screen(struct client *c, int draw_panes, int draw_status, int draw_borders) { - struct options *oo = &c->session->options; + struct options *oo = c->session->options; struct tty *tty = &c->tty; u_int top; int status, spos; @@ -276,7 +276,7 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) { struct session *s = c->session; struct window *w = s->curw->window; - struct options *oo = &w->options; + struct options *oo = w->options; struct tty *tty = &c->tty; struct window_pane *wp; struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; @@ -392,7 +392,7 @@ screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top) { struct tty *tty = &c->tty; struct session *s = c->session; - struct options *oo = &s->options; + struct options *oo = s->options; struct window *w = wp->window; struct grid_cell gc; u_int idx, px, py, i, j, xoff, yoff; diff --git a/server-client.c b/server-client.c index 13f905b2..8d6eefa0 100644 --- a/server-client.c +++ b/server-client.c @@ -490,7 +490,7 @@ server_client_assume_paste(struct session *s) struct timeval tv; int t; - if ((t = options_get_number(&s->options, "assume-paste-time")) == 0) + if ((t = options_get_number(s->options, "assume-paste-time")) == 0) return (0); timersub(&s->activity_time, &s->last_activity_time, &tv); @@ -556,7 +556,7 @@ server_client_handle_key(struct client *c, int key) m->valid = 1; m->key = key; - if (!options_get_number(&s->options, "mouse")) + if (!options_get_number(s->options, "mouse")) goto forward; } else m->valid = 0; @@ -593,7 +593,7 @@ retry: * If this is a repeating key, start the timer. Otherwise reset * the client back to the root table. */ - xtimeout = options_get_number(&s->options, "repeat-time"); + xtimeout = options_get_number(s->options, "repeat-time"); if (xtimeout != 0 && bd->can_repeat) { c->flags |= CLIENT_REPEAT; @@ -635,8 +635,8 @@ retry: * No match, but in the root table. Prefix switches to the prefix table * and everything else is passed through. */ - if (key == options_get_number(&s->options, "prefix") || - key == options_get_number(&s->options, "prefix2")) { + if (key == options_get_number(s->options, "prefix") || + key == options_get_number(s->options, "prefix2")) { server_client_key_table(c, "prefix"); server_status_client(c); return; @@ -713,7 +713,7 @@ server_client_check_focus(struct window_pane *wp) int push; /* Are focus events off? */ - if (!options_get_number(&global_options, "focus-events")) + if (!options_get_number(global_options, "focus-events")) return; /* Do we need to push the focus state? */ @@ -773,7 +773,7 @@ server_client_reset_state(struct client *c) struct window *w = c->session->curw->window; struct window_pane *wp = w->active; struct screen *s = wp->screen; - struct options *oo = &c->session->options; + struct options *oo = c->session->options; int status, mode, o; if (c->flags & CLIENT_SUSPENDED) @@ -862,7 +862,7 @@ server_client_check_redraw(struct client *c) return; if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { - if (options_get_number(&s->options, "set-titles")) + if (options_get_number(s->options, "set-titles")) server_client_set_title(c); if (c->message_string != NULL) @@ -922,7 +922,7 @@ server_client_set_title(struct client *c) char *title; struct format_tree *ft; - template = options_get_string(&s->options, "set-titles-string"); + template = options_get_string(s->options, "set-titles-string"); ft = format_create(); format_defaults(ft, c, NULL, NULL, NULL); @@ -1206,7 +1206,7 @@ server_client_dispatch_shell(struct client *c) { const char *shell; - shell = options_get_string(&global_s_options, "default-shell"); + shell = options_get_string(global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; proc_send_s(c->peer, MSG_SHELL, shell); diff --git a/server-fn.c b/server-fn.c index 095535ab..e9a04cb5 100644 --- a/server-fn.c +++ b/server-fn.c @@ -39,7 +39,7 @@ server_fill_environ(struct session *s, struct environ *env) long pid; if (s != NULL) { - term = options_get_string(&global_options, "default-terminal"); + term = options_get_string(global_options, "default-terminal"); environ_set(env, "TERM", term); idx = s->id; @@ -183,7 +183,7 @@ server_lock_client(struct client *c) if (c->flags & CLIENT_SUSPENDED) return; - cmd = options_get_string(&c->session->options, "lock-command"); + cmd = options_get_string(c->session->options, "lock-command"); if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) return; @@ -219,7 +219,7 @@ server_kill_window(struct window *w) server_redraw_session_group(s); } - if (options_get_number(&s->options, "renumber-windows")) { + if (options_get_number(s->options, "renumber-windows")) { if ((sg = session_group_find(s)) != NULL) { TAILQ_FOREACH(target_s, &sg->sessions, gentry) session_renumber_windows(target_s); @@ -272,7 +272,7 @@ server_link_window(struct session *src, struct winlink *srcwl, } if (dstidx == -1) - dstidx = -1 - options_get_number(&dst->options, "base-index"); + dstidx = -1 - options_get_number(dst->options, "base-index"); dstwl = session_attach(dst, srcwl->window, dstidx, cause); if (dstwl == NULL) return (-1); @@ -308,7 +308,7 @@ server_destroy_pane(struct window_pane *wp) wp->fd = -1; } - if (options_get_number(&w->options, "remain-on-exit")) { + if (options_get_number(w->options, "remain-on-exit")) { if (old_fd == -1) return; screen_write_start(&ctx, wp, &wp->base); @@ -371,7 +371,7 @@ server_destroy_session(struct session *s) struct client *c; struct session *s_new; - if (!options_get_number(&s->options, "detach-on-destroy")) + if (!options_get_number(s->options, "detach-on-destroy")) s_new = server_next_session(s); else s_new = NULL; @@ -407,7 +407,7 @@ server_check_unattached(void) RB_FOREACH(s, sessions, &sessions) { if (!(s->flags & SESSION_UNATTACHED)) continue; - if (options_get_number (&s->options, "destroy-unattached")) + if (options_get_number (s->options, "destroy-unattached")) session_destroy(s); } } @@ -418,7 +418,7 @@ server_set_identify(struct client *c) struct timeval tv; int delay; - delay = options_get_number(&c->session->options, "display-panes-time"); + delay = options_get_number(c->session->options, "display-panes-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; diff --git a/server.c b/server.c index a36e9a73..2af2c442 100644 --- a/server.c +++ b/server.c @@ -217,7 +217,7 @@ server_loop(void) server_client_loop(); - if (!options_get_number(&global_options, "exit-unattached")) { + if (!options_get_number(global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } diff --git a/session.c b/session.c index a929cada..6b641d48 100644 --- a/session.c +++ b/session.c @@ -120,10 +120,10 @@ session_create(const char *name, int argc, char **argv, const char *path, TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); - options_init(&s->options, &global_s_options); environ_init(&s->environ); if (env != NULL) environ_copy(env, &s->environ); + s->options = options_create(global_s_options); s->tio = NULL; if (tio != NULL) { @@ -190,6 +190,9 @@ session_free(unused int fd, unused short events, void *arg) log_debug("session %s freed (%d references)", s->name, s->references); if (s->references == 0) { + environ_free(&s->environ); + options_free(s->options); + free(s->name); free(s); } @@ -212,8 +215,6 @@ session_destroy(struct session *s) event_del(&s->lock_timer); session_group_remove(s); - environ_free(&s->environ); - options_free(&s->options); while (!TAILQ_EMPTY(&s->lastw)) winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw)); @@ -271,7 +272,7 @@ session_update_activity(struct session *s, struct timeval *from) if (~s->flags & SESSION_UNATTACHED) { timerclear(&tv); - tv.tv_sec = options_get_number(&s->options, "lock-after-time"); + tv.tv_sec = options_get_number(s->options, "lock-after-time"); if (tv.tv_sec != 0) evtimer_add(&s->lock_timer, &tv); } @@ -332,11 +333,11 @@ session_new(struct session *s, const char *name, int argc, char **argv, environ_copy(&s->environ, &env); server_fill_environ(s, &env); - shell = options_get_string(&s->options, "default-shell"); + shell = options_get_string(s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; - hlimit = options_get_number(&s->options, "history-limit"); + hlimit = options_get_number(s->options, "history-limit"); w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause); if (w == NULL) { @@ -348,8 +349,8 @@ session_new(struct session *s, const char *name, int argc, char **argv, notify_window_linked(s, w); environ_free(&env); - if (options_get_number(&s->options, "set-remain-on-exit")) - options_set_number(&w->options, "remain-on-exit", 1); + if (options_get_number(s->options, "set-remain-on-exit")) + options_set_number(w->options, "remain-on-exit", 1); session_group_synchronize_from(s); return (wl); @@ -712,7 +713,7 @@ session_renumber_windows(struct session *s) RB_INIT(&s->windows); /* Start renumbering from the base-index if it's set. */ - new_idx = options_get_number(&s->options, "base-index"); + new_idx = options_get_number(s->options, "base-index"); new_curw_idx = 0; /* Go through the winlinks and assign new indexes. */ diff --git a/status.c b/status.c index e17c1f5d..a2f46b95 100644 --- a/status.c +++ b/status.c @@ -61,7 +61,7 @@ status_prompt_find_history_file(void) const char *home, *history_file; char *path; - history_file = options_get_string(&global_options, "history-file"); + history_file = options_get_string(global_options, "history-file"); if (*history_file == '\0') return (NULL); if (*history_file == '/') @@ -160,7 +160,7 @@ status_timer_callback(unused int fd, unused short events, void *arg) c->flags |= CLIENT_STATUS; timerclear(&tv); - tv.tv_sec = options_get_number(&s->options, "status-interval"); + tv.tv_sec = options_get_number(s->options, "status-interval"); if (tv.tv_sec != 0) evtimer_add(&c->status_timer, &tv); @@ -178,7 +178,7 @@ status_timer_start(struct client *c) else evtimer_set(&c->status_timer, status_timer_callback, c); - if (s != NULL && options_get_number(&s->options, "status")) + if (s != NULL && options_get_number(s->options, "status")) status_timer_callback(-1, 0, c); } @@ -198,10 +198,10 @@ status_at_line(struct client *c) { struct session *s = c->session; - if (!options_get_number(&s->options, "status")) + if (!options_get_number(s->options, "status")) return (-1); - if (options_get_number(&s->options, "status-position") == 0) + if (options_get_number(s->options, "status-position") == 0) return (0); return (c->tty.sy - 1); } @@ -216,12 +216,12 @@ status_redraw_get_left(struct client *c, time_t t, int utf8flag, char *left; size_t leftlen; - style_apply_update(gc, &s->options, "status-left-style"); + style_apply_update(gc, s->options, "status-left-style"); - template = options_get_string(&s->options, "status-left"); + template = options_get_string(s->options, "status-left"); left = status_replace(c, NULL, template, t); - *size = options_get_number(&s->options, "status-left-length"); + *size = options_get_number(s->options, "status-left-length"); leftlen = screen_write_cstrlen(utf8flag, "%s", left); if (leftlen < *size) *size = leftlen; @@ -238,12 +238,12 @@ status_redraw_get_right(struct client *c, time_t t, int utf8flag, char *right; size_t rightlen; - style_apply_update(gc, &s->options, "status-right-style"); + style_apply_update(gc, s->options, "status-right-style"); - template = options_get_string(&s->options, "status-right"); + template = options_get_string(s->options, "status-right"); right = status_replace(c, NULL, template, t); - *size = options_get_number(&s->options, "status-right-length"); + *size = options_get_number(s->options, "status-right-length"); rightlen = screen_write_cstrlen(utf8flag, "%s", right); if (rightlen < *size) *size = rightlen; @@ -261,7 +261,7 @@ status_get_window_at(struct client *c, u_int x) x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { - oo = &wl->window->options; + oo = wl->window->options; len = strlen(options_get_string(oo, "window-status-separator")); if (x < wl->status_width) @@ -289,7 +289,7 @@ status_redraw(struct client *c) int larrow, rarrow, utf8flag; /* No status line? */ - if (c->tty.sy == 0 || !options_get_number(&s->options, "status")) + if (c->tty.sy == 0 || !options_get_number(s->options, "status")) return (1); left = right = NULL; larrow = rarrow = 0; @@ -298,7 +298,7 @@ status_redraw(struct client *c) t = time(NULL); /* Set up default colour. */ - style_apply(&stdgc, &s->options, "status-style"); + style_apply(&stdgc, s->options, "status-style"); /* Create the target screen. */ memcpy(&old_status, &c->status, sizeof old_status); @@ -313,7 +313,7 @@ status_redraw(struct client *c) goto out; /* Get UTF-8 flag. */ - utf8flag = options_get_number(&s->options, "status-utf8"); + utf8flag = options_get_number(s->options, "status-utf8"); /* Work out left and right strings. */ memcpy(&lgc, &stdgc, sizeof lgc); @@ -346,7 +346,7 @@ status_redraw(struct client *c) if (wl == s->curw) wloffset = wlwidth; - oo = &wl->window->options; + oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); seplen = screen_write_strlen(utf8flag, "%s", sep); wlwidth += wl->status_width + seplen; @@ -361,7 +361,7 @@ status_redraw(struct client *c) screen_write_cnputs(&ctx, -1, &wl->status_cell, utf8flag, "%s", wl->status_text); - oo = &wl->window->options; + oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep); } @@ -461,7 +461,7 @@ draw: else wloffset = 0; if (wlwidth < wlavailable) { - switch (options_get_number(&s->options, "status-justify")) { + switch (options_get_number(s->options, "status-justify")) { case 1: /* centred */ wloffset += (wlavailable - wlwidth) / 2; break; @@ -520,7 +520,7 @@ char * status_print(struct client *c, struct winlink *wl, time_t t, struct grid_cell *gc) { - struct options *oo = &wl->window->options; + struct options *oo = wl->window->options; struct session *s = c->session; const char *fmt; char *text; @@ -553,7 +553,7 @@ status_message_set(struct client *c, const char *fmt, ...) int delay; u_int first, limit; - limit = options_get_number(&global_options, "message-limit"); + limit = options_get_number(global_options, "message-limit"); status_prompt_clear(c); status_message_clear(c); @@ -577,7 +577,7 @@ status_message_set(struct client *c, const char *fmt, ...) free(msg); } - delay = options_get_number(&c->session->options, "display-time"); + delay = options_get_number(c->session->options, "display-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; @@ -631,13 +631,13 @@ status_message_redraw(struct client *c) memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(&s->options, "status-utf8"); + utf8flag = options_get_number(s->options, "status-utf8"); len = screen_write_strlen(utf8flag, "%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; - style_apply(&gc, &s->options, "message-style"); + style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); @@ -686,7 +686,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, c->prompt_flags = flags; - keys = options_get_number(&c->session->options, "status-keys"); + keys = options_get_number(c->session->options, "status-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit); else @@ -761,7 +761,7 @@ status_prompt_redraw(struct client *c) memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(&s->options, "status-utf8"); + utf8flag = options_get_number(s->options, "status-utf8"); len = screen_write_strlen(utf8flag, "%s", c->prompt_string); if (len > c->tty.sx) @@ -770,9 +770,9 @@ status_prompt_redraw(struct client *c) /* Change colours for command mode. */ if (c->prompt_mdata.mode == 1) - style_apply(&gc, &s->options, "message-command-style"); + style_apply(&gc, s->options, "message-command-style"); else - style_apply(&gc, &s->options, "message-style"); + style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); @@ -815,7 +815,7 @@ void status_prompt_key(struct client *c, int key) { struct session *sess = c->session; - struct options *oo = &sess->options; + struct options *oo = sess->options; struct paste_buffer *pb; char *s, *first, *last, word[64], swapc; const char *histstr, *bufdata, *wsep = NULL; diff --git a/tmux.c b/tmux.c index 01e73f5d..bdc426f9 100644 --- a/tmux.c +++ b/tmux.c @@ -38,9 +38,9 @@ extern char *malloc_options; #endif -struct options global_options; /* server options */ -struct options global_s_options; /* session options */ -struct options global_w_options; /* window options */ +struct options *global_options; /* server options */ +struct options *global_s_options; /* session options */ +struct options *global_w_options; /* window options */ struct environ global_environ; char *shell_cmd; @@ -281,22 +281,21 @@ main(int argc, char **argv) if (getcwd(tmp, sizeof tmp) != NULL) environ_set(&global_environ, "PWD", tmp); - options_init(&global_options, NULL); - options_table_populate_tree(server_options_table, &global_options); + global_options = options_create(NULL); + options_table_populate_tree(server_options_table, global_options); - options_init(&global_s_options, NULL); - options_table_populate_tree(session_options_table, &global_s_options); - options_set_string(&global_s_options, "default-shell", "%s", - getshell()); + global_s_options = options_create(NULL); + options_table_populate_tree(session_options_table, global_s_options); + options_set_string(global_s_options, "default-shell", "%s", getshell()); - options_init(&global_w_options, NULL); - options_table_populate_tree(window_options_table, &global_w_options); + global_w_options = options_create(NULL); + options_table_populate_tree(window_options_table, global_w_options); /* Enable UTF-8 if the first client is on UTF-8 terminal. */ if (flags & CLIENT_UTF8) { - options_set_number(&global_s_options, "status-utf8", 1); - options_set_number(&global_s_options, "mouse-utf8", 1); - options_set_number(&global_w_options, "utf8", 1); + options_set_number(global_s_options, "status-utf8", 1); + options_set_number(global_s_options, "mouse-utf8", 1); + options_set_number(global_w_options, "utf8", 1); } /* Override keys to vi if VISUAL or EDITOR are set. */ @@ -307,8 +306,8 @@ main(int argc, char **argv) keys = MODEKEY_VI; else keys = MODEKEY_EMACS; - options_set_number(&global_s_options, "status-keys", keys); - options_set_number(&global_w_options, "mode-keys", keys); + options_set_number(global_s_options, "status-keys", keys); + options_set_number(global_w_options, "mode-keys", keys); } /* diff --git a/tmux.h b/tmux.h index c4b12402..3b2d2d10 100644 --- a/tmux.h +++ b/tmux.h @@ -680,11 +680,6 @@ struct options_entry { RB_ENTRY(options_entry) entry; }; -struct options { - RB_HEAD(options_tree, options_entry) tree; - struct options *parent; -}; - /* Scheduled job. */ struct job { enum { @@ -866,6 +861,7 @@ TAILQ_HEAD(window_panes, window_pane); RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ +struct options; struct window { u_int id; @@ -899,7 +895,7 @@ struct window { #define WINDOW_FORCEHEIGHT 0x4000 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) - struct options options; + struct options *options; u_int references; @@ -993,7 +989,7 @@ struct session { struct winlink_stack lastw; struct winlinks windows; - struct options options; + struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; @@ -1405,9 +1401,9 @@ struct options_table_entry { #define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ -extern struct options global_options; -extern struct options global_s_options; -extern struct options global_w_options; +extern struct options *global_options; +extern struct options *global_s_options; +extern struct options *global_w_options; extern struct environ global_environ; extern char *shell_cmd; extern int debug_level; @@ -1509,10 +1505,10 @@ void notify_session_created(struct session *); void notify_session_closed(struct session *); /* options.c */ -int options_cmp(struct options_entry *, struct options_entry *); -RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); -void options_init(struct options *, struct options *); +struct options *options_create(struct options *); void options_free(struct options *); +struct options_entry *options_first(struct options *); +struct options_entry *options_next(struct options_entry *); struct options_entry *options_find1(struct options *, const char *); struct options_entry *options_find(struct options *, const char *); void options_remove(struct options *, const char *); diff --git a/tty-keys.c b/tty-keys.c index c1de2ab7..309e8c04 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -564,7 +564,7 @@ partial_key: } /* Get the time period. */ - delay = options_get_number(&global_options, "escape-time"); + delay = options_get_number(global_options, "escape-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; diff --git a/tty-term.c b/tty-term.c index 76626ddf..c6147559 100644 --- a/tty-term.c +++ b/tty-term.c @@ -457,7 +457,7 @@ tty_term_find(char *name, int fd, char **cause) } /* Apply terminal overrides. */ - s = options_get_string(&global_options, "terminal-overrides"); + s = options_get_string(global_options, "terminal-overrides"); tty_term_override(term, s); /* Delete curses data. */ diff --git a/tty.c b/tty.c index 3d6f29ca..6b6343c8 100644 --- a/tty.c +++ b/tty.c @@ -229,7 +229,7 @@ tty_start_tty(struct tty *tty) tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); if (tty_term_flag(tty->term, TTYC_XT)) { - if (options_get_number(&global_options, "focus-events")) { + if (options_get_number(global_options, "focus-events")) { tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } @@ -457,7 +457,7 @@ tty_set_italics(struct tty *tty) const char *s; if (tty_term_has(tty->term, TTYC_SITM)) { - s = options_get_string(&global_options, "default-terminal"); + s = options_get_string(global_options, "default-terminal"); if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { tty_putcode(tty, TTYC_SITM); return; @@ -1686,8 +1686,8 @@ tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) return; pgc = &wp->colgc; - agc = options_get_style(&wp->window->options, "window-active-style"); - wgc = options_get_style(&wp->window->options, "window-style"); + agc = options_get_style(wp->window->options, "window-active-style"); + wgc = options_get_style(wp->window->options, "window-style"); if (gc->fg == 8 && !(gc->flags & GRID_FLAG_FG256)) { if (pgc->fg != 8 || (pgc->flags & GRID_FLAG_FG256)) { diff --git a/window-choose.c b/window-choose.c index 37baf0d7..f1c3f94a 100644 --- a/window-choose.c +++ b/window-choose.c @@ -169,7 +169,7 @@ window_choose_init(struct window_pane *wp) screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_choice); else @@ -748,7 +748,7 @@ window_choose_write_line( { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct screen *s = &data->screen; struct grid_cell gc; size_t last, xoff = 0; @@ -759,7 +759,7 @@ window_choose_write_line( fatalx("called before callback assigned"); last = screen_size_y(s) - 1; - utf8flag = options_get_number(&wp->window->options, "utf8"); + utf8flag = options_get_number(wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); if (data->selected == data->top + py) style_apply(&gc, oo, "mode-style"); diff --git a/window-clock.c b/window-clock.c index 5bc546a9..e366714b 100644 --- a/window-clock.c +++ b/window-clock.c @@ -204,8 +204,8 @@ window_clock_draw_screen(struct window_pane *wp) struct tm *tm; u_int i, j, x, y, idx; - colour = options_get_number(&wp->window->options, "clock-mode-colour"); - style = options_get_number(&wp->window->options, "clock-mode-style"); + colour = options_get_number(wp->window->options, "clock-mode-colour"); + style = options_get_number(wp->window->options, "clock-mode-style"); screen_write_start(&ctx, NULL, s); diff --git a/window-copy.c b/window-copy.c index f81a2a16..3ba27c80 100644 --- a/window-copy.c +++ b/window-copy.c @@ -195,7 +195,7 @@ window_copy_init(struct window_pane *wp) s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else @@ -286,7 +286,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) if (backing == &wp->base) return; - utf8flag = options_get_number(&wp->window->options, "utf8"); + utf8flag = options_get_number(wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); @@ -629,13 +629,13 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, break; case MODEKEYCOPY_NEXTWORD: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word(wp, word_separators); break; case MODEKEYCOPY_NEXTWORDEND: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word_end(wp, word_separators); break; @@ -645,7 +645,7 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, break; case MODEKEYCOPY_PREVIOUSWORD: word_separators = - options_get_string(&sess->options, "word-separators"); + options_get_string(sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_previous_word(wp, word_separators); break; @@ -777,7 +777,7 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, return; input_on: - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_edit); else @@ -787,7 +787,7 @@ input_on: return; input_off: - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else @@ -1026,8 +1026,8 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) if (*searchstr == '\0') return; - utf8flag = options_get_number(&wp->window->options, "utf8"); - wrapflag = options_get_number(&wp->window->options, "wrap-search"); + utf8flag = options_get_number(wp->window->options, "utf8"); + wrapflag = options_get_number(wp->window->options, "wrap-search"); searchlen = screen_write_strlen(utf8flag, "%s", searchstr); screen_init(&ss, searchlen, 1, 0); @@ -1093,8 +1093,8 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) if (*searchstr == '\0') return; - utf8flag = options_get_number(&wp->window->options, "utf8"); - wrapflag = options_get_number(&wp->window->options, "wrap-search"); + utf8flag = options_get_number(wp->window->options, "utf8"); + wrapflag = options_get_number(wp->window->options, "wrap-search"); searchlen = screen_write_strlen(utf8flag, "%s", searchstr); screen_init(&ss, searchlen, 1, 0); @@ -1168,7 +1168,7 @@ window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct grid_cell gc; char hdr[512]; size_t last, xoff = 0, size = 0, limit; @@ -1301,7 +1301,7 @@ window_copy_update_selection(struct window_pane *wp, int may_redraw) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct grid_cell gc; u_int sx, sy, ty, cy; @@ -1401,7 +1401,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len) * bottom-right-most, regardless of copy direction. If it is vi, also * keep bottom-right-most character. */ - keys = options_get_number(&wp->window->options, "mode-keys"); + keys = options_get_number(wp->window->options, "mode-keys"); if (data->rectflag) { /* * Need to ignore the column with the cursor in it, which for @@ -1460,7 +1460,7 @@ window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, { struct screen_write_ctx ctx; - if (options_get_number(&global_options, "set-clipboard")) { + if (options_get_number(global_options, "set-clipboard")) { screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); @@ -1523,7 +1523,7 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) if (buf == NULL) return; - if (options_get_number(&global_options, "set-clipboard")) { + if (options_get_number(global_options, "set-clipboard")) { screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); @@ -2074,7 +2074,7 @@ window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) { struct window_copy_mode_data *data = wp->modedata; - struct options *oo = &wp->window->options; + struct options *oo = wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; int keys, expected = 1; diff --git a/window.c b/window.c index 9e77adcd..baaaa0f8 100644 --- a/window.c +++ b/window.c @@ -302,7 +302,7 @@ window_create1(u_int sx, u_int sy) w->sx = sx; w->sy = sy; - options_init(&w->options, &global_w_options); + w->options = options_create(global_w_options); w->references = 0; @@ -335,7 +335,7 @@ window_create(const char *name, int argc, char **argv, const char *path, w->active = TAILQ_FIRST(&w->panes); if (name != NULL) { w->name = xstrdup(name); - options_set_number(&w->options, "automatic-rename", 0); + options_set_number(w->options, "automatic-rename", 0); } else w->name = default_window_name(w); @@ -359,7 +359,7 @@ window_destroy(struct window *w) if (event_initialized(&w->alerts_timer)) evtimer_del(&w->alerts_timer); - options_free(&w->options); + options_free(w->options); window_destroy_panes(w); @@ -437,8 +437,8 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) * active or inactive pane do not have a custom style, they will need * to be redrawn. */ - agc = options_get_style(&w->options, "window-active-style"); - wgc = options_get_style(&w->options, "window-style"); + agc = options_get_style(w->options, "window-active-style"); + wgc = options_get_style(w->options, "window-style"); if (style_equal(agc, wgc)) return; if (style_equal(&grid_default_cell, &w->active->colgc)) @@ -598,7 +598,7 @@ window_pane_at_index(struct window *w, u_int idx) struct window_pane *wp; u_int n; - n = options_get_number(&w->options, "pane-base-index"); + n = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wp, &w->panes, entry) { if (n == idx) return (wp); @@ -636,7 +636,7 @@ window_pane_index(struct window_pane *wp, u_int *i) struct window_pane *wq; struct window *w = wp->window; - *i = options_get_number(&w->options, "pane-base-index"); + *i = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wq, &w->panes, entry) { if (wp == wq) { return (0); @@ -1002,7 +1002,7 @@ window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc, if (wp->saved_grid != NULL) return; - if (!options_get_number(&wp->window->options, "alternate-screen")) + if (!options_get_number(wp->window->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); @@ -1032,7 +1032,7 @@ window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc, if (wp->saved_grid == NULL) return; - if (!options_get_number(&wp->window->options, "alternate-screen")) + if (!options_get_number(wp->window->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); @@ -1120,7 +1120,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, if (KEYC_IS_MOUSE(key)) return; - if (options_get_number(&wp->window->options, "synchronize-panes")) { + if (options_get_number(wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 == wp || wp2->mode != NULL) continue; From bf9c933caed5c74be3c9c4da02d7c57a9dcf091d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 28 Oct 2015 09:51:55 +0000 Subject: [PATCH 378/703] Like options, move the environ struct into environ.c. --- client.c | 2 +- cmd-attach-session.c | 4 ++-- cmd-find.c | 2 +- cmd-new-session.c | 14 +++++++------- cmd-new-window.c | 4 ++-- cmd-respawn-pane.c | 20 ++++++++++---------- cmd-respawn-window.c | 20 ++++++++++---------- cmd-set-environment.c | 4 ++-- cmd-show-environment.c | 9 ++++++--- cmd-split-window.c | 20 ++++++++++---------- cmd-string.c | 6 +++--- cmd-switch-client.c | 2 +- environ.c | 30 +++++++++++++++++++++++++----- format.c | 4 ++-- job.c | 18 +++++++++--------- server-client.c | 8 ++++---- session.c | 22 +++++++++++----------- tmux.c | 8 ++++---- tmux.h | 29 +++++++++++++++-------------- 19 files changed, 125 insertions(+), 101 deletions(-) diff --git a/client.c b/client.c index aa453538..97382f63 100644 --- a/client.c +++ b/client.c @@ -294,7 +294,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) options_free(global_options); options_free(global_s_options); options_free(global_w_options); - environ_free(&global_environ); + environ_free(global_environ); /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a3623ec4..c570358c 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -123,7 +123,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (!Eflag) { update = options_get_string(s->options, "update-environment"); - environ_update(update, &c->environ, &s->environ); + environ_update(update, c->environ, s->environ); } c->session = s; @@ -154,7 +154,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (!Eflag) { update = options_get_string(s->options, "update-environment"); - environ_update(update, &c->environ, &s->environ); + environ_update(update, c->environ, s->environ); } c->session = s; diff --git a/cmd-find.c b/cmd-find.c index fbc06fb7..609297aa 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -121,7 +121,7 @@ cmd_find_try_TMUX(struct client *c, struct window *w) u_int session; struct session *s; - envent = environ_find(&c->environ, "TMUX"); + envent = environ_find(c->environ, "TMUX"); if (envent == NULL) return (NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index 48828e54..7b637bc6 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -60,7 +60,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client, *c0; struct session *s, *groupwith; struct window *w; - struct environ env; + struct environ *env; struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; const char *path; @@ -223,30 +223,30 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (c != NULL && c->session == NULL) - envent = environ_find(&c->environ, "PATH"); + envent = environ_find(c->environ, "PATH"); else - envent = environ_find(&global_environ, "PATH"); + envent = environ_find(global_environ, "PATH"); if (envent != NULL) path = envent->value; /* Construct the environment. */ - environ_init(&env); + env = environ_create(); if (c != NULL && !args_has(args, 'E')) { update = options_get_string(global_s_options, "update-environment"); - environ_update(update, &c->environ, &env); + environ_update(update, c->environ, env); } /* Create the new session. */ idx = -1 - options_get_number(global_s_options, "base-index"); - s = session_create(newname, argc, argv, path, cwd, &env, tiop, idx, sx, + s = session_create(newname, argc, argv, path, cwd, env, tiop, idx, sx, sy, &cause); + environ_free(env); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); goto error; } - environ_free(&env); /* Set the initial window name if one given. */ if (argc >= 0 && args_has(args, 'n')) { diff --git a/cmd-new-window.c b/cmd-new-window.c index a3712d76..70e70c68 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -86,9 +86,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 6575e8e4..864b45b8 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -46,7 +46,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp; struct session *s; - struct environ env; + struct environ *env; const char *path; char *cause; u_int idx; @@ -64,10 +64,10 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); window_pane_reset_mode(wp); screen_reinit(&wp->base); @@ -75,22 +75,22 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); - environ_free(&env); + environ_free(env); return (CMD_RETURN_ERROR); } wp->flags |= PANE_REDRAW; server_status_window(w); - environ_free(&env); + environ_free(env); return (CMD_RETURN_NORMAL); } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 06102ed0..8d8332b1 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -45,7 +45,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp; struct session *s; - struct environ env; + struct environ *env; const char *path; char *cause; struct environ_entry *envent; @@ -64,10 +64,10 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) } } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); @@ -78,17 +78,17 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, &env, + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); - environ_free(&env); + environ_free(env); server_destroy_pane(wp); return (CMD_RETURN_ERROR); } @@ -101,6 +101,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) recalculate_sizes(); server_redraw_window(w); - environ_free(&env); + environ_free(env); return (CMD_RETURN_NORMAL); } diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 83e63b48..864e1d9b 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -61,11 +61,11 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) value = args->argv[1]; if (args_has(self->args, 'g')) - env = &global_environ; + env = global_environ; else { if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); - env = &s->environ; + env = s->environ; } if (args_has(self->args, 'u')) { diff --git a/cmd-show-environment.c b/cmd-show-environment.c index af24d91b..96cfa289 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -91,12 +91,12 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) struct environ_entry *envent; if (args_has(self->args, 'g')) - env = &global_environ; + env = global_environ; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); - env = &s->environ; + env = s->environ; } if (args->argc != 0) { @@ -109,7 +109,10 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - RB_FOREACH(envent, environ, env) + envent = environ_first(env); + while (envent != NULL) { cmd_show_environment_print(self, cmdq, envent); + envent = environ_next(envent); + } return (CMD_RETURN_NORMAL); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 60b04d77..75b8eeb6 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -52,7 +52,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl; struct window *w; struct window_pane *wp, *new_wp = NULL; - struct environ env; + struct environ *env; const char *cmd, *path, *shell, *template; char **argv, *cause, *new_cause, *cp; u_int hlimit; @@ -67,10 +67,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) w = wl->window; server_unzoom_window(w); - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); if (args->argc == 0) { cmd = options_get_string(s->options, "default-command"); @@ -151,13 +151,13 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) - envent = environ_find(&cmdq->client->environ, "PATH"); + envent = environ_find(cmdq->client->environ, "PATH"); else - envent = environ_find(&s->environ, "PATH"); + envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; - if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, &env, + if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env, s->tio, &cause) != 0) goto error; @@ -170,7 +170,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) } else server_status_session(s); - environ_free(&env); + environ_free(env); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) @@ -193,7 +193,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); error: - environ_free(&env); + environ_free(env); if (new_wp != NULL) { layout_close_pane(new_wp); window_remove_pane(w, new_wp); diff --git a/cmd-string.c b/cmd-string.c index db1723cb..51554800 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -126,7 +126,7 @@ cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file, whitespace = argv[0] + strcspn(argv[0], " \t"); if (equals == NULL || equals > whitespace) break; - environ_put(&global_environ, argv[0]); + environ_put(global_environ, argv[0]); argc--; memmove(argv, argv + 1, argc * (sizeof *argv)); } @@ -303,7 +303,7 @@ cmd_string_variable(const char *s, size_t *p) buf = xrealloc(buf, len + 1); buf[len] = '\0'; - envent = environ_find(&global_environ, buf); + envent = environ_find(global_environ, buf); free(buf); if (envent == NULL) return (xstrdup("")); @@ -326,7 +326,7 @@ cmd_string_expand_tilde(const char *s, size_t *p) last = cmd_string_getc(s, p); if (last == EOF || last == '/' || last == ' '|| last == '\t') { - envent = environ_find(&global_environ, "HOME"); + envent = environ_find(global_environ, "HOME"); if (envent != NULL && *envent->value != '\0') home = envent->value; else if ((pw = getpwuid(getuid())) != NULL) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 5a8c8b08..dbdc5b63 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -121,7 +121,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && !args_has(args, 'E')) { update = options_get_string(s->options, "update-environment"); - environ_update(update, &c->environ, &s->environ); + environ_update(update, c->environ, s->environ); } if (c->session != NULL && c->session != s) diff --git a/environ.c b/environ.c index 11b8c849..43a9ce01 100644 --- a/environ.c +++ b/environ.c @@ -27,6 +27,9 @@ * Environment - manipulate a set of environment variables. */ +RB_HEAD(environ, environ_entry); +int environ_cmp(struct environ_entry *, struct environ_entry *); +RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); RB_GENERATE(environ, environ_entry, entry, environ_cmp); int @@ -36,25 +39,42 @@ environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) } /* Initialise the environment. */ -void -environ_init(struct environ *env) +struct environ * +environ_create(void) { + struct environ *env; + + env = xcalloc(1, sizeof *env); RB_INIT(env); + + return (env); } /* Free an environment. */ void environ_free(struct environ *env) { - struct environ_entry *envent; + struct environ_entry *envent, *envent1; - while (!RB_EMPTY(env)) { - envent = RB_ROOT(env); + RB_FOREACH_SAFE(envent, environ, env, envent1) { RB_REMOVE(environ, env, envent); free(envent->name); free(envent->value); free(envent); } + free(env); +} + +struct environ_entry * +environ_first(struct environ *env) +{ + return (RB_MIN(environ, env)); +} + +struct environ_entry * +environ_next(struct environ_entry *envent) +{ + return (RB_NEXT(environ, env, envent)); } /* Copy one environment into another. */ diff --git a/format.c b/format.c index 0add71e5..0d9fbc7f 100644 --- a/format.c +++ b/format.c @@ -631,9 +631,9 @@ format_find(struct format_tree *ft, const char *key, int modifiers) if (~modifiers & FORMAT_TIMESTRING) { envent = NULL; if (ft->s != NULL) - envent = environ_find(&ft->s->environ, key); + envent = environ_find(ft->s->environ, key); if (envent == NULL) - envent = environ_find(&global_environ, key); + envent = environ_find(global_environ, key); if (envent != NULL) { found = envent->value; goto found; diff --git a/job.c b/job.c index 0b348eda..8492fb52 100644 --- a/job.c +++ b/job.c @@ -45,22 +45,22 @@ job_run(const char *cmd, struct session *s, int cwd, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; - struct environ env; + struct environ *env; pid_t pid; int nullfd, out[2]; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); - environ_init(&env); - environ_copy(&global_environ, &env); + env = environ_create(); + environ_copy(global_environ, env); if (s != NULL) - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + environ_copy(s->environ, env); + server_fill_environ(s, env); switch (pid = fork()) { case -1: - environ_free(&env); + environ_free(env); close(out[0]); close(out[1]); return (NULL); @@ -70,8 +70,8 @@ job_run(const char *cmd, struct session *s, int cwd, if (cwd != -1 && fchdir(cwd) != 0) chdir("/"); - environ_push(&env); - environ_free(&env); + environ_push(env); + environ_free(env); if (dup2(out[1], STDIN_FILENO) == -1) fatal("dup2 failed"); @@ -96,7 +96,7 @@ job_run(const char *cmd, struct session *s, int cwd, } /* parent */ - environ_free(&env); + environ_free(env); close(out[1]); job = xmalloc(sizeof *job); diff --git a/server-client.c b/server-client.c index 8d6eefa0..7ae74c09 100644 --- a/server-client.c +++ b/server-client.c @@ -59,7 +59,7 @@ server_client_check_nested(struct client *c) if (c->tty.path == NULL) return (0); - envent = environ_find(&c->environ, "TMUX"); + envent = environ_find(c->environ, "TMUX"); if (envent == NULL || *envent->value == '\0') return (0); @@ -95,7 +95,7 @@ server_client_create(int fd) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); - environ_init(&c->environ); + c->environ = environ_create(); c->fd = -1; c->cwd = -1; @@ -219,7 +219,7 @@ server_client_lost(struct client *c) cmdq_free(c->cmdq); c->cmdq = NULL; - environ_free(&c->environ); + environ_free(c->environ); proc_remove_peer(c->peer); c->peer = NULL; @@ -1146,7 +1146,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) - environ_put(&c->environ, data); + environ_put(c->environ, data); log_debug("client %p IDENTIFY_ENVIRON %s", c, data); break; case MSG_IDENTIFY_CLIENTPID: diff --git a/session.c b/session.c index 6b641d48..217b5272 100644 --- a/session.c +++ b/session.c @@ -120,9 +120,9 @@ session_create(const char *name, int argc, char **argv, const char *path, TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); - environ_init(&s->environ); + s->environ = environ_create(); if (env != NULL) - environ_copy(env, &s->environ); + environ_copy(env, s->environ); s->options = options_create(global_s_options); s->tio = NULL; @@ -190,7 +190,7 @@ session_free(unused int fd, unused short events, void *arg) log_debug("session %s freed (%d references)", s->name, s->references); if (s->references == 0) { - environ_free(&s->environ); + environ_free(s->environ); options_free(s->options); free(s->name); @@ -319,7 +319,7 @@ session_new(struct session *s, const char *name, int argc, char **argv, { struct window *w; struct winlink *wl; - struct environ env; + struct environ *env; const char *shell; u_int hlimit; @@ -328,26 +328,26 @@ session_new(struct session *s, const char *name, int argc, char **argv, return (NULL); } - environ_init(&env); - environ_copy(&global_environ, &env); - environ_copy(&s->environ, &env); - server_fill_environ(s, &env); + env = environ_create(); + environ_copy(global_environ, env); + environ_copy(s->environ, env); + server_fill_environ(s, env); shell = options_get_string(s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; hlimit = options_get_number(s->options, "history-limit"); - w = window_create(name, argc, argv, path, shell, cwd, &env, s->tio, + w = window_create(name, argc, argv, path, shell, cwd, env, s->tio, s->sx, s->sy, hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); - environ_free(&env); + environ_free(env); return (NULL); } winlink_set_window(wl, w); notify_window_linked(s, w); - environ_free(&env); + environ_free(env); if (options_get_number(s->options, "set-remain-on-exit")) options_set_number(w->options, "remain-on-exit", 1); diff --git a/tmux.c b/tmux.c index bdc426f9..758479b8 100644 --- a/tmux.c +++ b/tmux.c @@ -41,7 +41,7 @@ extern char *malloc_options; struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ -struct environ global_environ; +struct environ *global_environ; char *shell_cmd; int debug_level; @@ -275,11 +275,11 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; } - environ_init(&global_environ); + global_environ = environ_create(); for (var = environ; *var != NULL; var++) - environ_put(&global_environ, *var); + environ_put(global_environ, *var); if (getcwd(tmp, sizeof tmp) != NULL) - environ_set(&global_environ, "PWD", tmp); + environ_set(global_environ, "PWD", tmp); global_options = options_create(NULL); options_table_populate_tree(server_options_table, global_options); diff --git a/tmux.h b/tmux.h index 3b2d2d10..9a55a1b1 100644 --- a/tmux.h +++ b/tmux.h @@ -36,6 +36,15 @@ extern char *__progname; extern char **environ; +struct client; +struct environ; +struct input_ctx; +struct mouse_event; +struct options; +struct session; +struct tmuxpeer; +struct tmuxproc; + /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" @@ -762,9 +771,6 @@ struct screen_write_ctx { * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. */ -struct client; -struct session; -struct mouse_event; struct window_mode { struct screen *(*init)(struct window_pane *); void (*free)(struct window_pane *); @@ -796,7 +802,6 @@ struct window_choose_data { }; /* Child window structure. */ -struct input_ctx; struct window_pane { u_int id; u_int active_point; @@ -861,7 +866,6 @@ TAILQ_HEAD(window_panes, window_pane); RB_HEAD(window_pane_tree, window_pane); /* Window structure. */ -struct options; struct window { u_int id; @@ -959,7 +963,6 @@ struct environ_entry { RB_ENTRY(environ_entry) entry; }; -RB_HEAD(environ, environ_entry); /* Client session. */ struct session_group { @@ -998,7 +1001,7 @@ struct session { struct termios *tio; - struct environ environ; + struct environ *environ; int references; @@ -1164,8 +1167,6 @@ struct message_entry { }; /* Client connection. */ -struct tmuxproc; -struct tmuxpeer; struct client { struct tmuxpeer *peer; @@ -1177,7 +1178,7 @@ struct client { struct timeval creation_time; struct timeval activity_time; - struct environ environ; + struct environ *environ; char *title; int cwd; @@ -1404,7 +1405,7 @@ struct options_table_entry { extern struct options *global_options; extern struct options *global_s_options; extern struct options *global_w_options; -extern struct environ global_environ; +extern struct environ *global_environ; extern char *shell_cmd; extern int debug_level; extern time_t start_time; @@ -1541,10 +1542,10 @@ void job_free(struct job *); void job_died(struct job *, int); /* environ.c */ -int environ_cmp(struct environ_entry *, struct environ_entry *); -RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); -void environ_init(struct environ *); +struct environ *environ_create(void); void environ_free(struct environ *); +struct environ_entry *environ_first(struct environ *); +struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); void environ_set(struct environ *, const char *, const char *); From 45f3cea263d1f99912cd6b353c91ccb872c26a71 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 29 Oct 2015 09:35:31 +0000 Subject: [PATCH 379/703] Break version check into a separate function, and limit version to 8 bits. --- proc.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/proc.c b/proc.c index 413b2125..12e50e2c 100644 --- a/proc.c +++ b/proc.c @@ -49,7 +49,8 @@ struct tmuxpeer { void *arg; }; -static void proc_update_event(struct tmuxpeer *); +static int peer_check_version(struct tmuxpeer *, struct imsg *); +static void proc_update_event(struct tmuxpeer *); static void proc_event_cb(unused int fd, short events, void *arg) @@ -57,7 +58,6 @@ proc_event_cb(unused int fd, short events, void *arg) struct tmuxpeer *peer = arg; ssize_t n; struct imsg imsg; - int v; if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { if ((n = imsg_read(&peer->ibuf)) == -1 || n == 0) { @@ -73,14 +73,7 @@ proc_event_cb(unused int fd, short events, void *arg) break; log_debug("peer %p message %d", peer, imsg.hdr.type); - v = imsg.hdr.peerid; - if (imsg.hdr.type != MSG_VERSION && - v != PROTOCOL_VERSION) { - log_debug("peer %p bad version %d", peer, v); - - proc_send(peer, MSG_VERSION, -1, NULL, 0); - peer->flags |= PEER_BAD; - + if (peer_check_version(peer, &imsg) != 0) { if (imsg.fd != -1) close(imsg.fd); imsg_free(&imsg); @@ -115,6 +108,24 @@ proc_signal_cb(int signo, unused short events, void *arg) tp->signalcb(signo); } +static int +peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) +{ + int version; + + version = imsg->hdr.peerid & 0xff; + if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) { + log_debug("peer %p bad version %d", peer, version); + + proc_send(peer, MSG_VERSION, -1, NULL, 0); + peer->flags |= PEER_BAD; + + return (-1); + } + imsg->hdr.peerid >>= 8; + return (0); +} + static void proc_update_event(struct tmuxpeer *peer) { From 01defc9f4965bb174e1d1295754d5a8695683054 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 31 Oct 2015 08:13:58 +0000 Subject: [PATCH 380/703] Because pledge(2) does not allow us to pass directory file descriptors around, we can't use file descriptors for the working directory because we will be unable to pass it to a privileged process to tell it where to read or write files or spawn children. So move tmux back to using strings for the current working directory. We try to check it exists with access() when it is set but ultimately fall back to ~ if it fails at time of use (or / if that fails too). --- client.c | 6 ++++-- cmd-attach-session.c | 14 ++++++-------- cmd-if-shell.c | 4 ++-- cmd-load-buffer.c | 23 +++++++++++++---------- cmd-new-session.c | 39 ++++++++++++++++----------------------- cmd-new-window.c | 32 ++++++++++++++------------------ cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- cmd-run-shell.c | 4 ++-- cmd-save-buffer.c | 35 +++++++++++++++++------------------ cmd-split-window.c | 33 +++++++++++++++------------------ format.c | 2 +- job.c | 9 ++++++--- server-client.c | 14 +++++++++----- session.c | 10 +++++----- tmux.h | 20 ++++++++++---------- window-copy.c | 2 +- window.c | 24 +++++++++++++----------- 18 files changed, 136 insertions(+), 139 deletions(-) diff --git a/client.c b/client.c index 97382f63..008bd631 100644 --- a/client.c +++ b/client.c @@ -273,8 +273,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags) client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); /* Save these before pledge(). */ - if ((cwd = getcwd(path, sizeof path)) == NULL) - cwd = "/"; + if ((cwd = getcwd(path, sizeof path)) == NULL) { + if ((cwd = find_home()) == NULL) + cwd = "/"; + } if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; diff --git a/cmd-attach-session.c b/cmd-attach-session.c index c570358c..b339b890 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -51,9 +51,8 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, struct window_pane *wp = NULL; const char *update; char *cause; - int fd; struct format_tree *ft; - char *cp; + char *cwd; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); @@ -97,18 +96,17 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); - cp = format_expand(ft, cflag); + cwd = format_expand(ft, cflag); format_free(ft); - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { + if (access(cwd, X_OK) != 0) { + free((void *)cwd); cmdq_error(cmdq, "bad working directory: %s", strerror(errno)); return (CMD_RETURN_ERROR); } - close(s->cwd); - s->cwd = fd; + free((void *)s->cwd); + s->cwd = cwd; } if (c->session != NULL) { diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 0271fdea..a9c84261 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -66,7 +66,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window_pane *wp = NULL; struct format_tree *ft; - int cwd; + const char *cwd; if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); @@ -83,7 +83,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) else if (s != NULL) cwd = s->cwd; else - cwd = -1; + cwd = NULL; } ft = format_create(); diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 897807d0..c55e6c11 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -49,10 +49,10 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; FILE *f; - const char *path, *bufname; - char *pdata, *new_pdata, *cause; + const char *path, *bufname, *cwd; + char *pdata, *new_pdata, *cause, *file, resolved[PATH_MAX]; size_t psize; - int ch, error, cwd, fd; + int ch, error; bufname = NULL; if (args_has(args, 'b')) @@ -75,13 +75,16 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else if ((s = cmd_find_current(cmdq)) != NULL) cwd = s->cwd; else - cwd = AT_FDCWD; + cwd = "."; - if ((fd = openat(cwd, path, O_RDONLY)) == -1 || - (f = fdopen(fd, "rb")) == NULL) { - if (fd != -1) - close(fd); - cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL) + f = NULL; + else + f = fopen(resolved, "rb"); + free(file); + if (f == NULL) { + cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); return (CMD_RETURN_ERROR); } @@ -97,7 +100,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) pdata[psize++] = ch; } if (ferror(f)) { - cmdq_error(cmdq, "%s: read error", path); + cmdq_error(cmdq, "%s: read error", resolved); goto error; } if (pdata != NULL) diff --git a/cmd-new-session.c b/cmd-new-session.c index 7b637bc6..90bb2e0e 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -63,10 +63,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct environ *env; struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; - const char *path; + const char *path, *cwd, *to_free; char **argv, *cmd, *cause, *cp; - int detached, already_attached, idx, cwd, fd = -1; - int argc; + int detached, already_attached, idx, argc; u_int sx, sy; struct format_tree *ft; struct environ_entry *envent; @@ -118,32 +117,26 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) already_attached = 1; /* Get the new session working directory. */ + to_free = NULL; if (args_has(args, 'c')) { ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, NULL); - cp = format_expand(ft, args_get(args, 'c')); + to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else - free(cp); - cwd = fd; + if (access(cwd, X_OK) != 0) { + free((void *)cwd); + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } } else if (c != NULL && c->session == NULL) cwd = c->cwd; else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) cwd = c0->session->cwd; - else { - fd = open(".", O_RDONLY); - cwd = fd; - } + else + cwd = "."; /* * If this is a new client, check for nesting and save the termios @@ -311,12 +304,12 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (!detached) cmdq->client_exit = 0; - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_ERROR); } diff --git a/cmd-new-window.c b/cmd-new-window.c index 70e70c68..a5085fff 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -49,9 +49,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct session *s; struct winlink *wl; - const char *cmd, *path, *template; + const char *cmd, *path, *template, *cwd, *to_free; char **argv, *cause, *cp; - int argc, idx, detached, cwd, fd = -1; + int argc, idx, detached; struct format_tree *ft; struct environ_entry *envent; @@ -92,24 +92,20 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; + to_free = NULL; if (args_has(args, 'c')) { ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); - cp = format_expand(ft, args_get(args, 'c')); + cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else - free(cp); - cwd = fd; + if (access(cwd, X_OK) != 0) { + free((void *)cwd); + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else @@ -165,12 +161,12 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) format_free(ft); } - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_ERROR); } diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 864b45b8..a50b9d06 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -81,7 +81,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env, + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 8d8332b1..93af1b68 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -84,7 +84,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) if (envent != NULL) path = envent->value; - if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, -1, env, + if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 17ac0f76..def3ef01 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -80,7 +80,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct winlink *wl = NULL; struct window_pane *wp = NULL; struct format_tree *ft; - int cwd; + const char *cwd; if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); @@ -97,7 +97,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) else if (s != NULL) cwd = s->cwd; else - cwd = -1; + cwd = NULL; } ft = format_create(); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 5916cd4d..dca2c8ef 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -57,10 +57,10 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c = cmdq->client; struct session *s; struct paste_buffer *pb; - const char *path, *bufname, *bufdata, *start, *end; - char *msg; + const char *path, *bufname, *bufdata, *start, *end, *cwd; + const char *flags; + char *msg, *file, resolved[PATH_MAX]; size_t size, used, msglen, bufsize; - int cwd, fd; FILE *f; if (!args_has(args, 'b')) { @@ -97,26 +97,25 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else if ((s = cmd_find_current(cmdq)) != NULL) cwd = s->cwd; else - cwd = AT_FDCWD; + cwd = "."; - f = NULL; - if (args_has(self->args, 'a')) { - fd = openat(cwd, path, O_CREAT|O_RDWR|O_APPEND, 0600); - if (fd != -1) - f = fdopen(fd, "ab"); - } else { - fd = openat(cwd, path, O_CREAT|O_RDWR|O_TRUNC, 0600); - if (fd != -1) - f = fdopen(fd, "wb"); - } + flags = "wb"; + if (args_has(self->args, 'a')) + flags = "ab"; + + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL) + f = NULL; + else + f = fopen(resolved, flags); + free(file); if (f == NULL) { - if (fd != -1) - close(fd); - cmdq_error(cmdq, "%s: %s", path, strerror(errno)); + cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); return (CMD_RETURN_ERROR); } + if (fwrite(bufdata, 1, bufsize, f) != bufsize) { - cmdq_error(cmdq, "%s: fwrite error", path); + cmdq_error(cmdq, "%s: write error", resolved); fclose(f); return (CMD_RETURN_ERROR); } diff --git a/cmd-split-window.c b/cmd-split-window.c index 75b8eeb6..6adb19a8 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -53,10 +53,10 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; struct window_pane *wp, *new_wp = NULL; struct environ *env; - const char *cmd, *path, *shell, *template; + const char *cmd, *path, *shell, *template, *cwd, *to_free; char **argv, *cause, *new_cause, *cp; u_int hlimit; - int argc, size, percentage, cwd, fd = -1; + int argc, size, percentage; enum layout_type type; struct layout_cell *lc; struct format_tree *ft; @@ -86,24 +86,20 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) argv = args->argv; } + to_free = NULL; if (args_has(args, 'c')) { ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); - cp = format_expand(ft, args_get(args, 'c')); + to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - if (cp != NULL && *cp != '\0') { - fd = open(cp, O_RDONLY|O_DIRECTORY); - free(cp); - if (fd == -1) { - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } - } else - free(cp); - cwd = fd; + if (access(cwd, X_OK) != 0) { + free((void *)cwd); + cmdq_error(cmdq, "bad working directory: %s", + strerror(errno)); + return (CMD_RETURN_ERROR); + } } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else @@ -188,8 +184,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) } notify_window_layout_changed(w); - if (fd != -1) - close(fd); + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_NORMAL); error: @@ -200,7 +196,8 @@ error: } cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); - if (fd != -1) - close(fd); + + if (to_free != NULL) + free((void *)to_free); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index 0d9fbc7f..847ba08a 100644 --- a/format.c +++ b/format.c @@ -239,7 +239,7 @@ format_job_get(struct format_tree *ft, const char *cmd) t = time(NULL); if (fj->job == NULL && ((ft->flags & FORMAT_FORCE) || fj->last != t)) { - fj->job = job_run(fj->cmd, NULL, -1, format_job_callback, + fj->job = job_run(fj->cmd, NULL, NULL, format_job_callback, NULL, fj); if (fj->job == NULL) { free(fj->out); diff --git a/job.c b/job.c index 8492fb52..0aee7b05 100644 --- a/job.c +++ b/job.c @@ -41,13 +41,14 @@ struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running, if it isn't already. */ struct job * -job_run(const char *cmd, struct session *s, int cwd, +job_run(const char *cmd, struct session *s, const char *cwd, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; struct environ *env; pid_t pid; int nullfd, out[2]; + const char *home; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); @@ -67,8 +68,10 @@ job_run(const char *cmd, struct session *s, int cwd, case 0: /* child */ clear_signals(1); - if (cwd != -1 && fchdir(cwd) != 0) - chdir("/"); + if (cwd == NULL || chdir(cwd) != 0) { + if ((home = find_home()) == NULL || chdir(home) != 0) + chdir("/"); + } environ_push(env); environ_free(env); diff --git a/server-client.c b/server-client.c index 7ae74c09..264e5e4a 100644 --- a/server-client.c +++ b/server-client.c @@ -98,7 +98,7 @@ server_client_create(int fd) c->environ = environ_create(); c->fd = -1; - c->cwd = -1; + c->cwd = NULL; c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; @@ -194,7 +194,7 @@ server_client_lost(struct client *c) screen_free(&c->status); free(c->title); - close(c->cwd); + free((void *)c->cwd); evtimer_del(&c->repeat_timer); @@ -1099,7 +1099,7 @@ error: void server_client_dispatch_identify(struct client *c, struct imsg *imsg) { - const char *data; + const char *data, *home; size_t datalen; int flags; @@ -1132,8 +1132,12 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) case MSG_IDENTIFY_CWD: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_CWD string"); - if ((c->cwd = open(data, O_RDONLY)) == -1) - c->cwd = open("/", O_RDONLY); + if (access(data, X_OK) == 0) + c->cwd = xstrdup(data); + else if ((home = find_home()) != NULL) + c->cwd = xstrdup(home); + else + c->cwd = xstrdup("/"); log_debug("client %p IDENTIFY_CWD %s", c, data); break; case MSG_IDENTIFY_STDIN: diff --git a/session.c b/session.c index 217b5272..cbaadcee 100644 --- a/session.c +++ b/session.c @@ -104,8 +104,8 @@ session_find_by_id(u_int id) /* Create a new session. */ struct session * session_create(const char *name, int argc, char **argv, const char *path, - int cwd, struct environ *env, struct termios *tio, int idx, u_int sx, - u_int sy, char **cause) + const char *cwd, struct environ *env, struct termios *tio, int idx, + u_int sx, u_int sy, char **cause) { struct session *s; struct winlink *wl; @@ -114,7 +114,7 @@ session_create(const char *name, int argc, char **argv, const char *path, s->references = 1; s->flags = 0; - s->cwd = dup(cwd); + s->cwd = xstrdup(cwd); s->curw = NULL; TAILQ_INIT(&s->lastw); @@ -224,7 +224,7 @@ session_destroy(struct session *s) winlink_remove(&s->windows, wl); } - close(s->cwd); + free((void *)s->cwd); session_unref(s); } @@ -315,7 +315,7 @@ session_previous_session(struct session *s) /* Create a new window on a session. */ struct winlink * session_new(struct session *s, const char *name, int argc, char **argv, - const char *path, int cwd, int idx, char **cause) + const char *path, const char *cwd, int idx, char **cause) { struct window *w; struct winlink *wl; diff --git a/tmux.h b/tmux.h index 9a55a1b1..f26f1c8c 100644 --- a/tmux.h +++ b/tmux.h @@ -829,7 +829,7 @@ struct window_pane { int argc; char **argv; char *shell; - int cwd; + const char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; @@ -976,7 +976,7 @@ struct session { u_int id; char *name; - int cwd; + const char *cwd; struct timeval creation_time; struct timeval last_attached_time; @@ -1181,7 +1181,7 @@ struct client { struct environ *environ; char *title; - int cwd; + const char *cwd; char *term; char *ttyname; @@ -1536,7 +1536,7 @@ int options_table_find(const char *, const struct options_table_entry **, /* job.c */ extern struct joblist all_jobs; -struct job *job_run(const char *, struct session *, int, +struct job *job_run(const char *, struct session *, const char *, void (*)(struct job *), void (*)(void *), void *); void job_free(struct job *); void job_died(struct job *, int); @@ -1982,8 +1982,8 @@ struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create1(u_int, u_int); struct window *window_create(const char *, int, char **, const char *, - const char *, int, struct environ *, struct termios *, - u_int, u_int, u_int, char **); + const char *, const char *, struct environ *, + struct termios *, u_int, u_int, u_int, char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); @@ -2010,7 +2010,7 @@ struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); int window_pane_spawn(struct window_pane *, int, char **, - const char *, const char *, int, struct environ *, + const char *, const char *, const char *, struct environ *, struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, @@ -2144,8 +2144,8 @@ struct session *session_find(const char *); struct session *session_find_by_id_str(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, int, char **, const char *, - int, struct environ *, struct termios *, int, u_int, - u_int, char **); + const char *, struct environ *, struct termios *, int, + u_int, u_int, char **); void session_destroy(struct session *); void session_unref(struct session *); int session_check_name(const char *); @@ -2153,7 +2153,7 @@ void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); struct winlink *session_new(struct session *, const char *, int, char **, - const char *, int, int, char **); + const char *, const char *, int, char **); struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); diff --git a/window-copy.c b/window-copy.c index 3ba27c80..c5c053bf 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1488,7 +1488,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); - job = job_run(expanded, sess, -1, NULL, NULL, NULL); + job = job_run(expanded, sess, NULL, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); free(expanded); diff --git a/window.c b/window.c index baaaa0f8..49408297 100644 --- a/window.c +++ b/window.c @@ -316,8 +316,8 @@ window_create1(u_int sx, u_int sy) struct window * window_create(const char *name, int argc, char **argv, const char *path, - const char *shell, int cwd, struct environ *env, struct termios *tio, - u_int sx, u_int sy, u_int hlimit, char **cause) + const char *shell, const char *cwd, struct environ *env, + struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { struct window *w; struct window_pane *wp; @@ -736,7 +736,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->argc = 0; wp->argv = NULL; wp->shell = NULL; - wp->cwd = -1; + wp->cwd = NULL; wp->fd = -1; wp->event = NULL; @@ -796,7 +796,7 @@ window_pane_destroy(struct window_pane *wp) RB_REMOVE(window_pane_tree, &all_window_panes, wp); - close(wp->cwd); + free((void *)wp->cwd); free(wp->shell); cmd_free_argv(wp->argc, wp->argv); free(wp); @@ -804,12 +804,12 @@ window_pane_destroy(struct window_pane *wp) int window_pane_spawn(struct window_pane *wp, int argc, char **argv, - const char *path, const char *shell, int cwd, struct environ *env, + const char *path, const char *shell, const char *cwd, struct environ *env, struct termios *tio, char **cause) { struct winsize ws; char *argv0, *cmd, **argvp, paneid[16]; - const char *ptr, *first; + const char *ptr, *first, *home; struct termios tio2; int i; @@ -826,9 +826,9 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, free(wp->shell); wp->shell = xstrdup(shell); } - if (cwd != -1) { - close(wp->cwd); - wp->cwd = dup(cwd); + if (cwd != NULL) { + free((void *)wp->cwd); + wp->cwd = xstrdup(cwd); } cmd = cmd_stringify_argv(wp->argc, wp->argv); @@ -847,8 +847,10 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, free(cmd); return (-1); case 0: - if (fchdir(wp->cwd) != 0) - chdir("/"); + if (chdir(wp->cwd) != 0) { + if ((home = find_home()) == NULL || chdir(home) != 0) + chdir("/"); + } if (tcgetattr(STDIN_FILENO, &tio2) != 0) fatal("tcgetattr failed"); From abb4e9e2fa68c38f71fedc6d1ce2e78fcaa59495 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 31 Oct 2015 13:12:03 +0000 Subject: [PATCH 381/703] The output log is only useful once and it means creating a file, so open it once at startup instead of in every call to tty_open. --- server-client.c | 1 - server.c | 2 ++ tmux.h | 3 +-- tty.c | 43 ++++++++++++++++++++----------------------- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/server-client.c b/server-client.c index 264e5e4a..f1e2deff 100644 --- a/server-client.c +++ b/server-client.c @@ -1178,7 +1178,6 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) proc_send(c->peer, MSG_STDIN, -1, NULL, 0); c->tty.fd = -1; - c->tty.log_fd = -1; close(c->fd); c->fd = -1; diff --git a/server.c b/server.c index 2af2c442..bc3fa51d 100644 --- a/server.c +++ b/server.c @@ -173,6 +173,8 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); + if (debug_level > 3) + tty_create_log(); if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); diff --git a/tmux.h b/tmux.h index f26f1c8c..25b51350 100644 --- a/tmux.h +++ b/tmux.h @@ -1102,8 +1102,6 @@ struct tty { int fd; struct bufferevent *event; - int log_fd; - struct termios tio; struct grid_cell cell; @@ -1555,6 +1553,7 @@ void environ_update(const char *, struct environ *, struct environ *); void environ_push(struct environ *); /* tty.c */ +void tty_create_log(void); void tty_init_termios(int, struct termios *, struct bufferevent *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, diff --git a/tty.c b/tty.c index 6b6343c8..c0ae79bb 100644 --- a/tty.c +++ b/tty.c @@ -31,6 +31,8 @@ #include "tmux.h" +static int tty_log_fd = -1; + void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); @@ -59,6 +61,18 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) +void +tty_create_log(void) +{ + char name[64]; + + xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid()); + + tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); +} + int tty_init(struct tty *tty, struct client *c, int fd, char *term) { @@ -68,7 +82,6 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) return (-1); memset(tty, 0, sizeof *tty); - tty->log_fd = -1; if (term == NULL || *term == '\0') tty->termname = xstrdup("unknown"); @@ -139,17 +152,6 @@ tty_set_size(struct tty *tty, u_int sx, u_int sy) { int tty_open(struct tty *tty, char **cause) { - char out[64]; - int fd; - - if (debug_level > 3) { - xsnprintf(out, sizeof out, "tmux-out-%ld.log", (long) getpid()); - fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - fatal("fcntl failed"); - tty->log_fd = fd; - } - tty->term = tty_term_find(tty->termname, tty->fd, cause); if (tty->term == NULL) { tty_close(tty); @@ -308,11 +310,6 @@ tty_stop_tty(struct tty *tty) void tty_close(struct tty *tty) { - if (tty->log_fd != -1) { - close(tty->log_fd); - tty->log_fd = -1; - } - if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); tty_stop_tty(tty); @@ -406,8 +403,8 @@ tty_puts(struct tty *tty, const char *s) return; bufferevent_write(tty->event, s, strlen(s)); - if (tty->log_fd != -1) - write(tty->log_fd, s, strlen(s)); + if (tty_log_fd != -1) + write(tty_log_fd, s, strlen(s)); } void @@ -438,16 +435,16 @@ tty_putc(struct tty *tty, u_char ch) tty->cx++; } - if (tty->log_fd != -1) - write(tty->log_fd, &ch, 1); + if (tty_log_fd != -1) + write(tty_log_fd, &ch, 1); } void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { bufferevent_write(tty->event, buf, len); - if (tty->log_fd != -1) - write(tty->log_fd, buf, len); + if (tty_log_fd != -1) + write(tty_log_fd, buf, len); tty->cx += width; } From b0a99e85b66c8d74022525908a9584ce1b002da4 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 31 Oct 2015 13:43:38 +0000 Subject: [PATCH 382/703] Don't shift version out of peerid, it is needed later. --- client.c | 2 +- proc.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client.c b/client.c index 008bd631..bc934d33 100644 --- a/client.c +++ b/client.c @@ -596,7 +596,7 @@ client_dispatch_wait(struct imsg *imsg) fprintf(stderr, "protocol version mismatch " "(client %d, server %u)\n", PROTOCOL_VERSION, - imsg->hdr.peerid); + imsg->hdr.peerid & 0xff); client_exitval = 1; proc_exit(client_proc); break; diff --git a/proc.c b/proc.c index 12e50e2c..6b84fc00 100644 --- a/proc.c +++ b/proc.c @@ -122,7 +122,6 @@ peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) return (-1); } - imsg->hdr.peerid >>= 8; return (0); } From ba7fb49fb9b972a03547381783abe91be3fcfa37 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 31 Oct 2015 14:51:15 +0000 Subject: [PATCH 383/703] Fall back silently to ~ or / rather than checking -c with access(), this was the old behaviour. --- cmd-attach-session.c | 6 ------ cmd-new-session.c | 7 ------- cmd-new-window.c | 7 ------- cmd-split-window.c | 7 ------- 4 files changed, 27 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index b339b890..e5589277 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -99,12 +99,6 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, cwd = format_expand(ft, cflag); format_free(ft); - if (access(cwd, X_OK) != 0) { - free((void *)cwd); - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } free((void *)s->cwd); s->cwd = cwd; } diff --git a/cmd-new-session.c b/cmd-new-session.c index 90bb2e0e..65dc6cf5 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -124,13 +124,6 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - - if (access(cwd, X_OK) != 0) { - free((void *)cwd); - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } } else if (c != NULL && c->session == NULL) cwd = c->cwd; else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) diff --git a/cmd-new-window.c b/cmd-new-window.c index a5085fff..fb89e24f 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -99,13 +99,6 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) NULL); cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - - if (access(cwd, X_OK) != 0) { - free((void *)cwd); - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else diff --git a/cmd-split-window.c b/cmd-split-window.c index 6adb19a8..fb766d3b 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -93,13 +93,6 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - - if (access(cwd, X_OK) != 0) { - free((void *)cwd); - cmdq_error(cmdq, "bad working directory: %s", - strerror(errno)); - return (CMD_RETURN_ERROR); - } } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else From 455284f1c02af1a4e0d68555e285563ce7fffb8f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 3 Nov 2015 15:07:36 +0000 Subject: [PATCH 384/703] Detach the client we are looping over, from Thomas Adam. --- cmd-attach-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index e5589277..cb1f589e 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -139,7 +139,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c->peer, MSG_DETACH, s->name); + proc_send_s(c_loop->peer, MSG_DETACH, s->name); } } From 5577535891e709f87f88311a1adef55dd6c6de85 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 5 Nov 2015 11:05:30 +0000 Subject: [PATCH 385/703] Pass through right click if mouse is on, from Patrick Palka. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index e12dd62c..83b94ac1 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -226,7 +226,7 @@ key_bindings_init(void) "bind -n WheelDownStatus next-window", "bind -n WheelUpStatus previous-window", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", - "bind -n MouseDown3Pane select-pane -mt=", + "bind -n MouseDown3Pane if-shell -Ft= '#{mouse_any_flag}' 'select-pane -t=; send-keys -M' 'select-pane -mt='", "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", }; u_int i; From e9b58d9de4d0161e9446017c5681298c776089a5 Mon Sep 17 00:00:00 2001 From: schwarze Date: Thu, 5 Nov 2015 16:44:25 +0000 Subject: [PATCH 386/703] Update the internal wcwidth(3) table of tmux(1) to match the data in /usr/src/share/locale/ctype/en_US.UTF-8.src, with one single exception: Keep U+00AD SOFT HYPHEN at width 1 rather than moving it to width 0, a tradition already observed in the old https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c . While here, manually rebalance the btree for optimal lookup speed. OK nicm@ --- utf8.c | 455 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 303 insertions(+), 152 deletions(-) diff --git a/utf8.c b/utf8.c index 12a0b29e..9a06c186 100644 --- a/utf8.c +++ b/utf8.c @@ -34,164 +34,315 @@ struct utf8_width_entry { struct utf8_width_entry *right; }; -/* Random order. Not optimal but it'll do for now... */ +/* Sorted, then repeatedly split in the middle to balance the tree. */ struct utf8_width_entry utf8_width_table[] = { - { 0x00951, 0x00954, 0, NULL, NULL }, - { 0x00ccc, 0x00ccd, 0, NULL, NULL }, - { 0x0fff9, 0x0fffb, 0, NULL, NULL }, - { 0x20000, 0x2fffd, 2, NULL, NULL }, - { 0x00ebb, 0x00ebc, 0, NULL, NULL }, - { 0x01932, 0x01932, 0, NULL, NULL }, - { 0x0070f, 0x0070f, 0, NULL, NULL }, - { 0x00a70, 0x00a71, 0, NULL, NULL }, - { 0x02329, 0x02329, 2, NULL, NULL }, - { 0x00acd, 0x00acd, 0, NULL, NULL }, - { 0x00ac7, 0x00ac8, 0, NULL, NULL }, - { 0x00a3c, 0x00a3c, 0, NULL, NULL }, - { 0x009cd, 0x009cd, 0, NULL, NULL }, - { 0x00591, 0x005bd, 0, NULL, NULL }, - { 0x01058, 0x01059, 0, NULL, NULL }, - { 0x0ffe0, 0x0ffe6, 2, NULL, NULL }, - { 0x01100, 0x0115f, 2, NULL, NULL }, - { 0x0fe20, 0x0fe23, 0, NULL, NULL }, - { 0x0302a, 0x0302f, 0, NULL, NULL }, - { 0x01772, 0x01773, 0, NULL, NULL }, - { 0x005bf, 0x005bf, 0, NULL, NULL }, - { 0x006ea, 0x006ed, 0, NULL, NULL }, - { 0x00bc0, 0x00bc0, 0, NULL, NULL }, - { 0x00962, 0x00963, 0, NULL, NULL }, - { 0x01732, 0x01734, 0, NULL, NULL }, - { 0x00d41, 0x00d43, 0, NULL, NULL }, - { 0x01b42, 0x01b42, 0, NULL, NULL }, - { 0x00a41, 0x00a42, 0, NULL, NULL }, - { 0x00eb4, 0x00eb9, 0, NULL, NULL }, - { 0x00b01, 0x00b01, 0, NULL, NULL }, - { 0x00e34, 0x00e3a, 0, NULL, NULL }, - { 0x03040, 0x03098, 2, NULL, NULL }, - { 0x0093c, 0x0093c, 0, NULL, NULL }, - { 0x00c4a, 0x00c4d, 0, NULL, NULL }, - { 0x01032, 0x01032, 0, NULL, NULL }, - { 0x00f37, 0x00f37, 0, NULL, NULL }, - { 0x00901, 0x00902, 0, NULL, NULL }, - { 0x00cbf, 0x00cbf, 0, NULL, NULL }, - { 0x0a806, 0x0a806, 0, NULL, NULL }, - { 0x00dd2, 0x00dd4, 0, NULL, NULL }, - { 0x00f71, 0x00f7e, 0, NULL, NULL }, - { 0x01752, 0x01753, 0, NULL, NULL }, - { 0x1d242, 0x1d244, 0, NULL, NULL }, - { 0x005c1, 0x005c2, 0, NULL, NULL }, - { 0x0309b, 0x0a4cf, 2, NULL, NULL }, - { 0xe0100, 0xe01ef, 0, NULL, NULL }, - { 0x017dd, 0x017dd, 0, NULL, NULL }, - { 0x00600, 0x00603, 0, NULL, NULL }, - { 0x009e2, 0x009e3, 0, NULL, NULL }, - { 0x00cc6, 0x00cc6, 0, NULL, NULL }, - { 0x0a80b, 0x0a80b, 0, NULL, NULL }, - { 0x01712, 0x01714, 0, NULL, NULL }, - { 0x00b3c, 0x00b3c, 0, NULL, NULL }, - { 0x01b00, 0x01b03, 0, NULL, NULL }, - { 0x007eb, 0x007f3, 0, NULL, NULL }, - { 0xe0001, 0xe0001, 0, NULL, NULL }, - { 0x1d185, 0x1d18b, 0, NULL, NULL }, - { 0x0feff, 0x0feff, 0, NULL, NULL }, - { 0x01b36, 0x01b3a, 0, NULL, NULL }, - { 0x01920, 0x01922, 0, NULL, NULL }, - { 0x00670, 0x00670, 0, NULL, NULL }, - { 0x00f90, 0x00f97, 0, NULL, NULL }, - { 0x01927, 0x01928, 0, NULL, NULL }, - { 0x0200b, 0x0200f, 0, NULL, NULL }, - { 0x0ff00, 0x0ff60, 2, NULL, NULL }, - { 0x0f900, 0x0faff, 2, NULL, NULL }, - { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, - { 0x00cbc, 0x00cbc, 0, NULL, NULL }, - { 0x00eb1, 0x00eb1, 0, NULL, NULL }, - { 0x10a38, 0x10a3a, 0, NULL, NULL }, - { 0x007a6, 0x007b0, 0, NULL, NULL }, - { 0x00f80, 0x00f84, 0, NULL, NULL }, + { 0x00b41, 0x00b44, 0, NULL, NULL }, + { 0x008e4, 0x00902, 0, NULL, NULL }, + { 0x006d6, 0x006dd, 0, NULL, NULL }, { 0x005c4, 0x005c5, 0, NULL, NULL }, - { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, - { 0x017c9, 0x017d3, 0, NULL, NULL }, - { 0x00d4d, 0x00d4d, 0, NULL, NULL }, - { 0x1d167, 0x1d169, 0, NULL, NULL }, - { 0x01036, 0x01037, 0, NULL, NULL }, - { 0xe0020, 0xe007f, 0, NULL, NULL }, - { 0x00f35, 0x00f35, 0, NULL, NULL }, - { 0x017b4, 0x017b5, 0, NULL, NULL }, - { 0x0206a, 0x0206f, 0, NULL, NULL }, - { 0x00c46, 0x00c48, 0, NULL, NULL }, - { 0x01939, 0x0193b, 0, NULL, NULL }, - { 0x01dc0, 0x01dca, 0, NULL, NULL }, - { 0x10a0c, 0x10a0f, 0, NULL, NULL }, - { 0x0102d, 0x01030, 0, NULL, NULL }, - { 0x017c6, 0x017c6, 0, NULL, NULL }, - { 0x00ec8, 0x00ecd, 0, NULL, NULL }, - { 0x00b41, 0x00b43, 0, NULL, NULL }, - { 0x017b7, 0x017bd, 0, NULL, NULL }, - { 0x1d173, 0x1d182, 0, NULL, NULL }, - { 0x00a47, 0x00a48, 0, NULL, NULL }, - { 0x0232a, 0x0232a, 2, NULL, NULL }, - { 0x01b3c, 0x01b3c, 0, NULL, NULL }, - { 0x10a01, 0x10a03, 0, NULL, NULL }, - { 0x00ae2, 0x00ae3, 0, NULL, NULL }, - { 0x00483, 0x00486, 0, NULL, NULL }, - { 0x0135f, 0x0135f, 0, NULL, NULL }, - { 0x01a17, 0x01a18, 0, NULL, NULL }, - { 0x006e7, 0x006e8, 0, NULL, NULL }, - { 0x03099, 0x0309a, 0, NULL, NULL }, - { 0x00b4d, 0x00b4d, 0, NULL, NULL }, - { 0x00ce2, 0x00ce3, 0, NULL, NULL }, - { 0x00bcd, 0x00bcd, 0, NULL, NULL }, - { 0x00610, 0x00615, 0, NULL, NULL }, - { 0x00f99, 0x00fbc, 0, NULL, NULL }, - { 0x009c1, 0x009c4, 0, NULL, NULL }, - { 0x00730, 0x0074a, 0, NULL, NULL }, + { 0x00591, 0x005bd, 0, NULL, NULL }, { 0x00300, 0x0036f, 0, NULL, NULL }, - { 0x03030, 0x0303e, 2, NULL, NULL }, - { 0x01b34, 0x01b34, 0, NULL, NULL }, - { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, - { 0x00dca, 0x00dca, 0, NULL, NULL }, - { 0x006d6, 0x006e4, 0, NULL, NULL }, - { 0x00f86, 0x00f87, 0, NULL, NULL }, - { 0x00b3f, 0x00b3f, 0, NULL, NULL }, - { 0x0fe30, 0x0fe6f, 2, NULL, NULL }, - { 0x01039, 0x01039, 0, NULL, NULL }, - { 0x0094d, 0x0094d, 0, NULL, NULL }, - { 0x00c55, 0x00c56, 0, NULL, NULL }, - { 0x00488, 0x00489, 0, NULL, NULL }, - { 0x00e47, 0x00e4e, 0, NULL, NULL }, - { 0x00a81, 0x00a82, 0, NULL, NULL }, - { 0x00ac1, 0x00ac5, 0, NULL, NULL }, - { 0x0202a, 0x0202e, 0, NULL, NULL }, - { 0x00dd6, 0x00dd6, 0, NULL, NULL }, - { 0x018a9, 0x018a9, 0, NULL, NULL }, - { 0x0064b, 0x0065e, 0, NULL, NULL }, - { 0x00abc, 0x00abc, 0, NULL, NULL }, - { 0x00b82, 0x00b82, 0, NULL, NULL }, - { 0x00f39, 0x00f39, 0, NULL, NULL }, - { 0x020d0, 0x020ef, 0, NULL, NULL }, - { 0x01dfe, 0x01dff, 0, NULL, NULL }, - { 0x30000, 0x3fffd, 2, NULL, NULL }, - { 0x00711, 0x00711, 0, NULL, NULL }, - { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, - { 0x0180b, 0x0180d, 0, NULL, NULL }, - { 0x10a3f, 0x10a3f, 0, NULL, NULL }, - { 0x00981, 0x00981, 0, NULL, NULL }, - { 0x0a825, 0x0a826, 0, NULL, NULL }, - { 0x00941, 0x00948, 0, NULL, NULL }, - { 0x01b6b, 0x01b73, 0, NULL, NULL }, - { 0x00e31, 0x00e31, 0, NULL, NULL }, - { 0x0fe10, 0x0fe19, 2, NULL, NULL }, - { 0x00a01, 0x00a02, 0, NULL, NULL }, - { 0x00a4b, 0x00a4d, 0, NULL, NULL }, - { 0x00f18, 0x00f19, 0, NULL, NULL }, - { 0x00fc6, 0x00fc6, 0, NULL, NULL }, - { 0x02e80, 0x03029, 2, NULL, NULL }, - { 0x00b56, 0x00b56, 0, NULL, NULL }, - { 0x009bc, 0x009bc, 0, NULL, NULL }, + { 0x00483, 0x00489, 0, NULL, NULL }, + { 0x005bf, 0x005bf, 0, NULL, NULL }, + { 0x005c1, 0x005c2, 0, NULL, NULL }, + { 0x00610, 0x0061a, 0, NULL, NULL }, + { 0x00600, 0x00605, 0, NULL, NULL }, { 0x005c7, 0x005c7, 0, NULL, NULL }, - { 0x02060, 0x02063, 0, NULL, NULL }, + { 0x0064b, 0x0065f, 0, NULL, NULL }, + { 0x0061c, 0x0061c, 0, NULL, NULL }, + { 0x00670, 0x00670, 0, NULL, NULL }, + { 0x007a6, 0x007b0, 0, NULL, NULL }, + { 0x006ea, 0x006ed, 0, NULL, NULL }, + { 0x006df, 0x006e4, 0, NULL, NULL }, + { 0x006e7, 0x006e8, 0, NULL, NULL }, + { 0x00711, 0x00711, 0, NULL, NULL }, + { 0x0070f, 0x0070f, 0, NULL, NULL }, + { 0x00730, 0x0074a, 0, NULL, NULL }, + { 0x0081b, 0x00823, 0, NULL, NULL }, + { 0x007eb, 0x007f3, 0, NULL, NULL }, + { 0x00816, 0x00819, 0, NULL, NULL }, + { 0x00829, 0x0082d, 0, NULL, NULL }, + { 0x00825, 0x00827, 0, NULL, NULL }, + { 0x00859, 0x0085b, 0, NULL, NULL }, + { 0x00a41, 0x00a42, 0, NULL, NULL }, + { 0x00981, 0x00981, 0, NULL, NULL }, + { 0x00941, 0x00948, 0, NULL, NULL }, + { 0x0093a, 0x0093a, 0, NULL, NULL }, + { 0x0093c, 0x0093c, 0, NULL, NULL }, + { 0x00951, 0x00957, 0, NULL, NULL }, + { 0x0094d, 0x0094d, 0, NULL, NULL }, + { 0x00962, 0x00963, 0, NULL, NULL }, + { 0x009e2, 0x009e3, 0, NULL, NULL }, + { 0x009c1, 0x009c4, 0, NULL, NULL }, + { 0x009bc, 0x009bc, 0, NULL, NULL }, + { 0x009cd, 0x009cd, 0, NULL, NULL }, + { 0x00a01, 0x00a02, 0, NULL, NULL }, + { 0x00a3c, 0x00a3c, 0, NULL, NULL }, + { 0x00ac1, 0x00ac5, 0, NULL, NULL }, + { 0x00a70, 0x00a71, 0, NULL, NULL }, + { 0x00a4b, 0x00a4d, 0, NULL, NULL }, + { 0x00a47, 0x00a48, 0, NULL, NULL }, + { 0x00a51, 0x00a51, 0, NULL, NULL }, + { 0x00a81, 0x00a82, 0, NULL, NULL }, + { 0x00a75, 0x00a75, 0, NULL, NULL }, + { 0x00abc, 0x00abc, 0, NULL, NULL }, + { 0x00ae2, 0x00ae3, 0, NULL, NULL }, + { 0x00ac7, 0x00ac8, 0, NULL, NULL }, + { 0x00acd, 0x00acd, 0, NULL, NULL }, + { 0x00b3c, 0x00b3c, 0, NULL, NULL }, + { 0x00b01, 0x00b01, 0, NULL, NULL }, + { 0x00b3f, 0x00b3f, 0, NULL, NULL }, + { 0x03190, 0x031ba, 2, NULL, NULL }, + { 0x017c9, 0x017d3, 0, NULL, NULL }, + { 0x00ec8, 0x00ecd, 0, NULL, NULL }, + { 0x00cc6, 0x00cc6, 0, NULL, NULL }, { 0x00c3e, 0x00c40, 0, NULL, NULL }, + { 0x00b82, 0x00b82, 0, NULL, NULL }, + { 0x00b56, 0x00b56, 0, NULL, NULL }, + { 0x00b4d, 0x00b4d, 0, NULL, NULL }, + { 0x00b62, 0x00b63, 0, NULL, NULL }, + { 0x00bcd, 0x00bcd, 0, NULL, NULL }, + { 0x00bc0, 0x00bc0, 0, NULL, NULL }, + { 0x00c00, 0x00c00, 0, NULL, NULL }, + { 0x00c62, 0x00c63, 0, NULL, NULL }, + { 0x00c4a, 0x00c4d, 0, NULL, NULL }, + { 0x00c46, 0x00c48, 0, NULL, NULL }, + { 0x00c55, 0x00c56, 0, NULL, NULL }, + { 0x00cbc, 0x00cbc, 0, NULL, NULL }, + { 0x00c81, 0x00c81, 0, NULL, NULL }, + { 0x00cbf, 0x00cbf, 0, NULL, NULL }, + { 0x00dd2, 0x00dd4, 0, NULL, NULL }, + { 0x00d41, 0x00d44, 0, NULL, NULL }, + { 0x00ce2, 0x00ce3, 0, NULL, NULL }, + { 0x00ccc, 0x00ccd, 0, NULL, NULL }, + { 0x00d01, 0x00d01, 0, NULL, NULL }, + { 0x00d62, 0x00d63, 0, NULL, NULL }, + { 0x00d4d, 0x00d4d, 0, NULL, NULL }, + { 0x00dca, 0x00dca, 0, NULL, NULL }, + { 0x00e47, 0x00e4e, 0, NULL, NULL }, + { 0x00e31, 0x00e31, 0, NULL, NULL }, + { 0x00dd6, 0x00dd6, 0, NULL, NULL }, + { 0x00e34, 0x00e3a, 0, NULL, NULL }, + { 0x00eb4, 0x00eb9, 0, NULL, NULL }, + { 0x00eb1, 0x00eb1, 0, NULL, NULL }, + { 0x00ebb, 0x00ebc, 0, NULL, NULL }, + { 0x0105e, 0x01060, 0, NULL, NULL }, + { 0x00f8d, 0x00f97, 0, NULL, NULL }, + { 0x00f39, 0x00f39, 0, NULL, NULL }, + { 0x00f35, 0x00f35, 0, NULL, NULL }, + { 0x00f18, 0x00f19, 0, NULL, NULL }, + { 0x00f37, 0x00f37, 0, NULL, NULL }, + { 0x00f80, 0x00f84, 0, NULL, NULL }, + { 0x00f71, 0x00f7e, 0, NULL, NULL }, + { 0x00f86, 0x00f87, 0, NULL, NULL }, + { 0x01032, 0x01037, 0, NULL, NULL }, + { 0x00fc6, 0x00fc6, 0, NULL, NULL }, + { 0x00f99, 0x00fbc, 0, NULL, NULL }, + { 0x0102d, 0x01030, 0, NULL, NULL }, + { 0x0103d, 0x0103e, 0, NULL, NULL }, + { 0x01039, 0x0103a, 0, NULL, NULL }, + { 0x01058, 0x01059, 0, NULL, NULL }, + { 0x0135d, 0x0135f, 0, NULL, NULL }, + { 0x01085, 0x01086, 0, NULL, NULL }, + { 0x01071, 0x01074, 0, NULL, NULL }, + { 0x01082, 0x01082, 0, NULL, NULL }, + { 0x0109d, 0x0109d, 0, NULL, NULL }, + { 0x0108d, 0x0108d, 0, NULL, NULL }, + { 0x01100, 0x011ff, 2, NULL, NULL }, + { 0x01772, 0x01773, 0, NULL, NULL }, + { 0x01732, 0x01734, 0, NULL, NULL }, + { 0x01712, 0x01714, 0, NULL, NULL }, + { 0x01752, 0x01753, 0, NULL, NULL }, + { 0x017b7, 0x017bd, 0, NULL, NULL }, + { 0x017b4, 0x017b5, 0, NULL, NULL }, + { 0x017c6, 0x017c6, 0, NULL, NULL }, + { 0x01c2c, 0x01c33, 0, NULL, NULL }, + { 0x01a7f, 0x01a7f, 0, NULL, NULL }, + { 0x01a17, 0x01a18, 0, NULL, NULL }, + { 0x01920, 0x01922, 0, NULL, NULL }, + { 0x0180b, 0x0180e, 0, NULL, NULL }, + { 0x017dd, 0x017dd, 0, NULL, NULL }, + { 0x018a9, 0x018a9, 0, NULL, NULL }, + { 0x01932, 0x01932, 0, NULL, NULL }, + { 0x01927, 0x01928, 0, NULL, NULL }, + { 0x01939, 0x0193b, 0, NULL, NULL }, + { 0x01a60, 0x01a60, 0, NULL, NULL }, + { 0x01a56, 0x01a56, 0, NULL, NULL }, + { 0x01a1b, 0x01a1b, 0, NULL, NULL }, + { 0x01a58, 0x01a5e, 0, NULL, NULL }, + { 0x01a65, 0x01a6c, 0, NULL, NULL }, + { 0x01a62, 0x01a62, 0, NULL, NULL }, + { 0x01a73, 0x01a7c, 0, NULL, NULL }, + { 0x01b80, 0x01b81, 0, NULL, NULL }, + { 0x01b36, 0x01b3a, 0, NULL, NULL }, + { 0x01b00, 0x01b03, 0, NULL, NULL }, + { 0x01ab0, 0x01abe, 0, NULL, NULL }, + { 0x01b34, 0x01b34, 0, NULL, NULL }, + { 0x01b42, 0x01b42, 0, NULL, NULL }, + { 0x01b3c, 0x01b3c, 0, NULL, NULL }, + { 0x01b6b, 0x01b73, 0, NULL, NULL }, + { 0x01be6, 0x01be6, 0, NULL, NULL }, + { 0x01ba8, 0x01ba9, 0, NULL, NULL }, + { 0x01ba2, 0x01ba5, 0, NULL, NULL }, + { 0x01bab, 0x01bad, 0, NULL, NULL }, + { 0x01bed, 0x01bed, 0, NULL, NULL }, + { 0x01be8, 0x01be9, 0, NULL, NULL }, + { 0x01bef, 0x01bf1, 0, NULL, NULL }, + { 0x02329, 0x0232a, 2, NULL, NULL }, + { 0x01dc0, 0x01df5, 0, NULL, NULL }, + { 0x01ce2, 0x01ce8, 0, NULL, NULL }, + { 0x01cd0, 0x01cd2, 0, NULL, NULL }, + { 0x01c36, 0x01c37, 0, NULL, NULL }, + { 0x01cd4, 0x01ce0, 0, NULL, NULL }, + { 0x01cf4, 0x01cf4, 0, NULL, NULL }, + { 0x01ced, 0x01ced, 0, NULL, NULL }, + { 0x01cf8, 0x01cf9, 0, NULL, NULL }, + { 0x02060, 0x02064, 0, NULL, NULL }, + { 0x0200b, 0x0200f, 0, NULL, NULL }, + { 0x01dfc, 0x01dff, 0, NULL, NULL }, + { 0x0202a, 0x0202e, 0, NULL, NULL }, + { 0x02066, 0x0206f, 0, NULL, NULL }, + { 0x020d0, 0x020f0, 0, NULL, NULL }, + { 0x03001, 0x03029, 2, NULL, NULL }, + { 0x02e80, 0x02e99, 2, NULL, NULL }, + { 0x02d7f, 0x02d7f, 0, NULL, NULL }, + { 0x02cef, 0x02cf1, 0, NULL, NULL }, + { 0x02de0, 0x02dff, 0, NULL, NULL }, + { 0x02f00, 0x02fd5, 2, NULL, NULL }, + { 0x02e9b, 0x02ef3, 2, NULL, NULL }, + { 0x02ff0, 0x02ffb, 2, NULL, NULL }, + { 0x03099, 0x0309a, 0, NULL, NULL }, + { 0x0302e, 0x0303e, 2, NULL, NULL }, + { 0x0302a, 0x0302d, 0, NULL, NULL }, + { 0x03041, 0x03096, 2, NULL, NULL }, + { 0x03105, 0x0312d, 2, NULL, NULL }, + { 0x0309b, 0x030ff, 2, NULL, NULL }, + { 0x03131, 0x0318e, 2, NULL, NULL }, + { 0x10a3f, 0x10a3f, 0, NULL, NULL }, + { 0x0aa4c, 0x0aa4c, 0, NULL, NULL }, + { 0x0a825, 0x0a826, 0, NULL, NULL }, + { 0x0a490, 0x0a4c6, 2, NULL, NULL }, + { 0x03250, 0x032fe, 2, NULL, NULL }, + { 0x031f0, 0x0321e, 2, NULL, NULL }, + { 0x031c0, 0x031e3, 2, NULL, NULL }, + { 0x03220, 0x03247, 2, NULL, NULL }, + { 0x04e00, 0x09fcc, 2, NULL, NULL }, + { 0x03300, 0x04db5, 2, NULL, NULL }, + { 0x0a000, 0x0a48c, 2, NULL, NULL }, + { 0x0a6f0, 0x0a6f1, 0, NULL, NULL }, + { 0x0a674, 0x0a67d, 0, NULL, NULL }, + { 0x0a66f, 0x0a672, 0, NULL, NULL }, + { 0x0a69f, 0x0a69f, 0, NULL, NULL }, + { 0x0a806, 0x0a806, 0, NULL, NULL }, + { 0x0a802, 0x0a802, 0, NULL, NULL }, + { 0x0a80b, 0x0a80b, 0, NULL, NULL }, + { 0x0a9b6, 0x0a9b9, 0, NULL, NULL }, + { 0x0a947, 0x0a951, 0, NULL, NULL }, + { 0x0a8e0, 0x0a8f1, 0, NULL, NULL }, + { 0x0a8c4, 0x0a8c4, 0, NULL, NULL }, + { 0x0a926, 0x0a92d, 0, NULL, NULL }, + { 0x0a980, 0x0a982, 0, NULL, NULL }, + { 0x0a960, 0x0a97c, 2, NULL, NULL }, + { 0x0a9b3, 0x0a9b3, 0, NULL, NULL }, + { 0x0aa29, 0x0aa2e, 0, NULL, NULL }, + { 0x0a9bc, 0x0a9bc, 0, NULL, NULL }, + { 0x0a9e5, 0x0a9e5, 0, NULL, NULL }, + { 0x0aa35, 0x0aa36, 0, NULL, NULL }, + { 0x0aa31, 0x0aa32, 0, NULL, NULL }, + { 0x0aa43, 0x0aa43, 0, NULL, NULL }, + { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, + { 0x0aaf6, 0x0aaf6, 0, NULL, NULL }, + { 0x0aab7, 0x0aab8, 0, NULL, NULL }, + { 0x0aab0, 0x0aab0, 0, NULL, NULL }, + { 0x0aa7c, 0x0aa7c, 0, NULL, NULL }, + { 0x0aab2, 0x0aab4, 0, NULL, NULL }, + { 0x0aac1, 0x0aac1, 0, NULL, NULL }, + { 0x0aabe, 0x0aabf, 0, NULL, NULL }, + { 0x0aaec, 0x0aaed, 0, NULL, NULL }, + { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, + { 0x0abe8, 0x0abe8, 0, NULL, NULL }, + { 0x0abe5, 0x0abe5, 0, NULL, NULL }, + { 0x0abed, 0x0abed, 0, NULL, NULL }, + { 0x0f900, 0x0fa6d, 2, NULL, NULL }, + { 0x0d800, 0x0f8ff, 0, NULL, NULL }, + { 0x0fa70, 0x0fad9, 2, NULL, NULL }, + { 0x0fff9, 0x0fffb, 0, NULL, NULL }, + { 0x0fe30, 0x0fe52, 2, NULL, NULL }, + { 0x0fe10, 0x0fe19, 2, NULL, NULL }, + { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, + { 0x0fe20, 0x0fe2d, 0, NULL, NULL }, + { 0x0fe68, 0x0fe6b, 2, NULL, NULL }, + { 0x0fe54, 0x0fe66, 2, NULL, NULL }, + { 0x0feff, 0x0feff, 0, NULL, NULL }, + { 0x10a01, 0x10a03, 0, NULL, NULL }, + { 0x102e0, 0x102e0, 0, NULL, NULL }, + { 0x101fd, 0x101fd, 0, NULL, NULL }, + { 0x10376, 0x1037a, 0, NULL, NULL }, + { 0x10a0c, 0x10a0f, 0, NULL, NULL }, { 0x10a05, 0x10a06, 0, NULL, NULL }, + { 0x10a38, 0x10a3a, 0, NULL, NULL }, + { 0x11633, 0x1163a, 0, NULL, NULL }, + { 0x11236, 0x11237, 0, NULL, NULL }, + { 0x11100, 0x11102, 0, NULL, NULL }, + { 0x1107f, 0x11081, 0, NULL, NULL }, + { 0x11001, 0x11001, 0, NULL, NULL }, + { 0x10ae5, 0x10ae6, 0, NULL, NULL }, + { 0x11038, 0x11046, 0, NULL, NULL }, + { 0x110b9, 0x110ba, 0, NULL, NULL }, + { 0x110b3, 0x110b6, 0, NULL, NULL }, + { 0x110bd, 0x110bd, 0, NULL, NULL }, + { 0x11180, 0x11181, 0, NULL, NULL }, + { 0x1112d, 0x11134, 0, NULL, NULL }, + { 0x11127, 0x1112b, 0, NULL, NULL }, + { 0x11173, 0x11173, 0, NULL, NULL }, + { 0x1122f, 0x11231, 0, NULL, NULL }, + { 0x111b6, 0x111be, 0, NULL, NULL }, + { 0x11234, 0x11234, 0, NULL, NULL }, + { 0x11370, 0x11374, 0, NULL, NULL }, + { 0x11301, 0x11301, 0, NULL, NULL }, + { 0x112df, 0x112df, 0, NULL, NULL }, + { 0x112e3, 0x112ea, 0, NULL, NULL }, + { 0x11340, 0x11340, 0, NULL, NULL }, + { 0x1133c, 0x1133c, 0, NULL, NULL }, + { 0x11366, 0x1136c, 0, NULL, NULL }, + { 0x114c2, 0x114c3, 0, NULL, NULL }, + { 0x114ba, 0x114ba, 0, NULL, NULL }, + { 0x114b3, 0x114b8, 0, NULL, NULL }, + { 0x114bf, 0x114c0, 0, NULL, NULL }, + { 0x115bc, 0x115bd, 0, NULL, NULL }, + { 0x115b2, 0x115b5, 0, NULL, NULL }, + { 0x115bf, 0x115c0, 0, NULL, NULL }, + { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, + { 0x16b30, 0x16b36, 0, NULL, NULL }, + { 0x116ad, 0x116ad, 0, NULL, NULL }, + { 0x1163f, 0x11640, 0, NULL, NULL }, + { 0x1163d, 0x1163d, 0, NULL, NULL }, + { 0x116ab, 0x116ab, 0, NULL, NULL }, + { 0x116b7, 0x116b7, 0, NULL, NULL }, + { 0x116b0, 0x116b5, 0, NULL, NULL }, + { 0x16af0, 0x16af4, 0, NULL, NULL }, + { 0x1bca0, 0x1bca3, 0, NULL, NULL }, + { 0x1b000, 0x1b001, 2, NULL, NULL }, + { 0x16f8f, 0x16f92, 0, NULL, NULL }, + { 0x1bc9d, 0x1bc9e, 0, NULL, NULL }, + { 0x1d173, 0x1d182, 0, NULL, NULL }, + { 0x1d167, 0x1d169, 0, NULL, NULL }, + { 0x1d185, 0x1d18b, 0, NULL, NULL }, + { 0x2a700, 0x2b734, 2, NULL, NULL }, + { 0x1f210, 0x1f23a, 2, NULL, NULL }, + { 0x1e8d0, 0x1e8d6, 0, NULL, NULL }, + { 0x1d242, 0x1d244, 0, NULL, NULL }, + { 0x1f200, 0x1f202, 2, NULL, NULL }, + { 0x1f250, 0x1f251, 2, NULL, NULL }, + { 0x1f240, 0x1f248, 2, NULL, NULL }, + { 0x20000, 0x2a6d6, 2, NULL, NULL }, + { 0xe0020, 0xe007f, 0, NULL, NULL }, + { 0x2f800, 0x2fa1d, 2, NULL, NULL }, + { 0x2b740, 0x2b81d, 2, NULL, NULL }, + { 0xe0001, 0xe0001, 0, NULL, NULL }, + { 0xf0000, 0xffffd, 0, NULL, NULL }, + { 0xe0100, 0xe01ef, 0, NULL, NULL }, + { 0x100000, 0x10fffd, 0, NULL, NULL }, }; struct utf8_width_entry *utf8_width_root = NULL; From dcdccf83338654e41281b3b4a8f1c8d2e0621a13 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 5 Nov 2015 23:32:21 +0000 Subject: [PATCH 387/703] Same bug as last commit, but in the other copy of the loop in this file... --- cmd-attach-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index cb1f589e..46133923 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -108,7 +108,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c->peer, MSG_DETACH, s->name); + proc_send_s(c_loop->peer, MSG_DETACH, s->name); } } From 005e462e01b58015f3e1d841b185186e6b5d4e14 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 10 Nov 2015 22:29:33 +0000 Subject: [PATCH 388/703] Handle absolute paths properly, and don't use resolved path in realpath() fails. --- cmd-load-buffer.c | 12 ++++++++---- cmd-save-buffer.c | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index c55e6c11..d81288f3 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -77,11 +77,15 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) else cwd = "."; - xasprintf(&file, "%s/%s", cwd, path); - if (realpath(file, resolved) == NULL) - f = NULL; + if (*path == '/') + file = xstrdup(path); else - f = fopen(resolved, "rb"); + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL) { + cmdq_error(cmdq, "%s: %s", file, strerror(errno)); + return (CMD_RETURN_ERROR); + } + f = fopen(resolved, "rb"); free(file); if (f == NULL) { cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index dca2c8ef..8de99075 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -103,11 +103,15 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'a')) flags = "ab"; - xasprintf(&file, "%s/%s", cwd, path); - if (realpath(file, resolved) == NULL) - f = NULL; + if (*path == '/') + file = xstrdup(path); else - f = fopen(resolved, flags); + xasprintf(&file, "%s/%s", cwd, path); + if (realpath(file, resolved) == NULL) { + cmdq_error(cmdq, "%s: %s", file, strerror(errno)); + return (CMD_RETURN_ERROR); + } + f = fopen(resolved, flags); free(file); if (f == NULL) { cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); From 6f3475c6c713245f5ec760298af9a04b8465b844 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 10 Nov 2015 22:33:47 +0000 Subject: [PATCH 389/703] If realpath() fails just try the original path. --- cmd-load-buffer.c | 5 +++-- cmd-save-buffer.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index d81288f3..9352cbe5 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -81,8 +81,9 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) file = xstrdup(path); else xasprintf(&file, "%s/%s", cwd, path); - if (realpath(file, resolved) == NULL) { - cmdq_error(cmdq, "%s: %s", file, strerror(errno)); + if (realpath(file, resolved) == NULL && + strlcpy(resolved, file, sizeof resolved) >= sizeof resolved) { + cmdq_error(cmdq, "%s: %s", file, strerror(ENAMETOOLONG)); return (CMD_RETURN_ERROR); } f = fopen(resolved, "rb"); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 8de99075..9f3deeda 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -107,8 +107,9 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) file = xstrdup(path); else xasprintf(&file, "%s/%s", cwd, path); - if (realpath(file, resolved) == NULL) { - cmdq_error(cmdq, "%s: %s", file, strerror(errno)); + if (realpath(file, resolved) == NULL && + strlcpy(resolved, file, sizeof resolved) >= sizeof resolved) { + cmdq_error(cmdq, "%s: %s", file, strerror(ENAMETOOLONG)); return (CMD_RETURN_ERROR); } f = fopen(resolved, flags); From 00c34df1868ffde8ece952729ddf2e4df09667be Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 11 Nov 2015 23:23:33 +0000 Subject: [PATCH 390/703] Drop mouse-utf8 option and always turn on UTF-8 mouse if the client says it supports UTF-8. --- options-table.c | 5 ----- server-client.c | 12 +++++------- tmux.1 | 4 ---- tmux.c | 1 - 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/options-table.c b/options-table.c index cfec138d..4e9a0a81 100644 --- a/options-table.c +++ b/options-table.c @@ -256,11 +256,6 @@ const struct options_table_entry session_options_table[] = { .default_num = 0 }, - { .name = "mouse-utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 - }, - { .name = "prefix", .type = OPTIONS_TABLE_KEY, .default_num = '\002', diff --git a/server-client.c b/server-client.c index f1e2deff..521f2737 100644 --- a/server-client.c +++ b/server-client.c @@ -801,14 +801,12 @@ server_client_reset_state(struct client *c) mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON; /* - * Set UTF-8 mouse input if required. If the terminal is UTF-8, the - * user has set mouse-utf8 and any mouse mode is in effect, turn on - * UTF-8 mouse input. If the receiving terminal hasn't requested it - * (that is, it isn't in s->mode), then it'll be converted in - * input_mouse. + * Set UTF-8 mouse input if required. If the terminal is UTF-8 and any + * mouse mode is in effect, turn on UTF-8 mouse input. If the receiving + * terminal hasn't requested it (that is, it isn't in s->mode), then + * it'll be converted in input_mouse. */ - if ((c->tty.flags & TTY_UTF8) && - (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8")) + if ((c->tty.flags & TTY_UTF8) && (mode & ALL_MOUSE_MODES)) mode |= MODE_MOUSE_UTF8; else mode &= ~MODE_MOUSE_UTF8; diff --git a/tmux.1 b/tmux.1 index 1fb99252..4420ac3a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2655,10 +2655,6 @@ captures the mouse and allows mouse events to be bound as key bindings. See the .Sx MOUSE SUPPORT section for details. -.It Xo Ic mouse-utf8 -.Op Ic on | off -.Xc -If enabled, request mouse input as UTF-8 on UTF-8 terminals. .It Ic prefix Ar key Set the key accepted as a prefix key. .It Ic prefix2 Ar key diff --git a/tmux.c b/tmux.c index 758479b8..1e68d9bb 100644 --- a/tmux.c +++ b/tmux.c @@ -294,7 +294,6 @@ main(int argc, char **argv) /* Enable UTF-8 if the first client is on UTF-8 terminal. */ if (flags & CLIENT_UTF8) { options_set_number(global_s_options, "status-utf8", 1); - options_set_number(global_s_options, "mouse-utf8", 1); options_set_number(global_w_options, "utf8", 1); } From 7062b0e65dcbb94bb190f6c50f4089b2ea6278bb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 08:19:18 +0000 Subject: [PATCH 391/703] Default history-file should be "" not NULL, from Greg Onufe. --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 4e9a0a81..d216b6d8 100644 --- a/options-table.c +++ b/options-table.c @@ -85,7 +85,7 @@ const struct options_table_entry server_options_table[] = { { .name = "history-file", .type = OPTIONS_TABLE_STRING, - .default_str = NULL + .default_str = "" }, { .name = "message-limit", From 69e0b8326ad0a983759518b90ed8632146341acf Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:05:34 +0000 Subject: [PATCH 392/703] Support UTF-8 key bindings by expanding the key type from int to uint64_t and converting UTF-8 to Unicode on input and the reverse on output. (This allows key bindings, there are still omissions - the largest being that the various prompts do not accept UTF-8.) --- cmd-bind-key.c | 7 ++-- cmd-list-keys.c | 13 +++++--- cmd-send-keys.c | 3 +- cmd-set-option.c | 2 +- cmd-unbind-key.c | 9 +++--- input-keys.c | 38 ++++++++++++++-------- key-bindings.c | 10 ++++-- key-string.c | 63 ++++++++++++++++++++++++------------ mode-key.c | 16 ++++++--- server-client.c | 40 +++++++++++------------ status.c | 4 +-- tmux.h | 84 ++++++++++++++++++++++++++---------------------- tty-keys.c | 62 ++++++++++++++++++++++------------- utf8.c | 34 ++++++++++++++++++-- window-choose.c | 23 ++++++------- window-clock.c | 5 +-- window-copy.c | 12 +++---- window.c | 2 +- xterm-keys.c | 21 ++++++------ 19 files changed, 280 insertions(+), 168 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index fda39efc..1867e814 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -29,7 +29,8 @@ enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, int); +enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, + key_code); const struct cmd_entry cmd_bind_key_entry = { "bind-key", "bind", @@ -45,7 +46,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; char *cause; struct cmd_list *cmdlist; - int key; + key_code key; const char *tablename; if (args_has(args, 't')) { @@ -89,7 +90,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) { struct args *args = self->args; const char *tablename; diff --git a/cmd-list-keys.c b/cmd-list-keys.c index d26486bd..c76f9f47 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -57,6 +57,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) char tmp[BUFSIZ]; size_t used; int repeat, width, tablewidth, keywidth; + u_int i; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, cmdq)); @@ -83,8 +84,8 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) width = strlen(table->name); if (width > tablewidth) - tablewidth =width; - width = strlen(key); + tablewidth = width; + width = utf8_cstrwidth(key); if (width > keywidth) keywidth = width; } @@ -102,8 +103,12 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) r = "-r "; else r = " "; - used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %-*s ", r, - (int)tablewidth, table->name, (int)keywidth, key); + used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %s", r, + (int)tablewidth, table->name, key); + for (i = 0; i < keywidth - utf8_cstrwidth(key); i++) { + if (strlcat(tmp, " ", sizeof tmp) < sizeof tmp) + used++; + } if (used < sizeof tmp) { cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); diff --git a/cmd-send-keys.c b/cmd-send-keys.c index dd796d60..73a308ae 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -53,7 +53,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct window_pane *wp; struct session *s; const u_char *str; - int i, key; + int i; + key_code key; if (args_has(args, 'M')) { wp = cmd_mouse_pane(m, &s, NULL); diff --git a/cmd-set-option.c b/cmd-set-option.c index 6762e6ad..c5a77b45 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -394,7 +394,7 @@ cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { - int key; + key_code key; if ((key = key_string_lookup_string(value)) == KEYC_NONE) { cmdq_error(cmdq, "bad key: %s", value); diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 493f6dde..cec538c0 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -26,8 +26,9 @@ * Unbind key from command. */ -enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, int); +enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, + key_code); const struct cmd_entry cmd_unbind_key_entry = { "unbind-key", "unbind", @@ -41,7 +42,7 @@ enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - int key; + key_code key; const char *tablename; if (!args_has(args, 'a')) { @@ -95,7 +96,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, int key) +cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) { struct args *args = self->args; const char *tablename; diff --git a/input-keys.c b/input-keys.c index 579c0f39..a761a088 100644 --- a/input-keys.c +++ b/input-keys.c @@ -34,7 +34,7 @@ void input_key_mouse(struct window_pane *, struct mouse_event *); struct input_key_ent { - int key; + key_code key; const char *data; int flags; @@ -137,15 +137,16 @@ const struct input_key_ent input_keys[] = { /* Translate a key code into an output key sequence. */ void -input_key(struct window_pane *wp, int key, struct mouse_event *m) +input_key(struct window_pane *wp, key_code key, struct mouse_event *m) { - const struct input_key_ent *ike; - u_int i; - size_t dlen; - char *out; - u_char ch; + const struct input_key_ent *ike; + u_int i; + size_t dlen; + char *out; + key_code justkey; + struct utf8_data utf8data; - log_debug("writing key 0x%x (%s) to %%%u", key, + log_debug("writing key 0x%llx (%s) to %%%u", key, key_string_lookup_key(key), wp->id); /* If this is a mouse key, pass off to mouse function. */ @@ -157,13 +158,22 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) /* * If this is a normal 7-bit key, just send it, with a leading escape - * if necessary. + * if necessary. If it is a UTF-8 key, split it and send it. */ - if (key != KEYC_NONE && (key & ~KEYC_ESCAPE) < 0x100) { + justkey = (key & ~KEYC_ESCAPE); + if (key != KEYC_NONE && justkey < 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); - ch = key & ~KEYC_ESCAPE; - bufferevent_write(wp->event, &ch, 1); + utf8data.data[0] = justkey; + bufferevent_write(wp->event, &utf8data.data[0], 1); + return; + } + if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { + if (utf8_split(justkey, &utf8data) != 0) + return; + if (key & KEYC_ESCAPE) + bufferevent_write(wp->event, "\033", 1); + bufferevent_write(wp->event, utf8data.data, utf8data.size); return; } @@ -196,11 +206,11 @@ input_key(struct window_pane *wp, int key, struct mouse_event *m) break; } if (i == nitems(input_keys)) { - log_debug("key 0x%x missing", key); + log_debug("key 0x%llx missing", key); return; } dlen = strlen(ike->data); - log_debug("found key 0x%x: \"%s\"", key, ike->data); + log_debug("found key 0x%llx: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) diff --git a/key-bindings.c b/key-bindings.c index 83b94ac1..47a7d867 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -37,7 +37,11 @@ key_table_cmp(struct key_table *e1, struct key_table *e2) int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { - return (bd1->key - bd2->key); + if (bd1->key < bd2->key) + return (-1); + if (bd1->key > bd2->key) + return (1); + return (0); } struct key_table * @@ -80,7 +84,7 @@ key_bindings_unref_table(struct key_table *table) } void -key_bindings_add(const char *name, int key, int can_repeat, +key_bindings_add(const char *name, key_code key, int can_repeat, struct cmd_list *cmdlist) { struct key_table *table; @@ -105,7 +109,7 @@ key_bindings_add(const char *name, int key, int can_repeat, } void -key_bindings_remove(const char *name, int key) +key_bindings_remove(const char *name, key_code key) { struct key_table *table; struct key_binding bd_find, *bd; diff --git a/key-string.c b/key-string.c index 88cd2a32..4285e129 100644 --- a/key-string.c +++ b/key-string.c @@ -22,12 +22,12 @@ #include "tmux.h" -int key_string_search_table(const char *); -int key_string_get_modifiers(const char **); +key_code key_string_search_table(const char *); +key_code key_string_get_modifiers(const char **); const struct { const char *string; - int key; + key_code key; } key_string_table[] = { /* Function keys. */ { "F1", KEYC_F1 }, @@ -98,7 +98,7 @@ const struct { }; /* Find key string in table. */ -int +key_code key_string_search_table(const char *string) { u_int i; @@ -111,10 +111,10 @@ key_string_search_table(const char *string) } /* Find modifiers. */ -int +key_code key_string_get_modifiers(const char **string) { - int modifiers; + key_code modifiers; modifiers = 0; while (((*string)[0] != '\0') && (*string)[1] == '-') { @@ -138,13 +138,16 @@ key_string_get_modifiers(const char **string) } /* Lookup a string and convert to a key value. */ -int +key_code key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; - int key, modifiers; + key_code key; u_short u; int size; + key_code modifiers; + struct utf8_data utf8data; + u_int i; /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { @@ -164,11 +167,21 @@ key_string_lookup_string(const char *string) return (KEYC_NONE); /* Is this a standard ASCII key? */ - if (string[1] == '\0') { - key = (u_char) string[0]; - if (key < 32 || key == 127 || key > 255) + if (string[1] == '\0' && (u_char)string[0] <= 127) { + key = (u_char)string[0]; + if (key < 32 || key == 127) return (KEYC_NONE); } else { + /* Try as a UTF-8 key. */ + if (utf8_open(&utf8data, (u_char)*string)) { + if (strlen(string) != utf8data.size) + return (KEYC_NONE); + for (i = 1; i < utf8data.size; i++) + utf8_append(&utf8data, (u_char)string[i]); + key = utf8_combine(&utf8data); + return (key | modifiers); + } + /* Otherwise look the key up in the table. */ key = key_string_search_table(string); if (key == KEYC_NONE) @@ -195,11 +208,12 @@ key_string_lookup_string(const char *string) /* Convert a key code into string format, with prefix if necessary. */ const char * -key_string_lookup_key(int key) +key_string_lookup_key(key_code key) { - static char out[24]; - char tmp[8]; - u_int i; + static char out[24]; + char tmp[8]; + u_int i; + struct utf8_data utf8data; *out = '\0'; @@ -237,23 +251,32 @@ key_string_lookup_key(int key) return (out); } + /* Is this a UTF-8 key? */ + if (key > 127 && key < KEYC_BASE) { + if (utf8_split(key, &utf8data) == 0) { + memcpy(out, utf8data.data, utf8data.size); + out[utf8data.size] = '\0'; + return (out); + } + } + /* Invalid keys are errors. */ if (key == 127 || key > 255) { - snprintf(out, sizeof out, "", key); + snprintf(out, sizeof out, "", key); return (out); } /* Check for standard or control key. */ - if (key >= 0 && key <= 32) { + if (key <= 32) { if (key == 0 || key > 26) - xsnprintf(tmp, sizeof tmp, "C-%c", 64 + key); + xsnprintf(tmp, sizeof tmp, "C-%c", (int)(64 + key)); else - xsnprintf(tmp, sizeof tmp, "C-%c", 96 + key); + xsnprintf(tmp, sizeof tmp, "C-%c", (int)(96 + key)); } else if (key >= 32 && key <= 126) { tmp[0] = key; tmp[1] = '\0'; } else if (key >= 128) - xsnprintf(tmp, sizeof tmp, "\\%o", key); + xsnprintf(tmp, sizeof tmp, "\\%llo", key); strlcat(out, tmp, sizeof out); return (out); diff --git a/mode-key.c b/mode-key.c index 5ed45bd8..a47cda0b 100644 --- a/mode-key.c +++ b/mode-key.c @@ -40,7 +40,7 @@ /* Entry in the default mode key tables. */ struct mode_key_entry { - int key; + key_code key; /* * Editing mode for vi: 0 is edit mode, keys not in the table are @@ -523,9 +523,15 @@ RB_GENERATE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); int mode_key_cmp(struct mode_key_binding *mbind1, struct mode_key_binding *mbind2) { - if (mbind1->mode != mbind2->mode) - return (mbind1->mode - mbind2->mode); - return (mbind1->key - mbind2->key); + if (mbind1->mode < mbind2->mode) + return (-1); + if (mbind1->mode > mbind2->mode) + return (1); + if (mbind1->key < mbind2->key) + return (-1); + if (mbind1->key > mbind2->key) + return (1); + return (0); } const char * @@ -588,7 +594,7 @@ mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree) } enum mode_key_cmd -mode_key_lookup(struct mode_key_data *mdata, int key, const char **arg) +mode_key_lookup(struct mode_key_data *mdata, key_code key, const char **arg) { struct mode_key_binding *mbind, mtmp; diff --git a/server-client.c b/server-client.c index 521f2737..fedc93bd 100644 --- a/server-client.c +++ b/server-client.c @@ -32,22 +32,22 @@ #include "tmux.h" -void server_client_key_table(struct client *, const char *); -void server_client_free(int, short, void *); -void server_client_check_focus(struct window_pane *); -void server_client_check_resize(struct window_pane *); -int server_client_check_mouse(struct client *); -void server_client_repeat_timer(int, short, void *); -void server_client_check_exit(struct client *); -void server_client_check_redraw(struct client *); -void server_client_set_title(struct client *); -void server_client_reset_state(struct client *); -int server_client_assume_paste(struct session *); +void server_client_key_table(struct client *, const char *); +void server_client_free(int, short, void *); +void server_client_check_focus(struct window_pane *); +void server_client_check_resize(struct window_pane *); +key_code server_client_check_mouse(struct client *); +void server_client_repeat_timer(int, short, void *); +void server_client_check_exit(struct client *); +void server_client_check_redraw(struct client *); +void server_client_set_title(struct client *); +void server_client_reset_state(struct client *); +int server_client_assume_paste(struct session *); -void server_client_dispatch(struct imsg *, void *); -void server_client_dispatch_command(struct client *, struct imsg *); -void server_client_dispatch_identify(struct client *, struct imsg *); -void server_client_dispatch_shell(struct client *); +void server_client_dispatch(struct imsg *, void *); +void server_client_dispatch_command(struct client *, struct imsg *); +void server_client_dispatch_identify(struct client *, struct imsg *); +void server_client_dispatch_shell(struct client *); /* Check if this client is inside this server. */ int @@ -257,7 +257,7 @@ server_client_free(unused int fd, unused short events, void *arg) } /* Check for mouse keys. */ -int +key_code server_client_check_mouse(struct client *c) { struct session *s = c->session; @@ -267,7 +267,7 @@ server_client_check_mouse(struct client *c) enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE; enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE; u_int x, y, b; - int key; + key_code key; log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); @@ -501,7 +501,7 @@ server_client_assume_paste(struct session *s) /* Handle data key input from client. */ void -server_client_handle_key(struct client *c, int key) +server_client_handle_key(struct client *c, key_code key) { struct mouse_event *m = &c->tty.mouse; struct session *s = c->session; @@ -635,8 +635,8 @@ retry: * No match, but in the root table. Prefix switches to the prefix table * and everything else is passed through. */ - if (key == options_get_number(s->options, "prefix") || - key == options_get_number(s->options, "prefix2")) { + if (key == (key_code)options_get_number(s->options, "prefix") || + key == (key_code)options_get_number(s->options, "prefix2")) { server_client_key_table(c, "prefix"); server_status_client(c); return; diff --git a/status.c b/status.c index a2f46b95..df80d2b8 100644 --- a/status.c +++ b/status.c @@ -812,7 +812,7 @@ status_prompt_redraw(struct client *c) /* Handle keys in prompt. */ void -status_prompt_key(struct client *c, int key) +status_prompt_key(struct client *c, key_code key) { struct session *sess = c->session; struct options *oo = sess->options; @@ -1116,7 +1116,7 @@ status_prompt_key(struct client *c, int key) status_prompt_clear(c); break; case MODEKEY_OTHER: - if ((key & 0xff00) != 0 || key < 32 || key == 127) + if (key <= 0x1f || key >= 0x7f) break; c->prompt_buffer = xrealloc(c->prompt_buffer, size + 2); diff --git a/tmux.h b/tmux.h index 25b51350..51ceefd8 100644 --- a/tmux.h +++ b/tmux.h @@ -95,13 +95,13 @@ struct tmuxproc; #define BELL_OTHER 3 /* Special key codes. */ -#define KEYC_NONE 0xfff -#define KEYC_BASE 0x1000 +#define KEYC_NONE 0xffff00000000ULL +#define KEYC_BASE 0x100000000000ULL /* Key modifier bits. */ -#define KEYC_ESCAPE 0x2000 -#define KEYC_CTRL 0x4000 -#define KEYC_SHIFT 0x8000 +#define KEYC_ESCAPE 0x200000000000ULL +#define KEYC_CTRL 0x400000000000ULL +#define KEYC_SHIFT 0x800000000000ULL /* Mask to obtain key w/o modifiers. */ #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT) @@ -121,8 +121,14 @@ struct tmuxproc; { #s "Status", KEYC_ ## name ## _STATUS }, \ { #s "Border", KEYC_ ## name ## _BORDER } +/* + * A single key. This can be ASCII or Unicode or one of the keys starting at + * KEYC_BASE. + */ +typedef uint64_t key_code; + /* Special key codes. */ -enum key_code { +enum { /* Focus events. */ KEYC_FOCUS_IN = KEYC_BASE, KEYC_FOCUS_OUT, @@ -570,7 +576,7 @@ struct mode_key_data { /* Binding between a key and a command. */ struct mode_key_binding { - int key; + key_code key; int mode; enum mode_key_cmd cmd; @@ -776,7 +782,7 @@ struct window_mode { void (*free)(struct window_pane *); void (*resize)(struct window_pane *, u_int, u_int); void (*key)(struct window_pane *, struct client *, struct session *, - int, struct mouse_event *); + key_code, struct mouse_event *); }; /* Structures for choose mode. */ @@ -1030,31 +1036,31 @@ RB_HEAD(sessions, session); /* Mouse input. */ struct mouse_event { - int valid; + int valid; - int key; - int statusat; + key_code key; + int statusat; - u_int x; - u_int y; - u_int b; + u_int x; + u_int y; + u_int b; - u_int lx; - u_int ly; - u_int lb; + u_int lx; + u_int ly; + u_int lb; - int s; - int w; - int wp; + int s; + int w; + int wp; - u_int sgr_type; - u_int sgr_b; + u_int sgr_type; + u_int sgr_b; }; /* TTY information. */ struct tty_key { char ch; - int key; + key_code key; struct tty_key *left; struct tty_key *right; @@ -1340,7 +1346,7 @@ struct cmd_entry { /* Key binding and key table. */ struct key_binding { - int key; + key_code key; struct cmd_list *cmdlist; int can_repeat; @@ -1488,7 +1494,8 @@ enum mode_key_cmd mode_key_fromstring(const struct mode_key_cmdstr *, const struct mode_key_table *mode_key_findtable(const char *); void mode_key_init_trees(void); void mode_key_init(struct mode_key_data *, struct mode_key_tree *); -enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int, const char **); +enum mode_key_cmd mode_key_lookup(struct mode_key_data *, key_code, + const char **); /* notify.c */ void notify_enable(void); @@ -1632,9 +1639,9 @@ const char *tty_term_describe(struct tty_term *, enum tty_code_code); const char *tty_acs_get(struct tty *, u_char); /* tty-keys.c */ -void tty_keys_build(struct tty *); -void tty_keys_free(struct tty *); -int tty_keys_next(struct tty *); +void tty_keys_build(struct tty *); +void tty_keys_free(struct tty *); +key_code tty_keys_next(struct tty *); /* arguments.c */ int args_cmp(struct args_entry *, struct args_entry *); @@ -1720,16 +1727,16 @@ int key_table_cmp(struct key_table *, struct key_table *); int key_bindings_cmp(struct key_binding *, struct key_binding *); struct key_table *key_bindings_get_table(const char *, int); void key_bindings_unref_table(struct key_table *); -void key_bindings_add(const char *, int, int, struct cmd_list *); -void key_bindings_remove(const char *, int); +void key_bindings_add(const char *, key_code, int, struct cmd_list *); +void key_bindings_remove(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *, struct mouse_event *); /* key-string.c */ -int key_string_lookup_string(const char *); -const char *key_string_lookup_key(int); +key_code key_string_lookup_string(const char *); +const char *key_string_lookup_key(key_code); /* alerts.c */ void alerts_reset_all(void); @@ -1753,7 +1760,7 @@ void server_add_accept(int); /* server-client.c */ int server_client_check_nested(struct client *); -void server_client_handle_key(struct client *, int); +void server_client_handle_key(struct client *, key_code); void server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); @@ -1803,7 +1810,7 @@ void status_prompt_set(struct client *, const char *, const char *, int (*)(void *, const char *), void (*)(void *), void *, int); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); -void status_prompt_key(struct client *, int); +void status_prompt_key(struct client *, key_code); void status_prompt_update(struct client *, const char *, const char *); void status_prompt_load_history(void); void status_prompt_save_history(void); @@ -1819,11 +1826,11 @@ struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); /* input-key.c */ -void input_key(struct window_pane *, int, struct mouse_event *); +void input_key(struct window_pane *, key_code, struct mouse_event *); /* xterm-keys.c */ -char *xterm_keys_lookup(int); -int xterm_keys_find(const char *, size_t, size_t *, int *); +char *xterm_keys_lookup(key_code); +int xterm_keys_find(const char *, size_t, size_t *, key_code *); /* colour.c */ int colour_find_rgb(u_char, u_char, u_char); @@ -2020,7 +2027,7 @@ int window_pane_set_mode( struct window_pane *, const struct window_mode *); void window_pane_reset_mode(struct window_pane *); void window_pane_key(struct window_pane *, struct client *, - struct session *, int, struct mouse_event *); + struct session *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); char *window_pane_search( struct window_pane *, const char *, u_int *); @@ -2179,6 +2186,7 @@ void utf8_set(struct utf8_data *, u_char); int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); u_int utf8_combine(const struct utf8_data *); +int utf8_split(u_int, struct utf8_data *); u_int utf8_split2(u_int, u_char *); int utf8_strvis(char *, const char *, size_t, int); struct utf8_data *utf8_fromcstr(const char *); diff --git a/tty-keys.c b/tty-keys.c index 309e8c04..31adf0f0 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -33,11 +33,11 @@ * into a ternary tree. */ -void tty_keys_add1(struct tty_key **, const char *, int); -void tty_keys_add(struct tty *, const char *, int); +void tty_keys_add1(struct tty_key **, const char *, key_code); +void tty_keys_add(struct tty *, const char *, key_code); void tty_keys_free1(struct tty_key *); -struct tty_key *tty_keys_find1( - struct tty_key *, const char *, size_t, size_t *); +struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, + size_t *); struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); void tty_keys_callback(int, short, void *); int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); @@ -45,7 +45,7 @@ int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { const char *string; - int key; + key_code key; }; const struct tty_default_key_raw tty_default_raw_keys[] = { /* @@ -165,7 +165,7 @@ const struct tty_default_key_raw tty_default_raw_keys[] = { /* Default terminfo(5) keys. */ struct tty_default_key_code { enum tty_code_code code; - int key; + key_code key; }; const struct tty_default_key_code tty_default_code_keys[] = { /* Function keys. */ @@ -317,7 +317,7 @@ const struct tty_default_key_code tty_default_code_keys[] = { /* Add key to tree. */ void -tty_keys_add(struct tty *tty, const char *s, int key) +tty_keys_add(struct tty *tty, const char *s, key_code key) { struct tty_key *tk; size_t size; @@ -325,17 +325,17 @@ tty_keys_add(struct tty *tty, const char *s, int key) keystr = key_string_lookup_key(key); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { - log_debug("new key %s: 0x%x (%s)", s, key, keystr); + log_debug("new key %s: 0x%llx (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); } else { - log_debug("replacing key %s: 0x%x (%s)", s, key, keystr); + log_debug("replacing key %s: 0x%llx (%s)", s, key, keystr); tk->key = key; } } /* Add next node to the tree. */ void -tty_keys_add1(struct tty_key **tkp, const char *s, int key) +tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) { struct tty_key *tk; @@ -464,15 +464,18 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) * Process at least one key in the buffer and invoke tty->key_callback. Return * 0 if there are no further keys, or 1 if there could be more in the buffer. */ -int +key_code tty_keys_next(struct tty *tty) { - struct tty_key *tk; - struct timeval tv; - const char *buf; - size_t len, size; - cc_t bspace; - int key, delay, expired = 0; + struct tty_key *tk; + struct timeval tv; + const char *buf; + size_t len, size; + cc_t bspace; + int delay, expired = 0; + key_code key; + struct utf8_data utf8data; + u_int i; /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); @@ -535,8 +538,23 @@ first_key: } } + /* Is this valid UTF-8? */ + if (utf8_open(&utf8data, (u_char)*buf)) { + size = utf8data.size; + if (len < size) { + if (expired) + goto discard_key; + goto partial_key; + } + for (i = 1; i < size; i++) + utf8_append(&utf8data, (u_char)buf[i]); + key = utf8_combine(&utf8data); + log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); + goto complete_key; + } + /* No key found, take first. */ - key = (u_char) *buf; + key = (u_char)*buf; size = 1; /* @@ -578,7 +596,7 @@ partial_key: return (0); complete_key: - log_debug("complete key %.*s %#x", (int) size, buf, key); + log_debug("complete key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); @@ -604,7 +622,7 @@ complete_key: return (1); discard_key: - log_debug("discard key %.*s %#x", (int) size, buf, key); + log_debug("discard key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); @@ -684,10 +702,10 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) utf8_append(&utf8data, buf[*size]); value = utf8_combine(&utf8data); } else - value = (u_char) buf[*size]; + value = (u_char)buf[*size]; (*size)++; } else { - value = (u_char) buf[*size]; + value = (u_char)buf[*size]; (*size)++; } diff --git a/utf8.c b/utf8.c index 9a06c186..e61bf996 100644 --- a/utf8.c +++ b/utf8.c @@ -394,6 +394,8 @@ utf8_open(struct utf8_data *utf8data, u_char ch) int utf8_append(struct utf8_data *utf8data, u_char ch) { + /* XXX this should do validity checks too! */ + if (utf8data->have >= utf8data->size) fatalx("UTF-8 character overflow"); if (utf8data->size > sizeof utf8data->data) @@ -467,18 +469,46 @@ utf8_combine(const struct utf8_data *utf8data) case 3: value = utf8data->data[2] & 0x3f; value |= (utf8data->data[1] & 0x3f) << 6; - value |= (utf8data->data[0] & 0x0f) << 12; + value |= (utf8data->data[0] & 0xf) << 12; break; case 4: value = utf8data->data[3] & 0x3f; value |= (utf8data->data[2] & 0x3f) << 6; value |= (utf8data->data[1] & 0x3f) << 12; - value |= (utf8data->data[0] & 0x07) << 18; + value |= (utf8data->data[0] & 0x7) << 18; break; } return (value); } +/* Split a UTF-8 character. */ +int +utf8_split(u_int uc, struct utf8_data *utf8data) +{ + if (uc < 0x7f) { + utf8data->size = 1; + utf8data->data[0] = uc; + } else if (uc < 0x7ff) { + utf8data->size = 2; + utf8data->data[0] = 0xc0 | ((uc >> 6) & 0x1f); + utf8data->data[1] = 0x80 | (uc & 0x3f); + } else if (uc < 0xffff) { + utf8data->size = 3; + utf8data->data[0] = 0xe0 | ((uc >> 12) & 0xf); + utf8data->data[1] = 0x80 | ((uc >> 6) & 0x3f); + utf8data->data[2] = 0x80 | (uc & 0x3f); + } else if (uc < 0x1fffff) { + utf8data->size = 4; + utf8data->data[0] = 0xf0 | ((uc >> 18) & 0x7); + utf8data->data[1] = 0x80 | ((uc >> 12) & 0x3f); + utf8data->data[2] = 0x80 | ((uc >> 6) & 0x3f); + utf8data->data[3] = 0x80 | (uc & 0x3f); + } else + return (-1); + utf8data->width = utf8_width(utf8data); + return (0); +} + /* Split a two-byte UTF-8 character. */ u_int utf8_split2(u_int uc, u_char *ptr) diff --git a/window-choose.c b/window-choose.c index f1c3f94a..fa0e95c8 100644 --- a/window-choose.c +++ b/window-choose.c @@ -29,11 +29,11 @@ struct screen *window_choose_init(struct window_pane *); void window_choose_free(struct window_pane *); void window_choose_resize(struct window_pane *, u_int, u_int); void window_choose_key(struct window_pane *, struct client *, - struct session *, int, struct mouse_event *); + struct session *, key_code, struct mouse_event *); void window_choose_default_callback(struct window_choose_data *); struct window_choose_mode_item *window_choose_get_item(struct window_pane *, - int, struct mouse_event *); + key_code, struct mouse_event *); void window_choose_fire_callback( struct window_pane *, struct window_choose_data *); @@ -86,9 +86,9 @@ struct window_choose_mode_data { void window_choose_free1(struct window_choose_mode_data *); int window_choose_key_index(struct window_choose_mode_data *, u_int); -int window_choose_index_key(struct window_choose_mode_data *, int); +int window_choose_index_key(struct window_choose_mode_data *, key_code); void window_choose_prompt_input(enum window_choose_input_type, - const char *, struct window_pane *, int); + const char *, struct window_pane *, key_code); void window_choose_reset_top(struct window_pane *, u_int); void @@ -314,7 +314,7 @@ window_choose_fire_callback( void window_choose_prompt_input(enum window_choose_input_type input_type, - const char *prompt, struct window_pane *wp, int key) + const char *prompt, struct window_pane *wp, key_code key) { struct window_choose_mode_data *data = wp->modedata; size_t input_len; @@ -490,7 +490,8 @@ window_choose_expand(struct window_pane *wp, struct session *s, u_int pos) } struct window_choose_mode_item * -window_choose_get_item(struct window_pane *wp, int key, struct mouse_event *m) +window_choose_get_item(struct window_pane *wp, key_code key, + struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; u_int x, y, idx; @@ -509,7 +510,7 @@ window_choose_get_item(struct window_pane *wp, int key, struct mouse_event *m) void window_choose_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, int key, struct mouse_event *m) + unused struct session *sess, key_code key, struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -743,8 +744,8 @@ window_choose_key(struct window_pane *wp, unused struct client *c, } void -window_choose_write_line( - struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, + u_int py) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; @@ -821,7 +822,7 @@ window_choose_key_index(struct window_choose_mode_data *data, u_int idx) } int -window_choose_index_key(struct window_choose_mode_data *data, int key) +window_choose_index_key(struct window_choose_mode_data *data, key_code key) { static const char keys[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" @@ -834,7 +835,7 @@ window_choose_index_key(struct window_choose_mode_data *data, int key) mkey = mode_key_lookup(&data->mdata, *ptr, NULL); if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) continue; - if (key == *ptr) + if (key == (key_code)*ptr) return (idx); idx++; } diff --git a/window-clock.c b/window-clock.c index e366714b..51f97250 100644 --- a/window-clock.c +++ b/window-clock.c @@ -28,7 +28,7 @@ struct screen *window_clock_init(struct window_pane *); void window_clock_free(struct window_pane *); void window_clock_resize(struct window_pane *, u_int, u_int); void window_clock_key(struct window_pane *, struct client *, - struct session *, int, struct mouse_event *); + struct session *, key_code, struct mouse_event *); void window_clock_timer_callback(int, short, void *); void window_clock_draw_screen(struct window_pane *); @@ -186,7 +186,8 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) void window_clock_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, unused int key, unused struct mouse_event *m) + unused struct session *sess, unused key_code key, + unused struct mouse_event *m) { window_pane_reset_mode(wp); } diff --git a/window-copy.c b/window-copy.c index c5c053bf..d3e5b7d9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -28,9 +28,9 @@ struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); void window_copy_key(struct window_pane *, struct client *, struct session *, - int, struct mouse_event *); -int window_copy_key_input(struct window_pane *, int); -int window_copy_key_numeric_prefix(struct window_pane *, int); + key_code, struct mouse_event *); +int window_copy_key_input(struct window_pane *, key_code); +int window_copy_key_numeric_prefix(struct window_pane *, key_code); void window_copy_redraw_selection(struct window_pane *, u_int); void window_copy_redraw_lines(struct window_pane *, u_int, u_int); @@ -368,7 +368,7 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) void window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, - int key, struct mouse_event *m) + key_code key, struct mouse_event *m) { const char *word_separators; struct window_copy_mode_data *data = wp->modedata; @@ -800,7 +800,7 @@ input_off: } int -window_copy_key_input(struct window_pane *wp, int key) +window_copy_key_input(struct window_pane *wp, key_code key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; @@ -897,7 +897,7 @@ window_copy_key_input(struct window_pane *wp, int key) } int -window_copy_key_numeric_prefix(struct window_pane *wp, int key) +window_copy_key_numeric_prefix(struct window_pane *wp, key_code key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; diff --git a/window.c b/window.c index 49408297..c5f9f176 100644 --- a/window.c +++ b/window.c @@ -1102,7 +1102,7 @@ window_pane_reset_mode(struct window_pane *wp) void window_pane_key(struct window_pane *wp, struct client *c, struct session *s, - int key, struct mouse_event *m) + key_code key, struct mouse_event *m) { struct window_pane *wp2; diff --git a/xterm-keys.c b/xterm-keys.c index 10d51c78..f1490fcc 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -40,11 +40,12 @@ * We accept any but always output the latter (it comes first in the table). */ -int xterm_keys_match(const char *, const char *, size_t, size_t *, u_int *); -int xterm_keys_modifiers(const char *, size_t, size_t *, u_int *); +int xterm_keys_match(const char *, const char *, size_t, size_t *, + key_code *); +int xterm_keys_modifiers(const char *, size_t, size_t *, key_code *); struct xterm_keys_entry { - int key; + key_code key; const char *template; }; @@ -115,7 +116,7 @@ const struct xterm_keys_entry xterm_keys_table[] = { */ int xterm_keys_match(const char *template, const char *buf, size_t len, - size_t *size, u_int *modifiers) + size_t *size, key_code *modifiers) { size_t pos; int retval; @@ -148,7 +149,8 @@ xterm_keys_match(const char *template, const char *buf, size_t len, /* Find modifiers from buffer. */ int -xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers) +xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, + key_code *modifiers) { u_int flags; @@ -179,11 +181,12 @@ xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, u_int *modifiers) * key), -1 for not found, 1 for partial match. */ int -xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) +xterm_keys_find(const char *buf, size_t len, size_t *size, key_code *key) { const struct xterm_keys_entry *entry; - u_int i, modifiers; + u_int i; int matched; + key_code modifiers; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; @@ -201,11 +204,11 @@ xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) /* Lookup a key number from the table. */ char * -xterm_keys_lookup(int key) +xterm_keys_lookup(key_code key) { const struct xterm_keys_entry *entry; u_int i; - int modifiers; + key_code modifiers; char *out; modifiers = 1; From a0f3999ce7b6df32324b493fa3c8007de93d2c48 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:07:10 +0000 Subject: [PATCH 393/703] Remove the mouse_utf8_flag format as well. --- format.c | 2 -- tmux.1 | 1 - 2 files changed, 3 deletions(-) diff --git a/format.c b/format.c index 847ba08a..f799dfb5 100644 --- a/format.c +++ b/format.c @@ -1138,8 +1138,6 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) !!(wp->base.mode & MODE_MOUSE_STANDARD)); format_add(ft, "mouse_button_flag", "%d", !!(wp->base.mode & MODE_MOUSE_BUTTON)); - format_add(ft, "mouse_utf8_flag", "%d", - !!(wp->base.mode & MODE_MOUSE_UTF8)); format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); } diff --git a/tmux.1 b/tmux.1 index 4420ac3a..a35c01f8 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3394,7 +3394,6 @@ The following variables are available, where appropriate: .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" -.It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" From 1b86f520ea1620628e569ea833c7b13306c18a4e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:09:11 +0000 Subject: [PATCH 394/703] Nuke the utf8 and status-utf8 options and make tmux only a UTF-8 terminal. We still support non-UTF-8 terminals outside tmux, but inside it is always UTF-8 (as when the utf8 and status-utf8 options were on). --- cmd-choose-buffer.c | 4 +-- cmd-list-buffers.c | 2 +- format.c | 5 ++-- input.c | 5 ---- options-table.c | 10 -------- paste.c | 7 ++--- screen-write.c | 35 +++++++++++++------------ status.c | 62 +++++++++++++++++++-------------------------- tmux.1 | 40 +++++------------------------ tmux.c | 6 ----- tmux.h | 32 +++++++++++------------ window-choose.c | 5 ++-- window-copy.c | 18 +++++-------- 13 files changed, 82 insertions(+), 149 deletions(-) diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index e790de6b..dbb20fc4 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -51,7 +51,6 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) char *action, *action_data; const char *template; u_int idx; - int utf8flag; if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { cmdq_error(cmdq, "no client available"); @@ -63,7 +62,6 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); - utf8flag = options_get_number(wl->window->options, "utf8"); if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); @@ -83,7 +81,7 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) cdata->idx = idx; cdata->ft_template = xstrdup(template); - format_defaults_paste_buffer(cdata->ft, pb, utf8flag); + format_defaults_paste_buffer(cdata->ft, pb); xasprintf(&action_data, "%s", paste_buffer_name(pb)); cdata->command = cmd_template_replace(action, action_data, 1); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 37571b80..3a8a790a 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -55,7 +55,7 @@ cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) pb = NULL; while ((pb = paste_walk(pb)) != NULL) { ft = format_create(); - format_defaults_paste_buffer(ft, pb, 0); + format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); diff --git a/format.c b/format.c index f799dfb5..bcac7934 100644 --- a/format.c +++ b/format.c @@ -1144,8 +1144,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp) /* Set default format keys for paste buffer. */ void -format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, - int utf8flag) +format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { size_t bufsize; char *s; @@ -1154,7 +1153,7 @@ format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb, format_add(ft, "buffer_size", "%zu", bufsize); format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); - s = paste_make_sample(pb, utf8flag); + s = paste_make_sample(pb); format_add(ft, "buffer_sample", "%s", s); free(s); } diff --git a/input.c b/input.c index 3a02b0ce..41276d9a 100644 --- a/input.c +++ b/input.c @@ -1921,11 +1921,6 @@ input_exit_rename(struct input_ctx *ictx) int input_utf8_open(struct input_ctx *ictx) { - if (!options_get_number(ictx->wp->window->options, "utf8")) { - /* Print, and do not switch state. */ - input_print(ictx); - return (-1); - } log_debug("%s", __func__); utf8_open(&ictx->utf8data, ictx->ch); diff --git a/options-table.c b/options-table.c index d216b6d8..cf4e2d14 100644 --- a/options-table.c +++ b/options-table.c @@ -416,11 +416,6 @@ const struct options_table_entry session_options_table[] = { .default_str = "bg=green,fg=black" }, - { .name = "status-utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ - }, - { .name = "update-environment", .type = OPTIONS_TABLE_STRING, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " @@ -624,11 +619,6 @@ const struct options_table_entry window_options_table[] = { .default_num = 0 }, - { .name = "utf8", - .type = OPTIONS_TABLE_FLAG, - .default_num = 0 /* overridden in main() */ - }, - { .name = "window-active-style", .type = OPTIONS_TABLE_STYLE, .default_str = "default" diff --git a/paste.c b/paste.c index ad3b56b5..5f60914f 100644 --- a/paste.c +++ b/paste.c @@ -275,7 +275,7 @@ paste_set(char *data, size_t size, const char *name, char **cause) /* Convert start of buffer into a nice string. */ char * -paste_make_sample(struct paste_buffer *pb, int utf8flag) +paste_make_sample(struct paste_buffer *pb) { char *buf; size_t len, used; @@ -287,10 +287,7 @@ paste_make_sample(struct paste_buffer *pb, int utf8flag) len = width; buf = xreallocarray(NULL, len, 4 + 4); - if (utf8flag) - used = utf8_strvis(buf, pb->data, len, flags); - else - used = strvisx(buf, pb->data, len, flags); + used = utf8_strvis(buf, pb->data, len, flags); if (pb->size > width || used > width) strlcpy(buf + width, "...", 4); return (buf); diff --git a/screen-write.c b/screen-write.c index f80048b6..6286d21f 100644 --- a/screen-write.c +++ b/screen-write.c @@ -73,7 +73,7 @@ screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, /* Calculate string length, with embedded formatting. */ size_t -screen_write_cstrlen(int utf8flag, const char *fmt, ...) +screen_write_cstrlen(const char *fmt, ...) { va_list ap; char *msg, *msg2, *ptr, *ptr2; @@ -98,7 +98,7 @@ screen_write_cstrlen(int utf8flag, const char *fmt, ...) } *ptr2 = '\0'; - size = screen_write_strlen(utf8flag, "%s", msg2); + size = screen_write_strlen("%s", msg2); free(msg); free(msg2); @@ -108,7 +108,7 @@ screen_write_cstrlen(int utf8flag, const char *fmt, ...) /* Calculate string length. */ size_t -screen_write_strlen(int utf8flag, const char *fmt, ...) +screen_write_strlen(const char *fmt, ...) { va_list ap; char *msg; @@ -122,7 +122,7 @@ screen_write_strlen(int utf8flag, const char *fmt, ...) ptr = msg; while (*ptr != '\0') { - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); @@ -134,7 +134,8 @@ screen_write_strlen(int utf8flag, const char *fmt, ...) size += utf8data.width; } else { - size++; + if (*ptr > 0x1f && *ptr < 0x7f) + size++; ptr++; } } @@ -151,25 +152,25 @@ screen_write_puts(struct screen_write_ctx *ctx, struct grid_cell *gc, va_list ap; va_start(ap, fmt); - screen_write_vnputs(ctx, -1, gc, 0, fmt, ap); + screen_write_vnputs(ctx, -1, gc, fmt, ap); va_end(ap); } /* Write string with length limit (-1 for unlimited). */ void screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, int utf8flag, const char *fmt, ...) + struct grid_cell *gc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - screen_write_vnputs(ctx, maxlen, gc, utf8flag, fmt, ap); + screen_write_vnputs(ctx, maxlen, gc, fmt, ap); va_end(ap); } void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, - struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap) + struct grid_cell *gc, const char *fmt, va_list ap) { char *msg; struct utf8_data utf8data; @@ -180,7 +181,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, ptr = msg; while (*ptr != '\0') { - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); @@ -208,7 +209,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, if (*ptr == '\001') gc->attr ^= GRID_ATTR_CHARSET; - else { + else if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, gc, *ptr); } @@ -221,8 +222,8 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, /* Write string, similar to nputs, but with embedded formatting (#[]). */ void -screen_write_cnputs(struct screen_write_ctx *ctx, - ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) +screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, + struct grid_cell *gc, const char *fmt, ...) { struct grid_cell lgc; struct utf8_data utf8data; @@ -253,7 +254,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, continue; } - if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); @@ -279,8 +280,10 @@ screen_write_cnputs(struct screen_write_ctx *ctx, if (maxlen > 0 && size + 1 > (size_t) maxlen) break; - size++; - screen_write_putc(ctx, &lgc, *ptr); + if (*ptr > 0x1f && *ptr < 0x7f) { + size++; + screen_write_putc(ctx, &lgc, *ptr); + } ptr++; } } diff --git a/status.c b/status.c index df80d2b8..a4bdf6fa 100644 --- a/status.c +++ b/status.c @@ -29,10 +29,10 @@ #include "tmux.h" -char *status_redraw_get_left(struct client *, time_t, int, struct grid_cell *, +char *status_redraw_get_left(struct client *, time_t, struct grid_cell *, + size_t *); +char *status_redraw_get_right(struct client *, time_t, struct grid_cell *, size_t *); -char *status_redraw_get_right(struct client *, time_t, int, - struct grid_cell *, size_t *); char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); @@ -208,8 +208,8 @@ status_at_line(struct client *c) /* Retrieve options for left string. */ char * -status_redraw_get_left(struct client *c, time_t t, int utf8flag, - struct grid_cell *gc, size_t *size) +status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc, + size_t *size) { struct session *s = c->session; const char *template; @@ -222,7 +222,7 @@ status_redraw_get_left(struct client *c, time_t t, int utf8flag, left = status_replace(c, NULL, template, t); *size = options_get_number(s->options, "status-left-length"); - leftlen = screen_write_cstrlen(utf8flag, "%s", left); + leftlen = screen_write_cstrlen("%s", left); if (leftlen < *size) *size = leftlen; return (left); @@ -230,8 +230,8 @@ status_redraw_get_left(struct client *c, time_t t, int utf8flag, /* Retrieve options for right string. */ char * -status_redraw_get_right(struct client *c, time_t t, int utf8flag, - struct grid_cell *gc, size_t *size) +status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc, + size_t *size) { struct session *s = c->session; const char *template; @@ -244,7 +244,7 @@ status_redraw_get_right(struct client *c, time_t t, int utf8flag, right = status_replace(c, NULL, template, t); *size = options_get_number(s->options, "status-right-length"); - rightlen = screen_write_cstrlen(utf8flag, "%s", right); + rightlen = screen_write_cstrlen("%s", right); if (rightlen < *size) *size = rightlen; return (right); @@ -286,7 +286,7 @@ status_redraw(struct client *c) u_int offset, needed; u_int wlstart, wlwidth, wlavailable, wloffset, wlsize; size_t llen, rlen, seplen; - int larrow, rarrow, utf8flag; + int larrow, rarrow; /* No status line? */ if (c->tty.sy == 0 || !options_get_number(s->options, "status")) @@ -312,14 +312,11 @@ status_redraw(struct client *c) if (c->tty.sy <= 1) goto out; - /* Get UTF-8 flag. */ - utf8flag = options_get_number(s->options, "status-utf8"); - /* Work out left and right strings. */ memcpy(&lgc, &stdgc, sizeof lgc); - left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen); + left = status_redraw_get_left(c, t, &lgc, &llen); memcpy(&rgc, &stdgc, sizeof rgc); - right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen); + right = status_redraw_get_right(c, t, &rgc, &rlen); /* * Figure out how much space we have for the window list. If there @@ -340,15 +337,14 @@ status_redraw(struct client *c) free(wl->status_text); memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell); wl->status_text = status_print(c, wl, t, &wl->status_cell); - wl->status_width = - screen_write_cstrlen(utf8flag, "%s", wl->status_text); + wl->status_width = screen_write_cstrlen("%s", wl->status_text); if (wl == s->curw) wloffset = wlwidth; oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); - seplen = screen_write_strlen(utf8flag, "%s", sep); + seplen = screen_write_strlen("%s", sep); wlwidth += wl->status_width + seplen; } @@ -358,12 +354,12 @@ status_redraw(struct client *c) /* And draw the window list into it. */ screen_write_start(&ctx, NULL, &window_list); RB_FOREACH(wl, winlinks, &s->windows) { - screen_write_cnputs(&ctx, - -1, &wl->status_cell, utf8flag, "%s", wl->status_text); + screen_write_cnputs(&ctx, -1, &wl->status_cell, "%s", + wl->status_text); oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); - screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep); + screen_write_nputs(&ctx, -1, &stdgc, "%s", sep); } screen_write_stop(&ctx); @@ -435,7 +431,7 @@ draw: /* Draw the left string and arrow. */ screen_write_cursormove(&ctx, 0, 0); if (llen != 0) - screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left); + screen_write_cnputs(&ctx, llen, &lgc, "%s", left); if (larrow != 0) { memcpy(&gc, &stdgc, sizeof gc); if (larrow == -1) @@ -453,7 +449,7 @@ draw: } else screen_write_cursormove(&ctx, c->tty.sx - rlen, 0); if (rlen != 0) - screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right); + screen_write_cnputs(&ctx, rlen, &rgc, "%s", right); /* Figure out the offset for the window list. */ if (llen != 0) @@ -624,16 +620,13 @@ status_message_redraw(struct client *c) struct screen old_status; size_t len; struct grid_cell gc; - int utf8flag; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(s->options, "status-utf8"); - - len = screen_write_strlen(utf8flag, "%s", c->message_string); + len = screen_write_strlen("%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; @@ -642,7 +635,7 @@ status_message_redraw(struct client *c) screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); - screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string); + screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); for (; len < c->tty.sx; len++) screen_write_putc(&ctx, &gc, ' '); @@ -754,16 +747,13 @@ status_prompt_redraw(struct client *c) struct screen old_status; size_t i, size, left, len, off; struct grid_cell gc, *gcp; - int utf8flag; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); - utf8flag = options_get_number(s->options, "status-utf8"); - - len = screen_write_strlen(utf8flag, "%s", c->prompt_string); + len = screen_write_strlen("%s", c->prompt_string); if (len > c->tty.sx) len = c->tty.sx; off = 0; @@ -777,19 +767,19 @@ status_prompt_redraw(struct client *c) screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); - screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string); + screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string); left = c->tty.sx - len; if (left != 0) { - size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer); + size = screen_write_strlen("%s", c->prompt_buffer); if (c->prompt_index >= left) { off = c->prompt_index - left + 1; if (c->prompt_index == size) left--; size = left; } - screen_write_nputs( - &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off); + screen_write_nputs(&ctx, left, &gc, "%s", c->prompt_buffer + + off); for (i = len + size; i < c->tty.sx; i++) screen_write_putc(&ctx, &gc, ' '); diff --git a/tmux.1 b/tmux.1 index a35c01f8..d84265c6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -190,13 +190,11 @@ flag explicitly informs .Nm that UTF-8 is supported. .Pp -If the server is started from a client passed -.Fl u -or where UTF-8 is detected, the -.Ic utf8 -and -.Ic status-utf8 -options are enabled in the global window and session options respectively. +Note that +.Nm +itself always accepts UTF-8, this controls whether it will send UTF-8 +characters to the terminal it is running it (if not, they are replaced by +.Ql _ ) . .It Fl v Request verbose logging. This option may be specified multiple times for increasing verbosity. @@ -2770,12 +2768,6 @@ Examples are: #[fg=yellow,bold]#(apm -l)%%#[default] [#S] .Ed .Pp -By default, UTF-8 in -.Ar string -is not interpreted, to enable UTF-8, use the -.Ic status-utf8 -option. -.Pp The default is .Ql "[#S] " . .It Ic status-left-length Ar length @@ -2805,9 +2797,7 @@ As with .Ar string will be passed to .Xr strftime 3 , -character pairs are replaced, and UTF-8 is dependent on the -.Ic status-utf8 -option. +character pairs are replaced. .It Ic status-right-length Ar length Set the maximum .Ar length @@ -2827,17 +2817,6 @@ For how to specify see the .Ic message-command-style option. -.It Xo Ic status-utf8 -.Op Ic on | off -.Xc -Instruct -.Nm -to treat top-bit-set characters in the -.Ic status-left -and -.Ic status-right -strings as UTF-8; notably, this is important for wide characters. -This option defaults to off. .It Ic update-environment Ar variables Set a space-separated string containing a list of environment variables to be copied into the session environment when a new session is created or an @@ -3084,13 +3063,6 @@ command. Duplicate input to any pane to all other panes in the same window (only for panes that are not in any special mode). .Pp -.It Xo Ic utf8 -.Op Ic on | off -.Xc -Instructs -.Nm -to expect UTF-8 sequences to appear in this window. -.Pp .It Ic window-active-style Ar style Set the style for the window's active pane. For how to specify diff --git a/tmux.c b/tmux.c index 1e68d9bb..9f9f9a50 100644 --- a/tmux.c +++ b/tmux.c @@ -291,12 +291,6 @@ main(int argc, char **argv) global_w_options = options_create(NULL); options_table_populate_tree(window_options_table, global_w_options); - /* Enable UTF-8 if the first client is on UTF-8 terminal. */ - if (flags & CLIENT_UTF8) { - options_set_number(global_s_options, "status-utf8", 1); - options_set_number(global_w_options, "utf8", 1); - } - /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { if (strrchr(s, '/') != NULL) diff --git a/tmux.h b/tmux.h index 51ceefd8..618240b0 100644 --- a/tmux.h +++ b/tmux.h @@ -1456,7 +1456,7 @@ void paste_free(struct paste_buffer *); void paste_add(char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); -char *paste_make_sample(struct paste_buffer *, int); +char *paste_make_sample(struct paste_buffer *); /* format.c */ #define FORMAT_STATUS 0x1 @@ -1475,7 +1475,7 @@ void format_defaults_window(struct format_tree *, struct window *); void format_defaults_pane(struct format_tree *, struct window_pane *); void format_defaults_paste_buffer(struct format_tree *, - struct paste_buffer *, int); + struct paste_buffer *); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; @@ -1892,24 +1892,24 @@ void grid_view_delete_cells(struct grid *, u_int, u_int, u_int); char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); /* screen-write.c */ -void screen_write_start( - struct screen_write_ctx *, struct window_pane *, struct screen *); +void screen_write_start(struct screen_write_ctx *, struct window_pane *, + struct screen *); void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); -size_t printflike(2, 3) screen_write_cstrlen(int, const char *, ...); -void printflike(5, 6) screen_write_cnputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, ...); -size_t printflike(2, 3) screen_write_strlen(int, const char *, ...); +size_t printflike(1, 2) screen_write_cstrlen(const char *, ...); +void printflike(4, 5) screen_write_cnputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, const char *, ...); +size_t printflike(1, 2) screen_write_strlen(const char *, ...); void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, struct grid_cell *, const char *, ...); -void printflike(5, 6) screen_write_nputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, ...); -void screen_write_vnputs(struct screen_write_ctx *, - ssize_t, struct grid_cell *, int, const char *, va_list); -void screen_write_putc( - struct screen_write_ctx *, struct grid_cell *, u_char); -void screen_write_copy(struct screen_write_ctx *, - struct screen *, u_int, u_int, u_int, u_int); +void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, const char *, ...); +void screen_write_vnputs(struct screen_write_ctx *, ssize_t, + struct grid_cell *, const char *, va_list); +void screen_write_putc(struct screen_write_ctx *, struct grid_cell *, + u_char); +void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, + u_int, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); void screen_write_mode_set(struct screen_write_ctx *, int); void screen_write_mode_clear(struct screen_write_ctx *, int); diff --git a/window-choose.c b/window-choose.c index fa0e95c8..5f406c78 100644 --- a/window-choose.c +++ b/window-choose.c @@ -754,13 +754,12 @@ window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, struct grid_cell gc; size_t last, xoff = 0; char hdr[32], label[32]; - int utf8flag, key; + int key; if (data->callbackfn == NULL) fatalx("called before callback assigned"); last = screen_size_y(s) - 1; - utf8flag = options_get_number(wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); if (data->selected == data->top + py) style_apply(&gc, oo, "mode-style"); @@ -777,7 +776,7 @@ window_choose_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, xsnprintf(label, sizeof label, "(%c)", key); else xsnprintf(label, sizeof label, "(%d)", item->pos); - screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, utf8flag, + screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, "%*s %s %s", data->width + 2, label, /* * Add indication to tree if necessary about whether it's diff --git a/window-copy.c b/window-copy.c index d3e5b7d9..1c1ea29c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -280,13 +280,11 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) struct screen *backing = data->backing; struct screen_write_ctx back_ctx, ctx; struct grid_cell gc; - int utf8flag; u_int old_hsize, old_cy; if (backing == &wp->base) return; - utf8flag = options_get_number(wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); @@ -301,7 +299,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) } else data->backing_written = 1; old_cy = backing->cy; - screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap); + screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap); screen_write_stop(&back_ctx); data->oy += screen_hsize(data->backing) - old_hsize; @@ -1021,19 +1019,18 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) struct grid_cell gc; size_t searchlen; u_int i, last, fx, fy, px; - int utf8flag, n, wrapped, wrapflag, cis; + int n, wrapped, wrapflag, cis; const char *ptr; if (*searchstr == '\0') return; - utf8flag = options_get_number(wp->window->options, "utf8"); wrapflag = options_get_number(wp->window->options, "wrap-search"); - searchlen = screen_write_strlen(utf8flag, "%s", searchstr); + searchlen = screen_write_strlen("%s", searchstr); screen_init(&ss, searchlen, 1, 0); screen_write_start(&ctx, NULL, &ss); memcpy(&gc, &grid_default_cell, sizeof gc); - screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); + screen_write_nputs(&ctx, -1, &gc, "%s", searchstr); screen_write_stop(&ctx); fx = data->cx; @@ -1088,19 +1085,18 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) struct grid_cell gc; size_t searchlen; u_int i, first, fx, fy, px; - int utf8flag, n, wrapped, wrapflag, cis; + int n, wrapped, wrapflag, cis; const char *ptr; if (*searchstr == '\0') return; - utf8flag = options_get_number(wp->window->options, "utf8"); wrapflag = options_get_number(wp->window->options, "wrap-search"); - searchlen = screen_write_strlen(utf8flag, "%s", searchstr); + searchlen = screen_write_strlen("%s", searchstr); screen_init(&ss, searchlen, 1, 0); screen_write_start(&ctx, NULL, &ss); memcpy(&gc, &grid_default_cell, sizeof gc); - screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); + screen_write_nputs(&ctx, -1, &gc, "%s", searchstr); screen_write_stop(&ctx); fx = data->cx; From c41673f3fae397e872214880bd005cce631cf11b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:10:50 +0000 Subject: [PATCH 395/703] If we know the terminal outside tmux is not UTF-8, replace UTF-8 in error messages and whatnot with underscores the same as we do when we draw UTF-8 characters as part of the screen. --- cmd-load-buffer.c | 7 ++++++- cmd-queue.c | 19 ++++++++++++++++--- tmux.h | 1 + utf8.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 9352cbe5..40bea9b7 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -133,7 +133,7 @@ void cmd_load_buffer_callback(struct client *c, int closed, void *data) { const char *bufname = data; - char *pdata, *cause; + char *pdata, *cause, *saved; size_t psize; if (!closed) @@ -154,6 +154,11 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) if (paste_set(pdata, psize, bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ + if (~c->flags & CLIENT_UTF8) { + saved = cause; + cause = utf8_sanitize(saved); + free(saved); + } evbuffer_add_printf(c->stderr_data, "%s", cause); server_push_stderr(c); free(pdata); diff --git a/cmd-queue.c b/cmd-queue.c index ff8c69cb..5015981c 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -69,14 +69,21 @@ cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) struct client *c = cmdq->client; struct window *w; va_list ap; + char *tmp, *msg; va_start(ap, fmt); if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { - evbuffer_add_vprintf(c->stdout_data, fmt, ap); - + if (~c->flags & CLIENT_UTF8) { + vasprintf(&tmp, fmt, ap); + msg = utf8_sanitize(tmp); + free(tmp); + evbuffer_add(c->stdout_data, msg, strlen(msg)); + free(msg); + } else + evbuffer_add_vprintf(c->stdout_data, fmt, ap); evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); } else { @@ -101,6 +108,7 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) va_list ap; char *msg; size_t msglen; + char *tmp; va_start(ap, fmt); msglen = xvasprintf(&msg, fmt, ap); @@ -109,9 +117,14 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) if (c == NULL) cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { + if (~c->flags & CLIENT_UTF8) { + tmp = msg; + msg = utf8_sanitize(tmp); + free(tmp); + msglen = strlen(msg); + } evbuffer_add(c->stderr_data, msg, msglen); evbuffer_add(c->stderr_data, "\n", 1); - server_push_stderr(c); c->retval = 1; } else { diff --git a/tmux.h b/tmux.h index 618240b0..de19159d 100644 --- a/tmux.h +++ b/tmux.h @@ -2189,6 +2189,7 @@ u_int utf8_combine(const struct utf8_data *); int utf8_split(u_int, struct utf8_data *); u_int utf8_split2(u_int, u_char *); int utf8_strvis(char *, const char *, size_t, int); +char *utf8_sanitize(const char *); struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); diff --git a/utf8.c b/utf8.c index e61bf996..cb20ea6d 100644 --- a/utf8.c +++ b/utf8.c @@ -585,6 +585,50 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) return (dst - start); } +/* + * Sanitize a string, changing any UTF-8 characters to '_'. Caller should free + * the returned string. Anything not valid printable ASCII or UTF-8 is + * stripped. + */ +char * +utf8_sanitize(const char *src) +{ + char *dst; + size_t n; + int more; + struct utf8_data utf8data; + u_int i; + + dst = NULL; + + n = 0; + while (*src != '\0') { + dst = xreallocarray(dst, n + 1, sizeof *dst); + if (utf8_open(&utf8data, *src)) { + more = 1; + while (*++src != '\0' && more) + more = utf8_append(&utf8data, *src); + if (!more) { + dst = xreallocarray(dst, n + utf8data.width, + sizeof *dst); + for (i = 0; i < utf8data.width; i++) + dst[n++] = '_'; + continue; + } + src -= utf8data.have; + } + if (*src > 0x1f && *src < 0x7f) + dst[n] = *src; + src++; + + n++; + } + + dst = xreallocarray(dst, n + 1, sizeof *dst); + dst[n] = '\0'; + return (dst); +} + /* * Convert a string into a buffer of UTF-8 characters. Terminated by size == 0. * Caller frees. From 0cc812ae342d1a71c0337db8ffb4d7701668cb38 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 11:24:08 +0000 Subject: [PATCH 396/703] tmux is UTF-8, so if $TMUX is set (tmux running in tmux), the client is UTF-8. Also try to make the existing checks more readable. --- tmux.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tmux.c b/tmux.c index 9f9f9a50..5429a7cb 100644 --- a/tmux.c +++ b/tmux.c @@ -191,8 +191,9 @@ find_home(void) int main(int argc, char **argv) { - char *s, *path, *label, **var, tmp[PATH_MAX]; - int opt, flags, keys; + char *path, *label, **var, tmp[PATH_MAX]; + const char *s; + int opt, flags, keys; #ifdef DEBUG malloc_options = (char *) "AFGJPX"; @@ -258,20 +259,25 @@ main(int argc, char **argv) "proc exec tty ps", NULL) != 0) err(1, "pledge"); - if (!(flags & CLIENT_UTF8)) { - /* - * If the user has set whichever of LC_ALL, LC_CTYPE or LANG - * exist (in that order) to contain UTF-8, it is a safe - * assumption that either they are using a UTF-8 terminal, or - * if not they know that output from UTF-8-capable programs may - * be wrong. - */ - if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { - if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') - s = getenv("LANG"); - } - if (s != NULL && (strcasestr(s, "UTF-8") != NULL || - strcasestr(s, "UTF8") != NULL)) + /* + * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. + * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain + * UTF-8, it is a safe assumption that either they are using a UTF-8 + * terminal, or if not they know that output from UTF-8-capable + * programs may be wrong. + */ + if (getenv("TMUX") != NULL) + flags |= CLIENT_UTF8; + else { + s = getenv("LC_ALL"); + if (s == NULL || *s == '\0') + s = getenv("LC_CTYPE"); + if (s == NULL || *s == '\0') + s = getenv("LANG"); + if (s == NULL || *s == '\0') + s = ""; + if (strcasestr(s, "UTF-8") != NULL || + strcasestr(s, "UTF8") != NULL) flags |= CLIENT_UTF8; } From d6daf37df4ccd7589d2d8f6911bf7270f12d1672 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 12:19:57 +0000 Subject: [PATCH 397/703] Tidy utf8.c a little: build table on first use, and make utf8_width take a u_int rather than splitting and then combining again in utf8_split. --- server.c | 1 - tmux.h | 2 +- utf8.c | 81 ++++++++++++++++++++------------------------------------ 3 files changed, 30 insertions(+), 54 deletions(-) diff --git a/server.c b/server.c index bc3fa51d..2808c0cc 100644 --- a/server.c +++ b/server.c @@ -186,7 +186,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) TAILQ_INIT(&session_groups); mode_key_init_trees(); key_bindings_init(); - utf8_build(); start_time = time(NULL); diff --git a/tmux.h b/tmux.h index de19159d..0d2d773d 100644 --- a/tmux.h +++ b/tmux.h @@ -2181,7 +2181,7 @@ void session_group_synchronize1(struct session *, struct session *); void session_renumber_windows(struct session *); /* utf8.c */ -void utf8_build(void); +u_int utf8_width(u_int); void utf8_set(struct utf8_data *, u_char); int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); diff --git a/utf8.c b/utf8.c index cb20ea6d..0926f4bc 100644 --- a/utf8.c +++ b/utf8.c @@ -35,7 +35,7 @@ struct utf8_width_entry { }; /* Sorted, then repeatedly split in the middle to balance the tree. */ -struct utf8_width_entry utf8_width_table[] = { +static struct utf8_width_entry utf8_width_table[] = { { 0x00b41, 0x00b44, 0, NULL, NULL }, { 0x008e4, 0x00902, 0, NULL, NULL }, { 0x006d6, 0x006dd, 0, NULL, NULL }, @@ -344,12 +344,9 @@ struct utf8_width_entry utf8_width_table[] = { { 0xe0100, 0xe01ef, 0, NULL, NULL }, { 0x100000, 0x10fffd, 0, NULL, NULL }, }; +static struct utf8_width_entry *utf8_width_root = NULL; -struct utf8_width_entry *utf8_width_root = NULL; - -int utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *); -u_int utf8_combine(const struct utf8_data *); -u_int utf8_width(const struct utf8_data *); +static void utf8_build(void); /* Set a single character. */ void @@ -405,40 +402,20 @@ utf8_append(struct utf8_data *utf8data, u_char ch) if (utf8data->have != utf8data->size) return (1); - utf8data->width = utf8_width(utf8data); - return (0); -} - -/* Check if two width tree entries overlap. */ -int -utf8_overlap(struct utf8_width_entry *item1, struct utf8_width_entry *item2) -{ - if (item1->first >= item2->first && item1->first <= item2->last) - return (1); - if (item1->last >= item2->first && item1->last <= item2->last) - return (1); - if (item2->first >= item1->first && item2->first <= item1->last) - return (1); - if (item2->last >= item1->first && item2->last <= item1->last) - return (1); + utf8data->width = utf8_width(utf8_combine(utf8data)); return (0); } /* Build UTF-8 width tree. */ -void +static void utf8_build(void) { struct utf8_width_entry **ptr, *item, *node; - u_int i, j; + u_int i; for (i = 0; i < nitems(utf8_width_table); i++) { item = &utf8_width_table[i]; - for (j = 0; j < nitems(utf8_width_table); j++) { - if (i != j && utf8_overlap(item, &utf8_width_table[j])) - log_fatalx("utf8 overlap: %u %u", i, j); - } - ptr = &utf8_width_root; while (*ptr != NULL) { node = *ptr; @@ -451,6 +428,27 @@ utf8_build(void) } } +/* Lookup width of UTF-8 data in tree. */ +u_int +utf8_width(u_int uc) +{ + struct utf8_width_entry *item; + + if (utf8_width_root == NULL) + utf8_build(); + + item = utf8_width_root; + while (item != NULL) { + if (uc < item->first) + item = item->left; + else if (uc > item->last) + item = item->right; + else + return (item->width); + } + return (1); +} + /* Combine UTF-8 into 32-bit Unicode. */ u_int utf8_combine(const struct utf8_data *utf8data) @@ -481,7 +479,7 @@ utf8_combine(const struct utf8_data *utf8data) return (value); } -/* Split a UTF-8 character. */ +/* Split 32-bit Unicode into UTF-8. */ int utf8_split(u_int uc, struct utf8_data *utf8data) { @@ -505,7 +503,7 @@ utf8_split(u_int uc, struct utf8_data *utf8data) utf8data->data[3] = 0x80 | (uc & 0x3f); } else return (-1); - utf8data->width = utf8_width(utf8data); + utf8data->width = utf8_width(uc); return (0); } @@ -522,27 +520,6 @@ utf8_split2(u_int uc, u_char *ptr) return (1); } -/* Lookup width of UTF-8 data in tree. */ -u_int -utf8_width(const struct utf8_data *utf8data) -{ - struct utf8_width_entry *item; - u_int value; - - value = utf8_combine(utf8data); - - item = utf8_width_root; - while (item != NULL) { - if (value < item->first) - item = item->left; - else if (value > item->last) - item = item->right; - else - return (item->width); - } - return (1); -} - /* * Encode len characters from src into dst, which is guaranteed to have four * bytes available for each character from src (for \abc or UTF-8) plus space From 1da7475d0e2cbfb8b301fcad5cbcfb3ee4c087bb Mon Sep 17 00:00:00 2001 From: jmc Date: Thu, 12 Nov 2015 12:36:34 +0000 Subject: [PATCH 398/703] tweak previous; ok nicm --- tmux.1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tmux.1 b/tmux.1 index d84265c6..e0646697 100644 --- a/tmux.1 +++ b/tmux.1 @@ -192,8 +192,8 @@ that UTF-8 is supported. .Pp Note that .Nm -itself always accepts UTF-8, this controls whether it will send UTF-8 -characters to the terminal it is running it (if not, they are replaced by +itself always accepts UTF-8; this controls whether it will send UTF-8 +characters to the terminal it is running (if not, they are replaced by .Ql _ ) . .It Fl v Request verbose logging. @@ -2796,8 +2796,8 @@ As with .Ic status-left , .Ar string will be passed to -.Xr strftime 3 , -character pairs are replaced. +.Xr strftime 3 +and character pairs are replaced. .It Ic status-right-length Ar length Set the maximum .Ar length From a209ea3953ba16742f6f6bb19b76ffdb1200960e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 12:43:36 +0000 Subject: [PATCH 399/703] Add utf8_padcstr and use it to align columns in list-keys. --- cmd-list-keys.c | 27 +++++++++++++++++---------- tmux.h | 1 + utf8.c | 21 +++++++++++++++++++++ 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index c76f9f47..3b6afa3e 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -18,6 +18,7 @@ #include +#include #include #include "tmux.h" @@ -54,10 +55,9 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct key_table *table; struct key_binding *bd; const char *key, *tablename, *r; - char tmp[BUFSIZ]; + char *cp, tmp[BUFSIZ]; size_t used; int repeat, width, tablewidth, keywidth; - u_int i; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, cmdq)); @@ -82,7 +82,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) if (bd->can_repeat) repeat = 1; - width = strlen(table->name); + width = utf8_cstrwidth(table->name); if (width > tablewidth) tablewidth = width; width = utf8_cstrwidth(key); @@ -103,13 +103,20 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) r = "-r "; else r = " "; - used = xsnprintf(tmp, sizeof tmp, "%s-T %-*s %s", r, - (int)tablewidth, table->name, key); - for (i = 0; i < keywidth - utf8_cstrwidth(key); i++) { - if (strlcat(tmp, " ", sizeof tmp) < sizeof tmp) - used++; - } - if (used < sizeof tmp) { + xsnprintf(tmp, sizeof tmp, "%s-T ", r); + + cp = utf8_padcstr(table->name, tablewidth); + strlcat(tmp, cp, sizeof tmp); + strlcat(tmp, " ", sizeof tmp); + free(cp); + + cp = utf8_padcstr(key, keywidth); + strlcat(tmp, cp, sizeof tmp); + strlcat(tmp, " ", sizeof tmp); + free(cp); + + used = strlen(tmp); + if (used < (sizeof tmp) - 1) { cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); } diff --git a/tmux.h b/tmux.h index 0d2d773d..cb7ed2c3 100644 --- a/tmux.h +++ b/tmux.h @@ -2194,6 +2194,7 @@ struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); char *utf8_trimcstr(const char *, u_int); +char *utf8_padcstr(const char *, u_int); /* procname.c */ char *get_proc_name(int, char *); diff --git a/utf8.c b/utf8.c index 0926f4bc..db738020 100644 --- a/utf8.c +++ b/utf8.c @@ -713,3 +713,24 @@ utf8_trimcstr(const char *s, u_int width) free(tmp); return (out); } + +/* Pad UTF-8 string to width. Caller frees. */ +char * +utf8_padcstr(const char *s, u_int width) +{ + size_t slen; + char *out; + u_int n, i; + + n = utf8_cstrwidth(s); + if (n >= width) + return (xstrdup(s)); + + slen = strlen(s); + out = xmalloc(slen + 1 + (width - n)); + memcpy(out, s, slen); + for (i = n; i < width; i++) + out[slen++] = ' '; + out[slen] = '\0'; + return (out); +} From f2d03f4fddddbdbc96566c55f92718c1f4319b33 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 14:50:57 +0000 Subject: [PATCH 400/703] grid_put_utf8 is unused, remove it. --- grid.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/grid.c b/grid.c index 99dafab2..b8c9cbb7 100644 --- a/grid.c +++ b/grid.c @@ -42,10 +42,6 @@ const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; memcpy(&gd->linedata[py].celldata[px], \ gc, sizeof gd->linedata[py].celldata[px]); \ } while (0) -#define grid_put_utf8(gd, px, py, gc) do { \ - memcpy(&gd->linedata[py].utf8data[px], \ - gc, sizeof gd->linedata[py].utf8data[px]); \ -} while (0) int grid_check_y(struct grid *, u_int); From e71a9154126c7ee853b9a657678de40d475279eb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 12 Nov 2015 22:04:37 +0000 Subject: [PATCH 401/703] Rename overly-long utf8data to ud throughout. --- input-keys.c | 10 ++--- key-string.c | 20 ++++----- screen-write.c | 38 ++++++++--------- tty-keys.c | 20 ++++----- utf8.c | 114 ++++++++++++++++++++++++------------------------- 5 files changed, 101 insertions(+), 101 deletions(-) diff --git a/input-keys.c b/input-keys.c index a761a088..3938c185 100644 --- a/input-keys.c +++ b/input-keys.c @@ -144,7 +144,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) size_t dlen; char *out; key_code justkey; - struct utf8_data utf8data; + struct utf8_data ud; log_debug("writing key 0x%llx (%s) to %%%u", key, key_string_lookup_key(key), wp->id); @@ -164,16 +164,16 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) if (key != KEYC_NONE && justkey < 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); - utf8data.data[0] = justkey; - bufferevent_write(wp->event, &utf8data.data[0], 1); + ud.data[0] = justkey; + bufferevent_write(wp->event, &ud.data[0], 1); return; } if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { - if (utf8_split(justkey, &utf8data) != 0) + if (utf8_split(justkey, &ud) != 0) return; if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); - bufferevent_write(wp->event, utf8data.data, utf8data.size); + bufferevent_write(wp->event, ud.data, ud.size); return; } diff --git a/key-string.c b/key-string.c index 4285e129..c2230218 100644 --- a/key-string.c +++ b/key-string.c @@ -146,7 +146,7 @@ key_string_lookup_string(const char *string) u_short u; int size; key_code modifiers; - struct utf8_data utf8data; + struct utf8_data ud; u_int i; /* Is this a hexadecimal value? */ @@ -173,12 +173,12 @@ key_string_lookup_string(const char *string) return (KEYC_NONE); } else { /* Try as a UTF-8 key. */ - if (utf8_open(&utf8data, (u_char)*string)) { - if (strlen(string) != utf8data.size) + if (utf8_open(&ud, (u_char)*string)) { + if (strlen(string) != ud.size) return (KEYC_NONE); - for (i = 1; i < utf8data.size; i++) - utf8_append(&utf8data, (u_char)string[i]); - key = utf8_combine(&utf8data); + for (i = 1; i < ud.size; i++) + utf8_append(&ud, (u_char)string[i]); + key = utf8_combine(&ud); return (key | modifiers); } @@ -213,7 +213,7 @@ key_string_lookup_key(key_code key) static char out[24]; char tmp[8]; u_int i; - struct utf8_data utf8data; + struct utf8_data ud; *out = '\0'; @@ -253,9 +253,9 @@ key_string_lookup_key(key_code key) /* Is this a UTF-8 key? */ if (key > 127 && key < KEYC_BASE) { - if (utf8_split(key, &utf8data) == 0) { - memcpy(out, utf8data.data, utf8data.size); - out[utf8data.size] = '\0'; + if (utf8_split(key, &ud) == 0) { + memcpy(out, ud.data, ud.size); + out[ud.size] = '\0'; return (out); } } diff --git a/screen-write.c b/screen-write.c index 6286d21f..4bf3ec72 100644 --- a/screen-write.c +++ b/screen-write.c @@ -112,7 +112,7 @@ screen_write_strlen(const char *fmt, ...) { va_list ap; char *msg; - struct utf8_data utf8data; + struct utf8_data ud; u_char *ptr; size_t left, size = 0; @@ -122,17 +122,17 @@ screen_write_strlen(const char *fmt, ...) ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while (utf8_append(&ud, *ptr)) ptr++; ptr++; - size += utf8data.width; + size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) size++; @@ -173,7 +173,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, const char *fmt, va_list ap) { char *msg; - struct utf8_data utf8data; + struct utf8_data ud; u_char *ptr; size_t left, size = 0; @@ -181,27 +181,27 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while (utf8_append(&ud, *ptr)) ptr++; ptr++; if (maxlen > 0 && - size + utf8data.width > (size_t) maxlen) { + size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } - size += utf8data.width; + size += ud.width; - grid_cell_set(gc, &utf8data); + grid_cell_set(gc, &ud); screen_write_cell(ctx, gc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) @@ -226,7 +226,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, const char *fmt, ...) { struct grid_cell lgc; - struct utf8_data utf8data; + struct utf8_data ud; va_list ap; char *msg; u_char *ptr, *last; @@ -254,27 +254,27 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, continue; } - if (*ptr > 0x7f && utf8_open(&utf8data, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { ptr++; left = strlen(ptr); - if (left < utf8data.size - 1) + if (left < ud.size - 1) break; - while (utf8_append(&utf8data, *ptr)) + while (utf8_append(&ud, *ptr)) ptr++; ptr++; if (maxlen > 0 && - size + utf8data.width > (size_t) maxlen) { + size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } - size += utf8data.width; + size += ud.width; - grid_cell_set(&lgc, &utf8data); + grid_cell_set(&lgc, &ud); screen_write_cell(ctx, &lgc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) diff --git a/tty-keys.c b/tty-keys.c index 31adf0f0..6a64ef15 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -474,7 +474,7 @@ tty_keys_next(struct tty *tty) cc_t bspace; int delay, expired = 0; key_code key; - struct utf8_data utf8data; + struct utf8_data ud; u_int i; /* Get key buffer. */ @@ -539,16 +539,16 @@ first_key: } /* Is this valid UTF-8? */ - if (utf8_open(&utf8data, (u_char)*buf)) { - size = utf8data.size; + if (utf8_open(&ud, (u_char)*buf)) { + size = ud.size; if (len < size) { if (expired) goto discard_key; goto partial_key; } for (i = 1; i < size; i++) - utf8_append(&utf8data, (u_char)buf[i]); - key = utf8_combine(&utf8data); + utf8_append(&ud, (u_char)buf[i]); + key = utf8_combine(&ud); log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); goto complete_key; } @@ -650,7 +650,7 @@ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; - struct utf8_data utf8data; + struct utf8_data ud; u_int i, value, x, y, b, sgr_b; u_char sgr_type, c; @@ -693,14 +693,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { - if (utf8_open(&utf8data, buf[*size])) { - if (utf8data.size != 2) + if (utf8_open(&ud, buf[*size])) { + if (ud.size != 2) return (-1); (*size)++; if (len <= *size) return (1); - utf8_append(&utf8data, buf[*size]); - value = utf8_combine(&utf8data); + utf8_append(&ud, buf[*size]); + value = utf8_combine(&ud); } else value = (u_char)buf[*size]; (*size)++; diff --git a/utf8.c b/utf8.c index db738020..82471c66 100644 --- a/utf8.c +++ b/utf8.c @@ -350,12 +350,12 @@ static void utf8_build(void); /* Set a single character. */ void -utf8_set(struct utf8_data *utf8data, u_char ch) +utf8_set(struct utf8_data *ud, u_char ch) { - *utf8data->data = ch; - utf8data->size = 1; + *ud->data = ch; + ud->size = 1; - utf8data->width = 1; + ud->width = 1; } /* @@ -368,18 +368,18 @@ utf8_set(struct utf8_data *utf8data, u_char ch) * Returns 1 if more UTF-8 to come, 0 if not UTF-8. */ int -utf8_open(struct utf8_data *utf8data, u_char ch) +utf8_open(struct utf8_data *ud, u_char ch) { - memset(utf8data, 0, sizeof *utf8data); + memset(ud, 0, sizeof *ud); if (ch >= 0xc2 && ch <= 0xdf) - utf8data->size = 2; + ud->size = 2; else if (ch >= 0xe0 && ch <= 0xef) - utf8data->size = 3; + ud->size = 3; else if (ch >= 0xf0 && ch <= 0xf4) - utf8data->size = 4; + ud->size = 4; else return (0); - utf8_append(utf8data, ch); + utf8_append(ud, ch); return (1); } @@ -389,20 +389,20 @@ utf8_open(struct utf8_data *utf8data, u_char ch) * Returns 1 if more UTF-8 data to come, 0 if finished. */ int -utf8_append(struct utf8_data *utf8data, u_char ch) +utf8_append(struct utf8_data *ud, u_char ch) { /* XXX this should do validity checks too! */ - if (utf8data->have >= utf8data->size) + if (ud->have >= ud->size) fatalx("UTF-8 character overflow"); - if (utf8data->size > sizeof utf8data->data) + if (ud->size > sizeof ud->data) fatalx("UTF-8 character size too large"); - utf8data->data[utf8data->have++] = ch; - if (utf8data->have != utf8data->size) + ud->data[ud->have++] = ch; + if (ud->have != ud->size) return (1); - utf8data->width = utf8_width(utf8_combine(utf8data)); + ud->width = utf8_width(utf8_combine(ud)); return (0); } @@ -451,29 +451,29 @@ utf8_width(u_int uc) /* Combine UTF-8 into 32-bit Unicode. */ u_int -utf8_combine(const struct utf8_data *utf8data) +utf8_combine(const struct utf8_data *ud) { u_int value; value = 0xff; - switch (utf8data->size) { + switch (ud->size) { case 1: - value = utf8data->data[0]; + value = ud->data[0]; break; case 2: - value = utf8data->data[1] & 0x3f; - value |= (utf8data->data[0] & 0x1f) << 6; + value = ud->data[1] & 0x3f; + value |= (ud->data[0] & 0x1f) << 6; break; case 3: - value = utf8data->data[2] & 0x3f; - value |= (utf8data->data[1] & 0x3f) << 6; - value |= (utf8data->data[0] & 0xf) << 12; + value = ud->data[2] & 0x3f; + value |= (ud->data[1] & 0x3f) << 6; + value |= (ud->data[0] & 0xf) << 12; break; case 4: - value = utf8data->data[3] & 0x3f; - value |= (utf8data->data[2] & 0x3f) << 6; - value |= (utf8data->data[1] & 0x3f) << 12; - value |= (utf8data->data[0] & 0x7) << 18; + value = ud->data[3] & 0x3f; + value |= (ud->data[2] & 0x3f) << 6; + value |= (ud->data[1] & 0x3f) << 12; + value |= (ud->data[0] & 0x7) << 18; break; } return (value); @@ -481,29 +481,29 @@ utf8_combine(const struct utf8_data *utf8data) /* Split 32-bit Unicode into UTF-8. */ int -utf8_split(u_int uc, struct utf8_data *utf8data) +utf8_split(u_int uc, struct utf8_data *ud) { if (uc < 0x7f) { - utf8data->size = 1; - utf8data->data[0] = uc; + ud->size = 1; + ud->data[0] = uc; } else if (uc < 0x7ff) { - utf8data->size = 2; - utf8data->data[0] = 0xc0 | ((uc >> 6) & 0x1f); - utf8data->data[1] = 0x80 | (uc & 0x3f); + ud->size = 2; + ud->data[0] = 0xc0 | ((uc >> 6) & 0x1f); + ud->data[1] = 0x80 | (uc & 0x3f); } else if (uc < 0xffff) { - utf8data->size = 3; - utf8data->data[0] = 0xe0 | ((uc >> 12) & 0xf); - utf8data->data[1] = 0x80 | ((uc >> 6) & 0x3f); - utf8data->data[2] = 0x80 | (uc & 0x3f); + ud->size = 3; + ud->data[0] = 0xe0 | ((uc >> 12) & 0xf); + ud->data[1] = 0x80 | ((uc >> 6) & 0x3f); + ud->data[2] = 0x80 | (uc & 0x3f); } else if (uc < 0x1fffff) { - utf8data->size = 4; - utf8data->data[0] = 0xf0 | ((uc >> 18) & 0x7); - utf8data->data[1] = 0x80 | ((uc >> 12) & 0x3f); - utf8data->data[2] = 0x80 | ((uc >> 6) & 0x3f); - utf8data->data[3] = 0x80 | (uc & 0x3f); + ud->size = 4; + ud->data[0] = 0xf0 | ((uc >> 18) & 0x7); + ud->data[1] = 0x80 | ((uc >> 12) & 0x3f); + ud->data[2] = 0x80 | ((uc >> 6) & 0x3f); + ud->data[3] = 0x80 | (uc & 0x3f); } else return (-1); - utf8data->width = utf8_width(uc); + ud->width = utf8_width(uc); return (0); } @@ -528,7 +528,7 @@ utf8_split2(u_int uc, u_char *ptr) int utf8_strvis(char *dst, const char *src, size_t len, int flag) { - struct utf8_data utf8data; + struct utf8_data ud; const char *start, *end; int more; size_t i; @@ -537,18 +537,18 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) end = src + len; while (src < end) { - if (utf8_open(&utf8data, *src)) { + if (utf8_open(&ud, *src)) { more = 1; while (++src < end && more) - more = utf8_append(&utf8data, *src); + more = utf8_append(&ud, *src); if (!more) { /* UTF-8 character finished. */ - for (i = 0; i < utf8data.size; i++) - *dst++ = utf8data.data[i]; + for (i = 0; i < ud.size; i++) + *dst++ = ud.data[i]; continue; - } else if (utf8data.have > 0) { + } else if (ud.have > 0) { /* Not a complete UTF-8 character. */ - src -= utf8data.have; + src -= ud.have; } } if (src < end - 1) @@ -573,7 +573,7 @@ utf8_sanitize(const char *src) char *dst; size_t n; int more; - struct utf8_data utf8data; + struct utf8_data ud; u_int i; dst = NULL; @@ -581,18 +581,18 @@ utf8_sanitize(const char *src) n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); - if (utf8_open(&utf8data, *src)) { + if (utf8_open(&ud, *src)) { more = 1; while (*++src != '\0' && more) - more = utf8_append(&utf8data, *src); + more = utf8_append(&ud, *src); if (!more) { - dst = xreallocarray(dst, n + utf8data.width, + dst = xreallocarray(dst, n + ud.width, sizeof *dst); - for (i = 0; i < utf8data.width; i++) + for (i = 0; i < ud.width; i++) dst[n++] = '_'; continue; } - src -= utf8data.have; + src -= ud.have; } if (*src > 0x1f && *src < 0x7f) dst[n] = *src; From c5689a5a4031a43769b8b721cafa6d1eab6abc44 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Nov 2015 08:09:28 +0000 Subject: [PATCH 402/703] Long overdue change to the way we store cells in the grid: now, instead of storing a full grid_cell with UTF-8 data and everything, store a new type grid_cell_entry. This can either be the cell itself (for ASCII cells), or an offset into an extended array (per line) for UTF-8 data. This avoid a large (8 byte) overhead on non-UTF-8 cells (by far the majority for most users) without the complexity of the shadow array we had before. Grid memory without any UTF-8 is about half. The disadvantage that cells can no longer be modified in place and need to be copied out of the grid and back but it turned out to be lot less complicated than I expected. --- Makefile | 1 - format.c | 1 + grid-cell.c | 55 ---------------- grid-view.c | 19 ++---- grid.c | 169 ++++++++++++++++++++++++++++++++++++------------- input.c | 4 +- screen-write.c | 92 ++++++++++++--------------- status.c | 7 +- tmux.h | 72 +++++++++++---------- tty.c | 37 ++++------- utf8.c | 17 +++++ window-copy.c | 105 ++++++++++++++---------------- 12 files changed, 297 insertions(+), 282 deletions(-) delete mode 100644 grid-cell.c diff --git a/Makefile b/Makefile index 89c8c5c8..10c6a783 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,6 @@ SRCS= alerts.c \ control-notify.c \ environ.c \ format.c \ - grid-cell.c \ grid-view.c \ grid.c \ input-keys.c \ diff --git a/format.c b/format.c index bcac7934..389026ac 100644 --- a/format.c +++ b/format.c @@ -415,6 +415,7 @@ format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) for (i = 0; i < gd->hsize; i++) { gl = &gd->linedata[i]; size += gl->cellsize * sizeof *gl->celldata; + size += gl->extdsize * sizeof *gl->extddata; } size += gd->hsize * sizeof *gd->linedata; diff --git a/grid-cell.c b/grid-cell.c deleted file mode 100644 index 09643a9c..00000000 --- a/grid-cell.c +++ /dev/null @@ -1,55 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2012 Nicholas Marriott - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#include - -#include "tmux.h" - -/* Get cell width. */ -u_int -grid_cell_width(const struct grid_cell *gc) -{ - return (gc->xstate >> 4); -} - -/* Get cell data. */ -void -grid_cell_get(const struct grid_cell *gc, struct utf8_data *ud) -{ - ud->size = gc->xstate & 0xf; - ud->width = gc->xstate >> 4; - memcpy(ud->data, gc->xdata, ud->size); -} - -/* Set cell data. */ -void -grid_cell_set(struct grid_cell *gc, const struct utf8_data *ud) -{ - memcpy(gc->xdata, ud->data, ud->size); - gc->xstate = (ud->width << 4) | ud->size; -} - -/* Set a single character as cell data. */ -void -grid_cell_one(struct grid_cell *gc, u_char ch) -{ - *gc->xdata = ch; - gc->xstate = (1 << 4) | 1; -} diff --git a/grid-view.c b/grid-view.c index badabd56..5edcfd53 100644 --- a/grid-view.c +++ b/grid-view.c @@ -30,24 +30,17 @@ #define grid_view_x(gd, x) (x) #define grid_view_y(gd, y) ((gd)->hsize + (y)) -/* Get cell for reading. */ -const struct grid_cell * -grid_view_peek_cell(struct grid *gd, u_int px, u_int py) +/* Get cel. */ +void +grid_view_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { - return (grid_peek_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); -} - -/* Get cell for writing. */ -struct grid_cell * -grid_view_get_cell(struct grid *gd, u_int px, u_int py) -{ - return (grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); + grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } /* Set cell. */ void -grid_view_set_cell( - struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +grid_view_set_cell(struct grid *gd, u_int px, u_int py, + const struct grid_cell *gc) { grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } diff --git a/grid.c b/grid.c index b8c9cbb7..36cde074 100644 --- a/grid.c +++ b/grid.c @@ -36,15 +36,17 @@ */ /* Default grid cell data. */ -const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; - -#define grid_put_cell(gd, px, py, gc) do { \ - memcpy(&gd->linedata[py].celldata[px], \ - gc, sizeof gd->linedata[py].celldata[px]); \ -} while (0) +const struct grid_cell grid_default_cell = { + 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } +}; +const struct grid_cell_entry grid_default_entry = { + 0, { .data = { 0, 8, 8, ' ' } } +}; int grid_check_y(struct grid *, u_int); +void grid_reflow_copy(struct grid_line *, u_int, struct grid_line *l, + u_int, u_int); void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, u_int); @@ -54,6 +56,13 @@ size_t grid_string_cells_bg(const struct grid_cell *, int *); void grid_string_cells_code(const struct grid_cell *, const struct grid_cell *, char *, size_t, int); +/* Copy default into a cell. */ +static void +grid_clear_cell(struct grid *gd, u_int px, u_int py) +{ + gd->linedata[py].celldata[px] = grid_default_entry; +} + /* Check grid y position. */ int grid_check_y(struct grid *gd, u_int py) @@ -95,6 +104,7 @@ grid_destroy(struct grid *gd) for (yy = 0; yy < gd->hsize + gd->sy; yy++) { gl = &gd->linedata[yy]; free(gl->celldata); + free(gl->extddata); } free(gd->linedata); @@ -107,7 +117,7 @@ int grid_compare(struct grid *ga, struct grid *gb) { struct grid_line *gla, *glb; - struct grid_cell *gca, *gcb; + struct grid_cell gca, gcb; u_int xx, yy; if (ga->sx != gb->sx || ga->sy != gb->sy) @@ -118,10 +128,10 @@ grid_compare(struct grid *ga, struct grid *gb) glb = &gb->linedata[yy]; if (gla->cellsize != glb->cellsize) return (1); - for (xx = 0; xx < ga->sx; xx++) { - gca = &gla->celldata[xx]; - gcb = &glb->celldata[xx]; - if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0) + for (xx = 0; xx < gla->cellsize; xx++) { + grid_get_cell(ga, xx, yy, &gca); + grid_get_cell(gb, xx, yy, &gcb); + if (memcmp(&gca, &gcb, sizeof (struct grid_cell)) != 0) return (1); } } @@ -224,7 +234,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx) gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); for (xx = gl->cellsize; xx < sx; xx++) - grid_put_cell(gd, xx, py, &grid_default_cell); + grid_clear_cell(gd, xx, py); gl->cellsize = sx; } @@ -238,37 +248,72 @@ grid_peek_line(struct grid *gd, u_int py) } /* Get cell for reading. */ -const struct grid_cell * -grid_peek_cell(struct grid *gd, u_int px, u_int py) +void +grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { - if (grid_check_y(gd, py) != 0) - return (&grid_default_cell); + struct grid_line *gl; + struct grid_cell_entry *gce; - if (px >= gd->linedata[py].cellsize) - return (&grid_default_cell); - return (&gd->linedata[py].celldata[px]); -} + if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) { + memcpy(gc, &grid_default_cell, sizeof *gc); + return; + } -/* Get cell at relative position (for writing). */ -struct grid_cell * -grid_get_cell(struct grid *gd, u_int px, u_int py) -{ - if (grid_check_y(gd, py) != 0) - return (NULL); + gl = &gd->linedata[py]; + gce = &gl->celldata[px]; - grid_expand_line(gd, py, px + 1); - return (&gd->linedata[py].celldata[px]); + if (gce->flags & GRID_FLAG_EXTENDED) { + if (gce->offset >= gl->extdsize) + memcpy(gc, &grid_default_cell, sizeof *gc); + else + memcpy(gc, &gl->extddata[gce->offset], sizeof *gc); + return; + } + + gc->flags = gce->flags & ~GRID_FLAG_EXTENDED; + gc->attr = gce->data.attr; + gc->fg = gce->data.fg; + gc->bg = gce->data.bg; + utf8_set(&gc->data, gce->data.data); } /* Set cell at relative position. */ void grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { + struct grid_line *gl; + struct grid_cell_entry *gce; + struct grid_cell *gcp; + if (grid_check_y(gd, py) != 0) return; grid_expand_line(gd, py, px + 1); - grid_put_cell(gd, px, py, gc); + + gl = &gd->linedata[py]; + gce = &gl->celldata[px]; + + if ((gce->flags & GRID_FLAG_EXTENDED) || gc->data.size != 1 || + gc->data.width != 1) { + if (~gce->flags & GRID_FLAG_EXTENDED) { + gl->extddata = xreallocarray(gl->extddata, + gl->extdsize + 1, sizeof *gl->extddata); + gce->offset = gl->extdsize++; + gce->flags = gc->flags | GRID_FLAG_EXTENDED; + } + + if (gce->offset >= gl->extdsize) + fatalx("offset too big"); + gcp = &gl->extddata[gce->offset]; + memcpy(gcp, gc, sizeof *gcp); + return; + } + + gce->flags = gc->flags & ~GRID_FLAG_EXTENDED; + gce->data.attr = gc->attr; + gce->data.fg = gc->fg; + gce->data.bg = gc->bg; + gce->data.data = gc->data.data[0]; } /* Clear area. */ @@ -300,7 +345,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) for (xx = px; xx < px + nx; xx++) { if (xx >= gd->linedata[yy].cellsize) break; - grid_put_cell(gd, xx, yy, &grid_default_cell); + grid_clear_cell(gd, xx, yy); } } } @@ -324,6 +369,10 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny) gl = &gd->linedata[yy]; free(gl->celldata); memset(gl, 0, sizeof *gl); + + free(gl->extddata); + gl->extddata = NULL; + gl->extdsize = 0; } } @@ -386,7 +435,7 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) for (xx = px; xx < px + nx; xx++) { if (xx >= dx && xx < dx + nx) continue; - grid_put_cell(gd, xx, py, &grid_default_cell); + grid_clear_cell(gd, xx, py); } } @@ -568,9 +617,8 @@ char * grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) { - const struct grid_cell *gc; + struct grid_cell gc; static struct grid_cell lastgc1; - struct utf8_data ud; const char *data; char *buf, code[128]; size_t len, off, size, codelen; @@ -590,21 +638,20 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, for (xx = px; xx < px + nx; xx++) { if (gl == NULL || xx >= gl->cellsize) break; - gc = &gl->celldata[xx]; - if (gc->flags & GRID_FLAG_PADDING) + grid_get_cell(gd, xx, py, &gc); + if (gc.flags & GRID_FLAG_PADDING) continue; - grid_cell_get(gc, &ud); if (with_codes) { - grid_string_cells_code(*lastgc, gc, code, sizeof code, + grid_string_cells_code(*lastgc, &gc, code, sizeof code, escape_c0); codelen = strlen(code); - memcpy(*lastgc, gc, sizeof *gc); + memcpy(*lastgc, &gc, sizeof **lastgc); } else codelen = 0; - data = ud.data; - size = ud.size; + data = gc.data.data; + size = gc.data.size; if (escape_c0 && size == 1 && *data == '\\') { data = "\\\\"; size = 2; @@ -663,11 +710,44 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, } else dstl->celldata = NULL; + if (srcl->extdsize != 0) { + dstl->extdsize = srcl->extdsize; + dstl->extddata = xreallocarray(NULL, dstl->extdsize, + sizeof *dstl->extddata); + memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * + sizeof *dstl->extddata); + } + sy++; dy++; } } +/* Copy a section of a line. */ +void +grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl, + u_int from, u_int to_copy) +{ + struct grid_cell_entry *gce; + u_int i, was; + + memcpy(&dst_gl->celldata[to], &src_gl->celldata[from], + to_copy * sizeof *dst_gl->celldata); + + for (i = to; i < to + to_copy; i++) { + gce = &dst_gl->celldata[i]; + if (~gce->flags & GRID_FLAG_EXTENDED) + continue; + was = gce->offset; + + dst_gl->extddata = xreallocarray(dst_gl->extddata, + dst_gl->extdsize + 1, sizeof *dst_gl->extddata); + gce->offset = dst_gl->extdsize++; + memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was], + sizeof *dst_gl->extddata); + } +} + /* Join line data. */ void grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, @@ -692,8 +772,7 @@ grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, dst_gl->cellsize = nx; /* Append as much as possible. */ - memcpy(&dst_gl->celldata[ox], &src_gl->celldata[0], - to_copy * sizeof src_gl->celldata[0]); + grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy); /* If there is any left in the source, split it. */ if (src_gl->cellsize > to_copy) { @@ -732,8 +811,7 @@ grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, dst_gl->flags |= GRID_LINE_WRAPPED; /* Copy the data. */ - memcpy(&dst_gl->celldata[0], &src_gl->celldata[offset], - to_copy * sizeof dst_gl->celldata[0]); + grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy); /* Move offset and reduce old line size. */ offset += to_copy; @@ -763,6 +841,7 @@ grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl) /* Clear old line. */ src_gl->celldata = NULL; + src_gl->extddata = NULL; } /* @@ -792,7 +871,7 @@ grid_reflow(struct grid *dst, struct grid *src, u_int new_x) /* Previous was wrapped. Try to join. */ grid_reflow_join(dst, &py, src_gl, new_x); } - previous_wrapped = src_gl->flags & GRID_LINE_WRAPPED; + previous_wrapped = (src_gl->flags & GRID_LINE_WRAPPED); } grid_destroy(src); diff --git a/input.c b/input.c index 41276d9a..c56cdc35 100644 --- a/input.c +++ b/input.c @@ -1006,7 +1006,7 @@ input_print(struct input_ctx *ictx) else ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; - grid_cell_one(&ictx->cell.cell, ictx->ch); + utf8_set(&ictx->cell.cell.data, ictx->ch); screen_write_cell(&ictx->ctx, &ictx->cell.cell); ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; @@ -1945,7 +1945,7 @@ input_utf8_close(struct input_ctx *ictx) utf8_append(&ictx->utf8data, ictx->ch); - grid_cell_set(&ictx->cell.cell, &ictx->utf8data); + utf8_copy(&ictx->cell.cell.data, &ictx->utf8data); screen_write_cell(&ictx->ctx, &ictx->cell.cell); return (0); diff --git a/screen-write.c b/screen-write.c index 4bf3ec72..14b8a41a 100644 --- a/screen-write.c +++ b/screen-write.c @@ -67,7 +67,7 @@ void screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch) { - grid_cell_one(gc, ch); + utf8_set(&gc->data, ch); screen_write_cell(ctx, gc); } @@ -126,7 +126,7 @@ screen_write_strlen(const char *fmt, ...) ptr++; left = strlen(ptr); - if (left < ud.size - 1) + if (left < (size_t)ud.size - 1) break; while (utf8_append(&ud, *ptr)) ptr++; @@ -185,7 +185,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, ptr++; left = strlen(ptr); - if (left < ud.size - 1) + if (left < (size_t)ud.size - 1) break; while (utf8_append(&ud, *ptr)) ptr++; @@ -201,7 +201,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, } size += ud.width; - grid_cell_set(gc, &ud); + utf8_copy(&gc->data, &ud); screen_write_cell(ctx, gc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) @@ -258,7 +258,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, ptr++; left = strlen(ptr); - if (left < ud.size - 1) + if (left < (size_t)ud.size - 1) break; while (utf8_append(&ud, *ptr)) ptr++; @@ -274,7 +274,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, } size += ud.width; - grid_cell_set(&lgc, &ud); + utf8_copy(&lgc.data, &ud); screen_write_cell(ctx, &lgc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) @@ -299,8 +299,7 @@ screen_write_copy(struct screen_write_ctx *ctx, struct screen *s = ctx->s; struct grid *gd = src->grid; struct grid_line *gl; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int xx, yy, cx, cy, ax, bx; cx = s->cx; @@ -324,12 +323,8 @@ screen_write_copy(struct screen_write_ctx *ctx, bx = px + nx; for (xx = ax; xx < bx; xx++) { - if (xx >= gl->cellsize) - gc = &grid_default_cell; - else - gc = &gl->celldata[xx]; - grid_cell_get(gc, &ud); - screen_write_cell(ctx, gc); + grid_get_cell(gd, xx, yy, &gc); + screen_write_cell(ctx, &gc); } if (px + nx == gd->sx && px + nx > gl->cellsize) screen_write_clearendofline(ctx); @@ -342,12 +337,12 @@ screen_write_copy(struct screen_write_ctx *ctx, /* Set up context for TTY command. */ void -screen_write_initctx( - struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last) +screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, + int save_last) { struct screen *s = ctx->s; struct grid *gd = s->grid; - const struct grid_cell *gc; + struct grid_cell gc; u_int xx; ttyctx->wp = ctx->wp; @@ -362,14 +357,14 @@ screen_write_initctx( return; /* Save the last cell on the screen. */ - gc = &grid_default_cell; + memcpy(&gc, &grid_default_cell, sizeof gc); for (xx = 1; xx <= screen_size_x(s); xx++) { - gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, screen_size_x(s) - xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; } ttyctx->last_width = xx; - memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell); + memcpy(&ttyctx->last_cell, &gc, sizeof ttyctx->last_cell); } /* Set a mode. */ @@ -507,7 +502,7 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) screen_write_initctx(ctx, &ttyctx, 0); memcpy(&gc, &grid_default_cell, sizeof gc); - grid_cell_one(&gc, 'E'); + utf8_set(&gc.data, 'E'); for (yy = 0; yy < screen_size_y(s); yy++) { for (xx = 0; xx < screen_size_x(s); xx++) @@ -904,14 +899,13 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int width, xx, last; - struct grid_cell tmp_gc, *tmp_gcp; - struct utf8_data ud; + struct grid_cell tmp_gc; int insert; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; - width = grid_cell_width(gc); + width = gc->data.width; /* * If this is a wide character and there is no room on the screen, for @@ -928,8 +922,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) * there is space. */ if (width == 0) { - grid_cell_get(gc, &ud); - if (screen_write_combine(ctx, &ud) == 0) { + if (screen_write_combine(ctx, &gc->data) == 0) { screen_write_initctx(ctx, &ttyctx, 0); tty_write(tty_cmd_utf8character, &ttyctx); } @@ -964,11 +957,11 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) * If the new character is UTF-8 wide, fill in padding cells. Have * already ensured there is enough room. */ - for (xx = s->cx + 1; xx < s->cx + width; xx++) { - tmp_gcp = grid_view_get_cell(gd, xx, s->cy); - if (tmp_gcp != NULL) - tmp_gcp->flags |= GRID_FLAG_PADDING; - } + memcpy(&tmp_gc, &grid_default_cell, sizeof tmp_gc); + tmp_gc.flags |= GRID_FLAG_PADDING; + tmp_gc.data.width = 0; + for (xx = s->cx + 1; xx < s->cx + width; xx++) + grid_view_set_cell(gd, xx, s->cy, &tmp_gc); /* Set the cell. */ grid_view_set_cell(gd, s->cx, s->cy, gc); @@ -990,8 +983,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) } if (screen_check_selection(s, s->cx - width, s->cy)) { memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); - grid_cell_get(gc, &ud); - grid_cell_set(&tmp_gc, &ud); + utf8_copy(&tmp_gc.data, &gc->data); tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET; tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET; tmp_gc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); @@ -1011,8 +1003,7 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) { struct screen *s = ctx->s; struct grid *gd = s->grid; - struct grid_cell *gc; - struct utf8_data ud1; + struct grid_cell gc; /* Can't combine if at 0. */ if (s->cx == 0) @@ -1023,17 +1014,18 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) fatalx("UTF-8 data empty"); /* Retrieve the previous cell. */ - gc = grid_view_get_cell(gd, s->cx - 1, s->cy); - grid_cell_get(gc, &ud1); + grid_view_get_cell(gd, s->cx - 1, s->cy, &gc); /* Check there is enough space. */ - if (ud1.size + ud->size > sizeof ud1.data) + if (gc.data.size + ud->size > sizeof gc.data.data) return (-1); - /* Append the data and set the cell. */ - memcpy(ud1.data + ud1.size, ud->data, ud->size); - ud1.size += ud->size; - grid_cell_set(gc, &ud1); + /* Append the data. */ + memcpy(gc.data.data + gc.data.size, ud->data, ud->size); + gc.data.size += ud->size; + + /* Set the new cell. */ + grid_view_set_cell(gd, s->cx - 1, s->cy, &gc); return (0); } @@ -1052,11 +1044,11 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) { struct screen *s = ctx->s; struct grid *gd = s->grid; - const struct grid_cell *gc; + struct grid_cell gc; u_int xx; - gc = grid_view_peek_cell(gd, s->cx, s->cy); - if (gc->flags & GRID_FLAG_PADDING) { + grid_view_get_cell(gd, s->cx, s->cy, &gc); + if (gc.flags & GRID_FLAG_PADDING) { /* * A padding cell, so clear any following and leading padding * cells back to the character. Don't overwrite the current @@ -1064,8 +1056,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) */ xx = s->cx + 1; while (--xx > 0) { - gc = grid_view_peek_cell(gd, xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } @@ -1080,8 +1072,8 @@ screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) */ xx = s->cx + width - 1; while (++xx < screen_size_x(s)) { - gc = grid_view_peek_cell(gd, xx, s->cy); - if (!(gc->flags & GRID_FLAG_PADDING)) + grid_view_get_cell(gd, xx, s->cy, &gc); + if (~gc.flags & GRID_FLAG_PADDING) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } diff --git a/status.c b/status.c index a4bdf6fa..326e5ad8 100644 --- a/status.c +++ b/status.c @@ -746,7 +746,7 @@ status_prompt_redraw(struct client *c) struct session *s = c->session; struct screen old_status; size_t i, size, left, len, off; - struct grid_cell gc, *gcp; + struct grid_cell gc; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); @@ -789,8 +789,9 @@ status_prompt_redraw(struct client *c) /* Apply fake cursor. */ off = len + c->prompt_index - off; - gcp = grid_view_get_cell(c->status.grid, off, 0); - gcp->attr ^= GRID_ATTR_REVERSE; + grid_view_get_cell(c->status.grid, off, 0, &gc); + gc.attr ^= GRID_ATTR_REVERSE; + grid_view_set_cell(c->status.grid, off, 0, &gc); if (grid_compare(c->status.grid, old_status.grid) == 0) { screen_free(&old_status); diff --git a/tmux.h b/tmux.h index cb7ed2c3..11f5fe58 100644 --- a/tmux.h +++ b/tmux.h @@ -621,11 +621,11 @@ struct mode_key_table { struct utf8_data { u_char data[UTF8_SIZE]; - size_t have; - size_t size; + u_char have; + u_char size; - u_int width; -}; + u_char width; +} __packed; /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 @@ -641,41 +641,56 @@ struct utf8_data { #define GRID_FLAG_FG256 0x1 #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 +#define GRID_FLAG_EXTENDED 0x8 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 /* Grid cell data. */ struct grid_cell { - u_char attr; - u_char flags; - u_char fg; - u_char bg; + u_char flags; + u_char attr; + u_char fg; + u_char bg; + struct utf8_data data; - u_char xstate; /* top 4 bits width, bottom 4 bits size */ - u_char xdata[UTF8_SIZE]; +}; +struct grid_cell_entry { + u_char flags; + union { + u_int offset; + struct { + u_char attr; + u_char fg; + u_char bg; + u_char data; + } data; + }; } __packed; /* Grid line. */ struct grid_line { - u_int cellsize; - struct grid_cell *celldata; + u_int cellsize; + struct grid_cell_entry *celldata; - int flags; + u_int extdsize; + struct grid_cell *extddata; + + int flags; } __packed; /* Entire grid of cells. */ struct grid { - int flags; -#define GRID_HISTORY 0x1 /* scroll lines into history */ + int flags; +#define GRID_HISTORY 0x1 /* scroll lines into history */ - u_int sx; - u_int sy; + u_int sx; + u_int sy; - u_int hsize; - u_int hlimit; + u_int hsize; + u_int hlimit; - struct grid_line *linedata; + struct grid_line *linedata; }; /* Option data structures. */ @@ -1854,9 +1869,8 @@ void grid_scroll_history(struct grid *); void grid_scroll_history_region(struct grid *, u_int, u_int); void grid_clear_history(struct grid *); void grid_expand_line(struct grid *, u_int, u_int); -const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); const struct grid_line *grid_peek_line(struct grid *, u_int); -struct grid_cell *grid_get_cell(struct grid *, u_int, u_int); +void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_clear_lines(struct grid *, u_int, u_int); @@ -1868,17 +1882,10 @@ void grid_duplicate_lines( struct grid *, u_int, struct grid *, u_int, u_int); u_int grid_reflow(struct grid *, struct grid *, u_int); -/* grid-cell.c */ -u_int grid_cell_width(const struct grid_cell *); -void grid_cell_get(const struct grid_cell *, struct utf8_data *); -void grid_cell_set(struct grid_cell *, const struct utf8_data *); -void grid_cell_one(struct grid_cell *, u_char); - /* grid-view.c */ -const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int); -struct grid_cell *grid_view_get_cell(struct grid *, u_int, u_int); -void grid_view_set_cell( - struct grid *, u_int, u_int, const struct grid_cell *); +void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); +void grid_view_set_cell(struct grid *, u_int, u_int, + const struct grid_cell *); void grid_view_clear_history(struct grid *); void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_view_scroll_region_up(struct grid *, u_int, u_int); @@ -2183,6 +2190,7 @@ void session_renumber_windows(struct session *); /* utf8.c */ u_int utf8_width(u_int); void utf8_set(struct utf8_data *, u_char); +void utf8_copy(struct utf8_data *, const struct utf8_data *); int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); u_int utf8_combine(const struct utf8_data *); diff --git a/tty.c b/tty.c index c0ae79bb..e67ebbb6 100644 --- a/tty.c +++ b/tty.c @@ -656,10 +656,8 @@ void tty_draw_line(struct tty *tty, const struct window_pane *wp, struct screen *s, u_int py, u_int ox, u_int oy) { - const struct grid_cell *gc; + struct grid_cell gc; struct grid_line *gl; - struct grid_cell tmpgc; - struct utf8_data ud; u_int i, sx; int flags; @@ -686,18 +684,13 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp, tty_cursor(tty, ox, oy + py); for (i = 0; i < sx; i++) { - gc = grid_view_peek_cell(s->grid, i, py); + grid_view_get_cell(s->grid, i, py, &gc); if (screen_check_selection(s, i, py)) { - memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc); - grid_cell_get(gc, &ud); - grid_cell_set(&tmpgc, &ud); - tmpgc.flags = gc->flags & - ~(GRID_FLAG_FG256|GRID_FLAG_BG256); - tmpgc.flags |= s->sel.cell.flags & + gc.flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); + gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); - tty_cell(tty, &tmpgc, wp); - } else - tty_cell(tty, gc, wp); + } + tty_cell(tty, &gc, wp); } if (sx < tty->sx) { @@ -1078,7 +1071,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); /* Is the cursor in the very last position? */ - width = grid_cell_width(ctx->cell); + width = ctx->cell->data.width; if (ctx->ocx > wp->sx - width) { if (ctx->xoff != 0 || wp->sx != tty->sx) { /* @@ -1095,7 +1088,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) * move as far left as possible and redraw the last * cell to move into the last position. */ - cx = screen_size_x(s) - grid_cell_width(&ctx->last_cell); + cx = screen_size_x(s) - ctx->last_cell.data.width; tty_cursor_pane(tty, ctx, cx, ctx->ocy); tty_cell(tty, &ctx->last_cell, wp); } @@ -1155,8 +1148,7 @@ void tty_cell(struct tty *tty, const struct grid_cell *gc, const struct window_pane *wp) { - struct utf8_data ud; - u_int i; + u_int i; /* Skip last character if terminal is stupid. */ if (tty->term->flags & TERM_EARLYWRAP && @@ -1171,23 +1163,22 @@ tty_cell(struct tty *tty, const struct grid_cell *gc, tty_attributes(tty, gc, wp); /* Get the cell and if ASCII write with putc to do ACS translation. */ - grid_cell_get(gc, &ud); - if (ud.size == 1) { - if (*ud.data < 0x20 || *ud.data == 0x7f) + if (gc->data.size == 1) { + if (*gc->data.data < 0x20 || *gc->data.data == 0x7f) return; - tty_putc(tty, *ud.data); + tty_putc(tty, *gc->data.data); return; } /* If not UTF-8, write _. */ if (!(tty->flags & TTY_UTF8)) { - for (i = 0; i < ud.width; i++) + for (i = 0; i < gc->data.width; i++) tty_putc(tty, '_'); return; } /* Write the data. */ - tty_putn(tty, ud.data, ud.size, ud.width); + tty_putn(tty, gc->data.data, gc->data.size, gc->data.width); } void diff --git a/utf8.c b/utf8.c index 82471c66..ecc5e718 100644 --- a/utf8.c +++ b/utf8.c @@ -352,10 +352,27 @@ static void utf8_build(void); void utf8_set(struct utf8_data *ud, u_char ch) { + u_int i; + *ud->data = ch; ud->size = 1; ud->width = 1; + + for (i = ud->size; i < sizeof ud->data; i++) + ud->data[i] = '\0'; +} + +/* Copy UTF-8 character. */ +void +utf8_copy(struct utf8_data *to, const struct utf8_data *from) +{ + u_int i; + + memcpy(to, from, sizeof *to); + + for (i = to->size; i < sizeof to->data; i++) + to->data[i] = '\0'; } /* diff --git a/window-copy.c b/window-copy.c index 1c1ea29c..a28cdecc 100644 --- a/window-copy.c +++ b/window-copy.c @@ -942,21 +942,21 @@ int window_copy_search_compare(struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx, int cis) { - const struct grid_cell *gc, *sgc; - struct utf8_data ud, sud; + struct grid_cell gc, sgc; + const struct utf8_data *ud, *sud; - gc = grid_peek_cell(gd, px, py); - grid_cell_get(gc, &ud); - sgc = grid_peek_cell(sgd, spx, 0); - grid_cell_get(sgc, &sud); + grid_get_cell(gd, px, py, &gc); + ud = &gc.data; + grid_get_cell(sgd, spx, 0, &sgc); + sud = &sgc.data; - if (ud.size != sud.size || ud.width != sud.width) + if (ud->size != sud->size || ud->width != sud->width) return (0); - if (cis && ud.size == 1) - return (tolower(ud.data[0]) == sud.data[0]); + if (cis && ud->size == 1) + return (tolower(ud->data[0]) == sud->data[0]); - return (memcmp(ud.data, sud.data, ud.size) == 0); + return (memcmp(ud->data, sud->data, ud->size) == 0); } int @@ -1541,12 +1541,12 @@ window_copy_append_selection(struct window_pane *wp, const char *bufname) } void -window_copy_copy_line(struct window_pane *wp, - char **buf, size_t *off, u_int sy, u_int sx, u_int ex) +window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, + u_int sx, u_int ex) { struct window_copy_mode_data *data = wp->modedata; struct grid *gd = data->backing->grid; - const struct grid_cell *gc; + struct grid_cell gc; struct grid_line *gl; struct utf8_data ud; u_int i, xx, wrapped = 0; @@ -1575,11 +1575,11 @@ window_copy_copy_line(struct window_pane *wp, if (sx < ex) { for (i = sx; i < ex; i++) { - gc = grid_peek_cell(gd, i, sy); - if (gc->flags & GRID_FLAG_PADDING) + grid_get_cell(gd, i, sy, &gc); + if (gc.flags & GRID_FLAG_PADDING) continue; - grid_cell_get(gc, &ud); - if (ud.size == 1 && (gc->attr & GRID_ATTR_CHARSET)) { + utf8_copy(&ud, &gc.data); + if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { s = tty_acs_get(NULL, ud.data[0]); if (s != NULL && strlen(s) <= sizeof ud.data) { ud.size = strlen(s); @@ -1618,16 +1618,17 @@ int window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set) { struct window_copy_mode_data *data = wp->modedata; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; + const struct utf8_data *ud; - gc = grid_peek_cell(data->backing->grid, px, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || gc->flags & GRID_FLAG_PADDING) + grid_get_cell(data->backing->grid, px, py, &gc); + + ud = &gc.data; + if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING)) return (0); - if (*ud.data == 0x00 || *ud.data == 0x7f) + if (*ud->data == 0x00 || *ud->data == 0x7f) return (0); - return (strchr(set, *ud.data) != NULL); + return (strchr(set, *ud->data) != NULL); } u_int @@ -1635,8 +1636,7 @@ window_copy_find_length(struct window_pane *wp, u_int py) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px; /* @@ -1649,9 +1649,8 @@ window_copy_find_length(struct window_pane *wp, u_int py) if (px > screen_size_x(s)) px = screen_size_x(s); while (px > 0) { - gc = grid_peek_cell(s->grid, px - 1, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || *ud.data != ' ') + grid_get_cell(s->grid, px - 1, py, &gc); + if (gc.data.size != 1 || *gc.data.data != ' ') break; px--; } @@ -1685,17 +1684,15 @@ window_copy_cursor_back_to_indentation(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int px, py, xx; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; px = 0; py = screen_hsize(data->backing) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(data->backing->grid, px, py); - grid_cell_get(gc, &ud); - if (ud.size != 1 || *ud.data != ' ') + grid_get_cell(data->backing->grid, px, py, &gc); + if (gc.data.size != 1 || *gc.data.data != ' ') break; px++; } @@ -1909,8 +1906,7 @@ window_copy_cursor_jump(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py, xx; px = data->cx + 1; @@ -1918,10 +1914,9 @@ window_copy_cursor_jump(struct window_pane *wp) xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1936,8 +1931,7 @@ window_copy_cursor_jump_back(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py; px = data->cx; @@ -1947,10 +1941,9 @@ window_copy_cursor_jump_back(struct window_pane *wp) px--; for (;;) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1967,8 +1960,7 @@ window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py, xx; px = data->cx + 1 + jump_again; @@ -1976,10 +1968,9 @@ window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) xx = window_copy_find_length(wp, py); while (px < xx) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px - 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1994,8 +1985,7 @@ window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; + struct grid_cell gc; u_int px, py; px = data->cx; @@ -2008,10 +1998,9 @@ window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) px--; for (;;) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { + grid_get_cell(back_s->grid, px, py, &gc); + if (!(gc.flags & GRID_FLAG_PADDING) && + gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px + 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); From 531869bd92f0daff3cc3c3cc0ab273846f411dc8 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Nov 2015 10:00:26 +0000 Subject: [PATCH 403/703] Add window_visible_layout which ignores zoomed panes and use it for control mode (which needs to know all panes), from George Nachman. --- control-notify.c | 11 +++++++++-- format.c | 15 +++++++++++++++ tmux.1 | 9 +++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/control-notify.c b/control-notify.c index 943d670c..16a98e3a 100644 --- a/control-notify.c +++ b/control-notify.c @@ -19,6 +19,8 @@ #include +#include + #include "tmux.h" #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ @@ -65,6 +67,10 @@ control_notify_window_layout_changed(struct window *w) struct format_tree *ft; struct winlink *wl; const char *template; + char *expanded; + + template = "%layout-change #{window_id} #{window_layout} " + "#{window_visible_layout} #{window_flags}"; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) @@ -81,13 +87,14 @@ control_notify_window_layout_changed(struct window *w) */ if (w->layout_root == NULL) continue; - template = "%layout-change #{window_id} #{window_layout}"; ft = format_create(); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { format_defaults(ft, c, NULL, wl, NULL); - control_write(c, "%s", format_expand(ft, template)); + expanded = format_expand(ft, template); + control_write(c, "%s", expanded); + free(expanded); } format_free(ft); } diff --git a/format.c b/format.c index 389026ac..9cfee277 100644 --- a/format.c +++ b/format.c @@ -48,6 +48,7 @@ void format_cb_host_short(struct format_tree *, struct format_entry *); void format_cb_pid(struct format_tree *, struct format_entry *); void format_cb_session_alerts(struct format_tree *, struct format_entry *); void format_cb_window_layout(struct format_tree *, struct format_entry *); +void format_cb_window_visible_layout(struct format_tree *, struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); @@ -362,6 +363,18 @@ format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) fe->value = layout_dump(w->layout_root); } +/* Callback for window_visible_layout. */ +void +format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe) +{ + struct window *w = ft->w; + + if (w == NULL) + return; + + fe->value = layout_dump(w->layout_root); +} + /* Callback for pane_start_command. */ void format_cb_start_command(struct format_tree *ft, struct format_entry *fe) @@ -1024,6 +1037,8 @@ format_defaults_window(struct format_tree *ft, struct window *w) format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_height", "%u", w->sy); format_add_cb(ft, "window_layout", format_cb_window_layout); + format_add_cb(ft, "window_visible_layout", + format_cb_window_visible_layout); format_add(ft, "window_panes", "%u", window_count_panes(w)); format_add(ft, "window_zoomed_flag", "%d", !!(w->flags & WINDOW_ZOOMED)); diff --git a/tmux.1 b/tmux.1 index e0646697..3a08c8cd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3412,11 +3412,12 @@ The following variables are available, where appropriate: .It Li "window_id" Ta "" Ta "Unique window ID" .It Li "window_index" Ta "#I" Ta "Index of window" .It Li "window_last_flag" Ta "" Ta "1 if window is the last used" -.It Li "window_layout" Ta "" Ta "Window layout description" +.It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" .It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" +.It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes" .It Li "window_width" Ta "" Ta "Width of window" .It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed" .It Li "wrap_flag" Ta "" Ta "Pane wrap flag" @@ -4007,12 +4008,16 @@ or an error occurred. If present, .Ar reason describes why the client exited. -.It Ic %layout-change Ar window-id Ar window-layout +.It Ic %layout-change Ar window-id Ar window-layout Ar window-visible-layout Ar window-flags The layout of a window with ID .Ar window-id changed. The new layout is .Ar window-layout . +The window's visible layout is +.Ar window-visible-layout +and the window flags are +.Ar window-flags . .It Ic %output Ar pane-id Ar value A window pane produced output. .Ar value From 88aa1c8dc3962380da8258b3be8fef63b2a2fff2 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Nov 2015 12:18:52 +0000 Subject: [PATCH 404/703] Two spacing and spelling nits. --- format.c | 3 ++- grid-view.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/format.c b/format.c index 9cfee277..04e06fc6 100644 --- a/format.c +++ b/format.c @@ -48,7 +48,8 @@ void format_cb_host_short(struct format_tree *, struct format_entry *); void format_cb_pid(struct format_tree *, struct format_entry *); void format_cb_session_alerts(struct format_tree *, struct format_entry *); void format_cb_window_layout(struct format_tree *, struct format_entry *); -void format_cb_window_visible_layout(struct format_tree *, struct format_entry *); +void format_cb_window_visible_layout(struct format_tree *, + struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); diff --git a/grid-view.c b/grid-view.c index 5edcfd53..f6708c89 100644 --- a/grid-view.c +++ b/grid-view.c @@ -30,7 +30,7 @@ #define grid_view_x(gd, x) (x) #define grid_view_y(gd, y) ((gd)->hsize + (y)) -/* Get cel. */ +/* Get cell. */ void grid_view_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { From b7397bf413643768a44a79d9a32fb887b9a80cde Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 13 Nov 2015 16:05:58 +0000 Subject: [PATCH 405/703] utf8 option is gone. --- window.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/window.c b/window.c index 7f2b2279..831abcd3 100644 --- a/window.c +++ b/window.c @@ -863,8 +863,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); tio2.c_cc[VERASE] = '\177'; #ifdef IUTF8 - if (options_get_number(wp->window->options, "utf8")) - tio2.c_iflag |= IUTF8; + tio2.c_iflag |= IUTF8; #endif if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) fatal("tcgetattr failed"); From c4893d8efdba87843f7f0d8a85c084e1e38d1430 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 13 Nov 2015 16:06:43 +0000 Subject: [PATCH 406/703] Log option names in fatal() for missing option. --- options.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/options.c b/options.c index 487918fd..8d2fb715 100644 --- a/options.c +++ b/options.c @@ -150,9 +150,9 @@ options_get_string(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - fatalx("missing option"); + log_fatalx("missing option %s", name); if (o->type != OPTIONS_STRING) - fatalx("option not a string"); + log_fatalx("option %s not a string", name); return (o->str); } @@ -180,9 +180,9 @@ options_get_number(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - fatalx("missing option"); + log_fatalx("missing option %s", name); if (o->type != OPTIONS_NUMBER) - fatalx("option not a number"); + log_fatalx("option %s not a number", name); return (o->num); } @@ -220,8 +220,8 @@ options_get_style(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - fatalx("missing option"); + log_fatalx("missing option %s", name); if (o->type != OPTIONS_STYLE) - fatalx("option not a style"); + log_fatalx("option %s not a style", name); return (&o->style); } From 908e6bb68f127f2bdf0c15ac25dde9ccc06e9104 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 08:25:12 +0000 Subject: [PATCH 407/703] Log more of UTF-8 input. --- input.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/input.c b/input.c index c56cdc35..babdecdb 100644 --- a/input.c +++ b/input.c @@ -1921,9 +1921,12 @@ input_exit_rename(struct input_ctx *ictx) int input_utf8_open(struct input_ctx *ictx) { - log_debug("%s", __func__); + struct utf8_data *ud = &ictx->utf8data; + + utf8_open(ud, ictx->ch); + + log_debug("%s %hhu", __func__, ud->size); - utf8_open(&ictx->utf8data, ictx->ch); return (0); } @@ -1931,9 +1934,12 @@ input_utf8_open(struct input_ctx *ictx) int input_utf8_add(struct input_ctx *ictx) { + struct utf8_data *ud = &ictx->utf8data; + + utf8_append(ud, ictx->ch); + log_debug("%s", __func__); - utf8_append(&ictx->utf8data, ictx->ch); return (0); } @@ -1941,11 +1947,14 @@ input_utf8_add(struct input_ctx *ictx) int input_utf8_close(struct input_ctx *ictx) { - log_debug("%s", __func__); + struct utf8_data *ud = &ictx->utf8data; - utf8_append(&ictx->utf8data, ictx->ch); + utf8_append(ud, ictx->ch); - utf8_copy(&ictx->cell.cell.data, &ictx->utf8data); + log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, + (int)ud->size, ud->data, ud->width); + + utf8_copy(&ictx->cell.cell.data, ud); screen_write_cell(&ictx->ctx, &ictx->cell.cell); return (0); From 31d880f751a65aa6bb5f0f12683cdb2c0cbbf5c0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 14 Nov 2015 09:04:13 +0000 Subject: [PATCH 408/703] Update the TODO file. --- TODO | 3 --- 1 file changed, 3 deletions(-) diff --git a/TODO b/TODO index 78d566a4..6094c023 100644 --- a/TODO +++ b/TODO @@ -18,7 +18,6 @@ * option to quote format (#{session_name:quoted}) * formats need conditions for >0 (for #P) * some way to pad # stuff with spaces - * last window update time and format for it * formats to show if a window is linked into multiple sessions, into multiple attached sessions, and is the active window in multiple attached sessions? @@ -51,10 +50,8 @@ * split-window -> split-pane? - better UTF-8 support: - * window names and titles * message display * prompt input - * multibyte key input * searching in copy mode - copy/paste improvements: From c56b81a2ce815f6d289232f20bb6e07cfd0e36ec Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 09:41:06 +0000 Subject: [PATCH 409/703] Push stdout and stderr to clients more aggressively, and add an event to continue if the send fails. --- client.c | 4 +-- cmd-capture-pane.c | 2 +- cmd-load-buffer.c | 2 +- cmd-queue.c | 6 ++-- cmd-save-buffer.c | 2 +- control.c | 4 +-- server-client.c | 90 ++++++++++++++++++++++++++++++++++++++++++++-- server-fn.c | 44 ----------------------- server.c | 2 +- tmux.c | 2 +- tmux.h | 4 +-- 11 files changed, 101 insertions(+), 61 deletions(-) diff --git a/client.c b/client.c index bc934d33..a6423630 100644 --- a/client.c +++ b/client.c @@ -289,7 +289,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) * * "sendfd" is dropped later in client_dispatch_wait(). */ - if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) + if (0 && pledge("stdio unix sendfd proc exec tty", NULL) != 0) fatal("pledge failed"); /* Free stuff that is not used in the client. */ @@ -541,7 +541,7 @@ client_dispatch_wait(struct imsg *imsg) * get the first message from the server. */ if (!pledge_applied) { - if (pledge("stdio unix proc exec tty", NULL) != 0) + if (0 && pledge("stdio unix proc exec tty", NULL) != 0) fatal("pledge failed"); pledge_applied = 1; }; diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 8958a12d..6a7af47a 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -203,7 +203,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) free(buf); if (args_has(args, 'P') && len > 0) evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } else { bufname = NULL; if (args_has(args, 'b')) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 40bea9b7..5ac2edc3 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -160,7 +160,7 @@ cmd_load_buffer_callback(struct client *c, int closed, void *data) free(saved); } evbuffer_add_printf(c->stderr_data, "%s", cause); - server_push_stderr(c); + server_client_push_stderr(c); free(pdata); free(cause); } diff --git a/cmd-queue.c b/cmd-queue.c index 5015981c..2d896212 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -85,7 +85,7 @@ cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) } else evbuffer_add_vprintf(c->stdout_data, fmt, ap); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } else { w = c->session->curw->window; if (w->active->mode != &window_copy_mode) { @@ -125,7 +125,7 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) } evbuffer_add(c->stderr_data, msg, msglen); evbuffer_add(c->stderr_data, "\n", 1); - server_push_stderr(c); + server_client_push_stderr(c); c->retval = 1; } else { *msg = toupper((u_char) *msg); @@ -146,7 +146,7 @@ cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, (long) cmdq->time, cmdq->number, flags); - server_push_stdout(c); + server_client_push_stdout(c); } /* Add command list to queue and begin processing if needed. */ diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 9f3deeda..daef88a8 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -130,7 +130,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) do_stdout: evbuffer_add(c->stdout_data, bufdata, bufsize); - server_push_stdout(c); + server_client_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: diff --git a/control.c b/control.c index 11fa2d80..f7264944 100644 --- a/control.c +++ b/control.c @@ -37,7 +37,7 @@ control_write(struct client *c, const char *fmt, ...) va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } /* Write a buffer, adding a terminal newline. Empties buffer. */ @@ -46,7 +46,7 @@ control_write_buffer(struct client *c, struct evbuffer *buffer) { evbuffer_add_buffer(c->stdout_data, buffer); evbuffer_add(c->stdout_data, "\n", 1); - server_push_stdout(c); + server_client_push_stdout(c); } /* Control input callback. Read lines and fire commands. */ diff --git a/server-client.c b/server-client.c index fedc93bd..645492d1 100644 --- a/server-client.c +++ b/server-client.c @@ -1035,9 +1035,6 @@ server_client_dispatch(struct imsg *imsg, void *arg) server_client_dispatch_shell(c); break; } - - server_push_stdout(c); - server_push_stderr(c); } /* Handle command message. */ @@ -1214,3 +1211,90 @@ server_client_dispatch_shell(struct client *c) proc_kill_peer(c->peer); } + +/* Event callback to push more stdout data if any left. */ +static void +server_client_stdout_cb(unused int fd, unused short events, void *arg) +{ + struct client *c = arg; + + if (~c->flags & CLIENT_DEAD) + server_client_push_stdout(c); + server_client_unref(c); +} + +/* Push stdout to client if possible. */ +void +server_client_push_stdout(struct client *c) +{ + struct msg_stdout_data data; + size_t sent, left; + + left = EVBUFFER_LENGTH(c->stdout_data); + while (left != 0) { + sent = left; + if (sent > sizeof data.data) + sent = sizeof data.data; + memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent); + data.size = sent; + + if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0) + break; + evbuffer_drain(c->stdout_data, sent); + + left = EVBUFFER_LENGTH(c->stdout_data); + log_debug("%s: client %p, sent %zu, left %zu", __func__, c, + sent, left); + } + if (left != 0) { + c->references++; + event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL); + log_debug("%s: client %p, queued", __func__, c); + } +} + +/* Event callback to push more stderr data if any left. */ +static void +server_client_stderr_cb(unused int fd, unused short events, void *arg) +{ + struct client *c = arg; + + if (~c->flags & CLIENT_DEAD) + server_client_push_stderr(c); + server_client_unref(c); +} + +/* Push stderr to client if possible. */ +void +server_client_push_stderr(struct client *c) +{ + struct msg_stderr_data data; + size_t sent, left; + + if (c->stderr_data == c->stdout_data) { + server_client_push_stdout(c); + return; + } + + left = EVBUFFER_LENGTH(c->stderr_data); + while (left != 0) { + sent = left; + if (sent > sizeof data.data) + sent = sizeof data.data; + memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent); + data.size = sent; + + if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0) + break; + evbuffer_drain(c->stderr_data, sent); + + left = EVBUFFER_LENGTH(c->stderr_data); + log_debug("%s: client %p, sent %zu, left %zu", __func__, c, + sent, left); + } + if (left != 0) { + c->references++; + event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL); + log_debug("%s: client %p, queued", __func__, c); + } +} diff --git a/server-fn.c b/server-fn.c index e9a04cb5..bded7246 100644 --- a/server-fn.c +++ b/server-fn.c @@ -450,50 +450,6 @@ server_callback_identify(unused int fd, unused short events, void *data) server_clear_identify(c); } -/* Push stdout to client if possible. */ -void -server_push_stdout(struct client *c) -{ - struct msg_stdout_data data; - size_t size; - - size = EVBUFFER_LENGTH(c->stdout_data); - if (size == 0) - return; - if (size > sizeof data.data) - size = sizeof data.data; - - memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); - data.size = size; - - if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) == 0) - evbuffer_drain(c->stdout_data, size); -} - -/* Push stderr to client if possible. */ -void -server_push_stderr(struct client *c) -{ - struct msg_stderr_data data; - size_t size; - - if (c->stderr_data == c->stdout_data) { - server_push_stdout(c); - return; - } - size = EVBUFFER_LENGTH(c->stderr_data); - if (size == 0) - return; - if (size > sizeof data.data) - size = sizeof data.data; - - memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); - data.size = size; - - if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) == 0) - evbuffer_drain(c->stderr_data, size); -} - /* Set stdin callback. */ int server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, diff --git a/server.c b/server.c index 2808c0cc..160b8cac 100644 --- a/server.c +++ b/server.c @@ -175,7 +175,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) if (debug_level > 3) tty_create_log(); - if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " + if (0 && pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); diff --git a/tmux.c b/tmux.c index 5429a7cb..3f110156 100644 --- a/tmux.c +++ b/tmux.c @@ -255,7 +255,7 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); - if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " + if (0 && pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " "proc exec tty ps", NULL) != 0) err(1, "pledge"); diff --git a/tmux.h b/tmux.h index 11f5fe58..e69b74b1 100644 --- a/tmux.h +++ b/tmux.h @@ -1781,6 +1781,8 @@ int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); void server_client_loop(void); +void server_client_push_stdout(struct client *); +void server_client_push_stderr(struct client *); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); @@ -1806,8 +1808,6 @@ void server_destroy_session(struct session *); void server_check_unattached(void); void server_set_identify(struct client *); void server_clear_identify(struct client *); -void server_push_stdout(struct client *); -void server_push_stderr(struct client *); int server_set_stdin_callback(struct client *, void (*)(struct client *, int, void *), void *, char **); void server_unzoom_window(struct window *); From 64333e3ef89047d1c09cdc5053af647dbd8344da Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 10:56:31 +0000 Subject: [PATCH 410/703] Be more strict about invalid UTF-8. --- input.c | 13 +++++++----- key-string.c | 6 ++++-- screen-write.c | 56 ++++++++++++++++++++++++++++---------------------- tmux.h | 2 +- tty-keys.c | 11 +++++++--- utf8.c | 42 ++++++++++++++++++++----------------- 6 files changed, 76 insertions(+), 54 deletions(-) diff --git a/input.c b/input.c index babdecdb..cb92e52f 100644 --- a/input.c +++ b/input.c @@ -446,11 +446,11 @@ const struct input_transition input_state_ground_table[] = { { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x7e, input_print, NULL }, { 0x7f, 0x7f, NULL, NULL }, - { 0x80, 0xc1, input_print, NULL }, + { 0x80, 0xc1, NULL, NULL }, { 0xc2, 0xdf, input_utf8_open, &input_state_utf8_one }, { 0xe0, 0xef, input_utf8_open, &input_state_utf8_two }, { 0xf0, 0xf4, input_utf8_open, &input_state_utf8_three }, - { 0xf5, 0xff, input_print, NULL }, + { 0xf5, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; @@ -1923,7 +1923,8 @@ input_utf8_open(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - utf8_open(ud, ictx->ch); + if (!utf8_open(ud, ictx->ch)) + log_fatalx("UTF-8 open invalid %#hhx", ictx->ch); log_debug("%s %hhu", __func__, ud->size); @@ -1936,7 +1937,8 @@ input_utf8_add(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - utf8_append(ud, ictx->ch); + if (utf8_append(ud, ictx->ch) != 1) + log_fatalx("UTF-8 add invalid %#hhx", ictx->ch); log_debug("%s", __func__); @@ -1949,7 +1951,8 @@ input_utf8_close(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - utf8_append(ud, ictx->ch); + if (utf8_append(ud, ictx->ch) != 0) + log_fatalx("UTF-8 close invalid %#hhx", ictx->ch); log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); diff --git a/key-string.c b/key-string.c index c2230218..81d014ac 100644 --- a/key-string.c +++ b/key-string.c @@ -144,7 +144,7 @@ key_string_lookup_string(const char *string) static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; key_code key; u_short u; - int size; + int size, more; key_code modifiers; struct utf8_data ud; u_int i; @@ -177,7 +177,9 @@ key_string_lookup_string(const char *string) if (strlen(string) != ud.size) return (KEYC_NONE); for (i = 1; i < ud.size; i++) - utf8_append(&ud, (u_char)string[i]); + more = utf8_append(&ud, (u_char)string[i]); + if (more != 0) + return (KEYC_NONE); key = utf8_combine(&ud); return (key | modifiers); } diff --git a/screen-write.c b/screen-write.c index 14b8a41a..9e1ef822 100644 --- a/screen-write.c +++ b/screen-write.c @@ -115,6 +115,7 @@ screen_write_strlen(const char *fmt, ...) struct utf8_data ud; u_char *ptr; size_t left, size = 0; + int more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -128,11 +129,12 @@ screen_write_strlen(const char *fmt, ...) left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while (utf8_append(&ud, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == 1) ptr++; ptr++; - size += ud.width; + if (more == 0) + size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) size++; @@ -176,6 +178,7 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct utf8_data ud; u_char *ptr; size_t left, size = 0; + int more; xvasprintf(&msg, fmt, ap); @@ -187,22 +190,24 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while (utf8_append(&ud, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == 1) ptr++; ptr++; - if (maxlen > 0 && - size + ud.width > (size_t) maxlen) { - while (size < (size_t) maxlen) { - screen_write_putc(ctx, gc, ' '); - size++; + if (more == 0) { + if (maxlen > 0 && + size + ud.width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; } - break; - } - size += ud.width; + size += ud.width; - utf8_copy(&gc->data, &ud); - screen_write_cell(ctx, gc); + utf8_copy(&gc->data, &ud); + screen_write_cell(ctx, gc); + } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; @@ -231,6 +236,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, char *msg; u_char *ptr, *last; size_t left, size = 0; + int more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -260,22 +266,24 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while (utf8_append(&ud, *ptr)) + while ((more = utf8_append(&ud, *ptr)) == 1) ptr++; ptr++; - if (maxlen > 0 && - size + ud.width > (size_t) maxlen) { - while (size < (size_t) maxlen) { - screen_write_putc(ctx, gc, ' '); - size++; + if (more == 0) { + if (maxlen > 0 && + size + ud.width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; } - break; - } - size += ud.width; + size += ud.width; - utf8_copy(&lgc.data, &ud); - screen_write_cell(ctx, &lgc); + utf8_copy(&lgc.data, &ud); + screen_write_cell(ctx, &lgc); + } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; diff --git a/tmux.h b/tmux.h index e69b74b1..2bc1f0a4 100644 --- a/tmux.h +++ b/tmux.h @@ -624,7 +624,7 @@ struct utf8_data { u_char have; u_char size; - u_char width; + u_char width; /* 0xff if invalid */ } __packed; /* Grid attributes. */ diff --git a/tty-keys.c b/tty-keys.c index 6a64ef15..4bdc061a 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -472,7 +472,7 @@ tty_keys_next(struct tty *tty) const char *buf; size_t len, size; cc_t bspace; - int delay, expired = 0; + int delay, expired = 0, more; key_code key; struct utf8_data ud; u_int i; @@ -547,7 +547,9 @@ first_key: goto partial_key; } for (i = 1; i < size; i++) - utf8_append(&ud, (u_char)buf[i]); + more = utf8_append(&ud, (u_char)buf[i]); + if (more != 0) + goto discard_key; key = utf8_combine(&ud); log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); goto complete_key; @@ -653,6 +655,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) struct utf8_data ud; u_int i, value, x, y, b, sgr_b; u_char sgr_type, c; + int more; /* * Standard mouse sequences are \033[M followed by three characters @@ -699,7 +702,9 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) (*size)++; if (len <= *size) return (1); - utf8_append(&ud, buf[*size]); + more = utf8_append(&ud, buf[*size]); + if (more != 0) + return (-1); value = utf8_combine(&ud); } else value = (u_char)buf[*size]; diff --git a/utf8.c b/utf8.c index ecc5e718..a36bccc3 100644 --- a/utf8.c +++ b/utf8.c @@ -403,22 +403,26 @@ utf8_open(struct utf8_data *ud, u_char ch) /* * Append character to UTF-8, closing if finished. * - * Returns 1 if more UTF-8 data to come, 0 if finished. + * Returns 1 if more UTF-8 data to come, 0 if finished and valid, -1 if + * finished and invalid. */ int utf8_append(struct utf8_data *ud, u_char ch) { - /* XXX this should do validity checks too! */ - if (ud->have >= ud->size) fatalx("UTF-8 character overflow"); if (ud->size > sizeof ud->data) fatalx("UTF-8 character size too large"); + if (ud->have != 0 && (ch & 0xc0) != 0x80) + ud->width = 0xff; + ud->data[ud->have++] = ch; if (ud->have != ud->size) return (1); + if (ud->width == 0xff) + return (-1); ud->width = utf8_width(utf8_combine(ud)); return (0); } @@ -556,15 +560,15 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) while (src < end) { if (utf8_open(&ud, *src)) { more = 1; - while (++src < end && more) + while (++src < end && more == 1) more = utf8_append(&ud, *src); - if (!more) { + if (more == 0) { /* UTF-8 character finished. */ for (i = 0; i < ud.size; i++) *dst++ = ud.data[i]; continue; } else if (ud.have > 0) { - /* Not a complete UTF-8 character. */ + /* Not a complete, valid UTF-8 character. */ src -= ud.have; } } @@ -600,9 +604,9 @@ utf8_sanitize(const char *src) dst = xreallocarray(dst, n + 1, sizeof *dst); if (utf8_open(&ud, *src)) { more = 1; - while (*++src != '\0' && more) + while (*++src != '\0' && more == 1) more = utf8_append(&ud, *src); - if (!more) { + if (more != 1) { dst = xreallocarray(dst, n + ud.width, sizeof *dst); for (i = 0; i < ud.width; i++) @@ -612,10 +616,8 @@ utf8_sanitize(const char *src) src -= ud.have; } if (*src > 0x1f && *src < 0x7f) - dst[n] = *src; + dst[n++] = *src; src++; - - n++; } dst = xreallocarray(dst, n + 1, sizeof *dst); @@ -641,18 +643,19 @@ utf8_fromcstr(const char *src) dst = xreallocarray(dst, n + 1, sizeof *dst); if (utf8_open(&dst[n], *src)) { more = 1; - while (*++src != '\0' && more) + while (*++src != '\0' && more == 1) more = utf8_append(&dst[n], *src); - if (!more) { + if (more != 1) { n++; continue; } src -= dst[n].have; } - utf8_set(&dst[n], *src); + if (*src > 0x1f && *src < 0x7f) { + utf8_set(&dst[n], *src); + n++; + } src++; - - n++; } dst = xreallocarray(dst, n + 1, sizeof *dst); @@ -693,15 +696,16 @@ utf8_cstrwidth(const char *s) while (*s != '\0') { if (utf8_open(&tmp, *s)) { more = 1; - while (*++s != '\0' && more) + while (*++s != '\0' && more == 1) more = utf8_append(&tmp, *s); - if (!more) { + if (more != 1) { width += tmp.width; continue; } s -= tmp.have; } - width++; + if (*s > 0x1f && *s < 0x7f) + width++; s++; } return (width); From dab63b029e94dcabe335abf7f89c66c28486a542 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 10:57:59 +0000 Subject: [PATCH 411/703] Couple of assignments to remove compiler warnings. --- key-string.c | 1 + tty-keys.c | 1 + 2 files changed, 2 insertions(+) diff --git a/key-string.c b/key-string.c index 81d014ac..ad7cbf50 100644 --- a/key-string.c +++ b/key-string.c @@ -176,6 +176,7 @@ key_string_lookup_string(const char *string) if (utf8_open(&ud, (u_char)*string)) { if (strlen(string) != ud.size) return (KEYC_NONE); + more = 1; for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); if (more != 0) diff --git a/tty-keys.c b/tty-keys.c index 4bdc061a..cc6b934a 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -546,6 +546,7 @@ first_key: goto discard_key; goto partial_key; } + more = 1; for (i = 1; i < size; i++) more = utf8_append(&ud, (u_char)buf[i]); if (more != 0) From f401791a5689799ddf3cfa6ecee7da60318febf7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 11:13:44 +0000 Subject: [PATCH 412/703] Rename a variable in utf8_combine for consistency and use 0xfffd for unknown Unicode. --- utf8.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/utf8.c b/utf8.c index a36bccc3..b27d6671 100644 --- a/utf8.c +++ b/utf8.c @@ -474,30 +474,30 @@ utf8_width(u_int uc) u_int utf8_combine(const struct utf8_data *ud) { - u_int value; + u_int uc; - value = 0xff; + uc = 0xfffd; switch (ud->size) { case 1: - value = ud->data[0]; + uc = ud->data[0]; break; case 2: - value = ud->data[1] & 0x3f; - value |= (ud->data[0] & 0x1f) << 6; + uc = ud->data[1] & 0x3f; + uc |= (ud->data[0] & 0x1f) << 6; break; case 3: - value = ud->data[2] & 0x3f; - value |= (ud->data[1] & 0x3f) << 6; - value |= (ud->data[0] & 0xf) << 12; + uc = ud->data[2] & 0x3f; + uc |= (ud->data[1] & 0x3f) << 6; + uc |= (ud->data[0] & 0xf) << 12; break; case 4: - value = ud->data[3] & 0x3f; - value |= (ud->data[2] & 0x3f) << 6; - value |= (ud->data[1] & 0x3f) << 12; - value |= (ud->data[0] & 0x7) << 18; + uc = ud->data[3] & 0x3f; + uc |= (ud->data[2] & 0x3f) << 6; + uc |= (ud->data[1] & 0x3f) << 12; + uc |= (ud->data[0] & 0x7) << 18; break; } - return (value); + return (uc); } /* Split 32-bit Unicode into UTF-8. */ From 205d15e82d9e4aa90c7980b509d3489ad8eb6c2a Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 11:45:43 +0000 Subject: [PATCH 413/703] All these return values from utf8_* are confusing, use an enum. --- input-keys.c | 2 +- input.c | 6 ++-- key-string.c | 10 +++---- screen-write.c | 24 +++++++-------- tmux.h | 11 +++++-- tty-keys.c | 14 ++++----- utf8.c | 80 +++++++++++++++++++++----------------------------- 7 files changed, 70 insertions(+), 77 deletions(-) diff --git a/input-keys.c b/input-keys.c index 3938c185..156cbe6a 100644 --- a/input-keys.c +++ b/input-keys.c @@ -169,7 +169,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) return; } if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { - if (utf8_split(justkey, &ud) != 0) + if (utf8_split(justkey, &ud) != UTF8_DONE) return; if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); diff --git a/input.c b/input.c index cb92e52f..f7b4e460 100644 --- a/input.c +++ b/input.c @@ -1923,7 +1923,7 @@ input_utf8_open(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - if (!utf8_open(ud, ictx->ch)) + if (utf8_open(ud, ictx->ch) != UTF8_MORE) log_fatalx("UTF-8 open invalid %#hhx", ictx->ch); log_debug("%s %hhu", __func__, ud->size); @@ -1937,7 +1937,7 @@ input_utf8_add(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - if (utf8_append(ud, ictx->ch) != 1) + if (utf8_append(ud, ictx->ch) != UTF8_MORE) log_fatalx("UTF-8 add invalid %#hhx", ictx->ch); log_debug("%s", __func__); @@ -1951,7 +1951,7 @@ input_utf8_close(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - if (utf8_append(ud, ictx->ch) != 0) + if (utf8_append(ud, ictx->ch) != UTF8_DONE) log_fatalx("UTF-8 close invalid %#hhx", ictx->ch); log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, diff --git a/key-string.c b/key-string.c index ad7cbf50..9a44892d 100644 --- a/key-string.c +++ b/key-string.c @@ -144,10 +144,11 @@ key_string_lookup_string(const char *string) static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; key_code key; u_short u; - int size, more; + int size; key_code modifiers; struct utf8_data ud; u_int i; + enum utf8_state more; /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { @@ -173,13 +174,12 @@ key_string_lookup_string(const char *string) return (KEYC_NONE); } else { /* Try as a UTF-8 key. */ - if (utf8_open(&ud, (u_char)*string)) { + if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { if (strlen(string) != ud.size) return (KEYC_NONE); - more = 1; for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); - if (more != 0) + if (more != UTF8_DONE) return (KEYC_NONE); key = utf8_combine(&ud); return (key | modifiers); @@ -256,7 +256,7 @@ key_string_lookup_key(key_code key) /* Is this a UTF-8 key? */ if (key > 127 && key < KEYC_BASE) { - if (utf8_split(key, &ud) == 0) { + if (utf8_split(key, &ud) == UTF8_DONE) { memcpy(out, ud.data, ud.size); out[ud.size] = '\0'; return (out); diff --git a/screen-write.c b/screen-write.c index 9e1ef822..a887b0f1 100644 --- a/screen-write.c +++ b/screen-write.c @@ -115,7 +115,7 @@ screen_write_strlen(const char *fmt, ...) struct utf8_data ud; u_char *ptr; size_t left, size = 0; - int more; + enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -123,17 +123,17 @@ screen_write_strlen(const char *fmt, ...) ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while ((more = utf8_append(&ud, *ptr)) == 1) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (more == 0) + if (more == UTF8_DONE) size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) @@ -178,23 +178,23 @@ screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct utf8_data ud; u_char *ptr; size_t left, size = 0; - int more; + enum utf8_state more; xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { - if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while ((more = utf8_append(&ud, *ptr)) == 1) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (more == 0) { + if (more == UTF8_DONE) { if (maxlen > 0 && size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { @@ -236,7 +236,7 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, char *msg; u_char *ptr, *last; size_t left, size = 0; - int more; + enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); @@ -260,17 +260,17 @@ screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, continue; } - if (*ptr > 0x7f && utf8_open(&ud, *ptr)) { + if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; - while ((more = utf8_append(&ud, *ptr)) == 1) + while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; - if (more == 0) { + if (more == UTF8_DONE) { if (maxlen > 0 && size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { diff --git a/tmux.h b/tmux.h index 2bc1f0a4..30e54a64 100644 --- a/tmux.h +++ b/tmux.h @@ -626,6 +626,11 @@ struct utf8_data { u_char width; /* 0xff if invalid */ } __packed; +enum utf8_state { + UTF8_MORE, + UTF8_DONE, + UTF8_ERROR +}; /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 @@ -2191,10 +2196,10 @@ void session_renumber_windows(struct session *); u_int utf8_width(u_int); void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); -int utf8_open(struct utf8_data *, u_char); -int utf8_append(struct utf8_data *, u_char); +enum utf8_state utf8_open(struct utf8_data *, u_char); +enum utf8_state utf8_append(struct utf8_data *, u_char); u_int utf8_combine(const struct utf8_data *); -int utf8_split(u_int, struct utf8_data *); +enum utf8_state utf8_split(u_int, struct utf8_data *); u_int utf8_split2(u_int, u_char *); int utf8_strvis(char *, const char *, size_t, int); char *utf8_sanitize(const char *); diff --git a/tty-keys.c b/tty-keys.c index cc6b934a..52dae4f7 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -472,9 +472,10 @@ tty_keys_next(struct tty *tty) const char *buf; size_t len, size; cc_t bspace; - int delay, expired = 0, more; + int delay, expired = 0; key_code key; struct utf8_data ud; + enum utf8_state more; u_int i; /* Get key buffer. */ @@ -539,17 +540,16 @@ first_key: } /* Is this valid UTF-8? */ - if (utf8_open(&ud, (u_char)*buf)) { + if ((more = utf8_open(&ud, (u_char)*buf) == UTF8_MORE)) { size = ud.size; if (len < size) { if (expired) goto discard_key; goto partial_key; } - more = 1; for (i = 1; i < size; i++) more = utf8_append(&ud, (u_char)buf[i]); - if (more != 0) + if (more != UTF8_DONE) goto discard_key; key = utf8_combine(&ud); log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); @@ -656,7 +656,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) struct utf8_data ud; u_int i, value, x, y, b, sgr_b; u_char sgr_type, c; - int more; + enum utf8_state more; /* * Standard mouse sequences are \033[M followed by three characters @@ -697,14 +697,14 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { - if (utf8_open(&ud, buf[*size])) { + if (utf8_open(&ud, buf[*size]) == UTF8_MORE) { if (ud.size != 2) return (-1); (*size)++; if (len <= *size) return (1); more = utf8_append(&ud, buf[*size]); - if (more != 0) + if (more != UTF8_DONE) return (-1); value = utf8_combine(&ud); } else diff --git a/utf8.c b/utf8.c index b27d6671..1bfea079 100644 --- a/utf8.c +++ b/utf8.c @@ -381,10 +381,8 @@ utf8_copy(struct utf8_data *to, const struct utf8_data *from) * 11000010-11011111 C2-DF start of 2-byte sequence * 11100000-11101111 E0-EF start of 3-byte sequence * 11110000-11110100 F0-F4 start of 4-byte sequence - * - * Returns 1 if more UTF-8 to come, 0 if not UTF-8. */ -int +enum utf8_state utf8_open(struct utf8_data *ud, u_char ch) { memset(ud, 0, sizeof *ud); @@ -395,18 +393,13 @@ utf8_open(struct utf8_data *ud, u_char ch) else if (ch >= 0xf0 && ch <= 0xf4) ud->size = 4; else - return (0); + return (UTF8_ERROR); utf8_append(ud, ch); - return (1); + return (UTF8_MORE); } -/* - * Append character to UTF-8, closing if finished. - * - * Returns 1 if more UTF-8 data to come, 0 if finished and valid, -1 if - * finished and invalid. - */ -int +/* Append character to UTF-8, closing if finished. */ +enum utf8_state utf8_append(struct utf8_data *ud, u_char ch) { if (ud->have >= ud->size) @@ -419,12 +412,12 @@ utf8_append(struct utf8_data *ud, u_char ch) ud->data[ud->have++] = ch; if (ud->have != ud->size) - return (1); + return (UTF8_MORE); if (ud->width == 0xff) - return (-1); + return (UTF8_ERROR); ud->width = utf8_width(utf8_combine(ud)); - return (0); + return (UTF8_DONE); } /* Build UTF-8 width tree. */ @@ -501,7 +494,7 @@ utf8_combine(const struct utf8_data *ud) } /* Split 32-bit Unicode into UTF-8. */ -int +enum utf8_state utf8_split(u_int uc, struct utf8_data *ud) { if (uc < 0x7f) { @@ -523,9 +516,9 @@ utf8_split(u_int uc, struct utf8_data *ud) ud->data[2] = 0x80 | ((uc >> 6) & 0x3f); ud->data[3] = 0x80 | (uc & 0x3f); } else - return (-1); + return (UTF8_ERROR); ud->width = utf8_width(uc); - return (0); + return (UTF8_DONE); } /* Split a two-byte UTF-8 character. */ @@ -551,26 +544,24 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) { struct utf8_data ud; const char *start, *end; - int more; + enum utf8_state more; size_t i; start = dst; end = src + len; while (src < end) { - if (utf8_open(&ud, *src)) { - more = 1; - while (++src < end && more == 1) + if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { + while (++src < end && more == UTF8_MORE) more = utf8_append(&ud, *src); - if (more == 0) { + if (more == UTF8_DONE) { /* UTF-8 character finished. */ for (i = 0; i < ud.size; i++) *dst++ = ud.data[i]; continue; - } else if (ud.have > 0) { - /* Not a complete, valid UTF-8 character. */ - src -= ud.have; } + /* Not a complete, valid UTF-8 character. */ + src -= ud.have; } if (src < end - 1) dst = vis(dst, src[0], flag, src[1]); @@ -593,7 +584,7 @@ utf8_sanitize(const char *src) { char *dst; size_t n; - int more; + enum utf8_state more; struct utf8_data ud; u_int i; @@ -602,11 +593,10 @@ utf8_sanitize(const char *src) n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); - if (utf8_open(&ud, *src)) { - more = 1; - while (*++src != '\0' && more == 1) + if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { + while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *src); - if (more != 1) { + if (more == UTF8_DONE) { dst = xreallocarray(dst, n + ud.width, sizeof *dst); for (i = 0; i < ud.width; i++) @@ -617,6 +607,8 @@ utf8_sanitize(const char *src) } if (*src > 0x1f && *src < 0x7f) dst[n++] = *src; + else + dst[n++] = '_'; src++; } @@ -634,27 +626,24 @@ utf8_fromcstr(const char *src) { struct utf8_data *dst; size_t n; - int more; + enum utf8_state more; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); - if (utf8_open(&dst[n], *src)) { - more = 1; - while (*++src != '\0' && more == 1) + if ((more = utf8_open(&dst[n], *src)) == UTF8_MORE) { + while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&dst[n], *src); - if (more != 1) { + if (more == UTF8_DONE) { n++; continue; } src -= dst[n].have; } - if (*src > 0x1f && *src < 0x7f) { - utf8_set(&dst[n], *src); - n++; - } + utf8_set(&dst[n], *src); + n++; src++; } @@ -690,21 +679,20 @@ utf8_cstrwidth(const char *s) { struct utf8_data tmp; u_int width; - int more; + enum utf8_state more; width = 0; while (*s != '\0') { - if (utf8_open(&tmp, *s)) { - more = 1; - while (*++s != '\0' && more == 1) + if ((more = utf8_open(&tmp, *s)) == UTF8_MORE) { + while (*++s != '\0' && more == UTF8_MORE) more = utf8_append(&tmp, *s); - if (more != 1) { + if (more == UTF8_DONE) { width += tmp.width; continue; } s -= tmp.have; } - if (*s > 0x1f && *s < 0x7f) + if (*s > 0x1f && *s != 0x7f) width++; s++; } From 3db0d50df44d91a110f9320cb4fd327c97b96df2 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 12:03:23 +0000 Subject: [PATCH 414/703] The private use area at U+E000 to U+F8FF is not very useful if it is width 0, make it width 1 instead. --- utf8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utf8.c b/utf8.c index 1bfea079..b391c5ec 100644 --- a/utf8.c +++ b/utf8.c @@ -264,7 +264,7 @@ static struct utf8_width_entry utf8_width_table[] = { { 0x0abe5, 0x0abe5, 0, NULL, NULL }, { 0x0abed, 0x0abed, 0, NULL, NULL }, { 0x0f900, 0x0fa6d, 2, NULL, NULL }, - { 0x0d800, 0x0f8ff, 0, NULL, NULL }, + { 0x0d800, 0x0dfff, 0, NULL, NULL }, { 0x0fa70, 0x0fad9, 2, NULL, NULL }, { 0x0fff9, 0x0fffb, 0, NULL, NULL }, { 0x0fe30, 0x0fe52, 2, NULL, NULL }, From 14d90e4901ef943e57e5dada7a0f53b2dc208571 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 14 Nov 2015 12:20:19 +0000 Subject: [PATCH 415/703] The character is an int so use %x not %hhx. --- input.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index f7b4e460..6e1fec90 100644 --- a/input.c +++ b/input.c @@ -1924,7 +1924,7 @@ input_utf8_open(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_open(ud, ictx->ch) != UTF8_MORE) - log_fatalx("UTF-8 open invalid %#hhx", ictx->ch); + log_fatalx("UTF-8 open invalid %#x", ictx->ch); log_debug("%s %hhu", __func__, ud->size); @@ -1938,7 +1938,7 @@ input_utf8_add(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_MORE) - log_fatalx("UTF-8 add invalid %#hhx", ictx->ch); + log_fatalx("UTF-8 add invalid %#x", ictx->ch); log_debug("%s", __func__); @@ -1952,7 +1952,7 @@ input_utf8_close(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_DONE) - log_fatalx("UTF-8 close invalid %#hhx", ictx->ch); + log_fatalx("UTF-8 close invalid %#x", ictx->ch); log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); From a582b622879714b0a100e8c7d3054f5a03476ca1 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 15 Nov 2015 14:32:48 +0000 Subject: [PATCH 416/703] Accidentally turned off pledge, turn it back on. --- client.c | 4 ++-- server.c | 2 +- tmux.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client.c b/client.c index a6423630..bc934d33 100644 --- a/client.c +++ b/client.c @@ -289,7 +289,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) * * "sendfd" is dropped later in client_dispatch_wait(). */ - if (0 && pledge("stdio unix sendfd proc exec tty", NULL) != 0) + if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) fatal("pledge failed"); /* Free stuff that is not used in the client. */ @@ -541,7 +541,7 @@ client_dispatch_wait(struct imsg *imsg) * get the first message from the server. */ if (!pledge_applied) { - if (0 && pledge("stdio unix proc exec tty", NULL) != 0) + if (pledge("stdio unix proc exec tty", NULL) != 0) fatal("pledge failed"); pledge_applied = 1; }; diff --git a/server.c b/server.c index 160b8cac..2808c0cc 100644 --- a/server.c +++ b/server.c @@ -175,7 +175,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) if (debug_level > 3) tty_create_log(); - if (0 && pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " + if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " "ps", NULL) != 0) fatal("pledge failed"); diff --git a/tmux.c b/tmux.c index 3f110156..5429a7cb 100644 --- a/tmux.c +++ b/tmux.c @@ -255,7 +255,7 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); - if (0 && pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " + if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " "proc exec tty ps", NULL) != 0) err(1, "pledge"); From 661d0dfac9ad355ad32a9eaebd61d9e340fcfc3b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 15 Nov 2015 22:50:38 +0000 Subject: [PATCH 417/703] Make key_code unsigned long long not uint64_t which is more portable for printf formats, and move UTF8_SIZE define down to near the rest of the UTF-8 bits. --- tmux.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tmux.h b/tmux.h index 30e54a64..b81bbfcd 100644 --- a/tmux.h +++ b/tmux.h @@ -57,12 +57,6 @@ struct tmuxproc; /* Automatic name refresh interval, in microseconds. Must be < 1 second. */ #define NAME_INTERVAL 500000 -/* - * UTF-8 data size. This must be big enough to hold combined characters as well - * as single. - */ -#define UTF8_SIZE 9 - /* * READ_SIZE is the maximum size of data to hold from a pty (the event high * watermark). READ_BACKOFF is the amount of data waiting to be output to a tty @@ -125,7 +119,7 @@ struct tmuxproc; * A single key. This can be ASCII or Unicode or one of the keys starting at * KEYC_BASE. */ -typedef uint64_t key_code; +typedef unsigned long long key_code; /* Special key codes. */ enum { @@ -617,7 +611,11 @@ struct mode_key_table { #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON) -/* A single UTF-8 character. */ +/* + * A single UTF-8 character. UTF8_SIZE must be big enough to hold at least one + * combining character as well. +*/ +#define UTF8_SIZE 9 struct utf8_data { u_char data[UTF8_SIZE]; From 775fb562bd0026ca4b2edc9bde99a85e4daf40ed Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 16 Nov 2015 22:57:51 +0000 Subject: [PATCH 418/703] 0x7f is a valid key. --- input-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input-keys.c b/input-keys.c index 156cbe6a..2915cb45 100644 --- a/input-keys.c +++ b/input-keys.c @@ -161,7 +161,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) * if necessary. If it is a UTF-8 key, split it and send it. */ justkey = (key & ~KEYC_ESCAPE); - if (key != KEYC_NONE && justkey < 0x7f) { + if (key != KEYC_NONE && justkey <= 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; From d0505fd04256247a5c8d56888ff19a9b6da80cea Mon Sep 17 00:00:00 2001 From: tobias Date: Tue, 17 Nov 2015 18:25:03 +0000 Subject: [PATCH 419/703] Merge xmalloc.[ch] files across base, skipping OpenSSH for now. ok nicm --- xmalloc.c | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/xmalloc.c b/xmalloc.c index b1570a3a..3db67af6 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -25,16 +25,13 @@ #include "tmux.h" char * -xstrdup(const char *s) +xstrdup(const char *str) { - char *ptr; - size_t len; + char *cp; - len = strlen(s) + 1; - ptr = xmalloc(len); - - strlcpy(ptr, s, len); - return (ptr); + if ((cp = strdup(str)) == NULL) + fatal("xstrdup"); + return (cp); } void * @@ -43,11 +40,9 @@ xcalloc(size_t nmemb, size_t size) void *ptr; if (size == 0 || nmemb == 0) - fatalx("zero size"); - if (SIZE_MAX / nmemb < size) - fatalx("nmemb * size > SIZE_MAX"); + fatalx("xcalloc: zero size"); if ((ptr = calloc(nmemb, size)) == NULL) - fatal("xcalloc failed"); + log_fatal("xcalloc: allocating %zu bytes", size); return (ptr); } @@ -58,9 +53,9 @@ xmalloc(size_t size) void *ptr; if (size == 0) - fatalx("zero size"); + fatalx("xmalloc: zero size"); if ((ptr = malloc(size)) == NULL) - fatal("xmalloc failed"); + log_fatal("xmalloc: allocating %zu bytes", size); return (ptr); } @@ -71,9 +66,9 @@ xrealloc(void *oldptr, size_t newsize) void *newptr; if (newsize == 0) - fatalx("zero size"); + fatalx("xrealloc: zero size"); if ((newptr = realloc(oldptr, newsize)) == NULL) - fatal("xrealloc failed"); + log_fatal("xrealloc: allocating %zu bytes", newsize); return (newptr); } @@ -81,15 +76,13 @@ xrealloc(void *oldptr, size_t newsize) void * xreallocarray(void *oldptr, size_t nmemb, size_t size) { - size_t newsize = nmemb * size; void *newptr; - if (newsize == 0) - fatalx("zero size"); - if (SIZE_MAX / nmemb < size) - fatalx("nmemb * size > SIZE_MAX"); - if ((newptr = realloc(oldptr, newsize)) == NULL) - fatal("xreallocarray failed"); + if (nmemb == 0 || size == 0) + fatalx("xreallocarray: zero size"); + if ((newptr = reallocarray(oldptr, nmemb, size)) == NULL) + log_fatal("xreallocarray: allocating %zu * %zu bytes", + nmemb, size); return (newptr); } @@ -114,7 +107,7 @@ xvasprintf(char **ret, const char *fmt, va_list ap) i = vasprintf(ret, fmt, ap); if (i < 0 || *ret == NULL) - fatal("xvasprintf failed"); + fatal("xvasprintf"); return (i); } @@ -138,11 +131,11 @@ xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) int i; if (len > INT_MAX) - fatalx("len > INT_MAX"); + fatalx("xvsnprintf: len > INT_MAX"); i = vsnprintf(buf, len, fmt, ap); - if (i < 0) - fatal("vsnprintf failed"); + if (i < 0 || i >= (int)len) + fatalx("xvsnprintf: overflow"); return (i); } From ca5e6bf5f2f11796bf2bdbe136ba534e46b2e86e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 09:22:29 +0000 Subject: [PATCH 420/703] Don't update activity time twice for new sessions, and add some logging. --- session.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/session.c b/session.c index cbaadcee..deb29d18 100644 --- a/session.c +++ b/session.c @@ -131,10 +131,6 @@ session_create(const char *name, int argc, char **argv, const char *path, memcpy(s->tio, tio, sizeof *s->tio); } - if (gettimeofday(&s->creation_time, NULL) != 0) - fatal("gettimeofday failed"); - session_update_activity(s, &s->creation_time); - s->sx = sx; s->sy = sy; @@ -151,6 +147,8 @@ session_create(const char *name, int argc, char **argv, const char *path, } RB_INSERT(sessions, &sessions, s); + log_debug("new session %s $%u", s->name, s->id); + if (gettimeofday(&s->creation_time, NULL) != 0) fatal("gettimeofday failed"); session_update_activity(s, &s->creation_time); @@ -265,6 +263,10 @@ session_update_activity(struct session *s, struct timeval *from) else memcpy(&s->activity_time, from, sizeof s->activity_time); + log_debug("session %s activity %lld.%06d (last %lld.%06d)", s->name, + (long long)s->activity_time.tv_sec, (int)s->activity_time.tv_usec, + (long long)last->tv_sec, (int)last->tv_usec); + if (evtimer_initialized(&s->lock_timer)) evtimer_del(&s->lock_timer); else From a77960c540b6ee27ad5538a638ae3615cd025539 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 12:54:29 +0000 Subject: [PATCH 421/703] Add reallocarray to compat. --- Makefile.am | 3 +++ compat/reallocarray.c | 40 ++++++++++++++++++++++++++++++++++++++++ configure.ac | 7 +++++++ 3 files changed, 50 insertions(+) create mode 100644 compat/reallocarray.c diff --git a/Makefile.am b/Makefile.am index 02784c4a..5b11400c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -234,6 +234,9 @@ endif if NO_OPENAT nodist_tmux_SOURCES += compat/openat.c endif +if NO_REALLOCARRAY +nodist_tmux_SOURCES += compat/reallocarray.c +endif # Install tmux.1 in the right format. install-exec-hook: diff --git a/compat/reallocarray.c b/compat/reallocarray.c new file mode 100644 index 00000000..f4705fcd --- /dev/null +++ b/compat/reallocarray.c @@ -0,0 +1,40 @@ +/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "tmux.h" + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} diff --git a/configure.ac b/configure.ac index a2b9e054..6cd859f0 100644 --- a/configure.ac +++ b/configure.ac @@ -365,6 +365,13 @@ if test "x$found_openat" = xyes; then fi AM_CONDITIONAL(NO_OPENAT, [test "x$found_openat" = xno]) +# Look for reallocarray, compat/reallocarray.c used if missing. +AC_CHECK_FUNC(reallocarray, found_reallocarray=yes, found_reallocarray=no) +if test "x$found_reallocarray" = xyes; then + AC_DEFINE(HAVE_REALLOCARRAY) +fi +AM_CONDITIONAL(NO_REALLOCARRAY, [test "x$found_reallocarray" = xno]) + # Look for getopt. glibc's getopt does not enforce argument order and the ways # of making it do so are stupid, so just use our own instead. AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) From 64571368dc19219fc1ef9b6c20034ee143cbed0d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 13:06:54 +0000 Subject: [PATCH 422/703] Sync the entire xmalloc.[ch] with the other users, but with the addition of xrealloc, xvasprintf, xvsnprintf. --- input.c | 6 +-- log.c | 4 +- options.c | 12 ++--- tmux.h | 21 ++------ tty-term.c | 6 +-- xmalloc.c | 138 ++++++++++++++++++++++++++--------------------------- xmalloc.h | 40 ++++++++++++++++ 7 files changed, 125 insertions(+), 102 deletions(-) create mode 100644 xmalloc.h diff --git a/input.c b/input.c index 6e1fec90..d8e80afb 100644 --- a/input.c +++ b/input.c @@ -1924,7 +1924,7 @@ input_utf8_open(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_open(ud, ictx->ch) != UTF8_MORE) - log_fatalx("UTF-8 open invalid %#x", ictx->ch); + fatalx("UTF-8 open invalid %#x", ictx->ch); log_debug("%s %hhu", __func__, ud->size); @@ -1938,7 +1938,7 @@ input_utf8_add(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_MORE) - log_fatalx("UTF-8 add invalid %#x", ictx->ch); + fatalx("UTF-8 add invalid %#x", ictx->ch); log_debug("%s", __func__); @@ -1952,7 +1952,7 @@ input_utf8_close(struct input_ctx *ictx) struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_DONE) - log_fatalx("UTF-8 close invalid %#x", ictx->ch); + fatalx("UTF-8 close invalid %#x", ictx->ch); log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); diff --git a/log.c b/log.c index 711b9b8a..cf65c9e3 100644 --- a/log.c +++ b/log.c @@ -102,7 +102,7 @@ log_debug(const char *msg, ...) /* Log a critical error with error string and die. */ __dead void -log_fatal(const char *msg, ...) +fatal(const char *msg, ...) { char *fmt; va_list ap; @@ -116,7 +116,7 @@ log_fatal(const char *msg, ...) /* Log a critical error and die. */ __dead void -log_fatalx(const char *msg, ...) +fatalx(const char *msg, ...) { char *fmt; va_list ap; diff --git a/options.c b/options.c index 8d2fb715..c355f9ce 100644 --- a/options.c +++ b/options.c @@ -150,9 +150,9 @@ options_get_string(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - log_fatalx("missing option %s", name); + fatalx("missing option %s", name); if (o->type != OPTIONS_STRING) - log_fatalx("option %s not a string", name); + fatalx("option %s not a string", name); return (o->str); } @@ -180,9 +180,9 @@ options_get_number(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - log_fatalx("missing option %s", name); + fatalx("missing option %s", name); if (o->type != OPTIONS_NUMBER) - log_fatalx("option %s not a number", name); + fatalx("option %s not a number", name); return (o->num); } @@ -220,8 +220,8 @@ options_get_style(struct options *oo, const char *name) struct options_entry *o; if ((o = options_find(oo, name)) == NULL) - log_fatalx("missing option %s", name); + fatalx("missing option %s", name); if (o->type != OPTIONS_STYLE) - log_fatalx("option %s not a style", name); + fatalx("option %s not a style", name); return (&o->style); } diff --git a/tmux.h b/tmux.h index b81bbfcd..421f2369 100644 --- a/tmux.h +++ b/tmux.h @@ -45,6 +45,8 @@ struct session; struct tmuxpeer; struct tmuxproc; +#include "xmalloc.h" + /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" @@ -67,10 +69,6 @@ struct tmuxproc; #define READ_BACKOFF 512 #define READ_TIME 100 -/* Fatal errors. */ -#define fatal(msg) log_fatal("%s: %s", __func__, msg); -#define fatalx(msg) log_fatalx("%s: %s", __func__, msg); - /* Definition to shut gcc up about unused arguments. */ #define unused __attribute__ ((unused)) @@ -2214,19 +2212,8 @@ char *get_proc_name(int, char *); void log_open(const char *); void log_close(void); void printflike(1, 2) log_debug(const char *, ...); -__dead void printflike(1, 2) log_fatal(const char *, ...); -__dead void printflike(1, 2) log_fatalx(const char *, ...); - -/* xmalloc.c */ -char *xstrdup(const char *); -void *xcalloc(size_t, size_t); -void *xmalloc(size_t); -void *xrealloc(void *, size_t); -void *xreallocarray(void *, size_t, size_t); -int printflike(2, 3) xasprintf(char **, const char *, ...); -int xvasprintf(char **, const char *, va_list); -int printflike(3, 4) xsnprintf(char *, size_t, const char *, ...); -int xvsnprintf(char *, size_t, const char *, va_list); +__dead void printflike(1, 2) fatal(const char *, ...); +__dead void printflike(1, 2) fatalx(const char *, ...); /* style.c */ int style_parse(const struct grid_cell *, diff --git a/tty-term.c b/tty-term.c index c6147559..5f2e1a94 100644 --- a/tty-term.c +++ b/tty-term.c @@ -556,7 +556,7 @@ tty_term_string(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (""); if (term->codes[code].type != TTYCODE_STRING) - log_fatalx("not a string: %d", code); + fatalx("not a string: %d", code); return (term->codes[code].value.string); } @@ -591,7 +591,7 @@ tty_term_number(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_NUMBER) - log_fatalx("not a number: %d", code); + fatalx("not a number: %d", code); return (term->codes[code].value.number); } @@ -601,7 +601,7 @@ tty_term_flag(struct tty_term *term, enum tty_code_code code) if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_FLAG) - log_fatalx("not a flag: %d", code); + fatalx("not a flag: %d", code); return (term->codes[code].value.flag); } diff --git a/xmalloc.c b/xmalloc.c index 3db67af6..afa8e585 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -1,141 +1,137 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2004 Nicholas Marriott + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER - * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". */ -#include - +#include +#include #include +#include #include #include #include "tmux.h" -char * -xstrdup(const char *str) +void * +xmalloc(size_t size) { - char *cp; + void *ptr; - if ((cp = strdup(str)) == NULL) - fatal("xstrdup"); - return (cp); + if (size == 0) + fatal("xmalloc: zero size"); + ptr = malloc(size); + if (ptr == NULL) + fatal("xmalloc: allocating %zu bytes: %s", + size, strerror(errno)); + return ptr; } void * xcalloc(size_t nmemb, size_t size) { - void *ptr; + void *ptr; if (size == 0 || nmemb == 0) - fatalx("xcalloc: zero size"); - if ((ptr = calloc(nmemb, size)) == NULL) - log_fatal("xcalloc: allocating %zu bytes", size); - - return (ptr); + fatal("xcalloc: zero size"); + ptr = calloc(nmemb, size); + if (ptr == NULL) + fatal("xcalloc: allocating %zu * %zu bytes: %s", + nmemb, size, strerror(errno)); + return ptr; } void * -xmalloc(size_t size) +xrealloc(void *ptr, size_t size) { - void *ptr; - - if (size == 0) - fatalx("xmalloc: zero size"); - if ((ptr = malloc(size)) == NULL) - log_fatal("xmalloc: allocating %zu bytes", size); - - return (ptr); + return xreallocarray(ptr, 1, size); } void * -xrealloc(void *oldptr, size_t newsize) +xreallocarray(void *ptr, size_t nmemb, size_t size) { - void *newptr; - - if (newsize == 0) - fatalx("xrealloc: zero size"); - if ((newptr = realloc(oldptr, newsize)) == NULL) - log_fatal("xrealloc: allocating %zu bytes", newsize); - - return (newptr); -} - -void * -xreallocarray(void *oldptr, size_t nmemb, size_t size) -{ - void *newptr; + void *new_ptr; if (nmemb == 0 || size == 0) - fatalx("xreallocarray: zero size"); - if ((newptr = reallocarray(oldptr, nmemb, size)) == NULL) - log_fatal("xreallocarray: allocating %zu * %zu bytes", - nmemb, size); + fatal("xreallocarray: zero size"); + new_ptr = reallocarray(ptr, nmemb, size); + if (new_ptr == NULL) + fatal("xreallocarray: allocating %zu * %zu bytes: %s", + nmemb, size, strerror(errno)); + return new_ptr; +} - return (newptr); +char * +xstrdup(const char *str) +{ + char *cp; + + if ((cp = strdup(str)) == NULL) + fatal("xstrdup: %s", strerror(errno)); + return cp; } int xasprintf(char **ret, const char *fmt, ...) { va_list ap; - int i; + int i; va_start(ap, fmt); i = xvasprintf(ret, fmt, ap); va_end(ap); - return (i); + return i; } int xvasprintf(char **ret, const char *fmt, va_list ap) { - int i; + int i; i = vasprintf(ret, fmt, ap); - if (i < 0 || *ret == NULL) - fatal("xvasprintf"); - return (i); + if (i < 0 || *ret == NULL) + fatal("xasprintf: %s", strerror(errno)); + + return i; } int -xsnprintf(char *buf, size_t len, const char *fmt, ...) +xsnprintf(char *str, size_t len, const char *fmt, ...) { va_list ap; - int i; + int i; va_start(ap, fmt); - i = xvsnprintf(buf, len, fmt, ap); + i = xvsnprintf(str, len, fmt, ap); va_end(ap); - return (i); + return i; } int -xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) +xvsnprintf(char *str, size_t len, const char *fmt, va_list ap) { - int i; + int i; if (len > INT_MAX) - fatalx("xvsnprintf: len > INT_MAX"); + fatal("xsnprintf: len > INT_MAX"); + + i = vsnprintf(str, len, fmt, ap); - i = vsnprintf(buf, len, fmt, ap); if (i < 0 || i >= (int)len) - fatalx("xvsnprintf: overflow"); + fatal("xsnprintf: overflow"); - return (i); + return i; } diff --git a/xmalloc.h b/xmalloc.h new file mode 100644 index 00000000..d331ce99 --- /dev/null +++ b/xmalloc.h @@ -0,0 +1,40 @@ +/* $OpenBSD$ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Mon Mar 20 22:09:17 1995 ylo + * + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef XMALLOC_H +#define XMALLOC_H + +void *xmalloc(size_t); +void *xcalloc(size_t, size_t); +void *xrealloc(void *, size_t); +void *xreallocarray(void *, size_t, size_t); +char *xstrdup(const char *); +int xasprintf(char **, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); +int xvasprintf(char **, const char *, va_list) + __attribute__((__nonnull__ (2))); +int xsnprintf(char *, size_t, const char *, ...) + __attribute__((__format__ (printf, 3, 4))) + __attribute__((__nonnull__ (3))) + __attribute__((__bounded__ (__string__, 1, 2))); +int xvsnprintf(char *, size_t, const char *, va_list) + __attribute__((__nonnull__ (3))) + __attribute__((__bounded__ (__string__, 1, 2))); + +#endif /* XMALLOC_H */ From 9bba26f8c53e8bf90daf5330bf959b3ce1f70426 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 14:09:17 +0000 Subject: [PATCH 423/703] Add reallocarray prototype. --- compat.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/compat.h b/compat.h index 8666a1d8..7d9b14fd 100644 --- a/compat.h +++ b/compat.h @@ -255,13 +255,18 @@ int unsetenv(const char *); #ifndef HAVE_CFMAKERAW /* cfmakeraw.c */ -void cfmakeraw(struct termios *); +void cfmakeraw(struct termios *); #endif #ifndef HAVE_OPENAT /* openat.c */ #define AT_FDCWD -100 -int openat(int, const char *, int, ...); +int openat(int, const char *, int, ...); +#endif + +#ifndef HAVE_REALLOCARRAY +/* reallocarray.c */ +void *reallocarray(void *, size_t, size_t size); #endif #ifdef HAVE_GETOPT From f8a1f8843c91ebb1262ec6a000fd495eeb27e179 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 14:10:48 +0000 Subject: [PATCH 424/703] Add -Wno-attributes. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 5b11400c..b561866a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,7 +30,7 @@ CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align -CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign +CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes CPPFLAGS += -DDEBUG endif if IS_COVERAGE From 5a5b950e8b86606edea8f61c40bf8af28ab4f08b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 14:13:55 +0000 Subject: [PATCH 425/703] Add s/foo/bar/: prefix for formats to substitute bar for foo. --- format.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++---------- tmux.1 | 7 +++++ 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/format.c b/format.c index 04e06fc6..e8d5dd09 100644 --- a/format.c +++ b/format.c @@ -99,6 +99,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_TIMESTRING 0x1 #define FORMAT_BASENAME 0x2 #define FORMAT_DIRNAME 0x4 +#define FORMAT_SUBSTITUTE 0x8 /* Entry in format tree. */ struct format_entry { @@ -682,8 +683,9 @@ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - char *copy, *copy0, *endptr, *ptr, *saved, *trimmed, *value; - size_t valuelen; + char *copy, *copy0, *endptr, *ptr, *found, *new, *value; + char *from = NULL, *to = NULL; + size_t valuelen, newlen, fromlen, tolen, used; u_long limit = 0; int modifiers = 0, brackets; @@ -721,6 +723,29 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, modifiers |= FORMAT_TIMESTRING; copy += 2; break; + case 's': + if (copy[1] != '/') + break; + from = copy + 2; + for (copy = from; *copy != '\0' && *copy != '/'; copy++) + /* nothing */; + if (copy[0] != '/' || copy == from) { + copy = copy0; + break; + } + copy[0] = '\0'; + to = copy + 1; + for (copy = to; *copy != '\0' && *copy != '/'; copy++) + /* nothing */; + if (copy[0] != '/' || copy[1] != ':') { + copy = copy0; + break; + } + copy[0] = '\0'; + + modifiers |= FORMAT_SUBSTITUTE; + copy += 2; + break; } /* @@ -734,7 +759,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, *ptr = '\0'; value = ptr + 1; - saved = format_find(ft, copy + 1, modifiers); + found = format_find(ft, copy + 1, modifiers); brackets = 0; for (ptr = ptr + 1; *ptr != '\0'; ptr++) { @@ -748,29 +773,56 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, if (*ptr == '\0') goto fail; - if (saved != NULL && *saved != '\0' && - (saved[0] != '0' || saved[1] != '\0')) { + if (found != NULL && *found != '\0' && + (found[0] != '0' || found[1] != '\0')) { *ptr = '\0'; } else value = ptr + 1; value = format_expand(ft, value); - free(saved); - saved = value; + free(found); } else { - saved = value = format_find(ft, copy, modifiers); + value = format_find(ft, copy, modifiers); if (value == NULL) - saved = value = xstrdup(""); + value = xstrdup(""); + } + + /* Perform substitution if any. */ + if (modifiers & FORMAT_SUBSTITUTE) { + fromlen = strlen(from); + tolen = strlen(to); + + newlen = strlen(value) + 1; + copy = new = xmalloc(newlen); + for (ptr = value; *ptr != '\0'; /* nothing */) { + if (strncmp(ptr, from, fromlen) != 0) { + *new++ = *ptr++; + continue; + } + used = new - copy; + + newlen += tolen; + copy = xrealloc(copy, newlen); + + new = copy + used; + memcpy(new, to, tolen); + + new += tolen; + ptr += fromlen; + } + *new = '\0'; + free(value); + value = copy; } /* Truncate the value if needed. */ if (limit != 0) { - value = trimmed = utf8_trimcstr(value, limit); - free(saved); - saved = trimmed; + new = utf8_trimcstr(value, limit); + free(value); + value = new; } - valuelen = strlen(value); /* Expand the buffer and copy in the value. */ + valuelen = strlen(value); while (*len - *off < valuelen + 1) { *buf = xreallocarray(*buf, 2, *len); *len *= 2; @@ -778,7 +830,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, memcpy(*buf + *off, value, valuelen); *off += valuelen; - free(saved); + free(value); free(copy0); return (0); diff --git a/tmux.1 b/tmux.1 index 3a08c8cd..a22bb3f3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3311,6 +3311,13 @@ prefixes are and .Xr dirname 3 of the variable respectively. +A prefix of the form +.Ql s/foo/bar/: +will substitute +.Ql foo +with +.Ql bar +throughout. .Pp In addition, the first line of a shell command's output may be inserted using .Ql #() . From 577c0e3e5a79e9f1f860487bc3f411d26758f026 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 14:27:44 +0000 Subject: [PATCH 426/703] Use __unused rather than rolling our own. --- alerts.c | 4 ++-- cfg.c | 2 +- client.c | 5 +++-- cmd-kill-server.c | 2 +- cmd-list-buffers.c | 2 +- cmd-list-keys.c | 6 +++--- cmd-lock-server.c | 2 +- cmd-pipe-pane.c | 4 ++-- cmd-set-option.c | 14 +++++++------- cmd-wait-for.c | 4 ++-- control-notify.c | 8 ++++---- control.c | 2 +- format.c | 8 ++++---- job.c | 5 +++-- log.c | 2 +- names.c | 2 +- proc.c | 4 ++-- screen-write.c | 2 +- server-client.c | 8 ++++---- server-fn.c | 2 +- server.c | 2 +- session.c | 4 ++-- status.c | 4 ++-- tmux.h | 3 --- tty-keys.c | 2 +- tty.c | 8 ++++---- window-choose.c | 4 ++-- window-clock.c | 8 ++++---- window-copy.c | 6 +++--- window.c | 8 ++++---- 30 files changed, 68 insertions(+), 69 deletions(-) diff --git a/alerts.c b/alerts.c index f1477030..32e0d496 100644 --- a/alerts.c +++ b/alerts.c @@ -35,7 +35,7 @@ int alerts_check_silence(struct session *, struct winlink *); void alerts_ring_bell(struct session *); void -alerts_timer(unused int fd, unused short events, void *arg) +alerts_timer(__unused int fd, __unused short events, void *arg) { struct window *w = arg; @@ -45,7 +45,7 @@ alerts_timer(unused int fd, unused short events, void *arg) } void -alerts_callback(unused int fd, unused short events, unused void *arg) +alerts_callback(__unused int fd, __unused short events, __unused void *arg) { struct window *w; struct session *s; diff --git a/cfg.c b/cfg.c index 2f457e6b..136c94d1 100644 --- a/cfg.c +++ b/cfg.c @@ -133,7 +133,7 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) } void -cfg_default_done(unused struct cmd_q *cmdq) +cfg_default_done(__unused struct cmd_q *cmdq) { if (--cfg_references != 0) return; diff --git a/client.c b/client.c index bc934d33..53a98a30 100644 --- a/client.c +++ b/client.c @@ -410,7 +410,8 @@ client_send_identify(const char *ttynam, const char *cwd) /* Callback for client stdin read events. */ void -client_stdin_callback(unused int fd, unused short events, unused void *arg) +client_stdin_callback(__unused int fd, __unused short events, + __unused void *arg) { struct msg_stdin_data data; @@ -512,7 +513,7 @@ client_signal(int sig) /* Callback for client read events. */ void -client_dispatch(struct imsg *imsg, unused void *arg) +client_dispatch(struct imsg *imsg, __unused void *arg) { if (imsg == NULL) { client_exitreason = CLIENT_EXIT_LOST_SERVER; diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 07d94302..4107e6b6 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -46,7 +46,7 @@ const struct cmd_entry cmd_start_server_entry = { }; enum cmd_retval -cmd_kill_server_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_kill_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { if (self->entry == &cmd_kill_server_entry) kill(getpid(), SIGTERM); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 3a8a790a..a0036032 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_list_buffers_entry = { }; enum cmd_retval -cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) +cmd_list_buffers_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 3b6afa3e..4355f24e 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -30,7 +30,7 @@ enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_q *); -enum cmd_retval cmd_list_keys_commands(struct cmd *, struct cmd_q *); +enum cmd_retval cmd_list_keys_commands(struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { "list-keys", "lsk", @@ -60,7 +60,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) int repeat, width, tablewidth, keywidth; if (self->entry == &cmd_list_commands_entry) - return (cmd_list_keys_commands(self, cmdq)); + return (cmd_list_keys_commands(cmdq)); if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); @@ -178,7 +178,7 @@ cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) } enum cmd_retval -cmd_list_keys_commands(unused struct cmd *self, struct cmd_q *cmdq) +cmd_list_keys_commands(struct cmd_q *cmdq) { const struct cmd_entry **entryp; const struct cmd_entry *entry; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index de76475d..a0204129 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -51,7 +51,7 @@ const struct cmd_entry cmd_lock_client_entry = { }; enum cmd_retval -cmd_lock_server_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_lock_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index df706432..87c802b9 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -142,8 +142,8 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) } void -cmd_pipe_pane_error_callback( - unused struct bufferevent *bufev, unused short what, void *data) +cmd_pipe_pane_error_callback(__unused struct bufferevent *bufev, + __unused short what, void *data) { struct window_pane *wp = data; diff --git a/cmd-set-option.c b/cmd-set-option.c index c5a77b45..894f0c43 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -350,7 +350,7 @@ cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, /* Set a string option. */ struct options_entry * -cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, +cmd_set_option_string(struct cmd *self, __unused struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -372,7 +372,7 @@ cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, /* Set a number option. */ struct options_entry * -cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_number(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -390,7 +390,7 @@ cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, /* Set a key option. */ struct options_entry * -cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_key(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -406,7 +406,7 @@ cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, /* Set a colour option. */ struct options_entry * -cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_colour(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -422,7 +422,7 @@ cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, /* Set an attributes option. */ struct options_entry * -cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_attributes(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -438,7 +438,7 @@ cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, /* Set a flag option. */ struct options_entry * -cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_flag(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -466,7 +466,7 @@ cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, /* Set a choice option. */ struct options_entry * -cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq, +cmd_set_option_choice(__unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 79e0b55e..04316d5e 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -111,7 +111,7 @@ cmd_wait_for_remove(struct wait_channel *wc) } enum cmd_retval -cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) +cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const char *name = args->argv[0]; @@ -130,7 +130,7 @@ cmd_wait_for_exec(struct cmd *self, unused struct cmd_q *cmdq) } enum cmd_retval -cmd_wait_for_signal(unused struct cmd_q *cmdq, const char *name, +cmd_wait_for_signal(__unused struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { struct cmd_q *wq, *wq1; diff --git a/control-notify.c b/control-notify.c index 16a98e3a..db3648a4 100644 --- a/control-notify.c +++ b/control-notify.c @@ -101,7 +101,7 @@ control_notify_window_layout_changed(struct window *w) } void -control_notify_window_unlinked(unused struct session *s, struct window *w) +control_notify_window_unlinked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; @@ -119,7 +119,7 @@ control_notify_window_unlinked(unused struct session *s, struct window *w) } void -control_notify_window_linked(unused struct session *s, struct window *w) +control_notify_window_linked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; @@ -183,7 +183,7 @@ control_notify_session_renamed(struct session *s) } void -control_notify_session_created(unused struct session *s) +control_notify_session_created(__unused struct session *s) { struct client *c; @@ -196,7 +196,7 @@ control_notify_session_created(unused struct session *s) } void -control_notify_session_close(unused struct session *s) +control_notify_session_close(__unused struct session *s) { struct client *c; diff --git a/control.c b/control.c index f7264944..e799a4cb 100644 --- a/control.c +++ b/control.c @@ -51,7 +51,7 @@ control_write_buffer(struct client *c, struct evbuffer *buffer) /* Control input callback. Read lines and fire commands. */ void -control_callback(struct client *c, int closed, unused void *data) +control_callback(struct client *c, int closed, __unused void *data) { char *line, *cause; struct cmd_list *cmdlist; diff --git a/format.c b/format.c index e8d5dd09..50fa7dea 100644 --- a/format.c +++ b/format.c @@ -259,7 +259,7 @@ format_job_get(struct format_tree *ft, const char *cmd) /* Remove old jobs. */ void -format_job_timer(unused int fd, unused short events, unused void *arg) +format_job_timer(__unused int fd, __unused short events, __unused void *arg) { struct format_job *fj, *fj1; time_t now; @@ -288,7 +288,7 @@ format_job_timer(unused int fd, unused short events, unused void *arg) /* Callback for host. */ void -format_cb_host(unused struct format_tree *ft, struct format_entry *fe) +format_cb_host(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1]; @@ -300,7 +300,7 @@ format_cb_host(unused struct format_tree *ft, struct format_entry *fe) /* Callback for host_short. */ void -format_cb_host_short(unused struct format_tree *ft, struct format_entry *fe) +format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1], *cp; @@ -315,7 +315,7 @@ format_cb_host_short(unused struct format_tree *ft, struct format_entry *fe) /* Callback for pid. */ void -format_cb_pid(unused struct format_tree *ft, struct format_entry *fe) +format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) { xasprintf(&fe->value, "%ld", (long)getpid()); } diff --git a/job.c b/job.c index 0aee7b05..e9799956 100644 --- a/job.c +++ b/job.c @@ -150,7 +150,7 @@ job_free(struct job *job) /* Called when output buffer falls below low watermark (default is 0). */ void -job_write_callback(unused struct bufferevent *bufev, void *data) +job_write_callback(__unused struct bufferevent *bufev, void *data) { struct job *job = data; size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); @@ -166,7 +166,8 @@ job_write_callback(unused struct bufferevent *bufev, void *data) /* Job buffer error callback. */ void -job_callback(unused struct bufferevent *bufev, unused short events, void *data) +job_callback(__unused struct bufferevent *bufev, __unused short events, + void *data) { struct job *job = data; diff --git a/log.c b/log.c index cf65c9e3..6c7be8b2 100644 --- a/log.c +++ b/log.c @@ -33,7 +33,7 @@ void log_vwrite(const char *, va_list); /* Log callback for libevent. */ void -log_event_cb(unused int severity, const char *msg) +log_event_cb(__unused int severity, const char *msg) { log_debug("%s", msg); } diff --git a/names.c b/names.c index 0e806ca0..7d956db3 100644 --- a/names.c +++ b/names.c @@ -29,7 +29,7 @@ void name_time_callback(int, short, void *); int name_time_expired(struct window *, struct timeval *); void -name_time_callback(unused int fd, unused short events, void *arg) +name_time_callback(__unused int fd, __unused short events, void *arg) { struct window *w = arg; diff --git a/proc.c b/proc.c index 6b84fc00..31fd136b 100644 --- a/proc.c +++ b/proc.c @@ -53,7 +53,7 @@ static int peer_check_version(struct tmuxpeer *, struct imsg *); static void proc_update_event(struct tmuxpeer *); static void -proc_event_cb(unused int fd, short events, void *arg) +proc_event_cb(__unused int fd, short events, void *arg) { struct tmuxpeer *peer = arg; ssize_t n; @@ -101,7 +101,7 @@ proc_event_cb(unused int fd, short events, void *arg) } static void -proc_signal_cb(int signo, unused short events, void *arg) +proc_signal_cb(int signo, __unused short events, void *arg) { struct tmuxproc *tp = arg; diff --git a/screen-write.c b/screen-write.c index a887b0f1..53067efe 100644 --- a/screen-write.c +++ b/screen-write.c @@ -42,7 +42,7 @@ screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, /* Finish writing. */ void -screen_write_stop(unused struct screen_write_ctx *ctx) +screen_write_stop(__unused struct screen_write_ctx *ctx) { } diff --git a/server-client.c b/server-client.c index 645492d1..04929dec 100644 --- a/server-client.c +++ b/server-client.c @@ -246,7 +246,7 @@ server_client_unref(struct client *c) /* Free dead client. */ void -server_client_free(unused int fd, unused short events, void *arg) +server_client_free(__unused int fd, __unused short events, void *arg) { struct client *c = arg; @@ -818,7 +818,7 @@ server_client_reset_state(struct client *c) /* Repeat time callback. */ void -server_client_repeat_timer(unused int fd, unused short events, void *data) +server_client_repeat_timer(__unused int fd, __unused short events, void *data) { struct client *c = data; @@ -1214,7 +1214,7 @@ server_client_dispatch_shell(struct client *c) /* Event callback to push more stdout data if any left. */ static void -server_client_stdout_cb(unused int fd, unused short events, void *arg) +server_client_stdout_cb(__unused int fd, __unused short events, void *arg) { struct client *c = arg; @@ -1255,7 +1255,7 @@ server_client_push_stdout(struct client *c) /* Event callback to push more stderr data if any left. */ static void -server_client_stderr_cb(unused int fd, unused short events, void *arg) +server_client_stderr_cb(__unused int fd, __unused short events, void *arg) { struct client *c = arg; diff --git a/server-fn.c b/server-fn.c index bded7246..3e4b6116 100644 --- a/server-fn.c +++ b/server-fn.c @@ -443,7 +443,7 @@ server_clear_identify(struct client *c) } void -server_callback_identify(unused int fd, unused short events, void *data) +server_callback_identify(__unused int fd, __unused short events, void *data) { struct client *c = data; diff --git a/server.c b/server.c index 2808c0cc..55a53a40 100644 --- a/server.c +++ b/server.c @@ -298,7 +298,7 @@ server_update_socket(void) /* Callback for server socket. */ void -server_accept(int fd, short events, unused void *data) +server_accept(int fd, short events, __unused void *data) { struct sockaddr_storage sa; socklen_t slen = sizeof sa; diff --git a/session.c b/session.c index deb29d18..c5721c9a 100644 --- a/session.c +++ b/session.c @@ -181,7 +181,7 @@ session_unref(struct session *s) /* Free session. */ void -session_free(unused int fd, unused short events, void *arg) +session_free(__unused int fd, __unused short events, void *arg) { struct session *s = arg; @@ -236,7 +236,7 @@ session_check_name(const char *name) /* Lock session if it has timed out. */ void -session_lock_timer(unused int fd, unused short events, void *arg) +session_lock_timer(__unused int fd, __unused short events, void *arg) { struct session *s = arg; diff --git a/status.c b/status.c index 326e5ad8..e09ac592 100644 --- a/status.c +++ b/status.c @@ -145,7 +145,7 @@ status_prompt_save_history(void) /* Status timer callback. */ void -status_timer_callback(unused int fd, unused short events, void *arg) +status_timer_callback(__unused int fd, __unused short events, void *arg) { struct client *c = arg; struct session *s = c->session; @@ -604,7 +604,7 @@ status_message_clear(struct client *c) /* Clear status line message after timer expires. */ void -status_message_callback(unused int fd, unused short event, void *data) +status_message_callback(__unused int fd, __unused short event, void *data) { struct client *c = data; diff --git a/tmux.h b/tmux.h index 421f2369..b6cdcd80 100644 --- a/tmux.h +++ b/tmux.h @@ -69,9 +69,6 @@ struct tmuxproc; #define READ_BACKOFF 512 #define READ_TIME 100 -/* Definition to shut gcc up about unused arguments. */ -#define unused __attribute__ ((unused)) - /* Attribute to make gcc check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) diff --git a/tty-keys.c b/tty-keys.c index 52dae4f7..706dcc30 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -635,7 +635,7 @@ discard_key: /* Key timer callback. */ void -tty_keys_callback(unused int fd, unused short events, void *data) +tty_keys_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; diff --git a/tty.c b/tty.c index e67ebbb6..13ca7f8e 100644 --- a/tty.c +++ b/tty.c @@ -172,7 +172,7 @@ tty_open(struct tty *tty, char **cause) } void -tty_read_callback(unused struct bufferevent *bufev, void *data) +tty_read_callback(__unused struct bufferevent *bufev, void *data) { struct tty *tty = data; @@ -181,8 +181,8 @@ tty_read_callback(unused struct bufferevent *bufev, void *data) } void -tty_error_callback( - unused struct bufferevent *bufev, unused short what, unused void *data) +tty_error_callback(__unused struct bufferevent *bufev, __unused short what, + __unused void *data) { } @@ -591,7 +591,7 @@ tty_repeat_space(struct tty *tty, u_int n) * pane. */ int -tty_large_region(unused struct tty *tty, const struct tty_ctx *ctx) +tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; diff --git a/window-choose.c b/window-choose.c index 5f406c78..cab3e5d8 100644 --- a/window-choose.c +++ b/window-choose.c @@ -509,8 +509,8 @@ window_choose_get_item(struct window_pane *wp, key_code key, } void -window_choose_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, key_code key, struct mouse_event *m) +window_choose_key(struct window_pane *wp, __unused struct client *c, + __unused struct session *sess, key_code key, struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; diff --git a/window-clock.c b/window-clock.c index 51f97250..e8451f22 100644 --- a/window-clock.c +++ b/window-clock.c @@ -120,7 +120,7 @@ const char window_clock_table[14][5][5] = { }; void -window_clock_timer_callback(unused int fd, unused short events, void *arg) +window_clock_timer_callback(__unused int fd, __unused short events, void *arg) { struct window_pane *wp = arg; struct window_clock_mode_data *data = wp->modedata; @@ -185,9 +185,9 @@ window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) } void -window_clock_key(struct window_pane *wp, unused struct client *c, - unused struct session *sess, unused key_code key, - unused struct mouse_event *m) +window_clock_key(struct window_pane *wp, __unused struct client *c, + __unused struct session *sess, __unused key_code key, + __unused struct mouse_event *m) { window_pane_reset_mode(wp); } diff --git a/window-copy.c b/window-copy.c index a28cdecc..37025302 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2232,7 +2232,7 @@ window_copy_rectangle_toggle(struct window_pane *wp) } void -window_copy_start_drag(struct client *c, unused struct mouse_event *m) +window_copy_start_drag(struct client *c, struct mouse_event *m) { struct window_pane *wp; u_int x, y; @@ -2253,7 +2253,7 @@ window_copy_start_drag(struct client *c, unused struct mouse_event *m) } void -window_copy_drag_update(unused struct client *c, struct mouse_event *m) +window_copy_drag_update(__unused struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_copy_mode_data *data; @@ -2274,7 +2274,7 @@ window_copy_drag_update(unused struct client *c, struct mouse_event *m) } void -window_copy_drag_release(unused struct client *c, struct mouse_event *m) +window_copy_drag_release(__unused struct client *c, struct mouse_event *m) { struct window_pane *wp; diff --git a/window.c b/window.c index c5f9f176..8ee517a1 100644 --- a/window.c +++ b/window.c @@ -916,13 +916,13 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, } void -window_pane_timer_callback(unused int fd, unused short events, void *data) +window_pane_timer_callback(__unused int fd, __unused short events, void *data) { window_pane_read_callback(NULL, data); } void -window_pane_read_callback(unused struct bufferevent *bufev, void *data) +window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; struct evbuffer *evb = wp->event->input; @@ -968,8 +968,8 @@ start_timer: } void -window_pane_error_callback(unused struct bufferevent *bufev, unused short what, - void *data) +window_pane_error_callback(__unused struct bufferevent *bufev, + __unused short what, void *data) { struct window_pane *wp = data; From 8b4b3ff4fcc73578f66697b158b56fdc28895368 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 14:37:08 +0000 Subject: [PATCH 427/703] Add __unused, will be needed shortly. --- compat.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compat.h b/compat.h index 7d9b14fd..6812e9a5 100644 --- a/compat.h +++ b/compat.h @@ -21,6 +21,9 @@ #define __attribute__(a) #endif +#ifndef __unused +#define __unused __attribute__ ((__unused__)) +#endif #ifndef __dead #define __dead __attribute__ ((__noreturn__)) #endif From c15487318b08a88aebc9cf583cbdc591b1bc34c8 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 18 Nov 2015 16:45:44 +0000 Subject: [PATCH 428/703] unused -> __unused. --- osdep-aix.c | 2 +- osdep-cygwin.c | 2 +- osdep-darwin.c | 4 ++-- osdep-hpux.c | 4 ++-- osdep-linux.c | 2 +- osdep-unknown.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osdep-aix.c b/osdep-aix.c index 0a3d12e4..ef7d6c7e 100644 --- a/osdep-aix.c +++ b/osdep-aix.c @@ -26,7 +26,7 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, char *tty) +osdep_get_name(__unused int fd, char *tty) { struct psinfo p; char *path; diff --git a/osdep-cygwin.c b/osdep-cygwin.c index 91bc5fc9..9a3ea408 100644 --- a/osdep-cygwin.c +++ b/osdep-cygwin.c @@ -27,7 +27,7 @@ #include "tmux.h" char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { FILE *f; char *path, *buf; diff --git a/osdep-darwin.c b/osdep-darwin.c index 7be70375..40b18951 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -28,10 +28,10 @@ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); -#define unused __attribute__ ((unused)) +#define __unused __attribute__ ((__unused__)) char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { struct proc_bsdinfo bsdinfo; pid_t pgrp; diff --git a/osdep-hpux.c b/osdep-hpux.c index 4baa6d49..a6d75f94 100644 --- a/osdep-hpux.c +++ b/osdep-hpux.c @@ -23,13 +23,13 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } char * -osdep_get_cwd(unused int fd) +osdep_get_cwd(__unused int fd) { return (NULL); } diff --git a/osdep-linux.c b/osdep-linux.c index ad4c11cd..00501efb 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -28,7 +28,7 @@ #include "tmux.h" char * -osdep_get_name(int fd, unused char *tty) +osdep_get_name(int fd, __unused char *tty) { FILE *f; char *path, *buf; diff --git a/osdep-unknown.c b/osdep-unknown.c index 4de1ee1e..9465db97 100644 --- a/osdep-unknown.c +++ b/osdep-unknown.c @@ -23,7 +23,7 @@ #include "tmux.h" char * -osdep_get_name(unused int fd, unused char *tty) +osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } From 82760a9960850ad33db050f3bec1ab84c55c2e30 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 18 Nov 2015 16:49:13 +0000 Subject: [PATCH 429/703] Use format_expand_time for display-message. --- cmd-display-message.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index ee9eafe9..1f9f40fa 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -54,9 +54,6 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) const char *template; char *msg; struct format_tree *ft; - char out[BUFSIZ]; - time_t t; - size_t len; if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); @@ -94,11 +91,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) ft = format_create(); format_defaults(ft, c, s, wl, wp); - t = time(NULL); - len = strftime(out, sizeof out, template, localtime(&t)); - out[len] = '\0'; - - msg = format_expand(ft, out); + msg = format_expand_time(ft, template, time(NULL)); if (args_has(self->args, 'p')) cmdq_print(cmdq, "%s", msg); else From 98967c5ec97feef99f8278df9c3b993bbff9c0d5 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Nov 2015 14:55:25 +0000 Subject: [PATCH 430/703] The activity flag could already be set, so queue the callback always (if not already queued) rather than only if the flag is being added. Fixes a problem reported by tim@ --- alerts.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/alerts.c b/alerts.c index 32e0d496..e08b183d 100644 --- a/alerts.c +++ b/alerts.c @@ -132,15 +132,15 @@ alerts_queue(struct window *w, int flags) if (!event_initialized(&w->alerts_timer)) evtimer_set(&w->alerts_timer, alerts_timer, w); - if (w->flags & flags) - return; - w->flags |= flags; - log_debug("@%u alerts flags added %#x", w->id, flags); + if (!alerts_fired) { + w->flags |= flags; + log_debug("@%u alerts flags added %#x", w->id, flags); - if (!alerts_fired && alerts_enabled(w, flags)) { - log_debug("alerts check queued (by @%u)", w->id); - event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); - alerts_fired = 1; + if (alerts_enabled(w, flags)) { + log_debug("alerts check queued (by @%u)", w->id); + event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); + alerts_fired = 1; + } } } From 374e273df5f66e90e06f60617495446facfbd4f3 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 19 Nov 2015 22:46:46 +0000 Subject: [PATCH 431/703] Only assume pasting with at least two characters, reduces problems for people who can type ^B c very fast, or who are using tmux inside something else that buffers. --- server-client.c | 12 ++++++++++-- tmux.h | 7 ++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/server-client.c b/server-client.c index 04929dec..64426a70 100644 --- a/server-client.c +++ b/server-client.c @@ -494,8 +494,16 @@ server_client_assume_paste(struct session *s) return (0); timersub(&s->activity_time, &s->last_activity_time, &tv); - if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) - return (1); + if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) { + log_debug("session %s pasting (flag %d)", s->name, + !!(s->flags & SESSION_PASTING)); + if (s->flags & SESSION_PASTING) + return (1); + s->flags |= SESSION_PASTING; + return (0); + } + log_debug("session %s not pasting", s->name); + s->flags &= ~SESSION_PASTING; return (0); } diff --git a/tmux.h b/tmux.h index b6cdcd80..d58ee8c9 100644 --- a/tmux.h +++ b/tmux.h @@ -33,6 +33,8 @@ #include #include +#include "xmalloc.h" + extern char *__progname; extern char **environ; @@ -45,8 +47,6 @@ struct session; struct tmuxpeer; struct tmuxproc; -#include "xmalloc.h" - /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" @@ -1014,6 +1014,7 @@ struct session { struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ +#define SESSION_PASTING 0x2 int flags; u_int attached; @@ -1147,7 +1148,7 @@ struct tty { struct tty_key *key_tree; }; -/* TTY command context and function pointer. */ +/* TTY command context. */ struct tty_ctx { struct window_pane *wp; From fce56c56efc57e80cc3ed755c192cf88822ebf0e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Nov 2015 12:01:19 +0000 Subject: [PATCH 432/703] Instead of separate tables for different types of options, give each option a scope type (server, session, window) in one table. --- cmd-set-option.c | 14 ++-- cmd-show-options.c | 45 ++++++------ options-table.c | 175 +++++++++++++++++++++++++++++++++++---------- status.c | 14 +--- tmux.c | 6 +- tmux.h | 32 +++++---- 6 files changed, 190 insertions(+), 96 deletions(-) diff --git a/cmd-set-option.c b/cmd-set-option.c index 894f0c43..7de91aa2 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -84,7 +84,7 @@ enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - const struct options_table_entry *table, *oe; + const struct options_table_entry *oe; struct session *s; struct winlink *wl; struct client *c; @@ -108,8 +108,8 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (cmd_set_option_user(self, cmdq, optstr, valstr)); /* Find the option entry, try each table. */ - table = oe = NULL; - if (options_table_find(optstr, &table, &oe) != 0) { + oe = NULL; + if (options_table_find(optstr, &oe) != 0) { if (!args_has(args, 'q')) { cmdq_error(cmdq, "ambiguous option: %s", optstr); return (CMD_RETURN_ERROR); @@ -124,10 +124,10 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - /* Work out the tree from the table. */ - if (table == server_options_table) + /* Work out the tree from the scope of the option. */ + if (oe->scope == OPTIONS_TABLE_SERVER) oo = global_options; - else if (table == window_options_table) { + else if (oe->scope == OPTIONS_TABLE_WINDOW) { if (args_has(self->args, 'g')) oo = global_w_options; else { @@ -141,7 +141,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) } oo = wl->window->options; } - } else if (table == session_options_table) { + } else if (oe->scope == OPTIONS_TABLE_SESSION) { if (args_has(self->args, 'g')) oo = global_s_options; else { diff --git a/cmd-show-options.c b/cmd-show-options.c index e99d665f..fd96a0e2 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -32,7 +32,7 @@ enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_show_options_one(struct cmd *, struct cmd_q *, struct options *, int); enum cmd_retval cmd_show_options_all(struct cmd *, struct cmd_q *, - const struct options_table_entry *, struct options *); + struct options *, enum options_table_scope); const struct cmd_entry cmd_show_options_entry = { "show-options", "show", @@ -53,19 +53,19 @@ const struct cmd_entry cmd_show_window_options_entry = { enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct session *s; - struct winlink *wl; - const struct options_table_entry *table; - struct options *oo; - int quiet; + struct args *args = self->args; + struct session *s; + struct winlink *wl; + struct options *oo; + int quiet; + enum options_table_scope scope; if (args_has(self->args, 's')) { oo = global_options; - table = server_options_table; + scope = OPTIONS_TABLE_SERVER; } else if (args_has(self->args, 'w') || self->entry == &cmd_show_window_options_entry) { - table = window_options_table; + scope = OPTIONS_TABLE_WINDOW; if (args_has(self->args, 'g')) oo = global_w_options; else { @@ -75,7 +75,7 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) oo = wl->window->options; } } else { - table = session_options_table; + scope = OPTIONS_TABLE_SESSION; if (args_has(self->args, 'g')) oo = global_s_options; else { @@ -88,7 +88,7 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) quiet = args_has(self->args, 'q'); if (args->argc == 0) - return (cmd_show_options_all(self, cmdq, table, oo)); + return (cmd_show_options_all(self, cmdq, oo, scope)); else return (cmd_show_options_one(self, cmdq, oo, quiet)); } @@ -99,7 +99,7 @@ cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, { struct args *args = self->args; const char *name = args->argv[0]; - const struct options_table_entry *table, *oe; + const struct options_table_entry *oe; struct options_entry *o; const char *optval; @@ -118,14 +118,14 @@ retry: return (CMD_RETURN_NORMAL); } - table = oe = NULL; - if (options_table_find(name, &table, &oe) != 0) { + oe = NULL; + if (options_table_find(name, &oe) != 0) { cmdq_error(cmdq, "ambiguous option: %s", name); return (CMD_RETURN_ERROR); } if (oe == NULL) { if (quiet) - return (CMD_RETURN_NORMAL); + return (CMD_RETURN_NORMAL); cmdq_error(cmdq, "unknown option: %s", name); return (CMD_RETURN_ERROR); } @@ -144,12 +144,13 @@ retry: } enum cmd_retval -cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, - const struct options_table_entry *table, struct options *oo) +cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, struct options *oo, + enum options_table_scope scope) { const struct options_table_entry *oe; struct options_entry *o; const char *optval; + int vflag; o = options_first(oo); while (o != NULL) { @@ -162,14 +163,14 @@ cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, o = options_next(o); } - for (oe = table; oe->name != NULL; oe++) { - if (oe->style != NULL) + vflag = args_has(self->args, 'v'); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->style != NULL || oe->scope != scope) continue; if ((o = options_find1(oo, oe->name)) == NULL) continue; - optval = options_table_print_entry(oe, o, - args_has(self->args, 'v')); - if (args_has(self->args, 'v')) + optval = options_table_print_entry(oe, o, vflag); + if (vflag) cmdq_print(cmdq, "%s", optval); else cmdq_print(cmdq, "%s %s", oe->name, optval); diff --git a/options-table.c b/options-table.c index cf4e2d14..a0e65ff4 100644 --- a/options-table.c +++ b/options-table.c @@ -53,9 +53,10 @@ const char *options_table_bell_action_list[] = { }; /* Server options. */ -const struct options_table_entry server_options_table[] = { +const struct options_table_entry options_table[] = { { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = INT_MAX, .default_num = 20 @@ -63,11 +64,13 @@ const struct options_table_entry server_options_table[] = { { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "screen" }, { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, .default_num = 500 @@ -75,21 +78,25 @@ const struct options_table_entry server_options_table[] = { { .name = "exit-unattached", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "history-file", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, .default_num = 100 @@ -97,28 +104,27 @@ const struct options_table_entry server_options_table[] = { { .name = "quiet", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "set-clipboard", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SERVER, .default_num = 1 }, { .name = "terminal-overrides", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" }, - { .name = NULL } -}; - -/* Session options. */ -const struct options_table_entry session_options_table[] = { { .name = "assume-paste-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 1, @@ -126,6 +132,7 @@ const struct options_table_entry session_options_table[] = { { .name = "base-index", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -133,47 +140,56 @@ const struct options_table_entry session_options_table[] = { { .name = "bell-action", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, .default_num = BELL_ANY }, { .name = "bell-on-alert", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "default-command", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "" }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = _PATH_BSHELL }, { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "detach-on-destroy", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-active-colour", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-colour", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 4 }, { .name = "display-panes-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, .default_num = 1000 @@ -181,6 +197,7 @@ const struct options_table_entry session_options_table[] = { { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, .default_num = 750 @@ -188,6 +205,7 @@ const struct options_table_entry session_options_table[] = { { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 2000 @@ -195,6 +213,7 @@ const struct options_table_entry session_options_table[] = { { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -202,77 +221,91 @@ const struct options_table_entry session_options_table[] = { { .name = "lock-command", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "lock -np" }, { .name = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-style" }, { .name = "message-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 3, .style = "message-style" }, { .name = "message-command-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-command-style" }, { .name = "message-command-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-command-style" }, { .name = "message-command-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 3, .style = "message-command-style" }, { .name = "message-command-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=black,fg=yellow" }, { .name = "message-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-style" }, { .name = "message-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=yellow,fg=black" }, { .name = "mouse", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "prefix", .type = OPTIONS_TABLE_KEY, + .scope = OPTIONS_TABLE_SESSION, .default_num = '\002', }, { .name = "prefix2", .type = OPTIONS_TABLE_KEY, + .scope = OPTIONS_TABLE_SESSION, .default_num = KEYC_NONE, }, { .name = "renumber-windows", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "repeat-time", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 500 @@ -280,44 +313,52 @@ const struct options_table_entry session_options_table[] = { { .name = "set-remain-on-exit", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" }, { .name = "status", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-style" }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 2, .style = "status-style" }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-style" }, { .name = "status-interval", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 15 @@ -325,41 +366,48 @@ const struct options_table_entry session_options_table[] = { { .name = "status-justify", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_justify_list, .default_num = 0 }, { .name = "status-keys", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_keys_list, .default_num = MODEKEY_EMACS }, { .name = "status-left", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "[#S] " }, { .name = "status-left-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-left-style" }, { .name = "status-left-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-left-style" }, { .name = "status-left-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-left-style" }, { .name = "status-left-length", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 10 @@ -367,40 +415,47 @@ const struct options_table_entry session_options_table[] = { { .name = "status-left-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_position_list, .default_num = 1 }, { .name = "status-right", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = " \"#{=21:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-right-style" }, { .name = "status-right-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-right-style" }, { .name = "status-right-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-right-style" }, { .name = "status-right-length", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 40 @@ -408,16 +463,19 @@ const struct options_table_entry session_options_table[] = { { .name = "status-right-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=green,fg=black" }, { .name = "update-environment", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " "SSH_CONNECTION WINDOWID XAUTHORITY" @@ -425,68 +483,75 @@ const struct options_table_entry session_options_table[] = { { .name = "visual-activity", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "visual-bell", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "visual-silence", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "word-separators", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, .default_str = " -_@" }, - { .name = NULL } -}; - -/* Window options. */ -const struct options_table_entry window_options_table[] = { { .name = "aggressive-resize", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "automatic-rename", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "automatic-rename-format", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" "#{?pane_dead,[dead],}" }, { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 4 }, { .name = "clock-mode-style", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_clock_mode_style_list, .default_num = 1 }, { .name = "force-height", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -494,6 +559,7 @@ const struct options_table_entry window_options_table[] = { { .name = "force-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -501,6 +567,7 @@ const struct options_table_entry window_options_table[] = { { .name = "main-pane-height", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 24 @@ -508,6 +575,7 @@ const struct options_table_entry window_options_table[] = { { .name = "main-pane-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 80 @@ -515,40 +583,47 @@ const struct options_table_entry window_options_table[] = { { .name = "mode-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "mode-style" }, { .name = "mode-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 3, .style = "mode-style" }, { .name = "mode-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "mode-style" }, { .name = "mode-keys", .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_mode_keys_list, .default_num = MODEKEY_EMACS }, { .name = "mode-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=yellow,fg=black" }, { .name = "monitor-activity", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "monitor-silence", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -556,6 +631,7 @@ const struct options_table_entry window_options_table[] = { { .name = "other-pane-height", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -563,6 +639,7 @@ const struct options_table_entry window_options_table[] = { { .name = "other-pane-width", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 @@ -570,23 +647,27 @@ const struct options_table_entry window_options_table[] = { { .name = "pane-active-border-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-active-border-style" }, { .name = "pane-active-border-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 2, .style = "pane-active-border-style" }, { .name = "pane-active-border-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "fg=green" }, { .name = "pane-base-index", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = USHRT_MAX, .default_num = 0 @@ -594,178 +675,210 @@ const struct options_table_entry window_options_table[] = { { .name = "pane-border-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-border-style" }, { .name = "pane-border-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-border-style" }, { .name = "pane-border-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "window-active-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-activity-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = GRID_ATTR_REVERSE, .style = "window-status-activity-style" }, { .name = "window-status-activity-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-activity-style" }, { .name = "window-status-activity-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-activity-style" }, { .name = "window-status-activity-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse" }, { .name = "window-status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-style" }, { .name = "window-status-bell-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = GRID_ATTR_REVERSE, .style = "window-status-bell-style" }, { .name = "window-status-bell-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-bell-style" }, { .name = "window-status-bell-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-bell-style" }, { .name = "window-status-bell-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse" }, { .name = "window-status-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-style" }, { .name = "window-status-current-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-current-style" }, { .name = "window-status-current-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-current-style" }, { .name = "window-status-current-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-current-style" }, { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-current-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-style" }, { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-last-attr", .type = OPTIONS_TABLE_ATTRIBUTES, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-last-style" }, { .name = "window-status-last-bg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-last-style" }, { .name = "window-status-last-fg", .type = OPTIONS_TABLE_COLOUR, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-last-style" }, { .name = "window-status-last-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-separator", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, .default_str = " " }, { .name = "window-status-style", .type = OPTIONS_TABLE_STYLE, + .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "wrap-search", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "xterm-keys", .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, @@ -774,12 +887,13 @@ const struct options_table_entry window_options_table[] = { /* Populate an options tree from a table. */ void -options_table_populate_tree( - const struct options_table_entry *table, struct options *oo) +options_table_populate_tree(enum options_table_scope scope, struct options *oo) { const struct options_table_entry *oe; - for (oe = table; oe->name != NULL; oe++) { + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope != scope) + continue; switch (oe->type) { case OPTIONS_TABLE_STRING: options_set_string(oo, oe->name, "%s", oe->default_str); @@ -845,33 +959,22 @@ options_table_print_entry(const struct options_table_entry *oe, /* Find an option. */ int -options_table_find( - const char *optstr, const struct options_table_entry **table, - const struct options_table_entry **oe) +options_table_find(const char *optstr, const struct options_table_entry **oe) { - static const struct options_table_entry *tables[] = { - server_options_table, - window_options_table, - session_options_table - }; const struct options_table_entry *oe_loop; - u_int i; - for (i = 0; i < nitems(tables); i++) { - for (oe_loop = tables[i]; oe_loop->name != NULL; oe_loop++) { - if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) - continue; + for (oe_loop = options_table; oe_loop->name != NULL; oe_loop++) { + if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) + continue; - /* If already found, ambiguous. */ - if (*oe != NULL) - return (-1); - *oe = oe_loop; - *table = tables[i]; + /* If already found, ambiguous. */ + if (*oe != NULL) + return (-1); + *oe = oe_loop; - /* Bail now if an exact match. */ - if (strcmp((*oe)->name, optstr) == 0) - break; - } + /* Bail now if an exact match. */ + if (strcmp(oe_loop->name, optstr) == 0) + break; } return (0); } diff --git a/status.c b/status.c index e09ac592..cbba2ead 100644 --- a/status.c +++ b/status.c @@ -1205,19 +1205,7 @@ status_prompt_complete_list(u_int *size, const char *s) list[(*size)++] = (*cmdent)->name; } } - for (oe = server_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = oe->name; - } - } - for (oe = session_options_table; oe->name != NULL; oe++) { - if (strncmp(oe->name, s, strlen(s)) == 0) { - list = xreallocarray(list, (*size) + 1, sizeof *list); - list[(*size)++] = oe->name; - } - } - for (oe = window_options_table; oe->name != NULL; oe++) { + for (oe = options_table; oe->name != NULL; oe++) { if (strncmp(oe->name, s, strlen(s)) == 0) { list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = oe->name; diff --git a/tmux.c b/tmux.c index 5429a7cb..9baa8183 100644 --- a/tmux.c +++ b/tmux.c @@ -288,14 +288,14 @@ main(int argc, char **argv) environ_set(global_environ, "PWD", tmp); global_options = options_create(NULL); - options_table_populate_tree(server_options_table, global_options); + options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); global_s_options = options_create(NULL); - options_table_populate_tree(session_options_table, global_s_options); + options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); options_set_string(global_s_options, "default-shell", "%s", getshell()); global_w_options = options_create(NULL); - options_table_populate_tree(window_options_table, global_w_options); + options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { diff --git a/tmux.h b/tmux.h index d58ee8c9..a112e7df 100644 --- a/tmux.h +++ b/tmux.h @@ -1393,19 +1393,25 @@ enum options_table_type { OPTIONS_TABLE_CHOICE, OPTIONS_TABLE_STYLE }; +enum options_table_scope { + OPTIONS_TABLE_SERVER, + OPTIONS_TABLE_SESSION, + OPTIONS_TABLE_WINDOW, +}; struct options_table_entry { - const char *name; - enum options_table_type type; + const char *name; + enum options_table_type type; + enum options_table_scope scope; - u_int minimum; - u_int maximum; - const char **choices; + u_int minimum; + u_int maximum; + const char **choices; - const char *default_str; - long long default_num; + const char *default_str; + long long default_num; - const char *style; + const char *style; }; /* Common command usages. */ @@ -1543,15 +1549,11 @@ struct options_entry *options_set_style(struct options *, const char *, struct grid_cell *options_get_style(struct options *, const char *); /* options-table.c */ -extern const struct options_table_entry server_options_table[]; -extern const struct options_table_entry session_options_table[]; -extern const struct options_table_entry window_options_table[]; -void options_table_populate_tree(const struct options_table_entry *, - struct options *); +extern const struct options_table_entry options_table[]; +void options_table_populate_tree(enum options_table_scope, struct options *); const char *options_table_print_entry(const struct options_table_entry *, struct options_entry *, int); -int options_table_find(const char *, const struct options_table_entry **, - const struct options_table_entry **); +int options_table_find(const char *, const struct options_table_entry **); /* job.c */ extern struct joblist all_jobs; From 40fefe2cbc92012d4ec5261ce71a57a112c5ec12 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Nov 2015 16:33:46 +0000 Subject: [PATCH 433/703] The alerts callback should be fired for bells even if bell-action is none because it also affects the status line bell indicator (and bell-action does not). Fixes a problem reported by tim@. --- alerts.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/alerts.c b/alerts.c index e08b183d..55e7044d 100644 --- a/alerts.c +++ b/alerts.c @@ -77,8 +77,8 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg) int alerts_enabled(struct window *w, int flags) { - struct session *s; - + if (flags & WINDOW_BELL) + return (1); if (flags & WINDOW_ACTIVITY) { if (options_get_number(w->options, "monitor-activity")) return (1); @@ -87,14 +87,6 @@ alerts_enabled(struct window *w, int flags) if (options_get_number(w->options, "monitor-silence") != 0) return (1); } - if (~flags & WINDOW_BELL) - return (0); - RB_FOREACH(s, sessions, &sessions) { - if (!session_has(s, w)) - continue; - if (options_get_number(s->options, "bell-action") != BELL_NONE) - return (1); - } return (0); } From 933929cd622478bb43afe590670613da2e9ff359 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 20 Nov 2015 22:02:54 +0000 Subject: [PATCH 434/703] Memory leaks and an uninitialized part of utf8_data, from Patrick Palka. --- cmd-if-shell.c | 1 + cmd-new-window.c | 2 +- utf8.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index a9c84261..47f259e7 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -97,6 +97,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) cmd = args->argv[1]; else if (args->argc == 3) cmd = args->argv[2]; + free(shellcmd); if (cmd == NULL) return (CMD_RETURN_NORMAL); if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { diff --git a/cmd-new-window.c b/cmd-new-window.c index fb89e24f..24204746 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -97,7 +97,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) ft = format_create(); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); - cwd = format_expand(ft, args_get(args, 'c')); + cwd = to_free = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; diff --git a/utf8.c b/utf8.c index b391c5ec..2210675a 100644 --- a/utf8.c +++ b/utf8.c @@ -355,6 +355,7 @@ utf8_set(struct utf8_data *ud, u_char ch) u_int i; *ud->data = ch; + ud->have = 1; ud->size = 1; ud->width = 1; From 2c482939fdd92f1bd187cdcc661890dfa4e45512 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 21 Nov 2015 08:03:18 +0000 Subject: [PATCH 435/703] Move tmux.h below system includes. --- compat/closefrom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compat/closefrom.c b/compat/closefrom.c index 591769dd..8c650aa5 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -14,8 +14,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "tmux.h" - #ifndef HAVE_CLOSEFROM #include @@ -47,6 +45,8 @@ # endif #endif +#include "tmux.h" + #ifndef OPEN_MAX # define OPEN_MAX 256 #endif From 4fcc02ee9dde0f11f6a6db77b2ba6271421cea78 Mon Sep 17 00:00:00 2001 From: tim Date: Sun, 22 Nov 2015 18:28:01 +0000 Subject: [PATCH 436/703] If display-time is set to 0, show status messages until a key is pressed; OK nicm@ --- options-table.c | 2 +- status.c | 14 ++++++++------ tmux.1 | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/options-table.c b/options-table.c index a0e65ff4..dc4922ba 100644 --- a/options-table.c +++ b/options-table.c @@ -198,7 +198,7 @@ const struct options_table_entry options_table[] = { { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, - .minimum = 1, + .minimum = 0, .maximum = INT_MAX, .default_num = 750 }, diff --git a/status.c b/status.c index cbba2ead..d05376c0 100644 --- a/status.c +++ b/status.c @@ -574,13 +574,15 @@ status_message_set(struct client *c, const char *fmt, ...) } delay = options_get_number(c->session->options, "display-time"); - tv.tv_sec = delay / 1000; - tv.tv_usec = (delay % 1000) * 1000L; + if (delay > 0) { + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; - if (event_initialized(&c->message_timer)) - evtimer_del(&c->message_timer); - evtimer_set(&c->message_timer, status_message_callback, c); - evtimer_add(&c->message_timer, &tv); + if (event_initialized(&c->message_timer)) + evtimer_del(&c->message_timer); + evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); + } c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_STATUS; diff --git a/tmux.1 b/tmux.1 index a22bb3f3..3fd383d6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2557,6 +2557,7 @@ command appear. .It Ic display-time Ar time Set the amount of time for which status line messages and other on-screen indicators are displayed. +If set to 0, messages and indicators are displayed until a key is pressed. .Ar time is in milliseconds. .It Ic history-limit Ar lines From 01a2ddf3f8d22c58bd8423be69ab0e7843d71652 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 22 Nov 2015 19:41:19 +0000 Subject: [PATCH 437/703] Add getpw to pledge, makes tmux work in YP environments, discovered by matthieu, ok deraadt --- server.c | 4 ++-- tmux.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server.c b/server.c index 55a53a40..e17b9356 100644 --- a/server.c +++ b/server.c @@ -175,8 +175,8 @@ server_start(struct event_base *base, int lockfd, char *lockfile) if (debug_level > 3) tty_create_log(); - if (pledge("stdio rpath wpath cpath fattr unix recvfd proc exec tty " - "ps", NULL) != 0) + if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " + "tty ps", NULL) != 0) fatal("pledge failed"); RB_INIT(&windows); diff --git a/tmux.c b/tmux.c index 9baa8183..aa827178 100644 --- a/tmux.c +++ b/tmux.c @@ -255,8 +255,8 @@ main(int argc, char **argv) if (shell_cmd != NULL && argc != 0) usage(); - if (pledge("stdio rpath wpath cpath flock fattr unix sendfd recvfd " - "proc exec tty ps", NULL) != 0) + if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " + "recvfd proc exec tty ps", NULL) != 0) err(1, "pledge"); /* From 28e72ae34d43dda28ca0e6dc652eaa1179c351c7 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 22 Nov 2015 19:42:57 +0000 Subject: [PATCH 438/703] Don't leak extddata, memset after freeing it, not before. From Patrick Palka. --- grid.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/grid.c b/grid.c index 36cde074..579eb966 100644 --- a/grid.c +++ b/grid.c @@ -368,11 +368,8 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny) for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; free(gl->celldata); - memset(gl, 0, sizeof *gl); - free(gl->extddata); - gl->extddata = NULL; - gl->extdsize = 0; + memset(gl, 0, sizeof *gl); } } From 32e510bd70eedbeec8590b9bf786b11430ddaac3 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Nov 2015 20:53:09 +0000 Subject: [PATCH 439/703] Remove support for the UTF-8 mouse extension. This was a briefly used, poor idea that was fairly quickly replaced by SGR mouse input (which is now widespread). It is impossible to tell the difference between UTF-8 and non-UTF-8 mouse input; since the mouse-utf8 option was removed tmux has not handled it correctly in any case; and it is ridiculous to have three different forms of mouse input. --- input-keys.c | 6 ------ input.c | 6 ------ screen-write.c | 2 +- server-client.c | 11 ----------- tmux.h | 2 +- tty-keys.c | 36 ++++++++---------------------------- tty.c | 11 +---------- 7 files changed, 11 insertions(+), 63 deletions(-) diff --git a/input-keys.c b/input-keys.c index 2915cb45..4fee2f58 100644 --- a/input-keys.c +++ b/input-keys.c @@ -243,17 +243,11 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) * is because an old style mouse release event cannot be converted into * the new SGR format, since the released button is unknown). Otherwise * pretend that tmux doesn't speak this extension, and fall back to the - * UTF-8 (1005) extension if the application requested, or to the * legacy format. */ if (m->sgr_type != ' ' && (wp->screen->mode & MODE_MOUSE_SGR)) { len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); - } else if (wp->screen->mode & MODE_MOUSE_UTF8) { - len = xsnprintf(buf, sizeof buf, "\033[M"); - len += utf8_split2(m->b + 32, &buf[len]); - len += utf8_split2(x + 33, &buf[len]); - len += utf8_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; diff --git a/input.c b/input.c index d8e80afb..b4663d10 100644 --- a/input.c +++ b/input.c @@ -1461,9 +1461,6 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 1004: screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON); break; - case 1005: - screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); - break; case 1006: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); break; @@ -1544,9 +1541,6 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); wp->flags |= PANE_FOCUSPUSH; /* force update */ break; - case 1005: - screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); - break; case 1006: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); break; diff --git a/screen-write.c b/screen-write.c index 53067efe..0c27945b 100644 --- a/screen-write.c +++ b/screen-write.c @@ -56,7 +56,7 @@ screen_write_reset(struct screen_write_ctx *ctx) screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); - s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); + s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_SGR); screen_write_clearscreen(ctx); screen_write_cursormove(ctx, 0, 0); diff --git a/server-client.c b/server-client.c index 64426a70..c5e93438 100644 --- a/server-client.c +++ b/server-client.c @@ -808,17 +808,6 @@ server_client_reset_state(struct client *c) if (options_get_number(oo, "mouse")) mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON; - /* - * Set UTF-8 mouse input if required. If the terminal is UTF-8 and any - * mouse mode is in effect, turn on UTF-8 mouse input. If the receiving - * terminal hasn't requested it (that is, it isn't in s->mode), then - * it'll be converted in input_mouse. - */ - if ((c->tty.flags & TTY_UTF8) && (mode & ALL_MOUSE_MODES)) - mode |= MODE_MOUSE_UTF8; - else - mode &= ~MODE_MOUSE_UTF8; - /* Set the terminal mode and reset attributes. */ tty_update_mode(&c->tty, mode, s); tty_reset(&c->tty); diff --git a/tmux.h b/tmux.h index a112e7df..e697b9f8 100644 --- a/tmux.h +++ b/tmux.h @@ -599,7 +599,7 @@ struct mode_key_table { #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 #define MODE_BLINKING 0x80 -#define MODE_MOUSE_UTF8 0x100 +/* 0x100 unused */ #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 #define MODE_FOCUSON 0x800 diff --git a/tty-keys.c b/tty-keys.c index 706dcc30..b5e36f8e 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -481,6 +481,7 @@ tty_keys_next(struct tty *tty) /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); len = EVBUFFER_LENGTH(tty->event->input); + if (len == 0) return (0); log_debug("keys are %zu (%.*s)", len, (int) len, buf); @@ -653,10 +654,8 @@ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; - struct utf8_data ud; - u_int i, value, x, y, b, sgr_b; + u_int i, x, y, b, sgr_b; u_char sgr_type, c; - enum utf8_state more; /* * Standard mouse sequences are \033[M followed by three characters @@ -686,8 +685,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (1); /* - * Third byte is M in old standard and UTF-8 extension, < in SGR - * extension. + * Third byte is M in old standard (and UTF-8 extension which we do not + * support), < in SGR extension. */ if (buf[2] == 'M') { /* Read the three inputs. */ @@ -695,32 +694,13 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) for (i = 0; i < 3; i++) { if (len <= *size) return (1); - - if (tty->mode & MODE_MOUSE_UTF8) { - if (utf8_open(&ud, buf[*size]) == UTF8_MORE) { - if (ud.size != 2) - return (-1); - (*size)++; - if (len <= *size) - return (1); - more = utf8_append(&ud, buf[*size]); - if (more != UTF8_DONE) - return (-1); - value = utf8_combine(&ud); - } else - value = (u_char)buf[*size]; - (*size)++; - } else { - value = (u_char)buf[*size]; - (*size)++; - } - + c = (u_char)buf[(*size)++]; if (i == 0) - b = value; + b = c; else if (i == 1) - x = value; + x = c; else - y = value; + y = c; } log_debug("mouse input: %.*s", (int)*size, buf); diff --git a/tty.c b/tty.c index 13ca7f8e..42be6d9f 100644 --- a/tty.c +++ b/tty.c @@ -521,21 +521,15 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } tty->cstyle = s->cstyle; } - if (changed & (ALL_MOUSE_MODES|MODE_MOUSE_UTF8)) { + if (changed & ALL_MOUSE_MODES) { if (mode & ALL_MOUSE_MODES) { /* - * Enable the UTF-8 (1005) extension if configured to. * Enable the SGR (1006) extension unconditionally, as * this is safe from misinterpretation. Do it in this * order, because in some terminals it's the last one * that takes effect and SGR is the preferred one. */ - if (mode & MODE_MOUSE_UTF8) - tty_puts(tty, "\033[?1005h"); - else - tty_puts(tty, "\033[?1005l"); tty_puts(tty, "\033[?1006h"); - if (mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002h"); else if (mode & MODE_MOUSE_STANDARD) @@ -545,10 +539,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_puts(tty, "\033[?1002l"); else if (tty->mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000l"); - tty_puts(tty, "\033[?1006l"); - if (tty->mode & MODE_MOUSE_UTF8) - tty_puts(tty, "\033[?1005l"); } } if (changed & MODE_KKEYPAD) { From 2adf3f42ee451b30ef492229dac322808f93d8dc Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 23 Nov 2015 23:47:57 +0000 Subject: [PATCH 440/703] Partly revert previous, it is harmless to keep support for UTF-8 mouse mode inside tmux, just no longer support it for tmux itself. --- input-keys.c | 6 ++++++ input.c | 6 ++++++ screen-write.c | 2 +- tmux.h | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/input-keys.c b/input-keys.c index 4fee2f58..2915cb45 100644 --- a/input-keys.c +++ b/input-keys.c @@ -243,11 +243,17 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) * is because an old style mouse release event cannot be converted into * the new SGR format, since the released button is unknown). Otherwise * pretend that tmux doesn't speak this extension, and fall back to the + * UTF-8 (1005) extension if the application requested, or to the * legacy format. */ if (m->sgr_type != ' ' && (wp->screen->mode & MODE_MOUSE_SGR)) { len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); + } else if (wp->screen->mode & MODE_MOUSE_UTF8) { + len = xsnprintf(buf, sizeof buf, "\033[M"); + len += utf8_split2(m->b + 32, &buf[len]); + len += utf8_split2(x + 33, &buf[len]); + len += utf8_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; diff --git a/input.c b/input.c index b4663d10..d8e80afb 100644 --- a/input.c +++ b/input.c @@ -1461,6 +1461,9 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx) case 1004: screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON); break; + case 1005: + screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); + break; case 1006: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); break; @@ -1541,6 +1544,9 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx) screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); wp->flags |= PANE_FOCUSPUSH; /* force update */ break; + case 1005: + screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); + break; case 1006: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); break; diff --git a/screen-write.c b/screen-write.c index 0c27945b..53067efe 100644 --- a/screen-write.c +++ b/screen-write.c @@ -56,7 +56,7 @@ screen_write_reset(struct screen_write_ctx *ctx) screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); - s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_SGR); + s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); screen_write_clearscreen(ctx); screen_write_cursormove(ctx, 0, 0); diff --git a/tmux.h b/tmux.h index e697b9f8..a112e7df 100644 --- a/tmux.h +++ b/tmux.h @@ -599,7 +599,7 @@ struct mode_key_table { #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 #define MODE_BLINKING 0x80 -/* 0x100 unused */ +#define MODE_MOUSE_UTF8 0x100 #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 #define MODE_FOCUSON 0x800 From b32ce34cf2b5e48eab4f626f378d50f365b594a3 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 09:34:55 +0000 Subject: [PATCH 441/703] Don't allow options in table without scope set. --- options-table.c | 2 ++ tmux.h | 1 + 2 files changed, 3 insertions(+) diff --git a/options-table.c b/options-table.c index dc4922ba..0dab0c0e 100644 --- a/options-table.c +++ b/options-table.c @@ -892,6 +892,8 @@ options_table_populate_tree(enum options_table_scope scope, struct options *oo) const struct options_table_entry *oe; for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope == OPTIONS_TABLE_NONE) + fatalx("no scope for %s", oe->name); if (oe->scope != scope) continue; switch (oe->type) { diff --git a/tmux.h b/tmux.h index a112e7df..591fe54e 100644 --- a/tmux.h +++ b/tmux.h @@ -1394,6 +1394,7 @@ enum options_table_type { OPTIONS_TABLE_STYLE }; enum options_table_scope { + OPTIONS_TABLE_NONE, OPTIONS_TABLE_SERVER, OPTIONS_TABLE_SESSION, OPTIONS_TABLE_WINDOW, From 7b085136a7291cbcdfcc53182fbd13aaca70306e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 24 Nov 2015 18:46:50 +0000 Subject: [PATCH 442/703] -sys/queue.h in proc.c, and nuke the unnecessary C++ header guards stuff and sys/cdefs.h in vis.h (it causes problems on some platforms). Reported by someone on GitHub, issue 212. --- compat/vis.h | 5 ----- proc.c | 1 - 2 files changed, 6 deletions(-) diff --git a/compat/vis.h b/compat/vis.h index 6795139c..9f12d236 100644 --- a/compat/vis.h +++ b/compat/vis.h @@ -73,9 +73,6 @@ */ #define UNVIS_END 1 /* no more characters */ -#include - -__BEGIN_DECLS char *vis(char *, int, int, int); int strvis(char *, const char *, int); int stravis(char **, const char *, int); @@ -85,6 +82,4 @@ int strunvis(char *, const char *); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t); -__END_DECLS - #endif /* !_VIS_H_ */ diff --git a/proc.c b/proc.c index 7aa59cff..3e1e81dd 100644 --- a/proc.c +++ b/proc.c @@ -17,7 +17,6 @@ */ #include -#include #include #include From 4ec61bef461aba6d5849bce971a241850b8d7ef6 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 20:40:51 +0000 Subject: [PATCH 443/703] Fix usage of detach-client. --- cmd-detach-client.c | 2 +- tmux.1 | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 813ac032..f7369df0 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -31,7 +31,7 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { "detach-client", "detach", "as:t:P", 0, 0, - "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, + "[-aP] [-s target-session] " CMD_TARGET_CLIENT_USAGE, CMD_READONLY, cmd_detach_client_exec }; diff --git a/tmux.1 b/tmux.1 index 3fd383d6..455de33b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -714,8 +714,7 @@ is used, .Ic update-environment option will not be applied. .It Xo Ic detach-client -.Op Fl P -.Op Fl a +.Op Fl aP .Op Fl s Ar target-session .Op Fl t Ar target-client .Xc From 9cccb8c1159b0a4747b5152e2df08e42207b574d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:19:46 +0000 Subject: [PATCH 444/703] Make the log stuff a bit tidier with some helper functions. --- cmd-show-messages.c | 1 - log.c | 35 +++++++++++++++++++++++++++++------ proc.c | 2 +- server.c | 2 +- tmux.c | 15 +-------------- tmux.h | 8 ++++---- 6 files changed, 36 insertions(+), 27 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 92ac5cc2..3d2edf9a 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -61,7 +61,6 @@ cmd_show_messages_server(struct cmd_q *cmdq) cmdq_print(cmdq, "started %s", tim); cmdq_print(cmdq, "socket path %s", socket_path); - cmdq_print(cmdq, "debug level %d", debug_level); cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); return (1); diff --git a/log.c b/log.c index 6c7be8b2..46f1673c 100644 --- a/log.c +++ b/log.c @@ -22,30 +22,53 @@ #include #include #include +#include #include #include "tmux.h" -FILE *log_file; +static FILE *log_file; +static int log_level; -void log_event_cb(int, const char *); -void log_vwrite(const char *, va_list); +static void log_event_cb(int, const char *); +static void log_vwrite(const char *, va_list); /* Log callback for libevent. */ -void +static void log_event_cb(__unused int severity, const char *msg) { log_debug("%s", msg); } +/* Increment log level. */ +void +log_add_level(void) +{ + log_level++; +} + +/* Get log level. */ +int +log_get_level(void) +{ + return (log_level); +} + /* Open logging to file. */ void -log_open(const char *path) +log_open(const char *name) { + char *path; + + if (log_level == 0) + return; + if (log_file != NULL) fclose(log_file); + xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid()); log_file = fopen(path, "w"); + free(path); if (log_file == NULL) return; @@ -65,7 +88,7 @@ log_close(void) } /* Write a log message. */ -void +static void log_vwrite(const char *msg, va_list ap) { char *fmt, *out; diff --git a/proc.c b/proc.c index 31fd136b..06340cd9 100644 --- a/proc.c +++ b/proc.c @@ -188,7 +188,7 @@ proc_start(const char *name, struct event_base *base, int forkflag, fatalx("event_reinit failed"); } - logfile(name); + log_open(name); setproctitle("%s (%s)", name, socket_path); log_debug("%s started (%ld): socket %s, protocol %d", name, diff --git a/server.c b/server.c index e17b9356..e9159422 100644 --- a/server.c +++ b/server.c @@ -173,7 +173,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) } close(pair[0]); - if (debug_level > 3) + if (log_get_level() > 3) tty_create_log(); if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " "tty ps", NULL) != 0) diff --git a/tmux.c b/tmux.c index aa827178..f5383fcf 100644 --- a/tmux.c +++ b/tmux.c @@ -44,7 +44,6 @@ struct options *global_w_options; /* window options */ struct environ *global_environ; char *shell_cmd; -int debug_level; time_t start_time; char socket_path[PATH_MAX]; @@ -61,18 +60,6 @@ usage(void) exit(1); } -void -logfile(const char *name) -{ - char *path; - - if (debug_level > 0) { - xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); - log_open(path); - free(path); - } -} - const char * getshell(void) { @@ -243,7 +230,7 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; break; case 'v': - debug_level++; + log_add_level(); break; default: usage(); diff --git a/tmux.h b/tmux.h index 591fe54e..5f469f92 100644 --- a/tmux.h +++ b/tmux.h @@ -1432,10 +1432,8 @@ extern struct options *global_s_options; extern struct options *global_w_options; extern struct environ *global_environ; extern char *shell_cmd; -extern int debug_level; extern time_t start_time; extern char socket_path[PATH_MAX]; -void logfile(const char *); const char *getshell(void); int checkshell(const char *); int areshell(const char *); @@ -2210,8 +2208,10 @@ char *utf8_padcstr(const char *, u_int); char *get_proc_name(int, char *); /* log.c */ -void log_open(const char *); -void log_close(void); +void log_add_level(void); +int log_get_level(void); +void log_open(const char *); +void log_close(void); void printflike(1, 2) log_debug(const char *, ...); __dead void printflike(1, 2) fatal(const char *, ...); __dead void printflike(1, 2) fatalx(const char *, ...); From bdbbd9711c05507161bc70ccdde91bdb719d943b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:23:44 +0000 Subject: [PATCH 445/703] Show libevent version in showmsgs -I. --- cmd-show-messages.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 3d2edf9a..8cf71f41 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -62,6 +62,8 @@ cmd_show_messages_server(struct cmd_q *cmdq) cmdq_print(cmdq, "started %s", tim); cmdq_print(cmdq, "socket path %s", socket_path); cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); + cmdq_print(cmdq, "libevent %s (%s)", event_get_version(), + event_get_method()); return (1); } From 4e3015a8925f72b35393afec250cc46e4f480641 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:32:36 +0000 Subject: [PATCH 446/703] Log some system and libevent information at startup. --- proc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/proc.c b/proc.c index 06340cd9..593c1b8b 100644 --- a/proc.c +++ b/proc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -170,6 +171,7 @@ proc_start(const char *name, struct event_base *base, int forkflag, void (*signalcb)(int)) { struct tmuxproc *tp; + struct utsname u; if (forkflag) { switch (fork()) { @@ -191,8 +193,13 @@ proc_start(const char *name, struct event_base *base, int forkflag, log_open(name); setproctitle("%s (%s)", name, socket_path); + if (uname(&u) < 0) + memset(&u, 0, sizeof u); + log_debug("%s started (%ld): socket %s, protocol %d", name, (long)getpid(), socket_path, PROTOCOL_VERSION); + log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, + u.version, event_get_version(), event_get_method()); tp = xcalloc(1, sizeof *tp); tp->name = xstrdup(name); From 9fd3318dd818c45432581beff5b1a5f6cb55f2ff Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:50:06 +0000 Subject: [PATCH 447/703] All kill-session -C to clear alerts in all windows, suggested by Aaron U'Ren. --- cmd-kill-session.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 74843eb6..3f39c241 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_session_entry = { "kill-session", NULL, - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, + "aCt:", 0, 0, + "[-aC] " CMD_TARGET_SESSION_USAGE, 0, cmd_kill_session_exec }; @@ -42,11 +42,18 @@ cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s, *sloop, *stmp; + struct winlink *wl; if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); - if (args_has(args, 'a')) { + if (args_has(args, 'C')) { + RB_FOREACH(wl, winlinks, &s->windows) { + wl->window->flags &= ~WINDOW_ALERTFLAGS; + wl->flags &= ~WINLINK_ALERTFLAGS; + } + server_redraw_session(s); + } else if (args_has(args, 'a')) { RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { if (sloop != s) { server_destroy_session(sloop); From 1e2df2d46496fc025330c25fa08e83d14e62d11b Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 21:52:06 +0000 Subject: [PATCH 448/703] Remove the -I part of show-messages which isn't really that useful; the server start time can now be accessed with a new start_time format (use: tmux display -p '#{t:start_time}') --- cmd-show-messages.c | 26 ++------------------------ format.c | 2 ++ server.c | 2 +- tmux.1 | 12 ++++++++---- tmux.c | 2 +- tmux.h | 6 +++--- 6 files changed, 17 insertions(+), 33 deletions(-) diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 8cf71f41..c98c2c91 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -33,8 +33,8 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { "show-messages", "showmsgs", - "IJTt:", 0, 0, - "[-IJT] " CMD_TARGET_CLIENT_USAGE, + "JTt:", 0, 0, + "[-JT] " CMD_TARGET_CLIENT_USAGE, 0, cmd_show_messages_exec }; @@ -47,27 +47,9 @@ const struct cmd_entry cmd_server_info_entry = { cmd_show_messages_exec }; -int cmd_show_messages_server(struct cmd_q *); int cmd_show_messages_terminals(struct cmd_q *, int); int cmd_show_messages_jobs(struct cmd_q *, int); -int -cmd_show_messages_server(struct cmd_q *cmdq) -{ - char *tim; - - tim = ctime(&start_time); - *strchr(tim, '\n') = '\0'; - - cmdq_print(cmdq, "started %s", tim); - cmdq_print(cmdq, "socket path %s", socket_path); - cmdq_print(cmdq, "protocol version %d", PROTOCOL_VERSION); - cmdq_print(cmdq, "libevent %s (%s)", event_get_version(), - event_get_method()); - - return (1); -} - int cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { @@ -118,10 +100,6 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) int done, blank; done = blank = 0; - if (args_has(args, 'I') || self->entry == &cmd_server_info_entry) { - blank = cmd_show_messages_server(cmdq); - done = 1; - } if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { blank = cmd_show_messages_terminals(cmdq, blank); done = 1; diff --git a/format.c b/format.c index 50fa7dea..bafd8ce6 100644 --- a/format.c +++ b/format.c @@ -488,6 +488,8 @@ format_create_flags(int flags) format_add_cb(ft, "host", format_cb_host); format_add_cb(ft, "host_short", format_cb_host_short); format_add_cb(ft, "pid", format_cb_pid); + format_add(ft, "socket_path", "%s", socket_path); + format_add_tv(ft, "start_time", &start_time); return (ft); } diff --git a/server.c b/server.c index e9159422..d950ed77 100644 --- a/server.c +++ b/server.c @@ -187,7 +187,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) mode_key_init_trees(); key_bindings_init(); - start_time = time(NULL); + gettimeofday(&start_time, NULL); server_fd = server_create_socket(); if (server_fd == -1) diff --git a/tmux.1 b/tmux.1 index 455de33b..cba38264 100644 --- a/tmux.1 +++ b/tmux.1 @@ -740,7 +740,7 @@ Kill the .Nm server and clients and destroy all sessions. .It Xo Ic kill-session -.Op Fl a +.Op Fl aC .Op Fl t Ar target-session .Xc Destroy the given session, closing any windows linked to it and no other @@ -748,6 +748,10 @@ sessions, and detaching all clients attached to it. If .Fl a is given, all sessions but the specified one is killed. +The +.Fl C +clears alerts (bell, activity, or silence) in all windows linked to the +session. .It Xo Ic list-clients .Op Fl F Ar format .Op Fl t Ar target-session @@ -887,7 +891,7 @@ is specified, only update the client's status bar. Rename the session to .Ar new-name . .It Xo Ic show-messages -.Op Fl IJT +.Op Fl JT .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) @@ -900,11 +904,10 @@ With .Fl t , display the log for .Ar target-client . -.Fl I , .Fl J and .Fl T -show debugging information about the running server, jobs and terminals. +show debugging information about jobs and terminals. .It Ic source-file Ar path .D1 (alias: Ic source ) Execute commands from @@ -3410,6 +3413,7 @@ The following variables are available, where appropriate: .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "start_time" Ta "" Ta "Server start time" .It Li "window_activity" Ta "" Ta "Integer time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" diff --git a/tmux.c b/tmux.c index f5383fcf..7e89d526 100644 --- a/tmux.c +++ b/tmux.c @@ -44,7 +44,7 @@ struct options *global_w_options; /* window options */ struct environ *global_environ; char *shell_cmd; -time_t start_time; +struct timeval start_time; char socket_path[PATH_MAX]; __dead void usage(void); diff --git a/tmux.h b/tmux.h index 5f469f92..97908d98 100644 --- a/tmux.h +++ b/tmux.h @@ -1431,9 +1431,9 @@ extern struct options *global_options; extern struct options *global_s_options; extern struct options *global_w_options; extern struct environ *global_environ; -extern char *shell_cmd; -extern time_t start_time; -extern char socket_path[PATH_MAX]; +extern char *shell_cmd; +extern struct timeval start_time; +extern char socket_path[PATH_MAX]; const char *getshell(void); int checkshell(const char *); int areshell(const char *); From bef217b241ec50d6aed87dbb3604acd1bdb74121 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:04:36 +0000 Subject: [PATCH 449/703] Switch a fprintf to a fatal, and wrap some long lines. --- client.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/client.c b/client.c index 53a98a30..fcb704fb 100644 --- a/client.c +++ b/client.c @@ -303,11 +303,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); if (client_flags & CLIENT_CONTROLCONTROL) { - if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { - fprintf(stderr, "tcgetattr failed: %s\n", - strerror(errno)); - return (1); - } + if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) + fatal("tcgetattr failed"); cfmakeraw(&tio); tio.c_iflag = ICRNL|IXANY; tio.c_oflag = OPOST|ONLCR; @@ -389,7 +386,8 @@ client_send_identify(const char *ttynam, const char *cwd) s = ""; proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); + proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, + strlen(ttynam) + 1); proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) @@ -401,8 +399,9 @@ client_send_identify(const char *ttynam, const char *cwd) for (ss = environ; *ss != NULL; ss++) { sslen = strlen(*ss) + 1; - if (sslen <= MAX_IMSGSIZE - IMSG_HEADER_SIZE) - proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); + if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) + continue; + proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); From 8976dac9e0c2e55b240ad55f4f7fa0a3b887c0e2 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:09:53 +0000 Subject: [PATCH 450/703] Remove malloc_options DEBUG bit. --- tmux.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tmux.c b/tmux.c index 7e89d526..c515ebb2 100644 --- a/tmux.c +++ b/tmux.c @@ -34,10 +34,6 @@ #include "tmux.h" -#ifdef DEBUG -extern char *malloc_options; -#endif - struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ @@ -182,10 +178,6 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; -#ifdef DEBUG - malloc_options = (char *) "AFGJPX"; -#endif - setlocale(LC_TIME, ""); tzset(); From c913fb99b6f0f7d08b77d8d021df7d7fa27f82d0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:27:22 +0000 Subject: [PATCH 451/703] Tidy the code that works out the socket path, and just use the full path in the global socket_path rather than copying it. --- client.c | 4 +-- tmux.c | 86 +++++++++++++++++++++++++------------------------------- tmux.h | 2 +- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/client.c b/client.c index fcb704fb..595aff9b 100644 --- a/client.c +++ b/client.c @@ -55,7 +55,7 @@ int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); -int client_connect(struct event_base *, char *, int); +int client_connect(struct event_base *, const char *, int); void client_send_identify(const char *, const char *); void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); @@ -96,7 +96,7 @@ client_get_lock(char *lockfile) /* Connect client to server. */ int -client_connect(struct event_base *base, char *path, int start_server) +client_connect(struct event_base *base, const char *path, int start_server) { struct sockaddr_un sa; size_t size; diff --git a/tmux.c b/tmux.c index c515ebb2..f2143755 100644 --- a/tmux.c +++ b/tmux.c @@ -41,10 +41,10 @@ struct environ *global_environ; char *shell_cmd; struct timeval start_time; -char socket_path[PATH_MAX]; +const char *socket_path; __dead void usage(void); -char *makesocketpath(const char *); +static char *make_label(const char *); __dead void usage(void) @@ -102,38 +102,48 @@ areshell(const char *shell) return (0); } -char * -makesocketpath(const char *label) +static char * +make_label(const char *label) { - char base[PATH_MAX], realbase[PATH_MAX], *path, *s; - struct stat sb; - u_int uid; + char *base, resolved[PATH_MAX], *path, *s; + struct stat sb; + u_int uid; + int saved_errno; + + if (label == NULL) + label = "default"; uid = getuid(); + if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') - xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); + xasprintf(&base, "%s/tmux-%u", s, uid); else - xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); + xasprintf(&base, "%s/tmux-%u", _PATH_TMP, uid); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) - return (NULL); + goto fail; if (lstat(base, &sb) != 0) - return (NULL); + goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; - return (NULL); + goto fail; } if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { errno = EACCES; - return (NULL); + goto fail; } - if (realpath(base, realbase) == NULL) - strlcpy(realbase, base, sizeof realbase); - - xasprintf(&path, "%s/%s", realbase, label); + if (realpath(base, resolved) == NULL) + strlcpy(resolved, base, sizeof resolved); + xasprintf(&path, "%s/%s", resolved, label); return (path); + +fail: + saved_errno = errno; + free(base); + errno = saved_errno; + return (NULL); } void @@ -289,41 +299,23 @@ main(int argc, char **argv) } /* - * Figure out the socket path. If specified on the command-line with -S - * or -L, use it, otherwise try $TMUX or assume -L default. + * If socket is specified on the command-line with -S or -L, it is + * used. Otherwise, $TMUX is checked and if that fails "default" is + * used. */ - if (path == NULL) { - /* If no -L, use the environment. */ - if (label == NULL) { - s = getenv("TMUX"); - if (s != NULL) { - path = xstrdup(s); - path[strcspn (path, ",")] = '\0'; - if (*path == '\0') { - free(path); - label = xstrdup("default"); - } - } else - label = xstrdup("default"); - } - - /* -L or default set. */ - if (label != NULL) { - if ((path = makesocketpath(label)) == NULL) { - fprintf(stderr, "can't create socket: %s\n", - strerror(errno)); - exit(1); - } + if (path == NULL && label == NULL) { + s = getenv("TMUX"); + if (s != NULL && *s != '\0' && *s != ',') { + path = xstrdup(s); + path[strcspn (path, ",")] = '\0'; } } - free(label); - - if (strlcpy(socket_path, path, sizeof socket_path) >= - sizeof socket_path) { - fprintf(stderr, "socket path too long: %s\n", path); + if (path == NULL && (path = make_label(label)) == NULL) { + fprintf(stderr, "can't create socket: %s\n", strerror(errno)); exit(1); } - free(path); + socket_path = path; + free(label); /* Pass control to the client. */ exit(client_main(event_init(), argc, argv, flags)); diff --git a/tmux.h b/tmux.h index 97908d98..50d7fb03 100644 --- a/tmux.h +++ b/tmux.h @@ -1433,7 +1433,7 @@ extern struct options *global_w_options; extern struct environ *global_environ; extern char *shell_cmd; extern struct timeval start_time; -extern char socket_path[PATH_MAX]; +extern const char *socket_path; const char *getshell(void); int checkshell(const char *); int areshell(const char *); From c18fbefe93dfbc7525b27e0129f8a0e8e9b70117 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:27:59 +0000 Subject: [PATCH 452/703] Document socket_path format. --- tmux.1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tmux.1 b/tmux.1 index cba38264..8fa6ad55 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3413,6 +3413,7 @@ The following variables are available, where appropriate: .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" +.It Li "socket_path" Ta "" "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" .It Li "window_activity" Ta "" Ta "Integer time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" From 73e30cbda8ade3a1c7ba3b771a911545826f76b7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 22:45:44 +0000 Subject: [PATCH 453/703] Actually show something (even if it not that helpful) if the server fails to start (for example if it can't create the socket), rather than hanging or showing nothing. --- client.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/client.c b/client.c index 595aff9b..5fdfd029 100644 --- a/client.c +++ b/client.c @@ -76,8 +76,12 @@ client_get_lock(char *lockfile) { int lockfd; - if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) - fatal("open failed"); + if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { + lockfd = open("/dev/null", O_WRONLY); + if (lockfd == -1) + fatal("open failed"); + return (lockfd); + } log_debug("lock file is %s", lockfile); if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { @@ -114,10 +118,10 @@ client_connect(struct event_base *base, const char *path, int start_server) retry: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("socket failed"); + return (-1); log_debug("trying connect"); - if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { + if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; @@ -255,6 +259,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) cmd_list_free(cmdlist); } + /* Create client process structure (starts logging). */ + client_proc = proc_start("client", base, 0, client_signal); + /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { @@ -267,9 +274,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } return (1); } - - /* Build process state. */ - client_proc = proc_start("client", base, 0, client_signal); client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); /* Save these before pledge(). */ @@ -365,7 +369,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) printf("%%exit\n"); printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); - } + } else + fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); return (client_exitval); } @@ -517,7 +522,11 @@ client_dispatch(struct imsg *imsg, __unused void *arg) if (imsg == NULL) { client_exitreason = CLIENT_EXIT_LOST_SERVER; client_exitval = 1; - } else if (client_attached) + proc_exit(client_proc); + return; + } + + if (client_attached) client_dispatch_attached(imsg); else client_dispatch_wait(imsg); From dca93c56e05ce631dd2f80da759f40c4d4b20ab5 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 23:01:51 +0000 Subject: [PATCH 454/703] Do lock failures slightly better, return a special value so we don't unlink the wrong thing. --- client.c | 34 ++++++++++++++++++---------------- server.c | 8 +++++--- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/client.c b/client.c index 5fdfd029..8144dd1c 100644 --- a/client.c +++ b/client.c @@ -67,23 +67,21 @@ const char *client_exit_message(void); /* * Get server create lock. If already held then server start is happening in - * another client, so block until the lock is released and return -1 to - * retry. Ignore other errors - just continue and start the server without the - * lock. + * another client, so block until the lock is released and return -2 to + * retry. Return -1 on failure to continue and start the server anyway. */ int client_get_lock(char *lockfile) { int lockfd; - if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { - lockfd = open("/dev/null", O_WRONLY); - if (lockfd == -1) - fatal("open failed"); - return (lockfd); - } log_debug("lock file is %s", lockfile); + if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { + log_debug("open failed: %s", strerror(errno)); + return (-1); + } + if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { log_debug("flock failed: %s", strerror(errno)); if (errno != EAGAIN) @@ -91,7 +89,7 @@ client_get_lock(char *lockfile) while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) /* nothing */; close(lockfd); - return (-1); + return (-2); } log_debug("flock succeeded"); @@ -131,12 +129,16 @@ retry: if (!locked) { xasprintf(&lockfile, "%s.lock", path); - if ((lockfd = client_get_lock(lockfile)) == -1) { - log_debug("didn't get lock"); + if ((lockfd = client_get_lock(lockfile)) < 0) { + log_debug("didn't get lock (%d)", lockfd); + free(lockfile); - goto retry; + lockfile = NULL; + + if (lockfd == -2) + goto retry; } - log_debug("got lock"); + log_debug("got lock (%d)", lockfd); /* * Always retry at least once, even if we got the lock, @@ -148,7 +150,7 @@ retry: goto retry; } - if (unlink(path) != 0 && errno != ENOENT) { + if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { free(lockfile); close(lockfd); return (-1); @@ -156,7 +158,7 @@ retry: fd = server_start(base, lockfd, lockfile); } - if (locked) { + if (locked && lockfd >= 0) { free(lockfile); close(lockfd); } diff --git a/server.c b/server.c index d950ed77..c45e8a2c 100644 --- a/server.c +++ b/server.c @@ -195,9 +195,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) server_update_socket(); server_client_create(pair[1]); - unlink(lockfile); - free(lockfile); - close(lockfd); + if (lockfd >= 0) { + unlink(lockfile); + free(lockfile); + close(lockfd); + } start_cfg(); From 3ff46b2e43d48376f74c0fd74b5616925d4063f0 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 23:22:51 +0000 Subject: [PATCH 455/703] Shell command from -c doesn't have to be global, pass it as an argument. --- client.c | 26 ++++++++++++++------------ tmux.c | 11 +++++------ tmux.h | 3 +-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/client.c b/client.c index 8144dd1c..5987efa5 100644 --- a/client.c +++ b/client.c @@ -53,7 +53,7 @@ enum msgtype client_exittype; const char *client_exitsession; int client_attached; -__dead void client_exec(const char *); +__dead void client_exec(const char *,const char *); int client_get_lock(char *); int client_connect(struct event_base *, const char *, int); void client_send_identify(const char *, const char *); @@ -62,7 +62,7 @@ void client_write(int, const char *, size_t); void client_signal(int); void client_dispatch(struct imsg *, void *); void client_dispatch_attached(struct imsg *); -void client_dispatch_wait(struct imsg *); +void client_dispatch_wait(struct imsg *, const char *); const char *client_exit_message(void); /* @@ -213,7 +213,8 @@ client_exit_message(void) /* Client main loop. */ int -client_main(struct event_base *base, int argc, char **argv, int flags) +client_main(struct event_base *base, int argc, char **argv, int flags, + const char *shellcmd) { struct cmd *cmd; struct cmd_list *cmdlist; @@ -234,7 +235,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags) /* Set up the initial command. */ cmdflags = 0; - if (shell_cmd != NULL) { + if (shellcmd != NULL) { msg = MSG_SHELL; cmdflags = CMD_STARTSERVER; } else if (argc == 0) { @@ -276,7 +277,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags) } return (1); } - client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); + client_peer = proc_add_peer(client_proc, fd, client_dispatch, + (void *)shellcmd); /* Save these before pledge(). */ if ((cwd = getcwd(path, sizeof path)) == NULL) { @@ -450,12 +452,12 @@ client_write(int fd, const char *data, size_t size) /* Run command in shell; used for -c. */ __dead void -client_exec(const char *shell) +client_exec(const char *shell, const char *shellcmd) { const char *name, *ptr; char *argv0; - log_debug("shell %s, command %s", shell, shell_cmd); + log_debug("shell %s, command %s", shell, shellcmd); ptr = strrchr(shell, '/'); if (ptr != NULL && *(ptr + 1) != '\0') @@ -473,7 +475,7 @@ client_exec(const char *shell) setblocking(STDERR_FILENO, 1); closefrom(STDERR_FILENO + 1); - execl(shell, argv0, "-c", shell_cmd, (char *) NULL); + execl(shell, argv0, "-c", shellcmd, (char *) NULL); fatal("execl failed"); } @@ -519,7 +521,7 @@ client_signal(int sig) /* Callback for client read events. */ void -client_dispatch(struct imsg *imsg, __unused void *arg) +client_dispatch(struct imsg *imsg, void *arg) { if (imsg == NULL) { client_exitreason = CLIENT_EXIT_LOST_SERVER; @@ -531,12 +533,12 @@ client_dispatch(struct imsg *imsg, __unused void *arg) if (client_attached) client_dispatch_attached(imsg); else - client_dispatch_wait(imsg); + client_dispatch_wait(imsg, arg); } /* Dispatch imsgs when in wait state (before MSG_READY). */ void -client_dispatch_wait(struct imsg *imsg) +client_dispatch_wait(struct imsg *imsg, const char *shellcmd) { char *data; ssize_t datalen; @@ -616,7 +618,7 @@ client_dispatch_wait(struct imsg *imsg) fatalx("bad MSG_SHELL string"); clear_signals(0); - client_exec(data); + client_exec(data, shellcmd); /* NOTREACHED */ case MSG_DETACH: case MSG_DETACHKILL: diff --git a/tmux.c b/tmux.c index f2143755..7d49dd30 100644 --- a/tmux.c +++ b/tmux.c @@ -39,7 +39,6 @@ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ struct environ *global_environ; -char *shell_cmd; struct timeval start_time; const char *socket_path; @@ -184,7 +183,7 @@ find_home(void) int main(int argc, char **argv) { - char *path, *label, **var, tmp[PATH_MAX]; + char *path, *label, **var, tmp[PATH_MAX], *shellcmd = NULL; const char *s; int opt, flags, keys; @@ -203,8 +202,8 @@ main(int argc, char **argv) flags |= CLIENT_256COLOURS; break; case 'c': - free(shell_cmd); - shell_cmd = xstrdup(optarg); + free(shellcmd); + shellcmd = xstrdup(optarg); break; case 'C': if (flags & CLIENT_CONTROL) @@ -241,7 +240,7 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (shell_cmd != NULL && argc != 0) + if (shellcmd != NULL && argc != 0) usage(); if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " @@ -318,5 +317,5 @@ main(int argc, char **argv) free(label); /* Pass control to the client. */ - exit(client_main(event_init(), argc, argv, flags)); + exit(client_main(event_init(), argc, argv, flags, shellcmd)); } diff --git a/tmux.h b/tmux.h index 50d7fb03..f4638bc8 100644 --- a/tmux.h +++ b/tmux.h @@ -1431,7 +1431,6 @@ extern struct options *global_options; extern struct options *global_s_options; extern struct options *global_w_options; extern struct environ *global_environ; -extern char *shell_cmd; extern struct timeval start_time; extern const char *socket_path; const char *getshell(void); @@ -1732,7 +1731,7 @@ int cmd_string_parse(const char *, struct cmd_list **, const char *, void cmd_wait_for_flush(void); /* client.c */ -int client_main(struct event_base *, int, char **, int); +int client_main(struct event_base *, int, char **, int, const char *); /* key-bindings.c */ RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); From 62d3af17f9e6aec244ab1d91c1c46fdbbf6f8a86 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 24 Nov 2015 23:46:15 +0000 Subject: [PATCH 456/703] Make environ_set va_args and use it to tidy up some calls. Also add a missing word in manpage (from jmc). --- cmd-set-environment.c | 4 ++-- environ.c | 46 ++++++++++++++++++++++++++++++------------- server-fn.c | 11 +++++------ tmux.1 | 2 +- tmux.c | 2 +- tmux.h | 6 ++++-- window.c | 7 +++---- 7 files changed, 48 insertions(+), 30 deletions(-) diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 864e1d9b..2c0010af 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -79,13 +79,13 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "can't specify a value with -r"); return (CMD_RETURN_ERROR); } - environ_set(env, name, NULL); + environ_clear(env, name); } else { if (value == NULL) { cmdq_error(cmdq, "no value specified"); return (CMD_RETURN_ERROR); } - environ_set(env, name, value); + environ_set(env, name, "%s", value); } return (CMD_RETURN_NORMAL); diff --git a/environ.c b/environ.c index 43a9ce01..de560896 100644 --- a/environ.c +++ b/environ.c @@ -83,8 +83,12 @@ environ_copy(struct environ *srcenv, struct environ *dstenv) { struct environ_entry *envent; - RB_FOREACH(envent, environ, srcenv) - environ_set(dstenv, envent->name, envent->value); + RB_FOREACH(envent, environ, srcenv) { + if (envent->value == NULL) + environ_clear(dstenv, envent->name); + else + environ_set(dstenv, envent->name, "%s", envent->value); + } } /* Find an environment variable. */ @@ -99,23 +103,37 @@ environ_find(struct environ *env, const char *name) /* Set an environment variable. */ void -environ_set(struct environ *env, const char *name, const char *value) +environ_set(struct environ *env, const char *name, const char *fmt, ...) +{ + struct environ_entry *envent; + va_list ap; + + va_start(ap, fmt); + if ((envent = environ_find(env, name)) != NULL) { + free(envent->value); + xvasprintf(&envent->value, fmt, ap); + } else { + envent = xmalloc(sizeof *envent); + envent->name = xstrdup(name); + xvasprintf(&envent->value, fmt, ap); + RB_INSERT(environ, env, envent); + } + va_end(ap); +} + +/* Clear an environment variable. */ +void +environ_clear(struct environ *env, const char *name) { struct environ_entry *envent; if ((envent = environ_find(env, name)) != NULL) { free(envent->value); - if (value != NULL) - envent->value = xstrdup(value); - else - envent->value = NULL; + envent->value = NULL; } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); - if (value != NULL) - envent->value = xstrdup(value); - else - envent->value = NULL; + envent->value = NULL; RB_INSERT(environ, env, envent); } } @@ -134,7 +152,7 @@ environ_put(struct environ *env, const char *var) name = xstrdup(var); name[strcspn(name, "=")] = '\0'; - environ_set(env, name, value); + environ_set(env, name, "%s", value); free(name); } @@ -166,9 +184,9 @@ environ_update(const char *vars, struct environ *srcenv, copyvars = next = xstrdup(vars); while ((var = strsep(&next, " ")) != NULL) { if ((envent = environ_find(srcenv, var)) == NULL) - environ_set(dstenv, var, NULL); + environ_clear(dstenv, var); else - environ_set(dstenv, envent->name, envent->value); + environ_set(dstenv, envent->name, "%s", envent->value); } free(copyvars); } diff --git a/server-fn.c b/server-fn.c index 3e4b6116..a22c964d 100644 --- a/server-fn.c +++ b/server-fn.c @@ -34,20 +34,19 @@ void server_callback_identify(int, short, void *); void server_fill_environ(struct session *s, struct environ *env) { - char var[PATH_MAX], *term; - u_int idx; - long pid; + char *term; + u_int idx; + long pid; if (s != NULL) { term = options_get_string(global_options, "default-terminal"); - environ_set(env, "TERM", term); + environ_set(env, "TERM", "%s", term); idx = s->id; } else idx = (u_int)-1; pid = getpid(); - xsnprintf(var, sizeof var, "%s,%ld,%u", socket_path, pid, idx); - environ_set(env, "TMUX", var); + environ_set(env, "TMUX", "%s,%ld,%u", socket_path, pid, idx); } void diff --git a/tmux.1 b/tmux.1 index 8fa6ad55..454af1d2 100644 --- a/tmux.1 +++ b/tmux.1 @@ -750,7 +750,7 @@ If is given, all sessions but the specified one is killed. The .Fl C -clears alerts (bell, activity, or silence) in all windows linked to the +flag clears alerts (bell, activity, or silence) in all windows linked to the session. .It Xo Ic list-clients .Op Fl F Ar format diff --git a/tmux.c b/tmux.c index 7d49dd30..68f0092d 100644 --- a/tmux.c +++ b/tmux.c @@ -273,7 +273,7 @@ main(int argc, char **argv) for (var = environ; *var != NULL; var++) environ_put(global_environ, *var); if (getcwd(tmp, sizeof tmp) != NULL) - environ_set(global_environ, "PWD", tmp); + environ_set(global_environ, "PWD", "%s", tmp); global_options = options_create(NULL); options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); diff --git a/tmux.h b/tmux.h index f4638bc8..2acfc75f 100644 --- a/tmux.h +++ b/tmux.h @@ -1567,7 +1567,9 @@ struct environ_entry *environ_first(struct environ *); struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); -void environ_set(struct environ *, const char *, const char *); +void printflike(3, 4) environ_set(struct environ *, const char *, const char *, + ...); +void environ_clear(struct environ *, const char *); void environ_put(struct environ *, const char *); void environ_unset(struct environ *, const char *); void environ_update(const char *, struct environ *, struct environ *); @@ -1739,7 +1741,7 @@ RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); extern struct key_tables key_tables; int key_table_cmp(struct key_table *, struct key_table *); int key_bindings_cmp(struct key_binding *, struct key_binding *); -struct key_table *key_bindings_get_table(const char *, int); +struct key_table *key_bindings_get_table(const char *, int); void key_bindings_unref_table(struct key_table *); void key_bindings_add(const char *, key_code, int, struct cmd_list *); void key_bindings_remove(const char *, key_code); diff --git a/window.c b/window.c index 8ee517a1..deda2895 100644 --- a/window.c +++ b/window.c @@ -808,7 +808,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, struct termios *tio, char **cause) { struct winsize ws; - char *argv0, *cmd, **argvp, paneid[16]; + char *argv0, *cmd, **argvp; const char *ptr, *first, *home; struct termios tio2; int i; @@ -863,9 +863,8 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, closefrom(STDERR_FILENO + 1); if (path != NULL) - environ_set(env, "PATH", path); - xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); - environ_set(env, "TMUX_PANE", paneid); + environ_set(env, "PATH", "%s", path); + environ_set(env, "TMUX_PANE", "%%%u", wp->id); environ_push(env); clear_signals(1); From ac8678aefe157d7e40c5bcedd12333eaedf0df92 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 25 Nov 2015 07:58:55 +0000 Subject: [PATCH 457/703] Don't print error if none to print. --- client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.c b/client.c index 5987efa5..516ed3bf 100644 --- a/client.c +++ b/client.c @@ -373,7 +373,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags, printf("%%exit\n"); printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); - } else + } else if (client_exitreason != CLIENT_EXIT_NONE) fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); return (client_exitval); From 260de2cb5e384a0cf193b5f9f996fa8fb2c5aaf8 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Wed, 25 Nov 2015 16:48:47 +0000 Subject: [PATCH 458/703] Remove logfile() --- proc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/proc.c b/proc.c index 6a68d74b..e4c69484 100644 --- a/proc.c +++ b/proc.c @@ -188,8 +188,6 @@ proc_start(const char *name, struct event_base *base, int forkflag, fatalx("event_reinit failed"); } - logfile(name); - #ifdef HAVE_SETPROCTITLE log_open(name); setproctitle("%s (%s)", name, socket_path); From a1bc339340abebfd9938a8e98f7cb26186c7e7c6 Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Wed, 25 Nov 2015 16:51:17 +0000 Subject: [PATCH 459/703] log_open() isn't conditional on proctitle --- proc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proc.c b/proc.c index e4c69484..f84ecf92 100644 --- a/proc.c +++ b/proc.c @@ -188,8 +188,9 @@ proc_start(const char *name, struct event_base *base, int forkflag, fatalx("event_reinit failed"); } -#ifdef HAVE_SETPROCTITLE log_open(name); + +#ifdef HAVE_SETPROCTITLE setproctitle("%s (%s)", name, socket_path); #endif From 3b83bda29c51c7b2c3aeef01dde1d3ba6a441e89 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 25 Nov 2015 23:35:24 +0000 Subject: [PATCH 460/703] Add to TODO. --- TODO | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TODO b/TODO index 6094c023..c52d42b6 100644 --- a/TODO +++ b/TODO @@ -121,3 +121,13 @@ * automatic pane logging * BCE? We are halfway there (output side is done for pane backgrounds), just need to change how screen/grid handles erase + * copy mode key bindings should just be a standard key table, using + something like "copy-mode start-selection"; it could use + command-prompt for search, goto, etc: + + bind -Temacs command-prompt -p'Search Up: ' 'copy-mode search-up %%' + + it'd need a separate lookup, because modes are per-pane, perhaps a + table() cb to give the table name ("vi" or "emacs"). anything in the + table fires the command, anything not in the table is injected as a + key From 6a2ca34216530c687027cf9e767d2b46c85976e6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 27 Nov 2015 15:06:43 +0000 Subject: [PATCH 461/703] Do not set a limit on the length of commands when printing them. --- arguments.c | 86 +++++++++++++++++++++++-------------------------- cmd-list-keys.c | 9 ++---- cmd-list.c | 35 +++++++++++--------- cmd-queue.c | 7 ++-- cmd.c | 24 +++++++------- tmux.h | 6 ++-- 6 files changed, 81 insertions(+), 86 deletions(-) diff --git a/arguments.c b/arguments.c index 54753de3..0a42cc38 100644 --- a/arguments.c +++ b/arguments.c @@ -128,77 +128,73 @@ args_free(struct args *args) free(args); } -/* Print a set of arguments. */ -size_t -args_print(struct args *args, char *buf, size_t len) +/* Add to string. */ +static void printflike(3, 4) +args_print_add(char **buf, size_t *len, const char *fmt, ...) { - size_t off, used; + va_list ap; + char *s; + size_t slen; + + va_start(ap, fmt); + slen = xvasprintf(&s, fmt, ap); + va_end(ap); + + *len += slen; + *buf = xrealloc(*buf, *len); + + strlcat(*buf, s, *len); + free(s); +} + +/* Print a set of arguments. */ +char * +args_print(struct args *args) +{ + size_t len; + char *buf; int i; - const char *quotes; struct args_entry *entry; - /* There must be at least one byte at the start. */ - if (len == 0) - return (0); - off = 0; + len = 1; + buf = xcalloc(1, len); /* Process the flags first. */ - buf[off++] = '-'; RB_FOREACH(entry, args_tree, &args->tree) { if (entry->value != NULL) continue; - if (off == len - 1) { - buf[off] = '\0'; - return (len); - } - buf[off++] = entry->flag; - buf[off] = '\0'; + if (*buf == '\0') + args_print_add(&buf, &len, "-"); + args_print_add(&buf, &len, "%c", entry->flag); } - if (off == 1) - buf[--off] = '\0'; /* Then the flags with arguments. */ RB_FOREACH(entry, args_tree, &args->tree) { if (entry->value == NULL) continue; - if (off >= len) { - /* snprintf will have zero terminated. */ - return (len); - } - - if (strchr(entry->value, ' ') != NULL) - quotes = "\""; + if (*buf != '\0') + args_print_add(&buf, &len, " -%c ", entry->flag); else - quotes = ""; - used = xsnprintf(buf + off, len - off, "%s-%c %s%s%s", - off != 0 ? " " : "", entry->flag, quotes, entry->value, - quotes); - if (used > len - off) - used = len - off; - off += used; + args_print_add(&buf, &len, "-%c ", entry->flag); + if (strchr(entry->value, ' ') != NULL) + args_print_add(&buf, &len, "\"%s\"", entry->value); + else + args_print_add(&buf, &len, "%s", entry->value); } /* And finally the argument vector. */ for (i = 0; i < args->argc; i++) { - if (off >= len) { - /* snprintf will have zero terminated. */ - return (len); - } - + if (*buf != '\0') + args_print_add(&buf, &len, " "); if (strchr(args->argv[i], ' ') != NULL) - quotes = "\""; + args_print_add(&buf, &len, "\"%s\"", args->argv[i]); else - quotes = ""; - used = xsnprintf(buf + off, len - off, "%s%s%s%s", - off != 0 ? " " : "", quotes, args->argv[i], quotes); - if (used > len - off) - used = len - off; - off += used; + args_print_add(&buf, &len, "%s", args->argv[i]); } - return (off); + return (buf); } /* Return if an argument is present. */ diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 4355f24e..f0a59c0b 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -56,7 +56,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct key_binding *bd; const char *key, *tablename, *r; char *cp, tmp[BUFSIZ]; - size_t used; int repeat, width, tablewidth, keywidth; if (self->entry == &cmd_list_commands_entry) @@ -115,11 +114,9 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) strlcat(tmp, " ", sizeof tmp); free(cp); - used = strlen(tmp); - if (used < (sizeof tmp) - 1) { - cmd_list_print(bd->cmdlist, tmp + used, - (sizeof tmp) - used); - } + cp = cmd_list_print(bd->cmdlist); + strlcat(tmp, cp, sizeof tmp); + free(cp); cmdq_print(cmdq, "bind-key %s", tmp); } diff --git a/cmd-list.c b/cmd-list.c index 0c75ed49..59fc7796 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -99,25 +99,28 @@ cmd_list_free(struct cmd_list *cmdlist) free(cmdlist); } -size_t -cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len) +char * +cmd_list_print(struct cmd_list *cmdlist) { struct cmd *cmd; - size_t off, used; + char *buf, *this; + size_t len; + + len = 1; + buf = xcalloc(1, len); - off = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - if (off >= len) - break; - off += cmd_print(cmd, buf + off, len - off); - if (off >= len) - break; - if (TAILQ_NEXT(cmd, qentry) != NULL) { - used = xsnprintf(buf + off, len - off, " ; "); - if (used > len - off) - used = len - off; - off += used; - } + this = cmd_print(cmd); + + len += strlen(this) + 3; + buf = xrealloc(buf, len); + + strlcat(buf, this, len); + if (TAILQ_NEXT(cmd, qentry) != NULL) + strlcat(buf, " ; ", len); + + free(this); } - return (off); + + return (buf); } diff --git a/cmd-queue.c b/cmd-queue.c index 2d896212..c85fb048 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -184,11 +184,12 @@ cmdq_continue_one(struct cmd_q *cmdq) { struct cmd *cmd = cmdq->cmd; enum cmd_retval retval; - char tmp[1024]; + char *s; int flags = !!(cmd->flags & CMD_CONTROL); - cmd_print(cmd, tmp, sizeof tmp); - log_debug("cmdq %p: %s", cmdq, tmp); + s = cmd_print(cmd); + log_debug("cmdq %p: %s", cmdq, s); + free(s); cmdq->time = time(NULL); cmdq->number++; diff --git a/cmd.c b/cmd.c index 0c20d656..824d9caf 100644 --- a/cmd.c +++ b/cmd.c @@ -384,21 +384,19 @@ usage: return (NULL); } -size_t -cmd_print(struct cmd *cmd, char *buf, size_t len) +char * +cmd_print(struct cmd *cmd) { - size_t off, used; + char *out, *s; - off = xsnprintf(buf, len, "%s ", cmd->entry->name); - if (off + 1 < len) { - used = args_print(cmd->args, buf + off, len - off - 1); - if (used == 0) - off--; - else - off += used; - buf[off] = '\0'; - } - return (off); + s = args_print(cmd->args); + if (*s != '\0') + xasprintf(&out, "%s %s", cmd->entry->name, s); + else + out = xstrdup(cmd->entry->name); + free(s); + + return (out); } /* Adjust current mouse position for a pane. */ diff --git a/tmux.h b/tmux.h index 2acfc75f..0808a30c 100644 --- a/tmux.h +++ b/tmux.h @@ -1665,7 +1665,7 @@ RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); struct args *args_create(int, ...); struct args *args_parse(const char *, int, char **); void args_free(struct args *); -size_t args_print(struct args *, char *, size_t); +char *args_print(struct args *); int args_has(struct args *, u_char); void args_set(struct args *, u_char, const char *); const char *args_get(struct args *, u_char); @@ -1694,7 +1694,7 @@ char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); -size_t cmd_print(struct cmd *, char *, size_t); +char *cmd_print(struct cmd *); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); @@ -1710,7 +1710,7 @@ enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); void cmd_list_free(struct cmd_list *); -size_t cmd_list_print(struct cmd_list *, char *, size_t); +char *cmd_list_print(struct cmd_list *); /* cmd-queue.c */ struct cmd_q *cmdq_new(struct client *); From 1d331c7e6263826e92bb22824d97d781106d0e51 Mon Sep 17 00:00:00 2001 From: guenther Date: Sun, 29 Nov 2015 17:06:59 +0000 Subject: [PATCH 462/703] Delete a duplicated line ok jmc@ --- tmux.1 | 1 - 1 file changed, 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 454af1d2..7d45688c 100644 --- a/tmux.1 +++ b/tmux.1 @@ -871,7 +871,6 @@ If is used, .Ic update-environment option will not be applied. -.Ic update-environment . .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client From a785a7f7005761a3ab52b8c336e1ff5881eb7d83 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Dec 2015 09:41:03 +0000 Subject: [PATCH 463/703] Do not deref wp if window_get_active_at returns NULL which can happen on very large terminals, from Michael Graczyk. --- server-client.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server-client.c b/server-client.c index c5e93438..d2abd356 100644 --- a/server-client.c +++ b/server-client.c @@ -332,10 +332,11 @@ server_client_check_mouse(struct client *c) where = BORDER; else { wp = window_get_active_at(s->curw->window, x, y); - if (wp != NULL) + if (wp != NULL) { where = PANE; - log_debug("mouse at %u,%u is on pane %%%u", x, y, - wp->id); + log_debug("mouse at %u,%u is on pane %%%u", + x, y, wp->id); + } } if (where == NOWHERE) return (KEYC_NONE); From 7236838dead7885b90c6a57736433bea63c26599 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Dec 2015 23:09:22 +0000 Subject: [PATCH 464/703] Mark new active pane changed after pane lost in window, and after break-pane. Reported by tim@. --- cmd-break-pane.c | 1 + window.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 39179cc7..707cd09b 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -82,6 +82,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) window_set_name(w, name); free(name); layout_init(w, wp); + wp->flags |= PANE_CHANGED; if (idx == -1) idx = -1 - options_get_number(dst_s->options, "base-index"); diff --git a/window.c b/window.c index deda2895..75b99672 100644 --- a/window.c +++ b/window.c @@ -579,6 +579,8 @@ window_lost_pane(struct window *w, struct window_pane *wp) if (w->active == NULL) w->active = TAILQ_NEXT(wp, entry); } + if (w->active != NULL) + w->active->flags |= PANE_CHANGED; } else if (wp == w->last) w->last = NULL; } From 3cdb2f0bb793b641ec07ef45b3fd485c2089d9e4 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 3 Dec 2015 14:43:24 +0000 Subject: [PATCH 465/703] Add to TODO. --- TODO | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index c52d42b6..b4b8231b 100644 --- a/TODO +++ b/TODO @@ -5,6 +5,8 @@ * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions + * list-keys should quote output so that bindings can just be used in + config file as-is - make command sequences more usable * don't require space after ; @@ -15,7 +17,7 @@ * way to set socket path from config file - format improvements: - * option to quote format (#{session_name:quoted}) + * option to quote format (#{q:session_name}) * formats need conditions for >0 (for #P) * some way to pad # stuff with spaces * formats to show if a window is linked into multiple sessions, into From 0417f1f2bef1e8899dad4564db797250ae2c8c49 Mon Sep 17 00:00:00 2001 From: claudio Date: Sat, 5 Dec 2015 13:18:24 +0000 Subject: [PATCH 466/703] EAGAIN handling for imsg_read. OK henning@ benno@ --- proc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proc.c b/proc.c index 593c1b8b..5f51b0ac 100644 --- a/proc.c +++ b/proc.c @@ -61,7 +61,8 @@ proc_event_cb(__unused int fd, short events, void *arg) struct imsg imsg; if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { - if ((n = imsg_read(&peer->ibuf)) == -1 || n == 0) { + if (((n = imsg_read(&peer->ibuf)) == -1 && errno != EAGAIN) || + n == 0) { peer->dispatchcb(NULL, peer->arg); return; } From b9563340b7e9f516f77e674791e7635e948ca7fa Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 7 Dec 2015 09:47:41 +0000 Subject: [PATCH 467/703] Fix bell indicators across detach, reported by Torbjorn Lonnemark, diff from Thomas Adam. --- alerts.c | 44 +++++++++++++++++++++++++++++++++----------- cmd-attach-session.c | 1 + tmux.h | 1 + 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/alerts.c b/alerts.c index 55e7044d..536ea750 100644 --- a/alerts.c +++ b/alerts.c @@ -29,6 +29,7 @@ int alerts_enabled(struct window *, int); void alerts_callback(int, short, void *); void alerts_reset(struct window *); +int alerts_check_all(struct session *, struct winlink *); int alerts_check_bell(struct session *, struct winlink *); int alerts_check_activity(struct session *, struct winlink *); int alerts_check_silence(struct session *, struct winlink *); @@ -54,16 +55,14 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg) RB_FOREACH(w, windows, &windows) { RB_FOREACH(s, sessions, &sessions) { + if (s->flags & SESSION_UNATTACHED) + continue; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window != w) continue; flags = w->flags; - alerts = alerts_check_bell(s, wl); - alerts |= alerts_check_activity(s, wl); - alerts |= alerts_check_silence(s, wl); - if (alerts != 0) - server_status_session(s); + alerts = alerts_check_all(s, wl); log_debug("%s:%d @%u alerts check, alerts %#x, " "flags %#x", s->name, wl->idx, w->id, @@ -74,6 +73,29 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg) alerts_fired = 0; } +int +alerts_check_all(struct session *s, struct winlink *wl) +{ + int alerts; + + alerts = alerts_check_bell(s, wl); + alerts |= alerts_check_activity(s, wl); + alerts |= alerts_check_silence(s, wl); + if (alerts != 0) + server_status_session(s); + + return (alerts); +} + +void +alerts_check_session(struct session *s) +{ + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) + alerts_check_all(s, wl); +} + int alerts_enabled(struct window *w, int flags) { @@ -143,12 +165,12 @@ alerts_check_bell(struct session *s, struct winlink *wl) struct window *w = wl->window; int action, visual; - if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) + if (!(w->flags & WINDOW_BELL)) return (0); - if (s->curw != wl || s->flags & SESSION_UNATTACHED) + if (s->curw != wl) { wl->flags |= WINLINK_BELL; - if (s->flags & SESSION_UNATTACHED) - return (0); + w->flags &= ~WINDOW_BELL; + } if (s->curw->window == w) w->flags &= ~WINDOW_BELL; @@ -190,7 +212,7 @@ alerts_check_activity(struct session *s, struct winlink *wl) if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) + if (s->curw == wl) return (0); if (!options_get_number(w->options, "monitor-activity")) @@ -222,7 +244,7 @@ alerts_check_silence(struct session *s, struct winlink *wl) if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) return (0); - if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) + if (s->curw == wl) return (0); if (options_get_number(w->options, "monitor-silence") == 0) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 46133923..5bde0d80 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -162,6 +162,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, cmdq->client_exit = 0; } recalculate_sizes(); + alerts_check_session(s); server_update_socket(); return (CMD_RETURN_NORMAL); diff --git a/tmux.h b/tmux.h index 0808a30c..a1e16955 100644 --- a/tmux.h +++ b/tmux.h @@ -1757,6 +1757,7 @@ const char *key_string_lookup_key(key_code); /* alerts.c */ void alerts_reset_all(void); void alerts_queue(struct window *, int); +void alerts_check_session(struct session *); /* server.c */ extern struct tmuxproc *server_proc; From d20a3ef57c1571898bb351f1f9287114be0aa419 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 7 Dec 2015 12:51:06 +0000 Subject: [PATCH 468/703] Update .mailmap file. --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index a9b07251..847494ba 100644 --- a/.mailmap +++ b/.mailmap @@ -1,4 +1,5 @@ Bob Beck beck +Claudio Jeker claudio Igor Sobrado sobrado Ingo Schwarze schwarze Jacek Masiulaniec jacekm @@ -29,6 +30,7 @@ Theo de Raadt Theo Deraadt Thomas Adam Thomas Thomas Adam n6tadam Thomas Adam Thomas Adam +Tim van der Molen tim Tobias Stoeckmann tobias Todd C Miller millert William Yodlowsky william From 5411033f66ced27168bbd407b8b9c4589072a39a Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 7 Dec 2015 12:54:34 +0000 Subject: [PATCH 469/703] Update tmux.vim from Teubel Gyorgy. --- examples/tmux.vim | 119 ++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 47 deletions(-) diff --git a/examples/tmux.vim b/examples/tmux.vim index 6f85db5b..d9b60408 100644 --- a/examples/tmux.vim +++ b/examples/tmux.vim @@ -31,15 +31,16 @@ syn keyword tmuxAction any current none syn keyword tmuxBoolean off on syn keyword tmuxCmds - \ attach[-session] - \ bind[-key] + \ attach + \ attach-session + \ bind + \ bind-key \ break-pane \ breakp \ capture-pane \ capturep \ choose-buffer \ choose-client - \ choose-list \ choose-session \ choose-tree \ choose-window @@ -47,124 +48,147 @@ syn keyword tmuxCmds \ clearhist \ clock-mode \ command-prompt - \ confirm[-before] + \ confirm + \ confirm-before \ copy-mode \ delete-buffer \ deleteb - \ detach[-client] - \ display[-message] + \ detach + \ detach-client + \ display + \ display-message \ display-panes \ displayp \ find-window \ findw - \ has[-session] - \ if[-shell] + \ has + \ has-session + \ if + \ if-shell + \ info \ join-pane \ joinp \ kill-pane - \ killp \ kill-server \ kill-session \ kill-window + \ killp \ killw + \ last \ last-pane + \ last-window \ lastp - \ last[-window] \ link-window \ linkw \ list-buffers - \ lsb \ list-clients - \ lsc \ list-commands - \ lscm \ list-keys - \ lsk \ list-panes - \ lsp \ list-sessions - \ ls \ list-windows - \ lsw \ load-buffer \ loadb + \ lock \ lock-client - \ lockc - \ lock[-server] + \ lock-server \ lock-session + \ lockc \ locks + \ ls + \ lsb + \ lsc + \ lscm + \ lsk + \ lsp + \ lsw \ move-pane - \ movep \ move-window + \ movep \ movew - \ new[-session] + \ new + \ new-session + \ new-window + \ neww + \ next \ next-layout + \ next-window \ nextl - \ next[-window] \ paste-buffer \ pasteb + \ path \ pipe-pane \ pipep + \ prev \ previous-layout + \ previous-window \ prevl - \ prev[ious-window] - \ refresh[-client] - \ rename[-session] + \ refresh + \ refresh-client + \ rename + \ rename-session \ rename-window \ renamew \ resize-pane \ resizep \ respawn-pane - \ respawnp \ respawn-window + \ respawnp \ respawnw \ rotate-window \ rotatew - \ run[-shell] + \ run + \ run-shell \ save-buffer \ saveb \ select-layout - \ selectl \ select-pane - \ selectp \ select-window + \ selectl + \ selectp \ selectw - \ send[-keys] + \ send + \ send-keys \ send-prefix \ server-info - \ info + \ set \ set-buffer - \ setb \ set-environment - \ setenv - \ set[-option] + \ set-option \ set-window-option + \ setb + \ setenv \ setw + \ show \ show-buffer - \ showb \ show-environment - \ showenv \ show-messages - \ showmsgs - \ show[-options] + \ show-options \ show-window-options + \ showb + \ showenv + \ showmsgs \ showw - \ source[-file] + \ source + \ source-file \ split-window \ splitw - \ start[-server] + \ start + \ start-server \ suspend-client \ suspendc \ swap-pane - \ swapp \ swap-window + \ swapp \ swapw \ switch-client \ switchc - \ unbind[-key] + \ unbind + \ unbind-key \ unlink-window \ unlinkw - \ wait[-for] + \ wait + \ wait-for syn keyword tmuxOptsSet \ assume-paste-time @@ -188,14 +212,11 @@ syn keyword tmuxOptsSet \ history-limit \ lock-after-time \ lock-command - \ lock-server \ message-command-style \ message-limit \ message-style \ mouse \ mouse-utf8 - \ pane-active-border-style - \ pane-border-style \ prefix \ prefix2 \ quiet @@ -215,8 +236,9 @@ syn keyword tmuxOptsSet \ status-position \ status-right \ status-right-length + \ status-right-style + \ status-style \ status-utf8 - \ staus-right-style \ terminal-overrides \ update-environment \ visual-activity @@ -229,6 +251,7 @@ syn keyword tmuxOptsSetw \ allow-rename \ alternate-screen \ automatic-rename + \ automatic-rename-format \ clock-mode-colour \ clock-mode-style \ force-height @@ -241,7 +264,9 @@ syn keyword tmuxOptsSetw \ monitor-silence \ other-pane-height \ other-pane-width + \ pane-active-border-style \ pane-base-index + \ pane-border-style \ remain-on-exit \ synchronize-panes \ utf8 From ff16836d1de31b81bc093c42e4c336fb7e12306f Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Dec 2015 00:47:27 +0000 Subject: [PATCH 470/703] pty is in section 4 --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 87c1ac8f..5d78b2ea 100644 --- a/tmux.1 +++ b/tmux.1 @@ -61,7 +61,7 @@ A window occupies the entire screen and may be split into rectangular panes, each of which is a separate pseudo terminal (the -.Xr pty 7 +.Xr pty 4 manual page documents the technical details of pseudo terminals). Any number of .Nm From b580a551914ae398162b0eec9bcbfa5901fabcdd Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Dec 2015 00:48:22 +0000 Subject: [PATCH 471/703] pty(7) -> pty(4) --- tmux.1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.1 b/tmux.1 index 5d78b2ea..dcf0dc57 100644 --- a/tmux.1 +++ b/tmux.1 @@ -370,7 +370,7 @@ These specify the client, session, window or pane which a command should affect. .Pp .Ar target-client is the name of the -.Xr pty 7 +.Xr pty 4 file to which the client is connected, for example either of .Pa /dev/ttyp1 or @@ -1319,7 +1319,7 @@ interactively from a list. After a client is chosen, .Ql %% is replaced by the client -.Xr pty 7 +.Xr pty 4 path in .Ar template and the result executed as a command. @@ -4165,6 +4165,6 @@ bind-key / command-prompt "split-window 'exec man %%'" bind-key S command-prompt "new-window -n %1 'ssh %1'" .Ed .Sh SEE ALSO -.Xr pty 7 +.Xr pty 4 .Sh AUTHORS .An Nicholas Marriott Aq Mt nicm@users.sourceforge.net From 98994a8bb1ca7602f16a5de0d4482efd299f5d7b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Dec 2015 00:49:10 +0000 Subject: [PATCH 472/703] termios(4) --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index dcf0dc57..931fe327 100644 --- a/tmux.1 +++ b/tmux.1 @@ -824,7 +824,7 @@ and specify the size of the initial window (80 by 24 if not given). .Pp If run from a terminal, any -.Xr termios 3 +.Xr termios 4 special characters are saved and used for new windows in the new session. .Pp The From dbfce2a4d8ea0bd4773eacf154fd3e3406d1be5e Mon Sep 17 00:00:00 2001 From: mmcc Date: Tue, 8 Dec 2015 00:51:17 +0000 Subject: [PATCH 473/703] Use ^= instead of a verbose alternative. ok nicm@ --- cmd-switch-client.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index dbdc5b63..4c847584 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -52,12 +52,8 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); - if (args_has(args, 'r')) { - if (c->flags & CLIENT_READONLY) - c->flags &= ~CLIENT_READONLY; - else - c->flags |= CLIENT_READONLY; - } + if (args_has(args, 'r')) + c->flags ^= CLIENT_READONLY; tablename = args_get(args, 'T'); if (tablename != NULL) { From d2fb0efcd197bf0d581a0f7b1e27223d095cb339 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Dec 2015 01:10:31 +0000 Subject: [PATCH 474/703] Add hooks infrastructure, basic commands (set-hook, show-hooks) and a couple of not very useful client hooks. This will eventually let commands be run at various points and on notifications. Joint work with Thomas Adam. --- Makefile | 2 + cmd-attach-session.c | 5 +- cmd-detach-client.c | 12 ++-- cmd-set-hook.c | 116 ++++++++++++++++++++++++++++++++++++ cmd.c | 4 ++ hooks.c | 139 +++++++++++++++++++++++++++++++++++++++++++ server-client.c | 15 +++++ session.c | 4 ++ tmux.1 | 46 ++++++++++++++ tmux.c | 3 + tmux.h | 31 ++++++++-- 11 files changed, 364 insertions(+), 13 deletions(-) create mode 100644 cmd-set-hook.c create mode 100644 hooks.c diff --git a/Makefile b/Makefile index 10c6a783..a60f1f19 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ SRCS= alerts.c \ cmd-send-keys.c \ cmd-set-buffer.c \ cmd-set-environment.c \ + cmd-set-hook.c \ cmd-set-option.c \ cmd-show-environment.c \ cmd-show-messages.c \ @@ -78,6 +79,7 @@ SRCS= alerts.c \ format.c \ grid-view.c \ grid.c \ + hooks.c \ input-keys.c \ input.c \ job.c \ diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 5bde0d80..45b05b09 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -108,7 +108,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c_loop->peer, MSG_DETACH, s->name); + server_client_detach(c, MSG_DETACH); } } @@ -139,7 +139,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - proc_send_s(c_loop->peer, MSG_DETACH, s->name); + server_client_detach(c_loop, MSG_DETACH); } } @@ -159,6 +159,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); + hooks_run(c->session->hooks, "client-attached", c); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index f7369df0..d8128eae 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -72,9 +72,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session != s) - continue; - proc_send_s(cloop->peer, msgtype, cloop->session->name); + if (cloop->session == s) + server_client_detach(cloop, msgtype); } return (CMD_RETURN_STOP); } @@ -85,13 +84,12 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'a')) { TAILQ_FOREACH(cloop, &clients, entry) { - if (cloop->session == NULL || cloop == c) - continue; - proc_send_s(cloop->peer, msgtype, cloop->session->name); + if (cloop->session != NULL && cloop != c) + server_client_detach(cloop, msgtype); } return (CMD_RETURN_NORMAL); } - proc_send_s(c->peer, msgtype, c->session->name); + server_client_detach(c, msgtype); return (CMD_RETURN_STOP); } diff --git a/cmd-set-hook.c b/cmd-set-hook.c new file mode 100644 index 00000000..f35e7a0a --- /dev/null +++ b/cmd-set-hook.c @@ -0,0 +1,116 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Thomas Adam + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Set or show global or session hooks. + */ + +enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmd_q *); + +const struct cmd_entry cmd_set_hook_entry = { + "set-hook", NULL, + "gt:u", 1, 2, + "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", + 0, + cmd_set_hook_exec +}; + +const struct cmd_entry cmd_show_hooks_entry = { + "show-hooks", NULL, + "gt:", 0, 1, + "[-g] " CMD_TARGET_SESSION_USAGE, + 0, + cmd_set_hook_exec +}; + +enum cmd_retval +cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) +{ + struct args *args = self->args; + struct session *s; + struct cmd_list *cmdlist; + struct hooks *hooks; + struct hook *hook; + char *cause, *tmp; + const char *name, *cmd; + + if (args_has(args, 'g')) + hooks = global_hooks; + else { + s = cmd_find_session(cmdq, args_get(args, 't'), 0); + if (s == NULL) + return (CMD_RETURN_ERROR); + hooks = s->hooks; + } + + if (self->entry == &cmd_show_hooks_entry) { + hook = hooks_first(hooks); + while (hook != NULL) { + tmp = cmd_list_print(hook->cmdlist); + cmdq_print(cmdq, "%s -> %s", hook->name, tmp); + free(tmp); + + hook = hooks_next(hook); + } + return (CMD_RETURN_NORMAL); + } + + name = args->argv[0]; + if (*name == '\0') { + cmdq_error(cmdq, "invalid hook name"); + return (CMD_RETURN_ERROR); + } + if (args->argc < 2) + cmd = NULL; + else + cmd = args->argv[1]; + + if (args_has(args, 'u')) { + if (cmd != NULL) { + cmdq_error(cmdq, "command passed to unset hook: %s", + name); + return (CMD_RETURN_ERROR); + } + if ((hook = hooks_find(hooks, name)) != NULL) + hooks_remove(hooks, hook); + return (CMD_RETURN_NORMAL); + } + + if (cmd == NULL) { + cmdq_error(cmdq, "no command to set hook: %s", name); + return (CMD_RETURN_ERROR); + } + if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { + if (cause != NULL) { + cmdq_error(cmdq, "%s", cause); + free(cause); + } + return (CMD_RETURN_ERROR); + } + hooks_add(hooks, name, cmdlist); + cmd_list_free(cmdlist); + + return (CMD_RETURN_NORMAL); +} diff --git a/cmd.c b/cmd.c index 824d9caf..a950a49a 100644 --- a/cmd.c +++ b/cmd.c @@ -96,10 +96,12 @@ extern const struct cmd_entry cmd_send_prefix_entry; extern const struct cmd_entry cmd_server_info_entry; extern const struct cmd_entry cmd_set_buffer_entry; extern const struct cmd_entry cmd_set_environment_entry; +extern const struct cmd_entry cmd_set_hook_entry; extern const struct cmd_entry cmd_set_option_entry; extern const struct cmd_entry cmd_set_window_option_entry; extern const struct cmd_entry cmd_show_buffer_entry; extern const struct cmd_entry cmd_show_environment_entry; +extern const struct cmd_entry cmd_show_hooks_entry; extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_options_entry; extern const struct cmd_entry cmd_show_window_options_entry; @@ -183,10 +185,12 @@ const struct cmd_entry *cmd_table[] = { &cmd_server_info_entry, &cmd_set_buffer_entry, &cmd_set_environment_entry, + &cmd_set_hook_entry, &cmd_set_option_entry, &cmd_set_window_option_entry, &cmd_show_buffer_entry, &cmd_show_environment_entry, + &cmd_show_hooks_entry, &cmd_show_messages_entry, &cmd_show_options_entry, &cmd_show_window_options_entry, diff --git a/hooks.c b/hooks.c new file mode 100644 index 00000000..95d6b9d8 --- /dev/null +++ b/hooks.c @@ -0,0 +1,139 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2012 Thomas Adam + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +struct hooks { + RB_HEAD(hooks_tree, hook) tree; + struct hooks *parent; +}; + +static int hooks_cmp(struct hook *, struct hook *); +RB_PROTOTYPE(hooks_tree, hook, entry, hooks_cmp); +RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); + +struct hook *hooks_find1(struct hooks *, const char *); + +static int +hooks_cmp(struct hook *hook1, struct hook *hook2) +{ + return (strcmp(hook1->name, hook2->name)); +} + +struct hooks * +hooks_create(struct hooks *parent) +{ + struct hooks *hooks; + + hooks = xcalloc(1, sizeof *hooks); + RB_INIT(&hooks->tree); + hooks->parent = parent; + return (hooks); +} + +void +hooks_free(struct hooks *hooks) +{ + struct hook *hook, *hook1; + + RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) + hooks_remove(hooks, hook); + free(hooks); +} + +struct hook * +hooks_first(struct hooks *hooks) +{ + return (RB_MIN(hooks_tree, &hooks->tree)); +} + +struct hook * +hooks_next(struct hook *hook) +{ + return (RB_NEXT(hooks_tree, &hooks->tree, hook)); +} + +void +hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) +{ + struct hook *hook; + + if ((hook = hooks_find1(hooks, name)) != NULL) + hooks_remove(hooks, hook); + + hook = xcalloc(1, sizeof *hook); + hook->name = xstrdup(name); + hook->cmdlist = cmdlist; + hook->cmdlist->references++; + RB_INSERT(hooks_tree, &hooks->tree, hook); +} + +void +hooks_remove(struct hooks *hooks, struct hook *hook) +{ + RB_REMOVE(hooks_tree, &hooks->tree, hook); + cmd_list_free(hook->cmdlist); + free((char *) hook->name); + free(hook); +} + +struct hook * +hooks_find1(struct hooks *hooks, const char *name) +{ + struct hook hook; + + hook.name = name; + return (RB_FIND(hooks_tree, &hooks->tree, &hook)); +} + +struct hook * +hooks_find(struct hooks *hooks, const char *name) +{ + struct hook hook0, *hook; + + hook0.name = name; + hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); + while (hook == NULL) { + hooks = hooks->parent; + if (hooks == NULL) + break; + hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); + } + return (hook); +} + +void +hooks_run(struct hooks *hooks, const char *name, struct client *c) +{ + struct hook *hook; + struct cmd_q *cmdq; + + hook = hooks_find(hooks, name); + if (hook == NULL) + return; + log_debug("running hook %s", name); + + cmdq = cmdq_new(c); + cmdq_run(cmdq, hook->cmdlist, NULL); + cmdq_free(cmdq); +} diff --git a/server-client.c b/server-client.c index d2abd356..c948e980 100644 --- a/server-client.c +++ b/server-client.c @@ -256,6 +256,19 @@ server_client_free(__unused int fd, __unused short events, void *arg) free(c); } +/* Detach a client. */ +void +server_client_detach(struct client *c, enum msgtype msgtype) +{ + struct session *s = c->session; + + if (s == NULL) + return; + + hooks_run(c->session->hooks, "client-detached", c); + proc_send_s(c->peer, msgtype, s->name); +} + /* Check for mouse keys. */ key_code server_client_check_mouse(struct client *c) @@ -995,6 +1008,8 @@ server_client_dispatch(struct imsg *imsg, void *arg) recalculate_sizes(); server_redraw_client(c); } + if (c->session != NULL) + hooks_run(c->session->hooks, "client-resized", c); break; case MSG_EXITING: if (datalen != 0) diff --git a/session.c b/session.c index c5721c9a..b3ae2b42 100644 --- a/session.c +++ b/session.c @@ -123,7 +123,9 @@ session_create(const char *name, int argc, char **argv, const char *path, s->environ = environ_create(); if (env != NULL) environ_copy(env, s->environ); + s->options = options_create(global_s_options); + s->hooks = hooks_create(global_hooks); s->tio = NULL; if (tio != NULL) { @@ -189,7 +191,9 @@ session_free(__unused int fd, __unused short events, void *arg) if (s->references == 0) { environ_free(s->environ); + options_free(s->options); + hooks_free(s->hooks); free(s->name); free(s); diff --git a/tmux.1 b/tmux.1 index 7d45688c..6971100e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3193,6 +3193,52 @@ is used. .Fl v shows only the option value, not the name. .El +.Sh HOOKS +.Nm +allows commands to run on various triggers, called +.Em hooks . +Each hook has a +.Em name . +The following hooks are available: +.Bl -tag -width "XXXXXXXXXXXXXXXX" +.It client-attached +Run when a client is attached. +.It client-detached +Run when a client is detached +.It client-resized +Run when a client is resized. +.El +.Pp +Hooks are managed with these commands: +.Bl -tag -width Ds +.It Xo Ic set-hook +.Op Fl g +.Op Fl t Ar target-session +.Ar hook-name +.Ar command +.Xc +Sets hook +.Ar hook-name +to +.Ar command . +If +.Fl g +is given, +.Em hook-name +is added to the global list of hooks, otherwise it is added to the session +hooks (for +.Ar target-session +with +.Fl t ) . +Like options, session hooks inherit from the global ones. +.It Xo Ic show-hooks +.Op Fl g +.Op Fl t Ar target-session +.Xc +Shows the global list of hooks with +.Fl g , +otherwise the session hooks. +.Ed .Sh MOUSE SUPPORT If the .Ic mouse diff --git a/tmux.c b/tmux.c index 68f0092d..fe5e54a5 100644 --- a/tmux.c +++ b/tmux.c @@ -38,6 +38,7 @@ struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ struct environ *global_environ; +struct hooks *global_hooks; struct timeval start_time; const char *socket_path; @@ -269,6 +270,8 @@ main(int argc, char **argv) flags |= CLIENT_UTF8; } + global_hooks = hooks_create(NULL); + global_environ = environ_create(); for (var = environ; *var != NULL; var++) environ_put(global_environ, *var); diff --git a/tmux.h b/tmux.h index a1e16955..fa589f57 100644 --- a/tmux.h +++ b/tmux.h @@ -691,6 +691,14 @@ struct grid { struct grid_line *linedata; }; +/* Hook data structures. */ +struct hook { + const char *name; + struct cmd_q *cmdq; + struct cmd_list *cmdlist; + RB_ENTRY(hook) entry; +}; + /* Option data structures. */ struct options_entry { char *name; @@ -1011,6 +1019,7 @@ struct session { struct winlink_stack lastw; struct winlinks windows; + struct hooks *hooks; struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ @@ -1427,10 +1436,11 @@ struct options_table_entry { #define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ -extern struct options *global_options; -extern struct options *global_s_options; -extern struct options *global_w_options; -extern struct environ *global_environ; +extern struct hooks *global_hooks; +extern struct options *global_options; +extern struct options *global_s_options; +extern struct options *global_w_options; +extern struct environ *global_environ; extern struct timeval start_time; extern const char *socket_path; const char *getshell(void); @@ -1495,6 +1505,18 @@ void format_defaults_pane(struct format_tree *, void format_defaults_paste_buffer(struct format_tree *, struct paste_buffer *); +/* hooks.c */ +struct hook; +struct hooks *hooks_create(struct hooks *); +void hooks_free(struct hooks *); +struct hook *hooks_first(struct hooks *); +struct hook *hooks_next(struct hook *); +void hooks_add(struct hooks *, const char *, struct cmd_list *); +void hooks_copy(struct hooks *, struct hooks *); +void hooks_remove(struct hooks *, struct hook *); +struct hook *hooks_find(struct hooks *, const char *); +void hooks_run(struct hooks *, const char *, struct client *); + /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; extern struct mode_key_tree mode_key_tree_vi_edit; @@ -1782,6 +1804,7 @@ void server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); +void server_client_detach(struct client *, enum msgtype); void server_client_loop(void); void server_client_push_stdout(struct client *); void server_client_push_stderr(struct client *); From 1f94274b92b9b367887196755bdd3a848b3b847a Mon Sep 17 00:00:00 2001 From: jmc Date: Tue, 8 Dec 2015 06:42:07 +0000 Subject: [PATCH 475/703] Ed was meant to be El; --- tmux.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmux.1 b/tmux.1 index 6971100e..cbd01d9e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3238,7 +3238,7 @@ Like options, session hooks inherit from the global ones. Shows the global list of hooks with .Fl g , otherwise the session hooks. -.Ed +.El .Sh MOUSE SUPPORT If the .Ic mouse From 8f671d3eefa0af67b9e753eeae032e3ded6ffd00 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Dec 2015 08:14:04 +0000 Subject: [PATCH 476/703] Spacing nits. --- options.c | 2 +- tmux.h | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/options.c b/options.c index c355f9ce..5f958d23 100644 --- a/options.c +++ b/options.c @@ -47,7 +47,7 @@ options_cmp(struct options_entry *o1, struct options_entry *o2) struct options * options_create(struct options *parent) { - struct options *oo; + struct options *oo; oo = xcalloc(1, sizeof *oo); RB_INIT(&oo->tree); diff --git a/tmux.h b/tmux.h index fa589f57..7757f12f 100644 --- a/tmux.h +++ b/tmux.h @@ -693,10 +693,12 @@ struct grid { /* Hook data structures. */ struct hook { - const char *name; - struct cmd_q *cmdq; - struct cmd_list *cmdlist; - RB_ENTRY(hook) entry; + const char *name; + + struct cmd_q *cmdq; + struct cmd_list *cmdlist; + + RB_ENTRY(hook) entry; }; /* Option data structures. */ From e0f26dcda36dc35741da6047a11efb853b3137d9 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 8 Dec 2015 08:34:18 +0000 Subject: [PATCH 477/703] Remove format_create_flags and just pass flags to format_create. --- cmd-attach-session.c | 2 +- cmd-break-pane.c | 2 +- cmd-display-message.c | 2 +- cmd-if-shell.c | 2 +- cmd-list-buffers.c | 2 +- cmd-list-clients.c | 2 +- cmd-list-panes.c | 2 +- cmd-list-sessions.c | 2 +- cmd-list-windows.c | 2 +- cmd-new-session.c | 4 ++-- cmd-new-window.c | 4 ++-- cmd-pipe-pane.c | 2 +- cmd-run-shell.c | 2 +- cmd-split-window.c | 4 ++-- control-notify.c | 2 +- format.c | 9 +-------- names.c | 2 +- server-client.c | 2 +- status.c | 8 ++++---- tmux.h | 3 +-- window-choose.c | 2 +- window-copy.c | 2 +- 22 files changed, 28 insertions(+), 36 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 45b05b09..dd953ebd 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -93,7 +93,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (cflag != NULL) { - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); cwd = format_expand(ft, cflag); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 707cd09b..75ae9044 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -101,7 +101,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, wp); diff --git a/cmd-display-message.c b/cmd-display-message.c index 1f9f40fa..2fb76caa 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -88,7 +88,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, s, wl, wp); msg = format_expand_time(ft, template, time(NULL)); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 47f259e7..9907a764 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -86,7 +86,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = NULL; } - ft = format_create(); + ft = format_create(0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index a0036032..a95cc336 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -54,7 +54,7 @@ cmd_list_buffers_exec(struct cmd *self, struct cmd_q *cmdq) pb = NULL; while ((pb = paste_walk(pb)) != NULL) { - ft = format_create(); + ft = format_create(0); format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 372b5283..2bde6b32 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -69,7 +69,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session == NULL || (s != NULL && s != c->session)) continue; - ft = format_create(); + ft = format_create(0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 0af391c5..14ee7064 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -125,7 +125,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { - ft = format_create(); + ft = format_create(0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 49ef9467..099b4f2e 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -61,7 +61,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmd_q *cmdq) n = 0; RB_FOREACH(s, sessions, &sessions) { - ft = format_create(); + ft = format_create(0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 3f6b2a4c..958380e2 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -105,7 +105,7 @@ cmd_list_windows_session( n = 0; RB_FOREACH(wl, winlinks, &s->windows) { - ft = format_create(); + ft = format_create(0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index 65dc6cf5..52e24ae6 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -119,7 +119,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Get the new session working directory. */ to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); @@ -283,7 +283,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); diff --git a/cmd-new-window.c b/cmd-new-window.c index 24204746..0865a9bc 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -94,7 +94,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); cwd = to_free = format_expand(ft, args_get(args, 'c')); @@ -143,7 +143,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, NULL); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 87c802b9..1577c252 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -89,7 +89,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) } /* Expand the command. */ - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, s, wl, wp); cmd = format_expand_time(ft, args->argv[0], time(NULL)); format_free(ft); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index def3ef01..a381fb91 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -100,7 +100,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = NULL; } - ft = format_create(); + ft = format_create(0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-split-window.c b/cmd-split-window.c index fb766d3b..859e1559 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -88,7 +88,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); @@ -165,7 +165,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; - ft = format_create(); + ft = format_create(0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, new_wp); diff --git a/control-notify.c b/control-notify.c index db3648a4..7036f6b5 100644 --- a/control-notify.c +++ b/control-notify.c @@ -88,7 +88,7 @@ control_notify_window_layout_changed(struct window *w) if (w->layout_root == NULL) continue; - ft = format_create(); + ft = format_create(0); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { format_defaults(ft, c, NULL, wl, NULL); diff --git a/format.c b/format.c index bafd8ce6..61a911ed 100644 --- a/format.c +++ b/format.c @@ -465,14 +465,7 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) /* Create a new tree. */ struct format_tree * -format_create(void) -{ - return (format_create_flags(0)); -} - -/* Create a new tree for the status line. */ -struct format_tree * -format_create_flags(int flags) +format_create(int flags) { struct format_tree *ft; diff --git a/names.c b/names.c index 7d956db3..268403e7 100644 --- a/names.c +++ b/names.c @@ -118,7 +118,7 @@ format_window_name(struct window *w) struct format_tree *ft; char *fmt, *name; - ft = format_create(); + ft = format_create(0); format_defaults_window(ft, w); format_defaults_pane(ft, w->active); diff --git a/server-client.c b/server-client.c index c948e980..92ce024d 100644 --- a/server-client.c +++ b/server-client.c @@ -933,7 +933,7 @@ server_client_set_title(struct client *c) template = options_get_string(s->options, "set-titles-string"); - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, NULL, NULL, NULL); title = format_expand_time(ft, template, time(NULL)); diff --git a/status.c b/status.c index d05376c0..5539fa31 100644 --- a/status.c +++ b/status.c @@ -500,9 +500,9 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) return (xstrdup("")); if (c->flags & CLIENT_STATUSFORCE) - ft = format_create_flags(FORMAT_STATUS|FORMAT_FORCE); + ft = format_create(FORMAT_STATUS|FORMAT_FORCE); else - ft = format_create_flags(FORMAT_STATUS); + ft = format_create(FORMAT_STATUS); format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand_time(ft, fmt, t); @@ -661,7 +661,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, int keys; time_t t; - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); @@ -722,7 +722,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) struct format_tree *ft; time_t t; - ft = format_create(); + ft = format_create(0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); diff --git a/tmux.h b/tmux.h index 7757f12f..5df72052 100644 --- a/tmux.h +++ b/tmux.h @@ -1492,8 +1492,7 @@ char *paste_make_sample(struct paste_buffer *); #define FORMAT_STATUS 0x1 #define FORMAT_FORCE 0x2 struct format_tree; -struct format_tree *format_create(void); -struct format_tree *format_create_flags(int); +struct format_tree *format_create(int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); diff --git a/window-choose.c b/window-choose.c index cab3e5d8..862531fb 100644 --- a/window-choose.c +++ b/window-choose.c @@ -186,7 +186,7 @@ window_choose_data_create(int type, struct client *c, struct session *s) wcd = xmalloc(sizeof *wcd); wcd->type = type; - wcd->ft = format_create(); + wcd->ft = format_create(0); wcd->ft_template = NULL; wcd->command = NULL; diff --git a/window-copy.c b/window-copy.c index 37025302..eb17460d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1480,7 +1480,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, if (buf == NULL) return; - ft = format_create(); + ft = format_create(0); format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); From a988c36ccb7d411d995edfa9dbdb7682295d62a0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 8 Dec 2015 08:46:06 +0000 Subject: [PATCH 478/703] Add to .mailmap, and sort. --- .mailmap | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.mailmap b/.mailmap index 847494ba..b4956775 100644 --- a/.mailmap +++ b/.mailmap @@ -1,10 +1,9 @@ Bob Beck beck -Claudio Jeker claudio +Claudio Jeker claudio Igor Sobrado sobrado Ingo Schwarze schwarze Jacek Masiulaniec jacekm Jason McIntyre jmc -Jason McIntyre jcm Joel Sing jsing Jonathan Gray jsg Kenneth R Westerback krw @@ -12,6 +11,7 @@ Marc Espie espie Matthew Dempsky matthew Matthias Kilian kili Matthieu Herrb matthieu +Michael McConville mmcc Miod Vallat miod Nicholas Marriott Nicholas Marriott Nicholas Marriott nicm @@ -25,12 +25,12 @@ Sebastian Benoit benno Stefan Sperling stsp Stuart Henderson sthen Ted Unangst tedu -Theo de Raadt deraadt Theo de Raadt Theo Deraadt +Theo de Raadt deraadt Thomas Adam Thomas -Thomas Adam n6tadam Thomas Adam Thomas Adam -Tim van der Molen tim +Thomas Adam n6tadam +Tim van der Molen tim Tobias Stoeckmann tobias Todd C Miller millert William Yodlowsky william From 01831da5f5dd2a1b7622ad421a757cacedbdfc59 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 12:27:36 +0000 Subject: [PATCH 479/703] Add cmdq as an argument to format_create and add a format for the command name (will also be used for more later). --- cmd-attach-session.c | 2 +- cmd-break-pane.c | 2 +- cmd-display-message.c | 2 +- cmd-if-shell.c | 2 +- cmd-list-buffers.c | 2 +- cmd-list-clients.c | 2 +- cmd-list-panes.c | 2 +- cmd-list-sessions.c | 2 +- cmd-list-windows.c | 2 +- cmd-new-session.c | 4 ++-- cmd-new-window.c | 4 ++-- cmd-pipe-pane.c | 2 +- cmd-run-shell.c | 2 +- cmd-split-window.c | 4 ++-- control-notify.c | 2 +- format.c | 5 ++++- names.c | 2 +- server-client.c | 2 +- status.c | 8 ++++---- tmux.1 | 1 + tmux.h | 2 +- window-choose.c | 2 +- window-copy.c | 2 +- 23 files changed, 32 insertions(+), 28 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index dd953ebd..73c82248 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -93,7 +93,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } if (cflag != NULL) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); cwd = format_expand(ft, cflag); diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 75ae9044..98d6ad3d 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -101,7 +101,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, wp); diff --git a/cmd-display-message.c b/cmd-display-message.c index 2fb76caa..7ca8e9c4 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -88,7 +88,7 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, c, s, wl, wp); msg = format_expand_time(ft, template, time(NULL)); diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 9907a764..3345e8ce 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -86,7 +86,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = NULL; } - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index a95cc336..218eb6ff 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -54,7 +54,7 @@ cmd_list_buffers_exec(struct cmd *self, struct cmd_q *cmdq) pb = NULL; while ((pb = paste_walk(pb)) != NULL) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 2bde6b32..2c13d398 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -69,7 +69,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session == NULL || (s != NULL && s != c->session)) continue; - ft = format_create(0); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 14ee7064..23e530e2 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -125,7 +125,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 099b4f2e..fed0c2ee 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -61,7 +61,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmd_q *cmdq) n = 0; RB_FOREACH(s, sessions, &sessions) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 958380e2..81793e10 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -105,7 +105,7 @@ cmd_list_windows_session( n = 0; RB_FOREACH(wl, winlinks, &s->windows) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index 52e24ae6..aee69e12 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -119,7 +119,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Get the new session working directory. */ to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); @@ -283,7 +283,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); diff --git a/cmd-new-window.c b/cmd-new-window.c index 0865a9bc..ddfd6a20 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -94,7 +94,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); cwd = to_free = format_expand(ft, args_get(args, 'c')); @@ -143,7 +143,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, NULL); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 1577c252..79ef7818 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -89,7 +89,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) } /* Expand the command. */ - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, c, s, wl, wp); cmd = format_expand_time(ft, args->argv[0], time(NULL)); format_free(ft); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index a381fb91..fd5d84d4 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -100,7 +100,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) cwd = NULL; } - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); diff --git a/cmd-split-window.c b/cmd-split-window.c index 859e1559..80d9e2b1 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -88,7 +88,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); @@ -165,7 +165,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; - ft = format_create(0); + ft = format_create(cmdq, 0); format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, new_wp); diff --git a/control-notify.c b/control-notify.c index 7036f6b5..a40f8d7c 100644 --- a/control-notify.c +++ b/control-notify.c @@ -88,7 +88,7 @@ control_notify_window_layout_changed(struct window *w) if (w->layout_root == NULL) continue; - ft = format_create(0); + ft = format_create(NULL, 0); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { format_defaults(ft, c, NULL, wl, NULL); diff --git a/format.c b/format.c index 61a911ed..3e666330 100644 --- a/format.c +++ b/format.c @@ -465,7 +465,7 @@ format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) /* Create a new tree. */ struct format_tree * -format_create(int flags) +format_create(struct cmd_q *cmdq, int flags) { struct format_tree *ft; @@ -484,6 +484,9 @@ format_create(int flags) format_add(ft, "socket_path", "%s", socket_path); format_add_tv(ft, "start_time", &start_time); + if (cmdq != NULL && cmdq->cmd != NULL) + format_add(ft, "command_name", "%s", cmdq->cmd->entry->name); + return (ft); } diff --git a/names.c b/names.c index 268403e7..3c25e215 100644 --- a/names.c +++ b/names.c @@ -118,7 +118,7 @@ format_window_name(struct window *w) struct format_tree *ft; char *fmt, *name; - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults_window(ft, w); format_defaults_pane(ft, w->active); diff --git a/server-client.c b/server-client.c index 92ce024d..8b6be5d9 100644 --- a/server-client.c +++ b/server-client.c @@ -933,7 +933,7 @@ server_client_set_title(struct client *c) template = options_get_string(s->options, "set-titles-string"); - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); title = format_expand_time(ft, template, time(NULL)); diff --git a/status.c b/status.c index 5539fa31..b555a9de 100644 --- a/status.c +++ b/status.c @@ -500,9 +500,9 @@ status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) return (xstrdup("")); if (c->flags & CLIENT_STATUSFORCE) - ft = format_create(FORMAT_STATUS|FORMAT_FORCE); + ft = format_create(NULL, FORMAT_STATUS|FORMAT_FORCE); else - ft = format_create(FORMAT_STATUS); + ft = format_create(NULL, FORMAT_STATUS); format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand_time(ft, fmt, t); @@ -661,7 +661,7 @@ status_prompt_set(struct client *c, const char *msg, const char *input, int keys; time_t t; - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); @@ -722,7 +722,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input) struct format_tree *ft; time_t t; - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); diff --git a/tmux.1 b/tmux.1 index cbd01d9e..a0dbd486 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3406,6 +3406,7 @@ The following variables are available, where appropriate: .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports utf8" .It Li "client_width" Ta "" Ta "Width of client" +.It Li "command_name" Ta "" Ta "Name of command in use, if any" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane" .It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" diff --git a/tmux.h b/tmux.h index 5df72052..082c9068 100644 --- a/tmux.h +++ b/tmux.h @@ -1492,7 +1492,7 @@ char *paste_make_sample(struct paste_buffer *); #define FORMAT_STATUS 0x1 #define FORMAT_FORCE 0x2 struct format_tree; -struct format_tree *format_create(int); +struct format_tree *format_create(struct cmd_q *, int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); diff --git a/window-choose.c b/window-choose.c index 862531fb..35170c65 100644 --- a/window-choose.c +++ b/window-choose.c @@ -186,7 +186,7 @@ window_choose_data_create(int type, struct client *c, struct session *s) wcd = xmalloc(sizeof *wcd); wcd->type = type; - wcd->ft = format_create(0); + wcd->ft = format_create(NULL, 0); wcd->ft_template = NULL; wcd->command = NULL; diff --git a/window-copy.c b/window-copy.c index eb17460d..e09a504c 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1480,7 +1480,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess, if (buf == NULL) return; - ft = format_create(0); + ft = format_create(NULL, 0); format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); From d7e11d0af78bacc7722998509ac93be7fcffc7b4 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 12:39:47 +0000 Subject: [PATCH 480/703] Check alerts when session changes, from Patrick Palka. --- cmd-switch-client.c | 1 + server-fn.c | 1 + 2 files changed, 2 insertions(+) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 4c847584..edc0fd69 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -131,6 +131,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) server_check_unattached(); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; + alerts_check_session(s); return (CMD_RETURN_NORMAL); } diff --git a/server-fn.c b/server-fn.c index a22c964d..07ade08c 100644 --- a/server-fn.c +++ b/server-fn.c @@ -389,6 +389,7 @@ server_destroy_session(struct session *s) session_update_activity(s_new, NULL); gettimeofday(&s_new->last_attached_time, NULL); server_redraw_client(c); + alerts_check_session(s_new); } } recalculate_sizes(); From bd5918760ecd1f40a574ccc8a302af869f68c27f Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 15:46:57 +0000 Subject: [PATCH 481/703] We cannot do hooks_find and then hooks_remove because it might have come from the parent (global) tree, instead make it remove by name like options. While here, also tidy up a few bits of options and hooks handling (use RB_FOREACH_SAFE, and a helper function for the free). --- cmd-set-hook.c | 3 +-- hooks.c | 28 +++++++++++++++++++--------- options.c | 42 +++++++++++++++++++++--------------------- tmux.h | 4 ++-- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/cmd-set-hook.c b/cmd-set-hook.c index f35e7a0a..ec58418e 100644 --- a/cmd-set-hook.c +++ b/cmd-set-hook.c @@ -93,8 +93,7 @@ cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) name); return (CMD_RETURN_ERROR); } - if ((hook = hooks_find(hooks, name)) != NULL) - hooks_remove(hooks, hook); + hooks_remove(hooks, name); return (CMD_RETURN_NORMAL); } diff --git a/hooks.c b/hooks.c index 95d6b9d8..6f548d23 100644 --- a/hooks.c +++ b/hooks.c @@ -32,7 +32,8 @@ static int hooks_cmp(struct hook *, struct hook *); RB_PROTOTYPE(hooks_tree, hook, entry, hooks_cmp); RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); -struct hook *hooks_find1(struct hooks *, const char *); +static struct hook *hooks_find1(struct hooks *, const char *); +static void hooks_free1(struct hooks *, struct hook *); static int hooks_cmp(struct hook *hook1, struct hook *hook2) @@ -51,13 +52,22 @@ hooks_create(struct hooks *parent) return (hooks); } +static void +hooks_free1(struct hooks *hooks, struct hook *hook) +{ + RB_REMOVE(hooks_tree, &hooks->tree, hook); + cmd_list_free(hook->cmdlist); + free((char *)hook->name); + free(hook); +} + void hooks_free(struct hooks *hooks) { struct hook *hook, *hook1; RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) - hooks_remove(hooks, hook); + hooks_free1(hooks, hook); free(hooks); } @@ -79,7 +89,7 @@ hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) struct hook *hook; if ((hook = hooks_find1(hooks, name)) != NULL) - hooks_remove(hooks, hook); + hooks_free1(hooks, hook); hook = xcalloc(1, sizeof *hook); hook->name = xstrdup(name); @@ -89,15 +99,15 @@ hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) } void -hooks_remove(struct hooks *hooks, struct hook *hook) +hooks_remove(struct hooks *hooks, const char *name) { - RB_REMOVE(hooks_tree, &hooks->tree, hook); - cmd_list_free(hook->cmdlist); - free((char *) hook->name); - free(hook); + struct hook *hook; + + if ((hook = hooks_find1(hooks, name)) != NULL) + hooks_free1(hooks, hook); } -struct hook * +static struct hook * hooks_find1(struct hooks *hooks, const char *name) { struct hook hook; diff --git a/options.c b/options.c index 5f958d23..02f0f957 100644 --- a/options.c +++ b/options.c @@ -34,11 +34,13 @@ struct options { struct options *parent; }; -int options_cmp(struct options_entry *, struct options_entry *); +static int options_cmp(struct options_entry *, struct options_entry *); RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); RB_GENERATE(options_tree, options_entry, entry, options_cmp); -int +static void options_free1(struct options *, struct options_entry *); + +static int options_cmp(struct options_entry *o1, struct options_entry *o2) { return (strcmp(o1->name, o2->name)); @@ -55,19 +57,23 @@ options_create(struct options *parent) return (oo); } +static void +options_free1(struct options *oo, struct options_entry *o) +{ + RB_REMOVE(options_tree, &oo->tree, o); + free((char *)o->name); + if (o->type == OPTIONS_STRING) + free(o->str); + free(o); +} + void options_free(struct options *oo) { - struct options_entry *o; + struct options_entry *o, *o1; - while (!RB_EMPTY(&oo->tree)) { - o = RB_ROOT(&oo->tree); - RB_REMOVE(options_tree, &oo->tree, o); - free(o->name); - if (o->type == OPTIONS_STRING) - free(o->str); - free(o); - } + RB_FOREACH_SAFE (o, options_tree, &oo->tree, o1) + options_free1(oo, o); free(oo); } @@ -88,7 +94,7 @@ options_find1(struct options *oo, const char *name) { struct options_entry p; - p.name = (char *) name; + p.name = (char *)name; return (RB_FIND(options_tree, &oo->tree, &p)); } @@ -97,7 +103,7 @@ options_find(struct options *oo, const char *name) { struct options_entry *o, p; - p.name = (char *) name; + p.name = (char *)name; o = RB_FIND(options_tree, &oo->tree, &p); while (o == NULL) { oo = oo->parent; @@ -113,14 +119,8 @@ options_remove(struct options *oo, const char *name) { struct options_entry *o; - if ((o = options_find1(oo, name)) == NULL) - return; - - RB_REMOVE(options_tree, &oo->tree, o); - free(o->name); - if (o->type == OPTIONS_STRING) - free(o->str); - free(o); + if ((o = options_find1(oo, name)) != NULL) + options_free1(oo, o); } struct options_entry * diff --git a/tmux.h b/tmux.h index 082c9068..a20b96c5 100644 --- a/tmux.h +++ b/tmux.h @@ -703,7 +703,7 @@ struct hook { /* Option data structures. */ struct options_entry { - char *name; + const char *name; enum { OPTIONS_STRING, @@ -1514,7 +1514,7 @@ struct hook *hooks_first(struct hooks *); struct hook *hooks_next(struct hook *); void hooks_add(struct hooks *, const char *, struct cmd_list *); void hooks_copy(struct hooks *, struct hooks *); -void hooks_remove(struct hooks *, struct hook *); +void hooks_remove(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *); void hooks_run(struct hooks *, const char *, struct client *); From f2be3ad46fc37e5689d836f994fa79968d047133 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 16:27:01 +0000 Subject: [PATCH 482/703] Mention {src,dst}-{window,pane} where we define target-{window,pane}. --- tmux.1 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index a0dbd486..115050a6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -416,6 +416,10 @@ If a session is omitted, the current session is used if available; if no current session is available, the most recently used is chosen. .Pp .Ar target-window +(or +.Ar src-window +or +.Ar dst-window ) specifies a window in the form .Em session Ns \&: Ns Em window . .Em session @@ -471,8 +475,11 @@ Each has a single-character alternative form. .El .Pp .Ar target-pane -may be a -pane ID or takes a similar form to +(or +.Ar src-pane +or +.Ar dst-pane ) +may be a pane ID or takes a similar form to .Ar target-window but with the optional addition of a period followed by a pane index or pane ID, for example: From 88bc8f3528b973adb17f541e21aa415923a10f1e Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 11 Dec 2015 16:37:21 +0000 Subject: [PATCH 483/703] Style nits and line wrapping of function declarations. --- cmd-list-windows.c | 4 ++-- layout.c | 22 +++++++++++----------- screen-write.c | 12 ++++++------ status.c | 4 ++-- tmux.h | 38 +++++++++++++++++++------------------- tty-term.c | 8 ++++---- tty.c | 12 ++++++------ window-choose.c | 32 ++++++++++++++++---------------- 8 files changed, 66 insertions(+), 66 deletions(-) diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 81793e10..1eaee2d7 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -81,8 +81,8 @@ cmd_list_windows_server(struct cmd *self, struct cmd_q *cmdq) } void -cmd_list_windows_session( - struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) +cmd_list_windows_session(struct cmd *self, struct session *s, + struct cmd_q *cmdq, int type) { struct args *args = self->args; struct winlink *wl; diff --git a/layout.c b/layout.c index 266d1f39..c448814a 100644 --- a/layout.c +++ b/layout.c @@ -85,9 +85,9 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) { struct layout_cell *lcchild; - log_debug( - "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc, - lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy); + log_debug("%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, + " ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, + lc->sy); switch (lc->type) { case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: @@ -100,8 +100,8 @@ layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) } void -layout_set_size( - struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff) +layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, + u_int yoff) { lc->sx = sx; lc->sy = sy; @@ -521,8 +521,8 @@ layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) /* Helper function to grow pane. */ int -layout_resize_pane_grow( - struct layout_cell *lc, enum layout_type type, int needed) +layout_resize_pane_grow(struct layout_cell *lc, enum layout_type type, + int needed) { struct layout_cell *lcadd, *lcremove; u_int size; @@ -562,8 +562,8 @@ layout_resize_pane_grow( /* Helper function to shrink pane. */ int -layout_resize_pane_shrink( - struct layout_cell *lc, enum layout_type type, int needed) +layout_resize_pane_shrink(struct layout_cell *lc, enum layout_type type, + int needed) { struct layout_cell *lcadd, *lcremove; u_int size; @@ -605,8 +605,8 @@ layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) * split. This must be followed by layout_assign_pane before much else happens! **/ struct layout_cell * -layout_split_pane( - struct window_pane *wp, enum layout_type type, int size, int insert_before) +layout_split_pane(struct window_pane *wp, enum layout_type type, int size, + int insert_before) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; u_int sx, sy, xoff, yoff, size1, size2; diff --git a/screen-write.c b/screen-write.c index 53067efe..e53d3799 100644 --- a/screen-write.c +++ b/screen-write.c @@ -767,8 +767,8 @@ screen_write_reverseindex(struct screen_write_ctx *ctx) /* Set scroll region. */ void -screen_write_scrollregion( - struct screen_write_ctx *ctx, u_int rupper, u_int rlower) +screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, + u_int rlower) { struct screen *s = ctx->s; @@ -874,16 +874,16 @@ screen_write_clearscreen(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; + u_int sx = screen_size_x(s); + u_int sy = screen_size_y(s); screen_write_initctx(ctx, &ttyctx, 0); /* Scroll into history if it is enabled. */ if (s->grid->flags & GRID_HISTORY) grid_view_clear_history(s->grid); - else { - grid_view_clear( - s->grid, 0, 0, screen_size_x(s), screen_size_y(s)); - } + else + grid_view_clear(s->grid, 0, 0, sx, sy); tty_write(tty_cmd_clearscreen, &ttyctx); } diff --git a/status.c b/status.c index b555a9de..ca78bd53 100644 --- a/status.c +++ b/status.c @@ -1124,8 +1124,8 @@ status_prompt_key(struct client *c, key_code key) } if (c->prompt_flags & PROMPT_SINGLE) { - if (c->prompt_callbackfn( - c->prompt_data, c->prompt_buffer) == 0) + if (c->prompt_callbackfn(c->prompt_data, + c->prompt_buffer) == 0) status_prompt_clear(c); } diff --git a/tmux.h b/tmux.h index a20b96c5..60d16fe3 100644 --- a/tmux.h +++ b/tmux.h @@ -1664,10 +1664,10 @@ void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); -const char *tty_term_string2( - struct tty_term *, enum tty_code_code, int, int); -const char *tty_term_ptr1( - struct tty_term *, enum tty_code_code, const void *); +const char *tty_term_string2(struct tty_term *, enum tty_code_code, int, + int); +const char *tty_term_ptr1(struct tty_term *, enum tty_code_code, + const void *); const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const void *, const void *); int tty_term_number(struct tty_term *, enum tty_code_code); @@ -1904,8 +1904,8 @@ void grid_move_lines(struct grid *, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); char *grid_string_cells(struct grid *, u_int, u_int, u_int, struct grid_cell **, int, int, int); -void grid_duplicate_lines( - struct grid *, u_int, struct grid *, u_int, u_int); +void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int, + u_int); u_int grid_reflow(struct grid *, struct grid *, u_int); /* grid-view.c */ @@ -2056,14 +2056,14 @@ void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); void window_pane_alternate_off(struct window_pane *, struct grid_cell *, int); -int window_pane_set_mode( - struct window_pane *, const struct window_mode *); +int window_pane_set_mode(struct window_pane *, + const struct window_mode *); void window_pane_reset_mode(struct window_pane *); void window_pane_key(struct window_pane *, struct client *, struct session *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); -char *window_pane_search( - struct window_pane *, const char *, u_int *); +char *window_pane_search(struct window_pane *, const char *, + u_int *); char *window_printable_flags(struct session *, struct winlink *); struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); @@ -2079,17 +2079,17 @@ u_int layout_count_cells(struct layout_cell *); struct layout_cell *layout_create_cell(struct layout_cell *); void layout_free_cell(struct layout_cell *); void layout_print_cell(struct layout_cell *, const char *, u_int); -void layout_destroy_cell(struct layout_cell *, struct layout_cell **); -void layout_set_size( - struct layout_cell *, u_int, u_int, u_int, u_int); -void layout_make_leaf( - struct layout_cell *, struct window_pane *); +void layout_destroy_cell(struct layout_cell *, + struct layout_cell **); +void layout_set_size(struct layout_cell *, u_int, u_int, u_int, + u_int); +void layout_make_leaf(struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); void layout_fix_offsets(struct layout_cell *); void layout_fix_panes(struct window *, u_int, u_int); u_int layout_resize_check(struct layout_cell *, enum layout_type); -void layout_resize_adjust( - struct layout_cell *, enum layout_type, int); +void layout_resize_adjust(struct layout_cell *, enum layout_type, + int); void layout_init(struct window *, struct window_pane *); void layout_free(struct window *); void layout_resize(struct window *, u_int, u_int); @@ -2098,8 +2098,8 @@ void layout_resize_pane(struct window_pane *, enum layout_type, void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); void layout_assign_pane(struct layout_cell *, struct window_pane *); -struct layout_cell *layout_split_pane( - struct window_pane *, enum layout_type, int, int); +struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, + int, int); void layout_close_pane(struct window_pane *); /* layout-custom.c */ diff --git a/tty-term.c b/tty-term.c index 5f2e1a94..27c904a4 100644 --- a/tty-term.c +++ b/tty-term.c @@ -406,12 +406,12 @@ tty_term_find(char *name, int fd, char **cause) if (setupterm(name, fd, &error) != OK) { switch (error) { case 1: - xasprintf( - cause, "can't use hardcopy terminal: %s", name); + xasprintf(cause, "can't use hardcopy terminal: %s", + name); break; case 0: - xasprintf( - cause, "missing or unsuitable terminal: %s", name); + xasprintf(cause, "missing or unsuitable terminal: %s", + name); break; case -1: xasprintf(cause, "can't find terminfo database"); diff --git a/tty.c b/tty.c index 42be6d9f..da5eac4d 100644 --- a/tty.c +++ b/tty.c @@ -48,8 +48,8 @@ void tty_colours_bg(struct tty *, const struct grid_cell *); int tty_large_region(struct tty *, const struct tty_ctx *); int tty_fake_bce(const struct tty *, const struct window_pane *); void tty_redraw_region(struct tty *, const struct tty_ctx *); -void tty_emulate_repeat( - struct tty *, enum tty_code_code, enum tty_code_code, u_int); +void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, + u_int); void tty_repeat_space(struct tty *, u_int); void tty_cell(struct tty *, const struct grid_cell *, const struct window_pane *); @@ -161,8 +161,8 @@ tty_open(struct tty *tty, char **cause) tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_TIMER); - tty->event = bufferevent_new( - tty->fd, tty_read_callback, NULL, tty_error_callback, tty); + tty->event = bufferevent_new(tty->fd, tty_read_callback, NULL, + tty_error_callback, tty); tty_start_tty(tty); @@ -1188,8 +1188,8 @@ tty_reset(struct tty *tty) /* Set region inside pane. */ void -tty_region_pane( - struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) +tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, + u_int rlower) { tty_region(tty, ctx->yoff + rupper, ctx->yoff + rlower); } diff --git a/window-choose.c b/window-choose.c index 35170c65..fdfc47a0 100644 --- a/window-choose.c +++ b/window-choose.c @@ -35,11 +35,11 @@ void window_choose_default_callback(struct window_choose_data *); struct window_choose_mode_item *window_choose_get_item(struct window_pane *, key_code, struct mouse_event *); -void window_choose_fire_callback( - struct window_pane *, struct window_choose_data *); +void window_choose_fire_callback(struct window_pane *, + struct window_choose_data *); void window_choose_redraw_screen(struct window_pane *); -void window_choose_write_line( - struct window_pane *, struct screen_write_ctx *, u_int); +void window_choose_write_line(struct window_pane *, + struct screen_write_ctx *, u_int); void window_choose_scroll_up(struct window_pane *); void window_choose_scroll_down(struct window_pane *); @@ -299,8 +299,8 @@ window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) } void -window_choose_fire_callback( - struct window_pane *wp, struct window_choose_data *wcd) +window_choose_fire_callback(struct window_pane *wp, + struct window_choose_data *wcd) { struct window_choose_mode_data *data = wp->modedata; @@ -614,10 +614,10 @@ window_choose_key(struct window_pane *wp, __unused struct client *c, window_choose_scroll_up(wp); else { screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, data->selected - data->top); - window_choose_write_line( - wp, &ctx, data->selected + 1 - data->top); + window_choose_write_line(wp, &ctx, + data->selected - data->top); + window_choose_write_line(wp, &ctx, + data->selected + 1 - data->top); screen_write_stop(&ctx); } break; @@ -634,10 +634,10 @@ window_choose_key(struct window_pane *wp, __unused struct client *c, if (data->selected < data->top + screen_size_y(s)) { screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, data->selected - data->top); - window_choose_write_line( - wp, &ctx, data->selected - 1 - data->top); + window_choose_write_line(wp, &ctx, + data->selected - data->top); + window_choose_write_line(wp, &ctx, + data->selected - 1 - data->top); screen_write_stop(&ctx); } else window_choose_scroll_down(wp); @@ -649,8 +649,8 @@ window_choose_key(struct window_pane *wp, __unused struct client *c, data->selected--; window_choose_scroll_up(wp); screen_write_start(&ctx, wp, NULL); - window_choose_write_line( - wp, &ctx, screen_size_y(s) - 1); + window_choose_write_line(wp, &ctx, + screen_size_y(s) - 1); screen_write_stop(&ctx); } else window_choose_scroll_up(wp); From 38cc1a1843b370eaeff749802d1d8803b73c4b93 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 11 Dec 2015 19:58:09 +0000 Subject: [PATCH 484/703] Look for ncurses with PKG_CONFIG, and remove libtinfo because it just causes confusion. --- configure.ac | 24 +++++++++++++++++------- tty-term.c | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 6cd859f0..824b4ab2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,5 @@ # configure.ac -# Miscellaneous bits. AC_INIT(tmux, 2.2) AC_CONFIG_AUX_DIR(etc) @@ -143,12 +142,23 @@ if test "x$found_libevent" = xno; then AC_MSG_ERROR("libevent not found") fi -# Look for curses. -AC_SEARCH_LIBS( - setupterm, - [terminfo curses ncurses tinfo], - found_curses=yes, - found_curses=no +# Look for ncurses +PKG_CHECK_MODULES( + LIBNCURSES, + ncurses, + [ + CPPFLAGS="$LIBNCURSES_CFLAGS $CPPFLAGS" + LIBS="$LIBNCURSES_LIBS $LIBS" + found_curses=yes + ], + [ + AC_SEARCH_LIBS( + setupterm, + [ncurses curses terminfo], + found_curses=yes, + found_curses=no + ) + ] ) if test "x$found_curses" = xno; then AC_MSG_ERROR("curses not found") diff --git a/tty-term.c b/tty-term.c index f536859e..21756e51 100644 --- a/tty-term.c +++ b/tty-term.c @@ -18,9 +18,9 @@ #include -#ifdef HAVE_CURSES_H +#if defined(HAVE_CURSES_H) #include -#else +#elif defined(HAVE_NCURSES_H) #include #endif #include From 39cf9c9d31954198ad73e2b6721a92fe782ee56c Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 12 Dec 2015 18:19:00 +0000 Subject: [PATCH 485/703] Allow prefix and prefix2 to be set to None to disable (useful if you would rather bind the prefix in the root table). --- cmd-bind-key.c | 2 +- cmd-send-keys.c | 23 +++++++++++++---------- cmd-set-option.c | 3 ++- cmd-unbind-key.c | 8 ++++---- input-keys.c | 4 ++-- key-string.c | 40 ++++++++++++++++++++++++---------------- server-client.c | 14 +++++++------- tmux.1 | 11 +++++++++++ tmux.h | 1 + tty-keys.c | 8 ++++---- 10 files changed, 69 insertions(+), 45 deletions(-) diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 1867e814..b13409cb 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -62,7 +62,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) } key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE) { + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 73a308ae..1461baa9 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -52,8 +52,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) struct mouse_event *m = &cmdq->item->mouse; struct window_pane *wp; struct session *s; - const u_char *str; - int i; + int i, literal; + const u_char *keystr; key_code key; if (args_has(args, 'M')) { @@ -82,14 +82,17 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) input_reset(wp); for (i = 0; i < args->argc; i++) { - str = args->argv[i]; - - if (!args_has(args, 'l') && - (key = key_string_lookup_string(str)) != KEYC_NONE) { - window_pane_key(wp, NULL, s, key, NULL); - } else { - for (; *str != '\0'; str++) - window_pane_key(wp, NULL, s, *str, NULL); + literal = args_has(args, 'l'); + if (!literal) { + key = key_string_lookup_string(args->argv[i]); + if (key != KEYC_NONE && key != KEYC_UNKNOWN) + window_pane_key(wp, NULL, s, key, NULL); + else + literal = 1; + } + if (literal) { + for (keystr = args->argv[i]; *keystr != '\0'; keystr++) + window_pane_key(wp, NULL, s, *keystr, NULL); } } diff --git a/cmd-set-option.c b/cmd-set-option.c index 7de91aa2..e2a4768c 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -396,7 +396,8 @@ cmd_set_option_key(__unused struct cmd *self, struct cmd_q *cmdq, { key_code key; - if ((key = key_string_lookup_string(value)) == KEYC_NONE) { + key = key_string_lookup_string(value); + if (key == KEYC_UNKNOWN) { cmdq_error(cmdq, "bad key: %s", value); return (NULL); } diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index cec538c0..66a4525e 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -51,7 +51,7 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } key = key_string_lookup_string(args->argv[0]); - if (key == KEYC_NONE) { + if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } @@ -60,13 +60,13 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "key given with -a"); return (CMD_RETURN_ERROR); } - key = KEYC_NONE; + key = KEYC_UNKNOWN; } if (args_has(args, 't')) return (cmd_unbind_key_mode_table(self, cmdq, key)); - if (key == KEYC_NONE) { + if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { key_bindings_remove_table("root"); @@ -109,7 +109,7 @@ cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code key) return (CMD_RETURN_ERROR); } - if (key == KEYC_NONE) { + if (key == KEYC_UNKNOWN) { while (!RB_EMPTY(mtab->tree)) { mbind = RB_ROOT(mtab->tree); RB_REMOVE(mode_key_tree, mtab->tree, mbind); diff --git a/input-keys.c b/input-keys.c index 2915cb45..3bc1f812 100644 --- a/input-keys.c +++ b/input-keys.c @@ -161,14 +161,14 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m) * if necessary. If it is a UTF-8 key, split it and send it. */ justkey = (key & ~KEYC_ESCAPE); - if (key != KEYC_NONE && justkey <= 0x7f) { + if (justkey <= 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; bufferevent_write(wp->event, &ud.data[0], 1); return; } - if (key != KEYC_NONE && justkey > 0x7f && justkey < KEYC_BASE) { + if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return; if (key & KEYC_ESCAPE) diff --git a/key-string.c b/key-string.c index 9a44892d..1ff3ca30 100644 --- a/key-string.c +++ b/key-string.c @@ -22,8 +22,8 @@ #include "tmux.h" -key_code key_string_search_table(const char *); -key_code key_string_get_modifiers(const char **); +static key_code key_string_search_table(const char *); +static key_code key_string_get_modifiers(const char **); const struct { const char *string; @@ -98,7 +98,7 @@ const struct { }; /* Find key string in table. */ -key_code +static key_code key_string_search_table(const char *string) { u_int i; @@ -107,11 +107,11 @@ key_string_search_table(const char *string) if (strcasecmp(string, key_string_table[i].string) == 0) return (key_string_table[i].key); } - return (KEYC_NONE); + return (KEYC_UNKNOWN); } /* Find modifiers. */ -key_code +static key_code key_string_get_modifiers(const char **string) { key_code modifiers; @@ -150,10 +150,14 @@ key_string_lookup_string(const char *string) u_int i; enum utf8_state more; + /* Is this no key? */ + if (strcasecmp(string, "None") == 0) + return (KEYC_NONE); + /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%hx%n", &u, &size) != 1 || size > 4) - return (KEYC_NONE); + return (KEYC_UNKNOWN); return (u); } @@ -165,30 +169,30 @@ key_string_lookup_string(const char *string) } modifiers |= key_string_get_modifiers(&string); if (string[0] == '\0') - return (KEYC_NONE); + return (KEYC_UNKNOWN); /* Is this a standard ASCII key? */ if (string[1] == '\0' && (u_char)string[0] <= 127) { key = (u_char)string[0]; if (key < 32 || key == 127) - return (KEYC_NONE); + return (KEYC_UNKNOWN); } else { /* Try as a UTF-8 key. */ if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { if (strlen(string) != ud.size) - return (KEYC_NONE); + return (KEYC_UNKNOWN); for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); if (more != UTF8_DONE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); key = utf8_combine(&ud); return (key | modifiers); } /* Otherwise look the key up in the table. */ key = key_string_search_table(string); - if (key == KEYC_NONE) - return (KEYC_NONE); + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); } /* Convert the standard control keys. */ @@ -202,7 +206,7 @@ key_string_lookup_string(const char *string) else if (key == 63) key = KEYC_BSPACE; else - return (KEYC_NONE); + return (KEYC_UNKNOWN); modifiers &= ~KEYC_CTRL; } @@ -222,9 +226,13 @@ key_string_lookup_key(key_code key) /* Handle no key. */ if (key == KEYC_NONE) - return (""); + return ("None"); + + /* Handle special keys. */ + if (key == KEYC_UNKNOWN) + return ("Unknown"); if (key == KEYC_MOUSE) - return (""); + return ("Mouse"); /* * Special case: display C-@ as C-Space. Could do this below in @@ -265,7 +273,7 @@ key_string_lookup_key(key_code key) /* Invalid keys are errors. */ if (key == 127 || key > 255) { - snprintf(out, sizeof out, "", key); + snprintf(out, sizeof out, "Invalid#%llx", key); return (out); } diff --git a/server-client.c b/server-client.c index 8b6be5d9..ed0eefdd 100644 --- a/server-client.c +++ b/server-client.c @@ -309,7 +309,7 @@ server_client_check_mouse(struct client *c) log_debug("down at %u,%u", x, y); } if (type == NOTYPE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); /* Always save the session. */ m->s = s->id; @@ -319,7 +319,7 @@ server_client_check_mouse(struct client *c) if (m->statusat != -1 && y == (u_int)m->statusat) { w = status_get_window_at(c, x); if (w == NULL) - return (KEYC_NONE); + return (KEYC_UNKNOWN); m->w = w->id; where = STATUS; } else @@ -352,7 +352,7 @@ server_client_check_mouse(struct client *c) } } if (where == NOWHERE) - return (KEYC_NONE); + return (KEYC_UNKNOWN); m->wp = wp->id; m->w = wp->window->id; } else @@ -371,7 +371,7 @@ server_client_check_mouse(struct client *c) } /* Convert to a key binding. */ - key = KEYC_NONE; + key = KEYC_UNKNOWN; switch (type) { case NOTYPE: break; @@ -483,8 +483,8 @@ server_client_check_mouse(struct client *c) } break; } - if (key == KEYC_NONE) - return (KEYC_NONE); + if (key == KEYC_UNKNOWN) + return (KEYC_UNKNOWN); /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) @@ -572,7 +572,7 @@ server_client_handle_key(struct client *c, key_code key) if (c->flags & CLIENT_READONLY) return; key = server_client_check_mouse(c); - if (key == KEYC_NONE) + if (key == KEYC_UNKNOWN) return; m->valid = 1; diff --git a/tmux.1 b/tmux.1 index 115050a6..b02237f1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2664,8 +2664,19 @@ See the section for details. .It Ic prefix Ar key Set the key accepted as a prefix key. +In addition to the standard keys described under +.Sx KEY BINDINGS , +.Ic prefix +can be set to the special key +.Ql None +to set no prefix. .It Ic prefix2 Ar key Set a secondary key accepted as a prefix key. +Like +.Ic prefix , +.Ic prefix2 +can be set to +.Ql None . .It Xo Ic renumber-windows .Op Ic on | off .Xc diff --git a/tmux.h b/tmux.h index 60d16fe3..d5e5d822 100644 --- a/tmux.h +++ b/tmux.h @@ -85,6 +85,7 @@ struct tmuxproc; /* Special key codes. */ #define KEYC_NONE 0xffff00000000ULL +#define KEYC_UNKNOWN 0xfffe00000000ULL #define KEYC_BASE 0x100000000000ULL /* Key modifier bits. */ diff --git a/tty-keys.c b/tty-keys.c index b5e36f8e..86839a17 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -344,7 +344,7 @@ tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) if (tk == NULL) { tk = *tkp = xcalloc(1, sizeof *tk); tk->ch = *s; - tk->key = KEYC_NONE; + tk->key = KEYC_UNKNOWN; } /* Find the next entry. */ @@ -444,7 +444,7 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) (*size)++; /* At the end of the string, return the current node. */ - if (len == 0 || (tk->next == NULL && tk->key != KEYC_NONE)) + if (len == 0 || (tk->next == NULL && tk->key != KEYC_UNKNOWN)) return (tk); /* Move into the next tree for the following character. */ @@ -534,7 +534,7 @@ first_key: if (tk->next != NULL) goto partial_key; key = tk->key; - if (key != KEYC_NONE) + if (key != KEYC_UNKNOWN) key |= KEYC_ESCAPE; goto complete_key; } @@ -620,7 +620,7 @@ complete_key: } /* Fire the key. */ - if (key != KEYC_NONE) + if (key != KEYC_UNKNOWN) server_client_handle_key(tty->client, key); return (1); From 6a50cf89b40d472cd1f8b439913ca4caddbfd001 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 12 Dec 2015 18:28:47 +0000 Subject: [PATCH 486/703] Return after changing key table. --- cmd-switch-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index edc0fd69..85cbce78 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -65,6 +65,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) table->references++; key_bindings_unref_table(c->keytable); c->keytable = table; + return (CMD_RETURN_NORMAL); } tflag = args_get(args, 't'); From 5ed17e84faed0a7655ec1eb3de291b60839dcb12 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 12 Dec 2015 18:32:24 +0000 Subject: [PATCH 487/703] Add key-table option to set the default key table for a session, allows different key bindings for different sessions and a few other things. --- cmd-attach-session.c | 2 ++ cmd-new-session.c | 1 + cmd-set-option.c | 4 ++++ cmd-switch-client.c | 1 + format.c | 4 +++- options-table.c | 6 ++++++ server-client.c | 36 +++++++++++++++++++++++++++--------- server-fn.c | 1 + tmux.1 | 5 +++++ tmux.h | 2 ++ 10 files changed, 52 insertions(+), 10 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 73c82248..00ced9df 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -119,6 +119,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); @@ -150,6 +151,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, } c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); diff --git a/cmd-new-session.c b/cmd-new-session.c index aee69e12..341399be 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -262,6 +262,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } else if (c->session != NULL) c->last_session = c->session; c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); diff --git a/cmd-set-option.c b/cmd-set-option.c index e2a4768c..daee62ac 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -183,6 +183,10 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) w->active->flags |= PANE_CHANGED; } } + if (strcmp(oe->name, "key-table") == 0) { + TAILQ_FOREACH(c, &clients, entry) + server_client_set_key_table(c, NULL); + } if (strcmp(oe->name, "status") == 0 || strcmp(oe->name, "status-interval") == 0) status_timer_start_all(); diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 85cbce78..4746b15a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -124,6 +124,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; + server_client_set_key_table(c, NULL); status_timer_start(c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); diff --git a/format.c b/format.c index 3e666330..79445936 100644 --- a/format.c +++ b/format.c @@ -1035,6 +1035,7 @@ void format_defaults_client(struct format_tree *ft, struct client *c) { struct session *s; + const char *name; if (ft->s == NULL) ft->s = c->session; @@ -1052,7 +1053,8 @@ format_defaults_client(struct format_tree *ft, struct client *c) format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); - if (strcmp(c->keytable->name, "root") == 0) + name = server_client_get_key_table(c); + if (strcmp(c->keytable->name, name) == 0) format_add(ft, "client_prefix", "%d", 0); else format_add(ft, "client_prefix", "%d", 1); diff --git a/options-table.c b/options-table.c index 0dab0c0e..63e7ab61 100644 --- a/options-table.c +++ b/options-table.c @@ -211,6 +211,12 @@ const struct options_table_entry options_table[] = { .default_num = 2000 }, + { .name = "key-table", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "root" + }, + { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, diff --git a/server-client.c b/server-client.c index ed0eefdd..c2b43f38 100644 --- a/server-client.c +++ b/server-client.c @@ -32,7 +32,6 @@ #include "tmux.h" -void server_client_key_table(struct client *, const char *); void server_client_free(int, short, void *); void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); @@ -72,13 +71,32 @@ server_client_check_nested(struct client *c) /* Set client key table. */ void -server_client_key_table(struct client *c, const char *name) +server_client_set_key_table(struct client *c, const char *name) { + if (name == NULL) + name = server_client_get_key_table(c); + key_bindings_unref_table(c->keytable); c->keytable = key_bindings_get_table(name, 1); c->keytable->references++; } +/* Get default key table. */ +const char * +server_client_get_key_table(struct client *c) +{ + struct session *s = c->session; + const char *name; + + if (s == NULL) + return ("root"); + + name = options_get_string(s->options, "key-table"); + if (*name == '\0') + return ("root"); + return (name); +} + /* Create a new client. */ void server_client_create(int fd) @@ -598,7 +616,7 @@ retry: * again in the root table. */ if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; @@ -625,7 +643,7 @@ retry: evtimer_add(&c->repeat_timer, &tv); } else { c->flags &= ~CLIENT_REPEAT; - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); } server_status_client(c); @@ -640,15 +658,15 @@ retry: * root table and try again. */ if (c->flags & CLIENT_REPEAT) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; } /* If no match and we're not in the root table, that's it. */ - if (strcmp(c->keytable->name, "root") != 0) { - server_client_key_table(c, "root"); + if (strcmp(c->keytable->name, server_client_get_key_table(c)) != 0) { + server_client_set_key_table(c, NULL); server_status_client(c); return; } @@ -659,7 +677,7 @@ retry: */ if (key == (key_code)options_get_number(s->options, "prefix") || key == (key_code)options_get_number(s->options, "prefix2")) { - server_client_key_table(c, "prefix"); + server_client_set_key_table(c, "prefix"); server_status_client(c); return; } @@ -834,7 +852,7 @@ server_client_repeat_timer(__unused int fd, __unused short events, void *data) struct client *c = data; if (c->flags & CLIENT_REPEAT) { - server_client_key_table(c, "root"); + server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); } diff --git a/server-fn.c b/server-fn.c index 07ade08c..a4c2dfea 100644 --- a/server-fn.c +++ b/server-fn.c @@ -384,6 +384,7 @@ server_destroy_session(struct session *s) } else { c->last_session = NULL; c->session = s_new; + server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s_new, NULL); diff --git a/tmux.1 b/tmux.1 index b02237f1..39e9bd4b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2572,6 +2572,11 @@ is in milliseconds. Set the maximum number of lines held in window history. This setting applies only to new windows - existing window histories are not resized and retain the limit at the point they were created. +.It Ic key-table Ar key-table +Set the default key table to +.Ar key-table +instead of +.Em root . .It Ic lock-after-time Ar number Lock the session (like the .Ic lock-session diff --git a/tmux.h b/tmux.h index d5e5d822..e10fbcdf 100644 --- a/tmux.h +++ b/tmux.h @@ -1800,6 +1800,8 @@ void server_update_socket(void); void server_add_accept(int); /* server-client.c */ +void server_client_set_key_table(struct client *, const char *); +const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, key_code); void server_client_create(int); From 92f187d1c2d84322860dc595da604260999a52f0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 12 Dec 2015 22:04:25 +0000 Subject: [PATCH 488/703] Need to use pkg-config --static when doing a static build. --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 824b4ab2..82e8a452 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,7 @@ AM_PROG_CC_C_O AC_PROG_CPP AC_PROG_EGREP AC_PROG_INSTALL +PKG_PROG_PKG_CONFIG # Default tmux.conf goes in /etc not ${prefix}/etc. test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc @@ -47,6 +48,7 @@ AC_ARG_ENABLE( ) if test "x$found_static" = xyes; then LDFLAGS="$LDFLAGS -static" + PKG_CONFIG="pkg-config --static" fi # Is this gcc? From 4a4daf13031673870341c68b990e20c314140118 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 14:32:38 +0000 Subject: [PATCH 489/703] Instead of every command resolving the target (-t or -s) itself, prepare the state (client, session, winlink, pane) for it it before entering the command. Each command provides some flags that tell the prepare step what it is expecting. This is a requirement for having hooks on commands (for example, if you hook "select-window -t1:2", the hook command should to operate on window 1:2 not whatever it thinks is the current window), and should allow some other target improvements. The old cmd_find_* functions remain for the moment but that layer will be dropped later. Joint work with Thomas Adam. --- cmd-attach-session.c | 43 ++----- cmd-break-pane.c | 23 ++-- cmd-capture-pane.c | 7 +- cmd-choose-buffer.c | 11 +- cmd-choose-client.c | 11 +- cmd-choose-tree.c | 17 ++- cmd-clear-history.c | 11 +- cmd-command-prompt.c | 7 +- cmd-confirm-before.c | 7 +- cmd-copy-mode.c | 9 +- cmd-detach-client.c | 19 +--- cmd-display-message.c | 32 +----- cmd-display-panes.c | 12 +- cmd-find-window.c | 14 +-- cmd-if-shell.c | 33 ++---- cmd-join-pane.c | 15 ++- cmd-kill-pane.c | 9 +- cmd-kill-session.c | 5 +- cmd-kill-window.c | 16 +-- cmd-list-clients.c | 10 +- cmd-list-panes.c | 17 +-- cmd-list-windows.c | 11 +- cmd-load-buffer.c | 2 +- cmd-lock-server.c | 24 ++-- cmd-move-window.c | 30 ++--- cmd-new-session.c | 45 ++++---- cmd-new-window.c | 28 +++-- cmd-paste-buffer.c | 8 +- cmd-pipe-pane.c | 14 +-- cmd-queue.c | 25 ++-- cmd-refresh-client.c | 7 +- cmd-rename-session.c | 7 +- cmd-rename-window.c | 8 +- cmd-resize-pane.c | 16 +-- cmd-respawn-pane.c | 14 +-- cmd-respawn-window.c | 16 +-- cmd-rotate-window.c | 11 +- cmd-run-shell.c | 33 ++---- cmd-save-buffer.c | 2 +- cmd-select-layout.c | 11 +- cmd-select-pane.c | 24 ++-- cmd-select-window.c | 21 +--- cmd-send-keys.c | 13 +-- cmd-set-environment.c | 10 +- cmd-set-hook.c | 13 +-- cmd-set-option.c | 34 ++---- cmd-show-environment.c | 11 +- cmd-show-messages.c | 7 +- cmd-show-options.c | 28 ++--- cmd-source-file.c | 3 +- cmd-split-window.c | 19 ++-- cmd-swap-pane.c | 24 ++-- cmd-swap-window.c | 21 ++-- cmd-switch-client.c | 44 ++------ cmd.c | 251 +++++++++++++++++++++++++++++++++++++++++ tmux.h | 42 ++++++- 56 files changed, 606 insertions(+), 599 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 00ced9df..c29e9d1a 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -36,48 +36,27 @@ const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", "c:dErt:", 0, 0, "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - CMD_STARTSERVER, + CMD_STARTSERVER|CMD_SESSION_T|CMD_PANE_T|CMD_PREFERUNATTACHED, cmd_attach_session_exec }; enum cmd_retval -cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, - const char *cflag, int Eflag) +cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, + int Eflag) { - struct session *s; + struct session *s = cmdq->state.tflag.s; struct client *c = cmdq->client, *c_loop; - struct winlink *wl = NULL; - struct window *w = NULL; - struct window_pane *wp = NULL; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; const char *update; - char *cause; + char *cause, *cwd; struct format_tree *ft; - char *cwd; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); return (CMD_RETURN_ERROR); } - if (tflag == NULL) { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - } else if (tflag[strcspn(tflag, ":.")] != '\0') { - if ((wl = cmd_find_pane(cmdq, tflag, &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - } else { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - w = window_find_by_id_str(tflag); - if (w == NULL) { - wp = window_pane_find_by_id_str(tflag); - if (wp != NULL) - w = wp->window; - } - if (w != NULL) - wl = winlink_find_by_window(&s->windows, w); - } - if (c == NULL) return (CMD_RETURN_NORMAL); if (server_client_check_nested(c)) { @@ -94,8 +73,7 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (cflag != NULL) { ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, - NULL, NULL); + format_defaults(ft, c, s, wl, wp); cwd = format_expand(ft, cflag); format_free(ft); @@ -176,7 +154,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - return (cmd_attach_session(cmdq, args_get(args, 't'), - args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'), - args_has(args, 'E'))); + return (cmd_attach_session(cmdq, args_has(args, 'd'), + args_has(args, 'r'), args_get(args, 'c'), args_has(args, 'E'))); } diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 98d6ad3d..eb07fb87 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_break_pane_entry = { "break-pane", "breakp", "dPF:s:t:", 0, 0, "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, - 0, + CMD_PANE_S|CMD_INDEX_T, cmd_break_pane_exec }; @@ -42,28 +42,22 @@ enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct session *src_s; - struct session *dst_s; - struct window_pane *wp; - struct window *w; + struct winlink *wl = cmdq->state.sflag.wl; + struct session *src_s = cmdq->state.sflag.s; + struct session *dst_s = cmdq->state.tflag.s; + struct window_pane *wp = cmdq->state.sflag.wp; + struct window *w = wl->window; char *name; char *cause; - int idx; + int idx = cmdq->state.tflag.idx; struct format_tree *ft; const char *template; char *cp; - wl = cmd_find_pane(cmdq, args_get(args, 's'), &src_s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst_s)) == -2) - return (CMD_RETURN_ERROR); if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { cmdq_error(cmdq, "index %d already in use", idx); return (CMD_RETURN_ERROR); } - w = wl->window; if (window_count_panes(w) == 1) { cmdq_error(cmdq, "can't break with only one pane"); @@ -102,8 +96,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) template = BREAK_PANE_TEMPLATE; ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), dst_s, wl, - wp); + format_defaults(ft, cmdq->state.c, dst_s, wl, wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 6a7af47a..9d22a0f2 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_capture_pane_entry = { "ab:CeE:JpPqS:t:", 0, 0, "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]" CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_capture_pane_exec }; @@ -175,14 +175,11 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; char *buf, *cause; const char *bufname; size_t len; - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - len = 0; if (args_has(args, 'P')) buf = cmd_capture_pane_pending(args, wp, &len); diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index dbb20fc4..4418d415 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_choose_buffer_entry = { "choose-buffer", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, + CMD_WINDOW_T, cmd_choose_buffer_exec }; @@ -44,15 +44,15 @@ enum cmd_retval cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; + struct winlink *wl = cmdq->state.tflag.wl; struct window_choose_data *cdata; - struct winlink *wl; struct paste_buffer *pb; char *action, *action_data; const char *template; u_int idx; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } @@ -60,9 +60,6 @@ cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if ((template = args_get(args, 'F')) == NULL) template = CHOOSE_BUFFER_TEMPLATE; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 93a141ee..c58bc826 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_choose_client_entry = { "choose-client", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, + CMD_WINDOW_T, cmd_choose_client_exec }; @@ -53,22 +53,19 @@ enum cmd_retval cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct client *c1; struct window_choose_data *cdata; - struct winlink *wl; + struct winlink *wl = cmdq->state.tflag.wl; const char *template; char *action; u_int idx, cur; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 0b0fea6a..766978fe 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -48,7 +48,7 @@ const struct cmd_entry cmd_choose_tree_entry = { "S:W:swub:c:t:", 0, 1, "[-suw] [-b session-template] [-c window template] [-S format] " \ "[-W format] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_choose_tree_exec }; @@ -56,7 +56,7 @@ const struct cmd_entry cmd_choose_session_entry = { "choose-session", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - 0, + CMD_WINDOW_T, cmd_choose_tree_exec }; @@ -64,7 +64,7 @@ const struct cmd_entry cmd_choose_window_entry = { "choose-window", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE "[-F format] [template]", - 0, + CMD_WINDOW_T, cmd_choose_tree_exec }; @@ -72,9 +72,9 @@ enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl, *wm; - struct session *s, *s2; - struct client *c; + struct client *c = cmdq->state.c; + struct winlink *wl = cmdq->state.tflag.wl, *wm; + struct session *s = cmdq->state.tflag.s, *s2; struct window_choose_data *wcd = NULL; const char *ses_template, *win_template; char *final_win_action, *cur_win_template; @@ -87,14 +87,11 @@ cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) ses_template = win_template = NULL; ses_action = win_action = NULL; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); diff --git a/cmd-clear-history.c b/cmd-clear-history.c index 63e9d548..a76cab80 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -30,20 +30,17 @@ const struct cmd_entry cmd_clear_history_entry = { "clear-history", "clearhist", "t:", 0, 0, CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_clear_history_exec }; enum cmd_retval -cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) +cmd_clear_history_exec(__unused struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; struct grid *gd; - if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); - gd = wp->base.grid; + gd = cmdq->state.tflag.wp->base.grid; if (wp->mode == &window_copy_mode) window_pane_reset_mode(wp); diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 1622e0b7..900fceb9 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_command_prompt_entry = { "command-prompt", NULL, "I:p:t:", 0, 1, "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", - 0, + CMD_CLIENT_T, cmd_command_prompt_exec }; @@ -58,13 +58,10 @@ cmd_command_prompt_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; const char *inputs, *prompts; struct cmd_command_prompt_cdata *cdata; - struct client *c; + struct client *c = cmdq->state.c; char *prompt, *ptr, *input = NULL; size_t n; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (c->prompt_string != NULL) return (CMD_RETURN_NORMAL); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 248515cd..17a575a2 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_confirm_before_entry = { "confirm-before", "confirm", "p:t:", 1, 1, "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - 0, + CMD_CLIENT_T, cmd_confirm_before_exec }; @@ -51,13 +51,10 @@ cmd_confirm_before_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_confirm_before_data *cdata; - struct client *c; + struct client *c = cmdq->state.c; char *cmd, *copy, *new_prompt, *ptr; const char *prompt; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if ((prompt = args_get(args, 'p')) != NULL) xasprintf(&new_prompt, "%s ", prompt); else { diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index e04b561b..79d06cee 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -29,8 +29,8 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { "copy-mode", NULL, "Met:u", 0, 0, - "[-Meu] " CMD_TARGET_PANE_USAGE, - 0, + "[-Mu] " CMD_TARGET_PANE_USAGE, + CMD_PANE_T, cmd_copy_mode_exec }; @@ -48,15 +48,14 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct client *c = cmdq->client; struct session *s; - struct window_pane *wp; + struct window_pane *wp = cmdq->state.tflag.wp; if (args_has(args, 'M')) { if ((wp = cmd_mouse_pane(&cmdq->item->mouse, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); - } else if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) - return (CMD_RETURN_ERROR); + } if (self->entry == &cmd_clock_mode_entry) { window_pane_set_mode(wp, &window_clock_mode); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index d8128eae..86f8063c 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { "detach-client", "detach", "as:t:P", 0, 0, - "[-aP] [-s target-session] " CMD_TARGET_CLIENT_USAGE, - CMD_READONLY, + "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, + CMD_READONLY|CMD_CLIENT_T|CMD_SESSION_S, cmd_detach_client_exec }; @@ -40,7 +40,7 @@ const struct cmd_entry cmd_suspend_client_entry = { "suspend-client", "suspendc", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_detach_client_exec }; @@ -48,13 +48,11 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c, *cloop; + struct client *c = cmdq->state.c, *cloop; struct session *s; enum msgtype msgtype; if (self->entry == &cmd_suspend_client_entry) { - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); tty_stop_tty(&c->tty); c->flags |= CLIENT_SUSPENDED; proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); @@ -67,10 +65,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) msgtype = MSG_DETACH; if (args_has(args, 's')) { - s = cmd_find_session(cmdq, args_get(args, 's'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - + s = cmdq->state.sflag.s; TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session == s) server_client_detach(cloop, msgtype); @@ -78,10 +73,6 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_STOP); } - c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - if (args_has(args, 'a')) { TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session != NULL && cloop != c) diff --git a/cmd-display-message.c b/cmd-display-message.c index 7ca8e9c4..201f8b75 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_display_message_entry = { "c:pt:F:", 0, 1, "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", - 0, + CMD_CLIENT_C|CMD_PANE_T, cmd_display_message_exec }; @@ -47,41 +47,19 @@ enum cmd_retval cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct client *c = cmdq->state.c; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; const char *template; char *msg; struct format_tree *ft; - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - } else { - wl = cmd_find_pane(cmdq, NULL, &s, &wp); - if (wl == NULL) - return (CMD_RETURN_ERROR); - } - if (args_has(args, 'F') && args->argc != 0) { cmdq_error(cmdq, "only one of -F or argument must be given"); return (CMD_RETURN_ERROR); } - if (args_has(args, 'c')) { - c = cmd_find_client(cmdq, args_get(args, 'c'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - } else { - c = cmd_find_client(cmdq, NULL, 1); - if (c == NULL && !args_has(self->args, 'p')) { - cmdq_error(cmdq, "no client available"); - return (CMD_RETURN_ERROR); - } - } - template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 9ce89712..714ee19e 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -30,20 +30,14 @@ const struct cmd_entry cmd_display_panes_entry = { "display-panes", "displayp", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_display_panes_exec }; enum cmd_retval -cmd_display_panes_exec(struct cmd *self, struct cmd_q *cmdq) +cmd_display_panes_exec(__unused struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c; - - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - - server_set_identify(c); + server_set_identify(cmdq->state.c); return (CMD_RETURN_NORMAL); } diff --git a/cmd-find-window.c b/cmd-find-window.c index e92ae60f..1733b717 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -51,7 +51,7 @@ const struct cmd_entry cmd_find_window_entry = { "find-window", "findw", "F:CNt:T", 1, 4, "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", - 0, + CMD_WINDOW_T, cmd_find_window_exec }; @@ -137,10 +137,10 @@ enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct window_choose_data *cdata; - struct session *s; - struct winlink *wl, *wm; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl, *wm; struct cmd_find_window_list find_list; struct cmd_find_window_data *find_data; struct cmd_find_window_data *find_data1; @@ -148,14 +148,10 @@ cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) const char *template; u_int i, match_flags; - if ((c = cmd_find_client(cmdq, NULL, 1)) == NULL) { + if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } - s = c->session; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); if ((template = args_get(args, 'F')) == NULL) template = FIND_WINDOW_TEMPLATE; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 3345e8ce..404f4671 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_if_shell_entry = { "if-shell", "if", "bFt:", 2, 3, "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command [command]", - 0, + CMD_PANE_T|CMD_CANFAIL, cmd_if_shell_exec }; @@ -61,31 +61,20 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_if_shell_data *cdata; char *shellcmd, *cmd, *cause; struct cmd_list *cmdlist; - struct client *c; - struct session *s = NULL; - struct winlink *wl = NULL; - struct window_pane *wp = NULL; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; struct format_tree *ft; const char *cwd; - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - cwd = wp->cwd; - } else { - c = cmd_find_client(cmdq, NULL, 1); - if (c != NULL && c->session != NULL) { - s = c->session; - wl = s->curw; - wp = wl->window->active; - } - if (cmdq->client != NULL && cmdq->client->session == NULL) - cwd = cmdq->client->cwd; - else if (s != NULL) - cwd = s->cwd; - else - cwd = NULL; - } + cwd = wp->cwd; + if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else if (s != NULL) + cwd = s->cwd; + else + cwd = NULL; ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index cc6432a4..6fc5b977 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_join_pane_entry = { "join-pane", "joinp", "bdhvp:l:s:t:", 0, 0, "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - 0, + CMD_PANE_MARKED_S|CMD_PANE_T, cmd_join_pane_exec }; @@ -45,7 +45,7 @@ const struct cmd_entry cmd_move_pane_entry = { "move-pane", "movep", "bdhvp:l:s:t:", 0, 0, "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - 0, + CMD_PANE_S|CMD_PANE_T, cmd_join_pane_exec }; @@ -68,16 +68,15 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) enum layout_type type; struct layout_cell *lc; - dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), &dst_s, &dst_wp); - if (dst_wl == NULL) - return (CMD_RETURN_ERROR); + dst_s = cmdq->state.tflag.s; + dst_wl = cmdq->state.tflag.wl; + dst_wp = cmdq->state.tflag.wp; dst_w = dst_wl->window; dst_idx = dst_wl->idx; server_unzoom_window(dst_w); - src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); + src_wl = cmdq->state.sflag.wl; + src_wp = cmdq->state.sflag.wp; src_w = src_wl->window; server_unzoom_window(src_w); diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index f4735fd2..d5f69ea9 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -32,19 +32,16 @@ const struct cmd_entry cmd_kill_pane_entry = { "kill-pane", "killp", "at:", 0, 0, "[-a] " CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_kill_pane_exec }; enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct window_pane *loopwp, *tmpwp, *wp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *loopwp, *tmpwp, *wp = cmdq->state.tflag.wp; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) - return (CMD_RETURN_ERROR); server_unzoom_window(wl->window); if (window_count_panes(wl->window) == 1) { diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 3f39c241..a4b0d5d2 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_kill_session_entry = { "kill-session", NULL, "aCt:", 0, 0, "[-aC] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_kill_session_exec }; @@ -44,8 +44,7 @@ cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) struct session *s, *sloop, *stmp; struct winlink *wl; - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); + s = cmdq->state.tflag.s; if (args_has(args, 'C')) { RB_FOREACH(wl, winlinks, &s->windows) { diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 4d346a71..4ab17472 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_kill_window_entry = { "kill-window", "killw", "at:", 0, 0, "[-a] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_kill_window_exec }; @@ -38,21 +38,17 @@ const struct cmd_entry cmd_unlink_window_entry = { "unlink-window", "unlinkw", "kt:", 0, 0, "[-k] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_kill_window_exec }; enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl, *wl2, *wl3; - struct window *w; - struct session *s; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; + struct args *args = self->args; + struct winlink *wl = cmdq->state.tflag.wl, *wl2, *wl3; + struct window *w = wl->window; + struct session *s = cmdq->state.tflag.s; if (self->entry == &cmd_unlink_window_entry) { if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 2c13d398..effd8275 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_list_clients_entry = { "list-clients", "lsc", "F:t:", 0, 0, "[-F format] " CMD_TARGET_SESSION_USAGE, - CMD_READONLY, + CMD_READONLY|CMD_SESSION_T, cmd_list_clients_exec }; @@ -54,11 +54,9 @@ cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) u_int idx; char *line; - if (args_has(args, 't')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - } else + if (args_has(args, 't')) + s = cmdq->state.tflag.s; + else s = NULL; if ((template = args_get(args, 'F')) == NULL) diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 23e530e2..2c9fa623 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_list_panes_entry = { "list-panes", "lsp", "asF:t:", 0, 0, "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_list_panes_exec }; @@ -46,22 +46,15 @@ enum cmd_retval cmd_list_panes_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; if (args_has(args, 'a')) cmd_list_panes_server(self, cmdq); - else if (args_has(args, 's')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + else if (args_has(args, 's')) cmd_list_panes_session(self, s, cmdq, 1); - } else { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); + else cmd_list_panes_window(self, s, wl, cmdq, 0); - } return (CMD_RETURN_NORMAL); } diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 1eaee2d7..992ba0de 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -49,7 +49,7 @@ const struct cmd_entry cmd_list_windows_entry = { "list-windows", "lsw", "F:at:", 0, 0, "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_list_windows_exec }; @@ -57,16 +57,11 @@ enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; if (args_has(args, 'a')) cmd_list_windows_server(self, cmdq); - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - cmd_list_windows_session(self, s, cmdq, 0); - } + else + cmd_list_windows_session(self, cmdq->state.tflag.s, cmdq, 0); return (CMD_RETURN_NORMAL); } diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 5ac2edc3..929e3bf2 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -72,7 +72,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((s = cmd_find_current(cmdq)) != NULL) + else if ((s = c->session) != NULL) cwd = s->cwd; else cwd = "."; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index a0204129..5d20ebd4 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_lock_session_entry = { "lock-session", "locks", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_lock_server_exec }; @@ -46,30 +46,20 @@ const struct cmd_entry cmd_lock_client_entry = { "lock-client", "lockc", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_lock_server_exec }; enum cmd_retval cmd_lock_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { - struct args *args = self->args; - struct client *c; - struct session *s; - if (self->entry == &cmd_lock_server_entry) server_lock(); - else if (self->entry == &cmd_lock_session_entry) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - server_lock_session(s); - } else { - c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (c == NULL) - return (CMD_RETURN_ERROR); - server_lock_client(c); - } + else if (self->entry == &cmd_lock_session_entry) + server_lock_session(cmdq->state.tflag.s); + else + server_lock_client(cmdq->state.c); + recalculate_sizes(); return (CMD_RETURN_NORMAL); diff --git a/cmd-move-window.c b/cmd-move-window.c index 1bd46ab2..9e3a1c09 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_move_window_entry = { "move-window", "movew", "adkrs:t:", 0, 0, "[-dkr] " CMD_SRCDST_WINDOW_USAGE, - 0, + CMD_WINDOW_S|CMD_MOVEW_R|CMD_INDEX_T, cmd_move_window_exec }; @@ -40,7 +40,7 @@ const struct cmd_entry cmd_link_window_entry = { "link-window", "linkw", "adks:t:", 0, 0, "[-dk] " CMD_SRCDST_WINDOW_USAGE, - 0, + CMD_WINDOW_S|CMD_INDEX_T, cmd_move_window_exec }; @@ -48,36 +48,28 @@ enum cmd_retval cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *src, *dst, *s; - struct winlink *wl; + struct session *src = cmdq->state.sflag.s; + struct session *dst = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.sflag.wl; char *cause; - int idx, kflag, dflag, sflag; + int idx = cmdq->state.tflag.idx, kflag, dflag, sflag; + + kflag = args_has(self->args, 'k'); + dflag = args_has(self->args, 'd'); if (args_has(args, 'r')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - - session_renumber_windows(s); + session_renumber_windows(dst); recalculate_sizes(); return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_window(cmdq, args_get(args, 's'), &src)) == NULL) - return (CMD_RETURN_ERROR); - if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst)) == -2) - return (CMD_RETURN_ERROR); - kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); sflag = args_has(self->args, 's'); if (args_has(self->args, 'a')) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - if ((idx = winlink_shuffle_up(s, s->curw)) == -1) + if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) return (CMD_RETURN_ERROR); } diff --git a/cmd-new-session.c b/cmd-new-session.c index 341399be..e589a361 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_new_session_entry = { "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", - CMD_STARTSERVER, + CMD_STARTSERVER|CMD_CANFAIL|CMD_SESSION_T, cmd_new_session_exec }; @@ -49,7 +49,7 @@ const struct cmd_entry cmd_has_session_entry = { "has-session", "has", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_new_session_exec }; @@ -57,8 +57,9 @@ enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c = cmdq->client, *c0; - struct session *s, *groupwith; + struct client *c = cmdq->client; + struct session *s, *attach_sess; + struct session *groupwith = cmdq->state.tflag.s; struct window *w; struct environ *env; struct termios tio, *tiop; @@ -71,8 +72,10 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct environ_entry *envent; if (self->entry == &cmd_has_session_entry) { - if (cmd_find_session(cmdq, args_get(args, 't'), 0) == NULL) - return (CMD_RETURN_ERROR); + /* + * cmd_prepare() will fail if the session cannot be found, + * hence always return success here. + */ return (CMD_RETURN_NORMAL); } @@ -87,9 +90,16 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "bad session name: %s", newname); return (CMD_RETURN_ERROR); } - if (session_find(newname) != NULL) { + if ((attach_sess = session_find(newname)) != NULL) { if (args_has(args, 'A')) { - return (cmd_attach_session(cmdq, newname, + /* + * This cmdq is now destined for + * attach-session. Because attach-session + * will have already been prepared, copy this + * session into its tflag so it can be used. + */ + cmdq->state.tflag.s = attach_sess; + return (cmd_attach_session(cmdq, args_has(args, 'D'), 0, NULL, args_has(args, 'E'))); } @@ -98,12 +108,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } } - target = args_get(args, 't'); - if (target != NULL) { - groupwith = cmd_find_session(cmdq, target, 0); - if (groupwith == NULL) - return (CMD_RETURN_ERROR); - } else + if ((target = args_get(args, 't')) == NULL) groupwith = NULL; /* Set -d if no client. */ @@ -120,14 +125,11 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), NULL, NULL, - NULL); + format_defaults(ft, c, NULL, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL) - cwd = c0->session->cwd; else cwd = "."; @@ -193,7 +195,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) /* Figure out the command for the new window. */ argc = -1; argv = NULL; - if (target == NULL && args->argc != 0) { + if (!args_has(args, 't') && args->argc != 0) { argc = args->argc; argv = args->argv; } else if (target == NULL) { @@ -245,7 +247,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) * If a target session is given, this is to be part of a session group, * so add it to the group and synchronize. */ - if (groupwith != NULL) { + if (args_has(args, 't')) { session_group_add(groupwith, s); session_group_synchronize_to(s); session_select(s, RB_MIN(winlinks, &s->windows)->idx); @@ -285,8 +287,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) template = NEW_SESSION_TEMPLATE; ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); + format_defaults(ft, c, s, NULL, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-new-window.c b/cmd-new-window.c index ddfd6a20..8154bdfb 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -39,7 +39,12 @@ const struct cmd_entry cmd_new_window_entry = { "ac:dF:kn:Pt:", 0, -1, "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", - 0, + /* + * Using PREP_CANFAIL here ensures that the wl is filled in + * regardless; making PREP_INDEX the thing we want -t to be used for + * in the specific case. + */ + CMD_INDEX_T|CMD_CANFAIL, cmd_new_window_exec }; @@ -47,26 +52,21 @@ enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct client *c = cmdq->state.c; + int idx = cmdq->state.tflag.idx; const char *cmd, *path, *template, *cwd, *to_free; char **argv, *cause, *cp; - int argc, idx, detached; + int argc, detached; struct format_tree *ft; struct environ_entry *envent; if (args_has(args, 'a')) { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); if ((idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } - } else { - idx = cmd_find_index(cmdq, args_get(args, 't'), &s); - if (idx == -2) - return (CMD_RETURN_ERROR); } detached = args_has(args, 'd'); @@ -95,8 +95,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); + format_defaults(ft, c, s, NULL, NULL); cwd = to_free = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -144,8 +143,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) template = NEW_WINDOW_TEMPLATE; ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, - NULL); + format_defaults(ft, c, s, wl, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 87f09ee6..92a31c53 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_paste_buffer_entry = { "paste-buffer", "pasteb", "db:prs:t:", 0, 0, "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_paste_buffer_exec }; @@ -45,16 +45,12 @@ enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct window_pane *wp; - struct session *s; + struct window_pane *wp = cmdq->state.tflag.wp; struct paste_buffer *pb; const char *sepstr, *bufname, *bufdata, *bufend, *line; size_t seplen, bufsize; int bracket = args_has(args, 'p'); - if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) - return (CMD_RETURN_ERROR); - bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 79ef7818..ad4b02e0 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -41,7 +41,7 @@ const struct cmd_entry cmd_pipe_pane_entry = { "pipe-pane", "pipep", "ot:", 0, 1, "[-o] " CMD_TARGET_PANE_USAGE " [command]", - 0, + CMD_PANE_T, cmd_pipe_pane_exec }; @@ -49,18 +49,14 @@ enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; - struct session *s; - struct winlink *wl; - struct window_pane *wp; + struct client *c = cmdq->state.c; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; char *cmd; int old_fd, pipe_fd[2], null_fd; struct format_tree *ft; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - c = cmd_find_client(cmdq, NULL, 1); - /* Destroy the old pipe. */ old_fd = wp->pipe_fd; if (wp->pipe_fd != -1) { diff --git a/cmd-queue.c b/cmd-queue.c index c85fb048..7b2675fa 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -25,7 +25,7 @@ #include "tmux.h" -enum cmd_retval cmdq_continue_one(struct cmd_q *); +static enum cmd_retval cmdq_continue_one(struct cmd_q *); /* Create new command queue. */ struct cmd_q * @@ -179,37 +179,40 @@ cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) } /* Process one command. */ -enum cmd_retval +static enum cmd_retval cmdq_continue_one(struct cmd_q *cmdq) { struct cmd *cmd = cmdq->cmd; enum cmd_retval retval; - char *s; + char *tmp; int flags = !!(cmd->flags & CMD_CONTROL); - s = cmd_print(cmd); - log_debug("cmdq %p: %s", cmdq, s); - free(s); + tmp = cmd_print(cmd); + log_debug("cmdq %p: %s", cmdq, tmp); + free(tmp); cmdq->time = time(NULL); cmdq->number++; cmdq_guard(cmdq, "begin", flags); + if (cmd_prepare_state(cmd, cmdq) != 0) + goto error; retval = cmd->entry->exec(cmd, cmdq); - if (retval == CMD_RETURN_ERROR) - cmdq_guard(cmdq, "error", flags); - else - cmdq_guard(cmdq, "end", flags); + cmdq_guard(cmdq, "end", flags); return (retval); + +error: + cmdq_guard(cmdq, "error", flags); + return (CMD_RETURN_ERROR); } /* Continue processing command queue. Returns 1 if finishes empty. */ int cmdq_continue(struct cmd_q *cmdq) { - struct client *c = cmdq->client; + struct client *c = cmdq->client; struct cmd_q_item *next; enum cmd_retval retval; int empty; diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 5a45ec25..9d1d0fce 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_refresh_client_entry = { "refresh-client", "refresh", "C:St:", 0, 0, "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_refresh_client_exec }; @@ -38,13 +38,10 @@ enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; const char *size; u_int w, h; - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - if (args_has(args, 'C')) { if ((size = args_get(args, 'C')) == NULL) { cmdq_error(cmdq, "missing size"); diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 481154ce..0c1a7e8c 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_rename_session_entry = { "rename-session", "rename", "t:", 1, 1, CMD_TARGET_SESSION_USAGE " new-name", - 0, + CMD_SESSION_T, cmd_rename_session_exec }; @@ -40,7 +40,7 @@ enum cmd_retval cmd_rename_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; + struct session *s = cmdq->state.tflag.s; const char *newname; newname = args->argv[0]; @@ -53,9 +53,6 @@ cmd_rename_session_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); } - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - RB_REMOVE(sessions, &sessions, s); free(s->name); s->name = xstrdup(newname); diff --git a/cmd-rename-window.c b/cmd-rename-window.c index bc85d96b..6609ebab 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_rename_window_entry = { "rename-window", "renamew", "t:", 1, 1, CMD_TARGET_WINDOW_USAGE " new-name", - 0, + CMD_WINDOW_T, cmd_rename_window_exec }; @@ -40,11 +40,7 @@ enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; - - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); + struct winlink *wl = cmdq->state.tflag.wl; window_set_name(wl->window, args->argv[0]); options_set_number(wl->window->options, "automatic-rename", 0); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index b342307d..a4de32df 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -33,9 +33,8 @@ void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); const struct cmd_entry cmd_resize_pane_entry = { "resize-pane", "resizep", "DLMRt:Ux:y:Z", 0, 1, - "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE - " [adjustment]", - 0, + "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", + CMD_PANE_T, cmd_resize_pane_exec }; @@ -43,13 +42,13 @@ enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct window_pane *wp = cmdq->state.tflag.wp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; struct client *c = cmdq->client; - struct session *s; - struct winlink *wl; - struct window *w; + struct session *s = cmdq->state.tflag.s; const char *errstr; char *cause; - struct window_pane *wp; u_int adjust; int x, y; @@ -63,10 +62,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) - return (CMD_RETURN_ERROR); w = wl->window; - if (args_has(args, 'Z')) { if (w->flags & WINDOW_ZOOMED) window_unzoom(w); diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index a50b9d06..45098d80 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_respawn_pane_entry = { "respawn-pane", "respawnp", "kt:", 0, -1, "[-k] " CMD_TARGET_PANE_USAGE " [command]", - 0, + CMD_PANE_T, cmd_respawn_pane_exec }; @@ -42,20 +42,16 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - struct session *s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; struct environ *env; const char *path; char *cause; u_int idx; struct environ_entry *envent; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (!args_has(self->args, 'k') && wp->fd != -1) { if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 93af1b68..f6550dee 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_respawn_window_entry = { "respawn-window", "respawnw", "kt:", 0, -1, "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", - 0, + CMD_WINDOW_T, cmd_respawn_window_exec }; @@ -41,25 +41,21 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; struct window_pane *wp; - struct session *s; struct environ *env; const char *path; char *cause; struct environ_entry *envent; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (!args_has(self->args, 'k')) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd == -1) continue; - cmdq_error(cmdq, - "window still active: %s:%d", s->name, wl->idx); + cmdq_error(cmdq, "window still active: %s:%d", s->name, + wl->idx); return (CMD_RETURN_ERROR); } } diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 859ff04a..4122886a 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -30,24 +30,19 @@ const struct cmd_entry cmd_rotate_window_entry = { "rotate-window", "rotatew", "Dt:U", 0, 0, "[-DU] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_rotate_window_exec }; enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct window *w; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; struct window_pane *wp, *wp2; struct layout_cell *lc; u_int sx, sy, xoff, yoff; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (args_has(self->args, 'D')) { wp = TAILQ_LAST(&w->panes, window_panes); TAILQ_REMOVE(&w->panes, wp, entry); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index fd5d84d4..0dae39ac 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_run_shell_entry = { "run-shell", "run", "bt:", 1, 1, "[-b] " CMD_TARGET_PANE_USAGE " shell-command", - 0, + CMD_PANE_T|CMD_CANFAIL, cmd_run_shell_exec }; @@ -75,31 +75,18 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct cmd_run_shell_data *cdata; char *shellcmd; - struct client *c; - struct session *s = NULL; - struct winlink *wl = NULL; - struct window_pane *wp = NULL; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window_pane *wp = cmdq->state.tflag.wp; struct format_tree *ft; const char *cwd; - if (args_has(args, 't')) { - wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); - cwd = wp->cwd; - } else { - c = cmd_find_client(cmdq, NULL, 1); - if (c != NULL && c->session != NULL) { - s = c->session; - wl = s->curw; - wp = wl->window->active; - } - if (cmdq->client != NULL && cmdq->client->session == NULL) - cwd = cmdq->client->cwd; - else if (s != NULL) - cwd = s->cwd; - else - cwd = NULL; - } - + if (cmdq->client != NULL && cmdq->client->session == NULL) + cwd = cmdq->client->cwd; + else if (s != NULL) + cwd = s->cwd; + else + cwd = NULL; ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index daef88a8..fc8d7bee 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -94,7 +94,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) if (c != NULL && c->session == NULL) cwd = c->cwd; - else if ((s = cmd_find_current(cmdq)) != NULL) + else if ((s = c->session) != NULL) cwd = s->cwd; else cwd = "."; diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 14737dc8..aac60083 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_select_layout_entry = { "select-layout", "selectl", "nopt:", 0, 1, "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", - 0, + CMD_WINDOW_T, cmd_select_layout_exec }; @@ -40,7 +40,7 @@ const struct cmd_entry cmd_next_layout_entry = { "next-layout", "nextl", "t:", 0, 0, CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_select_layout_exec }; @@ -48,7 +48,7 @@ const struct cmd_entry cmd_previous_layout_entry = { "previous-layout", "prevl", "t:", 0, 0, CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_select_layout_exec }; @@ -56,16 +56,13 @@ enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; + struct winlink *wl = cmdq->state.tflag.wl; struct window *w; const char *layoutname; char *oldlayout; int next, previous, layout; - if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) - return (CMD_RETURN_ERROR); w = wl->window; - server_unzoom_window(w); next = self->entry == &cmd_next_layout_entry; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 7986e98c..e83dc609 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -30,7 +30,7 @@ const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", "DdegLlMmP:Rt:U", 0, 0, "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_select_pane_exec }; @@ -38,7 +38,7 @@ const struct cmd_entry cmd_last_pane_entry = { "last-pane", "lastp", "det:", 0, 0, "[-de] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_select_pane_exec }; @@ -46,19 +46,15 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *wl; - struct window *w; - struct session *s; - struct window_pane *wp, *lastwp, *markedwp; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct session *s = cmdq->state.tflag.s; + struct window_pane *wp = cmdq->state.tflag.wp, *lastwp, *markedwp; const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; - if (w->last == NULL) { + if (wl->window->last == NULL) { cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } @@ -79,9 +75,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) + server_unzoom_window(wp->window); + if (!window_pane_visible(wp)) { + cmdq_error(cmdq, "pane not visible"); return (CMD_RETURN_ERROR); - w = wl->window; + } if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) diff --git a/cmd-select-window.c b/cmd-select-window.c index f530f1fe..ede60dae 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_select_window_entry = { "select-window", "selectw", "lnpTt:", 0, 0, "[-lnpT] " CMD_TARGET_WINDOW_USAGE, - 0, + CMD_WINDOW_T, cmd_select_window_exec }; @@ -40,7 +40,7 @@ const struct cmd_entry cmd_next_window_entry = { "next-window", "next", "at:", 0, 0, "[-a] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_select_window_exec }; @@ -48,7 +48,7 @@ const struct cmd_entry cmd_previous_window_entry = { "previous-window", "prev", "at:", 0, 0, "[-a] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_select_window_exec }; @@ -56,16 +56,15 @@ const struct cmd_entry cmd_last_window_entry = { "last-window", "last", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_select_window_exec }; enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct winlink *wl; - struct session *s; + struct winlink *wl = cmdq->state.tflag.wl; + struct session *s = cmdq->state.tflag.s; int next, previous, last, activity; next = self->entry == &cmd_next_window_entry; @@ -79,10 +78,6 @@ cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) last = 1; if (next || previous || last) { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - activity = args_has(self->args, 'a'); if (next) { if (session_next(s, activity) != 0) { @@ -103,10 +98,6 @@ cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) server_redraw_session(s); } else { - wl = cmd_find_window(cmdq, args_get(args, 't'), &s); - if (wl == NULL) - return (CMD_RETURN_ERROR); - /* * If -T and select-window is invoked on same window as * current, switch to previous window. diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 1461baa9..8f9b4ddf 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_send_keys_entry = { "send-keys", "send", "lRMt:", 0, -1, "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", - 0, + CMD_PANE_T, cmd_send_keys_exec }; @@ -41,7 +41,7 @@ const struct cmd_entry cmd_send_prefix_entry = { "send-prefix", NULL, "2t:", 0, 0, "[-2] " CMD_TARGET_PANE_USAGE, - 0, + CMD_PANE_T, cmd_send_keys_exec }; @@ -49,11 +49,11 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; + struct window_pane *wp = cmdq->state.tflag.wp; + struct session *s = cmdq->state.tflag.s; struct mouse_event *m = &cmdq->item->mouse; - struct window_pane *wp; - struct session *s; - int i, literal; const u_char *keystr; + int i, literal; key_code key; if (args_has(args, 'M')) { @@ -66,9 +66,6 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) - return (CMD_RETURN_ERROR); - if (self->entry == &cmd_send_prefix_entry) { if (args_has(args, '2')) key = options_get_number(s->options, "prefix2"); diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 2c0010af..4be967b7 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_set_environment_entry = { "set-environment", "setenv", "grt:u", 1, 2, "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - 0, + CMD_SESSION_T, cmd_set_environment_exec }; @@ -41,7 +41,6 @@ enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; struct environ *env; const char *name, *value; @@ -62,11 +61,8 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'g')) env = global_environ; - else { - if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - env = s->environ; - } + else + env = cmdq->state.tflag.s->environ; if (args_has(self->args, 'u')) { if (value != NULL) { diff --git a/cmd-set-hook.c b/cmd-set-hook.c index ec58418e..d75b0ba1 100644 --- a/cmd-set-hook.c +++ b/cmd-set-hook.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_set_hook_entry = { "set-hook", NULL, "gt:u", 1, 2, "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", - 0, + CMD_SESSION_T, cmd_set_hook_exec }; @@ -41,7 +41,7 @@ const struct cmd_entry cmd_show_hooks_entry = { "show-hooks", NULL, "gt:", 0, 1, "[-g] " CMD_TARGET_SESSION_USAGE, - 0, + CMD_SESSION_T, cmd_set_hook_exec }; @@ -49,7 +49,6 @@ enum cmd_retval cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; struct cmd_list *cmdlist; struct hooks *hooks; struct hook *hook; @@ -58,12 +57,8 @@ cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'g')) hooks = global_hooks; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - hooks = s->hooks; - } + else + hooks = cmdq->state.tflag.s->hooks; if (self->entry == &cmd_show_hooks_entry) { hook = hooks_first(hooks); diff --git a/cmd-set-option.c b/cmd-set-option.c index daee62ac..86856fbb 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -67,8 +67,8 @@ struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *, const struct cmd_entry cmd_set_option_entry = { "set-option", "set", "agoqst:uw", 1, 2, - "[-agosquw] [-t target-session|target-window] option [value]", - 0, + "[-agosquw] [-t target-window] option [value]", + CMD_WINDOW_T|CMD_CANFAIL, cmd_set_option_exec }; @@ -76,7 +76,7 @@ const struct cmd_entry cmd_set_window_option_entry = { "set-window-option", "setw", "agoqt:u", 1, 2, "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", - 0, + CMD_WINDOW_T|CMD_CANFAIL, cmd_set_option_exec }; @@ -84,12 +84,12 @@ enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - const struct options_table_entry *oe; - struct session *s; - struct winlink *wl; - struct client *c; - struct options *oo; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; struct window *w; + struct client *c; + const struct options_table_entry *oe; + struct options *oo; const char *optstr, *valstr; /* Get the option name and value. */ @@ -131,7 +131,6 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'g')) oo = global_w_options; else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) { cmdq_error(cmdq, "couldn't set '%s'%s", optstr, @@ -145,7 +144,6 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(self->args, 'g')) oo = global_s_options; else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) { cmdq_error(cmdq, "couldn't set '%s'%s", optstr, @@ -209,8 +207,8 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, const char *valstr) { struct args *args = self->args; - struct session *s; - struct winlink *wl; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; struct options *oo; if (args_has(args, 's')) @@ -219,21 +217,13 @@ cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr, self->entry == &cmd_set_window_option_entry) { if (args_has(self->args, 'g')) oo = global_w_options; - else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); + else oo = wl->window->options; - } } else { if (args_has(self->args, 'g')) oo = global_s_options; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + else oo = s->options; - } } if (args_has(args, 'u')) { diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 96cfa289..8feb2e22 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_show_environment_entry = { "show-environment", "showenv", "gst:", 0, 1, "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - 0, + CMD_SESSION_T, cmd_show_environment_exec }; @@ -86,18 +86,13 @@ enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; struct environ *env; struct environ_entry *envent; if (args_has(self->args, 'g')) env = global_environ; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); - env = s->environ; - } + else + env = cmdq->state.tflag.s->environ; if (args->argc != 0) { envent = environ_find(env, args->argv[0]); diff --git a/cmd-show-messages.c b/cmd-show-messages.c index c98c2c91..b80b9fe7 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -35,7 +35,7 @@ const struct cmd_entry cmd_show_messages_entry = { "show-messages", "showmsgs", "JTt:", 0, 0, "[-JT] " CMD_TARGET_CLIENT_USAGE, - 0, + CMD_CLIENT_T, cmd_show_messages_exec }; @@ -94,7 +94,7 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; + struct client *c = cmdq->state.c; struct message_entry *msg; char *tim; int done, blank; @@ -111,9 +111,6 @@ cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) if (done) return (CMD_RETURN_NORMAL); - if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) - return (CMD_RETURN_ERROR); - TAILQ_FOREACH(msg, &c->message_log, entry) { tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; diff --git a/cmd-show-options.c b/cmd-show-options.c index fd96a0e2..5437ec73 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_show_options_entry = { "show-options", "show", "gqst:vw", 0, 1, "[-gqsvw] [-t target-session|target-window] [option]", - 0, + CMD_WINDOW_T, cmd_show_options_exec }; @@ -46,19 +46,19 @@ const struct cmd_entry cmd_show_window_options_entry = { "show-window-options", "showw", "gvt:", 0, 1, "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - 0, + CMD_WINDOW_T, cmd_show_options_exec }; enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct session *s; - struct winlink *wl; - struct options *oo; - int quiet; - enum options_table_scope scope; + struct args *args = self->args; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct options *oo; + enum options_table_scope scope; + int quiet; if (args_has(self->args, 's')) { oo = global_options; @@ -68,22 +68,14 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) scope = OPTIONS_TABLE_WINDOW; if (args_has(self->args, 'g')) oo = global_w_options; - else { - wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); - if (wl == NULL) - return (CMD_RETURN_ERROR); + else oo = wl->window->options; - } } else { scope = OPTIONS_TABLE_SESSION; if (args_has(self->args, 'g')) oo = global_s_options; - else { - s = cmd_find_session(cmdq, args_get(args, 't'), 0); - if (s == NULL) - return (CMD_RETURN_ERROR); + else oo = s->options; - } } quiet = args_has(self->args, 'q'); diff --git a/cmd-source-file.c b/cmd-source-file.c index e5710a0c..e776712c 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -45,8 +45,7 @@ cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) struct cmd_q *cmdq1; char *cause; - cmdq1 = cmdq_new(NULL); - cmdq1->client = cmdq->client; + cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_source_file_done; cmdq1->data = cmdq; diff --git a/cmd-split-window.c b/cmd-split-window.c index 80d9e2b1..59efae35 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -40,7 +40,7 @@ const struct cmd_entry cmd_split_window_entry = { "bc:dF:l:hp:Pt:v", 0, -1, "[-bdhvP] [-c start-directory] [-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", - 0, + CMD_PANE_T, cmd_split_window_exec }; @@ -48,10 +48,10 @@ enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp, *new_wp = NULL; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct window *w = wl->window; + struct window_pane *wp = cmdq->state.tflag.wp, *new_wp = NULL; struct environ *env; const char *cmd, *path, *shell, *template, *cwd, *to_free; char **argv, *cause, *new_cause, *cp; @@ -62,9 +62,6 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) struct format_tree *ft; struct environ_entry *envent; - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - w = wl->window; server_unzoom_window(w); env = environ_create(); @@ -89,8 +86,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, NULL, - NULL); + format_defaults(ft, cmdq->state.c, s, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (cmdq->client != NULL && cmdq->client->session == NULL) @@ -166,8 +162,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) template = SPLIT_WINDOW_TEMPLATE; ft = format_create(cmdq, 0); - format_defaults(ft, cmd_find_client(cmdq, NULL, 1), s, wl, - new_wp); + format_defaults(ft, cmdq->state.c, s, wl, new_wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index dc2b7246..62d6bad3 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -32,7 +32,7 @@ const struct cmd_entry cmd_swap_pane_entry = { "swap-pane", "swapp", "dDs:t:U", 0, 0, "[-dDU] " CMD_SRCDST_PANE_USAGE, - 0, + CMD_PANE_MARKED_S|CMD_PANE_T, cmd_swap_pane_exec }; @@ -40,16 +40,18 @@ enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct winlink *src_wl, *dst_wl; + struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &dst_wp); - if (dst_wl == NULL) - return (CMD_RETURN_ERROR); + dst_wl = cmdq->state.tflag.wl; dst_w = dst_wl->window; + dst_wp = cmdq->state.tflag.wp; + src_wp = cmdq->state.sflag.wp; + src_wl = cmdq->state.sflag.wl; + src_w = src_wl->window; server_unzoom_window(dst_w); if (!args_has(args, 's')) { @@ -62,19 +64,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); - } else { - src_wl = cmd_find_pane_marked(cmdq, NULL, NULL, - &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); - src_w = src_wl->window; } - } else { - src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, - &src_wp); - if (src_wl == NULL) - return (CMD_RETURN_ERROR); - src_w = src_wl->window; } server_unzoom_window(src_w); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 51682e37..0ed3ca81 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -32,31 +32,28 @@ const struct cmd_entry cmd_swap_window_entry = { "swap-window", "swapw", "ds:t:", 0, 0, "[-d] " CMD_SRCDST_WINDOW_USAGE, - 0, + CMD_WINDOW_MARKED_S|CMD_WINDOW_MARKED_T, cmd_swap_window_exec }; enum cmd_retval cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - const char *target_src, *target_dst; struct session *src, *dst; struct session_group *sg_src, *sg_dst; struct winlink *wl_src, *wl_dst; struct window *w; - target_src = args_get(args, 's'); - if ((wl_src = cmd_find_window_marked(cmdq, target_src, &src)) == NULL) - return (CMD_RETURN_ERROR); - target_dst = args_get(args, 't'); - if ((wl_dst = cmd_find_window(cmdq, target_dst, &dst)) == NULL) - return (CMD_RETURN_ERROR); - + wl_src = cmdq->state.sflag.wl; + src = cmdq->state.sflag.s; sg_src = session_group_find(src); + + wl_dst = cmdq->state.tflag.wl; + dst = cmdq->state.tflag.s; sg_dst = session_group_find(dst); - if (src != dst && - sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) { + + if (src != dst && sg_src != NULL && sg_dst != NULL && + sg_src == sg_dst) { cmdq_error(cmdq, "can't move window, sessions are grouped"); return (CMD_RETURN_ERROR); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 4746b15a..919d0057 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -33,7 +33,7 @@ const struct cmd_entry cmd_switch_client_entry = { "switch-client", "switchc", "lc:Enpt:rT:", 0, 0, "[-Elnpr] [-c target-client] [-t target-session] [-T key-table]", - CMD_READONLY, + CMD_READONLY|CMD_CLIENT_C|CMD_PANE_T|CMD_SESSION_T|CMD_PREFERUNATTACHED, cmd_switch_client_exec }; @@ -41,16 +41,13 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; - struct client *c; - struct session *s = NULL; - struct winlink *wl = NULL; - struct window *w = NULL; - struct window_pane *wp = NULL; - const char *tflag, *tablename, *update; + struct cmd_state *state = &cmdq->state; + struct client *c = state->c; + struct session *s = cmdq->state.tflag.s; + struct window_pane *wp; + const char *tablename, *update; struct key_table *table; - if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) - return (CMD_RETURN_ERROR); if (args_has(args, 'r')) c->flags ^= CLIENT_READONLY; @@ -68,7 +65,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - tflag = args_get(args, 't'); if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { cmdq_error(cmdq, "can't find next session"); @@ -82,37 +78,21 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) } else if (args_has(args, 'l')) { if (c->last_session != NULL && session_alive(c->last_session)) s = c->last_session; + else + s = NULL; if (s == NULL) { cmdq_error(cmdq, "can't find last session"); return (CMD_RETURN_ERROR); } - } else { - if (tflag == NULL) { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - } else if (tflag[strcspn(tflag, ":.")] != '\0') { - if ((wl = cmd_find_pane(cmdq, tflag, &s, &wp)) == NULL) - return (CMD_RETURN_ERROR); - } else { - if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) - return (CMD_RETURN_ERROR); - w = window_find_by_id_str(tflag); - if (w == NULL) { - wp = window_pane_find_by_id_str(tflag); - if (wp != NULL) - w = wp->window; - } - if (w != NULL) - wl = winlink_find_by_window(&s->windows, w); - } - if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); - if (wl != NULL) { + s = state->tflag.s; + if (state->tflag.wl != NULL) { + wp = state->tflag.wp; if (wp != NULL) window_set_active_pane(wp->window, wp); - session_set_current(s, wl); + session_set_current(s, state->tflag.wl); } } diff --git a/cmd.c b/cmd.c index a950a49a..eac3c199 100644 --- a/cmd.c +++ b/cmd.c @@ -207,6 +207,10 @@ const struct cmd_entry *cmd_table[] = { NULL }; +static void cmd_clear_state(struct cmd_state *); +static struct client *cmd_get_state_client(struct cmd_q *, int); +static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); + int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { @@ -388,6 +392,253 @@ usage: return (NULL); } +static void +cmd_clear_state(struct cmd_state *state) +{ + state->c = NULL; + + state->tflag.s = NULL; + state->tflag.wl = NULL; + state->tflag.wp = NULL; + state->tflag.idx = -1; + + state->sflag.s = NULL; + state->sflag.wl = NULL; + state->sflag.wp = NULL; + state->sflag.idx = -1; +} + +static struct client * +cmd_get_state_client(struct cmd_q *cmdq, int quiet) +{ + struct cmd *cmd = cmdq->cmd; + struct args *args = cmd->args; + + switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { + case 0: + return (cmd_find_client(cmdq, NULL, 1)); + case CMD_CLIENT_C: + return (cmd_find_client(cmdq, args_get(args, 'c'), quiet)); + case CMD_CLIENT_T: + return (cmd_find_client(cmdq, args_get(args, 't'), quiet)); + default: + fatalx("both -t and -c for %s", cmd->entry->name); + } +} + +static int +cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) +{ + struct cmd_state *state = &cmdq->state; + struct cmd_state_flag *statef = NULL; + const char *flag; + int flags = cmd->entry->flags, everything = 0; + int allflags = 0; + int prefer = !!(flags & CMD_PREFERUNATTACHED); + struct session *s; + struct window *w; + struct winlink *wl; + struct window_pane *wp; + + if (c == 't') { + statef = &cmdq->state.tflag; + allflags = CMD_ALL_T; + } else if (c == 's') { + statef = &cmdq->state.sflag; + allflags = CMD_ALL_S; + } + + /* + * If the command wants something and no argument is present, use the + * base command instead. + */ + flag = args_get(cmd->args, c); + if (flag == NULL) { + if ((flags & allflags) == 0) + return (0); /* doesn't care about flag */ + cmd = cmdq->cmd; + everything = 1; + flag = args_get(cmd->args, c); + } + + /* + * If no flag and the current command is allowed to fail, just skip to + * fill in as much we can, otherwise continue and fail later if needed. + */ + if (flag == NULL && (flags & CMD_CANFAIL)) + goto complete_everything; + + /* Fill in state using command (current or base) flags. */ + switch (cmd->entry->flags & allflags) { + case 0: + break; + case CMD_SESSION_T|CMD_PANE_T: + case CMD_SESSION_S|CMD_PANE_S: + if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { + statef->wl = cmd_find_pane(cmdq, flag, &statef->s, + &statef->wp); + if (statef->wl == NULL) + return (-1); + } else { + statef->s = cmd_find_session(cmdq, flag, prefer); + if (statef->s == NULL) + return (-1); + + s = statef->s; + if (flag == NULL) { + statef->wl = s->curw; + statef->wp = s->curw->window->active; + } else { + if ((w = window_find_by_id_str(flag)) != NULL) + wp = w->active; + else { + wp = window_pane_find_by_id_str(flag); + if (wp != NULL) + w = wp->window; + } + wl = winlink_find_by_window(&s->windows, w); + if (wl != NULL) { + statef->wl = wl; + statef->wp = wp; + } + } + } + break; + case CMD_MOVEW_R|CMD_INDEX_T: + case CMD_MOVEW_R|CMD_INDEX_S: + statef->s = cmd_find_session(cmdq, flag, prefer); + if (statef->s == NULL) { + statef->idx = cmd_find_index(cmdq, flag, &statef->s); + if (statef->idx == -2) + return (-1); + } + break; + case CMD_SESSION_T: + case CMD_SESSION_S: + statef->s = cmd_find_session(cmdq, flag, prefer); + if (statef->s == NULL) + return (-1); + break; + case CMD_WINDOW_T: + case CMD_WINDOW_S: + statef->wl = cmd_find_window(cmdq, flag, &statef->s); + if (statef->wl == NULL) + return (-1); + break; + case CMD_WINDOW_MARKED_T: + case CMD_WINDOW_MARKED_S: + statef->wl = cmd_find_window_marked(cmdq, flag, &statef->s); + if (statef->wl == NULL) + return (-1); + break; + case CMD_PANE_T: + case CMD_PANE_S: + statef->wl = cmd_find_pane(cmdq, flag, &statef->s, + &statef->wp); + if (statef->wl == NULL) + return (-1); + break; + case CMD_PANE_MARKED_S: + case CMD_PANE_MARKED_T: + statef->wl = cmd_find_pane_marked(cmdq, flag, &statef->s, + &statef->wp); + if (statef->wl == NULL) + return (-1); + break; + case CMD_INDEX_T: + case CMD_INDEX_S: + statef->idx = cmd_find_index(cmdq, flag, &statef->s); + if (statef->idx == -2) + return (-1); + break; + default: + fatalx("too many -%c for %s", c, cmd->entry->name); + } + + /* + * If this is still the current command, it wants what it asked for and + * nothing more. If it's the base command, fill in as much as possible + * because the current command may have different flags. + */ + if (!everything) + return (0); + +complete_everything: + if (statef->s == NULL) { + if (state->c != NULL) + statef->s = state->c->session; + if (statef->s == NULL) + statef->s = cmd_find_current(cmdq); + if (statef->s == NULL) { + if (flags & CMD_CANFAIL) + return (0); + + cmdq_error(cmdq, "no current session"); + return (-1); + } + } + if (statef->wl == NULL) + statef->wl = cmd_find_window(cmdq, flag, &statef->s); + if (statef->wp == NULL) + statef->wl = cmd_find_pane(cmdq, flag, &statef->s, &statef->wp); + + return (0); +} + +int +cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) +{ + struct cmd_state *state = &cmdq->state; + struct args *args = cmd->args; + const char *cflag, *tflag; + char *tmp; + int error; + + tmp = cmd_print(cmd); + log_debug("preparing state for: %s (client %d)", tmp, + cmdq->client != NULL ? cmdq->client->fd : -1); + free(tmp); + + /* Start with an empty state. */ + cmd_clear_state(state); + + /* + * If the command wants a client and provides -c or -t, use it. If not, + * try the base command instead via cmd_get_state_client. No client is + * allowed if no flags, otherwise it must be available. + */ + switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { + case 0: + state->c = cmd_get_state_client(cmdq, 1); + break; + case CMD_CLIENT_C: + cflag = args_get(args, 'c'); + if (cflag == NULL) + state->c = cmd_get_state_client(cmdq, 0); + else + state->c = cmd_find_client(cmdq, cflag, 0); + if (state->c == NULL) + return (-1); + break; + case CMD_CLIENT_T: + tflag = args_get(args, 't'); + if (tflag == NULL) + state->c = cmd_get_state_client(cmdq, 0); + else + state->c = cmd_find_client(cmdq, tflag, 0); + if (state->c == NULL) + return (-1); + break; + default: + fatalx("both -c and -t for %s", cmd->entry->name); + } + + error = cmd_set_state_flag(cmd, cmdq, 't'); + if (error == 0) + error = cmd_set_state_flag(cmd, cmdq, 's'); + return (error); +} + char * cmd_print(struct cmd *cmd) { diff --git a/tmux.h b/tmux.h index e10fbcdf..5924470e 100644 --- a/tmux.h +++ b/tmux.h @@ -1293,6 +1293,20 @@ struct args { char **argv; }; +/* Context for a command about to be executed. */ +struct cmd_state_flag { + struct session *s; + struct winlink *wl; + struct window_pane *wp; + int idx; + +}; +struct cmd_state { + struct client *c; + struct cmd_state_flag tflag; + struct cmd_state_flag sflag; +}; + /* Command and list of commands. */ struct cmd { const struct cmd_entry *entry; @@ -1343,6 +1357,8 @@ struct cmd_q { struct cmd_q_item *item; struct cmd *cmd; + struct cmd_state state; + time_t time; u_int number; @@ -1365,10 +1381,31 @@ struct cmd_entry { #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 +#define CMD_SESSION_T 0x4 +#define CMD_SESSION_S 0x8 +#define CMD_WINDOW_T 0x10 +#define CMD_WINDOW_S 0x20 +#define CMD_PANE_T 0x40 +#define CMD_PANE_S 0x80 +#define CMD_CLIENT_T 0x100 +#define CMD_CLIENT_C 0x200 +#define CMD_INDEX_T 0x400 +#define CMD_INDEX_S 0x800 +#define CMD_CANFAIL 0x1000 +#define CMD_PREFERUNATTACHED 0x2000 +#define CMD_MOVEW_R 0x4000 /* for movew -r only */ +#define CMD_PANE_MARKED_S 0x8000 +#define CMD_PANE_MARKED_T 0x10000 +#define CMD_WINDOW_MARKED_T 0x20000 +#define CMD_WINDOW_MARKED_S 0x40000 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; +#define CMD_ALL_T (CMD_SESSION_T|CMD_WINDOW_T|CMD_PANE_T|CMD_INDEX_T| \ + CMD_MOVEW_R|CMD_PANE_MARKED_T|CMD_WINDOW_MARKED_T) +#define CMD_ALL_S (CMD_SESSION_S|CMD_WINDOW_S|CMD_PANE_S|CMD_INDEX_S| \ + CMD_PANE_MARKED_S|CMD_WINDOW_MARKED_S) /* Key binding and key table. */ struct key_binding { @@ -1718,6 +1755,7 @@ char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); +int cmd_prepare_state(struct cmd *, struct cmd_q *); char *cmd_print(struct cmd *); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); @@ -1728,8 +1766,8 @@ char *cmd_template_replace(const char *, const char *, int); extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ -enum cmd_retval cmd_attach_session(struct cmd_q *, const char *, int, int, - const char *, int); +enum cmd_retval cmd_attach_session(struct cmd_q *, int, int, const char *, + int); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); From 9d191a6093673646c6c42a753ea3a337686b2b8d Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 15:00:37 +0000 Subject: [PATCH 490/703] Move logging into cmd_find_target rather than each function. --- cmd-find.c | 45 ++++++++++++++++++++++----------------------- cmd.c | 5 ++--- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 609297aa..f85f0707 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -76,7 +76,7 @@ int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *); int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); -void cmd_find_log_state(const char *, const char *, struct cmd_find_state *); +void cmd_find_log_state(const char *, struct cmd_find_state *); struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, enum cmd_find_type, int); @@ -827,6 +827,13 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, char *colon, *period, *copy = NULL; const char *session, *window, *pane; + /* Log the arguments. */ + if (target == NULL) + log_debug("%s: target none, type %d", __func__, type); + else + log_debug("%s: target %s, type %d", __func__, target, type); + log_debug("%s: cmdq %p, flags %#x", __func__, cmdq, flags); + /* Find current state. */ cmd_find_clear_state(¤t, cmdq, flags); if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { @@ -873,7 +880,7 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, cmdq_error(cmdq, "no mouse target"); goto error; } - return (&fs); + goto found; } /* Marked target is a plain ~ or {marked}. */ @@ -888,7 +895,7 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, fs.idx = fs.wl->idx; fs.w = fs.wl->window; fs.wp = marked_window_pane; - return (&fs); + goto found; } /* Find separators if they exist. */ @@ -1053,13 +1060,16 @@ current: free(copy); if (flags & CMD_FIND_WINDOW_INDEX) current.idx = -1; + cmd_find_log_state(__func__, ¤t); return (¤t); error: free(copy); + log_debug(" error"); return (NULL); found: + cmd_find_log_state(__func__, &fs); free(copy); return (&fs); @@ -1081,29 +1091,25 @@ no_pane: /* Log the result. */ void -cmd_find_log_state(const char *f, const char *target, struct cmd_find_state *fs) +cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) { - log_debug("%s: target %s%s", f, target == NULL ? "none" : target, - fs != NULL ? "" : " (failed)"); - if (fs == NULL) - return; if (fs->s != NULL) - log_debug("\ts=$%u", fs->s->id); + log_debug("%s: s=$%u", prefix, fs->s->id); else - log_debug("\ts=none"); + log_debug("%s: s=none", prefix); if (fs->wl != NULL) { - log_debug("\twl=%u %d w=@%u %s", fs->wl->idx, + log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, fs->wl->window == fs->w, fs->w->id, fs->w->name); } else - log_debug("\twl=none"); + log_debug("%s: wl=none", prefix); if (fs->wp != NULL) - log_debug("\twp=%%%u", fs->wp->id); + log_debug("%s: wp=%%%u", prefix, fs->wp->id); else - log_debug("\twp=none"); + log_debug("%s: wp=none", prefix); if (fs->idx != -1) - log_debug("\tidx=%d", fs->idx); + log_debug("%s: idx=%d", prefix, fs->idx); else - log_debug("\tidx=none"); + log_debug("%s: idx=none", prefix); } /* Find the current session. */ @@ -1114,7 +1120,6 @@ cmd_find_current(struct cmd_q *cmdq) int flags = CMD_FIND_QUIET; fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, flags); - cmd_find_log_state(__func__, NULL, fs); if (fs == NULL) return (NULL); @@ -1132,7 +1137,6 @@ cmd_find_session(struct cmd_q *cmdq, const char *target, int prefer_unattached) flags |= CMD_FIND_PREFER_UNATTACHED; fs = cmd_find_target(cmdq, target, CMD_FIND_SESSION, flags); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1146,7 +1150,6 @@ cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) struct cmd_find_state *fs; fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, 0); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1164,7 +1167,6 @@ cmd_find_window_marked(struct cmd_q *cmdq, const char *target, int flags = CMD_FIND_DEFAULT_MARKED; fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1181,7 +1183,6 @@ cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, struct cmd_find_state *fs; fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, 0); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1201,7 +1202,6 @@ cmd_find_pane_marked(struct cmd_q *cmdq, const char *target, int flags = CMD_FIND_DEFAULT_MARKED; fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (NULL); @@ -1275,7 +1275,6 @@ cmd_find_index(struct cmd_q *cmdq, const char *target, struct session **sp) int flags = CMD_FIND_WINDOW_INDEX; fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - cmd_find_log_state(__func__, target, fs); if (fs == NULL) return (-2); diff --git a/cmd.c b/cmd.c index eac3c199..fc483740 100644 --- a/cmd.c +++ b/cmd.c @@ -538,8 +538,8 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) if (statef->wl == NULL) return (-1); break; - case CMD_PANE_MARKED_S: case CMD_PANE_MARKED_T: + case CMD_PANE_MARKED_S: statef->wl = cmd_find_pane_marked(cmdq, flag, &statef->s, &statef->wp); if (statef->wl == NULL) @@ -595,8 +595,7 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) int error; tmp = cmd_print(cmd); - log_debug("preparing state for: %s (client %d)", tmp, - cmdq->client != NULL ? cmdq->client->fd : -1); + log_debug("preparing state for: %s (client %p)", tmp, cmdq->client); free(tmp); /* Start with an empty state. */ From ff599f4004aaa6aae325ece5cbc996e2dc6f0b4f Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 15:32:12 +0000 Subject: [PATCH 491/703] Remove the cmd_find_{session,window,pane,index} functions (which are just wrappers around cmd_find_target) and just use cmd_find_target directly. --- cmd-find.c | 149 ----------------------------------------------------- cmd.c | 119 ++++++++++++++++++++++++++---------------- tmux.h | 40 +++++++++----- 3 files changed, 103 insertions(+), 205 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index f85f0707..8059957b 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -27,31 +27,6 @@ #include "tmux.h" -#define CMD_FIND_PREFER_UNATTACHED 0x1 -#define CMD_FIND_QUIET 0x2 -#define CMD_FIND_WINDOW_INDEX 0x4 -#define CMD_FIND_DEFAULT_MARKED 0x8 -#define CMD_FIND_EXACT_SESSION 0x10 -#define CMD_FIND_EXACT_WINDOW 0x20 - -enum cmd_find_type { - CMD_FIND_PANE, - CMD_FIND_WINDOW, - CMD_FIND_SESSION, -}; - -struct cmd_find_state { - struct cmd_q *cmdq; - int flags; - struct cmd_find_state *current; - - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - int idx; -}; - struct session *cmd_find_try_TMUX(struct client *, struct window *); int cmd_find_client_better(struct client *, struct client *); struct client *cmd_find_best_client(struct client **, u_int); @@ -78,9 +53,6 @@ int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); void cmd_find_log_state(const char *, struct cmd_find_state *); -struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, - enum cmd_find_type, int); - const char *cmd_find_session_table[][2] = { { NULL, NULL } }; @@ -1112,106 +1084,6 @@ cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) log_debug("%s: idx=none", prefix); } -/* Find the current session. */ -struct session * -cmd_find_current(struct cmd_q *cmdq) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_QUIET; - - fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, flags); - if (fs == NULL) - return (NULL); - - return (fs->s); -} - -/* Find the target session or report an error and return NULL. */ -struct session * -cmd_find_session(struct cmd_q *cmdq, const char *target, int prefer_unattached) -{ - struct cmd_find_state *fs; - int flags = 0; - - if (prefer_unattached) - flags |= CMD_FIND_PREFER_UNATTACHED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_SESSION, flags); - if (fs == NULL) - return (NULL); - - return (fs->s); -} - -/* Find the target window or report an error and return NULL. */ -struct winlink * -cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) -{ - struct cmd_find_state *fs; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, 0); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - return (fs->wl); -} - -/* Find the target window, defaulting to marked rather than current. */ -struct winlink * -cmd_find_window_marked(struct cmd_q *cmdq, const char *target, - struct session **sp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_DEFAULT_MARKED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - return (fs->wl); -} - -/* Find the target pane and report an error and return NULL. */ -struct winlink * -cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, - struct window_pane **wpp) -{ - struct cmd_find_state *fs; - - fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, 0); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - if (wpp != NULL) - *wpp = fs->wp; - return (fs->wl); -} - -/* Find the target pane, defaulting to marked rather than current. */ -struct winlink * -cmd_find_pane_marked(struct cmd_q *cmdq, const char *target, - struct session **sp, struct window_pane **wpp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_DEFAULT_MARKED; - - fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags); - if (fs == NULL) - return (NULL); - - if (sp != NULL) - *sp = fs->s; - if (wpp != NULL) - *wpp = fs->wp; - return (fs->wl); -} - /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) @@ -1261,24 +1133,3 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) log_debug("%s: target %s, return %p", __func__, target, c); return (c); } - -/* - * Find the target session and window index, whether or not it exists in the - * session. Return -2 on error or -1 if no window index is specified. This is - * used when parsing an argument for a window target that may not exist (for - * example if it is going to be created). - */ -int -cmd_find_index(struct cmd_q *cmdq, const char *target, struct session **sp) -{ - struct cmd_find_state *fs; - int flags = CMD_FIND_WINDOW_INDEX; - - fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); - if (fs == NULL) - return (-2); - - if (sp != NULL) - *sp = fs->s; - return (fs->idx); -} diff --git a/cmd.c b/cmd.c index fc483740..0bb804ef 100644 --- a/cmd.c +++ b/cmd.c @@ -433,13 +433,14 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) struct cmd_state_flag *statef = NULL; const char *flag; int flags = cmd->entry->flags, everything = 0; - int allflags = 0; - int prefer = !!(flags & CMD_PREFERUNATTACHED); + int allflags = 0, targetflags; struct session *s; struct window *w; struct winlink *wl; struct window_pane *wp; + struct cmd_find_state *fs; + /* Set up state for either -t or -s. */ if (c == 't') { statef = &cmdq->state.tflag; allflags = CMD_ALL_T; @@ -469,26 +470,35 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) goto complete_everything; /* Fill in state using command (current or base) flags. */ + if (flags & CMD_PREFERUNATTACHED) + targetflags = CMD_FIND_PREFER_UNATTACHED; + else + targetflags = 0; switch (cmd->entry->flags & allflags) { case 0: break; case CMD_SESSION_T|CMD_PANE_T: case CMD_SESSION_S|CMD_PANE_S: if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { - statef->wl = cmd_find_pane(cmdq, flag, &statef->s, - &statef->wp); - if (statef->wl == NULL) + fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, + targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->wl = fs->wl; + statef->wp = fs->wp; } else { - statef->s = cmd_find_session(cmdq, flag, prefer); - if (statef->s == NULL) + fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, + targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; - s = statef->s; if (flag == NULL) { - statef->wl = s->curw; - statef->wp = s->curw->window->active; + statef->wl = statef->s->curw; + statef->wp = statef->s->curw->window->active; } else { + s = statef->s; if ((w = window_find_by_id_str(flag)) != NULL) wp = w->active; else { @@ -506,50 +516,58 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) break; case CMD_MOVEW_R|CMD_INDEX_T: case CMD_MOVEW_R|CMD_INDEX_S: - statef->s = cmd_find_session(cmdq, flag, prefer); - if (statef->s == NULL) { - statef->idx = cmd_find_index(cmdq, flag, &statef->s); - if (statef->idx == -2) + fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, targetflags); + if (fs != NULL) + statef->s = fs->s; + else { + fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, + CMD_FIND_WINDOW_INDEX); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->idx = fs->idx; } break; case CMD_SESSION_T: case CMD_SESSION_S: - statef->s = cmd_find_session(cmdq, flag, prefer); - if (statef->s == NULL) - return (-1); - break; - case CMD_WINDOW_T: - case CMD_WINDOW_S: - statef->wl = cmd_find_window(cmdq, flag, &statef->s); - if (statef->wl == NULL) + fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; break; case CMD_WINDOW_MARKED_T: case CMD_WINDOW_MARKED_S: - statef->wl = cmd_find_window_marked(cmdq, flag, &statef->s); - if (statef->wl == NULL) - return (-1); - break; - case CMD_PANE_T: - case CMD_PANE_S: - statef->wl = cmd_find_pane(cmdq, flag, &statef->s, - &statef->wp); - if (statef->wl == NULL) + targetflags |= CMD_FIND_DEFAULT_MARKED; + /* FALLTHROUGH */ + case CMD_WINDOW_T: + case CMD_WINDOW_S: + fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->wl = fs->wl; break; case CMD_PANE_MARKED_T: case CMD_PANE_MARKED_S: - statef->wl = cmd_find_pane_marked(cmdq, flag, &statef->s, - &statef->wp); - if (statef->wl == NULL) + targetflags |= CMD_FIND_DEFAULT_MARKED; + /* FALLTHROUGH */ + case CMD_PANE_T: + case CMD_PANE_S: + fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, targetflags); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->wl = fs->wl; + statef->wp = fs->wp; break; case CMD_INDEX_T: case CMD_INDEX_S: - statef->idx = cmd_find_index(cmdq, flag, &statef->s); - if (statef->idx == -2) + fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, + CMD_FIND_WINDOW_INDEX); + if (fs == NULL) return (-1); + statef->s = fs->s; + statef->idx = fs->idx; break; default: fatalx("too many -%c for %s", c, cmd->entry->name); @@ -567,21 +585,34 @@ complete_everything: if (statef->s == NULL) { if (state->c != NULL) statef->s = state->c->session; - if (statef->s == NULL) - statef->s = cmd_find_current(cmdq); + if (statef->s == NULL) { + fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, + CMD_FIND_QUIET); + if (fs != NULL) + statef->s = fs->s; + } if (statef->s == NULL) { if (flags & CMD_CANFAIL) return (0); - cmdq_error(cmdq, "no current session"); return (-1); } } - if (statef->wl == NULL) - statef->wl = cmd_find_window(cmdq, flag, &statef->s); - if (statef->wp == NULL) - statef->wl = cmd_find_pane(cmdq, flag, &statef->s, &statef->wp); - + if (statef->wl == NULL) { + fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, 0); + if (fs != NULL) { + statef->s = fs->s; + statef->wl = fs->wl; + } + } + if (statef->wp == NULL) { + fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, 0); + if (fs != NULL) { + statef->s = fs->s; + statef->wl = fs->wl; + statef->wp = fs->wp; + } + } return (0); } diff --git a/tmux.h b/tmux.h index 5924470e..7a30fc71 100644 --- a/tmux.h +++ b/tmux.h @@ -1407,6 +1407,32 @@ struct cmd_entry { #define CMD_ALL_S (CMD_SESSION_S|CMD_WINDOW_S|CMD_PANE_S|CMD_INDEX_S| \ CMD_PANE_MARKED_S|CMD_WINDOW_MARKED_S) +/* Command find structures. */ +enum cmd_find_type { + CMD_FIND_PANE, + CMD_FIND_WINDOW, + CMD_FIND_SESSION, +}; +struct cmd_find_state { + struct cmd_q *cmdq; + int flags; + struct cmd_find_state *current; + + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + int idx; +}; + +/* Command fine flags. */ +#define CMD_FIND_PREFER_UNATTACHED 0x1 +#define CMD_FIND_QUIET 0x2 +#define CMD_FIND_WINDOW_INDEX 0x4 +#define CMD_FIND_DEFAULT_MARKED 0x8 +#define CMD_FIND_EXACT_SESSION 0x10 +#define CMD_FIND_EXACT_WINDOW 0x20 + /* Key binding and key table. */ struct key_binding { key_code key; @@ -1734,19 +1760,9 @@ long long args_strtonum(struct args *, u_char, long long, long long, char **); /* cmd-find.c */ -struct session *cmd_find_current(struct cmd_q *); -struct session *cmd_find_session(struct cmd_q *, const char *, int); -struct winlink *cmd_find_window(struct cmd_q *, const char *, - struct session **); -struct winlink *cmd_find_window_marked(struct cmd_q *, const char *, - struct session **); -struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, - struct window_pane **); -struct winlink *cmd_find_pane_marked(struct cmd_q *, const char *, - struct session **, struct window_pane **); +struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, + enum cmd_find_type, int); struct client *cmd_find_client(struct cmd_q *, const char *, int); -int cmd_find_index(struct cmd_q *, const char *, - struct session **); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); From 208e2dad1e197d6256fe977d675809996e8bcf89 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 16:11:42 +0000 Subject: [PATCH 492/703] If command returns error, report it. --- cmd-queue.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-queue.c b/cmd-queue.c index 7b2675fa..c0fc26c6 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -199,6 +199,8 @@ cmdq_continue_one(struct cmd_q *cmdq) if (cmd_prepare_state(cmd, cmdq) != 0) goto error; retval = cmd->entry->exec(cmd, cmdq); + if (retval == CMD_RETURN_ERROR) + goto error; cmdq_guard(cmdq, "end", flags); return (retval); From 9b7697db62dc1c809955e8f8fea2868c230cc503 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 16:44:35 +0000 Subject: [PATCH 493/703] Change cmd_find_target to use a state struct from the caller. --- cmd-find.c | 102 ++++++++++++++++++++++++++++------------------------- cmd.c | 100 ++++++++++++++++++++++++++------------------------- tmux.h | 4 +-- 3 files changed, 108 insertions(+), 98 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 8059957b..139ec069 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -789,15 +789,18 @@ cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) fs->idx = -1; } -/* Split target into pieces and resolve for the given type. */ -struct cmd_find_state * -cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, - int flags) +/* + * Split target into pieces and resolve for the given type. Fills in the given + * state. Returns 0 on success or -1 on error. + */ +int +cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, + const char *target, enum cmd_find_type type, int flags) { - static struct cmd_find_state fs, current; - struct mouse_event *m; - char *colon, *period, *copy = NULL; - const char *session, *window, *pane; + struct cmd_find_state current; + struct mouse_event *m; + char *colon, *period, *copy = NULL; + const char *session, *window, *pane; /* Log the arguments. */ if (target == NULL) @@ -822,8 +825,8 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, } /* Clear new state. */ - cmd_find_clear_state(&fs, cmdq, flags); - fs.current = ¤t; + cmd_find_clear_state(fs, cmdq, flags); + fs->current = ¤t; /* An empty or NULL target is the current. */ if (target == NULL || *target == '\0') @@ -834,20 +837,20 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, m = &cmdq->item->mouse; switch (type) { case CMD_FIND_PANE: - fs.wp = cmd_mouse_pane(m, &fs.s, &fs.wl); - if (fs.wp != NULL) - fs.w = fs.wl->window; + fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); + if (fs->wp != NULL) + fs->w = fs->wl->window; break; case CMD_FIND_WINDOW: case CMD_FIND_SESSION: - fs.wl = cmd_mouse_window(m, &fs.s); - if (fs.wl != NULL) { - fs.w = fs.wl->window; - fs.wp = fs.w->active; + fs->wl = cmd_mouse_window(m, &fs->s); + if (fs->wl != NULL) { + fs->w = fs->wl->window; + fs->wp = fs->w->active; } break; } - if (fs.wp == NULL) { + if (fs->wp == NULL) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no mouse target"); goto error; @@ -862,11 +865,11 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, cmdq_error(cmdq, "no marked target"); goto error; } - fs.s = marked_session; - fs.wl = marked_winlink; - fs.idx = fs.wl->idx; - fs.w = fs.wl->window; - fs.wp = marked_window_pane; + fs->s = marked_session; + fs->wl = marked_winlink; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = marked_window_pane; goto found; } @@ -919,11 +922,11 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* Set exact match flags. */ if (session != NULL && *session == '=') { session++; - fs.flags |= CMD_FIND_EXACT_SESSION; + fs->flags |= CMD_FIND_EXACT_SESSION; } if (window != NULL && *window == '=') { window++; - fs.flags |= CMD_FIND_EXACT_WINDOW; + fs->flags |= CMD_FIND_EXACT_WINDOW; } /* Empty is the same as NULL. */ @@ -956,32 +959,32 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* If the session isn't NULL, look it up. */ if (session != NULL) { /* This will fill in session. */ - if (cmd_find_get_session(&fs, session) != 0) + if (cmd_find_get_session(fs, session) != 0) goto no_session; /* If window and pane are NULL, use that session's current. */ if (window == NULL && pane == NULL) { - fs.wl = fs.s->curw; - fs.idx = -1; - fs.w = fs.wl->window; - fs.wp = fs.w->active; + fs->wl = fs->s->curw; + fs->idx = -1; + fs->w = fs->wl->window; + fs->wp = fs->w->active; goto found; } /* If window is present but pane not, find window in session. */ if (window != NULL && pane == NULL) { /* This will fill in winlink and window. */ - if (cmd_find_get_window_with_session(&fs, window) != 0) + if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; if (~flags & CMD_FIND_WINDOW_INDEX) - fs.wp = fs.wl->window->active; + fs->wp = fs->wl->window->active; goto found; } /* If pane is present but window not, find pane. */ if (window == NULL && pane != NULL) { /* This will fill in winlink and window and pane. */ - if (cmd_find_get_pane_with_session(&fs, pane) != 0) + if (cmd_find_get_pane_with_session(fs, pane) != 0) goto no_pane; goto found; } @@ -990,10 +993,10 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, * If window and pane are present, find both in session. This * will fill in winlink and window. */ - if (cmd_find_get_window_with_session(&fs, window) != 0) + if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; /* This will fill in pane. */ - if (cmd_find_get_pane_with_window(&fs, pane) != 0) + if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } @@ -1001,10 +1004,10 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* No session. If window and pane, try them. */ if (window != NULL && pane != NULL) { /* This will fill in session, winlink and window. */ - if (cmd_find_get_window(&fs, window) != 0) + if (cmd_find_get_window(fs, window) != 0) goto no_window; /* This will fill in pane. */ - if (cmd_find_get_pane_with_window(&fs, pane) != 0) + if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } @@ -1012,38 +1015,41 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* If just window is present, try it. */ if (window != NULL && pane == NULL) { /* This will fill in session, winlink and window. */ - if (cmd_find_get_window(&fs, window) != 0) + if (cmd_find_get_window(fs, window) != 0) goto no_window; if (~flags & CMD_FIND_WINDOW_INDEX) - fs.wp = fs.wl->window->active; + fs->wp = fs->wl->window->active; goto found; } /* If just pane is present, try it. */ if (window == NULL && pane != NULL) { /* This will fill in session, winlink, window and pane. */ - if (cmd_find_get_pane(&fs, pane) != 0) + if (cmd_find_get_pane(fs, pane) != 0) goto no_pane; goto found; } current: - /* None is the current session. */ - free(copy); + /* Use the current session. */ if (flags & CMD_FIND_WINDOW_INDEX) current.idx = -1; - cmd_find_log_state(__func__, ¤t); - return (¤t); + memcpy(fs, ¤t, sizeof *fs); + goto found; error: - free(copy); + fs->current = NULL; log_debug(" error"); - return (NULL); + + free(copy); + return (-1); found: - cmd_find_log_state(__func__, &fs); + fs->current = NULL; + cmd_find_log_state(__func__, fs); + free(copy); - return (&fs); + return (0); no_session: if (~flags & CMD_FIND_QUIET) diff --git a/cmd.c b/cmd.c index 0bb804ef..43002eb3 100644 --- a/cmd.c +++ b/cmd.c @@ -433,12 +433,12 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) struct cmd_state_flag *statef = NULL; const char *flag; int flags = cmd->entry->flags, everything = 0; - int allflags = 0, targetflags; + int allflags = 0, targetflags, error; struct session *s; struct window *w; struct winlink *wl; struct window_pane *wp; - struct cmd_find_state *fs; + struct cmd_find_state fs; /* Set up state for either -t or -s. */ if (c == 't') { @@ -480,19 +480,19 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) case CMD_SESSION_T|CMD_PANE_T: case CMD_SESSION_S|CMD_PANE_S: if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { - fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, targetflags); - if (fs == NULL) + if (error != 0) return (-1); - statef->s = fs->s; - statef->wl = fs->wl; - statef->wp = fs->wp; + statef->s = fs.s; + statef->wl = fs.wl; + statef->wp = fs.wp; } else { - fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, - targetflags); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, + CMD_FIND_SESSION, targetflags); + if (error != 0) return (-1); - statef->s = fs->s; + statef->s = fs.s; if (flag == NULL) { statef->wl = statef->s->curw; @@ -516,24 +516,26 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) break; case CMD_MOVEW_R|CMD_INDEX_T: case CMD_MOVEW_R|CMD_INDEX_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, targetflags); - if (fs != NULL) - statef->s = fs->s; + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_SESSION, + targetflags); + if (error == 0) + statef->s = fs.s; else { - fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, - CMD_FIND_WINDOW_INDEX); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, + CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); + if (error != 0) return (-1); - statef->s = fs->s; - statef->idx = fs->idx; + statef->s = fs.s; + statef->idx = fs.idx; } break; case CMD_SESSION_T: case CMD_SESSION_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_SESSION, targetflags); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_SESSION, + targetflags); + if (error != 0) return (-1); - statef->s = fs->s; + statef->s = fs.s; break; case CMD_WINDOW_MARKED_T: case CMD_WINDOW_MARKED_S: @@ -541,11 +543,12 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) /* FALLTHROUGH */ case CMD_WINDOW_T: case CMD_WINDOW_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, targetflags); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, + targetflags); + if (error != 0) return (-1); - statef->s = fs->s; - statef->wl = fs->wl; + statef->s = fs.s; + statef->wl = fs.wl; break; case CMD_PANE_MARKED_T: case CMD_PANE_MARKED_S: @@ -553,21 +556,22 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) /* FALLTHROUGH */ case CMD_PANE_T: case CMD_PANE_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, targetflags); - if (fs == NULL) + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, + targetflags); + if (error != 0) return (-1); - statef->s = fs->s; - statef->wl = fs->wl; - statef->wp = fs->wp; + statef->s = fs.s; + statef->wl = fs.wl; + statef->wp = fs.wp; break; case CMD_INDEX_T: case CMD_INDEX_S: - fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); - if (fs == NULL) + if (error != 0) return (-1); - statef->s = fs->s; - statef->idx = fs->idx; + statef->s = fs.s; + statef->idx = fs.idx; break; default: fatalx("too many -%c for %s", c, cmd->entry->name); @@ -586,10 +590,10 @@ complete_everything: if (state->c != NULL) statef->s = state->c->session; if (statef->s == NULL) { - fs = cmd_find_target(cmdq, NULL, CMD_FIND_SESSION, - CMD_FIND_QUIET); - if (fs != NULL) - statef->s = fs->s; + error = cmd_find_target(&fs, cmdq, NULL, + CMD_FIND_SESSION, CMD_FIND_QUIET); + if (error == 0) + statef->s = fs.s; } if (statef->s == NULL) { if (flags & CMD_CANFAIL) @@ -599,18 +603,18 @@ complete_everything: } } if (statef->wl == NULL) { - fs = cmd_find_target(cmdq, flag, CMD_FIND_WINDOW, 0); - if (fs != NULL) { - statef->s = fs->s; - statef->wl = fs->wl; + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, 0); + if (error != 0) { + statef->s = fs.s; + statef->wl = fs.wl; } } if (statef->wp == NULL) { - fs = cmd_find_target(cmdq, flag, CMD_FIND_PANE, 0); - if (fs != NULL) { - statef->s = fs->s; - statef->wl = fs->wl; - statef->wp = fs->wp; + error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, 0); + if (error != 0) { + statef->s = fs.s; + statef->wl = fs.wl; + statef->wp = fs.wp; } } return (0); diff --git a/tmux.h b/tmux.h index 7a30fc71..e6fb521e 100644 --- a/tmux.h +++ b/tmux.h @@ -1760,8 +1760,8 @@ long long args_strtonum(struct args *, u_char, long long, long long, char **); /* cmd-find.c */ -struct cmd_find_state *cmd_find_target(struct cmd_q *, const char *, - enum cmd_find_type, int); +int cmd_find_target(struct cmd_find_state *, struct cmd_q *, + const char *, enum cmd_find_type, int); struct client *cmd_find_client(struct cmd_q *, const char *, int); /* cmd.c */ From 9f5aca62a94f46be9c7637ebd3269d012c9346a5 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 17:55:14 +0000 Subject: [PATCH 494/703] Use struct cmd_find_state directly and remove cmd_state_flag, also change so that winlink is set even if an index is too. --- cmd-find.c | 20 +++++++++------ cmd-new-window.c | 7 +----- tmux.h | 63 +++++++++++++++++++++--------------------------- 3 files changed, 41 insertions(+), 49 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 139ec069..582f1759 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -467,11 +467,10 @@ cmd_find_get_window(struct cmd_find_state *fs, const char *window) /* Otherwise try as a session itself. */ if (cmd_find_get_session(fs, window) == 0) { - if (~fs->flags & CMD_FIND_WINDOW_INDEX) { - fs->wl = fs->s->curw; - fs->w = fs->wl->window; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + if (~fs->flags & CMD_FIND_WINDOW_INDEX) fs->idx = fs->wl->idx; - } return (0); } @@ -493,6 +492,13 @@ cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) log_debug("%s: %s", __func__, window); exact = (fs->flags & CMD_FIND_EXACT_WINDOW); + /* + * Start with the current window as the default. So if only an index is + * found, the window will be the current. + */ + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + /* Check for window ids starting with @. */ if (*window == '@') { fs->w = window_find_by_id_str(window); @@ -976,8 +982,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, /* This will fill in winlink and window. */ if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; - if (~flags & CMD_FIND_WINDOW_INDEX) - fs->wp = fs->wl->window->active; + fs->wp = fs->wl->window->active; goto found; } @@ -1017,8 +1022,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, /* This will fill in session, winlink and window. */ if (cmd_find_get_window(fs, window) != 0) goto no_window; - if (~flags & CMD_FIND_WINDOW_INDEX) - fs->wp = fs->wl->window->active; + fs->wp = fs->wl->window->active; goto found; } diff --git a/cmd-new-window.c b/cmd-new-window.c index 8154bdfb..18d2952b 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -39,12 +39,7 @@ const struct cmd_entry cmd_new_window_entry = { "ac:dF:kn:Pt:", 0, -1, "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", - /* - * Using PREP_CANFAIL here ensures that the wl is filled in - * regardless; making PREP_INDEX the thing we want -t to be used for - * in the specific case. - */ - CMD_INDEX_T|CMD_CANFAIL, + CMD_INDEX_T, cmd_new_window_exec }; diff --git a/tmux.h b/tmux.h index e6fb521e..5724b536 100644 --- a/tmux.h +++ b/tmux.h @@ -1293,18 +1293,37 @@ struct args { char **argv; }; -/* Context for a command about to be executed. */ -struct cmd_state_flag { - struct session *s; - struct winlink *wl; - struct window_pane *wp; - int idx; - +/* Command find structures. */ +enum cmd_find_type { + CMD_FIND_PANE, + CMD_FIND_WINDOW, + CMD_FIND_SESSION, }; +struct cmd_find_state { + struct cmd_q *cmdq; + int flags; + struct cmd_find_state *current; + + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + int idx; +}; + +/* Command find flags. */ +#define CMD_FIND_PREFER_UNATTACHED 0x1 +#define CMD_FIND_QUIET 0x2 +#define CMD_FIND_WINDOW_INDEX 0x4 +#define CMD_FIND_DEFAULT_MARKED 0x8 +#define CMD_FIND_EXACT_SESSION 0x10 +#define CMD_FIND_EXACT_WINDOW 0x20 + +/* Context for command being executed. */ struct cmd_state { struct client *c; - struct cmd_state_flag tflag; - struct cmd_state_flag sflag; + struct cmd_find_state tflag; + struct cmd_find_state sflag; }; /* Command and list of commands. */ @@ -1407,32 +1426,6 @@ struct cmd_entry { #define CMD_ALL_S (CMD_SESSION_S|CMD_WINDOW_S|CMD_PANE_S|CMD_INDEX_S| \ CMD_PANE_MARKED_S|CMD_WINDOW_MARKED_S) -/* Command find structures. */ -enum cmd_find_type { - CMD_FIND_PANE, - CMD_FIND_WINDOW, - CMD_FIND_SESSION, -}; -struct cmd_find_state { - struct cmd_q *cmdq; - int flags; - struct cmd_find_state *current; - - struct session *s; - struct winlink *wl; - struct window *w; - struct window_pane *wp; - int idx; -}; - -/* Command fine flags. */ -#define CMD_FIND_PREFER_UNATTACHED 0x1 -#define CMD_FIND_QUIET 0x2 -#define CMD_FIND_WINDOW_INDEX 0x4 -#define CMD_FIND_DEFAULT_MARKED 0x8 -#define CMD_FIND_EXACT_SESSION 0x10 -#define CMD_FIND_EXACT_WINDOW 0x20 - /* Key binding and key table. */ struct key_binding { key_code key; From 50f8ead4e602a8c47be7a715fff9d248a896f648 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 17:58:26 +0000 Subject: [PATCH 495/703] Don't log an error when doing the first check for move-window. --- cmd.c | 88 ++++++++++++++++++++++------------------------------------- 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/cmd.c b/cmd.c index 43002eb3..d7714ba2 100644 --- a/cmd.c +++ b/cmd.c @@ -430,7 +430,7 @@ static int cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) { struct cmd_state *state = &cmdq->state; - struct cmd_state_flag *statef = NULL; + struct cmd_find_state *fsf = NULL; const char *flag; int flags = cmd->entry->flags, everything = 0; int allflags = 0, targetflags, error; @@ -438,14 +438,13 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) struct window *w; struct winlink *wl; struct window_pane *wp; - struct cmd_find_state fs; /* Set up state for either -t or -s. */ if (c == 't') { - statef = &cmdq->state.tflag; + fsf = &cmdq->state.tflag; allflags = CMD_ALL_T; } else if (c == 's') { - statef = &cmdq->state.sflag; + fsf = &cmdq->state.sflag; allflags = CMD_ALL_S; } @@ -480,25 +479,21 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) case CMD_SESSION_T|CMD_PANE_T: case CMD_SESSION_S|CMD_PANE_S: if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, targetflags); if (error != 0) return (-1); - statef->s = fs.s; - statef->wl = fs.wl; - statef->wp = fs.wp; } else { - error = cmd_find_target(&fs, cmdq, flag, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, targetflags); if (error != 0) return (-1); - statef->s = fs.s; if (flag == NULL) { - statef->wl = statef->s->curw; - statef->wp = statef->s->curw->window->active; + fsf->wl = fsf->s->curw; + fsf->wp = fsf->s->curw->window->active; } else { - s = statef->s; + s = fsf->s; if ((w = window_find_by_id_str(flag)) != NULL) wp = w->active; else { @@ -508,34 +503,29 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) } wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { - statef->wl = wl; - statef->wp = wp; + fsf->wl = wl; + fsf->wp = wp; } } } break; case CMD_MOVEW_R|CMD_INDEX_T: case CMD_MOVEW_R|CMD_INDEX_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_SESSION, - targetflags); - if (error == 0) - statef->s = fs.s; - else { - error = cmd_find_target(&fs, cmdq, flag, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, + targetflags|CMD_FIND_QUIET); + if (error != 0) { + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); if (error != 0) return (-1); - statef->s = fs.s; - statef->idx = fs.idx; } break; case CMD_SESSION_T: case CMD_SESSION_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_SESSION, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, targetflags); if (error != 0) return (-1); - statef->s = fs.s; break; case CMD_WINDOW_MARKED_T: case CMD_WINDOW_MARKED_S: @@ -543,12 +533,10 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) /* FALLTHROUGH */ case CMD_WINDOW_T: case CMD_WINDOW_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, targetflags); if (error != 0) return (-1); - statef->s = fs.s; - statef->wl = fs.wl; break; case CMD_PANE_MARKED_T: case CMD_PANE_MARKED_S: @@ -556,22 +544,17 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) /* FALLTHROUGH */ case CMD_PANE_T: case CMD_PANE_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, targetflags); if (error != 0) return (-1); - statef->s = fs.s; - statef->wl = fs.wl; - statef->wp = fs.wp; break; case CMD_INDEX_T: case CMD_INDEX_S: - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); if (error != 0) return (-1); - statef->s = fs.s; - statef->idx = fs.idx; break; default: fatalx("too many -%c for %s", c, cmd->entry->name); @@ -586,36 +569,31 @@ cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) return (0); complete_everything: - if (statef->s == NULL) { + if (fsf->s == NULL) { if (state->c != NULL) - statef->s = state->c->session; - if (statef->s == NULL) { - error = cmd_find_target(&fs, cmdq, NULL, + fsf->s = state->c->session; + if (fsf->s == NULL) { + error = cmd_find_target(fsf, cmdq, NULL, CMD_FIND_SESSION, CMD_FIND_QUIET); - if (error == 0) - statef->s = fs.s; + if (error != 0) + fsf->s = NULL; } - if (statef->s == NULL) { + if (fsf->s == NULL) { if (flags & CMD_CANFAIL) return (0); cmdq_error(cmdq, "no current session"); return (-1); } } - if (statef->wl == NULL) { - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_WINDOW, 0); - if (error != 0) { - statef->s = fs.s; - statef->wl = fs.wl; - } + if (fsf->wl == NULL) { + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, 0); + if (error != 0) + return (-1); } - if (statef->wp == NULL) { - error = cmd_find_target(&fs, cmdq, flag, CMD_FIND_PANE, 0); - if (error != 0) { - statef->s = fs.s; - statef->wl = fs.wl; - statef->wp = fs.wp; - } + if (fsf->wp == NULL) { + error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, 0); + if (error != 0) + return (-1); } return (0); } From fd47084224ef1f0ddd22de1f40bd223bd7be5cf6 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 18:15:13 +0000 Subject: [PATCH 496/703] show-options and environment need CANFAIL flag. --- cmd-show-environment.c | 2 +- cmd-show-options.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 8feb2e22..723d3039 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -37,7 +37,7 @@ const struct cmd_entry cmd_show_environment_entry = { "show-environment", "showenv", "gst:", 0, 1, "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - CMD_SESSION_T, + CMD_SESSION_T|CMD_CANFAIL, cmd_show_environment_exec }; diff --git a/cmd-show-options.c b/cmd-show-options.c index 5437ec73..3b39a532 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -38,7 +38,7 @@ const struct cmd_entry cmd_show_options_entry = { "show-options", "show", "gqst:vw", 0, 1, "[-gqsvw] [-t target-session|target-window] [option]", - CMD_WINDOW_T, + CMD_WINDOW_T|CMD_CANFAIL, cmd_show_options_exec }; @@ -46,7 +46,7 @@ const struct cmd_entry cmd_show_window_options_entry = { "show-window-options", "showw", "gvt:", 0, 1, "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - CMD_WINDOW_T, + CMD_WINDOW_T|CMD_CANFAIL, cmd_show_options_exec }; From 66d1193a000e7ee0d6924f3c766b013ce0bc9e52 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 18:27:47 +0000 Subject: [PATCH 497/703] Remove an unnecessary function. --- cmd.c | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/cmd.c b/cmd.c index d7714ba2..789bcefb 100644 --- a/cmd.c +++ b/cmd.c @@ -207,9 +207,8 @@ const struct cmd_entry *cmd_table[] = { NULL }; -static void cmd_clear_state(struct cmd_state *); -static struct client *cmd_get_state_client(struct cmd_q *, int); -static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); +static void cmd_clear_state(struct cmd_state *); +static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) @@ -408,24 +407,6 @@ cmd_clear_state(struct cmd_state *state) state->sflag.idx = -1; } -static struct client * -cmd_get_state_client(struct cmd_q *cmdq, int quiet) -{ - struct cmd *cmd = cmdq->cmd; - struct args *args = cmd->args; - - switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { - case 0: - return (cmd_find_client(cmdq, NULL, 1)); - case CMD_CLIENT_C: - return (cmd_find_client(cmdq, args_get(args, 'c'), quiet)); - case CMD_CLIENT_T: - return (cmd_find_client(cmdq, args_get(args, 't'), quiet)); - default: - fatalx("both -t and -c for %s", cmd->entry->name); - } -} - static int cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) { @@ -603,7 +584,6 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) { struct cmd_state *state = &cmdq->state; struct args *args = cmd->args; - const char *cflag, *tflag; char *tmp; int error; @@ -621,23 +601,15 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) */ switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { case 0: - state->c = cmd_get_state_client(cmdq, 1); + state->c = cmd_find_client(cmdq, NULL, 1); break; case CMD_CLIENT_C: - cflag = args_get(args, 'c'); - if (cflag == NULL) - state->c = cmd_get_state_client(cmdq, 0); - else - state->c = cmd_find_client(cmdq, cflag, 0); + state->c = cmd_find_client(cmdq, args_get(args, 'c'), 0); if (state->c == NULL) return (-1); break; case CMD_CLIENT_T: - tflag = args_get(args, 't'); - if (tflag == NULL) - state->c = cmd_get_state_client(cmdq, 0); - else - state->c = cmd_find_client(cmdq, tflag, 0); + state->c = cmd_find_client(cmdq, args_get(args, 't'), 0); if (state->c == NULL) return (-1); break; From 72948d9f1d16dedf18dcb9b7ee96cc7ca803ef40 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 18:31:47 +0000 Subject: [PATCH 498/703] -c needs to be able for fail for display-message. --- cmd-display-message.c | 2 +- cmd.c | 15 ++++++++++----- tmux.h | 1 + 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/cmd-display-message.c b/cmd-display-message.c index 201f8b75..ddc55846 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_display_message_entry = { "c:pt:F:", 0, 1, "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", - CMD_CLIENT_C|CMD_PANE_T, + CMD_CLIENT_C|CMD_PANE_T|CMD_CLIENT_CANFAIL, cmd_display_message_exec }; diff --git a/cmd.c b/cmd.c index 789bcefb..93bab204 100644 --- a/cmd.c +++ b/cmd.c @@ -585,7 +585,7 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) struct cmd_state *state = &cmdq->state; struct args *args = cmd->args; char *tmp; - int error; + int error, quiet; tmp = cmd_print(cmd); log_debug("preparing state for: %s (client %p)", tmp, cmdq->client); @@ -594,6 +594,11 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) /* Start with an empty state. */ cmd_clear_state(state); + /* No error messages if can fail. */ + quiet = 0; + if (cmd->entry->flags & CMD_CLIENT_CANFAIL) + quiet = 1; + /* * If the command wants a client and provides -c or -t, use it. If not, * try the base command instead via cmd_get_state_client. No client is @@ -604,13 +609,13 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) state->c = cmd_find_client(cmdq, NULL, 1); break; case CMD_CLIENT_C: - state->c = cmd_find_client(cmdq, args_get(args, 'c'), 0); - if (state->c == NULL) + state->c = cmd_find_client(cmdq, args_get(args, 'c'), quiet); + if (!quiet && state->c == NULL) return (-1); break; case CMD_CLIENT_T: - state->c = cmd_find_client(cmdq, args_get(args, 't'), 0); - if (state->c == NULL) + state->c = cmd_find_client(cmdq, args_get(args, 't'), quiet); + if (!quiet && state->c == NULL) return (-1); break; default: diff --git a/tmux.h b/tmux.h index 5724b536..8b26431d 100644 --- a/tmux.h +++ b/tmux.h @@ -1417,6 +1417,7 @@ struct cmd_entry { #define CMD_PANE_MARKED_T 0x10000 #define CMD_WINDOW_MARKED_T 0x20000 #define CMD_WINDOW_MARKED_S 0x40000 +#define CMD_CLIENT_CANFAIL 0x80000 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); From 899bee0056b2d90b1c40c800473014b458ebd63d Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 21:17:37 +0000 Subject: [PATCH 499/703] Actually I thought cmd_get_state_client was unnecessary but it will be needed. --- cmd.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/cmd.c b/cmd.c index 93bab204..9f05a11e 100644 --- a/cmd.c +++ b/cmd.c @@ -207,8 +207,9 @@ const struct cmd_entry *cmd_table[] = { NULL }; -static void cmd_clear_state(struct cmd_state *); -static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); +static void cmd_clear_state(struct cmd_state *); +static struct client *cmd_get_state_client(struct cmd_q *, int); +static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) @@ -579,11 +580,30 @@ complete_everything: return (0); } +static struct client * +cmd_get_state_client(struct cmd_q *cmdq, int quiet) +{ + struct cmd *cmd = cmdq->cmd; + struct args *args = cmd->args; + + switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { + case 0: + return (cmd_find_client(cmdq, NULL, 1)); + case CMD_CLIENT_C: + return (cmd_find_client(cmdq, args_get(args, 'c'), quiet)); + case CMD_CLIENT_T: + return (cmd_find_client(cmdq, args_get(args, 't'), quiet)); + default: + fatalx("both -t and -c for %s", cmd->entry->name); + } +} + int cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) { struct cmd_state *state = &cmdq->state; struct args *args = cmd->args; + const char *cflag, *tflag; char *tmp; int error, quiet; @@ -606,14 +626,23 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) */ switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { case 0: - state->c = cmd_find_client(cmdq, NULL, 1); + state->c = cmd_get_state_client(cmdq, 1); break; case CMD_CLIENT_C: - state->c = cmd_find_client(cmdq, args_get(args, 'c'), quiet); + cflag = args_get(args, 'c'); + if (cflag == NULL) + state->c = cmd_get_state_client(cmdq, quiet); + else + state->c = cmd_find_client(cmdq, cflag, quiet); if (!quiet && state->c == NULL) return (-1); break; case CMD_CLIENT_T: + tflag = args_get(args, 't'); + if (tflag == NULL) + state->c = cmd_get_state_client(cmdq, 0); + else + state->c = cmd_find_client(cmdq, tflag, 0); state->c = cmd_find_client(cmdq, args_get(args, 't'), quiet); if (!quiet && state->c == NULL) return (-1); From ecfeee2e8255a77f82a07124c93c8dbc7683c421 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 13 Dec 2015 21:53:57 +0000 Subject: [PATCH 500/703] Use member names in cmd_entry definitions so I stop getting confused about the order. --- cmd-attach-session.c | 13 +++++++---- cmd-bind-key.c | 14 ++++++++---- cmd-break-pane.c | 13 +++++++---- cmd-capture-pane.c | 15 +++++++----- cmd-choose-buffer.c | 13 +++++++---- cmd-choose-client.c | 13 +++++++---- cmd-choose-tree.c | 42 +++++++++++++++++++++------------- cmd-clear-history.c | 13 +++++++---- cmd-command-prompt.c | 14 ++++++++---- cmd-confirm-before.c | 13 +++++++---- cmd-copy-mode.c | 26 +++++++++++++-------- cmd-detach-client.c | 26 +++++++++++++-------- cmd-display-message.c | 15 +++++++----- cmd-display-panes.c | 13 +++++++---- cmd-find-window.c | 13 +++++++---- cmd-if-shell.c | 14 ++++++++---- cmd-join-pane.c | 26 +++++++++++++-------- cmd-kill-pane.c | 13 +++++++---- cmd-kill-server.c | 26 +++++++++++++-------- cmd-kill-session.c | 13 +++++++---- cmd-kill-window.c | 26 +++++++++++++-------- cmd-list-buffers.c | 13 +++++++---- cmd-list-clients.c | 13 +++++++---- cmd-list-keys.c | 26 +++++++++++++-------- cmd-list-panes.c | 13 +++++++---- cmd-list-sessions.c | 13 +++++++---- cmd-list-windows.c | 13 +++++++---- cmd-load-buffer.c | 13 +++++++---- cmd-lock-server.c | 39 +++++++++++++++++++------------ cmd-move-window.c | 26 +++++++++++++-------- cmd-new-session.c | 30 ++++++++++++++---------- cmd-new-window.c | 15 +++++++----- cmd-paste-buffer.c | 14 ++++++++---- cmd-pipe-pane.c | 13 +++++++---- cmd-refresh-client.c | 13 +++++++---- cmd-rename-session.c | 13 +++++++---- cmd-rename-window.c | 13 +++++++---- cmd-resize-pane.c | 14 ++++++++---- cmd-respawn-pane.c | 13 +++++++---- cmd-respawn-window.c | 13 +++++++---- cmd-rotate-window.c | 13 +++++++---- cmd-run-shell.c | 13 +++++++---- cmd-save-buffer.c | 26 +++++++++++++-------- cmd-select-layout.c | 39 +++++++++++++++++++------------ cmd-select-pane.c | 26 +++++++++++++-------- cmd-select-window.c | 52 ++++++++++++++++++++++++++---------------- cmd-send-keys.c | 26 +++++++++++++-------- cmd-set-buffer.c | 26 +++++++++++++-------- cmd-set-environment.c | 13 +++++++---- cmd-set-hook.c | 26 +++++++++++++-------- cmd-set-option.c | 26 +++++++++++++-------- cmd-show-environment.c | 13 +++++++---- cmd-show-messages.c | 26 +++++++++++++-------- cmd-show-options.c | 26 +++++++++++++-------- cmd-source-file.c | 13 +++++++---- cmd-split-window.c | 15 +++++++----- cmd-swap-pane.c | 13 +++++++---- cmd-swap-window.c | 13 +++++++---- cmd-switch-client.c | 16 ++++++++----- cmd-unbind-key.c | 13 +++++++---- cmd-wait-for.c | 13 +++++++---- cmd.c | 6 ++--- tmux.h | 18 ++++++++------- 63 files changed, 716 insertions(+), 449 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index c29e9d1a..976f180b 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -33,11 +33,14 @@ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { - "attach-session", "attach", - "c:dErt:", 0, 0, - "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - CMD_STARTSERVER|CMD_SESSION_T|CMD_PANE_T|CMD_PREFERUNATTACHED, - cmd_attach_session_exec + .name = "attach-session", + .alias = "attach", + + .args = { "c:dErt:", 0, 0 }, + .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_STARTSERVER|CMD_SESSION_T|CMD_PANE_T|CMD_PREFERUNATTACHED, + .exec = cmd_attach_session_exec }; enum cmd_retval diff --git a/cmd-bind-key.c b/cmd-bind-key.c index b13409cb..df3285f7 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -33,11 +33,15 @@ enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, key_code); const struct cmd_entry cmd_bind_key_entry = { - "bind-key", "bind", - "cnrt:T:", 1, -1, - "[-cnr] [-t mode-table] [-T key-table] key command [arguments]", - 0, - cmd_bind_key_exec + .name = "bind-key", + .alias = "bind", + + .args = { "cnrt:T:", 1, -1 }, + .usage = "[-cnr] [-t mode-table] [-T key-table] key command " + "[arguments]", + + .flags = 0, + .exec = cmd_bind_key_exec }; enum cmd_retval diff --git a/cmd-break-pane.c b/cmd-break-pane.c index eb07fb87..62625c71 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { - "break-pane", "breakp", - "dPF:s:t:", 0, 0, - "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, - CMD_PANE_S|CMD_INDEX_T, - cmd_break_pane_exec + .name = "break-pane", + .alias = "breakp", + + .args = { "dPF:s:t:", 0, 0 }, + .usage = "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, + + .flags = CMD_PANE_S|CMD_INDEX_T, + .exec = cmd_break_pane_exec }; enum cmd_retval diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 9d22a0f2..0e3644e9 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -36,12 +36,15 @@ char *cmd_capture_pane_history(struct args *, struct cmd_q *, struct window_pane *, size_t *); const struct cmd_entry cmd_capture_pane_entry = { - "capture-pane", "capturep", - "ab:CeE:JpPqS:t:", 0, 0, - "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]" - CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_capture_pane_exec + .name = "capture-pane", + .alias = "capturep", + + .args = { "ab:CeE:JpPqS:t:", 0, 0 }, + .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " + "[-S start-line]" CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_capture_pane_exec }; char * diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 4418d415..fac792e9 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -33,11 +33,14 @@ enum cmd_retval cmd_choose_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_buffer_entry = { - "choose-buffer", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - CMD_WINDOW_T, - cmd_choose_buffer_exec + .name = "choose-buffer", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + + .flags = CMD_WINDOW_T, + .exec = cmd_choose_buffer_exec }; enum cmd_retval diff --git a/cmd-choose-client.c b/cmd-choose-client.c index c58bc826..ab0f6c50 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -38,11 +38,14 @@ enum cmd_retval cmd_choose_client_exec(struct cmd *, struct cmd_q *); void cmd_choose_client_callback(struct window_choose_data *); const struct cmd_entry cmd_choose_client_entry = { - "choose-client", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - CMD_WINDOW_T, - cmd_choose_client_exec + .name = "choose-client", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + + .flags = CMD_WINDOW_T, + .exec = cmd_choose_client_exec }; struct cmd_choose_client_data { diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 766978fe..faa9150c 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -44,28 +44,38 @@ enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_tree_entry = { - "choose-tree", NULL, - "S:W:swub:c:t:", 0, 1, - "[-suw] [-b session-template] [-c window template] [-S format] " \ - "[-W format] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_choose_tree_exec + .name = "choose-tree", + .alias = NULL, + + .args = { "S:W:swub:c:t:", 0, 1 }, + .usage = "[-suw] [-b session-template] [-c window template] " + "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_session_entry = { - "choose-session", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - CMD_WINDOW_T, - cmd_choose_tree_exec + .name = "choose-session", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + + .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", + .flags = CMD_WINDOW_T, + + .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_window_entry = { - "choose-window", NULL, - "F:t:", 0, 1, - CMD_TARGET_WINDOW_USAGE "[-F format] [template]", - CMD_WINDOW_T, - cmd_choose_tree_exec + .name = "choose-window", + .alias = NULL, + + .args = { "F:t:", 0, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]", + + .flags = CMD_WINDOW_T, + .exec = cmd_choose_tree_exec }; enum cmd_retval diff --git a/cmd-clear-history.c b/cmd-clear-history.c index a76cab80..a8c2bfdc 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -27,11 +27,14 @@ enum cmd_retval cmd_clear_history_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_clear_history_entry = { - "clear-history", "clearhist", - "t:", 0, 0, - CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_clear_history_exec + .name = "clear-history", + .alias = "clearhist", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_clear_history_exec }; enum cmd_retval diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 900fceb9..64b24bb5 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -35,11 +35,15 @@ int cmd_command_prompt_callback(void *, const char *); void cmd_command_prompt_free(void *); const struct cmd_entry cmd_command_prompt_entry = { - "command-prompt", NULL, - "I:p:t:", 0, 1, - "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", - CMD_CLIENT_T, - cmd_command_prompt_exec + .name = "command-prompt", + .alias = NULL, + + .args = { "I:p:t:", 0, 1 }, + .usage = "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " + "[template]", + + .flags = CMD_CLIENT_T, + .exec = cmd_command_prompt_exec }; struct cmd_command_prompt_cdata { diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 17a575a2..90e16992 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -34,11 +34,14 @@ int cmd_confirm_before_callback(void *, const char *); void cmd_confirm_before_free(void *); const struct cmd_entry cmd_confirm_before_entry = { - "confirm-before", "confirm", - "p:t:", 1, 1, - "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - CMD_CLIENT_T, - cmd_confirm_before_exec + .name = "confirm-before", + .alias = "confirm", + + .args = { "p:t:", 1, 1 }, + .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", + + .flags = CMD_CLIENT_T, + .exec = cmd_confirm_before_exec }; struct cmd_confirm_before_data { diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 79d06cee..7c6f6ca7 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -27,19 +27,25 @@ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { - "copy-mode", NULL, - "Met:u", 0, 0, - "[-Mu] " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_copy_mode_exec + .name = "copy-mode", + .alias = NULL, + + .args = { "Met:u", 0, 0 }, + .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_copy_mode_exec }; const struct cmd_entry cmd_clock_mode_entry = { - "clock-mode", NULL, - "t:", 0, 0, - CMD_TARGET_PANE_USAGE, - 0, - cmd_copy_mode_exec + .name = "clock-mode", + .alias = NULL, + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_PANE_USAGE, + + .flags = 0, + .exec = cmd_copy_mode_exec }; enum cmd_retval diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 86f8063c..6f6adbb9 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -29,19 +29,25 @@ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { - "detach-client", "detach", - "as:t:P", 0, 0, - "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, - CMD_READONLY|CMD_CLIENT_T|CMD_SESSION_S, - cmd_detach_client_exec + .name = "detach-client", + .alias = "detach", + + .args = { "as:t:P", 0, 0 }, + .usage = "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_READONLY|CMD_CLIENT_T|CMD_SESSION_S, + .exec = cmd_detach_client_exec }; const struct cmd_entry cmd_suspend_client_entry = { - "suspend-client", "suspendc", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_detach_client_exec + .name = "suspend-client", + .alias = "suspendc", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_detach_client_exec }; enum cmd_retval diff --git a/cmd-display-message.c b/cmd-display-message.c index ddc55846..fdfa6ae2 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -35,12 +35,15 @@ enum cmd_retval cmd_display_message_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_message_entry = { - "display-message", "display", - "c:pt:F:", 0, 1, - "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE - " [message]", - CMD_CLIENT_C|CMD_PANE_T|CMD_CLIENT_CANFAIL, - cmd_display_message_exec + .name = "display-message", + .alias = "display", + + .args = { "c:pt:F:", 0, 1 }, + .usage = "[-p] [-c target-client] [-F format] " + CMD_TARGET_PANE_USAGE " [message]", + + .flags = CMD_CLIENT_C|CMD_PANE_T|CMD_CLIENT_CANFAIL, + .exec = cmd_display_message_exec }; enum cmd_retval diff --git a/cmd-display-panes.c b/cmd-display-panes.c index 714ee19e..db1e4b08 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -27,11 +27,14 @@ enum cmd_retval cmd_display_panes_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_panes_entry = { - "display-panes", "displayp", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_display_panes_exec + .name = "display-panes", + .alias = "displayp", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_display_panes_exec }; enum cmd_retval diff --git a/cmd-find-window.c b/cmd-find-window.c index 1733b717..9c31cc0e 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -48,11 +48,14 @@ void cmd_find_window_callback(struct window_choose_data *); CMD_FIND_WINDOW_BY_NAME) const struct cmd_entry cmd_find_window_entry = { - "find-window", "findw", - "F:CNt:T", 1, 4, - "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", - CMD_WINDOW_T, - cmd_find_window_exec + .name = "find-window", + .alias = "findw", + + .args = { "F:CNt:T", 1, 4 }, + .usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", + + .flags = CMD_WINDOW_T, + .exec = cmd_find_window_exec }; struct cmd_find_window_data { diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 404f4671..4c2cacb1 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -36,11 +36,15 @@ void cmd_if_shell_done(struct cmd_q *); void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { - "if-shell", "if", - "bFt:", 2, 3, - "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command [command]", - CMD_PANE_T|CMD_CANFAIL, - cmd_if_shell_exec + .name = "if-shell", + .alias = "if", + + .args = { "bFt:", 2, 3 }, + .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " + "[command]", + + .flags = CMD_PANE_T|CMD_CANFAIL, + .exec = cmd_if_shell_exec }; struct cmd_if_shell_data { diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 6fc5b977..e5fbb423 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -34,19 +34,25 @@ enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmd_q *); enum cmd_retval join_pane(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_join_pane_entry = { - "join-pane", "joinp", - "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - CMD_PANE_MARKED_S|CMD_PANE_T, - cmd_join_pane_exec + .name = "join-pane", + .alias = "joinp", + + .args = { "bdhvp:l:s:t:", 0, 0 }, + .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + + .flags = CMD_PANE_MARKED_S|CMD_PANE_T, + .exec = cmd_join_pane_exec }; const struct cmd_entry cmd_move_pane_entry = { - "move-pane", "movep", - "bdhvp:l:s:t:", 0, 0, - "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - CMD_PANE_S|CMD_PANE_T, - cmd_join_pane_exec + .name = "move-pane", + .alias = "movep", + + .args = { "bdhvp:l:s:t:", 0, 0 }, + .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, + + .flags = CMD_PANE_S|CMD_PANE_T, + .exec = cmd_join_pane_exec }; enum cmd_retval diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index d5f69ea9..3e558d7d 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_pane_entry = { - "kill-pane", "killp", - "at:", 0, 0, - "[-a] " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_kill_pane_exec + .name = "kill-pane", + .alias = "killp", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_kill_pane_exec }; enum cmd_retval diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 4107e6b6..6f84e959 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -30,19 +30,25 @@ enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_server_entry = { - "kill-server", NULL, - "", 0, 0, - "", - 0, - cmd_kill_server_exec + .name = "kill-server", + .alias = NULL, + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_kill_server_exec }; const struct cmd_entry cmd_start_server_entry = { - "start-server", "start", - "", 0, 0, - "", - CMD_STARTSERVER, - cmd_kill_server_exec + .name = "start-server", + .alias = "start", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = CMD_STARTSERVER, + .exec = cmd_kill_server_exec }; enum cmd_retval diff --git a/cmd-kill-session.c b/cmd-kill-session.c index a4b0d5d2..a8d2d996 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -30,11 +30,14 @@ enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_session_entry = { - "kill-session", NULL, - "aCt:", 0, 0, - "[-aC] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_kill_session_exec + .name = "kill-session", + .alias = NULL, + + .args = { "aCt:", 0, 0 }, + .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_kill_session_exec }; enum cmd_retval diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 4ab17472..b8e1d5bc 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -27,19 +27,25 @@ enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_window_entry = { - "kill-window", "killw", - "at:", 0, 0, - "[-a] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_kill_window_exec + .name = "kill-window", + .alias = "killw", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_kill_window_exec }; const struct cmd_entry cmd_unlink_window_entry = { - "unlink-window", "unlinkw", - "kt:", 0, 0, - "[-k] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_kill_window_exec + .name = "unlink-window", + .alias = "unlinkw", + + .args = { "kt:", 0, 0 }, + .usage = "[-k] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_kill_window_exec }; enum cmd_retval diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 218eb6ff..a6007c33 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -33,11 +33,14 @@ enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_buffers_entry = { - "list-buffers", "lsb", - "F:", 0, 0, - "[-F format]", - 0, - cmd_list_buffers_exec + .name = "list-buffers", + .alias = "lsb", + + .args = { "F:", 0, 0 }, + .usage = "[-F format]", + + .flags = 0, + .exec = cmd_list_buffers_exec }; enum cmd_retval diff --git a/cmd-list-clients.c b/cmd-list-clients.c index effd8275..8a6fe8a9 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -36,11 +36,14 @@ enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_clients_entry = { - "list-clients", "lsc", - "F:t:", 0, 0, - "[-F format] " CMD_TARGET_SESSION_USAGE, - CMD_READONLY|CMD_SESSION_T, - cmd_list_clients_exec + .name = "list-clients", + .alias = "lsc", + + .args = { "F:t:", 0, 0 }, + .usage = "[-F format] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_READONLY|CMD_SESSION_T, + .exec = cmd_list_clients_exec }; enum cmd_retval diff --git a/cmd-list-keys.c b/cmd-list-keys.c index f0a59c0b..08796e5d 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -33,19 +33,25 @@ enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_q *); enum cmd_retval cmd_list_keys_commands(struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { - "list-keys", "lsk", - "t:T:", 0, 0, - "[-t mode-table] [-T key-table]", - 0, - cmd_list_keys_exec + .name = "list-keys", + .alias = "lsk", + + .args = { "t:T:", 0, 0 }, + .usage = "[-t mode-table] [-T key-table]", + + .flags = 0, + .exec = cmd_list_keys_exec }; const struct cmd_entry cmd_list_commands_entry = { - "list-commands", "lscm", - "", 0, 0, - "", - 0, - cmd_list_keys_exec + .name = "list-commands", + .alias = "lscm", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_list_keys_exec }; enum cmd_retval diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 2c9fa623..9f675b76 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -35,11 +35,14 @@ void cmd_list_panes_window(struct cmd *, struct session *, struct winlink *, struct cmd_q *, int); const struct cmd_entry cmd_list_panes_entry = { - "list-panes", "lsp", - "asF:t:", 0, 0, - "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_list_panes_exec + .name = "list-panes", + .alias = "lsp", + + .args = { "asF:t:", 0, 0 }, + .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_list_panes_exec }; enum cmd_retval diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index fed0c2ee..1fde7f86 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -39,11 +39,14 @@ enum cmd_retval cmd_list_sessions_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_sessions_entry = { - "list-sessions", "ls", - "F:", 0, 0, - "[-F format]", - 0, - cmd_list_sessions_exec + .name = "list-sessions", + .alias = "ls", + + .args = { "F:", 0, 0 }, + .usage = "[-F format]", + + .flags = 0, + .exec = cmd_list_sessions_exec }; enum cmd_retval diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 992ba0de..d34f8b8c 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -46,11 +46,14 @@ void cmd_list_windows_session(struct cmd *, struct session *, struct cmd_q *, int); const struct cmd_entry cmd_list_windows_entry = { - "list-windows", "lsw", - "F:at:", 0, 0, - "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_list_windows_exec + .name = "list-windows", + .alias = "lsw", + + .args = { "F:at:", 0, 0 }, + .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_list_windows_exec }; enum cmd_retval diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 929e3bf2..6fd2a767 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -35,11 +35,14 @@ enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmd_q *); void cmd_load_buffer_callback(struct client *, int, void *); const struct cmd_entry cmd_load_buffer_entry = { - "load-buffer", "loadb", - "b:", 1, 1, - CMD_BUFFER_USAGE " path", - 0, - cmd_load_buffer_exec + .name = "load-buffer", + .alias = "loadb", + + .args = { "b:", 1, 1 }, + .usage = CMD_BUFFER_USAGE " path", + + .flags = 0, + .exec = cmd_load_buffer_exec }; enum cmd_retval diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 5d20ebd4..777311b3 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -27,27 +27,36 @@ enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_lock_server_entry = { - "lock-server", "lock", - "", 0, 0, - "", - 0, - cmd_lock_server_exec + .name = "lock-server", + .alias = "lock", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_session_entry = { - "lock-session", "locks", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_lock_server_exec + .name = "lock-session", + .alias = "locks", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_client_entry = { - "lock-client", "lockc", - "t:", 0, 0, - CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_lock_server_exec + .name = "lock-client", + .alias = "lockc", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_lock_server_exec }; enum cmd_retval diff --git a/cmd-move-window.c b/cmd-move-window.c index 9e3a1c09..59a8538f 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -29,19 +29,25 @@ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { - "move-window", "movew", - "adkrs:t:", 0, 0, - "[-dkr] " CMD_SRCDST_WINDOW_USAGE, - CMD_WINDOW_S|CMD_MOVEW_R|CMD_INDEX_T, - cmd_move_window_exec + .name = "move-window", + .alias = "movew", + + .args = { "adkrs:t:", 0, 0 }, + .usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE, + + .flags = CMD_WINDOW_S|CMD_MOVEW_R|CMD_INDEX_T, + .exec = cmd_move_window_exec }; const struct cmd_entry cmd_link_window_entry = { - "link-window", "linkw", - "adks:t:", 0, 0, - "[-dk] " CMD_SRCDST_WINDOW_USAGE, - CMD_WINDOW_S|CMD_INDEX_T, - cmd_move_window_exec + .name = "link-window", + .alias = "linkw", + + .args = { "adks:t:", 0, 0 }, + .usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE, + + .flags = CMD_WINDOW_S|CMD_INDEX_T, + .exec = cmd_move_window_exec }; enum cmd_retval diff --git a/cmd-new-session.c b/cmd-new-session.c index e589a361..3cc07f46 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -36,21 +36,27 @@ enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { - "new-session", "new", - "Ac:dDEF:n:Ps:t:x:y:", 0, -1, - "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " - "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " - "[-y height] [command]", - CMD_STARTSERVER|CMD_CANFAIL|CMD_SESSION_T, - cmd_new_session_exec + .name = "new-session", + .alias = "new", + + .args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 }, + .usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " + "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " + "[-y height] [command]", + + .flags = CMD_STARTSERVER|CMD_CANFAIL|CMD_SESSION_T, + .exec = cmd_new_session_exec }; const struct cmd_entry cmd_has_session_entry = { - "has-session", "has", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_new_session_exec + .name = "has-session", + .alias = "has", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_new_session_exec }; enum cmd_retval diff --git a/cmd-new-window.c b/cmd-new-window.c index 18d2952b..b7a9c2be 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -35,12 +35,15 @@ enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { - "new-window", "neww", - "ac:dF:kn:Pt:", 0, -1, - "[-adkP] [-c start-directory] [-F format] [-n window-name] " - CMD_TARGET_WINDOW_USAGE " [command]", - CMD_INDEX_T, - cmd_new_window_exec + .name = "new-window", + .alias = "neww", + + .args = { "ac:dF:kn:Pt:", 0, -1 }, + .usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] " + CMD_TARGET_WINDOW_USAGE " [command]", + + .flags = CMD_INDEX_T, + .exec = cmd_new_window_exec }; enum cmd_retval diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 92a31c53..aa6e7805 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -34,11 +34,15 @@ void cmd_paste_buffer_filter(struct window_pane *, const char *, size_t, const char *, int); const struct cmd_entry cmd_paste_buffer_entry = { - "paste-buffer", "pasteb", - "db:prs:t:", 0, 0, - "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_paste_buffer_exec + .name = "paste-buffer", + .alias = "pasteb", + + .args = { "db:prs:t:", 0, 0 }, + .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " + CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_paste_buffer_exec }; enum cmd_retval diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index ad4b02e0..31b3a584 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -38,11 +38,14 @@ enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmd_q *); void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); const struct cmd_entry cmd_pipe_pane_entry = { - "pipe-pane", "pipep", - "ot:", 0, 1, - "[-o] " CMD_TARGET_PANE_USAGE " [command]", - CMD_PANE_T, - cmd_pipe_pane_exec + .name = "pipe-pane", + .alias = "pipep", + + .args = { "ot:", 0, 1 }, + .usage = "[-o] " CMD_TARGET_PANE_USAGE " [command]", + + .flags = CMD_PANE_T, + .exec = cmd_pipe_pane_exec }; enum cmd_retval diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 9d1d0fce..2cf69ac5 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -27,11 +27,14 @@ enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_refresh_client_entry = { - "refresh-client", "refresh", - "C:St:", 0, 0, - "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_refresh_client_exec + .name = "refresh-client", + .alias = "refresh", + + .args = { "C:St:", 0, 0 }, + .usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_refresh_client_exec }; enum cmd_retval diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 0c1a7e8c..c145dcb4 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_rename_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_session_entry = { - "rename-session", "rename", - "t:", 1, 1, - CMD_TARGET_SESSION_USAGE " new-name", - CMD_SESSION_T, - cmd_rename_session_exec + .name = "rename-session", + .alias = "rename", + + .args = { "t:", 1, 1 }, + .usage = CMD_TARGET_SESSION_USAGE " new-name", + + .flags = CMD_SESSION_T, + .exec = cmd_rename_session_exec }; enum cmd_retval diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 6609ebab..6a61b486 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_rename_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_window_entry = { - "rename-window", "renamew", - "t:", 1, 1, - CMD_TARGET_WINDOW_USAGE " new-name", - CMD_WINDOW_T, - cmd_rename_window_exec + .name = "rename-window", + .alias = "renamew", + + .args = { "t:", 1, 1 }, + .usage = CMD_TARGET_WINDOW_USAGE " new-name", + + .flags = CMD_WINDOW_T, + .exec = cmd_rename_window_exec }; enum cmd_retval diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index a4de32df..fb3302a2 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -31,11 +31,15 @@ enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *); void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); const struct cmd_entry cmd_resize_pane_entry = { - "resize-pane", "resizep", - "DLMRt:Ux:y:Z", 0, 1, - "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", - CMD_PANE_T, - cmd_resize_pane_exec + .name = "resize-pane", + .alias = "resizep", + + .args = { "DLMRt:Ux:y:Z", 0, 1 }, + .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " + "[adjustment]", + + .flags = CMD_PANE_T, + .exec = cmd_resize_pane_exec }; enum cmd_retval diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 45098d80..4a1ba60b 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_pane_entry = { - "respawn-pane", "respawnp", - "kt:", 0, -1, - "[-k] " CMD_TARGET_PANE_USAGE " [command]", - CMD_PANE_T, - cmd_respawn_pane_exec + .name = "respawn-pane", + .alias = "respawnp", + + .args = { "kt:", 0, -1 }, + .usage = "[-k] " CMD_TARGET_PANE_USAGE " [command]", + + .flags = CMD_PANE_T, + .exec = cmd_respawn_pane_exec }; enum cmd_retval diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index f6550dee..2b2a674f 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -30,11 +30,14 @@ enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_window_entry = { - "respawn-window", "respawnw", - "kt:", 0, -1, - "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", - CMD_WINDOW_T, - cmd_respawn_window_exec + .name = "respawn-window", + .alias = "respawnw", + + .args = { "kt:", 0, -1 }, + .usage = "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", + + .flags = CMD_WINDOW_T, + .exec = cmd_respawn_window_exec }; enum cmd_retval diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 4122886a..9966cff6 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -27,11 +27,14 @@ enum cmd_retval cmd_rotate_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rotate_window_entry = { - "rotate-window", "rotatew", - "Dt:U", 0, 0, - "[-DU] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_rotate_window_exec + .name = "rotate-window", + .alias = "rotatew", + + .args = { "Dt:U", 0, 0 }, + .usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_rotate_window_exec }; enum cmd_retval diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 0dae39ac..d84c3899 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -36,11 +36,14 @@ void cmd_run_shell_free(void *); void cmd_run_shell_print(struct job *, const char *); const struct cmd_entry cmd_run_shell_entry = { - "run-shell", "run", - "bt:", 1, 1, - "[-b] " CMD_TARGET_PANE_USAGE " shell-command", - CMD_PANE_T|CMD_CANFAIL, - cmd_run_shell_exec + .name = "run-shell", + .alias = "run", + + .args = { "bt:", 1, 1 }, + .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", + + .flags = CMD_PANE_T|CMD_CANFAIL, + .exec = cmd_run_shell_exec }; struct cmd_run_shell_data { diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index fc8d7bee..591390b5 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -35,19 +35,25 @@ enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_save_buffer_entry = { - "save-buffer", "saveb", - "ab:", 1, 1, - "[-a] " CMD_BUFFER_USAGE " path", - 0, - cmd_save_buffer_exec + .name = "save-buffer", + .alias = "saveb", + + .args = { "ab:", 1, 1 }, + .usage = "[-a] " CMD_BUFFER_USAGE " path", + + .flags = 0, + .exec = cmd_save_buffer_exec }; const struct cmd_entry cmd_show_buffer_entry = { - "show-buffer", "showb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - cmd_save_buffer_exec + .name = "show-buffer", + .alias = "showb", + + .args = { "b:", 0, 0 }, + .usage = CMD_BUFFER_USAGE, + + .flags = 0, + .exec = cmd_save_buffer_exec }; enum cmd_retval diff --git a/cmd-select-layout.c b/cmd-select-layout.c index aac60083..5bc0daad 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -29,27 +29,36 @@ enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_layout_entry = { - "select-layout", "selectl", - "nopt:", 0, 1, - "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", - CMD_WINDOW_T, - cmd_select_layout_exec + .name = "select-layout", + .alias = "selectl", + + .args = { "nopt:", 0, 1 }, + .usage = "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", + + .flags = CMD_WINDOW_T, + .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_next_layout_entry = { - "next-layout", "nextl", - "t:", 0, 0, - CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_select_layout_exec + .name = "next-layout", + .alias = "nextl", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_previous_layout_entry = { - "previous-layout", "prevl", - "t:", 0, 0, - CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_select_layout_exec + .name = "previous-layout", + .alias = "prevl", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_select_layout_exec }; enum cmd_retval diff --git a/cmd-select-pane.c b/cmd-select-pane.c index e83dc609..aa12ae1e 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -27,19 +27,25 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { - "select-pane", "selectp", - "DdegLlMmP:Rt:U", 0, 0, - "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_select_pane_exec + .name = "select-pane", + .alias = "selectp", + + .args = { "DdegLlMmP:Rt:U", 0, 0 }, + .usage = "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_select_pane_exec }; const struct cmd_entry cmd_last_pane_entry = { - "last-pane", "lastp", - "det:", 0, 0, - "[-de] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_select_pane_exec + .name = "last-pane", + .alias = "lastp", + + .args = { "det:", 0, 0 }, + .usage = "[-de] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_select_pane_exec }; enum cmd_retval diff --git a/cmd-select-window.c b/cmd-select-window.c index ede60dae..9b396cf9 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -29,35 +29,47 @@ enum cmd_retval cmd_select_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_window_entry = { - "select-window", "selectw", - "lnpTt:", 0, 0, - "[-lnpT] " CMD_TARGET_WINDOW_USAGE, - CMD_WINDOW_T, - cmd_select_window_exec + .name = "select-window", + .alias = "selectw", + + .args = { "lnpTt:", 0, 0 }, + .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE, + + .flags = CMD_WINDOW_T, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_next_window_entry = { - "next-window", "next", - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_select_window_exec + .name = "next-window", + .alias = "next", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_previous_window_entry = { - "previous-window", "prev", - "at:", 0, 0, - "[-a] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_select_window_exec + .name = "previous-window", + .alias = "prev", + + .args = { "at:", 0, 0 }, + .usage = "[-a] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_select_window_exec }; const struct cmd_entry cmd_last_window_entry = { - "last-window", "last", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_select_window_exec + .name = "last-window", + .alias = "last", + + .args = { "t:", 0, 0 }, + .usage = CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_select_window_exec }; enum cmd_retval diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 8f9b4ddf..aa78abca 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -30,19 +30,25 @@ enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_send_keys_entry = { - "send-keys", "send", - "lRMt:", 0, -1, - "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", - CMD_PANE_T, - cmd_send_keys_exec + .name = "send-keys", + .alias = "send", + + .args = { "lRMt:", 0, -1 }, + .usage = "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", + + .flags = CMD_PANE_T, + .exec = cmd_send_keys_exec }; const struct cmd_entry cmd_send_prefix_entry = { - "send-prefix", NULL, - "2t:", 0, 0, - "[-2] " CMD_TARGET_PANE_USAGE, - CMD_PANE_T, - cmd_send_keys_exec + .name = "send-prefix", + .alias = NULL, + + .args = { "2t:", 0, 0 }, + .usage = "[-2] " CMD_TARGET_PANE_USAGE, + + .flags = CMD_PANE_T, + .exec = cmd_send_keys_exec }; enum cmd_retval diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index e7f7627e..1494cf26 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -30,19 +30,25 @@ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { - "set-buffer", "setb", - "ab:n:", 0, 1, - "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", - 0, - cmd_set_buffer_exec + .name = "set-buffer", + .alias = "setb", + + .args = { "ab:n:", 0, 1 }, + .usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", + + .flags = 0, + .exec = cmd_set_buffer_exec }; const struct cmd_entry cmd_delete_buffer_entry = { - "delete-buffer", "deleteb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - cmd_set_buffer_exec + .name = "delete-buffer", + .alias = "deleteb", + + .args = { "b:", 0, 0 }, + .usage = CMD_BUFFER_USAGE, + + .flags = 0, + .exec = cmd_set_buffer_exec }; enum cmd_retval diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 4be967b7..61ab0470 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -30,11 +30,14 @@ enum cmd_retval cmd_set_environment_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_environment_entry = { - "set-environment", "setenv", - "grt:u", 1, 2, - "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - CMD_SESSION_T, - cmd_set_environment_exec + .name = "set-environment", + .alias = "setenv", + + .args = { "grt:u", 1, 2 }, + .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", + + .flags = CMD_SESSION_T, + .exec = cmd_set_environment_exec }; enum cmd_retval diff --git a/cmd-set-hook.c b/cmd-set-hook.c index d75b0ba1..e502fa79 100644 --- a/cmd-set-hook.c +++ b/cmd-set-hook.c @@ -30,19 +30,25 @@ enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_hook_entry = { - "set-hook", NULL, - "gt:u", 1, 2, - "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", - CMD_SESSION_T, - cmd_set_hook_exec + .name = "set-hook", + .alias = NULL, + + .args = { "gt:u", 1, 2 }, + .usage = "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", + + .flags = CMD_SESSION_T, + .exec = cmd_set_hook_exec }; const struct cmd_entry cmd_show_hooks_entry = { - "show-hooks", NULL, - "gt:", 0, 1, - "[-g] " CMD_TARGET_SESSION_USAGE, - CMD_SESSION_T, - cmd_set_hook_exec + .name = "show-hooks", + .alias = NULL, + + .args = { "gt:", 0, 1 }, + .usage = "[-g] " CMD_TARGET_SESSION_USAGE, + + .flags = CMD_SESSION_T, + .exec = cmd_set_hook_exec }; enum cmd_retval diff --git a/cmd-set-option.c b/cmd-set-option.c index 86856fbb..5190e181 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -65,19 +65,25 @@ struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *, const char *); const struct cmd_entry cmd_set_option_entry = { - "set-option", "set", - "agoqst:uw", 1, 2, - "[-agosquw] [-t target-window] option [value]", - CMD_WINDOW_T|CMD_CANFAIL, - cmd_set_option_exec + .name = "set-option", + .alias = "set", + + .args = { "agoqst:uw", 1, 2 }, + .usage = "[-agosquw] [-t target-window] option [value]", + + .flags = CMD_WINDOW_T|CMD_CANFAIL, + .exec = cmd_set_option_exec }; const struct cmd_entry cmd_set_window_option_entry = { - "set-window-option", "setw", - "agoqt:u", 1, 2, - "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", - CMD_WINDOW_T|CMD_CANFAIL, - cmd_set_option_exec + .name = "set-window-option", + .alias = "setw", + + .args = { "agoqt:u", 1, 2 }, + .usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", + + .flags = CMD_WINDOW_T|CMD_CANFAIL, + .exec = cmd_set_option_exec }; enum cmd_retval diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 723d3039..398a1e50 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -34,11 +34,14 @@ void cmd_show_environment_print(struct cmd *, struct cmd_q *, struct environ_entry *); const struct cmd_entry cmd_show_environment_entry = { - "show-environment", "showenv", - "gst:", 0, 1, - "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - CMD_SESSION_T|CMD_CANFAIL, - cmd_show_environment_exec + .name = "show-environment", + .alias = "showenv", + + .args = { "gst:", 0, 1 }, + .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", + + .flags = CMD_SESSION_T|CMD_CANFAIL, + .exec = cmd_show_environment_exec }; char * diff --git a/cmd-show-messages.c b/cmd-show-messages.c index b80b9fe7..e810abdc 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -32,19 +32,25 @@ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { - "show-messages", "showmsgs", - "JTt:", 0, 0, - "[-JT] " CMD_TARGET_CLIENT_USAGE, - CMD_CLIENT_T, - cmd_show_messages_exec + .name = "show-messages", + .alias = "showmsgs", + + .args = { "JTt:", 0, 0 }, + .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, + + .flags = CMD_CLIENT_T, + .exec = cmd_show_messages_exec }; const struct cmd_entry cmd_server_info_entry = { - "server-info", "info", - "", 0, 0, - "", - 0, - cmd_show_messages_exec + .name = "server-info", + .alias = "info", + + .args = { "", 0, 0 }, + .usage = "", + + .flags = 0, + .exec = cmd_show_messages_exec }; int cmd_show_messages_terminals(struct cmd_q *, int); diff --git a/cmd-show-options.c b/cmd-show-options.c index 3b39a532..4ef07e8f 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -35,19 +35,25 @@ enum cmd_retval cmd_show_options_all(struct cmd *, struct cmd_q *, struct options *, enum options_table_scope); const struct cmd_entry cmd_show_options_entry = { - "show-options", "show", - "gqst:vw", 0, 1, - "[-gqsvw] [-t target-session|target-window] [option]", - CMD_WINDOW_T|CMD_CANFAIL, - cmd_show_options_exec + .name = "show-options", + .alias = "show", + + .args = { "gqst:vw", 0, 1 }, + .usage = "[-gqsvw] [-t target-session|target-window] [option]", + + .flags = CMD_WINDOW_T|CMD_CANFAIL, + .exec = cmd_show_options_exec }; const struct cmd_entry cmd_show_window_options_entry = { - "show-window-options", "showw", - "gvt:", 0, 1, - "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - CMD_WINDOW_T|CMD_CANFAIL, - cmd_show_options_exec + .name = "show-window-options", + .alias = "showw", + + .args = { "gvt:", 0, 1 }, + .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", + + .flags = CMD_WINDOW_T|CMD_CANFAIL, + .exec = cmd_show_options_exec }; enum cmd_retval diff --git a/cmd-source-file.c b/cmd-source-file.c index e776712c..9d2d6d68 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_q *); void cmd_source_file_done(struct cmd_q *); const struct cmd_entry cmd_source_file_entry = { - "source-file", "source", - "", 1, 1, - "path", - 0, - cmd_source_file_exec + .name = "source-file", + .alias = "source", + + .args = { "", 1, 1 }, + .usage = "path", + + .flags = 0, + .exec = cmd_source_file_exec }; enum cmd_retval diff --git a/cmd-split-window.c b/cmd-split-window.c index 59efae35..cb776d63 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -36,12 +36,15 @@ enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { - "split-window", "splitw", - "bc:dF:l:hp:Pt:v", 0, -1, - "[-bdhvP] [-c start-directory] [-F format] [-p percentage|-l size] " - CMD_TARGET_PANE_USAGE " [command]", - CMD_PANE_T, - cmd_split_window_exec + .name = "split-window", + .alias = "splitw", + + .args = { "bc:dF:l:hp:Pt:v", 0, -1 }, + .usage = "[-bdhvP] [-c start-directory] [-F format] " + "[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", + + .flags = CMD_PANE_T, + .exec = cmd_split_window_exec }; enum cmd_retval diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 62d6bad3..7b50e3be 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_pane_entry = { - "swap-pane", "swapp", - "dDs:t:U", 0, 0, - "[-dDU] " CMD_SRCDST_PANE_USAGE, - CMD_PANE_MARKED_S|CMD_PANE_T, - cmd_swap_pane_exec + .name = "swap-pane", + .alias = "swapp", + + .args = { "dDs:t:U", 0, 0 }, + .usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, + + .flags = CMD_PANE_MARKED_S|CMD_PANE_T, + .exec = cmd_swap_pane_exec }; enum cmd_retval diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 0ed3ca81..38a3bf78 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -29,11 +29,14 @@ enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_window_entry = { - "swap-window", "swapw", - "ds:t:", 0, 0, - "[-d] " CMD_SRCDST_WINDOW_USAGE, - CMD_WINDOW_MARKED_S|CMD_WINDOW_MARKED_T, - cmd_swap_window_exec + .name = "swap-window", + .alias = "swapw", + + .args = { "ds:t:", 0, 0 }, + .usage = "[-d] " CMD_SRCDST_WINDOW_USAGE, + + .flags = CMD_WINDOW_MARKED_S|CMD_WINDOW_MARKED_T, + .exec = cmd_swap_window_exec }; enum cmd_retval diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 919d0057..5a1fe33d 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -30,11 +30,16 @@ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { - "switch-client", "switchc", - "lc:Enpt:rT:", 0, 0, - "[-Elnpr] [-c target-client] [-t target-session] [-T key-table]", - CMD_READONLY|CMD_CLIENT_C|CMD_PANE_T|CMD_SESSION_T|CMD_PREFERUNATTACHED, - cmd_switch_client_exec + .name = "switch-client", + .alias = "switchc", + + .args = { "lc:Enpt:rT:", 0, 0 }, + .usage = "[-Elnpr] [-c target-client] [-t target-session] " + "[-T key-table]", + + .flags = CMD_READONLY|CMD_CLIENT_C|CMD_PANE_T|CMD_SESSION_T| + CMD_PREFERUNATTACHED, + .exec = cmd_switch_client_exec }; enum cmd_retval @@ -48,7 +53,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) const char *tablename, *update; struct key_table *table; - if (args_has(args, 'r')) c->flags ^= CLIENT_READONLY; diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 66a4525e..8e89f21a 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, key_code); const struct cmd_entry cmd_unbind_key_entry = { - "unbind-key", "unbind", - "acnt:T:", 0, 1, - "[-acn] [-t mode-table] [-T key-table] key", - 0, - cmd_unbind_key_exec + .name = "unbind-key", + .alias = "unbind", + + .args = { "acnt:T:", 0, 1 }, + .usage = "[-acn] [-t mode-table] [-T key-table] key", + + .flags = 0, + .exec = cmd_unbind_key_exec }; enum cmd_retval diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 04316d5e..59f7dbfb 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -31,11 +31,14 @@ enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_wait_for_entry = { - "wait-for", "wait", - "LSU", 1, 1, - "[-L|-S|-U] channel", - 0, - cmd_wait_for_exec + .name = "wait-for", + .alias = "wait", + + .args = { "LSU", 1, 1 }, + .usage = "[-L|-S|-U] channel", + + .flags = 0, + .exec = cmd_wait_for_exec }; struct wait_channel { diff --git a/cmd.c b/cmd.c index 9f05a11e..f47991a3 100644 --- a/cmd.c +++ b/cmd.c @@ -353,12 +353,12 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) return (NULL); } - args = args_parse(entry->args_template, argc, argv); + args = args_parse(entry->args.template, argc, argv); if (args == NULL) goto usage; - if (entry->args_lower != -1 && args->argc < entry->args_lower) + if (entry->args.lower != -1 && args->argc < entry->args.lower) goto usage; - if (entry->args_upper != -1 && args->argc > entry->args_upper) + if (entry->args.upper != -1 && args->argc > entry->args.upper) goto usage; cmd = xcalloc(1, sizeof *cmd); diff --git a/tmux.h b/tmux.h index 8b26431d..c9adb2ee 100644 --- a/tmux.h +++ b/tmux.h @@ -1389,14 +1389,16 @@ struct cmd_q { /* Command definition. */ struct cmd_entry { - const char *name; - const char *alias; + const char *name; + const char *alias; - const char *args_template; - int args_lower; - int args_upper; + struct { + const char *template; + int lower; + int upper; + } args; - const char *usage; + const char *usage; #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 @@ -1418,9 +1420,9 @@ struct cmd_entry { #define CMD_WINDOW_MARKED_T 0x20000 #define CMD_WINDOW_MARKED_S 0x40000 #define CMD_CLIENT_CANFAIL 0x80000 - int flags; + int flags; - enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); + enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; #define CMD_ALL_T (CMD_SESSION_T|CMD_WINDOW_T|CMD_PANE_T|CMD_INDEX_T| \ CMD_MOVEW_R|CMD_PANE_MARKED_T|CMD_WINDOW_MARKED_T) From a3129fd4e820d7ccb3797fed491e7c021b63c568 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Dec 2015 00:31:54 +0000 Subject: [PATCH 501/703] Instead of combined flags for -c, -s, -t, split into different sets using an enum and simplify the parsing code. --- cmd-attach-session.c | 4 +- cmd-break-pane.c | 5 +- cmd-capture-pane.c | 4 +- cmd-choose-buffer.c | 4 +- cmd-choose-client.c | 4 +- cmd-choose-tree.c | 13 +- cmd-clear-history.c | 4 +- cmd-command-prompt.c | 4 +- cmd-confirm-before.c | 4 +- cmd-copy-mode.c | 4 +- cmd-detach-client.c | 9 +- cmd-display-message.c | 5 +- cmd-display-panes.c | 4 +- cmd-find-window.c | 4 +- cmd-if-shell.c | 4 +- cmd-join-pane.c | 10 +- cmd-kill-pane.c | 4 +- cmd-kill-session.c | 4 +- cmd-kill-window.c | 8 +- cmd-list-clients.c | 4 +- cmd-list-panes.c | 4 +- cmd-list-windows.c | 4 +- cmd-lock-server.c | 8 +- cmd-move-window.c | 10 +- cmd-new-session.c | 8 +- cmd-new-window.c | 4 +- cmd-paste-buffer.c | 4 +- cmd-pipe-pane.c | 4 +- cmd-refresh-client.c | 4 +- cmd-rename-session.c | 4 +- cmd-rename-window.c | 4 +- cmd-resize-pane.c | 4 +- cmd-respawn-pane.c | 4 +- cmd-respawn-window.c | 4 +- cmd-rotate-window.c | 4 +- cmd-run-shell.c | 4 +- cmd-select-layout.c | 12 +- cmd-select-pane.c | 8 +- cmd-select-window.c | 16 ++- cmd-send-keys.c | 8 +- cmd-set-environment.c | 4 +- cmd-set-hook.c | 8 +- cmd-set-option.c | 8 +- cmd-show-environment.c | 4 +- cmd-show-messages.c | 4 +- cmd-show-options.c | 8 +- cmd-split-window.c | 4 +- cmd-swap-pane.c | 29 ++-- cmd-swap-window.c | 5 +- cmd-switch-client.c | 6 +- cmd.c | 319 +++++++++++++---------------------------- log.c | 1 + tmux.h | 53 +++---- 53 files changed, 353 insertions(+), 328 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 976f180b..3b3f9e68 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -39,7 +39,9 @@ const struct cmd_entry cmd_attach_session_entry = { .args = { "c:dErt:", 0, 0 }, .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_STARTSERVER|CMD_SESSION_T|CMD_PANE_T|CMD_PREFERUNATTACHED, + .tflag = CMD_SESSION_WITHPANE, + + .flags = CMD_STARTSERVER, .exec = cmd_attach_session_exec }; diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 62625c71..c2b021fc 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -37,7 +37,10 @@ const struct cmd_entry cmd_break_pane_entry = { .args = { "dPF:s:t:", 0, 0 }, .usage = "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, - .flags = CMD_PANE_S|CMD_INDEX_T, + .sflag = CMD_PANE, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, .exec = cmd_break_pane_exec }; diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 0e3644e9..33f6cf08 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -43,7 +43,9 @@ const struct cmd_entry cmd_capture_pane_entry = { .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line]" CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_capture_pane_exec }; diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index fac792e9..1f8fbfb2 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -39,7 +39,9 @@ const struct cmd_entry cmd_choose_buffer_entry = { .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_buffer_exec }; diff --git a/cmd-choose-client.c b/cmd-choose-client.c index ab0f6c50..7d5fc606 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -44,7 +44,9 @@ const struct cmd_entry cmd_choose_client_entry = { .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_client_exec }; diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index faa9150c..db9222ba 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -51,7 +51,9 @@ const struct cmd_entry cmd_choose_tree_entry = { .usage = "[-suw] [-b session-template] [-c window template] " "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_tree_exec }; @@ -60,10 +62,11 @@ const struct cmd_entry cmd_choose_session_entry = { .alias = NULL, .args = { "F:t:", 0, 1 }, - .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_tree_exec }; @@ -74,7 +77,9 @@ const struct cmd_entry cmd_choose_window_entry = { .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_choose_tree_exec }; diff --git a/cmd-clear-history.c b/cmd-clear-history.c index a8c2bfdc..1236e7f1 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_clear_history_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_clear_history_exec }; diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 64b24bb5..9200ada1 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -42,7 +42,9 @@ const struct cmd_entry cmd_command_prompt_entry = { .usage = "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_command_prompt_exec }; diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 90e16992..b5a12821 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -40,7 +40,9 @@ const struct cmd_entry cmd_confirm_before_entry = { .args = { "p:t:", 1, 1 }, .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_confirm_before_exec }; diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 7c6f6ca7..1a006ebb 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_copy_mode_entry = { .args = { "Met:u", 0, 0 }, .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_copy_mode_exec }; diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 6f6adbb9..daf9a5c6 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -35,7 +35,10 @@ const struct cmd_entry cmd_detach_client_entry = { .args = { "as:t:P", 0, 0 }, .usage = "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, - .flags = CMD_READONLY|CMD_CLIENT_T|CMD_SESSION_S, + .sflag = CMD_SESSION, + .tflag = CMD_CLIENT, + + .flags = CMD_READONLY, .exec = cmd_detach_client_exec }; @@ -46,7 +49,9 @@ const struct cmd_entry cmd_suspend_client_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_detach_client_exec }; diff --git a/cmd-display-message.c b/cmd-display-message.c index fdfa6ae2..a041b5a1 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -42,7 +42,10 @@ const struct cmd_entry cmd_display_message_entry = { .usage = "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", - .flags = CMD_CLIENT_C|CMD_PANE_T|CMD_CLIENT_CANFAIL, + .cflag = CMD_CLIENT_CANFAIL, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_display_message_exec }; diff --git a/cmd-display-panes.c b/cmd-display-panes.c index db1e4b08..d8db4066 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_display_panes_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_display_panes_exec }; diff --git a/cmd-find-window.c b/cmd-find-window.c index 9c31cc0e..eb940d8c 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -54,7 +54,9 @@ const struct cmd_entry cmd_find_window_entry = { .args = { "F:CNt:T", 1, 4 }, .usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_find_window_exec }; diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 4c2cacb1..229289cd 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -43,7 +43,9 @@ const struct cmd_entry cmd_if_shell_entry = { .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " "[command]", - .flags = CMD_PANE_T|CMD_CANFAIL, + .tflag = CMD_PANE_CANFAIL, + + .flags = 0, .exec = cmd_if_shell_exec }; diff --git a/cmd-join-pane.c b/cmd-join-pane.c index e5fbb423..8b4117fa 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -40,7 +40,10 @@ const struct cmd_entry cmd_join_pane_entry = { .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - .flags = CMD_PANE_MARKED_S|CMD_PANE_T, + .sflag = CMD_PANE_MARKED, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_join_pane_exec }; @@ -51,7 +54,10 @@ const struct cmd_entry cmd_move_pane_entry = { .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, - .flags = CMD_PANE_S|CMD_PANE_T, + .sflag = CMD_PANE, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_join_pane_exec }; diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 3e558d7d..f843bc58 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_kill_pane_entry = { .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_kill_pane_exec }; diff --git a/cmd-kill-session.c b/cmd-kill-session.c index a8d2d996..4ca4e2c8 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_kill_session_entry = { .args = { "aCt:", 0, 0 }, .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_kill_session_exec }; diff --git a/cmd-kill-window.c b/cmd-kill-window.c index b8e1d5bc..7a8e9fa6 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_kill_window_entry = { .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_kill_window_exec }; @@ -44,7 +46,9 @@ const struct cmd_entry cmd_unlink_window_entry = { .args = { "kt:", 0, 0 }, .usage = "[-k] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_kill_window_exec }; diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 8a6fe8a9..75c6f570 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -42,7 +42,9 @@ const struct cmd_entry cmd_list_clients_entry = { .args = { "F:t:", 0, 0 }, .usage = "[-F format] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_READONLY|CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = CMD_READONLY, .exec = cmd_list_clients_exec }; diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 9f675b76..da0e0962 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -41,7 +41,9 @@ const struct cmd_entry cmd_list_panes_entry = { .args = { "asF:t:", 0, 0 }, .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_list_panes_exec }; diff --git a/cmd-list-windows.c b/cmd-list-windows.c index d34f8b8c..dd05ea85 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -52,7 +52,9 @@ const struct cmd_entry cmd_list_windows_entry = { .args = { "F:at:", 0, 0 }, .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_list_windows_exec }; diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 777311b3..9cdd816f 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -44,7 +44,9 @@ const struct cmd_entry cmd_lock_session_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_lock_server_exec }; @@ -55,7 +57,9 @@ const struct cmd_entry cmd_lock_client_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_lock_server_exec }; diff --git a/cmd-move-window.c b/cmd-move-window.c index 59a8538f..bb33ab38 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -35,7 +35,10 @@ const struct cmd_entry cmd_move_window_entry = { .args = { "adkrs:t:", 0, 0 }, .usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE, - .flags = CMD_WINDOW_S|CMD_MOVEW_R|CMD_INDEX_T, + .sflag = CMD_WINDOW, + .tflag = CMD_MOVEW_R, + + .flags = 0, .exec = cmd_move_window_exec }; @@ -46,7 +49,10 @@ const struct cmd_entry cmd_link_window_entry = { .args = { "adks:t:", 0, 0 }, .usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE, - .flags = CMD_WINDOW_S|CMD_INDEX_T, + .sflag = CMD_WINDOW, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, .exec = cmd_move_window_exec }; diff --git a/cmd-new-session.c b/cmd-new-session.c index 3cc07f46..f96003c7 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -44,7 +44,9 @@ const struct cmd_entry cmd_new_session_entry = { "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", - .flags = CMD_STARTSERVER|CMD_CANFAIL|CMD_SESSION_T, + .tflag = CMD_SESSION_CANFAIL, + + .flags = CMD_STARTSERVER, .exec = cmd_new_session_exec }; @@ -55,7 +57,9 @@ const struct cmd_entry cmd_has_session_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_new_session_exec }; diff --git a/cmd-new-window.c b/cmd-new-window.c index b7a9c2be..33f68935 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -42,7 +42,9 @@ const struct cmd_entry cmd_new_window_entry = { .usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", - .flags = CMD_INDEX_T, + .tflag = CMD_WINDOW_INDEX, + + .flags = 0, .exec = cmd_new_window_exec }; diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index aa6e7805..0728743a 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -41,7 +41,9 @@ const struct cmd_entry cmd_paste_buffer_entry = { .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_paste_buffer_exec }; diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 31b3a584..a2653dc5 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -44,7 +44,9 @@ const struct cmd_entry cmd_pipe_pane_entry = { .args = { "ot:", 0, 1 }, .usage = "[-o] " CMD_TARGET_PANE_USAGE " [command]", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_pipe_pane_exec }; diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 2cf69ac5..444e83fc 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_refresh_client_entry = { .args = { "C:St:", 0, 0 }, .usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_refresh_client_exec }; diff --git a/cmd-rename-session.c b/cmd-rename-session.c index c145dcb4..7fc6193d 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_rename_session_entry = { .args = { "t:", 1, 1 }, .usage = CMD_TARGET_SESSION_USAGE " new-name", - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_rename_session_exec }; diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 6a61b486..36c1bf31 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_rename_window_entry = { .args = { "t:", 1, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " new-name", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_rename_window_exec }; diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index fb3302a2..bb29cef9 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -38,7 +38,9 @@ const struct cmd_entry cmd_resize_pane_entry = { .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " "[adjustment]", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_resize_pane_exec }; diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 4a1ba60b..bff6c11b 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -37,7 +37,9 @@ const struct cmd_entry cmd_respawn_pane_entry = { .args = { "kt:", 0, -1 }, .usage = "[-k] " CMD_TARGET_PANE_USAGE " [command]", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_respawn_pane_exec }; diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 2b2a674f..aa4e169b 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_respawn_window_entry = { .args = { "kt:", 0, -1 }, .usage = "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_respawn_window_exec }; diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 9966cff6..014c1f2f 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_rotate_window_entry = { .args = { "Dt:U", 0, 0 }, .usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_rotate_window_exec }; diff --git a/cmd-run-shell.c b/cmd-run-shell.c index d84c3899..e857e9c9 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -42,7 +42,9 @@ const struct cmd_entry cmd_run_shell_entry = { .args = { "bt:", 1, 1 }, .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", - .flags = CMD_PANE_T|CMD_CANFAIL, + .tflag = CMD_PANE_CANFAIL, + + .flags = 0, .exec = cmd_run_shell_exec }; diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 5bc0daad..e6ede1af 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_select_layout_entry = { .args = { "nopt:", 0, 1 }, .usage = "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_layout_exec }; @@ -46,7 +48,9 @@ const struct cmd_entry cmd_next_layout_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_layout_exec }; @@ -57,7 +61,9 @@ const struct cmd_entry cmd_previous_layout_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_layout_exec }; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index aa12ae1e..7652f608 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -33,7 +33,9 @@ const struct cmd_entry cmd_select_pane_entry = { .args = { "DdegLlMmP:Rt:U", 0, 0 }, .usage = "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_select_pane_exec }; @@ -44,7 +46,9 @@ const struct cmd_entry cmd_last_pane_entry = { .args = { "det:", 0, 0 }, .usage = "[-de] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_pane_exec }; diff --git a/cmd-select-window.c b/cmd-select-window.c index 9b396cf9..82acc859 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -35,7 +35,9 @@ const struct cmd_entry cmd_select_window_entry = { .args = { "lnpTt:", 0, 0 }, .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE, - .flags = CMD_WINDOW_T, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_select_window_exec }; @@ -46,7 +48,9 @@ const struct cmd_entry cmd_next_window_entry = { .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_select_window_exec }; @@ -57,7 +61,9 @@ const struct cmd_entry cmd_previous_window_entry = { .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_select_window_exec }; @@ -68,7 +74,9 @@ const struct cmd_entry cmd_last_window_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_select_window_exec }; diff --git a/cmd-send-keys.c b/cmd-send-keys.c index aa78abca..377453b0 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_send_keys_entry = { .args = { "lRMt:", 0, -1 }, .usage = "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_send_keys_exec }; @@ -47,7 +49,9 @@ const struct cmd_entry cmd_send_prefix_entry = { .args = { "2t:", 0, 0 }, .usage = "[-2] " CMD_TARGET_PANE_USAGE, - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_send_keys_exec }; diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 61ab0470..0808dcb1 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_set_environment_entry = { .args = { "grt:u", 1, 2 }, .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_set_environment_exec }; diff --git a/cmd-set-hook.c b/cmd-set-hook.c index e502fa79..8ef02f8c 100644 --- a/cmd-set-hook.c +++ b/cmd-set-hook.c @@ -36,7 +36,9 @@ const struct cmd_entry cmd_set_hook_entry = { .args = { "gt:u", 1, 2 }, .usage = "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_set_hook_exec }; @@ -47,7 +49,9 @@ const struct cmd_entry cmd_show_hooks_entry = { .args = { "gt:", 0, 1 }, .usage = "[-g] " CMD_TARGET_SESSION_USAGE, - .flags = CMD_SESSION_T, + .tflag = CMD_SESSION, + + .flags = 0, .exec = cmd_set_hook_exec }; diff --git a/cmd-set-option.c b/cmd-set-option.c index 5190e181..13de02a3 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -71,7 +71,9 @@ const struct cmd_entry cmd_set_option_entry = { .args = { "agoqst:uw", 1, 2 }, .usage = "[-agosquw] [-t target-window] option [value]", - .flags = CMD_WINDOW_T|CMD_CANFAIL, + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, .exec = cmd_set_option_exec }; @@ -82,7 +84,9 @@ const struct cmd_entry cmd_set_window_option_entry = { .args = { "agoqt:u", 1, 2 }, .usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", - .flags = CMD_WINDOW_T|CMD_CANFAIL, + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, .exec = cmd_set_option_exec }; diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 398a1e50..ad9e9e37 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -40,7 +40,9 @@ const struct cmd_entry cmd_show_environment_entry = { .args = { "gst:", 0, 1 }, .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", - .flags = CMD_SESSION_T|CMD_CANFAIL, + .tflag = CMD_SESSION_CANFAIL, + + .flags = 0, .exec = cmd_show_environment_exec }; diff --git a/cmd-show-messages.c b/cmd-show-messages.c index e810abdc..68ef6674 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -38,7 +38,9 @@ const struct cmd_entry cmd_show_messages_entry = { .args = { "JTt:", 0, 0 }, .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, - .flags = CMD_CLIENT_T, + .tflag = CMD_CLIENT, + + .flags = 0, .exec = cmd_show_messages_exec }; diff --git a/cmd-show-options.c b/cmd-show-options.c index 4ef07e8f..e99574f8 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -41,7 +41,9 @@ const struct cmd_entry cmd_show_options_entry = { .args = { "gqst:vw", 0, 1 }, .usage = "[-gqsvw] [-t target-session|target-window] [option]", - .flags = CMD_WINDOW_T|CMD_CANFAIL, + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, .exec = cmd_show_options_exec }; @@ -52,7 +54,9 @@ const struct cmd_entry cmd_show_window_options_entry = { .args = { "gvt:", 0, 1 }, .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", - .flags = CMD_WINDOW_T|CMD_CANFAIL, + .tflag = CMD_WINDOW_CANFAIL, + + .flags = 0, .exec = cmd_show_options_exec }; diff --git a/cmd-split-window.c b/cmd-split-window.c index cb776d63..aee323eb 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -43,7 +43,9 @@ const struct cmd_entry cmd_split_window_entry = { .usage = "[-bdhvP] [-c start-directory] [-F format] " "[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", - .flags = CMD_PANE_T, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_split_window_exec }; diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 7b50e3be..84332c2f 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -35,14 +35,16 @@ const struct cmd_entry cmd_swap_pane_entry = { .args = { "dDs:t:U", 0, 0 }, .usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, - .flags = CMD_PANE_MARKED_S|CMD_PANE_T, + .sflag = CMD_PANE_MARKED, + .tflag = CMD_PANE, + + .flags = 0, .exec = cmd_swap_pane_exec }; enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; @@ -52,22 +54,23 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) dst_wl = cmdq->state.tflag.wl; dst_w = dst_wl->window; dst_wp = cmdq->state.tflag.wp; - src_wp = cmdq->state.sflag.wp; src_wl = cmdq->state.sflag.wl; src_w = src_wl->window; + src_wp = cmdq->state.sflag.wp; server_unzoom_window(dst_w); - if (!args_has(args, 's')) { + if (args_has(self->args, 'D')) { + src_wl = dst_wl; src_w = dst_w; - if (args_has(self->args, 'D')) { - src_wp = TAILQ_NEXT(dst_wp, entry); - if (src_wp == NULL) - src_wp = TAILQ_FIRST(&dst_w->panes); - } else if (args_has(self->args, 'U')) { - src_wp = TAILQ_PREV(dst_wp, window_panes, entry); - if (src_wp == NULL) - src_wp = TAILQ_LAST(&dst_w->panes, window_panes); - } + src_wp = TAILQ_NEXT(dst_wp, entry); + if (src_wp == NULL) + src_wp = TAILQ_FIRST(&dst_w->panes); + } else if (args_has(self->args, 'U')) { + src_wl = dst_wl; + src_w = dst_w; + src_wp = TAILQ_PREV(dst_wp, window_panes, entry); + if (src_wp == NULL) + src_wp = TAILQ_LAST(&dst_w->panes, window_panes); } server_unzoom_window(src_w); diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 38a3bf78..e1835820 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -35,7 +35,10 @@ const struct cmd_entry cmd_swap_window_entry = { .args = { "ds:t:", 0, 0 }, .usage = "[-d] " CMD_SRCDST_WINDOW_USAGE, - .flags = CMD_WINDOW_MARKED_S|CMD_WINDOW_MARKED_T, + .sflag = CMD_WINDOW_MARKED, + .tflag = CMD_WINDOW, + + .flags = 0, .exec = cmd_swap_window_exec }; diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 5a1fe33d..0f6acbf4 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -37,8 +37,10 @@ const struct cmd_entry cmd_switch_client_entry = { .usage = "[-Elnpr] [-c target-client] [-t target-session] " "[-T key-table]", - .flags = CMD_READONLY|CMD_CLIENT_C|CMD_PANE_T|CMD_SESSION_T| - CMD_PREFERUNATTACHED, + .cflag = CMD_CLIENT, + .tflag = CMD_SESSION_WITHPANE, + + .flags = CMD_READONLY, .exec = cmd_switch_client_exec }; diff --git a/cmd.c b/cmd.c index f47991a3..62be3af1 100644 --- a/cmd.c +++ b/cmd.c @@ -207,10 +207,6 @@ const struct cmd_entry *cmd_table[] = { NULL }; -static void cmd_clear_state(struct cmd_state *); -static struct client *cmd_get_state_client(struct cmd_q *, int); -static int cmd_set_state_flag(struct cmd *, struct cmd_q *, char); - int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { @@ -409,252 +405,131 @@ cmd_clear_state(struct cmd_state *state) } static int -cmd_set_state_flag(struct cmd *cmd, struct cmd_q *cmdq, char c) +cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, + const char *target, struct cmd_q *cmdq) { - struct cmd_state *state = &cmdq->state; - struct cmd_find_state *fsf = NULL; - const char *flag; - int flags = cmd->entry->flags, everything = 0; - int allflags = 0, targetflags, error; - struct session *s; - struct window *w; - struct winlink *wl; - struct window_pane *wp; + int targetflags, error; - /* Set up state for either -t or -s. */ - if (c == 't') { - fsf = &cmdq->state.tflag; - allflags = CMD_ALL_T; - } else if (c == 's') { - fsf = &cmdq->state.sflag; - allflags = CMD_ALL_S; + if (flag == CMD_SESSION_WITHPANE) { + if (target != NULL && target[strcspn(target, ":.")] != '\0') + flag = CMD_PANE; + else + flag = CMD_SESSION; } - /* - * If the command wants something and no argument is present, use the - * base command instead. - */ - flag = args_get(cmd->args, c); - if (flag == NULL) { - if ((flags & allflags) == 0) - return (0); /* doesn't care about flag */ - cmd = cmdq->cmd; - everything = 1; - flag = args_get(cmd->args, c); - } - - /* - * If no flag and the current command is allowed to fail, just skip to - * fill in as much we can, otherwise continue and fail later if needed. - */ - if (flag == NULL && (flags & CMD_CANFAIL)) - goto complete_everything; - - /* Fill in state using command (current or base) flags. */ - if (flags & CMD_PREFERUNATTACHED) - targetflags = CMD_FIND_PREFER_UNATTACHED; - else - targetflags = 0; - switch (cmd->entry->flags & allflags) { - case 0: - break; - case CMD_SESSION_T|CMD_PANE_T: - case CMD_SESSION_S|CMD_PANE_S: - if (flag != NULL && flag[strcspn(flag, ":.")] != '\0') { - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, - targetflags); - if (error != 0) - return (-1); - } else { - error = cmd_find_target(fsf, cmdq, flag, - CMD_FIND_SESSION, targetflags); - if (error != 0) - return (-1); - - if (flag == NULL) { - fsf->wl = fsf->s->curw; - fsf->wp = fsf->s->curw->window->active; - } else { - s = fsf->s; - if ((w = window_find_by_id_str(flag)) != NULL) - wp = w->active; - else { - wp = window_pane_find_by_id_str(flag); - if (wp != NULL) - w = wp->window; - } - wl = winlink_find_by_window(&s->windows, w); - if (wl != NULL) { - fsf->wl = wl; - fsf->wp = wp; - } - } - } - break; - case CMD_MOVEW_R|CMD_INDEX_T: - case CMD_MOVEW_R|CMD_INDEX_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, - targetflags|CMD_FIND_QUIET); - if (error != 0) { - error = cmd_find_target(fsf, cmdq, flag, - CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX); - if (error != 0) - return (-1); - } - break; - case CMD_SESSION_T: - case CMD_SESSION_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_SESSION, - targetflags); - if (error != 0) - return (-1); - break; - case CMD_WINDOW_MARKED_T: - case CMD_WINDOW_MARKED_S: - targetflags |= CMD_FIND_DEFAULT_MARKED; - /* FALLTHROUGH */ - case CMD_WINDOW_T: - case CMD_WINDOW_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, - targetflags); - if (error != 0) - return (-1); - break; - case CMD_PANE_MARKED_T: - case CMD_PANE_MARKED_S: - targetflags |= CMD_FIND_DEFAULT_MARKED; - /* FALLTHROUGH */ - case CMD_PANE_T: - case CMD_PANE_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, - targetflags); - if (error != 0) - return (-1); - break; - case CMD_INDEX_T: - case CMD_INDEX_S: - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, - CMD_FIND_WINDOW_INDEX); - if (error != 0) - return (-1); - break; - default: - fatalx("too many -%c for %s", c, cmd->entry->name); - } - - /* - * If this is still the current command, it wants what it asked for and - * nothing more. If it's the base command, fill in as much as possible - * because the current command may have different flags. - */ - if (!everything) + switch (flag) { + case CMD_NONE: + case CMD_CLIENT: + case CMD_CLIENT_CANFAIL: return (0); + case CMD_SESSION: + case CMD_SESSION_CANFAIL: + case CMD_SESSION_PREFERUNATTACHED: + case CMD_SESSION_WITHPANE: + targetflags = 0; + if (flag == CMD_SESSION_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_SESSION_PREFERUNATTACHED) + targetflags |= CMD_FIND_PREFER_UNATTACHED; -complete_everything: - if (fsf->s == NULL) { - if (state->c != NULL) - fsf->s = state->c->session; - if (fsf->s == NULL) { - error = cmd_find_target(fsf, cmdq, NULL, - CMD_FIND_SESSION, CMD_FIND_QUIET); - if (error != 0) - fsf->s = NULL; - } - if (fsf->s == NULL) { - if (flags & CMD_CANFAIL) - return (0); - cmdq_error(cmdq, "no current session"); + error = cmd_find_target(fs, cmdq, target, CMD_FIND_SESSION, + targetflags); + if (error != 0 && flag != CMD_SESSION_CANFAIL) return (-1); - } - } - if (fsf->wl == NULL) { - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_WINDOW, 0); - if (error != 0) + break; + case CMD_MOVEW_R: + error = cmd_find_target(fs, cmdq, target, CMD_FIND_SESSION, + CMD_FIND_QUIET); + if (error == 0) + break; + flag = CMD_WINDOW_INDEX; + /* FALLTHROUGH */ + case CMD_WINDOW: + case CMD_WINDOW_CANFAIL: + case CMD_WINDOW_MARKED: + case CMD_WINDOW_INDEX: + targetflags = 0; + if (flag == CMD_WINDOW_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_WINDOW_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + if (flag == CMD_WINDOW_INDEX) + targetflags |= CMD_FIND_WINDOW_INDEX; + + error = cmd_find_target(fs, cmdq, target, CMD_FIND_WINDOW, + targetflags); + if (error != 0 && flag != CMD_WINDOW_CANFAIL) return (-1); - } - if (fsf->wp == NULL) { - error = cmd_find_target(fsf, cmdq, flag, CMD_FIND_PANE, 0); - if (error != 0) + break; + case CMD_PANE: + case CMD_PANE_CANFAIL: + case CMD_PANE_MARKED: + targetflags = 0; + if (flag == CMD_PANE_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_PANE_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + + error = cmd_find_target(fs, cmdq, target, CMD_FIND_PANE, + targetflags); + if (error != 0 && flag != CMD_PANE_CANFAIL) return (-1); + break; } return (0); } -static struct client * -cmd_get_state_client(struct cmd_q *cmdq, int quiet) -{ - struct cmd *cmd = cmdq->cmd; - struct args *args = cmd->args; - - switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { - case 0: - return (cmd_find_client(cmdq, NULL, 1)); - case CMD_CLIENT_C: - return (cmd_find_client(cmdq, args_get(args, 'c'), quiet)); - case CMD_CLIENT_T: - return (cmd_find_client(cmdq, args_get(args, 't'), quiet)); - default: - fatalx("both -t and -c for %s", cmd->entry->name); - } -} - int cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) { - struct cmd_state *state = &cmdq->state; - struct args *args = cmd->args; - const char *cflag, *tflag; - char *tmp; - int error, quiet; + const struct cmd_entry *entry = cmd->entry; + struct cmd_state *state = &cmdq->state; + char *tmp; + enum cmd_entry_flag flag; + const char *s; + int error; tmp = cmd_print(cmd); - log_debug("preparing state for: %s (client %p)", tmp, cmdq->client); + log_debug("preparing state for %s (client %p)", tmp, cmdq->client); free(tmp); - /* Start with an empty state. */ cmd_clear_state(state); - /* No error messages if can fail. */ - quiet = 0; - if (cmd->entry->flags & CMD_CLIENT_CANFAIL) - quiet = 1; - - /* - * If the command wants a client and provides -c or -t, use it. If not, - * try the base command instead via cmd_get_state_client. No client is - * allowed if no flags, otherwise it must be available. - */ - switch (cmd->entry->flags & (CMD_CLIENT_C|CMD_CLIENT_T)) { - case 0: - state->c = cmd_get_state_client(cmdq, 1); - break; - case CMD_CLIENT_C: - cflag = args_get(args, 'c'); - if (cflag == NULL) - state->c = cmd_get_state_client(cmdq, quiet); + flag = cmd->entry->cflag; + if (flag == CMD_NONE) { + flag = cmd->entry->tflag; + if (flag == CMD_CLIENT || flag == CMD_CLIENT_CANFAIL) + s = args_get(cmd->args, 't'); else - state->c = cmd_find_client(cmdq, cflag, quiet); - if (!quiet && state->c == NULL) - return (-1); - break; - case CMD_CLIENT_T: - tflag = args_get(args, 't'); - if (tflag == NULL) - state->c = cmd_get_state_client(cmdq, 0); - else - state->c = cmd_find_client(cmdq, tflag, 0); - state->c = cmd_find_client(cmdq, args_get(args, 't'), quiet); - if (!quiet && state->c == NULL) + s = NULL; + } else + s = args_get(cmd->args, 'c'); + switch (flag) { + case CMD_CLIENT: + state->c = cmd_find_client(cmdq, s, 0); + if (state->c == NULL) return (-1); break; default: - fatalx("both -c and -t for %s", cmd->entry->name); + state->c = cmd_find_client(cmdq, s, 1); + break; } - error = cmd_set_state_flag(cmd, cmdq, 't'); - if (error == 0) - error = cmd_set_state_flag(cmd, cmdq, 's'); - return (error); + s = args_get(cmd->args, 't'); + log_debug("preparing -t state: target %s", s == NULL ? "none" : s); + + error = cmd_prepare_state_flag(&state->tflag, entry->tflag, s, cmdq); + if (error != 0) + return (error); + + s = args_get(cmd->args, 's'); + log_debug("preparing -s state: target %s", s == NULL ? "none" : s); + + error = cmd_prepare_state_flag(&state->sflag, entry->sflag, s, cmdq); + if (error != 0) + return (error); + + return (0); } char * diff --git a/log.c b/log.c index 46f1673c..200f9119 100644 --- a/log.c +++ b/log.c @@ -144,6 +144,7 @@ fatalx(const char *msg, ...) char *fmt; va_list ap; + abort(); va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); diff --git a/tmux.h b/tmux.h index c9adb2ee..04d1fba2 100644 --- a/tmux.h +++ b/tmux.h @@ -1387,6 +1387,30 @@ struct cmd_q { TAILQ_ENTRY(cmd_q) waitentry; }; +/* Command -c, -t or -s flags. */ +enum cmd_entry_flag { + CMD_NONE, + + CMD_CLIENT, + CMD_CLIENT_CANFAIL, + + CMD_SESSION, + CMD_SESSION_CANFAIL, + CMD_SESSION_PREFERUNATTACHED, + CMD_SESSION_WITHPANE, + + CMD_WINDOW, + CMD_WINDOW_CANFAIL, + CMD_WINDOW_MARKED, + CMD_WINDOW_INDEX, + + CMD_PANE, + CMD_PANE_CANFAIL, + CMD_PANE_MARKED, + + CMD_MOVEW_R, +}; + /* Command definition. */ struct cmd_entry { const char *name; @@ -1397,37 +1421,18 @@ struct cmd_entry { int lower; int upper; } args; - const char *usage; + enum cmd_entry_flag tflag; + enum cmd_entry_flag sflag; + enum cmd_entry_flag cflag; + #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 -#define CMD_SESSION_T 0x4 -#define CMD_SESSION_S 0x8 -#define CMD_WINDOW_T 0x10 -#define CMD_WINDOW_S 0x20 -#define CMD_PANE_T 0x40 -#define CMD_PANE_S 0x80 -#define CMD_CLIENT_T 0x100 -#define CMD_CLIENT_C 0x200 -#define CMD_INDEX_T 0x400 -#define CMD_INDEX_S 0x800 -#define CMD_CANFAIL 0x1000 -#define CMD_PREFERUNATTACHED 0x2000 -#define CMD_MOVEW_R 0x4000 /* for movew -r only */ -#define CMD_PANE_MARKED_S 0x8000 -#define CMD_PANE_MARKED_T 0x10000 -#define CMD_WINDOW_MARKED_T 0x20000 -#define CMD_WINDOW_MARKED_S 0x40000 -#define CMD_CLIENT_CANFAIL 0x80000 - int flags; + int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; -#define CMD_ALL_T (CMD_SESSION_T|CMD_WINDOW_T|CMD_PANE_T|CMD_INDEX_T| \ - CMD_MOVEW_R|CMD_PANE_MARKED_T|CMD_WINDOW_MARKED_T) -#define CMD_ALL_S (CMD_SESSION_S|CMD_WINDOW_S|CMD_PANE_S|CMD_INDEX_S| \ - CMD_PANE_MARKED_S|CMD_WINDOW_MARKED_S) /* Key binding and key table. */ struct key_binding { From a585a1b81a9122c2f7d7083304a4eb47d4e1c7ec Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Dec 2015 00:32:19 +0000 Subject: [PATCH 502/703] Remove some stray debug code. --- log.c | 1 - 1 file changed, 1 deletion(-) diff --git a/log.c b/log.c index 200f9119..46f1673c 100644 --- a/log.c +++ b/log.c @@ -144,7 +144,6 @@ fatalx(const char *msg, ...) char *fmt; va_list ap; - abort(); va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); From d5999f8b5cbbd3648d7a6c00100e5eab279a90bb Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 14 Dec 2015 23:30:58 +0000 Subject: [PATCH 503/703] Use cmd_find_clear_state instead of an extra function doing the same. --- cmd-find.c | 3 --- cmd.c | 20 +++----------------- tmux.h | 3 +++ 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 582f1759..8e118c00 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -50,9 +50,6 @@ int cmd_find_get_pane(struct cmd_find_state *, const char *); int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *); int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); -void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); -void cmd_find_log_state(const char *, struct cmd_find_state *); - const char *cmd_find_session_table[][2] = { { NULL, NULL } }; diff --git a/cmd.c b/cmd.c index 62be3af1..4f1e1b90 100644 --- a/cmd.c +++ b/cmd.c @@ -388,22 +388,6 @@ usage: return (NULL); } -static void -cmd_clear_state(struct cmd_state *state) -{ - state->c = NULL; - - state->tflag.s = NULL; - state->tflag.wl = NULL; - state->tflag.wp = NULL; - state->tflag.idx = -1; - - state->sflag.s = NULL; - state->sflag.wl = NULL; - state->sflag.wp = NULL; - state->sflag.idx = -1; -} - static int cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, const char *target, struct cmd_q *cmdq) @@ -493,7 +477,9 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) log_debug("preparing state for %s (client %p)", tmp, cmdq->client); free(tmp); - cmd_clear_state(state); + state->c = NULL; + cmd_find_clear_state(&state->tflag, NULL, 0); + cmd_find_clear_state(&state->sflag, NULL, 0); flag = cmd->entry->cflag; if (flag == CMD_NONE) { diff --git a/tmux.h b/tmux.h index 04d1fba2..c6bd48cf 100644 --- a/tmux.h +++ b/tmux.h @@ -1764,6 +1764,9 @@ long long args_strtonum(struct args *, u_char, long long, long long, int cmd_find_target(struct cmd_find_state *, struct cmd_q *, const char *, enum cmd_find_type, int); struct client *cmd_find_client(struct cmd_q *, const char *, int); +void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, + int); +void cmd_find_log_state(const char *, struct cmd_find_state *); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); From 12da13c9d1e0e015ed19761e352ee6b877b23aa4 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 00:00:01 +0000 Subject: [PATCH 504/703] Make the marked pane a cmd_find_state. --- cmd-find.c | 109 +++++++++++++++++++++++++++++----------------- cmd-select-pane.c | 4 +- screen-redraw.c | 4 +- server.c | 56 +++++++----------------- tmux.h | 7 +-- window.c | 4 +- 6 files changed, 95 insertions(+), 89 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 8e118c00..b7728ea2 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -792,6 +792,67 @@ cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) fs->idx = -1; } +/* Check if a state if valid. */ +int +cmd_find_valid_state(struct cmd_find_state *fs) +{ + struct winlink *wl; + + if (fs->s == NULL || fs->wl == NULL || fs->w == NULL || fs->wp == NULL) + return (0); + + if (!session_alive(fs->s)) + return (0); + + RB_FOREACH(wl, winlinks, &fs->s->windows) { + if (wl->window == fs->w && wl == fs->wl) + break; + } + if (wl == NULL) + return (0); + + if (fs->w != fs->wl->window) + return (0); + + if (!window_has_pane(fs->w, fs->wp)) + return (0); + return (window_pane_visible(fs->wp)); +} + +/* Copy a state. */ +void +cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src) +{ + dst->s = src->s; + dst->wl = src->wl; + dst->idx = dst->wl->idx; + dst->w = dst->wl->window; + dst->wp = src->wp; +} + +/* Log the result. */ +void +cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) +{ + if (fs->s != NULL) + log_debug("%s: s=$%u", prefix, fs->s->id); + else + log_debug("%s: s=none", prefix); + if (fs->wl != NULL) { + log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, + fs->wl->window == fs->w, fs->w->id, fs->w->name); + } else + log_debug("%s: wl=none", prefix); + if (fs->wp != NULL) + log_debug("%s: wp=%%%u", prefix, fs->wp->id); + else + log_debug("%s: wp=none", prefix); + if (fs->idx != -1) + log_debug("%s: idx=%d", prefix, fs->idx); + else + log_debug("%s: idx=none", prefix); +} + /* * Split target into pieces and resolve for the given type. Fills in the given * state. Returns 0 on success or -1 on error. @@ -814,17 +875,14 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, /* Find current state. */ cmd_find_clear_state(¤t, cmdq, flags); - if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { - current.s = marked_session; - current.wl = marked_winlink; - current.idx = current.wl->idx; - current.w = current.wl->window; - current.wp = marked_window_pane; - } - if (current.s == NULL && cmd_find_current_session(¤t) != 0) { - if (~flags & CMD_FIND_QUIET) - cmdq_error(cmdq, "no current session"); - goto error; + if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) + cmd_find_copy_state(¤t, &marked_pane); + else { + if (cmd_find_current_session(¤t) != 0) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no current session"); + goto error; + } } /* Clear new state. */ @@ -868,11 +926,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, cmdq_error(cmdq, "no marked target"); goto error; } - fs->s = marked_session; - fs->wl = marked_winlink; - fs->idx = fs->wl->idx; - fs->w = fs->wl->window; - fs->wp = marked_window_pane; + cmd_find_copy_state(fs, &marked_pane); goto found; } @@ -1068,29 +1122,6 @@ no_pane: goto error; } -/* Log the result. */ -void -cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) -{ - if (fs->s != NULL) - log_debug("%s: s=$%u", prefix, fs->s->id); - else - log_debug("%s: s=none", prefix); - if (fs->wl != NULL) { - log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, - fs->wl->window == fs->w, fs->w->id, fs->w->name); - } else - log_debug("%s: wl=none", prefix); - if (fs->wp != NULL) - log_debug("%s: wp=%%%u", prefix, fs->wp->id); - else - log_debug("%s: wp=none", prefix); - if (fs->idx != -1) - log_debug("%s: idx=%d", prefix, fs->idx); - else - log_debug("%s: idx=none", prefix); -} - /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 7652f608..6ebe753c 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -94,13 +94,13 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) return (CMD_RETURN_NORMAL); - lastwp = marked_window_pane; + lastwp = marked_pane.wp; if (args_has(args, 'M') || server_is_marked(s, wl, wp)) server_clear_marked(); else server_set_marked(s, wl, wp); - markedwp = marked_window_pane; + markedwp = marked_pane.wp; if (lastwp != NULL) { server_redraw_window_borders(lastwp->window); diff --git a/screen-redraw.c b/screen-redraw.c index fd4536d0..9958d04a 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -331,9 +331,9 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) continue; active = screen_redraw_check_is(i, j, type, w, w->active, wp); - if (server_is_marked(s, s->curw, marked_window_pane) && + if (server_is_marked(s, s->curw, marked_pane.wp) && screen_redraw_check_is(i, j, type, w, - marked_window_pane, wp)) { + marked_pane.wp, wp)) { if (active) tty_attributes(tty, &m_active_gc, NULL); else diff --git a/server.c b/server.c index c45e8a2c..0a1146f3 100644 --- a/server.c +++ b/server.c @@ -41,18 +41,14 @@ * Main server functions. */ -struct clients clients; +struct clients clients; -struct tmuxproc *server_proc; -int server_fd; -int server_exit; -struct event server_ev_accept; +struct tmuxproc *server_proc; +int server_fd; +int server_exit; +struct event server_ev_accept; -struct session *marked_session; -struct winlink *marked_winlink; -struct window *marked_window; -struct window_pane *marked_window_pane; -struct layout_cell *marked_layout_cell; +struct cmd_find_state marked_pane; int server_create_socket(void); int server_loop(void); @@ -68,22 +64,18 @@ void server_child_stopped(pid_t, int); void server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { - marked_session = s; - marked_winlink = wl; - marked_window = wl->window; - marked_window_pane = wp; - marked_layout_cell = wp->layout_cell; + cmd_find_clear_state(&marked_pane, NULL, 0); + marked_pane.s = s; + marked_pane.wl = wl; + marked_pane.w = wl->window; + marked_pane.wp = wp; } /* Clear marked pane. */ void server_clear_marked(void) { - marked_session = NULL; - marked_winlink = NULL; - marked_window = NULL; - marked_window_pane = NULL; - marked_layout_cell = NULL; + cmd_find_clear_state(&marked_pane, NULL, 0); } /* Is this the marked pane? */ @@ -92,9 +84,9 @@ server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { if (s == NULL || wl == NULL || wp == NULL) return (0); - if (marked_session != s || marked_winlink != wl) + if (marked_pane.s != s || marked_pane.wl != wl) return (0); - if (marked_window_pane != wp) + if (marked_pane.wp != wp) return (0); return (server_check_marked()); } @@ -103,25 +95,7 @@ server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) int server_check_marked(void) { - struct winlink *wl; - - if (marked_window_pane == NULL) - return (0); - if (marked_layout_cell != marked_window_pane->layout_cell) - return (0); - - if (!session_alive(marked_session)) - return (0); - RB_FOREACH(wl, winlinks, &marked_session->windows) { - if (wl->window == marked_window && wl == marked_winlink) - break; - } - if (wl == NULL) - return (0); - - if (!window_has_pane(marked_window, marked_window_pane)) - return (0); - return (window_pane_visible(marked_window_pane)); + return (cmd_find_valid_state(&marked_pane)); } /* Create server socket. */ diff --git a/tmux.h b/tmux.h index c6bd48cf..651990f0 100644 --- a/tmux.h +++ b/tmux.h @@ -1766,6 +1766,9 @@ int cmd_find_target(struct cmd_find_state *, struct cmd_q *, struct client *cmd_find_client(struct cmd_q *, const char *, int); void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); +int cmd_find_valid_state(struct cmd_find_state *); +void cmd_find_copy_state(struct cmd_find_state *, + struct cmd_find_state *); void cmd_find_log_state(const char *, struct cmd_find_state *); /* cmd.c */ @@ -1844,9 +1847,7 @@ void alerts_check_session(struct session *); /* server.c */ extern struct tmuxproc *server_proc; extern struct clients clients; -extern struct session *marked_session; -extern struct winlink *marked_winlink; -extern struct window_pane *marked_window_pane; +extern struct cmd_find_state marked_pane; void server_set_marked(struct session *, struct winlink *, struct window_pane *); void server_clear_marked(void); diff --git a/window.c b/window.c index 75b99672..f0e57190 100644 --- a/window.c +++ b/window.c @@ -568,7 +568,7 @@ window_add_pane(struct window *w, u_int hlimit) void window_lost_pane(struct window *w, struct window_pane *wp) { - if (wp == marked_window_pane) + if (wp == marked_pane.wp) server_clear_marked(); if (wp == w->active) { @@ -691,7 +691,7 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; - if (server_check_marked() && wl == marked_winlink) + if (server_check_marked() && wl == marked_pane.wl) flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; From 56d097cfe053f6cd50db9607503c25a80e3cd989 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 00:11:24 +0000 Subject: [PATCH 505/703] Don't copy marked pane when can just point to it. --- cmd-find.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index b7728ea2..ea1389a9 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -873,22 +873,23 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, log_debug("%s: target %s, type %d", __func__, target, type); log_debug("%s: cmdq %p, flags %#x", __func__, cmdq, flags); + /* Clear new state. */ + cmd_find_clear_state(fs, cmdq, flags); + /* Find current state. */ - cmd_find_clear_state(¤t, cmdq, flags); + fs->current = NULL; if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) - cmd_find_copy_state(¤t, &marked_pane); - else { + fs->current = &marked_pane; + if (fs->current == NULL) { + cmd_find_clear_state(¤t, cmdq, flags); if (cmd_find_current_session(¤t) != 0) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no current session"); goto error; } + fs->current = ¤t; } - /* Clear new state. */ - cmd_find_clear_state(fs, cmdq, flags); - fs->current = ¤t; - /* An empty or NULL target is the current. */ if (target == NULL || *target == '\0') goto current; @@ -1087,9 +1088,9 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, current: /* Use the current session. */ + cmd_find_copy_state(fs, fs->current); if (flags & CMD_FIND_WINDOW_INDEX) - current.idx = -1; - memcpy(fs, ¤t, sizeof *fs); + fs->idx = -1; goto found; error: From 6f417ec9438de4440522483be02c51803d628313 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 00:45:02 +0000 Subject: [PATCH 506/703] We changed somewhat recently to us the pty when tmux was run inside itself to work out the current pane. This is confusing in many cases (particularly notable is that "tmux neww\; splitw" would not split the new window), and the few advantages do not make up for the confusion. So drop this behaviour and return to using the current window and pane; keep the pty check but only use it to limit the list of possible current sessions. --- cmd-find.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index ea1389a9..dbfe99ef 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -249,7 +249,11 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) { struct window_pane *wp; - /* If this is running in a pane, that's great. */ + /* + * If this is running in a pane, we can use that to limit the list of + * sessions to those containing that pane (we still use the current + * window in the best session). + */ if (fs->cmdq->client->tty.path != NULL) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) { if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) @@ -262,11 +266,8 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) if (wp == NULL) goto unknown_pane; - /* We now know the window and pane. */ + /* Find the best session and winlink containing this pane. */ fs->w = wp->window; - fs->wp = wp; - - /* Find the best session and winlink. */ if (cmd_find_best_session_with_window(fs) != 0) { if (wp != NULL) { /* @@ -278,6 +279,13 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) } return (-1); } + + /* Use the current window and pane from this session. */ + fs->wl = fs->s->curw; + fs->idx = fs->wl->idx; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + return (0); unknown_pane: @@ -290,6 +298,7 @@ unknown_pane: fs->idx = fs->wl->idx; fs->w = fs->wl->window; fs->wp = fs->w->active; + return (0); } From 9d88d82d5e8caa7882a28ac95fda19754e5553e7 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 00:52:17 +0000 Subject: [PATCH 507/703] Allow list-keys and list-commands to be run without a running server. --- cmd-list-keys.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 08796e5d..4abe2473 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -39,7 +39,7 @@ const struct cmd_entry cmd_list_keys_entry = { .args = { "t:T:", 0, 0 }, .usage = "[-t mode-table] [-T key-table]", - .flags = 0, + .flags = CMD_STARTSERVER, .exec = cmd_list_keys_exec }; @@ -50,7 +50,7 @@ const struct cmd_entry cmd_list_commands_entry = { .args = { "", 0, 0 }, .usage = "", - .flags = 0, + .flags = CMD_STARTSERVER, .exec = cmd_list_keys_exec }; From ac9778395feb8551e0acbd2f32465b65aeba5e05 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 13:43:07 +0000 Subject: [PATCH 508/703] Some hooks API changes to fire a hook while waiting another cmdq and infrastructure that will be needed soon. --- cmd-attach-session.c | 2 +- hooks.c | 77 +++++++++++++++++++++++++++++++++++++++----- server-client.c | 4 +-- tmux.h | 8 ++++- 4 files changed, 79 insertions(+), 12 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 3b3f9e68..586863aa 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -144,7 +144,7 @@ cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); - hooks_run(c->session->hooks, "client-attached", c); + hooks_run(c->session->hooks, c, "client-attached"); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/hooks.c b/hooks.c index 6f548d23..424faf0c 100644 --- a/hooks.c +++ b/hooks.c @@ -34,6 +34,7 @@ RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); static struct hook *hooks_find1(struct hooks *, const char *); static void hooks_free1(struct hooks *, struct hook *); +static void hooks_emptyfn(struct cmd_q *); static int hooks_cmp(struct hook *hook1, struct hook *hook2) @@ -132,18 +133,78 @@ hooks_find(struct hooks *hooks, const char *name) return (hook); } -void -hooks_run(struct hooks *hooks, const char *name, struct client *c) +static void +hooks_emptyfn(struct cmd_q *hooks_cmdq) +{ + struct cmd_q *cmdq = hooks_cmdq->data; + + if (cmdq != NULL) { + if (hooks_cmdq->client_exit >= 0) + cmdq->client_exit = hooks_cmdq->client_exit; + if (!cmdq_free(cmdq)) + cmdq_continue(cmdq); + } + cmdq_free(hooks_cmdq); +} + +int +hooks_run(struct hooks *hooks, struct client *c, const char *fmt, ...) { struct hook *hook; - struct cmd_q *cmdq; + struct cmd_q *hooks_cmdq; + va_list ap; + char *name; + + va_start(ap, fmt); + xvasprintf(&name, fmt, ap); + va_end(ap); hook = hooks_find(hooks, name); - if (hook == NULL) - return; + if (hook == NULL) { + free(name); + return (-1); + } log_debug("running hook %s", name); + free(name); - cmdq = cmdq_new(c); - cmdq_run(cmdq, hook->cmdlist, NULL); - cmdq_free(cmdq); + hooks_cmdq = cmdq_new(c); + hooks_cmdq->flags |= CMD_Q_NOHOOKS; + hooks_cmdq->parent = NULL; + + cmdq_run(hooks_cmdq, hook->cmdlist, NULL); + cmdq_free(hooks_cmdq); + return (0); +} + +int +hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, const char *fmt, ...) +{ + struct hook *hook; + struct cmd_q *hooks_cmdq; + va_list ap; + char *name; + + va_start(ap, fmt); + xvasprintf(&name, fmt, ap); + va_end(ap); + + hook = hooks_find(hooks, name); + if (hook == NULL) { + free(name); + return (-1); + } + log_debug("running hook %s (parent %p)", name, cmdq); + free(name); + + hooks_cmdq = cmdq_new(cmdq->client); + hooks_cmdq->flags |= CMD_Q_NOHOOKS; + hooks_cmdq->parent = cmdq; + + hooks_cmdq->emptyfn = hooks_emptyfn; + hooks_cmdq->data = cmdq; + + if (cmdq != NULL) + cmdq->references++; + cmdq_run(hooks_cmdq, hook->cmdlist, NULL); + return (0); } diff --git a/server-client.c b/server-client.c index c2b43f38..ec16340b 100644 --- a/server-client.c +++ b/server-client.c @@ -283,7 +283,7 @@ server_client_detach(struct client *c, enum msgtype msgtype) if (s == NULL) return; - hooks_run(c->session->hooks, "client-detached", c); + hooks_run(c->session->hooks, c, "client-detached"); proc_send_s(c->peer, msgtype, s->name); } @@ -1027,7 +1027,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) server_redraw_client(c); } if (c->session != NULL) - hooks_run(c->session->hooks, "client-resized", c); + hooks_run(c->session->hooks, c, "client-resized"); break; case MSG_EXITING: if (datalen != 0) diff --git a/tmux.h b/tmux.h index 651990f0..2b1b29b0 100644 --- a/tmux.h +++ b/tmux.h @@ -1368,6 +1368,8 @@ struct cmd_q { int references; int flags; #define CMD_Q_DEAD 0x1 +#define CMD_Q_REENTRY 0x2 +#define CMD_Q_NOHOOKS 0x4 struct client *client; int client_exit; @@ -1375,6 +1377,7 @@ struct cmd_q { struct cmd_q_items queue; struct cmd_q_item *item; struct cmd *cmd; + struct cmd_q *parent; struct cmd_state state; @@ -1581,7 +1584,10 @@ void hooks_add(struct hooks *, const char *, struct cmd_list *); void hooks_copy(struct hooks *, struct hooks *); void hooks_remove(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *); -void hooks_run(struct hooks *, const char *, struct client *); +int printflike(3, 4) hooks_run(struct hooks *, struct client *, const char *, + ...); +int printflike(3, 4) hooks_wait(struct hooks *, struct cmd_q *, const char *, + ...); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; From 909b737289ee24f1c78c13c10d728a1cf990912d Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Dec 2015 14:32:55 +0000 Subject: [PATCH 509/703] Copy state directly rather than dereferencing wl (which could be NULL). --- cmd-find.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index dbfe99ef..cf39240a 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -834,8 +834,8 @@ cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src) { dst->s = src->s; dst->wl = src->wl; - dst->idx = dst->wl->idx; - dst->w = dst->wl->window; + dst->idx = src->idx; + dst->w = src->w; dst->wp = src->wp; } From 8eb1a7d5dc8d66ca7d17c72e4d8d0e58d6fd2824 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Dec 2015 21:47:00 +0000 Subject: [PATCH 510/703] showenv and setenv need to be CANFAIL. --- cmd-set-environment.c | 4 ++-- cmd-show-environment.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 0808dcb1..f701d7d9 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -36,7 +36,7 @@ const struct cmd_entry cmd_set_environment_entry = { .args = { "grt:u", 1, 2 }, .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", - .tflag = CMD_SESSION, + .tflag = CMD_SESSION_CANFAIL, .flags = 0, .exec = cmd_set_environment_exec @@ -64,7 +64,7 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) else value = args->argv[1]; - if (args_has(self->args, 'g')) + if (args_has(self->args, 'g') || cmdq->state.tflag.s == NULL) env = global_environ; else env = cmdq->state.tflag.s->environ; diff --git a/cmd-show-environment.c b/cmd-show-environment.c index ad9e9e37..54baafe4 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -94,7 +94,7 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) struct environ *env; struct environ_entry *envent; - if (args_has(self->args, 'g')) + if (args_has(self->args, 'g') || cmdq->state.tflag.s == NULL) env = global_environ; else env = cmdq->state.tflag.s->environ; From 021c64310daa4ae4c4e8af08f17c994f21237758 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Dec 2015 21:50:37 +0000 Subject: [PATCH 511/703] Add infrastructure to work out the best target given a pane or window alone and use it to add pane_died and pane_exited hooks. --- cmd-attach-session.c | 2 +- cmd-find.c | 53 ++++++++++++++++++++++++++++++++++++++++---- cmd-queue.c | 4 ++++ cmd-respawn-window.c | 2 +- hooks.c | 20 +++++++++++++++-- server-client.c | 4 ++-- server-fn.c | 9 +++++++- server.c | 2 +- tmux.1 | 6 +++++ window.c | 2 +- 10 files changed, 91 insertions(+), 13 deletions(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 586863aa..4d8a75f8 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -144,7 +144,7 @@ cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); - hooks_run(c->session->hooks, c, "client-attached"); + hooks_run(c->session->hooks, c, NULL, "client-attached"); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/cmd-find.c b/cmd-find.c index cf39240a..15dfa596 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -190,7 +190,7 @@ cmd_find_best_session_with_window(struct cmd_find_state *fs) u_int ssize; struct session *s; - if (fs->cmdq->client != NULL) { + if (fs->cmdq != NULL && fs->cmdq->client != NULL) { fs->s = cmd_find_try_TMUX(fs->cmdq->client, fs->w); if (fs->s != NULL) return (cmd_find_best_winlink_with_window(fs)); @@ -254,7 +254,7 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) * sessions to those containing that pane (we still use the current * window in the best session). */ - if (fs->cmdq->client->tty.path != NULL) { + if (fs->cmdq != NULL && fs->cmdq->client->tty.path != NULL) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) { if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) break; @@ -289,7 +289,9 @@ cmd_find_current_session_with_client(struct cmd_find_state *fs) return (0); unknown_pane: - fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL); + fs->s = NULL; + if (fs->cmdq != NULL) + fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL); if (fs->s == NULL) fs->s = cmd_find_best_session(NULL, 0, fs->flags); if (fs->s == NULL) @@ -310,7 +312,7 @@ int cmd_find_current_session(struct cmd_find_state *fs) { /* If we know the current client, use it. */ - if (fs->cmdq->client != NULL) { + if (fs->cmdq != NULL && fs->cmdq->client != NULL) { log_debug("%s: have client %p%s", __func__, fs->cmdq->client, fs->cmdq->client->session == NULL ? "" : " (with session)"); if (fs->cmdq->client->session == NULL) @@ -862,6 +864,49 @@ cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) log_debug("%s: idx=none", prefix); } +/* Find state from a session. */ +int +cmd_find_from_session(struct cmd_find_state *fs, struct session *s) +{ + cmd_find_clear_state(fs, NULL, 0); + + fs->s = s; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + fs->wp = fs->w->active; + + cmd_find_log_state(__func__, fs); + return (0); +} + +/* Find state from a window. */ +int +cmd_find_from_window(struct cmd_find_state *fs, struct window *w) +{ + cmd_find_clear_state(fs, NULL, 0); + + fs->w = w; + if (cmd_find_best_session_with_window(fs) != 0) + return (-1); + if (cmd_find_best_winlink_with_window(fs) != 0) + return (-1); + + cmd_find_log_state(__func__, fs); + return (0); +} + +/* Find state from a pane. */ +int +cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp) +{ + if (cmd_find_from_window(fs, wp->window) != 0) + return (-1); + fs->wp = wp; + + cmd_find_log_state(__func__, fs); + return (0); +} + /* * Split target into pieces and resolve for the given type. Fills in the given * state. Returns 0 on success or -1 on error. diff --git a/cmd-queue.c b/cmd-queue.c index c0fc26c6..5bc3226a 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -44,6 +44,9 @@ cmdq_new(struct client *c) cmdq->item = NULL; cmdq->cmd = NULL; + cmd_find_clear_state(&cmdq->current, NULL, 0); + cmdq->parent = NULL; + return (cmdq); } @@ -286,3 +289,4 @@ cmdq_flush(struct cmd_q *cmdq) } cmdq->item = NULL; } + diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index aa4e169b..da9a365a 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -90,7 +90,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(env); - server_destroy_pane(wp); + server_destroy_pane(wp, 0); return (CMD_RETURN_ERROR); } layout_init(w, wp); diff --git a/hooks.c b/hooks.c index 424faf0c..c1a016ae 100644 --- a/hooks.c +++ b/hooks.c @@ -42,6 +42,14 @@ hooks_cmp(struct hook *hook1, struct hook *hook2) return (strcmp(hook1->name, hook2->name)); } +struct hooks * +hooks_get(struct session *s) +{ + if (s != NULL) + return (s->hooks); + return (global_hooks); +} + struct hooks * hooks_create(struct hooks *parent) { @@ -148,7 +156,8 @@ hooks_emptyfn(struct cmd_q *hooks_cmdq) } int -hooks_run(struct hooks *hooks, struct client *c, const char *fmt, ...) +hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs, + const char *fmt, ...) { struct hook *hook; struct cmd_q *hooks_cmdq; @@ -169,6 +178,9 @@ hooks_run(struct hooks *hooks, struct client *c, const char *fmt, ...) hooks_cmdq = cmdq_new(c); hooks_cmdq->flags |= CMD_Q_NOHOOKS; + + if (fs != NULL) + cmd_find_copy_state(&hooks_cmdq->current, fs); hooks_cmdq->parent = NULL; cmdq_run(hooks_cmdq, hook->cmdlist, NULL); @@ -177,7 +189,8 @@ hooks_run(struct hooks *hooks, struct client *c, const char *fmt, ...) } int -hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, const char *fmt, ...) +hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs, + const char *fmt, ...) { struct hook *hook; struct cmd_q *hooks_cmdq; @@ -198,6 +211,9 @@ hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, const char *fmt, ...) hooks_cmdq = cmdq_new(cmdq->client); hooks_cmdq->flags |= CMD_Q_NOHOOKS; + + if (fs != NULL) + cmd_find_copy_state(&hooks_cmdq->current, fs); hooks_cmdq->parent = cmdq; hooks_cmdq->emptyfn = hooks_emptyfn; diff --git a/server-client.c b/server-client.c index ec16340b..9b88a165 100644 --- a/server-client.c +++ b/server-client.c @@ -283,7 +283,7 @@ server_client_detach(struct client *c, enum msgtype msgtype) if (s == NULL) return; - hooks_run(c->session->hooks, c, "client-detached"); + hooks_run(c->session->hooks, c, NULL, "client-detached"); proc_send_s(c->peer, msgtype, s->name); } @@ -1027,7 +1027,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) server_redraw_client(c); } if (c->session != NULL) - hooks_run(c->session->hooks, c, "client-resized"); + hooks_run(c->session->hooks, c, NULL, "client-resized"); break; case MSG_EXITING: if (datalen != 0) diff --git a/server-fn.c b/server-fn.c index a4c2dfea..0f35aaaf 100644 --- a/server-fn.c +++ b/server-fn.c @@ -293,12 +293,13 @@ server_unlink_window(struct session *s, struct winlink *wl) } void -server_destroy_pane(struct window_pane *wp) +server_destroy_pane(struct window_pane *wp, int hooks) { struct window *w = wp->window; int old_fd; struct screen_write_ctx ctx; struct grid_cell gc; + struct cmd_find_state fs; old_fd = wp->fd; if (wp->fd != -1) { @@ -319,6 +320,9 @@ server_destroy_pane(struct window_pane *wp) screen_write_puts(&ctx, &gc, "Pane is dead"); screen_write_stop(&ctx); wp->flags |= PANE_REDRAW; + + if (hooks && cmd_find_from_pane(&fs, wp) == 0) + hooks_run(hooks_get(fs.s), NULL, &fs, "pane-died"); return; } @@ -326,6 +330,9 @@ server_destroy_pane(struct window_pane *wp) layout_close_pane(wp); window_remove_pane(w, wp); + if (hooks && cmd_find_from_window(&fs, w) == 0) + hooks_run(hooks_get(fs.s), NULL, &fs, "pane-exited"); + if (TAILQ_EMPTY(&w->panes)) server_kill_window(w); else diff --git a/server.c b/server.c index 0a1146f3..170244c7 100644 --- a/server.c +++ b/server.c @@ -387,7 +387,7 @@ server_child_exited(pid_t pid, int status) TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { wp->status = status; - server_destroy_pane(wp); + server_destroy_pane(wp, 1); break; } } diff --git a/tmux.1 b/tmux.1 index 39e9bd4b..9017b4e7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3230,6 +3230,12 @@ Run when a client is attached. Run when a client is detached .It client-resized Run when a client is resized. +.It pane-died +Run when the program running in a pane exits, but +.Ic remain-on-exit +is on so the pane has not closed. +.It pane-exited +Run when the program running in a pane exits. .El .Pp Hooks are managed with these commands: diff --git a/window.c b/window.c index f0e57190..01d5f973 100644 --- a/window.c +++ b/window.c @@ -974,7 +974,7 @@ window_pane_error_callback(__unused struct bufferevent *bufev, { struct window_pane *wp = data; - server_destroy_pane(wp); + server_destroy_pane(wp, 1); } void From 99e9a4c7864c188857fe57b411312ee669f16b54 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 16 Dec 2015 22:05:35 +0000 Subject: [PATCH 512/703] send-keys -R should reset the input parser to ground state (so it can be used to escape from, for example, printf '\033]2;'). --- cmd-send-keys.c | 2 +- input.c | 44 +++++++++++++++++++++++--------------------- tmux.h | 19 +++++++++++++------ 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 377453b0..7b0b952c 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -86,7 +86,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) } if (args_has(args, 'R')) - input_reset(wp); + input_reset(wp, 1); for (i = 0; i < args->argc; i++) { literal = args_has(args, 'l'); diff --git a/input.c b/input.c index d8e80afb..ed1ebffc 100644 --- a/input.c +++ b/input.c @@ -762,24 +762,12 @@ input_init(struct window_pane *wp) ictx = wp->ictx = xcalloc(1, sizeof *ictx); - input_reset_cell(ictx); - - *ictx->interm_buf = '\0'; - ictx->interm_len = 0; - - *ictx->param_buf = '\0'; - ictx->param_len = 0; - ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); - *ictx->input_buf = '\0'; - ictx->input_len = 0; - - ictx->state = &input_state_ground; - ictx->flags = 0; - ictx->since_ground = evbuffer_new(); + + input_reset(wp, 0); } /* Destroy input parser. */ @@ -797,18 +785,32 @@ input_free(struct window_pane *wp) /* Reset input state and clear screen. */ void -input_reset(struct window_pane *wp) +input_reset(struct window_pane *wp, int clear) { struct input_ctx *ictx = wp->ictx; input_reset_cell(ictx); - if (wp->mode == NULL) - screen_write_start(&ictx->ctx, wp, &wp->base); - else - screen_write_start(&ictx->ctx, NULL, &wp->base); - screen_write_reset(&ictx->ctx); - screen_write_stop(&ictx->ctx); + if (clear) { + if (wp->mode == NULL) + screen_write_start(&ictx->ctx, wp, &wp->base); + else + screen_write_start(&ictx->ctx, NULL, &wp->base); + screen_write_reset(&ictx->ctx); + screen_write_stop(&ictx->ctx); + } + + *ictx->interm_buf = '\0'; + ictx->interm_len = 0; + + *ictx->param_buf = '\0'; + ictx->param_len = 0; + + *ictx->input_buf = '\0'; + ictx->input_len = 0; + + ictx->state = &input_state_ground; + ictx->flags = 0; } /* Return pending data. */ diff --git a/tmux.h b/tmux.h index 2b1b29b0..194d01d8 100644 --- a/tmux.h +++ b/tmux.h @@ -1379,6 +1379,7 @@ struct cmd_q { struct cmd *cmd; struct cmd_q *parent; + struct cmd_find_state current; struct cmd_state state; time_t time; @@ -1576,6 +1577,7 @@ void format_defaults_paste_buffer(struct format_tree *, /* hooks.c */ struct hook; +struct hooks *hooks_get(struct session *); struct hooks *hooks_create(struct hooks *); void hooks_free(struct hooks *); struct hook *hooks_first(struct hooks *); @@ -1584,10 +1586,10 @@ void hooks_add(struct hooks *, const char *, struct cmd_list *); void hooks_copy(struct hooks *, struct hooks *); void hooks_remove(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *); -int printflike(3, 4) hooks_run(struct hooks *, struct client *, const char *, - ...); -int printflike(3, 4) hooks_wait(struct hooks *, struct cmd_q *, const char *, - ...); +int printflike(4, 5) hooks_run(struct hooks *, struct client *, + struct cmd_find_state *, const char *, ...); +int printflike(4, 5) hooks_wait(struct hooks *, struct cmd_q *, + struct cmd_find_state *, const char *, ...); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; @@ -1776,6 +1778,11 @@ int cmd_find_valid_state(struct cmd_find_state *); void cmd_find_copy_state(struct cmd_find_state *, struct cmd_find_state *); void cmd_find_log_state(const char *, struct cmd_find_state *); +int cmd_find_from_session(struct cmd_find_state *, + struct session *); +int cmd_find_from_window(struct cmd_find_state *, struct window *); +int cmd_find_from_pane(struct cmd_find_state *, + struct window_pane *); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); @@ -1896,7 +1903,7 @@ void server_kill_window(struct window *); int server_link_window(struct session *, struct winlink *, struct session *, int, int, int, char **); void server_unlink_window(struct session *, struct winlink *); -void server_destroy_pane(struct window_pane *); +void server_destroy_pane(struct window_pane *, int); void server_destroy_session_group(struct session *); void server_destroy_session(struct session *); void server_check_unattached(void); @@ -1930,7 +1937,7 @@ void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); -void input_reset(struct window_pane *); +void input_reset(struct window_pane *, int); struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); From a337403868cb55f2d7f197cce6e5b18c1154540c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 17 Dec 2015 23:08:22 +0000 Subject: [PATCH 513/703] As well as setting up the state, actually use it in cmd_find_target. --- cmd-find.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index 15dfa596..9b1ca517 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -931,10 +931,11 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, cmd_find_clear_state(fs, cmdq, flags); /* Find current state. */ - fs->current = NULL; if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) fs->current = &marked_pane; - if (fs->current == NULL) { + else if (cmd_find_valid_state(&cmdq->current)) + fs->current = &cmdq->current; + else { cmd_find_clear_state(¤t, cmdq, flags); if (cmd_find_current_session(¤t) != 0) { if (~flags & CMD_FIND_QUIET) From bdbec099cc858e8150884b3bcd984d2586531537 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 19 Dec 2015 08:43:04 +0000 Subject: [PATCH 514/703] Make input off flag (selectp -d) apply to synchronize-panes too. --- window.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/window.c b/window.c index 01d5f973..67d047b9 100644 --- a/window.c +++ b/window.c @@ -1127,7 +1127,9 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 == wp || wp2->mode != NULL) continue; - if (wp2->fd != -1 && window_pane_visible(wp2)) + if (wp2->fd == -1 || wp2->flags & PANE_INPUTOFF) + continue; + if (window_pane_visible(wp2)) input_key(wp2, key, NULL); } } From 83c96d2685a36166f2826c43e644c4b3a6c6d535 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 20 Dec 2015 11:25:13 +0000 Subject: [PATCH 515/703] No need to set cwd on Cygwin now, from Yuya Adachi. --- server-client.c | 1 - 1 file changed, 1 deletion(-) diff --git a/server-client.c b/server-client.c index 611b2212..4f094a77 100644 --- a/server-client.c +++ b/server-client.c @@ -1203,7 +1203,6 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) #ifdef __CYGWIN__ c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); - c->cwd = open(".", O_RDONLY); #endif if (c->flags & CLIENT_CONTROL) { From f2ec911b8a0bb88d23f16b7d9e0f41f29d4ac541 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 21 Dec 2015 09:20:13 +0000 Subject: [PATCH 516/703] Detach the right session with -d. --- cmd-attach-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 4d8a75f8..993f4c75 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -91,7 +91,7 @@ cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_client_detach(c, MSG_DETACH); + server_client_detach(c_loop, MSG_DETACH); } } From 60cbdf9ccd4c1232089272cfdc8ee17ca1bb8d3d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 23 Dec 2015 00:12:57 +0000 Subject: [PATCH 517/703] Repair switch-client -l and switch-client with a window target. --- cmd-switch-client.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 0f6acbf4..bc9f5585 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -90,10 +90,9 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "can't find last session"); return (CMD_RETURN_ERROR); } + } else { if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); - - s = state->tflag.s; if (state->tflag.wl != NULL) { wp = state->tflag.wp; if (wp != NULL) From 915b3a0255f78a5907d40c07bd197da966596040 Mon Sep 17 00:00:00 2001 From: Alexander Bergmann Date: Sun, 29 Nov 2015 09:33:24 +0000 Subject: [PATCH 518/703] Remove libssh and msgpack package forks and use system libraries. --- Makefile.am | 16 +- libssh/.gitignore | 8 - libssh/AUTHORS | 15 - libssh/BSD | 24 - libssh/CMakeLists.txt | 151 - libssh/COPYING | 460 --- libssh/CPackConfig.cmake | 53 - libssh/CTestConfig.cmake | 9 - libssh/ChangeLog | 360 -- libssh/CodingStyle | 59 - libssh/ConfigureChecks.cmake | 229 -- libssh/DefineOptions.cmake | 33 - libssh/INSTALL | 101 - libssh/README | 163 - libssh/SubmittingPatches | 118 - libssh/build/.gitkeep | 0 libssh/cmake/Modules/AddCMockaTest.cmake | 23 - libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS | 22 - .../cmake/Modules/CheckCCompilerFlagSSP.cmake | 26 - .../cmake/Modules/DefineCMakeDefaults.cmake | 30 - .../cmake/Modules/DefineCompilerFlags.cmake | 84 - .../Modules/DefineInstallationPaths.cmake | 109 - .../Modules/DefinePlatformDefaults.cmake | 32 - libssh/cmake/Modules/FindArgp.cmake | 60 - libssh/cmake/Modules/FindCMocka.cmake | 66 - libssh/cmake/Modules/FindGCrypt.cmake | 75 - libssh/cmake/Modules/FindGSSAPI.cmake | 324 -- libssh/cmake/Modules/FindNSIS.cmake | 55 - libssh/cmake/Modules/FindNaCl.cmake | 61 - libssh/cmake/Modules/FindOpenSSL.cmake | 208 - libssh/cmake/Modules/FindZLIB.cmake | 120 - .../Modules/MacroEnsureOutOfSourceBuild.cmake | 17 - libssh/cmake/Modules/UseDoxygen.cmake | 140 - libssh/config.h.cmake | 176 - libssh/doc/CMakeLists.txt | 5 - libssh/doc/Doxyfile.in | 1917 --------- libssh/doc/authentication.dox | 375 -- libssh/doc/command.dox | 94 - libssh/doc/curve25519-sha256@libssh.org.txt | 119 - libssh/doc/forwarding.dox | 230 -- libssh/doc/guided_tour.dox | 455 --- libssh/doc/introduction.dox | 49 - libssh/doc/linking.dox | 30 - libssh/doc/mainpage.dox | 224 -- libssh/doc/scp.dox | 268 -- libssh/doc/sftp.dox | 431 --- libssh/doc/shell.dox | 361 -- libssh/doc/tbd.dox | 14 - libssh/doc/threading.dox | 66 - libssh/examples/CMakeLists.txt | 66 - libssh/examples/authentication.c | 176 - libssh/examples/connect_ssh.c | 67 - libssh/examples/examples_common.h | 22 - libssh/examples/exec.c | 66 - libssh/examples/knownhosts.c | 110 - libssh/examples/libssh_scp.c | 327 -- libssh/examples/libsshpp.cpp | 33 - libssh/examples/libsshpp_noexcept.cpp | 41 - libssh/examples/proxy.c | 347 -- libssh/examples/sample.c | 514 --- libssh/examples/samplesftp.c | 268 -- libssh/examples/samplesshd-cb.c | 306 -- libssh/examples/samplesshd-kbdint.c | 413 -- libssh/examples/scp_download.c | 175 - libssh/examples/senddata.c | 64 - libssh/examples/ssh_server_fork.c | 697 ---- libssh/examples/sshnetcat.c | 265 -- libssh/include/CMakeLists.txt | 3 - libssh/include/libssh/CMakeLists.txt | 39 - libssh/include/libssh/agent.h | 118 - libssh/include/libssh/auth.h | 111 - libssh/include/libssh/bignum.h | 32 - libssh/include/libssh/bind.h | 53 - libssh/include/libssh/buffer.h | 85 - libssh/include/libssh/callbacks.h | 855 ----- libssh/include/libssh/channels.h | 118 - libssh/include/libssh/crc32.h | 28 - libssh/include/libssh/crypto.h | 125 - libssh/include/libssh/curve25519.h | 57 - libssh/include/libssh/dh.h | 51 - libssh/include/libssh/ecdh.h | 43 - libssh/include/libssh/ed25519.h | 79 - libssh/include/libssh/fe25519.h | 68 - libssh/include/libssh/ge25519.h | 43 - libssh/include/libssh/gssapi.h | 45 - libssh/include/libssh/kex.h | 50 - libssh/include/libssh/keys.h | 56 - libssh/include/libssh/knownhosts.h | 27 - libssh/include/libssh/legacy.h | 120 - libssh/include/libssh/libcrypto.h | 104 - libssh/include/libssh/libgcrypt.h | 78 - libssh/include/libssh/libssh.h | 674 ---- libssh/include/libssh/libsshpp.hpp | 614 --- libssh/include/libssh/messages.h | 108 - libssh/include/libssh/misc.h | 92 - libssh/include/libssh/options.h | 28 - libssh/include/libssh/packet.h | 89 - libssh/include/libssh/pcap.h | 46 - libssh/include/libssh/pki.h | 128 - libssh/include/libssh/pki_priv.h | 108 - libssh/include/libssh/poll.h | 159 - libssh/include/libssh/priv.h | 305 -- libssh/include/libssh/sc25519.h | 74 - libssh/include/libssh/scp.h | 55 - libssh/include/libssh/server.h | 336 -- libssh/include/libssh/session.h | 216 -- libssh/include/libssh/sftp.h | 1000 ----- libssh/include/libssh/socket.h | 69 - libssh/include/libssh/ssh1.h | 82 - libssh/include/libssh/ssh2.h | 80 - libssh/include/libssh/string.h | 41 - libssh/include/libssh/threads.h | 31 - libssh/include/libssh/wrapper.h | 103 - libssh/libssh-build-tree-settings.cmake.in | 1 - libssh/libssh-config-version.cmake.in | 11 - libssh/libssh-config.cmake.in | 13 - libssh/libssh.pc.cmake | 6 - libssh/libssh_threads.pc.cmake | 6 - libssh/obj/build_make.sh | 197 - libssh/src/CMakeLists.txt | 299 -- libssh/src/agent.c | 577 --- libssh/src/auth.c | 1810 --------- libssh/src/auth1.c | 229 -- libssh/src/base64.c | 292 -- libssh/src/bignum.c | 96 - libssh/src/bind.c | 504 --- libssh/src/buffer.c | 996 ----- libssh/src/callbacks.c | 99 - libssh/src/channels.c | 3418 ----------------- libssh/src/channels1.c | 395 -- libssh/src/client.c | 704 ---- libssh/src/config.c | 399 -- libssh/src/connect.c | 516 --- libssh/src/crc32.c | 95 - libssh/src/curve25519.c | 286 -- libssh/src/curve25519_ref.c | 272 -- libssh/src/dh.c | 1124 ------ libssh/src/ecdh.c | 349 -- libssh/src/ed25519.c | 222 -- libssh/src/error.c | 139 - libssh/src/fe25519.c | 416 -- libssh/src/gcrypt_missing.c | 101 - libssh/src/ge25519.c | 367 -- libssh/src/ge25519_base.data | 858 ----- libssh/src/getpass.c | 288 -- libssh/src/gssapi.c | 876 ----- libssh/src/gzip.c | 221 -- libssh/src/init.c | 85 - libssh/src/kex.c | 650 ---- libssh/src/kex1.c | 497 --- libssh/src/known_hosts.c | 748 ---- libssh/src/legacy.c | 728 ---- libssh/src/libcrypto.c | 706 ---- libssh/src/libgcrypt.c | 601 --- libssh/src/log.c | 241 -- libssh/src/match.c | 189 - libssh/src/messages.c | 1379 ------- libssh/src/misc.c | 1035 ----- libssh/src/options.c | 1568 -------- libssh/src/packet.c | 605 --- libssh/src/packet1.c | 373 -- libssh/src/packet_cb.c | 257 -- libssh/src/packet_crypt.c | 193 - libssh/src/pcap.c | 511 --- libssh/src/pki.c | 1649 -------- libssh/src/pki_crypto.c | 1671 -------- libssh/src/pki_ed25519.c | 305 -- libssh/src/pki_gcrypt.c | 1713 --------- libssh/src/poll.c | 970 ----- libssh/src/sc25519.c | 373 -- libssh/src/scp.c | 837 ---- libssh/src/server.c | 1207 ------ libssh/src/session.c | 888 ----- libssh/src/sftp.c | 3081 --------------- libssh/src/sftpserver.c | 521 --- libssh/src/socket.c | 872 ----- libssh/src/string.c | 271 -- libssh/src/threads.c | 197 - libssh/src/threads/CMakeLists.txt | 127 - libssh/src/threads/pthread.c | 99 - libssh/src/wrapper.c | 466 --- libssh/tests/CMakeLists.txt | 52 - libssh/tests/authentication.c | 74 - libssh/tests/benchmarks/CMakeLists.txt | 21 - libssh/tests/benchmarks/bench1.sh | 13 - libssh/tests/benchmarks/bench2.sh | 13 - libssh/tests/benchmarks/bench_raw.c | 285 -- libssh/tests/benchmarks/bench_scp.c | 150 - libssh/tests/benchmarks/bench_sftp.c | 239 -- libssh/tests/benchmarks/benchmarks.c | 400 -- libssh/tests/benchmarks/benchmarks.h | 99 - libssh/tests/benchmarks/latency.c | 148 - libssh/tests/chmodtest.c | 33 - libssh/tests/client/CMakeLists.txt | 15 - libssh/tests/client/torture_algorithms.c | 331 -- libssh/tests/client/torture_auth.c | 443 --- libssh/tests/client/torture_connect.c | 156 - libssh/tests/client/torture_forward.c | 94 - libssh/tests/client/torture_knownhosts.c | 300 -- libssh/tests/client/torture_proxycommand.c | 57 - libssh/tests/client/torture_request_env.c | 114 - libssh/tests/client/torture_session.c | 112 - libssh/tests/client/torture_sftp_dir.c | 74 - libssh/tests/client/torture_sftp_read.c | 83 - libssh/tests/client/torture_sftp_static.c | 32 - libssh/tests/cmdline.c | 71 - libssh/tests/connection.c | 31 - libssh/tests/ctest-default.cmake | 71 - libssh/tests/generate.py | 10 - libssh/tests/pkd/CMakeLists.txt | 35 - libssh/tests/pkd/pkd_client.h | 69 - libssh/tests/pkd/pkd_daemon.c | 500 --- libssh/tests/pkd/pkd_daemon.h | 40 - libssh/tests/pkd/pkd_hello.c | 534 --- libssh/tests/pkd/pkd_keyutil.c | 138 - libssh/tests/pkd/pkd_keyutil.h | 40 - libssh/tests/pkd/pkd_util.c | 46 - libssh/tests/pkd/pkd_util.h | 16 - libssh/tests/sftp_stress/main.c | 174 - libssh/tests/test_exec.c | 62 - libssh/tests/test_pcap.c | 50 - libssh/tests/test_socket.c | 93 - libssh/tests/test_ssh_bind_accept_fd.c | 139 - libssh/tests/test_tunnel.c | 76 - libssh/tests/tests.h | 8 - libssh/tests/torture.c | 556 --- libssh/tests/torture.h | 84 - libssh/tests/unittests/CMakeLists.txt | 18 - libssh/tests/unittests/torture_buffer.c | 268 -- libssh/tests/unittests/torture_callbacks.c | 111 - libssh/tests/unittests/torture_channel.c | 49 - libssh/tests/unittests/torture_init.c | 23 - libssh/tests/unittests/torture_isipaddr.c | 56 - libssh/tests/unittests/torture_keyfiles.c | 261 -- libssh/tests/unittests/torture_list.c | 91 - libssh/tests/unittests/torture_misc.c | 210 - libssh/tests/unittests/torture_options.c | 215 -- libssh/tests/unittests/torture_pki.c | 1338 ------- libssh/tests/unittests/torture_rand.c | 68 - libssh/tests/valgrind.supp | 117 - msgpack/.gitignore | 58 - msgpack/AUTHORS | 1 - msgpack/COPYING | 14 - msgpack/ChangeLog | 51 - msgpack/Doxyfile | 1552 -------- msgpack/LICENSE | 202 - msgpack/Makefile.am | 23 - msgpack/NOTICE | 4 - msgpack/README.md | 73 - msgpack/README_crosslang.md | 38 - msgpack/bootstrap | 127 - msgpack/cases.json | 1 - msgpack/cases.mpac | Bin 213 -> 0 bytes msgpack/cases_compact.mpac | Bin 116 -> 0 bytes msgpack/cases_gen.rb | 99 - msgpack/configure.in | 100 - msgpack/crosslang.cc | 133 - msgpack/crosslang.rb | 88 - msgpack/example/custom.cc | 58 - msgpack/example/protocol.cc | 86 - msgpack/example/simple.c | 37 - msgpack/example/simple.cc | 37 - msgpack/example/stream.cc | 136 - msgpack/msgpack.pc.in | 10 - msgpack/msgpack_vc.postbuild.bat | 45 - msgpack/msgpack_vc8.sln | 20 - msgpack/msgpack_vc8.vcproj | 299 -- msgpack/pack_define.h | 26 - msgpack/pack_template.h | 771 ---- msgpack/preprocess | 34 - msgpack/src/Makefile.am | 107 - msgpack/src/gcc_atomic.cpp | 17 - msgpack/src/gcc_atomic.h | 33 - msgpack/src/msgpack.h | 30 - msgpack/src/msgpack.hpp | 24 - msgpack/src/msgpack/object.h | 98 - msgpack/src/msgpack/object.hpp | 412 -- msgpack/src/msgpack/pack.h | 143 - msgpack/src/msgpack/pack.hpp | 318 -- msgpack/src/msgpack/sbuffer.h | 111 - msgpack/src/msgpack/sbuffer.hpp | 112 - msgpack/src/msgpack/type.hpp | 16 - msgpack/src/msgpack/type/bool.hpp | 55 - msgpack/src/msgpack/type/define.hpp.erb | 136 - msgpack/src/msgpack/type/deque.hpp | 77 - msgpack/src/msgpack/type/fixint.hpp | 172 - msgpack/src/msgpack/type/float.hpp | 82 - msgpack/src/msgpack/type/int.hpp | 211 - msgpack/src/msgpack/type/list.hpp | 77 - msgpack/src/msgpack/type/map.hpp | 205 - msgpack/src/msgpack/type/nil.hpp | 65 - msgpack/src/msgpack/type/pair.hpp | 61 - msgpack/src/msgpack/type/raw.hpp | 94 - msgpack/src/msgpack/type/set.hpp | 122 - msgpack/src/msgpack/type/string.hpp | 62 - .../src/msgpack/type/tr1/unordered_map.hpp | 129 - .../src/msgpack/type/tr1/unordered_set.hpp | 122 - msgpack/src/msgpack/type/tuple.hpp.erb | 206 - msgpack/src/msgpack/type/vector.hpp | 81 - msgpack/src/msgpack/unpack.h | 260 -- msgpack/src/msgpack/unpack.hpp | 374 -- msgpack/src/msgpack/version.h.in | 40 - msgpack/src/msgpack/vrefbuffer.h | 142 - msgpack/src/msgpack/vrefbuffer.hpp | 97 - msgpack/src/msgpack/zbuffer.h | 207 - msgpack/src/msgpack/zbuffer.hpp | 100 - msgpack/src/msgpack/zone.h | 147 - msgpack/src/msgpack/zone.hpp.erb | 155 - msgpack/src/object.cpp | 87 - msgpack/src/objectc.c | 237 -- msgpack/src/unpack.c | 468 --- msgpack/src/version.c | 17 - msgpack/src/vrefbuffer.c | 220 -- msgpack/src/zone.c | 221 -- msgpack/sysdep.h | 195 - msgpack/test/Makefile.am | 54 - msgpack/test/buffer.cc | 72 - msgpack/test/cases.cc | 38 - msgpack/test/convert.cc | 76 - msgpack/test/fixint.cc | 55 - msgpack/test/fixint_c.cc | 32 - msgpack/test/msgpack_test.cpp | 982 ----- msgpack/test/msgpackc_test.cpp | 424 -- msgpack/test/object.cc | 134 - msgpack/test/pack_unpack.cc | 123 - msgpack/test/pack_unpack_c.cc | 70 - msgpack/test/streaming.cc | 220 -- msgpack/test/streaming_c.cc | 98 - msgpack/test/version.cc | 13 - msgpack/test/zone.cc | 78 - msgpack/unpack_define.h | 93 - msgpack/unpack_template.h | 413 -- 332 files changed, 2 insertions(+), 83090 deletions(-) delete mode 100644 libssh/.gitignore delete mode 100644 libssh/AUTHORS delete mode 100644 libssh/BSD delete mode 100644 libssh/CMakeLists.txt delete mode 100644 libssh/COPYING delete mode 100644 libssh/CPackConfig.cmake delete mode 100644 libssh/CTestConfig.cmake delete mode 100644 libssh/ChangeLog delete mode 100644 libssh/CodingStyle delete mode 100644 libssh/ConfigureChecks.cmake delete mode 100644 libssh/DefineOptions.cmake delete mode 100644 libssh/INSTALL delete mode 100644 libssh/README delete mode 100644 libssh/SubmittingPatches delete mode 100644 libssh/build/.gitkeep delete mode 100644 libssh/cmake/Modules/AddCMockaTest.cmake delete mode 100644 libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS delete mode 100644 libssh/cmake/Modules/CheckCCompilerFlagSSP.cmake delete mode 100644 libssh/cmake/Modules/DefineCMakeDefaults.cmake delete mode 100644 libssh/cmake/Modules/DefineCompilerFlags.cmake delete mode 100644 libssh/cmake/Modules/DefineInstallationPaths.cmake delete mode 100644 libssh/cmake/Modules/DefinePlatformDefaults.cmake delete mode 100644 libssh/cmake/Modules/FindArgp.cmake delete mode 100644 libssh/cmake/Modules/FindCMocka.cmake delete mode 100644 libssh/cmake/Modules/FindGCrypt.cmake delete mode 100644 libssh/cmake/Modules/FindGSSAPI.cmake delete mode 100644 libssh/cmake/Modules/FindNSIS.cmake delete mode 100644 libssh/cmake/Modules/FindNaCl.cmake delete mode 100644 libssh/cmake/Modules/FindOpenSSL.cmake delete mode 100644 libssh/cmake/Modules/FindZLIB.cmake delete mode 100644 libssh/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake delete mode 100644 libssh/cmake/Modules/UseDoxygen.cmake delete mode 100644 libssh/config.h.cmake delete mode 100644 libssh/doc/CMakeLists.txt delete mode 100644 libssh/doc/Doxyfile.in delete mode 100644 libssh/doc/authentication.dox delete mode 100644 libssh/doc/command.dox delete mode 100644 libssh/doc/curve25519-sha256@libssh.org.txt delete mode 100644 libssh/doc/forwarding.dox delete mode 100644 libssh/doc/guided_tour.dox delete mode 100644 libssh/doc/introduction.dox delete mode 100644 libssh/doc/linking.dox delete mode 100644 libssh/doc/mainpage.dox delete mode 100644 libssh/doc/scp.dox delete mode 100644 libssh/doc/sftp.dox delete mode 100644 libssh/doc/shell.dox delete mode 100644 libssh/doc/tbd.dox delete mode 100644 libssh/doc/threading.dox delete mode 100644 libssh/examples/CMakeLists.txt delete mode 100644 libssh/examples/authentication.c delete mode 100644 libssh/examples/connect_ssh.c delete mode 100644 libssh/examples/examples_common.h delete mode 100644 libssh/examples/exec.c delete mode 100644 libssh/examples/knownhosts.c delete mode 100644 libssh/examples/libssh_scp.c delete mode 100644 libssh/examples/libsshpp.cpp delete mode 100644 libssh/examples/libsshpp_noexcept.cpp delete mode 100644 libssh/examples/proxy.c delete mode 100644 libssh/examples/sample.c delete mode 100644 libssh/examples/samplesftp.c delete mode 100644 libssh/examples/samplesshd-cb.c delete mode 100644 libssh/examples/samplesshd-kbdint.c delete mode 100644 libssh/examples/scp_download.c delete mode 100644 libssh/examples/senddata.c delete mode 100644 libssh/examples/ssh_server_fork.c delete mode 100644 libssh/examples/sshnetcat.c delete mode 100644 libssh/include/CMakeLists.txt delete mode 100644 libssh/include/libssh/CMakeLists.txt delete mode 100644 libssh/include/libssh/agent.h delete mode 100644 libssh/include/libssh/auth.h delete mode 100644 libssh/include/libssh/bignum.h delete mode 100644 libssh/include/libssh/bind.h delete mode 100644 libssh/include/libssh/buffer.h delete mode 100644 libssh/include/libssh/callbacks.h delete mode 100644 libssh/include/libssh/channels.h delete mode 100644 libssh/include/libssh/crc32.h delete mode 100644 libssh/include/libssh/crypto.h delete mode 100644 libssh/include/libssh/curve25519.h delete mode 100644 libssh/include/libssh/dh.h delete mode 100644 libssh/include/libssh/ecdh.h delete mode 100644 libssh/include/libssh/ed25519.h delete mode 100644 libssh/include/libssh/fe25519.h delete mode 100644 libssh/include/libssh/ge25519.h delete mode 100644 libssh/include/libssh/gssapi.h delete mode 100644 libssh/include/libssh/kex.h delete mode 100644 libssh/include/libssh/keys.h delete mode 100644 libssh/include/libssh/knownhosts.h delete mode 100644 libssh/include/libssh/legacy.h delete mode 100644 libssh/include/libssh/libcrypto.h delete mode 100644 libssh/include/libssh/libgcrypt.h delete mode 100644 libssh/include/libssh/libssh.h delete mode 100644 libssh/include/libssh/libsshpp.hpp delete mode 100644 libssh/include/libssh/messages.h delete mode 100644 libssh/include/libssh/misc.h delete mode 100644 libssh/include/libssh/options.h delete mode 100644 libssh/include/libssh/packet.h delete mode 100644 libssh/include/libssh/pcap.h delete mode 100644 libssh/include/libssh/pki.h delete mode 100644 libssh/include/libssh/pki_priv.h delete mode 100644 libssh/include/libssh/poll.h delete mode 100644 libssh/include/libssh/priv.h delete mode 100644 libssh/include/libssh/sc25519.h delete mode 100644 libssh/include/libssh/scp.h delete mode 100644 libssh/include/libssh/server.h delete mode 100644 libssh/include/libssh/session.h delete mode 100644 libssh/include/libssh/sftp.h delete mode 100644 libssh/include/libssh/socket.h delete mode 100644 libssh/include/libssh/ssh1.h delete mode 100644 libssh/include/libssh/ssh2.h delete mode 100644 libssh/include/libssh/string.h delete mode 100644 libssh/include/libssh/threads.h delete mode 100644 libssh/include/libssh/wrapper.h delete mode 100644 libssh/libssh-build-tree-settings.cmake.in delete mode 100644 libssh/libssh-config-version.cmake.in delete mode 100644 libssh/libssh-config.cmake.in delete mode 100644 libssh/libssh.pc.cmake delete mode 100644 libssh/libssh_threads.pc.cmake delete mode 100755 libssh/obj/build_make.sh delete mode 100644 libssh/src/CMakeLists.txt delete mode 100644 libssh/src/agent.c delete mode 100644 libssh/src/auth.c delete mode 100644 libssh/src/auth1.c delete mode 100644 libssh/src/base64.c delete mode 100644 libssh/src/bignum.c delete mode 100644 libssh/src/bind.c delete mode 100644 libssh/src/buffer.c delete mode 100644 libssh/src/callbacks.c delete mode 100644 libssh/src/channels.c delete mode 100644 libssh/src/channels1.c delete mode 100644 libssh/src/client.c delete mode 100644 libssh/src/config.c delete mode 100644 libssh/src/connect.c delete mode 100644 libssh/src/crc32.c delete mode 100644 libssh/src/curve25519.c delete mode 100644 libssh/src/curve25519_ref.c delete mode 100644 libssh/src/dh.c delete mode 100644 libssh/src/ecdh.c delete mode 100644 libssh/src/ed25519.c delete mode 100644 libssh/src/error.c delete mode 100644 libssh/src/fe25519.c delete mode 100644 libssh/src/gcrypt_missing.c delete mode 100644 libssh/src/ge25519.c delete mode 100644 libssh/src/ge25519_base.data delete mode 100644 libssh/src/getpass.c delete mode 100644 libssh/src/gssapi.c delete mode 100644 libssh/src/gzip.c delete mode 100644 libssh/src/init.c delete mode 100644 libssh/src/kex.c delete mode 100644 libssh/src/kex1.c delete mode 100644 libssh/src/known_hosts.c delete mode 100644 libssh/src/legacy.c delete mode 100644 libssh/src/libcrypto.c delete mode 100644 libssh/src/libgcrypt.c delete mode 100644 libssh/src/log.c delete mode 100644 libssh/src/match.c delete mode 100644 libssh/src/messages.c delete mode 100644 libssh/src/misc.c delete mode 100644 libssh/src/options.c delete mode 100644 libssh/src/packet.c delete mode 100644 libssh/src/packet1.c delete mode 100644 libssh/src/packet_cb.c delete mode 100644 libssh/src/packet_crypt.c delete mode 100644 libssh/src/pcap.c delete mode 100644 libssh/src/pki.c delete mode 100644 libssh/src/pki_crypto.c delete mode 100644 libssh/src/pki_ed25519.c delete mode 100644 libssh/src/pki_gcrypt.c delete mode 100644 libssh/src/poll.c delete mode 100644 libssh/src/sc25519.c delete mode 100644 libssh/src/scp.c delete mode 100644 libssh/src/server.c delete mode 100644 libssh/src/session.c delete mode 100644 libssh/src/sftp.c delete mode 100644 libssh/src/sftpserver.c delete mode 100644 libssh/src/socket.c delete mode 100644 libssh/src/string.c delete mode 100644 libssh/src/threads.c delete mode 100644 libssh/src/threads/CMakeLists.txt delete mode 100644 libssh/src/threads/pthread.c delete mode 100644 libssh/src/wrapper.c delete mode 100644 libssh/tests/CMakeLists.txt delete mode 100644 libssh/tests/authentication.c delete mode 100644 libssh/tests/benchmarks/CMakeLists.txt delete mode 100644 libssh/tests/benchmarks/bench1.sh delete mode 100755 libssh/tests/benchmarks/bench2.sh delete mode 100644 libssh/tests/benchmarks/bench_raw.c delete mode 100644 libssh/tests/benchmarks/bench_scp.c delete mode 100644 libssh/tests/benchmarks/bench_sftp.c delete mode 100644 libssh/tests/benchmarks/benchmarks.c delete mode 100644 libssh/tests/benchmarks/benchmarks.h delete mode 100644 libssh/tests/benchmarks/latency.c delete mode 100644 libssh/tests/chmodtest.c delete mode 100644 libssh/tests/client/CMakeLists.txt delete mode 100644 libssh/tests/client/torture_algorithms.c delete mode 100644 libssh/tests/client/torture_auth.c delete mode 100644 libssh/tests/client/torture_connect.c delete mode 100644 libssh/tests/client/torture_forward.c delete mode 100644 libssh/tests/client/torture_knownhosts.c delete mode 100644 libssh/tests/client/torture_proxycommand.c delete mode 100644 libssh/tests/client/torture_request_env.c delete mode 100644 libssh/tests/client/torture_session.c delete mode 100644 libssh/tests/client/torture_sftp_dir.c delete mode 100644 libssh/tests/client/torture_sftp_read.c delete mode 100644 libssh/tests/client/torture_sftp_static.c delete mode 100644 libssh/tests/cmdline.c delete mode 100644 libssh/tests/connection.c delete mode 100644 libssh/tests/ctest-default.cmake delete mode 100755 libssh/tests/generate.py delete mode 100644 libssh/tests/pkd/CMakeLists.txt delete mode 100644 libssh/tests/pkd/pkd_client.h delete mode 100644 libssh/tests/pkd/pkd_daemon.c delete mode 100644 libssh/tests/pkd/pkd_daemon.h delete mode 100644 libssh/tests/pkd/pkd_hello.c delete mode 100644 libssh/tests/pkd/pkd_keyutil.c delete mode 100644 libssh/tests/pkd/pkd_keyutil.h delete mode 100644 libssh/tests/pkd/pkd_util.c delete mode 100644 libssh/tests/pkd/pkd_util.h delete mode 100644 libssh/tests/sftp_stress/main.c delete mode 100644 libssh/tests/test_exec.c delete mode 100644 libssh/tests/test_pcap.c delete mode 100644 libssh/tests/test_socket.c delete mode 100644 libssh/tests/test_ssh_bind_accept_fd.c delete mode 100644 libssh/tests/test_tunnel.c delete mode 100644 libssh/tests/tests.h delete mode 100644 libssh/tests/torture.c delete mode 100644 libssh/tests/torture.h delete mode 100644 libssh/tests/unittests/CMakeLists.txt delete mode 100644 libssh/tests/unittests/torture_buffer.c delete mode 100644 libssh/tests/unittests/torture_callbacks.c delete mode 100644 libssh/tests/unittests/torture_channel.c delete mode 100644 libssh/tests/unittests/torture_init.c delete mode 100644 libssh/tests/unittests/torture_isipaddr.c delete mode 100644 libssh/tests/unittests/torture_keyfiles.c delete mode 100644 libssh/tests/unittests/torture_list.c delete mode 100644 libssh/tests/unittests/torture_misc.c delete mode 100644 libssh/tests/unittests/torture_options.c delete mode 100644 libssh/tests/unittests/torture_pki.c delete mode 100644 libssh/tests/unittests/torture_rand.c delete mode 100644 libssh/tests/valgrind.supp delete mode 100644 msgpack/.gitignore delete mode 100644 msgpack/AUTHORS delete mode 100644 msgpack/COPYING delete mode 100644 msgpack/ChangeLog delete mode 100644 msgpack/Doxyfile delete mode 100644 msgpack/LICENSE delete mode 100644 msgpack/Makefile.am delete mode 100644 msgpack/NOTICE delete mode 100644 msgpack/README.md delete mode 100644 msgpack/README_crosslang.md delete mode 100755 msgpack/bootstrap delete mode 100644 msgpack/cases.json delete mode 100644 msgpack/cases.mpac delete mode 100644 msgpack/cases_compact.mpac delete mode 100644 msgpack/cases_gen.rb delete mode 100644 msgpack/configure.in delete mode 100644 msgpack/crosslang.cc delete mode 100644 msgpack/crosslang.rb delete mode 100644 msgpack/example/custom.cc delete mode 100644 msgpack/example/protocol.cc delete mode 100644 msgpack/example/simple.c delete mode 100644 msgpack/example/simple.cc delete mode 100644 msgpack/example/stream.cc delete mode 100644 msgpack/msgpack.pc.in delete mode 100644 msgpack/msgpack_vc.postbuild.bat delete mode 100644 msgpack/msgpack_vc8.sln delete mode 100644 msgpack/msgpack_vc8.vcproj delete mode 100644 msgpack/pack_define.h delete mode 100644 msgpack/pack_template.h delete mode 100755 msgpack/preprocess delete mode 100644 msgpack/src/Makefile.am delete mode 100644 msgpack/src/gcc_atomic.cpp delete mode 100644 msgpack/src/gcc_atomic.h delete mode 100644 msgpack/src/msgpack.h delete mode 100644 msgpack/src/msgpack.hpp delete mode 100644 msgpack/src/msgpack/object.h delete mode 100644 msgpack/src/msgpack/object.hpp delete mode 100644 msgpack/src/msgpack/pack.h delete mode 100644 msgpack/src/msgpack/pack.hpp delete mode 100644 msgpack/src/msgpack/sbuffer.h delete mode 100644 msgpack/src/msgpack/sbuffer.hpp delete mode 100644 msgpack/src/msgpack/type.hpp delete mode 100644 msgpack/src/msgpack/type/bool.hpp delete mode 100644 msgpack/src/msgpack/type/define.hpp.erb delete mode 100644 msgpack/src/msgpack/type/deque.hpp delete mode 100644 msgpack/src/msgpack/type/fixint.hpp delete mode 100644 msgpack/src/msgpack/type/float.hpp delete mode 100644 msgpack/src/msgpack/type/int.hpp delete mode 100644 msgpack/src/msgpack/type/list.hpp delete mode 100644 msgpack/src/msgpack/type/map.hpp delete mode 100644 msgpack/src/msgpack/type/nil.hpp delete mode 100644 msgpack/src/msgpack/type/pair.hpp delete mode 100644 msgpack/src/msgpack/type/raw.hpp delete mode 100644 msgpack/src/msgpack/type/set.hpp delete mode 100644 msgpack/src/msgpack/type/string.hpp delete mode 100644 msgpack/src/msgpack/type/tr1/unordered_map.hpp delete mode 100644 msgpack/src/msgpack/type/tr1/unordered_set.hpp delete mode 100644 msgpack/src/msgpack/type/tuple.hpp.erb delete mode 100644 msgpack/src/msgpack/type/vector.hpp delete mode 100644 msgpack/src/msgpack/unpack.h delete mode 100644 msgpack/src/msgpack/unpack.hpp delete mode 100644 msgpack/src/msgpack/version.h.in delete mode 100644 msgpack/src/msgpack/vrefbuffer.h delete mode 100644 msgpack/src/msgpack/vrefbuffer.hpp delete mode 100644 msgpack/src/msgpack/zbuffer.h delete mode 100644 msgpack/src/msgpack/zbuffer.hpp delete mode 100644 msgpack/src/msgpack/zone.h delete mode 100644 msgpack/src/msgpack/zone.hpp.erb delete mode 100644 msgpack/src/object.cpp delete mode 100644 msgpack/src/objectc.c delete mode 100644 msgpack/src/unpack.c delete mode 100644 msgpack/src/version.c delete mode 100644 msgpack/src/vrefbuffer.c delete mode 100644 msgpack/src/zone.c delete mode 100644 msgpack/sysdep.h delete mode 100644 msgpack/test/Makefile.am delete mode 100644 msgpack/test/buffer.cc delete mode 100644 msgpack/test/cases.cc delete mode 100644 msgpack/test/convert.cc delete mode 100644 msgpack/test/fixint.cc delete mode 100644 msgpack/test/fixint_c.cc delete mode 100644 msgpack/test/msgpack_test.cpp delete mode 100644 msgpack/test/msgpackc_test.cpp delete mode 100644 msgpack/test/object.cc delete mode 100644 msgpack/test/pack_unpack.cc delete mode 100644 msgpack/test/pack_unpack_c.cc delete mode 100644 msgpack/test/streaming.cc delete mode 100644 msgpack/test/streaming_c.cc delete mode 100644 msgpack/test/version.cc delete mode 100644 msgpack/test/zone.cc delete mode 100644 msgpack/unpack_define.h delete mode 100644 msgpack/unpack_template.h diff --git a/Makefile.am b/Makefile.am index ac5ff3eb..989d27b0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,6 @@ CFLAGS += -D_GNU_SOURCE endif CFLAGS += -Wno-unused-parameter -Wno-unused-variable -CFLAGS += -Ilibssh/include/ -Imsgpack/src # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. @@ -243,17 +242,6 @@ nodist_tmate_SOURCES += compat/b64_ntop.c endif tmate_LDADD = \ - libssh/build/src/libssh.a \ - msgpack/src/.libs/libmsgpackc.a + -lssh \ + -lmsgpackc -*.c: $(tmate_LDADD) - -libssh/build/src/libssh.a: - cd libssh/build; ([ -f Makefile ] || cmake .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF) - +make -C libssh/build ssh_static - -msgpack/src/.libs/libmsgpackc.a: - cd msgpack; ([ -f Makefile ] || (./bootstrap && ./configure)) - +make -C msgpack/src libmsgpackc.la - -.PHONY: libssh/build/src/libssh.a msgpack/src/.libs/libmsgpackc.a diff --git a/libssh/.gitignore b/libssh/.gitignore deleted file mode 100644 index c3c0e572..00000000 --- a/libssh/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.a -*.o -.* -*.swp -*~$ -build -cscope.* -tags diff --git a/libssh/AUTHORS b/libssh/AUTHORS deleted file mode 100644 index 51b3e46f..00000000 --- a/libssh/AUTHORS +++ /dev/null @@ -1,15 +0,0 @@ -Author(s): -Aris Adamantiadis (project initiator) - -Andreas Schneider (developer) - -Nick Zitzmann (mostly client SFTP stuff) - -Norbert Kiesel (getaddrinfo and other patches) - -Jean-Philippe Garcia Ballester (Port to libgcrypt and configure.in voodoo, debian packaging) - -Contributor(s): - -Laurent Bigonville (debian packaging) - diff --git a/libssh/BSD b/libssh/BSD deleted file mode 100644 index b8dba0d2..00000000 --- a/libssh/BSD +++ /dev/null @@ -1,24 +0,0 @@ -Some parts are under the BSDv2 License : - - -Copyright (c) 2000 Markus Friedl. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt deleted file mode 100644 index 48559f37..00000000 --- a/libssh/CMakeLists.txt +++ /dev/null @@ -1,151 +0,0 @@ -project(libssh C) - -# Required cmake version -cmake_minimum_required(VERSION 2.6.0) - -# global needed variables -set(APPLICATION_NAME ${PROJECT_NAME}) - -set(APPLICATION_VERSION_MAJOR "0") -set(APPLICATION_VERSION_MINOR "7") -set(APPLICATION_VERSION_PATCH "0") - -set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}") - -# SOVERSION scheme: CURRENT.AGE.REVISION -# If there was an incompatible interface change: -# Increment CURRENT. Set AGE and REVISION to 0 -# If there was a compatible interface change: -# Increment AGE. Set REVISION to 0 -# If the source code was changed, but there were no interface changes: -# Increment REVISION. -set(LIBRARY_VERSION "4.3.0") -set(LIBRARY_SOVERSION "4") - -# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked -set(CMAKE_MODULE_PATH - ${CMAKE_SOURCE_DIR}/cmake/Modules -) - -# add definitions -include(DefineCMakeDefaults) -include(DefinePlatformDefaults) -include(DefineCompilerFlags) -include(DefineInstallationPaths) -include(DefineOptions.cmake) -include(CPackConfig.cmake) - -# disallow in-source build -include(MacroEnsureOutOfSourceBuild) -macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source build. Please create a separate build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there.") - -# search for libraries -if (WITH_ZLIB) - find_package(ZLIB REQUIRED) -endif (WITH_ZLIB) - -if (WITH_GCRYPT) - find_package(GCrypt 1.5.0 REQUIRED) - if (NOT GCRYPT_FOUND) - message(FATAL_ERROR "Could not find GCrypt") - endif (NOT GCRYPT_FOUND) -else (WITH_GCRYPT) - find_package(OpenSSL) - if (NOT OPENSSL_FOUND) - find_package(GCrypt) - if (NOT GCRYPT_FOUND) - message(FATAL_ERROR "Could not find OpenSSL or GCrypt") - endif (NOT GCRYPT_FOUND) - endif (NOT OPENSSL_FOUND) -endif(WITH_GCRYPT) - -# Find out if we have threading available -set(CMAKE_THREAD_PREFER_PTHREADS ON) -find_package(Threads) - -if (WITH_GSSAPI) - find_package(GSSAPI) -endif (WITH_GSSAPI) - -if (WITH_NACL) - find_package(NaCl) - if (NOT NACL_FOUND) - set(WITH_NACL OFF) - endif (NOT NACL_FOUND) -endif (WITH_NACL) - -# config.h checks -include(ConfigureChecks.cmake) -configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) - -# check subdirectories -add_subdirectory(doc) -add_subdirectory(include) -add_subdirectory(src) - -# pkg-config file -configure_file(libssh.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc) -configure_file(libssh_threads.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libssh_threads.pc) -install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc - ${CMAKE_CURRENT_BINARY_DIR}/libssh_threads.pc - DESTINATION - ${LIB_INSTALL_DIR}/pkgconfig - COMPONENT - pkgconfig -) - -# cmake config files -set(LIBSSH_LIBRARY_NAME @CMAKE_SHARED_LIBRARY_PREFIX@ssh@CMAKE_SHARED_LIBRARY_SUFFIX@) -set(LIBSSH_THREADS_LIBRARY_NAME @CMAKE_SHARED_LIBRARY_PREFIX@ssh@CMAKE_SHARED_LIBRARY_SUFFIX@) - -configure_file(${PROJECT_NAME}-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake @ONLY) -configure_file(${PROJECT_NAME}-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake @ONLY) -install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake - DESTINATION - ${CMAKE_INSTALL_DIR}/${PROJECT_NAME} - COMPONENT - devel -) - - -# in tree build settings -configure_file(libssh-build-tree-settings.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-build-tree-settings.cmake @ONLY) - -if (WITH_EXAMPLES) - add_subdirectory(examples) -endif (WITH_EXAMPLES) - -if (WITH_TESTING) - find_package(CMocka REQUIRED) - include(AddCMockaTest) - add_subdirectory(tests) -endif (WITH_TESTING) - - -message(STATUS "********************************************") -message(STATUS "********** ${PROJECT_NAME} build options : **********") - -message(STATUS "zlib support: ${WITH_ZLIB}") -message(STATUS "libgcrypt support: ${WITH_GCRYPT}") -message(STATUS "libnacl support: ${WITH_NACL}") -message(STATUS "SSH-1 support: ${WITH_SSH1}") -message(STATUS "SFTP support: ${WITH_SFTP}") -message(STATUS "Server support : ${WITH_SERVER}") -message(STATUS "GSSAPI support : ${WITH_GSSAPI}") -message(STATUS "Pcap debugging support : ${WITH_PCAP}") -message(STATUS "With static library: ${WITH_STATIC_LIB}") -message(STATUS "Unit testing: ${WITH_TESTING}") -message(STATUS "Client code Unit testing: ${WITH_CLIENT_TESTING}") -if (WITH_INTERNAL_DOC) - message(STATUS "Internal documentation generation") -else (WITH_INTERNAL_DOC) - message(STATUS "Public API documentation generation") -endif (WITH_INTERNAL_DOC) -message(STATUS "Benchmarks: ${WITH_BENCHMARKS}") -message(STATUS "********************************************") - diff --git a/libssh/COPYING b/libssh/COPYING deleted file mode 100644 index cb7d9b73..00000000 --- a/libssh/COPYING +++ /dev/null @@ -1,460 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - Linking with OpenSSL -17. In addition, as a special exception, we give permission to link the code of its release of libssh with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU Lesser General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. - END OF TERMS AND CONDITIONS diff --git a/libssh/CPackConfig.cmake b/libssh/CPackConfig.cmake deleted file mode 100644 index 2c4c3b5b..00000000 --- a/libssh/CPackConfig.cmake +++ /dev/null @@ -1,53 +0,0 @@ -# For help take a look at: -# http://www.cmake.org/Wiki/CMake:CPackConfiguration - -### general settings -set(CPACK_PACKAGE_NAME ${APPLICATION_NAME}) -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The SSH library") -set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README") -set(CPACK_PACKAGE_VENDOR "The SSH Library Development Team") -set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING") - - -### versions -set(CPACK_PACKAGE_VERSION_MAJOR "0") -set(CPACK_PACKAGE_VERSION_MINOR "5") -set(CPACK_PACKAGE_VERSION_PATCH "90") -set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") - - -### source generator -set(CPACK_SOURCE_GENERATOR "TGZ") -set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build/;/obj/;tags;cscope.*") -set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") - -if (WIN32) - set(CPACK_GENERATOR "ZIP") - - ### nsis generator - find_package(NSIS) - if (NSIS_MAKE) - set(CPACK_GENERATOR "${CPACK_GENERATOR};NSIS") - set(CPACK_NSIS_DISPLAY_NAME "The SSH Library") - set(CPACK_NSIS_COMPRESSOR "/SOLID zlib") - set(CPACK_NSIS_MENU_LINKS "http://www.libssh.org/" "libssh homepage") - endif (NSIS_MAKE) -endif (WIN32) - -set(CPACK_PACKAGE_INSTALL_DIRECTORY "libssh") - -set(CPACK_PACKAGE_FILE_NAME ${APPLICATION_NAME}-${CPACK_PACKAGE_VERSION}) - -set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") -set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C/C++ Headers") -set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION - "Libraries used to build programs which use libssh") -set(CPACK_COMPONENT_HEADERS_DESCRIPTION - "C/C++ header files for use with libssh") -set(CPACK_COMPONENT_HEADERS_DEPENDS libraries) -#set(CPACK_COMPONENT_APPLICATIONS_GROUP "Runtime") -set(CPACK_COMPONENT_LIBRARIES_GROUP "Development") -set(CPACK_COMPONENT_HEADERS_GROUP "Development") - -include(CPack) diff --git a/libssh/CTestConfig.cmake b/libssh/CTestConfig.cmake deleted file mode 100644 index 20d2e8f5..00000000 --- a/libssh/CTestConfig.cmake +++ /dev/null @@ -1,9 +0,0 @@ -set(UPDATE_TYPE "true") - -set(CTEST_PROJECT_NAME "libssh") -set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") - -set(CTEST_DROP_METHOD "http") -set(CTEST_DROP_SITE "test.libssh.org") -set(CTEST_DROP_LOCATION "/submit.php?project=libssh") -set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/libssh/ChangeLog b/libssh/ChangeLog deleted file mode 100644 index 6f4d906c..00000000 --- a/libssh/ChangeLog +++ /dev/null @@ -1,360 +0,0 @@ -ChangeLog -========== - -version 0.6.0 (released 2013-XX-XX) - * Added new publicy key API. - * Added new userauth API. - * Added gssapi-mic userauth. - * Added new callback based server API. - * Added Elliptic Curve DSA (ECDSA) support (with OpenSSL). - * Added Elliptic Curve Diffie Hellman (ECDH) support. - * Added improved logging system. - * Added SSH-agent forwarding. - * Added key-reexchange. - * Improved documentation. - * Fixed timeout handling. - -version 0.5.5 (released 2013-07-26) - * BUG 103: Fix ProxyCommand parsing. - * Fix setting -D_FORTIFY_SOURCE=2. - * Fix pollset error return if emtpy. - * Fix NULL pointer checks in channel functions. - * Several bugfixes. - -version 0.5.4 (released 2013-01-22) - * CVE-2013-0176 - NULL dereference leads to denial of service - * Fixed several NULL pointer dereferences in SSHv1. - * Fixed a free crash bug in options parsing. - -version 0.5.3 (released 2012-11-20) - * CVE-2012-4559 Fixed multiple double free() flaws. - * CVE-2012-4560 Fixed multiple buffer overflow flaws. - * CVE-2012-4561 Fixed multiple invalid free() flaws. - * BUG #84 - Fix bug in sftp_mkdir not returning on error. - * BUG #85 - Fixed a possible channel infinite loop if the connection dropped. - * BUG #88 - Added missing channel request_state and set it to accepted. - * BUG #89 - Reset error state to no error on successful SSHv1 authentiction. - * Fixed a possible use after free in ssh_free(). - * Fixed multiple possible NULL pointer dereferences. - * Fixed multiple memory leaks in error paths. - * Fixed timeout handling. - * Fixed regression in pre-connected socket setting. - * Handle all unknown global messages. - -version 0.5.2 (released 2011-09-17) - * Increased window size x10. - * Fixed SSHv1. - * Fixed bugged lists. - * Fixed use-after-free + inconsistent callbacks call in poll. - * Fixed scp documentation. - * Fixed possible infinite loop in channel_read(). - * Fixed handling of short reads of sftp_async_read(). - * Fixed handling request service timeout in blocking mode. - * Fixed ssh_auth_list() documentation. - * Fixed incorrect return values in ssh_channel_write(). - * Fixed an infinite loop in the termination callback. - * Fixed handling of SSH_AGAIN in channel_open(). - * Fixed "status -5 inflating zlib packet" - -version 0.5.1 (released 2011-08-09) - * Added checks for NULL pointers in string.c. - * Set the channel max packet size to 32768. - * Don't (de)compress empty buffers. - * Fixed ssh_scp_write so it works when doing recursive copy. - * Fixed another source of endless wait. - * Fixed an endless loop in case of a channel_open error. - * Fixed session timeout handling. - * Fixed ssh_channel_from_local() loop. - * Fixed permissions of scp example when we copy a file. - * Workaround ssh_get_user_home_dir on LDAP users. - * Added pkg-config support for libssh_threads. - * Fixed compilation without server and sftp modes. - * Fix static .lib overwriting on Windows. - -version 0.5.0 (released 2011-06-01) - * Added ssh_ prefix to all functions. - * Added complete Windows support. - * Added improved server support. - * Added unit tests for a lot of functions. - * Added asynchronous service request. - * Added a multiplatform ssh_getpass() function. - * Added a tutorial. - * Added a lot of documentation. - * Fixed a lot of bugs. - * Fixed several memory leaks. - -version 0.4.8 (released 2011-01-15) - * Fixed memory leaks in session signing. - * Fixed memory leak in ssh_print_hexa. - * Fixed problem with ssh_connect w/ timeout and fd > 1024. - * Fixed some warnings on OS/2. - * Fixed installation path for OS/2. - -version 0.4.7 (released 2010-12-28) - * Fixed a possible memory leak in ssh_get_user_home(). - * Fixed a memory leak in sftp_xstat. - * Fixed uninitialized fd->revents member. - * Fixed timout value in ssh_channel_accept(). - * Fixed length checks in ssh_analyze_banner(). - * Fixed a possible data overread and crash bug. - * Fixed setting max_fd which breaks ssh_select(). - * Fixed some pedantic build warnings. - * Fixed a memory leak with session->bindaddr. - -version 0.4.6 (released 2010-09-03) - * Added a cleanup function to free the ws2_32 library. - * Fixed build with gcc 3.4. - * Fixed the Windows build on Vista and newer. - * Fixed the usage of WSAPoll() on Windows. - * Fixed "@deprecated" in doxygen - * Fixed some mingw warnings. - * Fixed handling of opened channels. - * Fixed keepalive problem on older openssh servers. - * Fixed testing for big endian on Windows. - * Fixed the Windows preprocessor macros and defines. - -version 0.4.5 (released 2010-07-13) - * Added option to bind a client to an ip address. - * Fixed the ssh socket polling function. - * Fixed Windows related bugs in bsd_poll(). - * Fixed serveral build warnings. - -version 0.4.4 (released 2010-06-01) - * Fixed a bug in the expand function for escape sequences. - * Fixed a bug in the tilde expand function. - * Fixed a bug in setting the options. - -version 0.4.3 (released 2010-05-18) - * Added global/keepalive responses. - * Added runtime detection of WSAPoll(). - * Added a select(2) based poll-emulation if poll(2) is not available. - * Added a function to expand an escaped string. - * Added a function to expand the tilde from a path. - * Added a proxycommand support. - * Added ssh_privatekey_type public function - * Added the possibility to define _OPENSSL_DIR and _ZLIB_DIR. - * Fixed sftp_chown. - * Fixed sftp_rename on protocol version 3. - * Fixed a blocking bug in channel_poll. - * Fixed config parsing wich has overwritten user specified values. - * Fixed hashed [host]:port format in knownhosts - * Fixed Windows build. - * Fixed doublefree happening after a negociation error. - * Fixed aes*-ctr with <= OpenSSL 0.9.7b. - * Fixed some documentation. - * Fixed exec example which has broken read usage. - * Fixed broken algorithm choice for server. - * Fixed a typo that we don't export all symbols. - * Removed the unneeded dependency to doxygen. - * Build examples only on the Linux plattform. - -version 0.4.2 (released 2010-03-15) - * Added owner and group information in sftp attributes. - * Added missing SSH_OPTIONS_FD option. - * Added printout of owner and group in the sftp example. - * Added a prepend function for ssh_list. - * Added send back replies to openssh's keepalives. - * Fixed documentation in scp code - * Fixed longname parsing, this only workings with readdir. - * Fixed and added support for several identity files. - * Fixed sftp_parse_longname() on Windows. - * Fixed a race condition bug in ssh_scp_close() - * Remove config support for SSHv1 Cipher variable. - * Rename ssh_list_add to ssh_list_append. - * Rename ssh_list_get_head to ssh_list_pop_head - -version 0.4.1 (released 2010-02-13) - * Added support for aes128-ctr, aes192-ctr and aes256-ctr encryption. - * Added an example for exec. - * Added private key type detection feature in privatekey_from_file(). - * Fixed zlib compression fallback. - * Fixed kex bug that client preference should be prioritary - * Fixed known_hosts file set by the user. - * Fixed a memleak in channel_accept(). - * Fixed underflow when leave_function() are unbalanced - * Fixed memory corruption in handle_channel_request_open(). - * Fixed closing of a file handle case of errors in privatekey_from_file(). - * Fixed ssh_get_user_home_dir() to be thread safe. - * Fixed the doxygen documentation. - -version 0.4.0 (released 2009-12-10) - * Added scp support. - * Added support for sending signals (RFC 4254, section 6.9). - * Added MSVC support. - * Added support for ~/.ssh/config. - * Added sftp extension support. - * Added X11 forwarding support for client. - * Added forward listening. - * Added support for openssh extensions (statvfs, fstatvfs). - * Added a cleaned up interface for setting options. - * Added a generic way to handle sockets asynchronously. - * Added logging of the sftp flags used to open a file. - * Added full poll() support and poll-emulation for win32. - * Added missing 64bit functions in sftp. - * Added support for ~/ and SSH_DIR/ in filenames instead of %s/. - * Fixed Fix channel_get_exit_status bug. - * Fixed calltrace logging to make it optional. - * Fixed compilation on Solaris. - * Fixed resolving of ip addresses. - * Fixed libssh compilation without server support. - * Fixed possible memory corruptions (ticket #14). - -version 0.3.4 (released 2009-09-14) - * Added ssh_basename and ssh_dirname. - * Added a portable ssh_mkdir function. - * Added a sftp_tell64() function. - * Added missing NULL pointer checks to crypt_set_algorithms_server. - * Fixed ssh_write_knownhost if ~/.ssh doesn't exist. - * Fixed a possible integer overflow in buffer_get_data(). - * Fixed possible security bug in packet_decrypt(). - * Fixed a possible stack overflow in agent code. - -version 0.3.3 (released 2009-08-18) - * Fixed double free pointer crash in dsa_public_to_string. - * Fixed channel_get_exit_status bug. - * Fixed ssh_finalize which didn't clear the flag. - * Fixed memory leak introduced by previous bugfix. - * Fixed channel_poll broken when delayed EOF recvd. - * Fixed stupid "can't parse known host key" bug. - * Fixed possible memory corruption (ticket #14). - -version 0.3.2 (released 2009-08-05) - * Added ssh_init() function. - * Added sftp_readlink() function. - * Added sftp_symlink() function. - * Fixed ssh_write_knownhost(). - * Fixed compilation on Solaris. - * Fixed SSHv1 compilation. - -version 0.3.1 (released 2009-07-14) - * Added return code SSH_SERVER_FILE_NOT_FOUND. - * Fixed compilation of SSHv1. - * Fixed several memory leaks. - * Fixed possible infinite loops. - * Fixed a possible crash bug. - * Fixed build warnings. - * Fixed cmake on BSD. -version 0.3.1 (released 2009-07-14) - * Added return code SSH_SERVER_FILE_NOT_FOUND. - * Fixed compilation of SSHv1. - * Fixed several memory leaks. - * Fixed possible infinite loops. - * Fixed a possible crash bug. - * Fixed build warnings. - * Fixed cmake on BSD. - -version 0.3 (released 2009-05-21) - * Added support for ssh-agent authentication. - * Added POSIX like sftp implementation. - * Added error checking to all functions. - * Added const to arguments where it was needed. - * Added a channel_get_exit_status() function. - * Added a channel_read_buffer() function, channel_read() is now - a POSIX like function. - * Added a more generic auth callback function. - * Added printf attribute checking for log and error functions. - * Added runtime function tracer support. - * Added NSIS build support with CPack. - * Added openssh hashed host support. - * Added API documentation for all public functions. - * Added asynchronous SFTP read function. - * Added a ssh_bind_set_fd() function. - * Fixed known_hosts parsing. - * Fixed a lot of build warnings. - * Fixed the Windows build. - * Fixed a lot of memory leaks. - * Fixed a double free corruption in the server support. - * Fixed the "ssh_accept:" bug in server support. - * Fixed important channel bugs. - * Refactored the socket handling. - * Switched to CMake build system. - * Improved performance. - -version 0.2 (released 2007-11-29) - * General cleanup - * More comprehensive API - * Up-to-date Doxygen documentation of each public function - * Basic server-based support - * Libgcrypt support (alternative to openssl and its license) - * SSH1 support (disabled by default) - * Added 3des-cbc - * A lot of bugfixes - -version 0.11-dev - * Server implementation development. - * Small bug corrected when connecting to sun ssh servers. - * Channel wierdness corrected (writing huge data packets) - * Channel_read_nonblocking added - * Channel bug where stderr wasn't correctly read fixed. - * Added sftp_file_set_nonblocking(), which is nonblocking SFTP IO - * Connect_status callback. - * Priv.h contains the internal functions, libssh.h the public interface - * Options_set_timeout (thx marcelo) really working. - * Tcp tunneling through channel_open_forward. - * Channel_request_exec() - * Channel_request_env() - * Ssh_get_pubkey_hash() - * Ssh_is_server_known() - * Ssh_write_known_host() - * Options_set_ssh_dir - * How could this happen ! there weren't any channel_close ! - * Nasty channel_free bug resolved. - * Removed the unsigned long all around the code. use only u8,u32 & u64. - * It now compiles and runs under amd64 ! - * Channel_request_pty_size - * Channel_change_pty_size - * Options_copy() - * Ported the doc to an HTML file. - * Small bugfix in packet.c - * Prefixed error constants with SSH_ - * Sftp_stat, sftp_lstat, sftp_fstat. thanks Michel Bardiaux for the patch. - * Again channel number mismatch fixed. - * Fixed a bug in ssh_select making the select fail when a signal has been - caught. - * Keyboard-interactive authentication working. - -version 0.1 (released 2004-03-05) - * Begining of sftp subsystem implementation. - * Some cleanup into channels implementation - * Now every channel functions is called by its CHANNEL handler. - * Added channel_poll() and channel_read(). - * Changed the client so it uses the new channel_poll and channel_read interface - * Small use-after-free bug with channels resolved - * Changed stupidities in lot of function names. - * Removed a debug output file opened by default. - * Added API.txt, the libssh programmer handbook. - * Various bug fixes from Nick Zitzmann. - * Developed a cryptographic structure for handling protocols. - * An autoconf script which took me half of a day to set up. - * A ssh_select wrapper has been written. - -version 0.0.4 (released 2003-10-10) - * Some terminal code (eof handling) added - * Channels bugfix (it still needs some tweaking though) - * Zlib support - * Added a wrapper.c file. The goal is to provide a similar API to every - cryptographic functions. bignums and sha/md5 are wrapped now. - * More work than it first looks. - * Support for other crypto libs planed (lighter libs) - * Fixed stupid select() bug. - * Libssh now compiles and links with openssl 0.9.6 - * RSA pubkey authentication code now works ! - -version 0.0.3 (released 2003-09-15) - * Added install target in makefile - * Some cleanup in headers files and source code - * Change default banner and project name to libssh. - * New file auth.c to support more and more authentication ways - * Bugfix(read offbyone) in send_kex - * A base64 parser. don't read the source, it's awful. pure 0xbadc0de. - * Changed the client filename to "ssh". logic isn't it ? - * Dss publickey authentication ! still need to wait for the rsa one - * Bugfix in packet.c - * New misc.c contains misc functions - -version 0.0.2 (released 2003-09-03) - * Initial release. - * Client supports both ssh and dss hostkey verification, but doesn't compare them to openssh's files. (~/.ssh/known_hosts) - * The only supported authentication method is password. - * Compiles on linux and openbsd. freebsd and netbsd should work, too - * Lot of work which hasn't been discussed here. diff --git a/libssh/CodingStyle b/libssh/CodingStyle deleted file mode 100644 index 93cc382c..00000000 --- a/libssh/CodingStyle +++ /dev/null @@ -1,59 +0,0 @@ -Coding Style Conventions -======================== - -Coding style guidelines are about reducing the number of unnecessary -reformatting patches and making things easier for developers to work together. - -You don't have to like them or even agree with them, but once put in place we -all have to abide by them (or vote to change them). However, coding style -should never outweigh coding itself and so the guidelines described here are -hopefully easy enough to follow as they are very common and supported by tools -and editors. - -The basic style for C code is the Linux kernel coding style [1] with one -excecption, we use 4 spaces instead of tabs. This closely matches what most -libssh developers use already anyways, with a few exceptions as mentioned -below. - -To shorthen this here are the highlights: - -* Maximum line width is 80 characters - - The reason is not about people with low-res screens but rather sticking - to 80 columns prevents you from easily nesting more than one level of - if statements or other code blocks. - -* Use 4 spaces to indent - -* No trailing whitespaces - -* Follow the K&R guidelines. We won't go through all of them here. Do you - have a copy of "The C Programming Language" anyways right? - -Editors -======== - -VIM ----- - -set ts=4 sw=4 et cindent - -For Vim, the following settings in $HOME/.vimrc will also deal with -displaying trailing whitespace: - - if has("syntax") && (&t_Co > 2 || has("gui_running")) - syntax on - function! ActivateInvisibleCharIndicator() - syntax match TrailingSpace "[ \t]\+$" display containedin=ALL - highlight TrailingSpace ctermbg=Red - endf - autocmd BufNewFile,BufRead * call ActivateInvisibleCharIndicator() - endif - " Show tabs, trailing whitespace, and continued lines visually - set list listchars=tab:»·,trail:·,extends:… - - " highlight overly long lines same as TODOs. - set textwidth=80 - autocmd BufNewFile,BufRead *.c,*.h exec 'match Todo /\%>' . &textwidth . 'v.\+/' - -[1] https://www.kernel.org/doc/Documentation/CodingStyle diff --git a/libssh/ConfigureChecks.cmake b/libssh/ConfigureChecks.cmake deleted file mode 100644 index befb4290..00000000 --- a/libssh/ConfigureChecks.cmake +++ /dev/null @@ -1,229 +0,0 @@ -include(CheckIncludeFile) -include(CheckSymbolExists) -include(CheckFunctionExists) -include(CheckLibraryExists) -include(CheckTypeSize) -include(CheckCXXSourceCompiles) -include(TestBigEndian) - -set(PACKAGE ${APPLICATION_NAME}) -set(VERSION ${APPLICATION_VERSION}) -set(DATADIR ${DATA_INSTALL_DIR}) -set(LIBDIR ${LIB_INSTALL_DIR}) -set(PLUGINDIR "${PLUGIN_INSTALL_DIR}-${LIBRARY_SOVERSION}") -set(SYSCONFDIR ${SYSCONF_INSTALL_DIR}) - -set(BINARYDIR ${CMAKE_BINARY_DIR}) -set(SOURCEDIR ${CMAKE_SOURCE_DIR}) - -function(COMPILER_DUMPVERSION _OUTPUT_VERSION) - # Remove whitespaces from the argument. - # This is needed for CC="ccache gcc" cmake .. - string(REPLACE " " "" _C_COMPILER_ARG "${CMAKE_C_COMPILER_ARG1}") - - execute_process( - COMMAND - ${CMAKE_C_COMPILER} ${_C_COMPILER_ARG} -dumpversion - OUTPUT_VARIABLE _COMPILER_VERSION - ) - - string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" - _COMPILER_VERSION "${_COMPILER_VERSION}") - - set(${_OUTPUT_VERSION} ${_COMPILER_VERSION} PARENT_SCOPE) -endfunction() - -if(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) - compiler_dumpversion(GNUCC_VERSION) - if (NOT GNUCC_VERSION EQUAL 34) - set(CMAKE_REQUIRED_FLAGS "-fvisibility=hidden") - check_c_source_compiles( -"void __attribute__((visibility(\"default\"))) test() {} -int main(void){ return 0; } -" WITH_VISIBILITY_HIDDEN) - set(CMAKE_REQUIRED_FLAGS "") - endif (NOT GNUCC_VERSION EQUAL 34) -endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2) - -# HEADER FILES -check_include_file(argp.h HAVE_ARGP_H) -check_include_file(pty.h HAVE_PTY_H) -check_include_file(utmp.h HAVE_UTMP_H) -check_include_file(termios.h HAVE_TERMIOS_H) -check_include_file(unistd.h HAVE_UNISTD_H) -check_include_file(util.h HAVE_UTIL_H) -check_include_file(libutil.h HAVE_LIBUTIL_H) - -if (WIN32) - check_include_files("winsock2.h;ws2tcpip.h;wspiapi.h" HAVE_WSPIAPI_H) - if (NOT HAVE_WSPIAPI_H) - message(STATUS "WARNING: Without wspiapi.h, this build will only work on Windows XP and newer versions") - endif (NOT HAVE_WSPIAPI_H) - check_include_files("winsock2.h;ws2tcpip.h" HAVE_WS2TCPIP_H) -endif (WIN32) - -set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) -check_include_file(openssl/aes.h HAVE_OPENSSL_AES_H) - -set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) -check_include_file(openssl/blowfish.h HAVE_OPENSSL_BLOWFISH_H) - -set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) -check_include_file(openssl/des.h HAVE_OPENSSL_DES_H) - -set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) -check_include_file(openssl/ecdh.h HAVE_OPENSSL_ECDH_H) - -set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) -check_include_file(openssl/ec.h HAVE_OPENSSL_EC_H) - -set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) -check_include_file(openssl/ecdsa.h HAVE_OPENSSL_ECDSA_H) - -if (CMAKE_HAVE_PTHREAD_H) - set(HAVE_PTHREAD_H 1) -endif (CMAKE_HAVE_PTHREAD_H) - -if (NOT WITH_GCRYPT) - if (HAVE_OPENSSL_EC_H AND HAVE_OPENSSL_ECDSA_H) - set(HAVE_OPENSSL_ECC 1) - endif (HAVE_OPENSSL_EC_H AND HAVE_OPENSSL_ECDSA_H) - - if (HAVE_OPENSSL_ECC) - set(HAVE_ECC 1) - endif (HAVE_OPENSSL_ECC) -endif (NOT WITH_GCRYPT) - -# FUNCTIONS - -check_function_exists(isblank HAVE_ISBLANK) -check_function_exists(strncpy HAVE_STRNCPY) -check_function_exists(vsnprintf HAVE_VSNPRINTF) -check_function_exists(snprintf HAVE_SNPRINTF) -check_function_exists(poll HAVE_POLL) -check_function_exists(select HAVE_SELECT) -check_function_exists(getaddrinfo HAVE_GETADDRINFO) - -check_symbol_exists(ntohll sys/types.h HAVE_NTOHLL) -check_symbol_exists(htonll sys/types.h HAVE_HTONLL) - -if (NOT HAVE_NTOHLL) - check_function_exists(ntohll HAVE_NTOHLL) -endif (NOT HAVE_NTOHLL) -if (NOT HAVE_HTONLL) - check_function_exists(ntohll HAVE_HTONLL) -endif (NOT HAVE_HTONLL) - - -if (WIN32) - check_function_exists(_strtoui64 HAVE__STRTOUI64) - - check_function_exists(_vsnprintf_s HAVE__VSNPRINTF_S) - check_function_exists(_vsnprintf HAVE__VSNPRINTF) - check_function_exists(_snprintf HAVE__SNPRINTF) - check_function_exists(_snprintf_s HAVE__SNPRINTF_S) - - if (HAVE_WSPIAPI_H OR HAVE_WS2TCPIP_H) - set(HAVE_GETADDRINFO TRUE) - set(HAVE_GETHOSTBYNAME TRUE) - if (MSVC) - set(HAVE_NTOHLL TRUE) - set(HAVE_HTONLL TRUE) - endif (MSVC) - endif (HAVE_WSPIAPI_H OR HAVE_WS2TCPIP_H) - - set(HAVE_SELECT TRUE) -endif (WIN32) - -if (UNIX) - if (NOT LINUX) - # libsocket (Solaris) - check_library_exists(socket getaddrinfo "" HAVE_LIBSOCKET) - if (HAVE_LIBSOCKET) - set(HAVE_GETADDRINFO TRUE) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} socket) - endif (HAVE_LIBSOCKET) - - # libnsl/inet_pton (Solaris) - check_library_exists(nsl inet_pton "" HAVE_LIBNSL) - if (HAVE_LIBNSL) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} nsl) - endif (HAVE_LIBNSL) - - # librt - check_library_exists(rt nanosleep "" HAVE_LIBRT) - endif (NOT LINUX) - - check_library_exists(rt clock_gettime "" HAVE_CLOCK_GETTIME) - if (HAVE_LIBRT OR HAVE_CLOCK_GETTIME) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} rt) - endif (HAVE_LIBRT OR HAVE_CLOCK_GETTIME) - - check_library_exists(util forkpty "" HAVE_LIBUTIL) - check_function_exists(cfmakeraw HAVE_CFMAKERAW) - check_function_exists(strtoull HAVE_STRTOULL) - check_function_exists(__strtoull HAVE___STRTOULL) -endif (UNIX) - -set(LIBSSH_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CACHE INTERNAL "libssh required system libraries") - -# LIBRARIES -if (OPENSSL_FOUND) - set(HAVE_LIBCRYPTO 1) -endif (OPENSSL_FOUND) - -if (GCRYPT_FOUND) - set(HAVE_LIBGCRYPT 1) - if (GCRYPT_VERSION VERSION_GREATER "1.4.6") - #set(HAVE_GCRYPT_ECC 1) - #set(HAVE_ECC 1) - endif (GCRYPT_VERSION VERSION_GREATER "1.4.6") -endif (GCRYPT_FOUND) - -if (CMAKE_USE_PTHREADS_INIT) - set(HAVE_PTHREAD 1) -endif (CMAKE_USE_PTHREADS_INIT) - -# OPTIONS -check_c_source_compiles(" -__thread int tls; - -int main(void) { - return 0; -}" HAVE_GCC_THREAD_LOCAL_STORAGE) - -check_c_source_compiles(" -__declspec(thread) int tls; - -int main(void) { - return 0; -}" HAVE_MSC_THREAD_LOCAL_STORAGE) - -check_c_source_compiles(" -#include - -int main(void) -{ - char buf[] = \"This is some content\"; - - memset(buf, '\\\\0', sizeof(buf)); __asm__ volatile(\"\" : : \"r\"(&buf) : \"memory\"); - - return 0; -}" HAVE_GCC_VOLATILE_MEMORY_PROTECTION) - -if (WITH_DEBUG_CRYPTO) - set(DEBUG_CRYPTO 1) -endif (WITH_DEBUG_CRYPTO) - -if (WITH_DEBUG_CALLTRACE) - set(DEBUG_CALLTRACE 1) -endif (WITH_DEBUG_CALLTRACE) - -if (WITH_GSSAPI AND NOT GSSAPI_FOUND) - set(WITH_GSSAPI 0) -endif (WITH_GSSAPI AND NOT GSSAPI_FOUND) - -# ENDIAN -if (NOT WIN32) - test_big_endian(WORDS_BIGENDIAN) -endif (NOT WIN32) diff --git a/libssh/DefineOptions.cmake b/libssh/DefineOptions.cmake deleted file mode 100644 index ab7819a5..00000000 --- a/libssh/DefineOptions.cmake +++ /dev/null @@ -1,33 +0,0 @@ -option(WITH_GSSAPI "Build with GSSAPI support" ON) -option(WITH_ZLIB "Build with ZLIB support" ON) -option(WITH_SSH1 "Build with SSH1 support" OFF) -option(WITH_SFTP "Build with SFTP support" ON) -option(WITH_SERVER "Build with SSH server support" ON) -option(WITH_STATIC_LIB "Build with a static library" OFF) -option(WITH_DEBUG_CRYPTO "Build with cryto debug output" OFF) -option(WITH_DEBUG_CALLTRACE "Build with calltrace debug output" ON) -option(WITH_GCRYPT "Compile against libgcrypt" OFF) -option(WITH_PCAP "Compile with Pcap generation support" ON) -option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF) -option(WITH_TESTING "Build with unit tests" OFF) -option(WITH_CLIENT_TESTING "Build with client tests; requires a running sshd" OFF) -option(WITH_BENCHMARKS "Build benchmarks tools" OFF) -option(WITH_EXAMPLES "Build examples" ON) -option(WITH_NACL "Build with libnacl (curve25519" ON) -if (WITH_ZLIB) - set(WITH_LIBZ ON) -else (WITH_ZLIB) - set(WITH_LIBZ OFF) -endif (WITH_ZLIB) - -if(WITH_BENCHMARKS) - set(WITH_TESTING ON) -endif(WITH_BENCHMARKS) - -if (WITH_TESTING) - set(WITH_STATIC_LIB ON) -endif (WITH_TESTING) - -if (WITH_NACL) - set(WITH_NACL ON) -endif (WITH_NACL) \ No newline at end of file diff --git a/libssh/INSTALL b/libssh/INSTALL deleted file mode 100644 index 25960367..00000000 --- a/libssh/INSTALL +++ /dev/null @@ -1,101 +0,0 @@ -# How to build from source - -## Requirements - -### Common requirements - -In order to build libssh, you need to install several components: - -- A C compiler -- [CMake](http://www.cmake.org) >= 2.6.0. -- [openssl](http://www.openssl.org) >= 0.9.8 -or -- [gcrypt](http://www.gnu.org/directory/Security/libgcrypt.html) >= 1.4 - -optional: -- [libz](http://www.zlib.net) >= 1.2 - -Note that these version numbers are version we know works correctly. If you -build and run libssh successfully with an older version, please let us know. - -Windows binaries known to be working: - -- http://www.slproweb.com/products/Win32OpenSSL.html -- http://zlib.net/ -> zlib compiled DLL - -We installed them in C:\Program Files - -## Building -First, you need to configure the compilation, using CMake. Go inside the -`build` dir. Create it if it doesn't exist. - -GNU/Linux, MacOS X, MSYS/MinGW: - - cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug .. - make - -On Windows you should choose a makefile gernerator with -G or use - - cmake-gui.exe .. - -### CMake standard options -Here is a list of the most interesting options provided out of the box by -CMake. - -- CMAKE_BUILD_TYPE: The type of build (can be Debug Release MinSizeRel - RelWithDebInfo) -- CMAKE_INSTALL_PREFIX: The prefix to use when running make install (Default - to /usr/local on GNU/Linux and MacOS X) -- CMAKE_C_COMPILER: The path to the C compiler -- CMAKE_CXX_COMPILER: The path to the C++ compiler - -### CMake options defined for libssh - -Options are defined in the following files: - -- DefineOptions.cmake - -They can be changed with the -D option: - -`cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DWITH_ZLIB=OFF ..` - -### Browsing/editing CMake options - -In addition to passing options on the command line, you can browse and edit -CMake options using `cmakesetup` (Windows), `cmake-gui` or `ccmake` (GNU/Linux -and MacOS X). - -- Go to the build dir -- On Windows: run `cmakesetup` -- On GNU/Linux and MacOS X: run `ccmake ..` - -### Useful Windows options: - -If you have installed OpenSSL or ZLIB in non standard directories, maybe you -want to set: - -OPENSSL_ROOT_DIR - -and - -ZLIB_ROOT_DIR - -## Installing - -If you want to install libssh after compilation run: - - make install - -## Running - -The libssh binary can be found in the `build/src` directory. -You can use `build/examples/samplessh` which is a sample client to -test libssh on UNIX. - -## About this document - -This document is written using [Markdown][] syntax, making it possible to -provide usable information in both plain text and HTML format. Whenever -modifying this document please use [Markdown][] syntax. - -[markdown]: http://www.daringfireball.net/projects/markdown diff --git a/libssh/README b/libssh/README deleted file mode 100644 index e3170ce3..00000000 --- a/libssh/README +++ /dev/null @@ -1,163 +0,0 @@ - _ _ _ _ - (_) (_) (_) (_) - (_) _ (_) _ _ _ _ _ (_) _ - (_) (_) (_)(_) _ (_)(_) (_)(_) (_)(_) _ - (_) (_) (_) (_) _ (_) _ (_) (_) (_) - (_) (_) (_)(_)(_) (_)(_) (_)(_) (_) (_).org - - The SSH library -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -1* Why ? --_-_-_-_-_ - -Why not ? :) I've began to work on my own implementation of the ssh protocol -because i didn't like the currently public ones. -Not any allowed you to import and use the functions as a powerful library, -and so i worked on a library-based SSH implementation which was non-existing -in the free and open source software world. - - -2* How/Who ? --_-_-_-_-_-_-_ - -If you downloaded this file, you must know what it is : a library for -accessing ssh client services through C libraries calls in a simple manner. -Everybody can use this software under the terms of the LGPL - see the COPYING -file - -If you ask yourself how to compile libssh, please read INSTALL before anything. - -3* Where ? --_-_-_-_-_-_ - -http://www.libssh.org - -4* API Changes ! --_-_-_-_-_-_-_-_-_ - -Changes between 0.4 and 0.5 ---------------------------- - -We use the ssh_ prefix as namespace for every function now. There is a legacy.h -which could be used to get the old function names. - -Changes between 0.3 and 0.4 ---------------------------- - -We changed libssh to be typesafe now: - -SSH_SESSION *session -> ssh_session session -SFTP_SESSION *sftp -> sftp_session sftp -CHANNEL *channel -> ssh_channel channel -STRING *string -> ssh_string string -... - -The options structure has been removed and there is a new function. This -function can set all available options now. You can find the enum in the -header file and it is documented. Example: - -ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - -5* Copyright policy --_-_-_-_-_-_-_-_-_-_ - -libssh is a project with distributed copyright ownership, which means we prefer -the copyright on parts of libssh to be held by individuals rather than -corporations if possible. There are historical legal reasons for this, but one -of the best ways to explain it is that it’s much easier to work with -individuals who have ownership than corporate legal departments if we ever need -to make reasonable compromises with people using and working with libssh. - -We track the ownership of every part of libssh via git, our source code control -system, so we know the provenance of every piece of code that is committed to -libssh. - -So if possible, if you’re doing libssh changes on behalf of a company who -normally owns all the work you do please get them to assign personal copyright -ownership of your changes to you as an individual, that makes things very easy -for us to work with and avoids bringing corporate legal departments into the -picture. - -If you can’t do this we can still accept patches from you owned by your -employer under a standard employment contract with corporate copyright -ownership. It just requires a simple set-up process first. - -We use a process very similar to the way things are done in the Linux Kernel -community, so it should be very easy to get a sign off from your corporate -legal department. The only changes we’ve made are to accommodate the license we -use, which is LGPLv2 (or later) whereas the Linux kernel uses GPLv2. - -The process is called signing. - -How to sign your work ----------------------- - -Once you have permission to contribute to libssh from your employer, simply -email a copy of the following text from your corporate email address to: - -contributing@libssh.org - --------------------------------------------------------------------------- -libssh Developer's Certificate of Origin. Version 1.0 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the appropriate - version of the GNU General Public License; or - -(b) The contribution is based upon previous work that, to the best of - my knowledge, is covered under an appropriate open source license - and I have the right under that license to submit that work with - modifications, whether created in whole or in part by me, under - the GNU General Public License, in the appropriate version; or - -(c) The contribution was provided directly to me by some other - person who certified (a) or (b) and I have not modified it. - -(d) I understand and agree that this project and the contribution are - public and that a record of the contribution (including all - metadata and personal information I submit with it, including my - sign-off) is maintained indefinitely and may be redistributed - consistent with the libssh Team's policies and the requirements of - the GNU GPL where they are relevant. - -(e) I am granting this work to this project under the terms of the - GNU Lesser General Public License as published by the - Free Software Foundation; either version 2.1 of - the License, or (at the option of the project) any later version. - -http://www.gnu.org/licenses/lgpl-2.1.html --------------------------------------------------------------------------- - -We will maintain a copy of that email as a record that you have the rights to -contribute code to libssh under the required licenses whilst working for the -company where the email came from. - -Then when sending in a patch via the normal mechanisms described above, add a -line that states: - - - Signed-off-by: Random J Developer - - -using your real name and the email address you sent the original email you used -to send the libssh Developer’s Certificate of Origin to us (sorry, no -pseudonyms or anonymous contributions.) - -That’s it! Such code can then quite happily contain changes that have copyright -messages such as: - - - (c) Example Corporation. - - -and can be merged into the libssh codebase in the same way as patches from any -other individual. You don’t need to send in a copy of the libssh Developer’s -Certificate of Origin for each patch, or inside each patch. Just the sign-off -message is all that is required once we’ve received the initial email. - -Have fun and happy libssh hacking! - -The libssh Team diff --git a/libssh/SubmittingPatches b/libssh/SubmittingPatches deleted file mode 100644 index 66b54e76..00000000 --- a/libssh/SubmittingPatches +++ /dev/null @@ -1,118 +0,0 @@ -How to contribute a patch to libssh -==================================== - -Simple, just make the code change, and email it as either a "diff -u" -change, or as a "git format-patch" change against the original source -code to libssh@libssh.org, or attach it to a bug report at -https://red.libssh.org/ - -For larger code changes, breaking the changes up into a set of simple -patches, each of which does a single thing, are much easier to review. -Patch sets like that will most likely have an easier time being merged -into the libssh code than large single patches that make lots of -changes in one large diff. - -Ownership of the contributed code -================================== - -libssh is a project with distributed copyright ownership, which means -we prefer the copyright on parts of libssh to be held by individuals -rather than corporations if possible. There are historical legal -reasons for this, but one of the best ways to explain it is that it's -much easier to work with individuals who have ownership than corporate -legal departments if we ever need to make reasonable compromises with -people using and working with libssh. - -We track the ownership of every part of libssh via http://git.libssh.org, -our source code control system, so we know the provenance of every piece -of code that is committed to libssh. - -So if possible, if you're doing libssh changes on behalf of a company -who normally owns all the work you do please get them to assign -personal copyright ownership of your changes to you as an individual, -that makes things very easy for us to work with and avoids bringing -corporate legal departments into the picture. - -If you can't do this we can still accept patches from you owned by -your employer under a standard employment contract with corporate -copyright ownership. It just requires a simple set-up process first. - -We use a process very similar to the way things are done in the Linux -Kernel community, so it should be very easy to get a sign off from -your corporate legal department. The only changes we've made are to -accommodate the license we use, which is LGPLv2 (or later) whereas the -Linux kernel uses GPLv2. - -The process is called signing. - -How to sign your work ----------------------- - -Once you have permission to contribute to libssh from your employer, simply -email a copy of the following text from your corporate email address to: - -contributing@libssh.org - - - -libssh Developer's Certificate of Origin. Version 1.0 - - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the appropriate - version of the GNU General Public License; or - -(b) The contribution is based upon previous work that, to the best of - my knowledge, is covered under an appropriate open source license - and I have the right under that license to submit that work with - modifications, whether created in whole or in part by me, under - the GNU General Public License, in the appropriate version; or - -(c) The contribution was provided directly to me by some other - person who certified (a) or (b) and I have not modified it. - -(d) I understand and agree that this project and the contribution are - public and that a record of the contribution (including all - metadata and personal information I submit with it, including my - sign-off) is maintained indefinitely and may be redistributed - consistent with the libssh Team's policies and the requirements of - the GNU GPL where they are relevant. - -(e) I am granting this work to this project under the terms of the - GNU Lesser General Public License as published by the - Free Software Foundation; either version 2.1 of - the License, or (at the option of the project) any later version. - - http://www.gnu.org/licenses/lgpl-2.1.html - - -We will maintain a copy of that email as a record that you have the -rights to contribute code to libssh under the required licenses whilst -working for the company where the email came from. - -Then when sending in a patch via the normal mechanisms described -above, add a line that states: - - Signed-off-by: Random J Developer - -using your real name and the email address you sent the original email -you used to send the libssh Developer's Certificate of Origin to us -(sorry, no pseudonyms or anonymous contributions.) - -That's it! Such code can then quite happily contain changes that have -copyright messages such as: - - (c) Example Corporation. - -and can be merged into the libssh codebase in the same way as patches -from any other individual. You don't need to send in a copy of the -libssh Developer's Certificate of Origin for each patch, or inside each -patch. Just the sign-off message is all that is required once we've -received the initial email. - -Have fun and happy libssh hacking ! - -The libssh Team - diff --git a/libssh/build/.gitkeep b/libssh/build/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/libssh/cmake/Modules/AddCMockaTest.cmake b/libssh/cmake/Modules/AddCMockaTest.cmake deleted file mode 100644 index 19eff622..00000000 --- a/libssh/cmake/Modules/AddCMockaTest.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# - ADD_CHECK_TEST(test_name test_source linklib1 ... linklibN) - -# Copyright (c) 2007 Daniel Gollub -# Copyright (c) 2007-2010 Andreas Schneider -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -enable_testing() -include(CTest) - -if(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW) - set(CMAKE_C_FLAGS_PROFILING "-g -O0 -Wall -W -Wshadow -Wunused-variable -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers -Wwrite-strings -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Compiler Flags") - set(CMAKE_SHARED_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") - set(CMAKE_MODULE_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") - set(CMAKE_EXEC_LINKER_FLAGS_PROFILING " -fprofile-arcs -ftest-coverage" CACHE STRING "Profiling Linker Flags") -endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW) - -function (ADD_CMOCKA_TEST _testName _testSource) - add_executable(${_testName} ${_testSource}) - target_link_libraries(${_testName} ${ARGN}) - add_test(${_testName} ${CMAKE_CURRENT_BINARY_DIR}/${_testName}) -endfunction (ADD_CMOCKA_TEST) diff --git a/libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS b/libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS deleted file mode 100644 index 4b417765..00000000 --- a/libssh/cmake/Modules/COPYING-CMAKE-SCRIPTS +++ /dev/null @@ -1,22 +0,0 @@ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libssh/cmake/Modules/CheckCCompilerFlagSSP.cmake b/libssh/cmake/Modules/CheckCCompilerFlagSSP.cmake deleted file mode 100644 index 2fe43954..00000000 --- a/libssh/cmake/Modules/CheckCCompilerFlagSSP.cmake +++ /dev/null @@ -1,26 +0,0 @@ -# - Check whether the C compiler supports a given flag in the -# context of a stack checking compiler option. - -# CHECK_C_COMPILER_FLAG_SSP(FLAG VARIABLE) -# -# FLAG - the compiler flag -# VARIABLE - variable to store the result -# -# This actually calls check_c_source_compiles. -# See help for CheckCSourceCompiles for a listing of variables -# that can modify the build. - -# Copyright (c) 2006, Alexander Neundorf, -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - - -include(CheckCSourceCompiles) - -function(CHECK_C_COMPILER_FLAG_SSP _FLAG _RESULT) - set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") - set(CMAKE_REQUIRED_DEFINITIONS "${_FLAG}") - check_c_source_compiles("int main(int argc, char **argv) { char buffer[256]; return buffer[argc]=0;}" ${_RESULT}) - set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") -endfunction(CHECK_C_COMPILER_FLAG_SSP) diff --git a/libssh/cmake/Modules/DefineCMakeDefaults.cmake b/libssh/cmake/Modules/DefineCMakeDefaults.cmake deleted file mode 100644 index 22eda6fa..00000000 --- a/libssh/cmake/Modules/DefineCMakeDefaults.cmake +++ /dev/null @@ -1,30 +0,0 @@ -# Always include srcdir and builddir in include path -# This saves typing ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY} in -# about every subdir -# since cmake 2.4.0 -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -# Put the include dirs which are in the source or build tree -# before all other include dirs, so the headers in the sources -# are prefered over the already installed ones -# since cmake 2.4.1 -set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) - -# Use colored output -# since cmake 2.4.0 -set(CMAKE_COLOR_MAKEFILE ON) - -# Define the generic version of the libraries here -set(GENERIC_LIB_VERSION "0.1.0") -set(GENERIC_LIB_SOVERSION "0") - -# Set the default build type to release with debug info -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE RelWithDebInfo - CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." - ) -endif (NOT CMAKE_BUILD_TYPE) - -# Create the compile command database for clang by default -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/libssh/cmake/Modules/DefineCompilerFlags.cmake b/libssh/cmake/Modules/DefineCompilerFlags.cmake deleted file mode 100644 index bfbc38fc..00000000 --- a/libssh/cmake/Modules/DefineCompilerFlags.cmake +++ /dev/null @@ -1,84 +0,0 @@ -# define system dependent compiler flags - -include(CheckCCompilerFlag) -include(CheckCCompilerFlagSSP) - -if (UNIX AND NOT WIN32) - # - # Define GNUCC compiler flags - # - if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") - - # add -Wconversion ? - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes -Wdeclaration-after-statement") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused -Wfloat-equal -Wpointer-arith -Wwrite-strings -Wformat-security") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-format-attribute") - - # with -fPIC - check_c_compiler_flag("-fPIC" WITH_FPIC) - if (WITH_FPIC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") - endif (WITH_FPIC) - - check_c_compiler_flag_ssp("-fstack-protector" WITH_STACK_PROTECTOR) - if (WITH_STACK_PROTECTOR) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector") - endif (WITH_STACK_PROTECTOR) - - if (CMAKE_BUILD_TYPE) - string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) - if (CMAKE_BUILD_TYPE_LOWER MATCHES (release|relwithdebinfo|minsizerel)) - check_c_compiler_flag("-Wp,-D_FORTIFY_SOURCE=2" WITH_FORTIFY_SOURCE) - if (WITH_FORTIFY_SOURCE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wp,-D_FORTIFY_SOURCE=2") - endif (WITH_FORTIFY_SOURCE) - endif() - endif() - endif (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") - - # - # Check for large filesystem support - # - if (CMAKE_SIZEOF_VOID_P MATCHES "8") - # with large file support - execute_process( - COMMAND - getconf LFS64_CFLAGS - OUTPUT_VARIABLE - _lfs_CFLAGS - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - else (CMAKE_SIZEOF_VOID_P MATCHES "8") - # with large file support - execute_process( - COMMAND - getconf LFS_CFLAGS - OUTPUT_VARIABLE - _lfs_CFLAGS - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif (CMAKE_SIZEOF_VOID_P MATCHES "8") - if (_lfs_CFLAGS) - string(REGEX REPLACE "[\r\n]" " " "${_lfs_CFLAGS}" "${${_lfs_CFLAGS}}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_lfs_CFLAGS}") - endif (_lfs_CFLAGS) - -endif (UNIX AND NOT WIN32) - -if (MSVC) - # Use secure functions by defaualt and suppress warnings about - #"deprecated" functions - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") -endif (MSVC) - -# This removes this annoying warning -# "warning: 'BN_CTX_free' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations]" -if (OSX) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") -endif (OSX) - diff --git a/libssh/cmake/Modules/DefineInstallationPaths.cmake b/libssh/cmake/Modules/DefineInstallationPaths.cmake deleted file mode 100644 index 88e08cad..00000000 --- a/libssh/cmake/Modules/DefineInstallationPaths.cmake +++ /dev/null @@ -1,109 +0,0 @@ -if (UNIX OR OS2) - IF (NOT APPLICATION_NAME) - MESSAGE(STATUS "${PROJECT_NAME} is used as APPLICATION_NAME") - SET(APPLICATION_NAME ${PROJECT_NAME}) - ENDIF (NOT APPLICATION_NAME) - - # Suffix for Linux - SET(LIB_SUFFIX - CACHE STRING "Define suffix of directory name (32/64)" - ) - - SET(EXEC_INSTALL_PREFIX - "${CMAKE_INSTALL_PREFIX}" - CACHE PATH "Base directory for executables and libraries" - ) - SET(SHARE_INSTALL_PREFIX - "${CMAKE_INSTALL_PREFIX}/share" - CACHE PATH "Base directory for files which go to share/" - ) - SET(DATA_INSTALL_PREFIX - "${SHARE_INSTALL_PREFIX}/${APPLICATION_NAME}" - CACHE PATH "The parent directory where applications can install their data") - - # The following are directories where stuff will be installed to - SET(BIN_INSTALL_DIR - "${EXEC_INSTALL_PREFIX}/bin" - CACHE PATH "The ${APPLICATION_NAME} binary install dir (default prefix/bin)" - ) - SET(SBIN_INSTALL_DIR - "${EXEC_INSTALL_PREFIX}/sbin" - CACHE PATH "The ${APPLICATION_NAME} sbin install dir (default prefix/sbin)" - ) - SET(LIB_INSTALL_DIR - "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" - CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/lib)" - ) - SET(LIBEXEC_INSTALL_DIR - "${EXEC_INSTALL_PREFIX}/libexec" - CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/libexec)" - ) - SET(PLUGIN_INSTALL_DIR - "${LIB_INSTALL_DIR}/${APPLICATION_NAME}" - CACHE PATH "The subdirectory relative to the install prefix where plugins will be installed (default is prefix/lib/${APPLICATION_NAME})" - ) - SET(INCLUDE_INSTALL_DIR - "${CMAKE_INSTALL_PREFIX}/include" - CACHE PATH "The subdirectory to the header prefix (default prefix/include)" - ) - - set(CMAKE_INSTALL_DIR - "${LIB_INSTALL_DIR}/cmake" - CACHE PATH "The subdirectory to install cmake config files") - - SET(DATA_INSTALL_DIR - "${DATA_INSTALL_PREFIX}" - CACHE PATH "The parent directory where applications can install their data (default prefix/share/${APPLICATION_NAME})" - ) - SET(HTML_INSTALL_DIR - "${DATA_INSTALL_PREFIX}/doc/HTML" - CACHE PATH "The HTML install dir for documentation (default data/doc/html)" - ) - SET(ICON_INSTALL_DIR - "${DATA_INSTALL_PREFIX}/icons" - CACHE PATH "The icon install dir (default data/icons/)" - ) - SET(SOUND_INSTALL_DIR - "${DATA_INSTALL_PREFIX}/sounds" - CACHE PATH "The install dir for sound files (default data/sounds)" - ) - - SET(LOCALE_INSTALL_DIR - "${SHARE_INSTALL_PREFIX}/locale" - CACHE PATH "The install dir for translations (default prefix/share/locale)" - ) - - SET(XDG_APPS_DIR - "${SHARE_INSTALL_PREFIX}/applications/" - CACHE PATH "The XDG apps dir" - ) - SET(XDG_DIRECTORY_DIR - "${SHARE_INSTALL_PREFIX}/desktop-directories" - CACHE PATH "The XDG directory" - ) - - SET(SYSCONF_INSTALL_DIR - "${EXEC_INSTALL_PREFIX}/etc" - CACHE PATH "The ${APPLICATION_NAME} sysconfig install dir (default prefix/etc)" - ) - SET(MAN_INSTALL_DIR - "${SHARE_INSTALL_PREFIX}/man" - CACHE PATH "The ${APPLICATION_NAME} man install dir (default prefix/man)" - ) - SET(INFO_INSTALL_DIR - "${SHARE_INSTALL_PREFIX}/info" - CACHE PATH "The ${APPLICATION_NAME} info install dir (default prefix/info)" - ) -else() - # Same same - set(BIN_INSTALL_DIR "bin" CACHE PATH "-") - set(SBIN_INSTALL_DIR "sbin" CACHE PATH "-") - set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "-") - set(INCLUDE_INSTALL_DIR "include" CACHE PATH "-") - set(CMAKE_INSTALL_DIR "CMake" CACHE PATH "-") - set(PLUGIN_INSTALL_DIR "plugins" CACHE PATH "-") - set(HTML_INSTALL_DIR "doc/HTML" CACHE PATH "-") - set(ICON_INSTALL_DIR "icons" CACHE PATH "-") - set(SOUND_INSTALL_DIR "soudns" CACHE PATH "-") - set(LOCALE_INSTALL_DIR "lang" CACHE PATH "-") -endif () diff --git a/libssh/cmake/Modules/DefinePlatformDefaults.cmake b/libssh/cmake/Modules/DefinePlatformDefaults.cmake deleted file mode 100644 index 77f8a461..00000000 --- a/libssh/cmake/Modules/DefinePlatformDefaults.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# Set system vars - -if (CMAKE_SYSTEM_NAME MATCHES "Linux") - set(LINUX TRUE) -endif(CMAKE_SYSTEM_NAME MATCHES "Linux") - -if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - set(FREEBSD TRUE) - set(BSD TRUE) -endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - -if (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") - set(OPENBSD TRUE) - set(BSD TRUE) -endif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") - -if (CMAKE_SYSTEM_NAME MATCHES "NetBSD") - set(NETBSD TRUE) - set(BSD TRUE) -endif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") - -if (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") - set(SOLARIS TRUE) -endif (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") - -if (CMAKE_SYSTEM_NAME MATCHES "OS2") - set(OS2 TRUE) -endif (CMAKE_SYSTEM_NAME MATCHES "OS2") - -if (CMAKE_SYSTEM_NAME MATCHES "Darwin") - set (OSX TRUE) -endif (CMAKE_SYSTEM_NAME MATCHES "Darwin") diff --git a/libssh/cmake/Modules/FindArgp.cmake b/libssh/cmake/Modules/FindArgp.cmake deleted file mode 100644 index 8dedc855..00000000 --- a/libssh/cmake/Modules/FindArgp.cmake +++ /dev/null @@ -1,60 +0,0 @@ -# - Try to find Argp -# Once done this will define -# -# ARGP_FOUND - system has Argp -# ARGP_INCLUDE_DIRS - the Argp include directory -# ARGP_LIBRARIES - Link these to use Argp -# ARGP_DEFINITIONS - Compiler switches required for using Argp -# -# Copyright (c) 2010 Andreas Schneider -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - - -if (ARGP_LIBRARIES AND ARGP_INCLUDE_DIRS) - # in cache already - set(ARGP_FOUND TRUE) -else (ARGP_LIBRARIES AND ARGP_INCLUDE_DIRS) - - find_path(ARGP_INCLUDE_DIR - NAMES - argp.h - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ) - - find_library(ARGP_LIBRARY - NAMES - argp - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - set(ARGP_INCLUDE_DIRS - ${ARGP_INCLUDE_DIR} - ) - - if (ARGP_LIBRARY) - set(ARGP_LIBRARIES - ${ARGP_LIBRARIES} - ${ARGP_LIBRARY} - ) - endif (ARGP_LIBRARY) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Argp DEFAULT_MSG ARGP_LIBRARIES ARGP_INCLUDE_DIRS) - - # show the ARGP_INCLUDE_DIRS and ARGP_LIBRARIES variables only in the advanced view - mark_as_advanced(ARGP_INCLUDE_DIRS ARGP_LIBRARIES) - -endif (ARGP_LIBRARIES AND ARGP_INCLUDE_DIRS) - diff --git a/libssh/cmake/Modules/FindCMocka.cmake b/libssh/cmake/Modules/FindCMocka.cmake deleted file mode 100644 index 76b4ba74..00000000 --- a/libssh/cmake/Modules/FindCMocka.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# - Try to find CMocka -# Once done this will define -# -# CMOCKA_ROOT_DIR - Set this variable to the root installation of CMocka -# -# Read-Only variables: -# CMOCKA_FOUND - system has CMocka -# CMOCKA_INCLUDE_DIR - the CMocka include directory -# CMOCKA_LIBRARIES - Link these to use CMocka -# CMOCKA_DEFINITIONS - Compiler switches required for using CMocka -# -#============================================================================= -# Copyright (c) 2011-2012 Andreas Schneider -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# - -set(_CMOCKA_ROOT_HINTS -) - -set(_CMOCKA_ROOT_PATHS - "$ENV{PROGRAMFILES}/cmocka" -) - -find_path(CMOCKA_ROOT_DIR - NAMES - include/cmocka.h - HINTS - ${_CMOCKA_ROOT_HINTS} - PATHS - ${_CMOCKA_ROOT_PATHS} -) -mark_as_advanced(CMOCKA_ROOT_DIR) - -find_path(CMOCKA_INCLUDE_DIR - NAMES - cmocka.h - PATHS - ${CMOCKA_ROOT_DIR}/include -) - -find_library(CMOCKA_LIBRARY - NAMES - cmocka - PATHS - ${CMOCKA_ROOT_DIR}/lib -) - -if (CMOCKA_LIBRARY) - set(CMOCKA_LIBRARIES - ${CMOCKA_LIBRARIES} - ${CMOCKA_LIBRARY} - ) -endif (CMOCKA_LIBRARY) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CMocka DEFAULT_MSG CMOCKA_LIBRARIES CMOCKA_INCLUDE_DIR) - -# show the CMOCKA_INCLUDE_DIR and CMOCKA_LIBRARIES variables only in the advanced view -mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARIES) diff --git a/libssh/cmake/Modules/FindGCrypt.cmake b/libssh/cmake/Modules/FindGCrypt.cmake deleted file mode 100644 index 5f1fe40b..00000000 --- a/libssh/cmake/Modules/FindGCrypt.cmake +++ /dev/null @@ -1,75 +0,0 @@ -# - Try to find GCrypt -# Once done this will define -# -# GCRYPT_FOUND - system has GCrypt -# GCRYPT_INCLUDE_DIRS - the GCrypt include directory -# GCRYPT_LIBRARIES - Link these to use GCrypt -# GCRYPT_DEFINITIONS - Compiler switches required for using GCrypt -# -#============================================================================= -# Copyright (c) 2009-2012 Andreas Schneider -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# - -set(_GCRYPT_ROOT_HINTS - $ENV{GCRYTPT_ROOT_DIR} - ${GCRYPT_ROOT_DIR}) - -set(_GCRYPT_ROOT_PATHS - "$ENV{PROGRAMFILES}/libgcrypt") - -set(_GCRYPT_ROOT_HINTS_AND_PATHS - HINTS ${_GCRYPT_ROOT_HINTS} - PATHS ${_GCRYPT_ROOT_PATHS}) - - -find_path(GCRYPT_INCLUDE_DIR - NAMES - gcrypt.h - HINTS - ${_GCRYPT_ROOT_HINTS_AND_PATHS} -) - -find_library(GCRYPT_LIBRARY - NAMES - gcrypt - gcrypt11 - libgcrypt-11 - HINTS - ${_GCRYPT_ROOT_HINTS_AND_PATHS} -) -set(GCRYPT_LIBRARIES ${GCRYPT_LIBRARY}) - -if (GCRYPT_INCLUDE_DIR) - file(STRINGS "${GCRYPT_INCLUDE_DIR}/gcrypt.h" _gcrypt_version_str REGEX "^#define GCRYPT_VERSION \"[0-9]+.[0-9]+.[0-9]+\"") - - string(REGEX REPLACE "^.*GCRYPT_VERSION.*([0-9]+.[0-9]+.[0-9]+).*" "\\1" GCRYPT_VERSION "${_gcrypt_version_str}") -endif (GCRYPT_INCLUDE_DIR) - -include(FindPackageHandleStandardArgs) -if (GCRYPT_VERSION) - find_package_handle_standard_args(GCrypt - REQUIRED_VARS - GCRYPT_INCLUDE_DIR - GCRYPT_LIBRARIES - VERSION_VAR - GCRYPT_VERSION - FAIL_MESSAGE - "Could NOT find GCrypt, try to set the path to GCrypt root folder in the system variable GCRYPT_ROOT_DIR" - ) -else (GCRYPT_VERSION) - find_package_handle_standard_args(GCrypt - "Could NOT find GCrypt, try to set the path to GCrypt root folder in the system variable GCRYPT_ROOT_DIR" - GCRYPT_INCLUDE_DIR - GCRYPT_LIBRARIES) -endif (GCRYPT_VERSION) - -# show the GCRYPT_INCLUDE_DIRS and GCRYPT_LIBRARIES variables only in the advanced view -mark_as_advanced(GCRYPT_INCLUDE_DIR GCRYPT_LIBRARIES) diff --git a/libssh/cmake/Modules/FindGSSAPI.cmake b/libssh/cmake/Modules/FindGSSAPI.cmake deleted file mode 100644 index 8520d35d..00000000 --- a/libssh/cmake/Modules/FindGSSAPI.cmake +++ /dev/null @@ -1,324 +0,0 @@ -# - Try to find GSSAPI -# Once done this will define -# -# KRB5_CONFIG - Path to krb5-config -# GSSAPI_ROOT_DIR - Set this variable to the root installation of GSSAPI -# -# Read-Only variables: -# GSSAPI_FLAVOR_MIT - set to TURE if MIT Kerberos has been found -# GSSAPI_FLAVOR_HEIMDAL - set to TRUE if Heimdal Keberos has been found -# GSSAPI_FOUND - system has GSSAPI -# GSSAPI_INCLUDE_DIR - the GSSAPI include directory -# GSSAPI_LIBRARIES - Link these to use GSSAPI -# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI -# -#============================================================================= -# Copyright (c) 2013 Andreas Schneider -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# - -find_path(GSSAPI_ROOT_DIR - NAMES - include/gssapi.h - include/gssapi/gssapi.h - HINTS - ${_GSSAPI_ROOT_HINTS} - PATHS - ${_GSSAPI_ROOT_PATHS} -) -mark_as_advanced(GSSAPI_ROOT_DIR) - -if (UNIX) - find_program(KRB5_CONFIG - NAMES - krb5-config - PATHS - ${GSSAPI_ROOT_DIR}/bin - /opt/local/bin) - mark_as_advanced(KRB5_CONFIG) - - if (KRB5_CONFIG) - # Check if we have MIT KRB5 - execute_process( - COMMAND - ${KRB5_CONFIG} --vendor - RESULT_VARIABLE - _GSSAPI_VENDOR_RESULT - OUTPUT_VARIABLE - _GSSAPI_VENDOR_STRING) - - if (_GSSAPI_VENDOR_STRING MATCHES ".*Massachusetts.*") - set(GSSAPI_FLAVOR_MIT TRUE) - else() - execute_process( - COMMAND - ${KRB5_CONFIG} --libs gssapi - RESULT_VARIABLE - _GSSAPI_LIBS_RESULT - OUTPUT_VARIABLE - _GSSAPI_LIBS_STRING) - - if (_GSSAPI_LIBS_STRING MATCHES ".*roken.*") - set(GSSAPI_FLAVOR_HEIMDAL TRUE) - endif() - endif() - - # Get the include dir - execute_process( - COMMAND - ${KRB5_CONFIG} --cflags gssapi - RESULT_VARIABLE - _GSSAPI_INCLUDE_RESULT - OUTPUT_VARIABLE - _GSSAPI_INCLUDE_STRING) - string(REGEX REPLACE "(\r?\n)+$" "" _GSSAPI_INCLUDE_STRING "${_GSSAPI_INCLUDE_STRING}") - string(REGEX REPLACE " *-I" "" _GSSAPI_INCLUDEDIR "${_GSSAPI_INCLUDE_STRING}") - endif() - - if (NOT GSSAPI_FLAVOR_MIT AND NOT GSSAPI_FLAVOR_HEIMDAL) - # Check for HEIMDAL - find_package(PkgConfig) - if (PKG_CONFIG_FOUND) - pkg_check_modules(_GSSAPI heimdal-gssapi) - endif (PKG_CONFIG_FOUND) - - if (_GSSAPI_FOUND) - set(GSSAPI_FLAVOR_HEIMDAL TRUE) - else() - find_path(_GSSAPI_ROKEN - NAMES - roken.h - PATHS - ${GSSAPI_ROOT_DIR}/include - ${_GSSAPI_INCLUDEDIR}) - if (_GSSAPI_ROKEN) - set(GSSAPI_FLAVOR_HEIMDAL TRUE) - endif() - endif () - endif() -endif (UNIX) - -find_path(GSSAPI_INCLUDE_DIR - NAMES - gssapi.h - gssapi/gssapi.h - PATHS - ${GSSAPI_ROOT_DIR}/include - ${_GSSAPI_INCLUDEDIR} -) - -if (GSSAPI_FLAVOR_MIT) - find_library(GSSAPI_LIBRARY - NAMES - gssapi_krb5 - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(KRB5_LIBRARY - NAMES - krb5 - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(K5CRYPTO_LIBRARY - NAMES - k5crypto - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(COM_ERR_LIBRARY - NAMES - com_err - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - if (GSSAPI_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${GSSAPI_LIBRARY} - ) - endif (GSSAPI_LIBRARY) - - if (KRB5_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${KRB5_LIBRARY} - ) - endif (KRB5_LIBRARY) - - if (K5CRYPTO_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${K5CRYPTO_LIBRARY} - ) - endif (K5CRYPTO_LIBRARY) - - if (COM_ERR_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${COM_ERR_LIBRARY} - ) - endif (COM_ERR_LIBRARY) -endif (GSSAPI_FLAVOR_MIT) - -if (GSSAPI_FLAVOR_HEIMDAL) - find_library(GSSAPI_LIBRARY - NAMES - gssapi - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(KRB5_LIBRARY - NAMES - krb5 - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(HCRYPTO_LIBRARY - NAMES - hcrypto - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(COM_ERR_LIBRARY - NAMES - com_err - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(HEIMNTLM_LIBRARY - NAMES - heimntlm - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(HX509_LIBRARY - NAMES - hx509 - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(ASN1_LIBRARY - NAMES - asn1 - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(WIND_LIBRARY - NAMES - wind - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - find_library(ROKEN_LIBRARY - NAMES - roken - PATHS - ${GSSAPI_ROOT_DIR}/lib - ${_GSSAPI_LIBDIR} - ) - - if (GSSAPI_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${GSSAPI_LIBRARY} - ) - endif (GSSAPI_LIBRARY) - - if (KRB5_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${KRB5_LIBRARY} - ) - endif (KRB5_LIBRARY) - - if (HCRYPTO_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${HCRYPTO_LIBRARY} - ) - endif (HCRYPTO_LIBRARY) - - if (COM_ERR_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${COM_ERR_LIBRARY} - ) - endif (COM_ERR_LIBRARY) - - if (HEIMNTLM_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${HEIMNTLM_LIBRARY} - ) - endif (HEIMNTLM_LIBRARY) - - if (HX509_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${HX509_LIBRARY} - ) - endif (HX509_LIBRARY) - - if (ASN1_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${ASN1_LIBRARY} - ) - endif (ASN1_LIBRARY) - - if (WIND_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${WIND_LIBRARY} - ) - endif (WIND_LIBRARY) - - if (ROKEN_LIBRARY) - set(GSSAPI_LIBRARIES - ${GSSAPI_LIBRARIES} - ${WIND_LIBRARY} - ) - endif (ROKEN_LIBRARY) -endif (GSSAPI_FLAVOR_HEIMDAL) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR) - -if (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES) - set(GSSAPI_FOUND TRUE) -endif (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES) - -# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view -mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES) diff --git a/libssh/cmake/Modules/FindNSIS.cmake b/libssh/cmake/Modules/FindNSIS.cmake deleted file mode 100644 index 21f80d86..00000000 --- a/libssh/cmake/Modules/FindNSIS.cmake +++ /dev/null @@ -1,55 +0,0 @@ -# - Try to find NSIS -# Once done this will define -# -# NSIS_ROOT_PATH - Set this variable to the root installation of NSIS -# -# Read-Only variables: -# -# NSIS_FOUND - system has NSIS -# NSIS_MAKE - NSIS creator executable -# -#============================================================================= -# Copyright (c) 2010-2013 Andreas Schneider -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# - -if (WIN32) - set(_NSIS_ROOT_HINTS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS;Default]") - - set(_NSIS_ROOT_PATHS - $ENV{PROGRAMFILES}/NSIS) - - find_path(NSIS_ROOT_PATH - NAMES - Include/Library.nsh - HINTS - ${_NSIS_ROOT_HINTS} - PATHS - ${_NSIS_ROOT_PATHS} - ) - mark_as_advanced(NSIS_ROOT_PATH) -endif (WIN32) - -find_program(NSIS_MAKE - NAMES - makensis - PATHS - ${NSIS_ROOT_PATH} -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(NSIS DEFAULT_MSG NSIS_MAKE) - -if (NSIS_MAKE) - set(NSIS_FOUND TRUE) -endif (NSIS_MAKE) - -mark_as_advanced(NSIS_MAKE) diff --git a/libssh/cmake/Modules/FindNaCl.cmake b/libssh/cmake/Modules/FindNaCl.cmake deleted file mode 100644 index b1a8da45..00000000 --- a/libssh/cmake/Modules/FindNaCl.cmake +++ /dev/null @@ -1,61 +0,0 @@ -# - Try to find NaCl -# Once done this will define -# -# NACL_FOUND - system has NaCl -# NACL_INCLUDE_DIRS - the NaCl include directory -# NACL_LIBRARIES - Link these to use NaCl -# NACL_DEFINITIONS - Compiler switches required for using NaCl -# -# Copyright (c) 2010 Andreas Schneider -# Copyright (c) 2013 Aris Adamantiadis -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - - -if (NACL_LIBRARIES AND NACL_INCLUDE_DIRS) - # in cache already - set(NACL_FOUND TRUE) -else (NACL_LIBRARIES AND NACL_INCLUDE_DIRS) - - find_path(NACL_INCLUDE_DIR - NAMES - nacl/crypto_box_curve25519xsalsa20poly1305.h - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ) - - find_library(NACL_LIBRARY - NAMES - nacl - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ) - - set(NACL_INCLUDE_DIRS - ${NACL_INCLUDE_DIR} - ) - - if (NACL_LIBRARY) - set(NACL_LIBRARIES - ${NACL_LIBRARIES} - ${NACL_LIBRARY} - ) - endif (NACL_LIBRARY) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(NaCl DEFAULT_MSG NACL_LIBRARIES NACL_INCLUDE_DIRS) - - # show the NACL_INCLUDE_DIRS and NACL_LIBRARIES variables only in the advanced view - mark_as_advanced(NACL_INCLUDE_DIRS NACL_LIBRARIES) - -endif (NACL_LIBRARIES AND NACL_INCLUDE_DIRS) - diff --git a/libssh/cmake/Modules/FindOpenSSL.cmake b/libssh/cmake/Modules/FindOpenSSL.cmake deleted file mode 100644 index 565190c6..00000000 --- a/libssh/cmake/Modules/FindOpenSSL.cmake +++ /dev/null @@ -1,208 +0,0 @@ -# - Try to find OpenSSL -# Once done this will define -# -# OPENSSL_ROOT_DIR - Set this variable to the root installation of OpenSSL -# -# Read-Only variables: -# OPENSSL_FOUND - system has OpenSSL -# OPENSSL_INCLUDE_DIRS - the OpenSSL include directory -# OPENSSL_LIBRARIES - Link these to use OpenSSL -# OPENSSL_DEFINITIONS - Compiler switches required for using OpenSSL -# -#============================================================================= -# Copyright (c) 2006-2009 Kitware, Inc. -# Copyright (c) 2006 Alexander Neundorf -# Copyright (c) 2009-2010 Mathieu Malaterre -# Copyright (c) 2011 Andreas Schneider -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# - -if (OPENSSL_LIBRARIES AND OPENSSL_INCLUDE_DIRS) - # in cache already - set(OPENSSL_FOUND TRUE) -else (OPENSSL_LIBRARIES AND OPENSSL_INCLUDE_DIRS) - - if (UNIX) - find_package(PkgConfig) - if (PKG_CONFIG_FOUND) - pkg_check_modules(_OPENSSL openssl) - endif (PKG_CONFIG_FOUND) - endif (UNIX) - - # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_ROOT_HINTS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ) - - set(_OPENSSL_ROOT_PATHS - "C:/OpenSSL/" - "C:/OpenSSL-Win32/" - "C:/OpenSSL-Win64/" - "$ENV{PROGRAMFILES}/OpenSSL" - "$ENV{PROGRAMFILES}/OpenSSL-Win32" - "$ENV{PROGRAMFILES}/OpenSSL-Win64" - ) - - find_path(OPENSSL_ROOT_DIR - NAMES - include/openssl/ssl.h - HINTS - ${_OPENSSL_ROOT_HINTS} - PATHS - ${_OPENSSL_ROOT_PATHS} - ) - mark_as_advanced(OPENSSL_ROOT_DIR) - - find_path(OPENSSL_INCLUDE_DIR - NAMES - openssl/ssl.h - PATHS - /usr/local/include - /opt/local/include - /sw/include - /usr/lib/sfw/include - ${OPENSSL_ROOT_DIR}/include - ) - - set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) - mark_as_advanced(OPENSSL_INCLUDE_DIRS) - - if (WIN32 AND NOT CYGWIN) - # MINGW should go here too - if (MSVC) - # /MD and /MDd are the standard values - if someone wants to use - # others, the libnames have to change here too - # use also ssl and ssleay32 in debug as fallback for openssl < 0.9.8b - # TODO: handle /MT and static lib - # In Visual C++ naming convention each of these four kinds of Windows libraries has it's standard suffix: - # * MD for dynamic-release - # * MDd for dynamic-debug - # * MT for static-release - # * MTd for static-debug - - # Implementation details: - # We are using the libraries located in the VC subdir instead of the parent directory eventhough : - # libeay32MD.lib is identical to ../libeay32.lib, and - # ssleay32MD.lib is identical to ../ssleay32.lib - find_library(LIB_EAY_DEBUG - NAMES - libeay32MDd - libeay32 - PATHS - ${OPENSSL_ROOT_DIR}/lib/VC - ) - - find_library(LIB_EAY_RELEASE - NAMES - libeay32MD - libeay32 - PATHS - ${OPENSSL_ROOT_DIR}/lib/VC - ) - - find_library(SSL_EAY_DEBUG - NAMES - ssleay32MDd - ssleay32 - ssl - PATHS ${OPENSSL_ROOT_DIR}/lib/VC - ) - - find_library(SSL_EAY_RELEASE - NAMES - ssleay32MD - ssleay32 - ssl - PATHS - ${OPENSSL_ROOT_DIR}/lib/VC - ) - - if (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) - set(OPENSSL_LIBRARIES - optimized ${SSL_EAY_RELEASE} debug ${SSL_EAY_DEBUG} - optimized ${LIB_EAY_RELEASE} debug ${LIB_EAY_DEBUG} - ) - else (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) - set( OPENSSL_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} ) - endif (CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) - - mark_as_advanced(SSL_EAY_DEBUG SSL_EAY_RELEASE) - mark_as_advanced(LIB_EAY_DEBUG LIB_EAY_RELEASE) - elseif (MINGW) - # same player, for MingW - find_library(LIB_EAY - NAMES - libeay32 - PATHS - ${OPENSSL_ROOT_DIR}/lib/MinGW - ) - - find_library(SSL_EAY - NAMES - ssleay32 - PATHS - ${OPENSSL_ROOT_DIR}/lib/MinGW - ) - - mark_as_advanced(SSL_EAY LIB_EAY) - set(OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY}) - else(MSVC) - # Not sure what to pick for -say- intel, let's use the toplevel ones and hope someone report issues: - find_library(LIB_EAY - NAMES - libeay32 - PATHS - ${OPENSSL_ROOT_DIR}/lib - ) - - find_library(SSL_EAY - NAMES - ssleay32 - PATHS - ${OPENSSL_ROOT_DIR}/lib - ) - - mark_as_advanced(SSL_EAY LIB_EAY) - set(OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY}) - endif(MSVC) - else (WIN32 AND NOT CYGWIN) - find_library(OPENSSL_SSL_LIBRARIES - NAMES - ssl - ssleay32 - ssleay32MD - PATHS - ${_OPENSSL_LIBDIR} - /opt/local/lib - /sw/lib - /usr/sfw/lib/64 - /usr/sfw/lib - ) - - find_library(OPENSSL_CRYPTO_LIBRARIES - NAMES - crypto - PATHS - ${_OPENSSL_LIBDIR} - /opt/local/lib - /sw/lib - /usr/sfw/lib/64 - /usr/sfw/lib - ) - - mark_as_advanced(OPENSSL_CRYPTO_LIBRARIES OPENSSL_SSL_LIBRARIES) - set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES}) - endif (WIN32 AND NOT CYGWIN) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(OpenSSL DEFAULT_MSG OPENSSL_LIBRARIES OPENSSL_INCLUDE_DIRS) - -endif (OPENSSL_LIBRARIES AND OPENSSL_INCLUDE_DIRS) diff --git a/libssh/cmake/Modules/FindZLIB.cmake b/libssh/cmake/Modules/FindZLIB.cmake deleted file mode 100644 index f68207e4..00000000 --- a/libssh/cmake/Modules/FindZLIB.cmake +++ /dev/null @@ -1,120 +0,0 @@ -# - Try to find ZLIB -# Once done this will define -# -# ZLIB_ROOT_DIR - Set this variable to the root installation of ZLIB -# -# Read-Only variables: -# ZLIB_FOUND - system has ZLIB -# ZLIB_INCLUDE_DIRS - the ZLIB include directory -# ZLIB_LIBRARIES - Link these to use ZLIB -# -# ZLIB_VERSION_STRING - The version of zlib found (x.y.z) -# ZLIB_VERSION_MAJOR - The major version of zlib -# ZLIB_VERSION_MINOR - The minor version of zlib -# ZLIB_VERSION_PATCH - The patch version of zlib -# ZLIB_VERSION_TWEAK - The tweak version of zlib -# -# The following variable are provided for backward compatibility -# -# ZLIB_MAJOR_VERSION - The major version of zlib -# ZLIB_MINOR_VERSION - The minor version of zlib -# ZLIB_PATCH_VERSION - The patch version of zlib -# -#============================================================================= -# Copyright (c) 2001-2009 Kitware, Inc. -# Copyright (c) 2011 Andreas Schneider -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# - -if (ZLIB_LIBRARIES AND ZLIB_INCLUDE_DIRS) - # in cache already - set(ZLIB_FOUND TRUE) -else (ZLIB_LIBRARIES AND ZLIB_INCLUDE_DIRS) - - set(_ZLIB_ROOT_HINTS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\Zlib;InstallPath]/include" - ) - - set(_ZLIB_ROOT_PATHS - "$ENV{PROGRAMFILES}/zlib" - ) - - find_path(ZLIB_ROOT_DIR - NAMES - include/zlib.h - HINTS - ${_ZLIB_ROOT_HINTS} - PATHS - ${_ZLIB_ROOT_PATHS} - ) - mark_as_advanced(ZLIB_ROOT_DIR) - - # check for header file - find_path(ZLIB_INCLUDE_DIR - NAMES - zlib.h - PATHS - /usr/local/include - /opt/local/include - /sw/include - /usr/lib/sfw/include - ${ZLIB_ROOT_DIR}/include - ) - mark_as_advanced(ZLIB_INCLUDE_DIR) - - # check version number - if (ZLIB_INCLUDE_DIR AND EXISTS "${ZLIB_INCLUDE_DIR}/zlib.h") - file(STRINGS "${ZLIB_INCLUDE_DIR}/zlib.h" ZLIB_H REGEX "^#define ZLIB_VERSION \"[^\"]*\"$") - - string(REGEX REPLACE "^.*ZLIB_VERSION \"([0-9]+).*$" "\\1" ZLIB_VERSION_MAJOR "${ZLIB_H}") - string(REGEX REPLACE "^.*ZLIB_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" ZLIB_VERSION_MINOR "${ZLIB_H}") - string(REGEX REPLACE "^.*ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" ZLIB_VERSION_PATCH "${ZLIB_H}") - - set(ZLIB_VERSION_STRING "${ZLIB_VERSION_MAJOR}.${ZLIB_VERSION_MINOR}.${ZLIB_VERSION_PATCH}") - - # only append a TWEAK version if it exists: - set(ZLIB_VERSION_TWEAK "") - if ("${ZLIB_H}" MATCHES "^.*ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+).*$") - set(ZLIB_VERSION_TWEAK "${CMAKE_MATCH_1}") - set(ZLIB_VERSION_STRING "${ZLIB_VERSION_STRING}.${ZLIB_VERSION_TWEAK}") - endif ("${ZLIB_H}" MATCHES "^.*ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+).*$") - - set(ZLIB_MAJOR_VERSION "${ZLIB_VERSION_MAJOR}") - set(ZLIB_MINOR_VERSION "${ZLIB_VERSION_MINOR}") - set(ZLIB_PATCH_VERSION "${ZLIB_VERSION_PATCH}") - endif (ZLIB_INCLUDE_DIR AND EXISTS "${ZLIB_INCLUDE_DIR}/zlib.h") - - find_library(ZLIB_LIBRARY - NAMES - z - zdll - zlib - zlib1 - zlibd - PATHS - /usr/local/lib - /opt/local/lib - /sw/lib - /usr/sfw/lib/64 - /usr/sfw/lib - ${ZLIB_ROOT_DIR}/lib - ) - mark_as_advanced(ZLIB_LIBRARY) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(ZLIB DEFAULT_MSG ZLIB_INCLUDE_DIR ZLIB_LIBRARY) - #find_package_handle_standard_args(ZLIB REQUIRED_VARS ZLIB_INCLUDE_DIR ZLIB_LIBRARY - # VERSION_VAR ZLIB_VERSION_STRING) - - if (ZLIB_FOUND) - set(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR}) - set(ZLIB_LIBRARIES ${ZLIB_LIBRARY}) - endif (ZLIB_FOUND) -endif (ZLIB_LIBRARIES AND ZLIB_INCLUDE_DIRS) diff --git a/libssh/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake b/libssh/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake deleted file mode 100644 index a2e94809..00000000 --- a/libssh/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# - MACRO_ENSURE_OUT_OF_SOURCE_BUILD() -# MACRO_ENSURE_OUT_OF_SOURCE_BUILD() - -# Copyright (c) 2006, Alexander Neundorf, -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -macro (MACRO_ENSURE_OUT_OF_SOURCE_BUILD _errorMessage) - - string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" _insource) - if (_insource) - message(SEND_ERROR "${_errorMessage}") - message(FATAL_ERROR "Remove the file CMakeCache.txt in ${CMAKE_SOURCE_DIR} first.") - endif (_insource) - -endmacro (MACRO_ENSURE_OUT_OF_SOURCE_BUILD) diff --git a/libssh/cmake/Modules/UseDoxygen.cmake b/libssh/cmake/Modules/UseDoxygen.cmake deleted file mode 100644 index 72c384d2..00000000 --- a/libssh/cmake/Modules/UseDoxygen.cmake +++ /dev/null @@ -1,140 +0,0 @@ -# - Run Doxygen -# -# Adds a doxygen target that runs doxygen to generate the html -# and optionally the LaTeX API documentation. -# The doxygen target is added to the doc target as a dependency. -# i.e.: the API documentation is built with: -# make doc -# -# USAGE: GLOBAL INSTALL -# -# Install it with: -# cmake ./ && sudo make install -# Add the following to the CMakeLists.txt of your project: -# include(UseDoxygen OPTIONAL) -# Optionally copy Doxyfile.in in the directory of CMakeLists.txt and edit it. -# -# USAGE: INCLUDE IN PROJECT -# -# set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) -# include(UseDoxygen) -# Add the Doxyfile.in and UseDoxygen.cmake files to the projects source directory. -# -# -# CONFIGURATION -# -# To configure Doxygen you can edit Doxyfile.in and set some variables in cmake. -# Variables you may define are: -# DOXYFILE_SOURCE_DIR - Path where the Doxygen input files are. -# Defaults to the current source directory. -# DOXYFILE_EXTRA_SOURCES - Additional source diretories/files for Doxygen to scan. -# The Paths should be in double quotes and separated by space. e.g.: -# "${CMAKE_CURRENT_BINARY_DIR}/foo.c" "${CMAKE_CURRENT_BINARY_DIR}/bar/" -# -# DOXYFILE_OUTPUT_DIR - Path where the Doxygen output is stored. -# Defaults to "${CMAKE_CURRENT_BINARY_DIR}/doc". -# -# DOXYFILE_LATEX - ON/OFF; Set to "ON" if you want the LaTeX documentation -# to be built. -# DOXYFILE_LATEX_DIR - Directory relative to DOXYFILE_OUTPUT_DIR where -# the Doxygen LaTeX output is stored. Defaults to "latex". -# -# DOXYFILE_HTML_DIR - Directory relative to DOXYFILE_OUTPUT_DIR where -# the Doxygen html output is stored. Defaults to "html". -# - -# -# Copyright (c) 2009, 2010, 2011 Tobias Rautenkranz -# -# Redistribution and use is allowed according to the terms of the New -# BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# - -macro(usedoxygen_set_default name value type docstring) - if(NOT DEFINED "${name}") - set("${name}" "${value}" CACHE "${type}" "${docstring}") - endif() -endmacro() - -find_package(Doxygen) - -if(DOXYGEN_FOUND) - find_file(DOXYFILE_IN "Doxyfile.in" - PATHS "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_ROOT}/Modules/" - NO_DEFAULT_PATH - DOC "Path to the doxygen configuration template file") - set(DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(DOXYFILE_IN DEFAULT_MSG "DOXYFILE_IN") -endif() - -if(DOXYGEN_FOUND AND DOXYFILE_IN_FOUND) - usedoxygen_set_default(DOXYFILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc" - PATH "Doxygen output directory") - usedoxygen_set_default(DOXYFILE_HTML_DIR "html" - STRING "Doxygen HTML output directory") - usedoxygen_set_default(DOXYFILE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" - PATH "Input files source directory") - usedoxygen_set_default(DOXYFILE_EXTRA_SOURCE_DIRS "" - STRING "Additional source files/directories separated by space") - set(DOXYFILE_SOURCE_DIRS "\"${DOXYFILE_SOURCE_DIR}\" ${DOXYFILE_EXTRA_SOURCES}") - - usedoxygen_set_default(DOXYFILE_LATEX YES BOOL "Generate LaTeX API documentation" OFF) - usedoxygen_set_default(DOXYFILE_LATEX_DIR "latex" STRING "LaTex output directory") - - mark_as_advanced(DOXYFILE_OUTPUT_DIR DOXYFILE_HTML_DIR DOXYFILE_LATEX_DIR - DOXYFILE_SOURCE_DIR DOXYFILE_EXTRA_SOURCE_DIRS DOXYFILE_IN) - - - set_property(DIRECTORY - APPEND PROPERTY - ADDITIONAL_MAKE_CLEAN_FILES - "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_HTML_DIR}") - - add_custom_target(doxygen - COMMAND "${DOXYGEN_EXECUTABLE}" - "${DOXYFILE}" - COMMENT "Writing documentation to ${DOXYFILE_OUTPUT_DIR}..." - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") - - set(DOXYFILE_DOT "NO") - if(DOXYGEN_DOT_EXECUTABLE) - set(DOXYFILE_DOT "YES") - endif() - - ## LaTeX - set(DOXYFILE_PDFLATEX "NO") - - set_property(DIRECTORY APPEND PROPERTY - ADDITIONAL_MAKE_CLEAN_FILES - "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") - - if(DOXYFILE_LATEX STREQUAL "ON") - set(DOXYFILE_GENERATE_LATEX "YES") - find_package(LATEX) - find_program(DOXYFILE_MAKE make) - mark_as_advanced(DOXYFILE_MAKE) - if(LATEX_COMPILER AND MAKEINDEX_COMPILER AND DOXYFILE_MAKE) - if(PDFLATEX_COMPILER) - set(DOXYFILE_PDFLATEX "YES") - endif() - - add_custom_command(TARGET doxygen - POST_BUILD - COMMAND "${DOXYFILE_MAKE}" - COMMENT "Running LaTeX for Doxygen documentation in ${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}..." - WORKING_DIRECTORY "${DOXYFILE_OUTPUT_DIR}/${DOXYFILE_LATEX_DIR}") - else() - set(DOXYGEN_LATEX "NO") - endif() - else() - set(DOXYFILE_GENERATE_LATEX "NO") - endif() - - - configure_file("${DOXYFILE_IN}" "${DOXYFILE}" @ONLY) - - add_custom_target(doc) - add_dependencies(doc doxygen) -endif() diff --git a/libssh/config.h.cmake b/libssh/config.h.cmake deleted file mode 100644 index 55e37aca..00000000 --- a/libssh/config.h.cmake +++ /dev/null @@ -1,176 +0,0 @@ -/* Name of package */ -#cmakedefine PACKAGE "${APPLICATION_NAME}" - -/* Version number of package */ -#cmakedefine VERSION "${APPLICATION_VERSION}" - -#cmakedefine LOCALEDIR "${LOCALE_INSTALL_DIR}" -#cmakedefine DATADIR "${DATADIR}" -#cmakedefine LIBDIR "${LIBDIR}" -#cmakedefine PLUGINDIR "${PLUGINDIR}" -#cmakedefine SYSCONFDIR "${SYSCONFDIR}" -#cmakedefine BINARYDIR "${BINARYDIR}" -#cmakedefine SOURCEDIR "${SOURCEDIR}" - -/************************** HEADER FILES *************************/ - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_ARGP_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_PTY_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_UTMP_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_UTIL_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_LIBUTIL_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_TERMIOS_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_UNISTD_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_OPENSSL_AES_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_WSPIAPI_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_OPENSSL_BLOWFISH_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_OPENSSL_DES_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_OPENSSL_ECDH_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_OPENSSL_EC_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_OPENSSL_ECDSA_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_PTHREAD_H 1 - -/* Define to 1 if you have eliptic curve cryptography in openssl */ -#cmakedefine HAVE_OPENSSL_ECC 1 - -/* Define to 1 if you have eliptic curve cryptography in gcrypt */ -#cmakedefine HAVE_GCRYPT_ECC 1 - -/* Define to 1 if you have eliptic curve cryptography */ -#cmakedefine HAVE_ECC 1 - -/*************************** FUNCTIONS ***************************/ - -/* Define to 1 if you have the `snprintf' function. */ -#cmakedefine HAVE_SNPRINTF 1 - -/* Define to 1 if you have the `_snprintf' function. */ -#cmakedefine HAVE__SNPRINTF 1 - -/* Define to 1 if you have the `_snprintf_s' function. */ -#cmakedefine HAVE__SNPRINTF_S 1 - -/* Define to 1 if you have the `vsnprintf' function. */ -#cmakedefine HAVE_VSNPRINTF 1 - -/* Define to 1 if you have the `_vsnprintf' function. */ -#cmakedefine HAVE__VSNPRINTF 1 - -/* Define to 1 if you have the `_vsnprintf_s' function. */ -#cmakedefine HAVE__VSNPRINTF_S 1 - -/* Define to 1 if you have the `isblank' function. */ -#cmakedefine HAVE_ISBLANK 1 - -/* Define to 1 if you have the `strncpy' function. */ -#cmakedefine HAVE_STRNCPY 1 - -/* Define to 1 if you have the `cfmakeraw' function. */ -#cmakedefine HAVE_CFMAKERAW 1 - -/* Define to 1 if you have the `getaddrinfo' function. */ -#cmakedefine HAVE_GETADDRINFO 1 - -/* Define to 1 if you have the `poll' function. */ -#cmakedefine HAVE_POLL 1 - -/* Define to 1 if you have the `select' function. */ -#cmakedefine HAVE_SELECT 1 - -/* Define to 1 if you have the `clock_gettime' function. */ -#cmakedefine HAVE_CLOCK_GETTIME 1 - -/* Define to 1 if you have the `ntohll' function. */ -#cmakedefine HAVE_NTOHLL 1 - -/* Define to 1 if you have the `htonll' function. */ -#cmakedefine HAVE_HTONLL 1 - -/* Define to 1 if you have the `strtoull' function. */ -#cmakedefine HAVE_STRTOULL 1 - -/* Define to 1 if you have the `__strtoull' function. */ -#cmakedefine HAVE___STRTOULL 1 - -/* Define to 1 if you have the `_strtoui64' function. */ -#cmakedefine HAVE__STRTOUI64 1 - -/*************************** LIBRARIES ***************************/ - -/* Define to 1 if you have the `crypto' library (-lcrypto). */ -#cmakedefine HAVE_LIBCRYPTO 1 - -/* Define to 1 if you have the `gcrypt' library (-lgcrypt). */ -#cmakedefine HAVE_LIBGCRYPT 1 - -/* Define to 1 if you have the `pthread' library (-lpthread). */ -#cmakedefine HAVE_PTHREAD 1 - -/**************************** OPTIONS ****************************/ - -#cmakedefine HAVE_GCC_THREAD_LOCAL_STORAGE 1 -#cmakedefine HAVE_MSC_THREAD_LOCAL_STORAGE 1 - -#cmakedefine HAVE_GCC_VOLATILE_MEMORY_PROTECTION 1 - -/* Define to 1 if you want to enable GSSAPI */ -#cmakedefine WITH_GSSAPI 1 - -/* Define to 1 if you want to enable ZLIB */ -#cmakedefine WITH_ZLIB 1 - -/* Define to 1 if you want to enable SFTP */ -#cmakedefine WITH_SFTP 1 - -/* Define to 1 if you want to enable SSH1 */ -#cmakedefine WITH_SSH1 1 - -/* Define to 1 if you want to enable server support */ -#cmakedefine WITH_SERVER 1 - -/* Define to 1 if you want to enable debug output for crypto functions */ -#cmakedefine DEBUG_CRYPTO 1 - -/* Define to 1 if you want to enable pcap output support (experimental) */ -#cmakedefine WITH_PCAP 1 - -/* Define to 1 if you want to enable calltrace debug output */ -#cmakedefine DEBUG_CALLTRACE 1 - -/* Define to 1 if you want to enable NaCl support */ -#cmakedefine WITH_NACL 1 - -/*************************** ENDIAN *****************************/ - -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#cmakedefine WORDS_BIGENDIAN 1 diff --git a/libssh/doc/CMakeLists.txt b/libssh/doc/CMakeLists.txt deleted file mode 100644 index 31242811..00000000 --- a/libssh/doc/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -# -# Build the documentation -# -include(UseDoxygen OPTIONAL) - diff --git a/libssh/doc/Doxyfile.in b/libssh/doc/Doxyfile.in deleted file mode 100644 index a7a9ffbb..00000000 --- a/libssh/doc/Doxyfile.in +++ /dev/null @@ -1,1917 +0,0 @@ -# Doxyfile 1.8.4 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed -# in front of the TAG it is preceding . -# All text after a hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" "). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or sequence of words) that should -# identify the project. Note that if you do not use Doxywizard you need -# to put quotes around the project name if it contains spaces. - -PROJECT_NAME = @APPLICATION_NAME@ - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = @APPLICATION_VERSION@ - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, -# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, -# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. Note that you specify absolute paths here, but also -# relative paths, which will be relative from the directory where doxygen is -# started. - -STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = @CMAKE_SOURCE_DIR@ - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 2 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding -# "class=itcl::class" will allow you to use the command class in the -# itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, -# and language is one of the parsers supported by doxygen: IDL, Java, -# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, -# C++. For instance to make doxygen treat .inc files as Fortran files (default -# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note -# that for custom extensions you also need to set FILE_PATTERNS otherwise the -# files are not read by doxygen. - -EXTENSION_MAPPING = - -# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all -# comments according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you -# can mix doxygen, HTML, and XML commands with Markdown formatting. -# Disable only in case of backward compatibilities issues. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES (the -# default) will make doxygen replace the get and set methods by a property in -# the documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or -# section (for LaTeX and RTF). - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and -# unions with only public data fields or simple typedef fields will be shown -# inline in the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO (the default), structs, classes, and unions are shown on a separate -# page (for HTML and Man pages) or section (for LaTeX and RTF). - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = YES - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can -# be an expensive process and often the same symbol appear multiple times in -# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too -# small doxygen will become slower. If the cache is too large, memory is wasted. -# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid -# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 -# symbols. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = NO - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = YES - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = YES - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = @CMAKE_INTERNAL_DOC@ - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = YES - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if section-label ... \endif -# and \cond section-label ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files -# containing the references data. This must be a list of .bib files. The -# .bib extension is automatically appended if omitted. Using this command -# requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style -# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this -# feature you need bibtex and perl available in the search path. Do not use -# file names with spaces, bibtex cannot handle them. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = YES - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = @CMAKE_SOURCE_DIR@/include/libssh \ - @CMAKE_SOURCE_DIR@/src \ - @CMAKE_SOURCE_DIR@/doc - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl - -FILE_PATTERNS = *.cpp \ - *.cc \ - *.c \ - *.h \ - *.hh \ - *.hpp \ - *.dox - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = */.git/* \ - */.svn/* \ - */cmake/* \ - */obj/* - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/examples - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = *.c \ - *.h \ - INSTALL \ - DEPENDENCIES \ - CHANGELOG \ - LICENSE \ - LGPL - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = YES - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be ignored. -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C, C++ and Fortran comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 2 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is advised to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If left blank doxygen will -# generate a default style sheet. Note that it is recommended to use -# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this -# tag will in the future become obsolete. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional -# user-defined cascading style sheet that is included after the standard -# style sheets created by doxygen. Using this option one can overrule -# certain style aspects. This is preferred over using HTML_STYLESHEET -# since it does not replace the standard style sheet and is therefor more -# robust against future updates. Doxygen will copy the style sheet file to -# the output directory. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the style sheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = NO - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of -# entries shown in the various tree structured indices initially; the user -# can expand and collapse entries dynamically later on. Doxygen will expand -# the tree to such a level that at most the specified number of entries are -# visible (unless a fully collapsed tree already exceeds this amount). -# So setting the number of entries 1 will produce a full collapsed tree by -# default. 0 is a special value representing an infinite number of entries -# and will result in a full expanded tree by default. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely -# identify the documentation publisher. This should be a reverse domain-name -# style string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) -# at top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. Since the tabs have the same information as the -# navigation tree you can set this option to NO if you already set -# GENERATE_TREEVIEW to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. -# Since the tree basically has the same information as the tab index you -# could consider to set DISABLE_INDEX to NO when enabling this option. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you may also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and -# SVG. The default value is HTML-CSS, which is slower, but has the best -# compatibility. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to -# the MathJax Content Delivery Network so you can quickly see the result without -# installing MathJax. -# However, it is strongly recommended to install a local -# copy of MathJax from http://www.mathjax.org before deployment. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension -# names that should be enabled during MathJax rendering. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript -# pieces of code that will be used on startup of the MathJax code. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = NO - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. -# There are two flavours of web server based search depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. -# See the manual for details. - -SERVER_BASED_SEARCH = NO - -# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP -# script for searching. Instead the search results are written to an XML file -# which needs to be processed by an external indexer. Doxygen will invoke an -# external search engine pointed to by the SEARCHENGINE_URL option to obtain -# the search results. Doxygen ships with an example indexer (doxyindexer) and -# search engine (doxysearch.cgi) which are based on the open source search -# engine library Xapian. See the manual for configuration details. - -EXTERNAL_SEARCH = NO - -# The SEARCHENGINE_URL should point to a search engine hosted by a web server -# which will returned the search results when EXTERNAL_SEARCH is enabled. -# Doxygen ships with an example search engine (doxysearch) which is based on -# the open source search engine library Xapian. See the manual for configuration -# details. - -SEARCHENGINE_URL = - -# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed -# search data is written to a file for indexing by an external tool. With the -# SEARCHDATA_FILE tag the name of this file can be specified. - -SEARCHDATA_FILE = searchdata.xml - -# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the -# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is -# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple -# projects and redirect the results back to the right project. - -EXTERNAL_SEARCH_ID = - -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen -# projects other than the one defined by this configuration file, but that are -# all added to the same external search index. Each project needs to have a -# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id -# of to a relative location where the documentation can be found. -# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... - -EXTRA_SEARCH_MAPPINGS = - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = @LATEX_COMPILER@ - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = @MAKEINDEX_COMPILER@ - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4 will be used. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for -# the generated latex document. The footer should contain everything after -# the last chapter. If it is left blank doxygen will generate a -# standard footer. Notice: only use this tag if you know what you are doing! - -LATEX_FOOTER = - -# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images -# or other source files which should be copied to the LaTeX output directory. -# Note that the files will be copied as-is; there are no commands or markers -# available. - -LATEX_EXTRA_FILES = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = YES - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -# The LATEX_BIB_STYLE tag can be used to specify the style to use for the -# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See -# http://en.wikipedia.org/wiki/BibTeX for more info. - -LATEX_BIB_STYLE = plain - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load style sheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = YES - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options related to the DOCBOOK output -#--------------------------------------------------------------------------- - -# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files -# that can be used to generate PDF. - -GENERATE_DOCBOOK = NO - -# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in -# front of it. If left blank docbook will be used as the default path. - -DOCBOOK_OUTPUT = docbook - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = YES - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# pointed to by INCLUDE_PATH will be searched when a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = WITH_SERVER WITH_SFTP WITH_PCAP - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that -# overrules the definition found in the source code. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all references to function-like macros -# that are alone on a line, have an all uppercase name, and do not end with a -# semicolon, because these will confuse the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. For each -# tag file the location of the external documentation should be added. The -# format of a tag file without this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths -# or URLs. Note that each tag file must have a unique name (where the name does -# NOT include the path). If a tag file is not located in the directory in which -# doxygen is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = @CMAKE_CURRENT_BINARY_DIR@/html/@PROJECT_NAME@.TAGFILE - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = YES - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed -# in the related pages index. If set to NO, only the current project's -# pages will be listed. - -EXTERNAL_PAGES = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to -# install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = NO - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = @DOXYGEN_DOT_FOUND@ - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will use the Helvetica font for all dot files that -# doxygen generates. When you want a differently looking font you can specify -# the font name using DOT_FONTNAME. You need to make sure dot is able to find -# the font, which can be done by putting it in a standard location or by setting -# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the -# directory containing the font. - -DOT_FONTNAME = - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the Helvetica font. -# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to -# set the path where dot can find it. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If the UML_LOOK tag is enabled, the fields and methods are shown inside -# the class node. If there are many fields or methods and many nodes the -# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS -# threshold limits the number of items for each type to make the size more -# manageable. Set this to 0 for no limit. Note that the threshold may be -# exceeded by 50% before the limit is enforced. - -UML_LIMIT_NUM_FIELDS = 10 - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will generate a graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are svg, png, jpg, or gif. -# If left blank png will be used. If you choose svg you need to set -# HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible in IE 9+ (other browsers do not have this requirement). - -DOT_IMAGE_FORMAT = png - -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. -# Note that this requires a modern browser other than Internet Explorer. -# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you -# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible. Older versions of IE do not have SVG support. - -INTERACTIVE_SVG = NO - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = @DOXYGEN_DOT_EXECUTABLE_PATH@ - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the -# \mscfile command). - -MSCFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = YES - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/libssh/doc/authentication.dox b/libssh/doc/authentication.dox deleted file mode 100644 index 30690e8c..00000000 --- a/libssh/doc/authentication.dox +++ /dev/null @@ -1,375 +0,0 @@ -/** -@page libssh_tutor_authentication Chapter 2: A deeper insight on authentication -@section authentication_details A deeper insight on authentication - -In our guided tour, we merely mentioned that the user needed to authenticate. -We didn't explain much in detail how that was supposed to happen. -This chapter explains better the four authentication methods: with public keys, -with a password, with challenges and responses (keyboard-interactive), and with -no authentication at all. - -If your software is supposed to connect to an arbitrary server, then you -might need to support all authentication methods. If your software will -connect only to a given server, then it might be enough for your software -to support only the authentication methods used by that server. If you are -the administrator of the server, it might be your call to choose those -authentication methods. - -It is not the purpose of this document to review in detail the advantages -and drawbacks of each authentication method. You are therefore invited -to read the abundant documentation on this topic to fully understand the -advantages and security risks linked to each method. - - -@subsection pubkeys Authenticating with public keys - -libssh is fully compatible with the openssh public and private keys. You -can either use the automatic public key authentication method provided by -libssh, or roll your own using the public key functions. - -The process of authenticating by public key to a server is the following: - - you scan a list of files that contain public keys. each key is sent to - the SSH server, until the server acknowledges a key (a key it knows can be - used to authenticate the user). - - then, you retrieve the private key for this key and send a message - proving that you know that private key. - -The function ssh_userauth_autopubkey() does this using the available keys in -"~/.ssh/". The return values are the following: - - SSH_AUTH_ERROR: some serious error happened during authentication - - SSH_AUTH_DENIED: no key matched - - SSH_AUTH_SUCCESS: you are now authenticated - - SSH_AUTH_PARTIAL: some key matched but you still have to provide an other - mean of authentication (like a password). - -The ssh_userauth_publickey_auto() function also tries to authenticate using the -SSH agent, if you have one running, or the "none" method otherwise. - -If you wish to authenticate with public key by your own, follow these steps: - - Retrieve the public key with ssh_import_pubkey_file(). - - Offer the public key to the SSH server using ssh_userauth_try_publickey(). - If the return value is SSH_AUTH_SUCCESS, the SSH server accepts to - authenticate using the public key and you can go to the next step. - - Retrieve the private key, using the ssh_pki_import_privkey_file() function. - If a passphrase is needed, either the passphrase specified as argument or - a callback will be used. - - Authenticate using ssh_userauth_publickey() with your private key. - - Do not forget cleaning up memory using ssh_key_free(). - -Here is a minimalistic example of public key authentication: - -@code -int authenticate_pubkey(ssh_session session) -{ - int rc; - - rc = ssh_userauth_publickey_auto(session, NULL); - - if (rc == SSH_AUTH_ERROR) - { - fprintf(stderr, "Authentication failed: %s\n", - ssh_get_error(session)); - return SSH_AUTH_ERROR; - } - - return rc; -} -@endcode - -@see ssh_userauth_publickey_auto() -@see ssh_userauth_try_publickey() -@see ssh_userauth_publickey() -@see ssh_pki_import_pubkey_file() -@see ssh_pki_import_privkey_file() -@see ssh_key_free() - - -@subsection password Authenticating with a password - -The function ssh_userauth_password() serves the purpose of authenticating -using a password. It will return SSH_AUTH_SUCCESS if the password worked, -or one of other constants otherwise. It's your work to ask the password -and to deallocate it in a secure manner. - -If your server complains that the password is wrong, but you can still -authenticate using openssh's client (issuing password), it's probably -because openssh only accept keyboard-interactive. Switch to -keyboard-interactive authentication, or try to configure plain text passwords -on the SSH server. - -Here is a small example of password authentication: - -@code -int authenticate_password(ssh_session session) -{ - char *password; - int rc; - - password = getpass("Enter your password: "); - rc = ssh_userauth_password(session, NULL, password); - if (rc == SSH_AUTH_ERROR) - { - fprintf(stderr, "Authentication failed: %s\n", - ssh_get_error(session)); - return SSH_AUTH_ERROR; - } - - return rc; -} -@endcode - -@see ssh_userauth_password - - -@subsection keyb_int The keyboard-interactive authentication method - -The keyboard-interactive method is, as its name tells, interactive. The -server will issue one or more challenges that the user has to answer, -until the server takes an authentication decision. - -ssh_userauth_kbdint() is the the main keyboard-interactive function. -It will return SSH_AUTH_SUCCESS,SSH_AUTH_DENIED, SSH_AUTH_PARTIAL, -SSH_AUTH_ERROR, or SSH_AUTH_INFO, depending on the result of the request. - -The keyboard-interactive authentication method of SSH2 is a feature that -permits the server to ask a certain number of questions in an interactive -manner to the client, until it decides to accept or deny the login. - -To begin, you call ssh_userauth_kbdint() (just set user and submethods to -NULL) and store the answer. - -If the answer is SSH_AUTH_INFO, it means that the server has sent a few -questions that you should ask the user. You can retrieve these questions -with the following functions: ssh_userauth_kbdint_getnprompts(), -ssh_userauth_kbdint_getname(), ssh_userauth_kbdint_getinstruction(), and -ssh_userauth_kbdint_getprompt(). - -Set the answer for each question in the challenge using -ssh_userauth_kbdint_setanswer(). - -Then, call again ssh_userauth_kbdint() and start the process again until -these functions returns something else than SSH_AUTH_INFO. - -Here are a few remarks: - - Even the first call can return SSH_AUTH_DENIED or SSH_AUTH_SUCCESS. - - The server can send an empty question set (this is the default behavior - on my system) after you have sent the answers to the first questions. - You must still parse the answer, it might contain some - message from the server saying hello or such things. Just call - ssh_userauth_kbdint() until needed. - - The meaning of "name", "prompt", "instruction" may be a little - confusing. An explanation is given in the RFC section that follows. - -Here is a little note about how to use the information from -keyboard-interactive authentication, coming from the RFC itself (rfc4256): - -@verbatim - - 3.3 User Interface Upon receiving a request message, the client SHOULD - prompt the user as follows: A command line interface (CLI) client SHOULD - print the name and instruction (if non-empty), adding newlines. Then for - each prompt in turn, the client SHOULD display the prompt and read the - user input. - - A graphical user interface (GUI) client has many choices on how to prompt - the user. One possibility is to use the name field (possibly prefixed - with the application's name) as the title of a dialog window in which - the prompt(s) are presented. In that dialog window, the instruction field - would be a text message, and the prompts would be labels for text entry - fields. All fields SHOULD be presented to the user, for example an - implementation SHOULD NOT discard the name field because its windows lack - titles; it SHOULD instead find another way to display this information. If - prompts are presented in a dialog window, then the client SHOULD NOT - present each prompt in a separate window. - - All clients MUST properly handle an instruction field with embedded - newlines. They SHOULD also be able to display at least 30 characters for - the name and prompts. If the server presents names or prompts longer than 30 - characters, the client MAY truncate these fields to the length it can - display. If the client does truncate any fields, there MUST be an obvious - indication that such truncation has occured. - - The instruction field SHOULD NOT be truncated. Clients SHOULD use control - character filtering as discussed in [SSH-ARCH] to avoid attacks by - including terminal control characters in the fields to be displayed. - - For each prompt, the corresponding echo field indicates whether or not - the user input should be echoed as characters are typed. Clients SHOULD - correctly echo/mask user input for each prompt independently of other - prompts in the request message. If a client does not honor the echo field - for whatever reason, then the client MUST err on the side of - masking input. A GUI client might like to have a checkbox toggling - echo/mask. Clients SHOULD NOT add any additional characters to the prompt - such as ": " (colon-space); the server is responsible for supplying all - text to be displayed to the user. Clients MUST also accept empty responses - from the user and pass them on as empty strings. -@endverbatim - -The following example shows how to perform keyboard-interactive authentication: - -@code -int authenticate_kbdint(ssh_session session) -{ - int rc; - - rc = ssh_userauth_kbdint(session, NULL, NULL); - while (rc == SSH_AUTH_INFO) - { - const char *name, *instruction; - int nprompts, iprompt; - - name = ssh_userauth_kbdint_getname(session); - instruction = ssh_userauth_kbdint_getinstruction(session); - nprompts = ssh_userauth_kbdint_getnprompts(session); - - if (strlen(name) > 0) - printf("%s\n", name); - if (strlen(instruction) > 0) - printf("%s\n", instruction); - for (iprompt = 0; iprompt < nprompts; iprompt++) - { - const char *prompt; - char echo; - - prompt = ssh_userauth_kbdint_getprompt(session, iprompt, &echo); - if (echo) - { - char buffer[128], *ptr; - - printf("%s", prompt); - if (fgets(buffer, sizeof(buffer), stdin) == NULL) - return SSH_AUTH_ERROR; - buffer[sizeof(buffer) - 1] = '\0'; - if ((ptr = strchr(buffer, '\n')) != NULL) - *ptr = '\0'; - if (ssh_userauth_kbdint_setanswer(session, iprompt, buffer) < 0) - return SSH_AUTH_ERROR; - memset(buffer, 0, strlen(buffer)); - } - else - { - char *ptr; - - ptr = getpass(prompt); - if (ssh_userauth_kbdint_setanswer(session, iprompt, ptr) < 0) - return SSH_AUTH_ERROR; - } - } - rc = ssh_userauth_kbdint(session, NULL, NULL); - } - return rc; -} -@endcode - -@see ssh_userauth_kbdint() -@see ssh_userauth_kbdint_getnprompts() -@see ssh_userauth_kbdint_getname() -@see ssh_userauth_kbdint_getinstruction() -@see ssh_userauth_kbdint_getprompt() -@see ssh_userauth_kbdint_setanswer() - - -@subsection none Authenticating with "none" method - -The primary purpose of the "none" method is to get authenticated **without** -any credential. Don't do that, use one of the other authentication methods, -unless you really want to grant anonymous access. - -If the account has no password, and if the server is configured to let you -pass, ssh_userauth_none() might answer SSH_AUTH_SUCCESS. - -The following example shows how to perform "none" authentication: - -@code -int authenticate_kbdint(ssh_session session) -{ - int rc; - - rc = ssh_userauth_none(session, NULL); - return rc; -} -@endcode - -@subsection auth_list Getting the list of supported authentications - -You are not meant to choose a given authentication method, you can -let the server tell you which methods are available. Once you know them, -you try them one after the other. - -The following example shows how to get the list of available authentication -methods with ssh_userauth_list() and how to use the result: - -@code -int test_several_auth_methods(ssh_session session) -{ - int method, rc; - - rc = ssh_userauth_none(session, NULL); - if (rc != SSH_AUTH_SUCCESS) { - return rc; - } - - method = ssh_userauth_list(session, NULL); - - if (method & SSH_AUTH_METHOD_NONE) - { // For the source code of function authenticate_none(), - // refer to the corresponding example - rc = authenticate_none(session); - if (rc == SSH_AUTH_SUCCESS) return rc; - } - if (method & SSH_AUTH_METHOD_PUBLICKEY) - { // For the source code of function authenticate_pubkey(), - // refer to the corresponding example - rc = authenticate_pubkey(session); - if (rc == SSH_AUTH_SUCCESS) return rc; - } - if (method & SSH_AUTH_METHOD_INTERACTIVE) - { // For the source code of function authenticate_kbdint(), - // refer to the corresponding example - rc = authenticate_kbdint(session); - if (rc == SSH_AUTH_SUCCESS) return rc; - } - if (method & SSH_AUTH_METHOD_PASSWORD) - { // For the source code of function authenticate_password(), - // refer to the corresponding example - rc = authenticate_password(session); - if (rc == SSH_AUTH_SUCCESS) return rc; - } - return SSH_AUTH_ERROR; -} -@endcode - - -@subsection banner Getting the banner - -The SSH server might send a banner, which you can retrieve with -ssh_get_issue_banner(), then display to the user. - -The following example shows how to retrieve and dispose the issue banner: - -@code -int display_banner(ssh_session session) -{ - int rc; - char *banner; - -/* - *** Does not work without calling ssh_userauth_none() first *** - *** That will be fixed *** -*/ - rc = ssh_userauth_none(session, NULL); - if (rc == SSH_AUTH_ERROR) - return rc; - - banner = ssh_get_issue_banner(session); - if (banner) - { - printf("%s\n", banner); - free(banner); - } - - return rc; -} -@endcode - -*/ diff --git a/libssh/doc/command.dox b/libssh/doc/command.dox deleted file mode 100644 index 76113543..00000000 --- a/libssh/doc/command.dox +++ /dev/null @@ -1,94 +0,0 @@ -/** -@page libssh_tutor_command Chapter 4: Passing a remote command -@section remote_command Passing a remote command - -Previous chapter has shown how to open a full shell session, with an attached -terminal or not. If you only need to execute a command on the remote end, -you don't need all that complexity. - -The method described here is suited for executing only one remote command. -If you need to issue several commands in a row, you should consider using -a non-interactive remote shell, as explained in previous chapter. - -@see shell - - -@subsection exec_remote Executing a remote command - -The first steps for executing a remote command are identical to those -for opening remote shells. You first need a SSH channel, and then -a SSH session that uses this channel: - -@code -int show_remote_files(ssh_session session) -{ - ssh_channel channel; - int rc; - - channel = ssh_channel_new(session); - if (channel == NULL) return SSH_ERROR; - - rc = ssh_channel_open_session(channel); - if (rc != SSH_OK) - { - ssh_channel_free(channel); - return rc; - } -@endcode - -Once a session is open, you can start the remote command with -ssh_channel_request_exec(): - -@code - rc = ssh_channel_request_exec(channel, "ls -l"); - if (rc != SSH_OK) - { - ssh_channel_close(channel); - ssh_channel_free(channel); - return rc; - } -@endcode - -If the remote command displays data, you get them with ssh_channel_read(). -This function returns the number of bytes read. If there is no more -data to read on the channel, this function returns 0, and you can go to next step. -If an error has been encountered, it returns a negative value: - -@code - char buffer[256]; - unsigned int nbytes; - - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - while (nbytes > 0) - { - if (fwrite(buffer, 1, nbytes, stdout) != nbytes) - { - ssh_channel_close(channel); - ssh_channel_free(channel); - return SSH_ERROR; - } - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - } - - if (nbytes < 0) - { - ssh_channel_close(channel); - ssh_channel_free(channel); - return SSH_ERROR; - } -@endcode - -Once you read the result of the remote command, you send an -end-of-file to the channel, close it, and free the memory -that it used: - -@code - ssh_channel_send_eof(channel); - ssh_channel_close(channel); - ssh_channel_free(channel); - - return SSH_OK; -} -@endcode - -*/ diff --git a/libssh/doc/curve25519-sha256@libssh.org.txt b/libssh/doc/curve25519-sha256@libssh.org.txt deleted file mode 100644 index e68dc5e6..00000000 --- a/libssh/doc/curve25519-sha256@libssh.org.txt +++ /dev/null @@ -1,119 +0,0 @@ -curve25519-sha256@libssh.org.txt Aris Adamantiadis - 21/9/2013 - -1. Introduction - -This document describes the key exchange methode curve25519-sha256@libssh.org -for SSH version 2 protocol. It is provided as an alternative to the existing -key exchange mechanisms based on either Diffie-Hellman or Elliptic Curve Diffie- -Hellman [RFC5656]. -The reason is the following : During summer of 2013, revelations from ex- -consultant at NSA Edward Snowden gave proof that NSA willingly inserts backdoors -into softwares, hardware components and published standards. While it is still -believed that the mathematics behind ECC cryptography are still sound and solid, -some people (including Bruce Schneier [SCHNEIER]), showed their lack of confidence -in NIST-published curves such as nistp256, nistp384, nistp521, for which constant -parameters (including the generator point) are defined without explanation. It -is also believed that NSA had a word to say in their definition. These curves -are not the most secure or fastest possible for their key sizes [DJB], and -researchers think it is possible that NSA have ways of cracking NIST curves. -It is also interesting to note that SSH belongs to the list of protocols the NSA -claims to be able to eavesdrop. Having a secure replacement would make passive -attacks much harder if such a backdoor exists. - -However an alternative exists in the form of Curve25519. This algorithm has been -proposed in 2006 by DJB [Curve25519]. Its main stengths are its speed, its -constant-time run time (and resistance against side-channel attacks), and its -lack of nebulous hard-coded constants. - -The reference version being used in this document is the one described in -[Curve25519] as implemented in the library NaCl [NaCl]. -This document does not attempts to provide alternatives to the ecdsa-sha1-* -authentication keys. - -2. Key exchange - -The key exchange procedure is very similar to the one described chapter 4 of -[RFC5656]. Public ephemeral keys are transmitted over SSH encapsulated into -standard SSH strings. - -The following is an overview of the key exchange process: - -Client Server ------- ------ -Generate ephemeral key pair. -SSH_MSG_KEX_ECDH_INIT --------> - Verify that client public key - length is 32 bytes. - Generate ephemeral key pair. - Compute shared secret. - Generate and sign exchange hash. - <-------- SSH_MSG_KEX_ECDH_REPLY -Verify that server public key length is 32 bytes. -* Verify host keys belong to server. -Compute shared secret. -Generate exchange hash. -Verify server's signature. - -* Optional but strongly recommanded as this protects against MITM attacks. - -This is implemented using the same messages as described in RFC5656 chapter 4 - -3. Method Name - -The name of this key exchange method is "curve25519-sha256@libssh.org". - -4. Implementation considerations - -The whole method is based on the curve25519 scalar multiplication. In this -method, a private key is a scalar of 256 bits, and a public key is a point -of 256 bits. - -4.1. Private key generation - -A 32 bytes private key should be generated for each new connection, - using a secure PRNG. The following actions must be done on the private key: - mysecret[0] &= 248; - mysecret[31] &= 127; - mysecret[31] |= 64; -In order to keep the key valid. However, many cryptographic libraries will do -this automatically. -It should be noted that, in opposition to NIST curves, no special validation -should be done to ensure the result is a valid and secure private key. - -4.2 Public key generation - -The 32 bytes public key of either a client or a server must be generated using -the 32 bytes private key and a common generator base. This base is defined as 9 -followed by all zeroes: - const unsigned char basepoint[32] = {9}; - -The public key is calculated using the cryptographic scalar multiplication: - const unsigned char privkey[32]; - unsigned char pubkey[32]; - crypto_scalarmult (pubkey, privkey, basepoint); -However some cryptographic libraries may provide a combined function: - crypto_scalarmult_base (pubkey, privkey); - -It should be noted that, in opposition to NIST curves, no special validation -should be done to ensure the received public keys are valid curves point. The -Curve25519 algorithm ensure that every possible public key maps to a valid -ECC Point. - -4.3 Shared secret generation - -The shared secret, k, is defined in SSH specifications to be a big integer. -This number is calculated using the following procedure: - - X is the 32 bytes point obtained by the scalar multiplication of the other - side's public key and the local private key scalar. - - The whole 32 bytes of the number X are then converted into a big integer k. - This conversion follows the network byte order. This step differs from - RFC5656. - -[RFC5656] http://tools.ietf.org/html/rfc5656 -[SCHNEIER] https://www.schneier.com/blog/archives/2013/09/the_nsa_is_brea.html#c1675929 -[DJB] http://cr.yp.to/talks/2013.05.31/slides-dan+tanja-20130531-4x3.pdf -[Curve25519] "Curve25519: new Diffie-Hellman speed records." - http://cr.yp.to/ecdh/curve25519-20060209.pdf \ No newline at end of file diff --git a/libssh/doc/forwarding.dox b/libssh/doc/forwarding.dox deleted file mode 100644 index be4ab94e..00000000 --- a/libssh/doc/forwarding.dox +++ /dev/null @@ -1,230 +0,0 @@ -/** -@page libssh_tutor_forwarding Chapter 7: Forwarding connections (tunnel) -@section forwarding_connections Forwarding connections - -Port forwarding comes in SSH protocol in two different flavours: -direct or reverse port forwarding. Direct port forwarding is also -named local port forwardind, and reverse port forwarding is also called -remote port forwarding. SSH also allows X11 tunnels. - - - -@subsection forwarding_direct Direct port forwarding - -Direct port forwarding is from client to server. The client opens a tunnel, -and forwards whatever data to the server. Then, the server connects to an -end point. The end point can reside on another machine or on the SSH -server itself. - -Example of use of direct port forwarding: -@verbatim -Mail client application Google Mail - | ^ - 5555 (arbitrary) | - | 143 (IMAP2) - V | - SSH client =====> SSH server - -Legend: ---P-->: port connexion through port P -=====>: SSH tunnel -@endverbatim -A mail client connects to port 5555 of a client. An encrypted tunnel is -established to the server. The server connects to port 143 of Google Mail (the -end point). Now the local mail client can retreive mail. - - -@subsection forwarding_reverse Reverse port forwarding - -The reverse forwarding is slightly different. It goes from server to client, -even though the client has the initiative of establishing the tunnel. -Once the tunnel is established, the server will listen on a port. Whenever -a connection to this port is made, the server forwards the data to the client. - -Example of use of reverse port forwarding: -@verbatim - Local mail server Mail client application - ^ | - | 5555 (arbitrary) - 143 (IMAP2) | - | V - SSH client <===== SSH server - -Legend: ---P-->: port connexion through port P -=====>: SSH tunnel -@endverbatim -In this example, the SSH client establishes the tunnel, -but it is used to forward the connections established at -the server to the client. - - -@subsection forwarding_x11 X11 tunnels - -X11 tunnels allow a remote application to display locally. - -Example of use of X11 tunnels: -@verbatim - Local display Graphical application - (X11 server) (X11 client) - ^ | - | V - SSH client <===== SSH server - -Legend: ------>: X11 connection through X11 display number -=====>: SSH tunnel -@endverbatim -The SSH tunnel is established by the client. - -How to establish X11 tunnels with libssh has already been described in -this tutorial. - -@see x11 - - -@subsection libssh_direct Doing direct port forwarding with libssh - -To do direct port forwarding, call function ssh_channel_open_forward(): - - you need a separate channel for the tunnel as first parameter; - - second and third parameters are the remote endpoint; - - fourth and fifth parameters are sent to the remote server - so that they can be logged on that server. - -If you don't plan to forward the data you will receive to any local port, -just put fake values like "localhost" and 5555 as your local host and port. - -The example below shows how to open a direct channel that would be -used to retrieve google's home page from the remote SSH server. - -@code -int direct_forwarding(ssh_session session) -{ - ssh_channel forwarding_channel; - int rc; - char *http_get = "GET / HTTP/1.1\nHost: www.google.com\n\n"; - int nbytes, nwritten; - - forwarding_channel = ssh_channel_new(session); - if (forwarding_channel == NULL) { - return rc; - } - - rc = ssh_channel_open_forward(forwarding_channel, - "www.google.com", 80, - "localhost", 5555); - if (rc != SSH_OK) - { - ssh_channel_free(forwarding_channel); - return rc; - } - - nbytes = strlen(http_get); - nwritten = ssh_channel_write(forwarding_channel, - http_get, - nbytes); - if (nbytes != nwritten) - { - ssh_channel_free(forwarding_channel); - return SSH_ERROR; - } - - ... - - ssh_channel_free(forwarding_channel); - return SSH_OK; -} -@endcode - -The data sent by Google can be retrieved for example with ssh_select() -and ssh_channel_read(). Goggle's home page can then be displayed on the -local SSH client, saved into a local file, made available on a local port, -or whatever use you have for it. - - -@subsection libssh_reverse Doing reverse port forwarding with libssh - -To do reverse port forwarding, call ssh_channel_listen_forward(), -then ssh_channel_accept_forward(). - -When you call ssh_channel_listen_forward(), you can let the remote server -chose the non-priviledged port it should listen to. Otherwise, you can chose -your own priviledged or non-priviledged port. Beware that you should have -administrative priviledges on the remote server to open a priviledged port -(port number < 1024). - -Below is an example of a very rough web server waiting for connections on port -8080 of remote SSH server. The incoming connections are passed to the -local libssh application, which handles them: - -@code -int web_server(ssh_session session) -{ - int rc; - ssh_channel channel; - char buffer[256]; - int nbytes, nwritten; - int port = 0; - char *helloworld = "" -"HTTP/1.1 200 OK\n" -"Content-Type: text/html\n" -"Content-Length: 113\n" -"\n" -"\n" -" \n" -" Hello, World!\n" -" \n" -" \n" -"

    Hello, World!

    \n" -" \n" -"\n"; - - rc = ssh_channel_listen_forward(session, NULL, 8080, NULL); - if (rc != SSH_OK) - { - fprintf(stderr, "Error opening remote port: %s\n", - ssh_get_error(session)); - return rc; - } - - channel = ssh_channel_accept_forward(session, 60000, &port); - if (channel == NULL) - { - fprintf(stderr, "Error waiting for incoming connection: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - while (1) - { - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - if (nbytes < 0) - { - fprintf(stderr, "Error reading incoming data: %s\n", - ssh_get_error(session)); - ssh_channel_send_eof(channel); - ssh_channel_free(channel); - return SSH_ERROR; - } - if (strncmp(buffer, "GET /", 5)) continue; - - nbytes = strlen(helloworld); - nwritten = ssh_channel_write(channel, helloworld, nbytes); - if (nwritten != nbytes) - { - fprintf(stderr, "Error sending answer: %s\n", - ssh_get_error(session)); - ssh_channel_send_eof(channel); - ssh_channel_free(channel); - return SSH_ERROR; - } - printf("Sent answer\n"); - } - - ssh_channel_send_eof(channel); - ssh_channel_free(channel); - return SSH_OK; -} -@endcode - -*/ diff --git a/libssh/doc/guided_tour.dox b/libssh/doc/guided_tour.dox deleted file mode 100644 index 2fa906e0..00000000 --- a/libssh/doc/guided_tour.dox +++ /dev/null @@ -1,455 +0,0 @@ -/** -@page libssh_tutor_guided_tour Chapter 1: A typical SSH session -@section ssh_session A typical SSH session - -A SSH session goes through the following steps: - - - Before connecting to the server, you can set up if you wish one or other - server public key authentication, i.e. DSA or RSA. You can choose - cryptographic algorithms you trust and compression algorithms if any. You - must of course set up the hostname. - - - The connection is established. A secure handshake is made, and resulting from - it, a public key from the server is gained. You MUST verify that the public - key is legitimate, using for instance the MD5 fingerprint or the known hosts - file. - - - The client must authenticate: the classical ways are password, or - public keys (from dsa and rsa key-pairs generated by openssh). - If a SSH agent is running, it is possible to use it. - - - Now that the user has been authenticated, you must open one or several - channels. Channels are different subways for information into a single ssh - connection. Each channel has a standard stream (stdout) and an error stream - (stderr). You can theoretically open an infinity of channels. - - - With the channel you opened, you can do several things: - - Execute a single command. - - Open a shell. You may want to request a pseudo-terminal before. - - Invoke the sftp subsystem to transfer files. - - Invoke the scp subsystem to transfer files. - - Invoke your own subsystem. This is outside the scope of this document, - but can be done. - - - When everything is finished, just close the channels, and then the connection. - -The sftp and scp subsystems use channels, but libssh hides them to -the programmer. If you want to use those subsystems, instead of a channel, -you'll usually open a "sftp session" or a "scp session". - - -@subsection setup Creating the session and setting options - -The most important object in a SSH connection is the SSH session. In order -to allocate a new SSH session, you use ssh_new(). Don't forget to -always verify that the allocation successed. -@code -#include -#include - -int main() -{ - ssh_session my_ssh_session = ssh_new(); - if (my_ssh_session == NULL) - exit(-1); - ... - ssh_free(my_ssh_session); -} -@endcode - -libssh follows the allocate-it-deallocate-it pattern. Each object that you allocate -using xxxxx_new() must be deallocated using xxxxx_free(). In this case, ssh_new() -does the allocation and ssh_free() does the contrary. - -The ssh_options_set() function sets the options of the session. The most important options are: - - SSH_OPTIONS_HOST: the name of the host you want to connect to - - SSH_OPTIONS_PORT: the used port (default is port 22) - - SSH_OPTIONS_USER: the system user under which you want to connect - - SSH_OPTIONS_LOG_VERBOSITY: the quantity of messages that are printed - -The complete list of options can be found in the documentation of ssh_options_set(). -The only mandatory option is SSH_OPTIONS_HOST. If you don't use SSH_OPTIONS_USER, -the local username of your account will be used. - -Here is a small example of how to use it: - -@code -#include -#include - -int main() -{ - ssh_session my_ssh_session; - int verbosity = SSH_LOG_PROTOCOL; - int port = 22; - - my_ssh_session = ssh_new(); - if (my_ssh_session == NULL) - exit(-1); - - ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost"); - ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT, &port); - - ... - - ssh_free(my_ssh_session); -} -@endcode - -Please notice that all parameters are passed to ssh_options_set() as pointers, -even if you need to set an integer value. - -@see ssh_new -@see ssh_free -@see ssh_options_set -@see ssh_options_parse_config -@see ssh_options_copy -@see ssh_options_getopt - - -@subsection connect Connecting to the server - -Once all settings have been made, you can connect using ssh_connect(). That -function will return SSH_OK if the connection worked, SSH_ERROR otherwise. - -You can get the English error string with ssh_get_error() in order to show the -user what went wrong. Then, use ssh_disconnect() when you want to stop -the session. - -Here's an example: - -@code -#include -#include -#include - -int main() -{ - ssh_session my_ssh_session; - int rc; - - my_ssh_session = ssh_new(); - if (my_ssh_session == NULL) - exit(-1); - - ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost"); - - rc = ssh_connect(my_ssh_session); - if (rc != SSH_OK) - { - fprintf(stderr, "Error connecting to localhost: %s\n", - ssh_get_error(my_ssh_session)); - exit(-1); - } - - ... - - ssh_disconnect(my_ssh_session); - ssh_free(my_ssh_session); -} -@endcode - - -@subsection serverauth Authenticating the server - -Once you're connected, the following step is mandatory: you must check that the server -you just connected to is known and safe to use (remember, SSH is about security and -authentication). - -There are two ways of doing this: - - The first way (recommended) is to use the ssh_is_server_known() - function. This function will look into the known host file - (~/.ssh/known_hosts on UNIX), look for the server hostname's pattern, - and determine whether this host is present or not in the list. - - The second way is to use ssh_get_pubkey_hash() to get a binary version - of the public key hash value. You can then use your own database to check - if this public key is known and secure. - -You can also use the ssh_get_pubkey_hash() to show the public key hash -value to the user, in case he knows what the public key hash value is -(some paranoid people write their public key hash values on paper before -going abroad, just in case ...). - -If the remote host is being used to for the first time, you can ask the user whether -he/she trusts it. Once he/she concluded that the host is valid and worth being -added in the known hosts file, you use ssh_write_knownhost() to register it in -the known hosts file, or any other way if you use your own database. - -The following example is part of the examples suite available in the -examples/ directory: - -@code -#include -#include - -int verify_knownhost(ssh_session session) -{ - int state, hlen; - unsigned char *hash = NULL; - char *hexa; - char buf[10]; - - state = ssh_is_server_known(session); - - hlen = ssh_get_pubkey_hash(session, &hash); - if (hlen < 0) - return -1; - - switch (state) - { - case SSH_SERVER_KNOWN_OK: - break; /* ok */ - - case SSH_SERVER_KNOWN_CHANGED: - fprintf(stderr, "Host key for server changed: it is now:\n"); - ssh_print_hexa("Public key hash", hash, hlen); - fprintf(stderr, "For security reasons, connection will be stopped\n"); - free(hash); - return -1; - - case SSH_SERVER_FOUND_OTHER: - fprintf(stderr, "The host key for this server was not found but an other" - "type of key exists.\n"); - fprintf(stderr, "An attacker might change the default server key to" - "confuse your client into thinking the key does not exist\n"); - free(hash); - return -1; - - case SSH_SERVER_FILE_NOT_FOUND: - fprintf(stderr, "Could not find known host file.\n"); - fprintf(stderr, "If you accept the host key here, the file will be" - "automatically created.\n"); - /* fallback to SSH_SERVER_NOT_KNOWN behavior */ - - case SSH_SERVER_NOT_KNOWN: - hexa = ssh_get_hexa(hash, hlen); - fprintf(stderr,"The server is unknown. Do you trust the host key?\n"); - fprintf(stderr, "Public key hash: %s\n", hexa); - free(hexa); - if (fgets(buf, sizeof(buf), stdin) == NULL) - { - free(hash); - return -1; - } - if (strncasecmp(buf, "yes", 3) != 0) - { - free(hash); - return -1; - } - if (ssh_write_knownhost(session) < 0) - { - fprintf(stderr, "Error %s\n", strerror(errno)); - free(hash); - return -1; - } - break; - - case SSH_SERVER_ERROR: - fprintf(stderr, "Error %s", ssh_get_error(session)); - free(hash); - return -1; - } - - free(hash); - return 0; -} -@endcode - -@see ssh_connect -@see ssh_disconnect -@see ssh_get_error -@see ssh_get_error_code -@see ssh_get_pubkey_hash -@see ssh_is_server_known -@see ssh_write_knownhost - - -@subsection auth Authenticating the user - -The authentication process is the way a service provider can identify a -user and verify his/her identity. The authorization process is about enabling -the authenticated user the access to ressources. In SSH, the two concepts -are linked. After authentication, the server can grant the user access to -several ressources such as port forwarding, shell, sftp subsystem, and so on. - -libssh supports several methods of authentication: - - "none" method. This method allows to get the available authentications - methods. It also gives the server a chance to authenticate the user with - just his/her login. Some very old hardware uses this feature to fallback - the user on a "telnet over SSH" style of login. - - password method. A password is sent to the server, which accepts it or not. - - keyboard-interactive method. The server sends several challenges to the - user, who must answer correctly. This makes possible the authentication - via a codebook for instance ("give code at 23:R on page 3"). - - public key method. The host knows the public key of the user, and the - user must prove he knows the associated private key. This can be done - manually, or delegated to the SSH agent as we'll see later. - -All these methods can be combined. You can for instance force the user to -authenticate with at least two of the authentication methods. In that case, -one speaks of "Partial authentication". A partial authentication is a -response from authentication functions stating that your credential was -accepted, but yet another one is required to get in. - -The example below shows an authentication with password: - -@code -#include -#include -#include - -int main() -{ - ssh_session my_ssh_session; - int rc; - char *password; - - // Open session and set options - my_ssh_session = ssh_new(); - if (my_ssh_session == NULL) - exit(-1); - ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "localhost"); - - // Connect to server - rc = ssh_connect(my_ssh_session); - if (rc != SSH_OK) - { - fprintf(stderr, "Error connecting to localhost: %s\n", - ssh_get_error(my_ssh_session)); - ssh_free(my_ssh_session); - exit(-1); - } - - // Verify the server's identity - // For the source code of verify_knowhost(), check previous example - if (verify_knownhost(my_ssh_session) < 0) - { - ssh_disconnect(my_ssh_session); - ssh_free(my_ssh_session); - exit(-1); - } - - // Authenticate ourselves - password = getpass("Password: "); - rc = ssh_userauth_password(my_ssh_session, NULL, password); - if (rc != SSH_AUTH_SUCCESS) - { - fprintf(stderr, "Error authenticating with password: %s\n", - ssh_get_error(my_ssh_session)); - ssh_disconnect(my_ssh_session); - ssh_free(my_ssh_session); - exit(-1); - } - - ... - - ssh_disconnect(my_ssh_session); - ssh_free(my_ssh_session); -} -@endcode - -@see @ref authentication_details - - -@subsection using_ssh Doing something - -At this point, the authenticity of both server and client is established. -Time has come to take advantage of the many possibilities offered by the SSH -protocol: execute a remote command, open remote shells, transfer files, -forward ports, etc. - -The example below shows how to execute a remote command: - -@code -int show_remote_processes(ssh_session session) -{ - ssh_channel channel; - int rc; - char buffer[256]; - unsigned int nbytes; - - channel = ssh_channel_new(session); - if (channel == NULL) - return SSH_ERROR; - - rc = ssh_channel_open_session(channel); - if (rc != SSH_OK) - { - ssh_channel_free(channel); - return rc; - } - - rc = ssh_channel_request_exec(channel, "ps aux"); - if (rc != SSH_OK) - { - ssh_channel_close(channel); - ssh_channel_free(channel); - return rc; - } - - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - while (nbytes > 0) - { - if (write(1, buffer, nbytes) != nbytes) - { - ssh_channel_close(channel); - ssh_channel_free(channel); - return SSH_ERROR; - } - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - } - - if (nbytes < 0) - { - ssh_channel_close(channel); - ssh_channel_free(channel); - return SSH_ERROR; - } - - ssh_channel_send_eof(channel); - ssh_channel_close(channel); - ssh_channel_free(channel); - - return SSH_OK; -} -@endcode - -@see @ref opening_shell -@see @ref remote_command -@see @ref sftp_subsystem -@see @ref scp_subsystem - - -@subsection errors Handling the errors - -All the libssh functions which return an error value also set an English error message -describing the problem. - -Error values are typically SSH_ERROR for integer values, or NULL for pointers. - -The function ssh_get_error() returns a pointer to the static error message. - -ssh_error_code() returns the error code number : SSH_NO_ERROR, -SSH_REQUEST_DENIED, SSH_INVALID_REQUEST, SSH_CONNECTION_LOST, SSH_FATAL, -or SSH_INVALID_DATA. SSH_REQUEST_DENIED means the ssh server refused your -request, but the situation is recoverable. The others mean something happened -to the connection (some encryption problems, server problems, ...). -SSH_INVALID_REQUEST means the library got some garbage from server, but -might be recoverable. SSH_FATAL means the connection has an important -problem and isn't probably recoverable. - -Most of time, the error returned are SSH_FATAL, but some functions -(generaly the ssh_request_xxx ones) may fail because of server denying request. -In these cases, SSH_REQUEST_DENIED is returned. - -ssh_get_error() and ssh_get_error_code() take a ssh_session as a parameter. -That's for thread safety, error messages that can be attached to a session -aren't static anymore. Any error that happens during ssh_options_xxx() -or ssh_connect() (i.e., outside of any session) can be retrieved by -giving NULL as argument. - -The SFTP subsystem has its own error codes, in addition to libssh ones. - - -*/ diff --git a/libssh/doc/introduction.dox b/libssh/doc/introduction.dox deleted file mode 100644 index cd786497..00000000 --- a/libssh/doc/introduction.dox +++ /dev/null @@ -1,49 +0,0 @@ -/** -@page libssh_tutorial The Tutorial -@section introduction Introduction - -libssh is a C library that enables you to write a program that uses the -SSH protocol. With it, you can remotely execute programs, transfer -files, or use a secure and transparent tunnel for your remote programs. -The SSH protocol is encrypted, ensures data integrity, and provides strong -means of authenticating both the server of the client. The library hides -a lot of technical details from the SSH protocol, but this does not -mean that you should not try to know about and understand these details. - -libssh is a Free Software / Open Source project. The libssh library -is distributed under LGPL license. The libssh project has nothing to do with -"libssh2", which is a completly different and independant project. - -libssh can run on top of either libgcrypt or libcrypto, -two general-purpose cryptographic libraries. - -This tutorial concentrates for its main part on the "client" side of libssh. -To learn how to accept incoming SSH connexions (how to write a SSH server), -you'll have to jump to the end of this document. - -This tutorial describes libssh version 0.5.0. This version is a little different -from the 0.4.X series. However, the examples should work with -little changes on versions like 0.4.2 and later. - - -Table of contents: - -@subpage libssh_tutor_guided_tour - -@subpage libssh_tutor_authentication - -@subpage libssh_tutor_shell - -@subpage libssh_tutor_command - -@subpage libssh_tutor_sftp - -@subpage libssh_tutor_scp - -@subpage libssh_tutor_forwarding - -@subpage libssh_tutor_threads - -@subpage libssh_tutor_todo - -*/ diff --git a/libssh/doc/linking.dox b/libssh/doc/linking.dox deleted file mode 100644 index 8cbc415c..00000000 --- a/libssh/doc/linking.dox +++ /dev/null @@ -1,30 +0,0 @@ -/** - -@page libssh_linking The Linking HowTo - -@section dynamic Dynamic Linking - -On UNIX and Windows systems its the same, you need at least the libssh.h -header file and the libssh shared library. - -@section static Static Linking - -@warning The libssh library is licensed under the LGPL! Make sure you -understand what this means to your codebase if you want to distribute -binaries and link statically against LGPL code! - -On UNIX systems linking against the static version of the library is the -same as linking against the shared library. Both have the same name. Some -build system require to use the full path to the static library. - -To be able to compile the application you're developing you need to either pass -LIBSSH_STATIC as a define in the compiler command line or define it before you -include libssh.h. This is required cause the dynamic library needs to specify -the dllimport attribute. - -@code -#define LIBSSH_STATIC 1 -#include -@endcode - -*/ diff --git a/libssh/doc/mainpage.dox b/libssh/doc/mainpage.dox deleted file mode 100644 index 70f774ad..00000000 --- a/libssh/doc/mainpage.dox +++ /dev/null @@ -1,224 +0,0 @@ -/** - -@mainpage - -This is the online reference for developing with the libssh library. It -documents the libssh C API and the C++ wrapper. - -@section main-linking Linking - -We created a small howto how to link libssh against your application, read -@subpage libssh_linking. - -@section main-tutorial Tutorial - -You should start by reading @subpage libssh_tutorial, then reading the documentation of -the interesting functions as you go. - -@section main-features Features - -The libssh library provides: - - - Key Exchange Methods: curve25519-sha256@libssh.org, ecdh-sha2-nistp256, diffie-hellman-group1-sha1, diffie-hellman-group14-sha1 - - Hostkey Types: ecdsa-sha2-nistp256, ssh-dss, ssh-rsa - - Ciphers: aes256-ctr, aes192-ctr, aes128-ctr, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, des-cbc-ssh1, blowfish-cbc, none - - Compression Schemes: zlib, zlib@openssh.com, none - - MAC hashes: hmac-sha1, none - - Authentication: none, password, public-key, hostbased, keyboard-interactive, gssapi-with-mic - - Channels: shell, exec (incl. SCP wrapper), direct-tcpip, subsystem, auth-agent-req@openssh.com - - Global Requests: tcpip-forward, forwarded-tcpip - - Channel Requests: x11, pty, exit-status, signal, exit-signal, keepalive@openssh.com, auth-agent-req@openssh.com - - Subsystems: sftp(version 3), publickey(version 2), OpenSSH Extensions - - SFTP: statvfs@openssh.com, fstatvfs@openssh.com - - Thread-safe: Just don't share sessions - - Non-blocking: it can be used both blocking and non-blocking - - Your sockets: the app hands over the socket, or uses libssh sockets - - OpenSSL or gcrypt: builds with either - -@section main-additional-features Additional Features - - - Client and server support - - SSHv2 and SSHv1 protocol support - - Supports Linux, UNIX, BSD, Solaris, OS/2 and Windows - - Automated test cases with nightly tests - - Event model based on poll(2), or a poll(2)-emulation. - -@section main-copyright Copyright Policy - -libssh is a project with distributed copyright ownership, which means we prefer -the copyright on parts of libssh to be held by individuals rather than -corporations if possible. There are historical legal reasons for this, but one -of the best ways to explain it is that it’s much easier to work with -individuals who have ownership than corporate legal departments if we ever need -to make reasonable compromises with people using and working with libssh. - -We track the ownership of every part of libssh via git, our source code control -system, so we know the provenance of every piece of code that is committed to -libssh. - -So if possible, if you’re doing libssh changes on behalf of a company who -normally owns all the work you do please get them to assign personal copyright -ownership of your changes to you as an individual, that makes things very easy -for us to work with and avoids bringing corporate legal departments into the -picture. - -If you can’t do this we can still accept patches from you owned by your -employer under a standard employment contract with corporate copyright -ownership. It just requires a simple set-up process first. - -We use a process very similar to the way things are done in the Linux Kernel -community, so it should be very easy to get a sign off from your corporate -legal department. The only changes we’ve made are to accommodate the license we -use, which is LGPLv2 (or later) whereas the Linux kernel uses GPLv2. - -The process is called signing. - -How to sign your work ----------------------- - -Once you have permission to contribute to libssh from your employer, simply -email a copy of the following text from your corporate email address to: - -contributing@libssh.org - -@verbatim -libssh Developer's Certificate of Origin. Version 1.0 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the appropriate - version of the GNU General Public License; or - -(b) The contribution is based upon previous work that, to the best of - my knowledge, is covered under an appropriate open source license - and I have the right under that license to submit that work with - modifications, whether created in whole or in part by me, under - the GNU General Public License, in the appropriate version; or - -(c) The contribution was provided directly to me by some other - person who certified (a) or (b) and I have not modified it. - -(d) I understand and agree that this project and the contribution are - public and that a record of the contribution (including all - metadata and personal information I submit with it, including my - sign-off) is maintained indefinitely and may be redistributed - consistent with the libssh Team's policies and the requirements of - the GNU GPL where they are relevant. - -(e) I am granting this work to this project under the terms of the - GNU Lesser General Public License as published by the - Free Software Foundation; either version 2.1 of - the License, or (at the option of the project) any later version. - -http://www.gnu.org/licenses/lgpl-2.1.html -@endverbatim - -We will maintain a copy of that email as a record that you have the rights to -contribute code to libssh under the required licenses whilst working for the -company where the email came from. - -Then when sending in a patch via the normal mechanisms described above, add a -line that states: - -@verbatim - Signed-off-by: Random J Developer -@endverbatim - -using your real name and the email address you sent the original email you used -to send the libssh Developer’s Certificate of Origin to us (sorry, no -pseudonyms or anonymous contributions.) - -That’s it! Such code can then quite happily contain changes that have copyright -messages such as: - -@verbatim - (c) Example Corporation. -@endverbatim - -and can be merged into the libssh codebase in the same way as patches from any -other individual. You don’t need to send in a copy of the libssh Developer’s -Certificate of Origin for each patch, or inside each patch. Just the sign-off -message is all that is required once we’ve received the initial email. - -Have fun and happy libssh hacking! - -The libssh Team - -@section main-rfc Internet standard - -@subsection main-rfc-secsh Secure Shell (SSH) - -The following RFC documents described SSH-2 protcol as an Internet standard. - - - RFC 4250, - The Secure Shell (SSH) Protocol Assigned Numbers - - RFC 4251, - The Secure Shell (SSH) Protocol Architecture - - RFC 4252, - The Secure Shell (SSH) Authentication Protocol - - RFC 4253, - The Secure Shell (SSH) Transport Layer Protocol - - RFC 4254, - The Secure Shell (SSH) Connection Protocol - - RFC 4255, - Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints - - RFC 4256, - Generic Message Exchange Authentication for the Secure Shell Protocol (SSH) - - RFC 4335, - The Secure Shell (SSH) Session Channel Break Extension - - RFC 4344, - The Secure Shell (SSH) Transport Layer Encryption Modes - - RFC 4345, - Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol - -It was later modified and expanded by the following RFCs. - - - RFC 4419, - Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer - Protocol - - RFC 4432, - RSA Key Exchange for the Secure Shell (SSH) Transport Layer Protocol - - RFC 4462, - Generic Security Service Application Program Interface (GSS-API) - Authentication and Key Exchange for the Secure Shell (SSH) Protocol - - RFC 4716, - The Secure Shell (SSH) Public Key File Format - - RFC 5647, - AES Galois Counter Mode for the Secure Shell Transport Layer Protocol - - RFC 5656, - Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer - -Interesting cryptography documents: - - - PKCS #11, PKCS #11 reference documents, describing interface with smartcards. - -@subsection main-rfc-sftp Secure Shell File Transfer Protocol (SFTP) - -The protocol is not an Internet standard but it is still widely implemented. -OpenSSH and most other implementation implement Version 3 of the protocol. We -do the same in libssh. - - - - draft-ietf-secsh-filexfer-02.txt, - SSH File Transfer Protocol - -@subsection main-rfc-extensions Secure Shell Extensions - -The libssh project has an extension to support Curve25519 which is also supported by -the OpenSSH project. - - - curve25519-sha256@libssh.org, - Curve25519-SHA256 for ECDH KEX - -The OpenSSH project has defined some extensions to the protocol. We support some of -them like the statvfs calls in SFTP or the ssh-agent. - - - - OpenSSH's deviations and extensions - - - OpenSSH's ssh-agent - - - OpenSSH's pubkey certificate authentication - -*/ diff --git a/libssh/doc/scp.dox b/libssh/doc/scp.dox deleted file mode 100644 index 1e7db780..00000000 --- a/libssh/doc/scp.dox +++ /dev/null @@ -1,268 +0,0 @@ -/** -@page libssh_tutor_scp Chapter 6: The SCP subsystem -@section scp_subsystem The SCP subsystem - -The SCP subsystem has far less functionnality than the SFTP subsystem. -However, if you only need to copy files from and to the remote system, -it does its job. - - -@subsection scp_session Opening and closing a SCP session - -Like in the SFTP subsystem, you don't handle the SSH channels directly. -Instead, you open a "SCP session". - -When you open your SCP session, you have to choose between read or write mode. -You can't do both in the same session. So you specify either SSH_SCP_READ or -SSH_SCP_WRITE as the second parameter of function ssh_scp_new(). - -Another important mode flag for opening your SCP session is SSH_SCP_RECURSIVE. -When you use SSH_SCP_RECURSIVE, you declare that you are willing to emulate -the behaviour of "scp -r" command in your program, no matter it is for -reading or for writing. - -Once your session is created, you initialize it with ssh_scp_init(). When -you have finished transferring files, you terminate the SCP connection with -ssh_scp_close(). Finally, you can dispose the SCP connection with -ssh_scp_free(). - -The example below does the maintenance work to open a SCP connection for writing in -recursive mode: - -@code -int scp_write(ssh_session session) -{ - ssh_scp scp; - int rc; - - scp = ssh_scp_new - (session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, "."); - if (scp == NULL) - { - fprintf(stderr, "Error allocating scp session: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - rc = ssh_scp_init(scp); - if (rc != SSH_OK) - { - fprintf(stderr, "Error initializing scp session: %s\n", - ssh_get_error(session)); - ssh_scp_free(scp); - return rc; - } - - ... - - ssh_scp_close(scp); - ssh_scp_free(scp); - return SSH_OK; -} -@endcode - -The example below shows how to open a connection to read a single file: - -@code -int scp_read(ssh_session session) -{ - ssh_scp scp; - int rc; - - scp = ssh_scp_new - (session, SSH_SCP_READ, "helloworld/helloworld.txt"); - if (scp == NULL) - { - fprintf(stderr, "Error allocating scp session: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - rc = ssh_scp_init(scp); - if (rc != SSH_OK) - { - fprintf(stderr, "Error initializing scp session: %s\n", - ssh_get_error(session)); - ssh_scp_free(scp); - return rc; - } - - ... - - ssh_scp_close(scp); - ssh_scp_free(scp); - return SSH_OK; -} - -@endcode - - -@subsection scp_write Creating files and directories - -You create directories with ssh_scp_push_directory(). In recursive mode, -you are placed in this directory once it is created. If the directory -already exists and if you are in recursive mode, you simply enter that -directory. - -Creating files is done in two steps. First, you prepare the writing with -ssh_scp_push_file(). Then, you write the data with ssh_scp_write(). -The length of the data to write must be identical between both function calls. -There's no need to "open" nor "close" the file, this is done automatically -on the remote end. If the file already exists, it is overwritten and truncated. - -The following example creates a new directory named "helloworld/", then creates -a file named "helloworld.txt" in that directory: - -@code -int scp_helloworld(ssh_session session, ssh_scp scp) -{ - int rc; - const char *helloworld = "Hello, world!\n"; - int length = strlen(helloworld); - - rc = ssh_scp_push_directory(scp, "helloworld", S_IRWXU); - if (rc != SSH_OK) - { - fprintf(stderr, "Can't create remote directory: %s\n", - ssh_get_error(session)); - return rc; - } - - rc = ssh_scp_push_file - (scp, "helloworld.txt", length, S_IRUSR | S_IWUSR); - if (rc != SSH_OK) - { - fprintf(stderr, "Can't open remote file: %s\n", - ssh_get_error(session)); - return rc; - } - - rc = ssh_scp_write(scp, helloworld, length); - if (rc != SSH_OK) - { - fprintf(stderr, "Can't write to remote file: %s\n", - ssh_get_error(session)); - return rc; - } - - return SSH_OK; -} -@endcode - - -@subsection scp_recursive_write Copying full directory trees to the remote server - -Let's say you want to copy the following tree of files to the remote site: - -@verbatim - +-- file1 - +-- B --+ - | +-- file2 --- A --+ - | +-- file3 - +-- C --+ - +-- file4 -@endverbatim - -You would do it that way: - - open the session in recursive mode - - enter directory A - - enter its subdirectory B - - create file1 in B - - create file2 in B - - leave directory B - - enter subdirectory C - - create file3 in C - - create file4 in C - - leave directory C - - leave directory A - -To leave a directory, call ssh_scp_leave_directory(). - - -@subsection scp_read Reading files and directories - - -To receive files, you pull requests from the other side with ssh_scp_pull_request(). -If this function returns SSH_SCP_REQUEST_NEWFILE, then you must get ready for -the reception. You can get the size of the data to receive with ssh_scp_request_get_size() -and allocate a buffer accordingly. When you are ready, you accept the request with -ssh_scp_accept_request(), then read the data with ssh_scp_read(). - -The following example receives a single file. The name of the file to -receive has been given earlier, when the scp session was opened: - -@code -int scp_receive(ssh_session session, ssh_scp scp) -{ - int rc; - int size, mode; - char *filename, *buffer; - - rc = ssh_scp_pull_request(scp); - if (rc != SSH_SCP_REQUEST_NEWFILE) - { - fprintf(stderr, "Error receiving information about file: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - size = ssh_scp_request_get_size(scp); - filename = strdup(ssh_scp_request_get_filename(scp)); - mode = ssh_scp_request_get_permissions(scp); - printf("Receiving file %s, size %d, permisssions 0%o\n", - filename, size, mode); - free(filename); - - buffer = malloc(size); - if (buffer == NULL) - { - fprintf(stderr, "Memory allocation error\n"); - return SSH_ERROR; - } - - ssh_scp_accept_request(scp); - rc = ssh_scp_read(scp, buffer, size); - if (rc == SSH_ERROR) - { - fprintf(stderr, "Error receiving file data: %s\n", - ssh_get_error(session)); - free(buffer); - return rc; - } - printf("Done\n"); - - write(1, buffer, size); - free(buffer); - - rc = ssh_scp_pull_request(scp); - if (rc != SSH_SCP_REQUEST_EOF) - { - fprintf(stderr, "Unexpected request: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - return SSH_OK; -} -@endcode - -In this example, since we just requested a single file, we expect ssh_scp_request() -to return SSH_SCP_REQUEST_NEWFILE first, then SSH_SCP_REQUEST_EOF. That's quite a -naive approach; for example, the remote server might send a warning as well -(return code SSH_SCP_REQUEST_WARNING) and the example would fail. A more comprehensive -reception program would receive the requests in a loop and analyze them carefully -until SSH_SCP_REQUEST_EOF has been received. - - -@subsection scp_recursive_read Receiving full directory trees from the remote server - -If you opened the SCP session in recursive mode, the remote end will be -telling you when to change directory. - -In that case, when ssh_scp_pull_request() answers -SSH_SCP_REQUEST_NEWDIRECTORY, you should make that local directory (if -it does not exist yet) and enter it. When ssh_scp_pull_request() answers -SSH_SCP_REQUEST_ENDDIRECTORY, you should leave the current directory. - -*/ diff --git a/libssh/doc/sftp.dox b/libssh/doc/sftp.dox deleted file mode 100644 index 8b7c7e1a..00000000 --- a/libssh/doc/sftp.dox +++ /dev/null @@ -1,431 +0,0 @@ -/** -@page libssh_tutor_sftp Chapter 5: The SFTP subsystem -@section sftp_subsystem The SFTP subsystem - -SFTP stands for "Secure File Transfer Protocol". It enables you to safely -transfer files between the local and the remote computer. It reminds a lot -of the old FTP protocol. - -SFTP is a rich protocol. It lets you do over the network almost everything -that you can do with local files: - - send files - - modify only a portion of a file - - receive files - - receive only a portion of a file - - get file owner and group - - get file permissions - - set file owner and group - - set file permissions - - remove files - - rename files - - create a directory - - remove a directory - - retrieve the list of files in a directory - - get the target of a symbolic link - - create symbolic links - - get information about mounted filesystems. - -The current implemented version of the SFTP protocol is version 3. All functions -aren't implemented yet, but the most important are. - - -@subsection sftp_session Opening and closing a SFTP session - -Unlike with remote shells and remote commands, when you use the SFTP subsystem, -you don't handle directly the SSH channels. Instead, you open a "SFTP session". - -The function sftp_new() creates a new SFTP session. The function sftp_init() -initializes it. The function sftp_free() deletes it. - -As you see, all the SFTP-related functions start with the "sftp_" prefix -instead of the usual "ssh_" prefix. - -The example below shows how to use these functions: - -@code -#include - -int sftp_helloworld(ssh_session session) -{ - sftp_session sftp; - int rc; - - sftp = sftp_new(session); - if (sftp == NULL) - { - fprintf(stderr, "Error allocating SFTP session: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - rc = sftp_init(sftp); - if (rc != SSH_OK) - { - fprintf(stderr, "Error initializing SFTP session: %s.\n", - sftp_get_error(sftp)); - sftp_free(sftp); - return rc; - } - - ... - - sftp_free(sftp); - return SSH_OK; -} -@endcode - - -@subsection sftp_errors Analyzing SFTP errors - -In case of a problem, the function sftp_get_error() returns a SFTP-specific -error number, in addition to the regular SSH error number returned by -ssh_get_error_number(). - -Possible errors are: - - SSH_FX_OK: no error - - SSH_FX_EOF: end-of-file encountered - - SSH_FX_NO_SUCH_FILE: file does not exist - - SSH_FX_PERMISSION_DENIED: permission denied - - SSH_FX_FAILURE: generic failure - - SSH_FX_BAD_MESSAGE: garbage received from server - - SSH_FX_NO_CONNECTION: no connection has been set up - - SSH_FX_CONNECTION_LOST: there was a connection, but we lost it - - SSH_FX_OP_UNSUPPORTED: operation not supported by libssh yet - - SSH_FX_INVALID_HANDLE: invalid file handle - - SSH_FX_NO_SUCH_PATH: no such file or directory path exists - - SSH_FX_FILE_ALREADY_EXISTS: an attempt to create an already existing file or directory has been made - - SSH_FX_WRITE_PROTECT: write-protected filesystem - - SSH_FX_NO_MEDIA: no media was in remote drive - - -@subsection sftp_mkdir Creating a directory - -The function sftp_mkdir() tahes the "SFTP session" we juste created as -its first argument. It also needs the name of the file to create, and the -desired permissions. The permissions are the same as for the usual mkdir() -function. To get a comprehensive list of the available permissions, use the -"man 2 stat" command. The desired permissions are combined with the remote -user's mask to determine the effective permissions. - -The code below creates a directory named "helloworld" in the current directory that -can be read and written only by its owner: - -@code -#include -#include - -int sftp_helloworld(ssh_session session, sftp_session sftp) -{ - int rc; - - rc = sftp_mkdir(sftp, "helloworld", S_IRWXU); - if (rc != SSH_OK) - { - if (sftp_get_error(sftp) != SSH_FX_FILE_ALREADY_EXISTS) - { - fprintf(stderr, "Can't create directory: %s\n", - ssh_get_error(session)); - return rc; - } - } - - ... - - return SSH_OK; -} -@endcode - -Unlike its equivalent in the SCP subsystem, this function does NOT change the -current directory to the newly created subdirectory. - - -@subsection sftp_write Copying a file to the remote computer - -You handle the contents of a remote file just like you would do with a -local file: you open the file in a given mode, move the file pointer in it, -read or write data, and close the file. - -The sftp_open() function is very similar to the regular open() function, -excepted that it returns a file handle of type sftp_file. This file handle -is then used by the other file manipulation functions and remains valid -until you close the remote file with sftp_close(). - -The example below creates a new file named "helloworld.txt" in the -newly created "helloworld" directory. If the file already exists, it will -be truncated. It then writes the famous "Hello, World!" sentence to the -file, followed by a new line character. Finally, the file is closed: - -@code -#include -#include -#include - -int sftp_helloworld(ssh_session session, sftp_session sftp) -{ - int access_type = O_WRONLY | O_CREAT | O_TRUNC; - sftp_file file; - const char *helloworld = "Hello, World!\n"; - int length = strlen(helloworld); - int rc, nwritten; - - ... - - file = sftp_open(sftp, "helloworld/helloworld.txt", - access_type, S_IRWXU); - if (file == NULL) - { - fprintf(stderr, "Can't open file for writing: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - nwritten = sftp_write(file, helloworld, length); - if (nwritten != length) - { - fprintf(stderr, "Can't write data to file: %s\n", - ssh_get_error(session)); - sftp_close(file); - return SSH_ERROR; - } - - rc = sftp_close(file); - if (rc != SSH_OK) - { - fprintf(stderr, "Can't close the written file: %s\n", - ssh_get_error(session)); - return rc; - } - - return SSH_OK; -} -@endcode - - -@subsection sftp_read Reading a file from the remote computer - -The nice thing with reading a file over the network through SFTP is that it -can be done both in a synchronous way or an asynchronous way. If you read the file -asynchronously, your program can do something else while it waits for the -results to come. - -Synchronous read is done with sftp_read(). - -Files are normally transferred in chunks. A good chunk size is 16 KB. The following -example transfers the remote file "/etc/profile" in 16 KB chunks. For each chunk we -request, sftp_read blocks till the data has been received: - -@code -// Good chunk size -#define MAX_XFER_BUF_SIZE 16384 - -int sftp_read_sync(ssh_session session, sftp_session sftp) -{ - int access_type; - sftp_file file; - char buffer[MAX_XFER_BUF_SIZE]; - int nbytes, nwritten, rc; - int fd; - - access_type = O_RDONLY; - file = sftp_open(sftp, "/etc/profile", - access_type, 0); - if (file == NULL) { - fprintf(stderr, "Can't open file for reading: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - fd = open("/path/to/profile", O_CREAT); - if (fd < 0) { - fprintf(stderr, "Can't open file for writing: %s\n", - strerror(errno)); - return SSH_ERROR; - } - - for (;;) { - nbytes = sftp_read(file, buffer, sizeof(buffer)); - if (nbytes == 0) { - break; // EOF - } else if (nbytes < 0) { - fprintf(stderr, "Error while reading file: %s\n", - ssh_get_error(session)); - sftp_close(file); - return SSH_ERROR; - } - - nwritten = write(fd, buf, nbytes); - if (nwritten != nbytes) { - fprintf(stderr, "Error writing: %s\n", - strerror(errno)); - sftp_close(file); - return SSH_ERROR; - } - } - - rc = sftp_close(file); - if (rc != SSH_OK) { - fprintf(stderr, "Can't close the read file: %s\n", - ssh_get_error(session)); - return rc; - } - - return SSH_OK; -} -@endcode - -Asynchronous read is done in two steps, first sftp_async_read_begin(), which -returns a "request handle", and then sftp_async_read(), which uses that request handle. -If the file has been opened in nonblocking mode, then sftp_async_read() -might return SSH_AGAIN, which means that the request hasn't completed yet -and that the function should be called again later on. Otherwise, -sftp_async_read() waits for the data to come. To open a file in nonblocking mode, -call sftp_file_set_nonblocking() right after you opened it. Default is blocking mode. - -The example below reads a very big file in asynchronous, nonblocking, mode. Each -time the data are not ready yet, a counter is incrementer. - -@code -// Good chunk size -#define MAX_XFER_BUF_SIZE 16384 - -int sftp_read_async(ssh_session session, sftp_session sftp) -{ - int access_type; - sftp_file file; - char buffer[MAX_XFER_BUF_SIZE]; - int async_request; - int nbytes; - long counter; - int rc; - - access_type = O_RDONLY; - file = sftp_open(sftp, "some_very_big_file", - access_type, 0); - if (file == NULL) { - fprintf(stderr, "Can't open file for reading: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - sftp_file_set_nonblocking(file); - - async_request = sftp_async_read_begin(file, sizeof(buffer)); - counter = 0L; - usleep(10000); - if (async_request >= 0) { - nbytes = sftp_async_read(file, buffer, sizeof(buffer), - async_request); - } else { - nbytes = -1; - } - - while (nbytes > 0 || nbytes == SSH_AGAIN) { - if (nbytes > 0) { - write(1, buffer, nbytes); - async_request = sftp_async_read_begin(file, sizeof(buffer)); - } else { - counter++; - } - usleep(10000); - - if (async_request >= 0) { - nbytes = sftp_async_read(file, buffer, sizeof(buffer), - async_request); - } else { - nbytes = -1; - } - } - - if (nbytes < 0) { - fprintf(stderr, "Error while reading file: %s\n", - ssh_get_error(session)); - sftp_close(file); - return SSH_ERROR; - } - - printf("The counter has reached value: %ld\n", counter); - - rc = sftp_close(file); - if (rc != SSH_OK) { - fprintf(stderr, "Can't close the read file: %s\n", - ssh_get_error(session)); - return rc; - } - - return SSH_OK; -} -@endcode - -@subsection sftp_ls Listing the contents of a directory - -The functions sftp_opendir(), sftp_readdir(), sftp_dir_eof(), -and sftp_closedir() enable to list the contents of a directory. -They use a new handle_type, "sftp_dir", which gives access to the -directory being read. - -In addition, sftp_readdir() returns a "sftp_attributes" which is a pointer -to a structure with informations about a directory entry: - - name: the name of the file or directory - - size: its size in bytes - - etc. - -sftp_readdir() might return NULL under two conditions: - - when the end of the directory has been met - - when an error occured - -To tell the difference, call sftp_dir_eof(). - -The attributes must be freed with sftp_attributes_free() when no longer -needed. - -The following example reads the contents of some remote directory: - -@code -int sftp_list_dir(ssh_session session, sftp_session sftp) -{ - sftp_dir dir; - sftp_attributes attributes; - int rc; - - dir = sftp_opendir(sftp, "/var/log"); - if (!dir) - { - fprintf(stderr, "Directory not opened: %s\n", - ssh_get_error(session)); - return SSH_ERROR; - } - - printf("Name Size Perms Owner\tGroup\n"); - - while ((attributes = sftp_readdir(sftp, dir)) != NULL) - { - printf("%-20s %10llu %.8o %s(%d)\t%s(%d)\n", - attributes->name, - (long long unsigned int) attributes->size, - attributes->permissions, - attributes->owner, - attributes->uid, - attributes->group, - attributes->gid); - - sftp_attributes_free(attributes); - } - - if (!sftp_dir_eof(dir)) - { - fprintf(stderr, "Can't list directory: %s\n", - ssh_get_error(session)); - sftp_closedir(dir); - return SSH_ERROR; - } - - rc = sftp_closedir(dir); - if (rc != SSH_OK) - { - fprintf(stderr, "Can't close directory: %s\n", - ssh_get_error(session)); - return rc; - } -} -@endcode - -*/ diff --git a/libssh/doc/shell.dox b/libssh/doc/shell.dox deleted file mode 100644 index 35fdfc82..00000000 --- a/libssh/doc/shell.dox +++ /dev/null @@ -1,361 +0,0 @@ -/** -@page libssh_tutor_shell Chapter 3: Opening a remote shell -@section opening_shell Opening a remote shell - -We already mentioned that a single SSH connection can be shared -between several "channels". Channels can be used for different purposes. - -This chapter shows how to open one of these channels, and how to use it to -start a command interpreter on a remote computer. - - -@subsection open_channel Opening and closing a channel - -The ssh_channel_new() function creates a channel. It returns the channel as -a variable of type ssh_channel. - -Once you have this channel, you open a SSH session that uses it with -ssh_channel_open_session(). - -Once you don't need the channel anymore, you can send an end-of-file -to it with ssh_channel_close(). At this point, you can destroy the channel -with ssh_channel_free(). - -The code sample below achieves these tasks: - -@code -int shell_session(ssh_session session) -{ - ssh_channel channel; - int rc; - - channel = ssh_channel_new(session); - if (channel == NULL) - return SSH_ERROR; - - rc = ssh_channel_open_session(channel); - if (rc != SSH_OK) - { - ssh_channel_free(channel); - return rc; - } - - ... - - ssh_channel_close(channel); - ssh_channel_send_eof(channel); - ssh_channel_free(channel); - - return SSH_OK; -} -@endcode - - -@subsection interactive Interactive and non-interactive sessions - -A "shell" is a command interpreter. It is said to be "interactive" -if there is a human user typing the commands, one after the -other. The contrary, a non-interactive shell, is similar to -the execution of commands in the background: there is no attached -terminal. - -If you plan using an interactive shell, you need to create a -pseud-terminal on the remote side. A remote terminal is usually referred -to as a "pty", for "pseudo-teletype". The remote processes won't see the -difference with a real text-oriented terminal. - -If needed, you request the pty with the function ssh_channel_request_pty(). -Then you define its dimensions (number of rows and columns) -with ssh_channel_change_pty_size(). - -Be your session interactive or not, the next step is to request a -shell with ssh_channel_request_shell(). - -@code -int interactive_shell_session(ssh_channel channel) -{ - int rc; - - rc = ssh_channel_request_pty(channel); - if (rc != SSH_OK) return rc; - - rc = ssh_channel_change_pty_size(channel, 80, 24); - if (rc != SSH_OK) return rc; - - rc = ssh_channel_request_shell(channel); - if (rc != SSH_OK) return rc; - - ... - - return rc; -} -@endcode - - -@subsection read_data Displaying the data sent by the remote computer - -In your program, you will usually need to receive all the data "displayed" -into the remote pty. You will usually analyse, log, or display this data. - -ssh_channel_read() and ssh_channel_read_nonblocking() are the simplest -way to read data from a channel. If you only need to read from a single -channel, they should be enough. - -The example below shows how to wait for remote data using ssh_channel_read(): - -@code -int interactive_shell_session(ssh_channel channel) -{ - int rc; - char buffer[256]; - int nbytes; - - rc = ssh_channel_request_pty(channel); - if (rc != SSH_OK) return rc; - - rc = ssh_channel_change_pty_size(channel, 80, 24); - if (rc != SSH_OK) return rc; - - rc = ssh_channel_request_shell(channel); - if (rc != SSH_OK) return rc; - - while (ssh_channel_is_open(channel) && - !ssh_channel_is_eof(channel)) - { - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - if (nbytes < 0) - return SSH_ERROR; - - if (nbytes > 0) - write(1, buffer, nbytes); - } - - return rc; -} -@endcode - -Unlike ssh_channel_read(), ssh_channel_read_nonblocking() never waits for -remote data to be ready. It returns immediately. - -If you plan to use ssh_channel_read_nonblocking() repeatedly in a loop, -you should use a "passive wait" function like usleep(3) in the same -loop. Otherwise, your program will consume all the CPU time, and your -computer might become unresponsive. - - -@subsection write_data Sending user input to the remote computer - -User's input is sent to the remote site with ssh_channel_write(). - -The following example shows how to combine a nonblocking read from a SSH -channel with a nonblocking read from the keyboard. The local input is then -sent to the remote computer: - -@code -/* Under Linux, this function determines whether a key has been pressed. - Under Windows, it is a standard function, so you need not redefine it. -*/ -int kbhit() -{ - struct timeval tv = { 0L, 0L }; - fd_set fds; - - FD_ZERO(&fds); - FD_SET(0, &fds); - - return select(1, &fds, NULL, NULL, &tv); -} - -/* A very simple terminal emulator: - - print data received from the remote computer - - send keyboard input to the remote computer -*/ -int interactive_shell_session(ssh_channel channel) -{ - /* Session and terminal initialization skipped */ - ... - - char buffer[256]; - int nbytes, nwritten; - - while (ssh_channel_is_open(channel) && - !ssh_channel_is_eof(channel)) - { - nbytes = ssh_channel_read_nonblocking(channel, buffer, sizeof(buffer), 0); - if (nbytes < 0) return SSH_ERROR; - if (nbytes > 0) - { - nwritten = write(1, buffer, nbytes); - if (nwritten != nbytes) return SSH_ERROR; - - if (!kbhit()) - { - usleep(50000L); // 0.05 second - continue; - } - - nbytes = read(0, buffer, sizeof(buffer)); - if (nbytes < 0) return SSH_ERROR; - if (nbytes > 0) - { - nwritten = ssh_channel_write(channel, buffer, nbytes); - if (nwritten != nbytes) return SSH_ERROR; - } - } - - return rc; -} -@endcode - -Of course, this is a poor terminal emulator, since the echo from the keys -pressed should not be done locally, but should be done by the remote side. -Also, user's input should not be sent once "Enter" key is pressed, but -immediately after each key is pressed. This can be accomplished -by setting the local terminal to "raw" mode with the cfmakeraw(3) function. -cfmakeraw() is a standard function under Linux, on other systems you can -recode it with: - -@code -static void cfmakeraw(struct termios *termios_p) -{ - termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); - termios_p->c_oflag &= ~OPOST; - termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); - termios_p->c_cflag &= ~(CSIZE|PARENB); - termios_p->c_cflag |= CS8; -} -@endcode - -If you are not using a local terminal, but some kind of graphical -environment, the solution to this kind of "echo" problems will be different. - - -@subsection select_loop A more elaborate way to get the remote data - -*** Warning: ssh_select() and ssh_channel_select() are not relevant anymore, - since libssh is about to provide an easier system for asynchronous - communications. This subsection should be removed then. *** - -ssh_channel_read() and ssh_channel_read_nonblocking() functions are simple, -but they are not adapted when you expect data from more than one SSH channel, -or from other file descriptors. Last example showed how getting data from -the standard input (the keyboard) at the same time as data from the SSH -channel was complicated. The functions ssh_select() and ssh_channel_select() -provide a more elegant way to wait for data coming from many sources. - -The functions ssh_select() and ssh_channel_select() remind of the standard -UNIX select(2) function. The idea is to wait for "something" to happen: -incoming data to be read, outcoming data to block, or an exception to -occur. Both these functions do a "passive wait", i.e. you can safely use -them repeatedly in a loop, it will not consume exaggerate processor time -and make your computer unresponsive. It is quite common to use these -functions in your application's main loop. - -The difference between ssh_select() and ssh_channel_select() is that -ssh_channel_select() is simpler, but allows you only to watch SSH channels. -ssh_select() is more complete and enables watching regular file descriptors -as well, in the same function call. - -Below is an example of a function that waits both for remote SSH data to come, -as well as standard input from the keyboard: - -@code -int interactive_shell_session(ssh_session session, ssh_channel channel) -{ - /* Session and terminal initialization skipped */ - ... - - char buffer[256]; - int nbytes, nwritten; - - while (ssh_channel_is_open(channel) && - !ssh_channel_is_eof(channel)) - { - struct timeval timeout; - ssh_channel in_channels[2], out_channels[2]; - fd_set fds; - int maxfd; - - timeout.tv_sec = 30; - timeout.tv_usec = 0; - in_channels[0] = channel; - in_channels[1] = NULL; - FD_ZERO(&fds); - FD_SET(0, &fds); - FD_SET(ssh_get_fd(session), &fds); - maxfd = ssh_get_fd(session) + 1; - - ssh_select(in_channels, out_channels, maxfd, &fds, &timeout); - - if (out_channels[0] != NULL) - { - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - if (nbytes < 0) return SSH_ERROR; - if (nbytes > 0) - { - nwritten = write(1, buffer, nbytes); - if (nwritten != nbytes) return SSH_ERROR; - } - } - - if (FD_ISSET(0, &fds)) - { - nbytes = read(0, buffer, sizeof(buffer)); - if (nbytes < 0) return SSH_ERROR; - if (nbytes > 0) - { - nwritten = ssh_channel_write(channel, buffer, nbytes); - if (nbytes != nwritten) return SSH_ERROR; - } - } - } - - return rc; -} -@endcode - - -@subsection x11 Using graphical applications on the remote side - -If your remote application is graphical, you can forward the X11 protocol to -your local computer. - -To do that, you first declare that you accept X11 connections with -ssh_channel_accept_x11(). Then you create the forwarding tunnel for -the X11 protocol with ssh_channel_request_x11(). - -The following code performs channel initialization and shell session -opening, and handles a parallel X11 connection: - -@code -int interactive_shell_session(ssh_channel channel) -{ - int rc; - ssh_channel x11channel; - - rc = ssh_channel_request_pty(channel); - if (rc != SSH_OK) return rc; - - rc = ssh_channel_change_pty_size(channel, 80, 24); - if (rc != SSH_OK) return rc; - - rc = ssh_channel_request_x11(channel, 0, NULL, NULL, 0); - if (rc != SSH_OK) return rc; - - rc = ssh_channel_request_shell(channel); - if (rc != SSH_OK) return rc; - - /* Read the data sent by the remote computer here */ - ... -} -@endcode - -Don't forget to set the $DISPLAY environment variable on the remote -side, or the remote applications won't try using the X11 tunnel: - -@code -$ export DISPLAY=:0 -$ xclock & -@endcode - -*/ diff --git a/libssh/doc/tbd.dox b/libssh/doc/tbd.dox deleted file mode 100644 index 921337ed..00000000 --- a/libssh/doc/tbd.dox +++ /dev/null @@ -1,14 +0,0 @@ -/** -@page libssh_tutor_todo To be done - -*** To be written *** - -@section sshd Writing a libssh-based server - -*** To be written *** - -@section cpp The libssh C++ wrapper - -*** To be written *** - -*/ diff --git a/libssh/doc/threading.dox b/libssh/doc/threading.dox deleted file mode 100644 index 95eee6bb..00000000 --- a/libssh/doc/threading.dox +++ /dev/null @@ -1,66 +0,0 @@ -/** -@page libssh_tutor_threads Chapter 8: Threads with libssh -@section threads_with_libssh How to use libssh with threads - -libssh may be used in multithreaded applications, but under several conditions : - - Threading must be initialized during the initialization of libssh. This - initialization must be done outside of any threading context. - - If pthreads is being used by your application (or your framework's backend), - you must link with libssh_threads dynamic library and initialize - threading with the ssh_threads_pthreads threading object. - - If an other threading library is being used by your application, you must - implement all the methods of the ssh_threads_callbacks_struct structure - and initialize libssh with it. - - At all times, you may use different sessions inside threads, make parallel - connections, read/write on different sessions and so on. You *cannot* use a - single session (or channels for a single session) in several threads at the same - time. This will most likely lead to internal state corruption. This limitation is - being worked out and will maybe disappear later. - -@subsection threads_init Initialization of threads - -To initialize threading, you must first select the threading model you want to -use, using ssh_threads_set_callbacks(), then call ssh_init(). - -@code -#include -... -ssh_threads_set_callbacks(ssh_threads_get_noop()); -ssh_init(); -@endcode - -ssh_threads_noop is the threading structure that does nothing. It's the -threading callbacks being used by default when you're not using threading. - -@subsection threads_pthread Using libpthread with libssh - -If your application is using libpthread, you may simply use the libpthread -threading backend: - -@code -#include -... -ssh_threads_set_callbacks(ssh_threads_get_pthread()); -ssh_init(); -@endcode - -However, you must be sure to link with the library ssh_threads. If -you're using gcc, you must use the commandline -@code -gcc -o output input.c -lssh -lssh_threads -@endcode - - -@subsection threads_other Using another threading library - -You must find your way in the ssh_threads_callbacks_struct structure. You must -implement the following methods : -- mutex_lock -- mutex_unlock -- mutex_init -- mutex_destroy -- thread_id - -libgcrypt 1.6 and bigger backend does not support custom callback. Using anything else than pthreads (ssh_threads_get_pthread()) here will fail. -Good luck ! -*/ diff --git a/libssh/examples/CMakeLists.txt b/libssh/examples/CMakeLists.txt deleted file mode 100644 index 4998f041..00000000 --- a/libssh/examples/CMakeLists.txt +++ /dev/null @@ -1,66 +0,0 @@ -project(libssh-examples C CXX) - -set(examples_SRCS - authentication.c - knownhosts.c - connect_ssh.c -) - -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR} -) - -if (BSD OR SOLARIS OR OSX) - find_package(Argp) -endif (BSD OR SOLARIS OR OSX) - -if (UNIX AND NOT WIN32) - add_executable(libssh_scp libssh_scp.c ${examples_SRCS}) - target_link_libraries(libssh_scp ${LIBSSH_SHARED_LIBRARY}) - - add_executable(scp_download scp_download.c ${examples_SRCS}) - target_link_libraries(scp_download ${LIBSSH_SHARED_LIBRARY}) - - add_executable(sshnetcat sshnetcat.c ${examples_SRCS}) - target_link_libraries(sshnetcat ${LIBSSH_SHARED_LIBRARY}) - - if (WITH_SFTP) - add_executable(samplesftp samplesftp.c ${examples_SRCS}) - target_link_libraries(samplesftp ${LIBSSH_SHARED_LIBRARY}) - endif (WITH_SFTP) - - add_executable(samplessh sample.c ${examples_SRCS}) - target_link_libraries(samplessh ${LIBSSH_SHARED_LIBRARY}) - - if (WITH_SERVER) - if (HAVE_LIBUTIL) - add_executable(ssh_server_fork ssh_server_fork.c) - target_link_libraries(ssh_server_fork ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES} util) - endif (HAVE_LIBUTIL) - - if (WITH_GSSAPI AND GSSAPI_FOUND) - add_executable(samplesshd-cb samplesshd-cb.c) - target_link_libraries(samplesshd-cb ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES}) - - add_executable(proxy proxy.c) - target_link_libraries(proxy ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES}) - endif (WITH_GSSAPI AND GSSAPI_FOUND) - - add_executable(samplesshd-kbdint samplesshd-kbdint.c) - target_link_libraries(samplesshd-kbdint ${LIBSSH_SHARED_LIBRARY} ${ARGP_LIBRARIES}) - - endif (WITH_SERVER) -endif (UNIX AND NOT WIN32) - -add_executable(exec exec.c ${examples_SRCS}) -target_link_libraries(exec ${LIBSSH_SHARED_LIBRARY}) - -add_executable(senddata senddata.c ${examples_SRCS}) -target_link_libraries(senddata ${LIBSSH_SHARED_LIBRARY}) - -add_executable(libsshpp libsshpp.cpp) -target_link_libraries(libsshpp ${LIBSSH_SHARED_LIBRARY}) - -add_executable(libsshpp_noexcept libsshpp_noexcept.cpp) -target_link_libraries(libsshpp_noexcept ${LIBSSH_SHARED_LIBRARY}) diff --git a/libssh/examples/authentication.c b/libssh/examples/authentication.c deleted file mode 100644 index b9f70f5b..00000000 --- a/libssh/examples/authentication.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * authentication.c - * This file contains an example of how to do an authentication to a - * SSH server using libssh - */ - -/* -Copyright 2003-2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. - */ - -#include -#include -#include - -#include -#include "examples_common.h" - -int authenticate_kbdint(ssh_session session, const char *password) { - int err; - - err = ssh_userauth_kbdint(session, NULL, NULL); - while (err == SSH_AUTH_INFO) { - const char *instruction; - const char *name; - char buffer[128]; - int i, n; - - name = ssh_userauth_kbdint_getname(session); - instruction = ssh_userauth_kbdint_getinstruction(session); - n = ssh_userauth_kbdint_getnprompts(session); - - if (name && strlen(name) > 0) { - printf("%s\n", name); - } - - if (instruction && strlen(instruction) > 0) { - printf("%s\n", instruction); - } - - for (i = 0; i < n; i++) { - const char *answer; - const char *prompt; - char echo; - - prompt = ssh_userauth_kbdint_getprompt(session, i, &echo); - if (prompt == NULL) { - break; - } - - if (echo) { - char *p; - - printf("%s", prompt); - - if (fgets(buffer, sizeof(buffer), stdin) == NULL) { - return SSH_AUTH_ERROR; - } - - buffer[sizeof(buffer) - 1] = '\0'; - if ((p = strchr(buffer, '\n'))) { - *p = '\0'; - } - - if (ssh_userauth_kbdint_setanswer(session, i, buffer) < 0) { - return SSH_AUTH_ERROR; - } - - memset(buffer, 0, strlen(buffer)); - } else { - if (password && strstr(prompt, "Password:")) { - answer = password; - } else { - buffer[0] = '\0'; - - if (ssh_getpass(prompt, buffer, sizeof(buffer), 0, 0) < 0) { - return SSH_AUTH_ERROR; - } - answer = buffer; - } - err = ssh_userauth_kbdint_setanswer(session, i, answer); - memset(buffer, 0, sizeof(buffer)); - if (err < 0) { - return SSH_AUTH_ERROR; - } - } - } - err=ssh_userauth_kbdint(session,NULL,NULL); - } - - return err; -} - -static void error(ssh_session session){ - fprintf(stderr,"Authentication failed: %s\n",ssh_get_error(session)); -} - -int authenticate_console(ssh_session session){ - int rc; - int method; - char password[128] = {0}; - char *banner; - - // Try to authenticate - rc = ssh_userauth_none(session, NULL); - if (rc == SSH_AUTH_ERROR) { - error(session); - return rc; - } - - method = ssh_userauth_list(session, NULL); - while (rc != SSH_AUTH_SUCCESS) { - if (method & SSH_AUTH_METHOD_GSSAPI_MIC){ - rc = ssh_userauth_gssapi(session); - if(rc == SSH_AUTH_ERROR) { - error(session); - return rc; - } else if (rc == SSH_AUTH_SUCCESS) { - break; - } - } - // Try to authenticate with public key first - if (method & SSH_AUTH_METHOD_PUBLICKEY) { - rc = ssh_userauth_publickey_auto(session, NULL, NULL); - if (rc == SSH_AUTH_ERROR) { - error(session); - return rc; - } else if (rc == SSH_AUTH_SUCCESS) { - break; - } - } - - // Try to authenticate with keyboard interactive"; - if (method & SSH_AUTH_METHOD_INTERACTIVE) { - rc = authenticate_kbdint(session, NULL); - if (rc == SSH_AUTH_ERROR) { - error(session); - return rc; - } else if (rc == SSH_AUTH_SUCCESS) { - break; - } - } - - if (ssh_getpass("Password: ", password, sizeof(password), 0, 0) < 0) { - return SSH_AUTH_ERROR; - } - - // Try to authenticate with password - if (method & SSH_AUTH_METHOD_PASSWORD) { - rc = ssh_userauth_password(session, NULL, password); - if (rc == SSH_AUTH_ERROR) { - error(session); - return rc; - } else if (rc == SSH_AUTH_SUCCESS) { - break; - } - } - memset(password, 0, sizeof(password)); - } - - banner = ssh_get_issue_banner(session); - if (banner) { - printf("%s\n",banner); - ssh_string_free_char(banner); - } - - return rc; -} diff --git a/libssh/examples/connect_ssh.c b/libssh/examples/connect_ssh.c deleted file mode 100644 index c9e4ef6e..00000000 --- a/libssh/examples/connect_ssh.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * connect_ssh.c - * This file contains an example of how to connect to a - * SSH server using libssh - */ - -/* -Copyright 2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. - */ - -#include -#include "examples_common.h" -#include - -ssh_session connect_ssh(const char *host, const char *user,int verbosity){ - ssh_session session; - int auth=0; - - session=ssh_new(); - if (session == NULL) { - return NULL; - } - - if(user != NULL){ - if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) { - ssh_free(session); - return NULL; - } - } - - if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) { - ssh_free(session); - return NULL; - } - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - if(ssh_connect(session)){ - fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session)); - ssh_disconnect(session); - ssh_free(session); - return NULL; - } - if(verify_knownhost(session)<0){ - ssh_disconnect(session); - ssh_free(session); - return NULL; - } - auth=authenticate_console(session); - if(auth==SSH_AUTH_SUCCESS){ - return session; - } else if(auth==SSH_AUTH_DENIED){ - fprintf(stderr,"Authentication failed\n"); - } else { - fprintf(stderr,"Error while authenticating : %s\n",ssh_get_error(session)); - } - ssh_disconnect(session); - ssh_free(session); - return NULL; -} diff --git a/libssh/examples/examples_common.h b/libssh/examples/examples_common.h deleted file mode 100644 index 13eb455c..00000000 --- a/libssh/examples/examples_common.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ -#ifndef EXAMPLES_COMMON_H_ -#define EXAMPLES_COMMON_H_ - -#include -int authenticate_console(ssh_session session); -int authenticate_kbdint(ssh_session session, const char *password); -int verify_knownhost(ssh_session session); -ssh_session connect_ssh(const char *hostname, const char *user, int verbosity); - -#endif /* EXAMPLES_COMMON_H_ */ diff --git a/libssh/examples/exec.c b/libssh/examples/exec.c deleted file mode 100644 index 4d5e0c1a..00000000 --- a/libssh/examples/exec.c +++ /dev/null @@ -1,66 +0,0 @@ -/* simple exec example */ -#include - -#include -#include "examples_common.h" - -int main(void) { - ssh_session session; - ssh_channel channel; - char buffer[256]; - int nbytes; - int rc; - - session = connect_ssh("localhost", NULL, 0); - if (session == NULL) { - ssh_finalize(); - return 1; - } - - channel = ssh_channel_new(session);; - if (channel == NULL) { - ssh_disconnect(session); - ssh_free(session); - ssh_finalize(); - return 1; - } - - rc = ssh_channel_open_session(channel); - if (rc < 0) { - goto failed; - } - - rc = ssh_channel_request_exec(channel, "lsof"); - if (rc < 0) { - goto failed; - } - - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - while (nbytes > 0) { - if (fwrite(buffer, 1, nbytes, stdout) != (unsigned int) nbytes) { - goto failed; - } - nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); - } - - if (nbytes < 0) { - goto failed; - } - - ssh_channel_send_eof(channel); - ssh_channel_close(channel); - ssh_channel_free(channel); - ssh_disconnect(session); - ssh_free(session); - ssh_finalize(); - - return 0; -failed: - ssh_channel_close(channel); - ssh_channel_free(channel); - ssh_disconnect(session); - ssh_free(session); - ssh_finalize(); - - return 1; -} diff --git a/libssh/examples/knownhosts.c b/libssh/examples/knownhosts.c deleted file mode 100644 index 5097cd93..00000000 --- a/libssh/examples/knownhosts.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * knownhosts.c - * This file contains an example of how verify the identity of a - * SSH server using libssh - */ - -/* -Copyright 2003-2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. - */ - -#include -#include -#include -#include - -#include -#include "examples_common.h" - -#ifdef _WIN32 -#define strncasecmp _strnicmp -#endif - -int verify_knownhost(ssh_session session){ - char *hexa; - int state; - char buf[10]; - unsigned char *hash = NULL; - size_t hlen; - ssh_key srv_pubkey; - int rc; - - state=ssh_is_server_known(session); - - rc = ssh_get_publickey(session, &srv_pubkey); - if (rc < 0) { - return -1; - } - - rc = ssh_get_publickey_hash(srv_pubkey, - SSH_PUBLICKEY_HASH_SHA1, - &hash, - &hlen); - ssh_key_free(srv_pubkey); - if (rc < 0) { - return -1; - } - - switch(state){ - case SSH_SERVER_KNOWN_OK: - break; /* ok */ - case SSH_SERVER_KNOWN_CHANGED: - fprintf(stderr,"Host key for server changed : server's one is now :\n"); - ssh_print_hexa("Public key hash",hash, hlen); - ssh_clean_pubkey_hash(&hash); - fprintf(stderr,"For security reason, connection will be stopped\n"); - return -1; - case SSH_SERVER_FOUND_OTHER: - fprintf(stderr,"The host key for this server was not found but an other type of key exists.\n"); - fprintf(stderr,"An attacker might change the default server key to confuse your client" - "into thinking the key does not exist\n" - "We advise you to rerun the client with -d or -r for more safety.\n"); - return -1; - case SSH_SERVER_FILE_NOT_FOUND: - fprintf(stderr,"Could not find known host file. If you accept the host key here,\n"); - fprintf(stderr,"the file will be automatically created.\n"); - /* fallback to SSH_SERVER_NOT_KNOWN behavior */ - case SSH_SERVER_NOT_KNOWN: - hexa = ssh_get_hexa(hash, hlen); - fprintf(stderr,"The server is unknown. Do you trust the host key ?\n"); - fprintf(stderr, "Public key hash: %s\n", hexa); - ssh_string_free_char(hexa); - if (fgets(buf, sizeof(buf), stdin) == NULL) { - ssh_clean_pubkey_hash(&hash); - return -1; - } - if(strncasecmp(buf,"yes",3)!=0){ - ssh_clean_pubkey_hash(&hash); - return -1; - } - fprintf(stderr,"This new key will be written on disk for further usage. do you agree ?\n"); - if (fgets(buf, sizeof(buf), stdin) == NULL) { - ssh_clean_pubkey_hash(&hash); - return -1; - } - if(strncasecmp(buf,"yes",3)==0){ - if (ssh_write_knownhost(session) < 0) { - ssh_clean_pubkey_hash(&hash); - fprintf(stderr, "error %s\n", strerror(errno)); - return -1; - } - } - - break; - case SSH_SERVER_ERROR: - ssh_clean_pubkey_hash(&hash); - fprintf(stderr,"%s",ssh_get_error(session)); - return -1; - } - ssh_clean_pubkey_hash(&hash); - return 0; -} diff --git a/libssh/examples/libssh_scp.c b/libssh/examples/libssh_scp.c deleted file mode 100644 index 99281db8..00000000 --- a/libssh/examples/libssh_scp.c +++ /dev/null @@ -1,327 +0,0 @@ -/* libssh_scp.c - * Sample implementation of a SCP client - */ - -/* -Copyright 2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. - */ - -#include -#include -#include -#include -#include - -#include -#include "examples_common.h" - -static char **sources; -static int nsources; -static char *destination; -static int verbosity=0; - -struct location { - int is_ssh; - char *user; - char *host; - char *path; - ssh_session session; - ssh_scp scp; - FILE *file; -}; - -enum { - READ, - WRITE -}; - -static void usage(const char *argv0){ - fprintf(stderr,"Usage : %s [options] [[user@]host1:]file1 ... \n" - " [[user@]host2:]destination\n" - "sample scp client - libssh-%s\n", -// "Options :\n", -// " -r : use RSA to verify host public key\n", - argv0, - ssh_version(0)); - exit(0); -} - -static int opts(int argc, char **argv){ - int i; - while((i=getopt(argc,argv,"v"))!=-1){ - switch(i){ - case 'v': - verbosity++; - break; - default: - fprintf(stderr,"unknown option %c\n",optopt); - usage(argv[0]); - return -1; - } - } - nsources=argc-optind-1; - if(nsources < 1){ - usage(argv[0]); - return -1; - } - sources=malloc((nsources + 1) * sizeof(char *)); - if(sources == NULL) - return -1; - for(i=0;ihost=location->user=NULL; - ptr=strchr(loc,':'); - if(ptr != NULL){ - location->is_ssh=1; - location->path=strdup(ptr+1); - *ptr='\0'; - ptr=strchr(loc,'@'); - if(ptr != NULL){ - location->host=strdup(ptr+1); - *ptr='\0'; - location->user=strdup(loc); - } else { - location->host=strdup(loc); - } - } else { - location->is_ssh=0; - location->path=strdup(loc); - } - return location; -} - -static int open_location(struct location *loc, int flag){ - if(loc->is_ssh && flag==WRITE){ - loc->session=connect_ssh(loc->host,loc->user,verbosity); - if(!loc->session){ - fprintf(stderr,"Couldn't connect to %s\n",loc->host); - return -1; - } - loc->scp=ssh_scp_new(loc->session,SSH_SCP_WRITE,loc->path); - if(!loc->scp){ - fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); - return -1; - } - if(ssh_scp_init(loc->scp)==SSH_ERROR){ - fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); - ssh_scp_free(loc->scp); - loc->scp = NULL; - return -1; - } - return 0; - } else if(loc->is_ssh && flag==READ){ - loc->session=connect_ssh(loc->host, loc->user,verbosity); - if(!loc->session){ - fprintf(stderr,"Couldn't connect to %s\n",loc->host); - return -1; - } - loc->scp=ssh_scp_new(loc->session,SSH_SCP_READ,loc->path); - if(!loc->scp){ - fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); - return -1; - } - if(ssh_scp_init(loc->scp)==SSH_ERROR){ - fprintf(stderr,"error : %s\n",ssh_get_error(loc->session)); - ssh_scp_free(loc->scp); - loc->scp = NULL; - return -1; - } - return 0; - } else { - loc->file=fopen(loc->path,flag==READ ? "r":"w"); - if(!loc->file){ - if(errno==EISDIR){ - if(chdir(loc->path)){ - fprintf(stderr,"Error changing directory to %s: %s\n",loc->path,strerror(errno)); - return -1; - } - return 0; - } - fprintf(stderr,"Error opening %s: %s\n",loc->path,strerror(errno)); - return -1; - } - return 0; - } - return -1; -} - -/** @brief copies files from source location to destination - * @param src source location - * @param dest destination location - * @param recursive Copy also directories - */ -static int do_copy(struct location *src, struct location *dest, int recursive){ - int size; - socket_t fd; - struct stat s; - int w,r; - char buffer[16384]; - int total=0; - int mode; - char *filename = NULL; - /* recursive mode doesn't work yet */ - (void)recursive; - /* Get the file name and size*/ - if(!src->is_ssh){ - fd = fileno(src->file); - if (fd < 0) { - fprintf(stderr, "Invalid file pointer, error: %s\n", strerror(errno)); - return -1; - } - r = fstat(fd, &s); - if (r < 0) { - return -1; - } - size=s.st_size; - mode = s.st_mode & ~S_IFMT; - filename=ssh_basename(src->path); - } else { - size=0; - do { - r=ssh_scp_pull_request(src->scp); - if(r==SSH_SCP_REQUEST_NEWDIR){ - ssh_scp_deny_request(src->scp,"Not in recursive mode"); - continue; - } - if(r==SSH_SCP_REQUEST_NEWFILE){ - size=ssh_scp_request_get_size(src->scp); - filename=strdup(ssh_scp_request_get_filename(src->scp)); - mode=ssh_scp_request_get_permissions(src->scp); - //ssh_scp_accept_request(src->scp); - break; - } - if(r==SSH_ERROR){ - fprintf(stderr,"Error: %s\n",ssh_get_error(src->session)); - ssh_string_free_char(filename); - return -1; - } - } while(r != SSH_SCP_REQUEST_NEWFILE); - } - - if(dest->is_ssh){ - r=ssh_scp_push_file(dest->scp,src->path, size, mode); - // snprintf(buffer,sizeof(buffer),"C0644 %d %s\n",size,src->path); - if(r==SSH_ERROR){ - fprintf(stderr,"error: %s\n",ssh_get_error(dest->session)); - ssh_string_free_char(filename); - ssh_scp_free(dest->scp); - return -1; - } - } else { - if(!dest->file){ - dest->file=fopen(filename,"w"); - if(!dest->file){ - fprintf(stderr,"Cannot open %s for writing: %s\n",filename,strerror(errno)); - if(src->is_ssh) - ssh_scp_deny_request(src->scp,"Cannot open local file"); - ssh_string_free_char(filename); - return -1; - } - } - if(src->is_ssh){ - ssh_scp_accept_request(src->scp); - } - } - do { - if(src->is_ssh){ - r=ssh_scp_read(src->scp,buffer,sizeof(buffer)); - if(r==SSH_ERROR){ - fprintf(stderr,"Error reading scp: %s\n",ssh_get_error(src->session)); - ssh_string_free_char(filename); - return -1; - } - if(r==0) - break; - } else { - r=fread(buffer,1,sizeof(buffer),src->file); - if(r==0) - break; - if(r<0){ - fprintf(stderr,"Error reading file: %s\n",strerror(errno)); - ssh_string_free_char(filename); - return -1; - } - } - if(dest->is_ssh){ - w=ssh_scp_write(dest->scp,buffer,r); - if(w == SSH_ERROR){ - fprintf(stderr,"Error writing in scp: %s\n",ssh_get_error(dest->session)); - ssh_scp_free(dest->scp); - dest->scp=NULL; - ssh_string_free_char(filename); - return -1; - } - } else { - w=fwrite(buffer,r,1,dest->file); - if(w<=0){ - fprintf(stderr,"Error writing in local file: %s\n",strerror(errno)); - ssh_string_free_char(filename); - return -1; - } - } - total+=r; - - } while(total < size); - ssh_string_free_char(filename); - printf("wrote %d bytes\n",total); - return 0; -} - -int main(int argc, char **argv){ - struct location *dest, *src; - int i; - int r; - if(opts(argc,argv)<0) - return EXIT_FAILURE; - dest=parse_location(destination); - if(open_location(dest,WRITE)<0) - return EXIT_FAILURE; - for(i=0;iis_ssh && dest->scp != NULL) { - r=ssh_scp_close(dest->scp); - if(r == SSH_ERROR){ - fprintf(stderr,"Error closing scp: %s\n",ssh_get_error(dest->session)); - ssh_scp_free(dest->scp); - dest->scp=NULL; - return -1; - } - } else { - fclose(dest->file); - dest->file=NULL; - } - ssh_disconnect(dest->session); - ssh_finalize(); - return 0; -} diff --git a/libssh/examples/libsshpp.cpp b/libssh/examples/libsshpp.cpp deleted file mode 100644 index 8f042a45..00000000 --- a/libssh/examples/libsshpp.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2010 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -*/ - -/* This file demonstrates the use of the C++ wrapper to libssh */ - -#include -#include -#include - -int main(int argc, const char **argv){ - ssh::Session session; - try { - if(argc>1) - session.setOption(SSH_OPTIONS_HOST,argv[1]); - else - session.setOption(SSH_OPTIONS_HOST,"localhost"); - session.connect(); - session.userauthPublickeyAuto(); - session.disconnect(); - } catch (ssh::SshException e){ - std::cout << "Error during connection : "; - std::cout << e.getError() << std::endl; - } - return 0; -} diff --git a/libssh/examples/libsshpp_noexcept.cpp b/libssh/examples/libsshpp_noexcept.cpp deleted file mode 100644 index eff8cc19..00000000 --- a/libssh/examples/libsshpp_noexcept.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2010 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -*/ - -/* This file demonstrates the use of the C++ wrapper to libssh - * specifically, without C++ exceptions - */ - -#include -#define SSH_NO_CPP_EXCEPTIONS -#include - -int main(int argc, const char **argv){ - ssh::Session session,s2; - int err; - if(argc>1) - err=session.setOption(SSH_OPTIONS_HOST,argv[1]); - else - err=session.setOption(SSH_OPTIONS_HOST,"localhost"); - if(err==SSH_ERROR) - goto error; - err=session.connect(); - if(err==SSH_ERROR) - goto error; - err=session.userauthPublickeyAuto(); - if(err==SSH_ERROR) - goto error; - - return 0; - error: - std::cout << "Error during connection : "; - std::cout << session.getError() << std::endl; - return 1; -} diff --git a/libssh/examples/proxy.c b/libssh/examples/proxy.c deleted file mode 100644 index dcf4d0d7..00000000 --- a/libssh/examples/proxy.c +++ /dev/null @@ -1,347 +0,0 @@ -/* This is a sample implementation of a libssh based SSH proxy */ -/* -Copyright 2003-2013 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ - -#include "config.h" - -#include -#include -#include - -#ifdef HAVE_ARGP_H -#include -#endif -#include -#include -#include - -#define USER "myuser" -#define PASSWORD "mypassword" - -static int authenticated=0; -static int tries = 0; -static int error = 0; -static ssh_channel chan=NULL; -static char *username; -static ssh_gssapi_creds client_creds = NULL; - -static int auth_password(ssh_session session, const char *user, - const char *password, void *userdata){ - - (void)userdata; - - printf("Authenticating user %s pwd %s\n",user, password); - if(strcmp(user,USER) == 0 && strcmp(password, PASSWORD) == 0){ - authenticated = 1; - printf("Authenticated\n"); - return SSH_AUTH_SUCCESS; - } - if (tries >= 3){ - printf("Too many authentication tries\n"); - ssh_disconnect(session); - error = 1; - return SSH_AUTH_DENIED; - } - tries++; - return SSH_AUTH_DENIED; -} - -static int auth_gssapi_mic(ssh_session session, const char *user, const char *principal, void *userdata){ - (void)userdata; - client_creds = ssh_gssapi_get_creds(session); - printf("Authenticating user %s with gssapi principal %s\n",user, principal); - if (client_creds != NULL) - printf("Received some gssapi credentials\n"); - else - printf("Not received any forwardable creds\n"); - printf("authenticated\n"); - authenticated = 1; - username = strdup(principal); - return SSH_AUTH_SUCCESS; -} - -static int pty_request(ssh_session session, ssh_channel channel, const char *term, - int x,int y, int px, int py, void *userdata){ - (void) session; - (void) channel; - (void) term; - (void) x; - (void) y; - (void) px; - (void) py; - (void) userdata; - printf("Allocated terminal\n"); - return 0; -} - -static int shell_request(ssh_session session, ssh_channel channel, void *userdata){ - (void)session; - (void)channel; - (void)userdata; - printf("Allocated shell\n"); - return 0; -} -struct ssh_channel_callbacks_struct channel_cb = { - .channel_pty_request_function = pty_request, - .channel_shell_request_function = shell_request -}; - -static ssh_channel new_session_channel(ssh_session session, void *userdata){ - (void) session; - (void) userdata; - if(chan != NULL) - return NULL; - printf("Allocated session channel\n"); - chan = ssh_channel_new(session); - ssh_callbacks_init(&channel_cb); - ssh_set_channel_callbacks(chan, &channel_cb); - return chan; -} - - -#ifdef HAVE_ARGP_H -const char *argp_program_version = "libssh proxy example " -SSH_STRINGIFY(LIBSSH_VERSION); -const char *argp_program_bug_address = ""; - -/* Program documentation. */ -static char doc[] = "libssh -- a Secure Shell protocol implementation"; - -/* A description of the arguments we accept. */ -static char args_doc[] = "BINDADDR"; - -/* The options we understand. */ -static struct argp_option options[] = { - { - .name = "port", - .key = 'p', - .arg = "PORT", - .flags = 0, - .doc = "Set the port to bind.", - .group = 0 - }, - { - .name = "hostkey", - .key = 'k', - .arg = "FILE", - .flags = 0, - .doc = "Set the host key.", - .group = 0 - }, - { - .name = "dsakey", - .key = 'd', - .arg = "FILE", - .flags = 0, - .doc = "Set the dsa key.", - .group = 0 - }, - { - .name = "rsakey", - .key = 'r', - .arg = "FILE", - .flags = 0, - .doc = "Set the rsa key.", - .group = 0 - }, - { - .name = "verbose", - .key = 'v', - .arg = NULL, - .flags = 0, - .doc = "Get verbose output.", - .group = 0 - }, - {NULL, 0, NULL, 0, NULL, 0} -}; - -/* Parse a single option. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* Get the input argument from argp_parse, which we - * know is a pointer to our arguments structure. - */ - ssh_bind sshbind = state->input; - - switch (key) { - case 'p': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); - break; - case 'd': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); - break; - case 'k': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); - break; - case 'r': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); - break; - case 'v': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - /* Too many arguments. */ - argp_usage (state); - } - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - /* Not enough arguments. */ - argp_usage (state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; -#endif /* HAVE_ARGP_H */ - -int main(int argc, char **argv){ - ssh_session session; - ssh_bind sshbind; - ssh_event mainloop; - ssh_session client_session; - - struct ssh_server_callbacks_struct cb = { - .userdata = NULL, - .auth_password_function = auth_password, - .auth_gssapi_mic_function = auth_gssapi_mic, - .channel_open_request_session_function = new_session_channel - }; - - char buf[2048]; - char host[128]=""; - char *ptr; - int i,r, rc; - - sshbind=ssh_bind_new(); - session=ssh_new(); - - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, "sshd_rsa"); - -#ifdef HAVE_ARGP_H - /* - * Parse our arguments; every option seen by parse_opt will - * be reflected in arguments. - */ - argp_parse (&argp, argc, argv, 0, 0, sshbind); -#else - (void) argc; - (void) argv; -#endif - - if(ssh_bind_listen(sshbind)<0){ - printf("Error listening to socket: %s\n",ssh_get_error(sshbind)); - return 1; - } - r=ssh_bind_accept(sshbind,session); - if(r==SSH_ERROR){ - printf("error accepting a connection : %s\n",ssh_get_error(sshbind)); - return 1; - } - ssh_callbacks_init(&cb); - ssh_set_server_callbacks(session, &cb); - - if (ssh_handle_key_exchange(session)) { - printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); - return 1; - } - ssh_set_auth_methods(session,SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_GSSAPI_MIC); - mainloop = ssh_event_new(); - ssh_event_add_session(mainloop, session); - - while (!(authenticated && chan != NULL)){ - if(error) - break; - r = ssh_event_dopoll(mainloop, -1); - if (r == SSH_ERROR){ - printf("Error : %s\n",ssh_get_error(session)); - ssh_disconnect(session); - return 1; - } - } - if(error){ - printf("Error, exiting loop\n"); - return 1; - } else - printf("Authenticated and got a channel\n"); - if (!client_creds){ - snprintf(buf,sizeof(buf), "Sorry, but you do not have forwardable tickets. Try again with -K\r\n"); - ssh_channel_write(chan,buf,strlen(buf)); - printf("%s",buf); - ssh_disconnect(session); - return 1; - } - snprintf(buf,sizeof(buf), "Hello %s, welcome to the Sample SSH proxy.\r\nPlease select your destination: ", username); - ssh_channel_write(chan, buf, strlen(buf)); - do{ - i=ssh_channel_read(chan,buf, 2048, 0); - if(i>0) { - ssh_channel_write(chan, buf, i); - if(strlen(host) + i < sizeof(host)){ - strncat(host, buf, i); - } - if (strchr(host, '\x0d')) { - *strchr(host, '\x0d')='\0'; - ssh_channel_write(chan, "\n", 1); - break; - } - } else { - printf ("Error: %s\n", ssh_get_error(session) ); - return 1; - } - } while (i>0); - snprintf(buf,sizeof(buf),"Trying to connect to \"%s\"\r\n", host); - ssh_channel_write(chan, buf, strlen(buf)); - printf("%s",buf); - - client_session = ssh_new(); - - /* ssh servers expect username without realm */ - ptr = strchr(username,'@'); - if(ptr) - *ptr= '\0'; - ssh_options_set(client_session, SSH_OPTIONS_HOST, host); - ssh_options_set(client_session, SSH_OPTIONS_USER, username); - ssh_gssapi_set_creds(client_session, client_creds); - rc = ssh_connect(client_session); - if (rc != SSH_OK){ - printf("Error connecting to %s: %s", host, ssh_get_error(client_session)); - return 1; - } - rc = ssh_userauth_none(client_session, NULL); - if(rc == SSH_AUTH_SUCCESS){ - printf("Authenticated using method none\n"); - } else { - rc = ssh_userauth_gssapi(client_session); - if(rc != SSH_AUTH_SUCCESS){ - printf("GSSAPI Authentication failed: %s\n",ssh_get_error(client_session)); - return 1; - } - } - snprintf(buf,sizeof(buf), "Authentication success\r\n"); - printf("%s",buf); - ssh_channel_write(chan,buf,strlen(buf)); - ssh_disconnect(client_session); - ssh_disconnect(session); - ssh_bind_free(sshbind); - ssh_finalize(); - return 0; -} - diff --git a/libssh/examples/sample.c b/libssh/examples/sample.c deleted file mode 100644 index 616372cb..00000000 --- a/libssh/examples/sample.c +++ /dev/null @@ -1,514 +0,0 @@ -/* client.c */ -/* -Copyright 2003-2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ - -#include "config.h" -#include -#include -#include - -#include -#include - -#ifdef HAVE_TERMIOS_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_PTY_H -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include - - -#include "examples_common.h" -#define MAXCMD 10 - -static char *host; -static char *user; -static char *cmds[MAXCMD]; -static struct termios terminal; - -static char *pcap_file=NULL; - -static char *proxycommand; - -static int auth_callback(const char *prompt, char *buf, size_t len, - int echo, int verify, void *userdata) { - (void) verify; - (void) userdata; - - return ssh_getpass(prompt, buf, len, echo, verify); -} - -struct ssh_callbacks_struct cb = { - .auth_function=auth_callback, - .userdata=NULL -}; - -static void add_cmd(char *cmd){ - int n; - - for (n = 0; (n < MAXCMD) && cmds[n] != NULL; n++); - - if (n == MAXCMD) { - return; - } - cmds[n]=strdup(cmd); -} - -static void usage(){ - fprintf(stderr,"Usage : ssh [options] [login@]hostname\n" - "sample client - libssh-%s\n" - "Options :\n" - " -l user : log in as user\n" - " -p port : connect to port\n" - " -d : use DSS to verify host public key\n" - " -r : use RSA to verify host public key\n" -#ifdef WITH_PCAP - " -P file : create a pcap debugging file\n" -#endif -#ifndef _WIN32 - " -T proxycommand : command to execute as a socket proxy\n" -#endif - , - ssh_version(0)); - exit(0); -} - -static int opts(int argc, char **argv){ - int i; -// for(i=0;ic_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); - termios_p->c_oflag &= ~OPOST; - termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); - termios_p->c_cflag &= ~(CSIZE|PARENB); - termios_p->c_cflag |= CS8; -} -#endif - - -static void do_cleanup(int i) { - /* unused variable */ - (void) i; - - tcsetattr(0,TCSANOW,&terminal); -} - -static void do_exit(int i) { - /* unused variable */ - (void) i; - - do_cleanup(0); - exit(0); -} - -ssh_channel chan; -int signal_delayed=0; - -static void sigwindowchanged(int i){ - (void) i; - signal_delayed=1; -} - -static void setsignal(void){ - signal(SIGWINCH, sigwindowchanged); - signal_delayed=0; -} - -static void sizechanged(void){ - struct winsize win = { 0, 0, 0, 0 }; - ioctl(1, TIOCGWINSZ, &win); - ssh_channel_change_pty_size(chan,win.ws_col, win.ws_row); -// printf("Changed pty size\n"); - setsignal(); -} - -/* There are two flavors of select loop: the one based on - * ssh_select and the one based on channel_select. - * The ssh_select one permits you to give your own file descriptors to - * follow. It is thus a complete select loop. - * The second one only selects on channels. It is simplier to use - * but doesn't permit you to fill in your own file descriptor. It is - * more adapted if you can't use ssh_select as a main loop (because - * you already have another main loop system). - */ - -#ifdef USE_CHANNEL_SELECT - -/* channel_select base main loop, with a standard select(2) - */ -static void select_loop(ssh_session session,ssh_channel channel){ - fd_set fds; - struct timeval timeout; - char buffer[4096]; - ssh_buffer readbuf=ssh_buffer_new(); - ssh_channel channels[2]; - int lus; - int eof=0; - int maxfd; - int ret; - while(channel){ - /* when a signal is caught, ssh_select will return - * with SSH_EINTR, which means it should be started - * again. It lets you handle the signal the faster you - * can, like in this window changed example. Of course, if - * your signal handler doesn't call libssh at all, you're - * free to handle signals directly in sighandler. - */ - do{ - FD_ZERO(&fds); - if(!eof) - FD_SET(0,&fds); - timeout.tv_sec=30; - timeout.tv_usec=0; - FD_SET(ssh_get_fd(session),&fds); - maxfd=ssh_get_fd(session)+1; - ret=select(maxfd,&fds,NULL,NULL,&timeout); - if(ret==EINTR) - continue; - if(FD_ISSET(0,&fds)){ - lus=read(0,buffer,sizeof(buffer)); - if(lus) - ssh_channel_write(channel,buffer,lus); - else { - eof=1; - ssh_channel_send_eof(channel); - } - } - if(FD_ISSET(ssh_get_fd(session),&fds)){ - ssh_set_fd_toread(session); - } - channels[0]=channel; // set the first channel we want to read from - channels[1]=NULL; - ret=ssh_channel_select(channels,NULL,NULL,NULL); // no specific timeout - just poll - if(signal_delayed) - sizechanged(); - } while (ret==EINTR || ret==SSH_EINTR); - - // we already looked for input from stdin. Now, we are looking for input from the channel - - if(channel && ssh_channel_is_closed(channel)){ - ssh_channel_free(channel); - channel=NULL; - channels[0]=NULL; - } - if(channels[0]){ - while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,0)>0){ - lus=channel_read_buffer(channel,readbuf,0,0); - if(lus==-1){ - fprintf(stderr, "Error reading channel: %s\n", - ssh_get_error(session)); - return; - } - if(lus==0){ - ssh_channel_free(channel); - channel=channels[0]=NULL; - } else - if (write(1,ssh_buffer_get_begin(readbuf),lus) < 0) { - fprintf(stderr, "Error writing to buffer\n"); - return; - } - } - while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,1)>0){ /* stderr */ - lus=channel_read_buffer(channel,readbuf,0,1); - if(lus==-1){ - fprintf(stderr, "Error reading channel: %s\n", - ssh_get_error(session)); - return; - } - if(lus==0){ - ssh_channel_free(channel); - channel=channels[0]=NULL; - } else - if (write(2,ssh_buffer_get_begin(readbuf),lus) < 0) { - fprintf(stderr, "Error writing to buffer\n"); - return; - } - } - } - if(channel && ssh_channel_is_closed(channel)){ - ssh_channel_free(channel); - channel=NULL; - } - } - ssh_buffer_free(readbuf); -} -#else /* CHANNEL_SELECT */ - -static void select_loop(ssh_session session,ssh_channel channel){ - fd_set fds; - struct timeval timeout; - char buffer[4096]; - /* channels will be set to the channels to poll. - * outchannels will contain the result of the poll - */ - ssh_channel channels[2], outchannels[2]; - int lus; - int eof=0; - int maxfd; - unsigned int r; - int ret; - while(channel){ - do{ - FD_ZERO(&fds); - if(!eof) - FD_SET(0,&fds); - timeout.tv_sec=30; - timeout.tv_usec=0; - FD_SET(ssh_get_fd(session),&fds); - maxfd=ssh_get_fd(session)+1; - channels[0]=channel; // set the first channel we want to read from - channels[1]=NULL; - ret=ssh_select(channels,outchannels,maxfd,&fds,&timeout); - if(signal_delayed) - sizechanged(); - if(ret==EINTR) - continue; - if(FD_ISSET(0,&fds)){ - lus=read(0,buffer,sizeof(buffer)); - if(lus) - ssh_channel_write(channel,buffer,lus); - else { - eof=1; - ssh_channel_send_eof(channel); - } - } - if(channel && ssh_channel_is_closed(channel)){ - ssh_channel_free(channel); - channel=NULL; - channels[0]=NULL; - } - if(outchannels[0]){ - while(channel && ssh_channel_is_open(channel) && (r = ssh_channel_poll(channel,0))!=0){ - lus=ssh_channel_read(channel,buffer,sizeof(buffer) > r ? r : sizeof(buffer),0); - if(lus==-1){ - fprintf(stderr, "Error reading channel: %s\n", - ssh_get_error(session)); - return; - } - if(lus==0){ - ssh_channel_free(channel); - channel=channels[0]=NULL; - } else - if (write(1,buffer,lus) < 0) { - fprintf(stderr, "Error writing to buffer\n"); - return; - } - } - while(channel && ssh_channel_is_open(channel) && (r = ssh_channel_poll(channel,1))!=0){ /* stderr */ - lus=ssh_channel_read(channel,buffer,sizeof(buffer) > r ? r : sizeof(buffer),1); - if(lus==-1){ - fprintf(stderr, "Error reading channel: %s\n", - ssh_get_error(session)); - return; - } - if(lus==0){ - ssh_channel_free(channel); - channel=channels[0]=NULL; - } else - if (write(2,buffer,lus) < 0) { - fprintf(stderr, "Error writing to buffer\n"); - return; - } - } - } - if(channel && ssh_channel_is_closed(channel)){ - ssh_channel_free(channel); - channel=NULL; - } - } while (ret==EINTR || ret==SSH_EINTR); - - } -} - -#endif - -static void shell(ssh_session session){ - ssh_channel channel; - struct termios terminal_local; - int interactive=isatty(0); - channel = ssh_channel_new(session); - if(interactive){ - tcgetattr(0,&terminal_local); - memcpy(&terminal,&terminal_local,sizeof(struct termios)); - } - if(ssh_channel_open_session(channel)){ - printf("error opening channel : %s\n",ssh_get_error(session)); - return; - } - chan=channel; - if(interactive){ - ssh_channel_request_pty(channel); - sizechanged(); - } - if(ssh_channel_request_shell(channel)){ - printf("Requesting shell : %s\n",ssh_get_error(session)); - return; - } - if(interactive){ - cfmakeraw(&terminal_local); - tcsetattr(0,TCSANOW,&terminal_local); - setsignal(); - } - signal(SIGTERM,do_cleanup); - select_loop(session,channel); - if(interactive) - do_cleanup(0); -} - -static void batch_shell(ssh_session session){ - ssh_channel channel; - char buffer[1024]; - int i,s=0; - for(i=0;i -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif - -#include -#include - -#include "examples_common.h" -#ifdef WITH_SFTP - -static int verbosity; -static char *destination; - -#define DATALEN 65536 -static void do_sftp(ssh_session session){ - sftp_session sftp=sftp_new(session); - sftp_dir dir; - sftp_attributes file; - sftp_statvfs_t sftpstatvfs; - struct statvfs sysstatvfs; - sftp_file fichier; - sftp_file to; - int len=1; - unsigned int i; - char data[DATALEN]={0}; - char *lnk; - - unsigned int count; - - if(!sftp){ - fprintf(stderr, "sftp error initialising channel: %s\n", - ssh_get_error(session)); - return; - } - if(sftp_init(sftp)){ - fprintf(stderr, "error initialising sftp: %s\n", - ssh_get_error(session)); - return; - } - - printf("Additional SFTP extensions provided by the server:\n"); - count = sftp_extensions_get_count(sftp); - for (i = 0; i < count; i++) { - printf("\t%s, version: %s\n", - sftp_extensions_get_name(sftp, i), - sftp_extensions_get_data(sftp, i)); - } - - /* test symlink and readlink */ - if (sftp_symlink(sftp, "/tmp/this_is_the_link", - "/tmp/sftp_symlink_test") < 0) { - fprintf(stderr, "Could not create link (%s)\n", ssh_get_error(session)); - return; - } - - lnk = sftp_readlink(sftp, "/tmp/sftp_symlink_test"); - if (lnk == NULL) { - fprintf(stderr, "Could not read link (%s)\n", ssh_get_error(session)); - return; - } - printf("readlink /tmp/sftp_symlink_test: %s\n", lnk); - - sftp_unlink(sftp, "/tmp/sftp_symlink_test"); - - if (sftp_extension_supported(sftp, "statvfs@openssh.com", "2")) { - sftpstatvfs = sftp_statvfs(sftp, "/tmp"); - if (sftpstatvfs == NULL) { - fprintf(stderr, "statvfs failed (%s)\n", ssh_get_error(session)); - return; - } - - printf("sftp statvfs:\n" - "\tfile system block size: %llu\n" - "\tfundamental fs block size: %llu\n" - "\tnumber of blocks (unit f_frsize): %llu\n" - "\tfree blocks in file system: %llu\n" - "\tfree blocks for non-root: %llu\n" - "\ttotal file inodes: %llu\n" - "\tfree file inodes: %llu\n" - "\tfree file inodes for to non-root: %llu\n" - "\tfile system id: %llu\n" - "\tbit mask of f_flag values: %llu\n" - "\tmaximum filename length: %llu\n", - (unsigned long long) sftpstatvfs->f_bsize, - (unsigned long long) sftpstatvfs->f_frsize, - (unsigned long long) sftpstatvfs->f_blocks, - (unsigned long long) sftpstatvfs->f_bfree, - (unsigned long long) sftpstatvfs->f_bavail, - (unsigned long long) sftpstatvfs->f_files, - (unsigned long long) sftpstatvfs->f_ffree, - (unsigned long long) sftpstatvfs->f_favail, - (unsigned long long) sftpstatvfs->f_fsid, - (unsigned long long) sftpstatvfs->f_flag, - (unsigned long long) sftpstatvfs->f_namemax); - - sftp_statvfs_free(sftpstatvfs); - - if (statvfs("/tmp", &sysstatvfs) < 0) { - fprintf(stderr, "statvfs failed (%s)\n", strerror(errno)); - return; - } - - printf("sys statvfs:\n" - "\tfile system block size: %llu\n" - "\tfundamental fs block size: %llu\n" - "\tnumber of blocks (unit f_frsize): %llu\n" - "\tfree blocks in file system: %llu\n" - "\tfree blocks for non-root: %llu\n" - "\ttotal file inodes: %llu\n" - "\tfree file inodes: %llu\n" - "\tfree file inodes for to non-root: %llu\n" - "\tfile system id: %llu\n" - "\tbit mask of f_flag values: %llu\n" - "\tmaximum filename length: %llu\n", - (unsigned long long) sysstatvfs.f_bsize, - (unsigned long long) sysstatvfs.f_frsize, - (unsigned long long) sysstatvfs.f_blocks, - (unsigned long long) sysstatvfs.f_bfree, - (unsigned long long) sysstatvfs.f_bavail, - (unsigned long long) sysstatvfs.f_files, - (unsigned long long) sysstatvfs.f_ffree, - (unsigned long long) sysstatvfs.f_favail, - (unsigned long long) sysstatvfs.f_fsid, - (unsigned long long) sysstatvfs.f_flag, - (unsigned long long) sysstatvfs.f_namemax); - } - - /* the connection is made */ - /* opening a directory */ - dir=sftp_opendir(sftp,"./"); - if(!dir) { - fprintf(stderr, "Directory not opened(%s)\n", ssh_get_error(session)); - return ; - } - /* reading the whole directory, file by file */ - while((file=sftp_readdir(sftp,dir))){ - fprintf(stderr, "%30s(%.8o) : %s(%.5d) %s(%.5d) : %.10llu bytes\n", - file->name, - file->permissions, - file->owner, - file->uid, - file->group, - file->gid, - (long long unsigned int) file->size); - sftp_attributes_free(file); - } - /* when file=NULL, an error has occured OR the directory listing is end of file */ - if(!sftp_dir_eof(dir)){ - fprintf(stderr, "Error: %s\n", ssh_get_error(session)); - return; - } - if(sftp_closedir(dir)){ - fprintf(stderr, "Error: %s\n", ssh_get_error(session)); - return; - } - /* this will open a file and copy it into your /home directory */ - /* the small buffer size was intended to stress the library. of course, you can use a buffer till 20kbytes without problem */ - - fichier=sftp_open(sftp,"/usr/bin/ssh",O_RDONLY, 0); - if(!fichier){ - fprintf(stderr, "Error opening /usr/bin/ssh: %s\n", - ssh_get_error(session)); - return; - } - /* open a file for writing... */ - to=sftp_open(sftp,"ssh-copy",O_WRONLY | O_CREAT, 0700); - if(!to){ - fprintf(stderr, "Error opening ssh-copy for writing: %s\n", - ssh_get_error(session)); - return; - } - while((len=sftp_read(fichier,data,4096)) > 0){ - if(sftp_write(to,data,len)!=len){ - fprintf(stderr, "Error writing %d bytes: %s\n", - len, ssh_get_error(session)); - return; - } - } - printf("finished\n"); - if(len<0) - fprintf(stderr, "Error reading file: %s\n", ssh_get_error(session)); - sftp_close(fichier); - sftp_close(to); - printf("fichiers ferm\n"); - to=sftp_open(sftp,"/tmp/grosfichier",O_WRONLY|O_CREAT, 0644); - for(i=0;i<1000;++i){ - len=sftp_write(to,data,DATALEN); - printf("wrote %d bytes\n",len); - if(len != DATALEN){ - printf("chunk %d : %d (%s)\n",i,len,ssh_get_error(session)); - } - } - sftp_close(to); - - /* close the sftp session */ - sftp_free(sftp); - printf("sftp session terminated\n"); -} - -static void usage(const char *argv0){ - fprintf(stderr,"Usage : %s [-v] remotehost\n" - "sample sftp test client - libssh-%s\n" - "Options :\n" - " -v : increase log verbosity\n", - argv0, - ssh_version(0)); - exit(0); -} - -static int opts(int argc, char **argv){ - int i; - while((i=getopt(argc,argv,"v"))!=-1){ - switch(i){ - case 'v': - verbosity++; - break; - default: - fprintf(stderr,"unknown option %c\n",optopt); - usage(argv[0]); - return -1; - } - } - - destination=argv[optind]; - if(destination == NULL){ - usage(argv[0]); - return -1; - } - return 0; -} - -int main(int argc, char **argv){ - ssh_session session; - if(opts(argc,argv)<0) - return EXIT_FAILURE; - session=connect_ssh(destination,NULL,verbosity); - if(session == NULL) - return EXIT_FAILURE; - do_sftp(session); - ssh_disconnect(session); - ssh_free(session); - return 0; -} - - - -#endif diff --git a/libssh/examples/samplesshd-cb.c b/libssh/examples/samplesshd-cb.c deleted file mode 100644 index f93ab4b4..00000000 --- a/libssh/examples/samplesshd-cb.c +++ /dev/null @@ -1,306 +0,0 @@ -/* This is a sample implementation of a libssh based SSH server */ -/* -Copyright 2003-2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ - -#include "config.h" - -#include -#include -#include - -#ifdef HAVE_ARGP_H -#include -#endif -#include -#include -#include - -#ifndef KEYS_FOLDER -#ifdef _WIN32 -#define KEYS_FOLDER -#else -#define KEYS_FOLDER "/etc/ssh/" -#endif -#endif - -#define USER "myuser" -#define PASSWORD "mypassword" - -static int authenticated=0; -static int tries = 0; -static int error = 0; -static ssh_channel chan=NULL; - -static int auth_password(ssh_session session, const char *user, - const char *password, void *userdata){ - (void)userdata; - printf("Authenticating user %s pwd %s\n",user, password); - if(strcmp(user,USER) == 0 && strcmp(password, PASSWORD) == 0){ - authenticated = 1; - printf("Authenticated\n"); - return SSH_AUTH_SUCCESS; - } - if (tries >= 3){ - printf("Too many authentication tries\n"); - ssh_disconnect(session); - error = 1; - return SSH_AUTH_DENIED; - } - tries++; - return SSH_AUTH_DENIED; -} - -static int auth_gssapi_mic(ssh_session session, const char *user, const char *principal, void *userdata){ - ssh_gssapi_creds creds = ssh_gssapi_get_creds(session); - (void)userdata; - printf("Authenticating user %s with gssapi principal %s\n",user, principal); - if (creds != NULL) - printf("Received some gssapi credentials\n"); - else - printf("Not received any forwardable creds\n"); - printf("authenticated\n"); - authenticated = 1; - return SSH_AUTH_SUCCESS; -} - -static int pty_request(ssh_session session, ssh_channel channel, const char *term, - int x,int y, int px, int py, void *userdata){ - (void) session; - (void) channel; - (void) term; - (void) x; - (void) y; - (void) px; - (void) py; - (void) userdata; - printf("Allocated terminal\n"); - return 0; -} - -static int shell_request(ssh_session session, ssh_channel channel, void *userdata){ - (void)session; - (void)channel; - (void)userdata; - printf("Allocated shell\n"); - return 0; -} -struct ssh_channel_callbacks_struct channel_cb = { - .channel_pty_request_function = pty_request, - .channel_shell_request_function = shell_request -}; - -static ssh_channel new_session_channel(ssh_session session, void *userdata){ - (void) session; - (void) userdata; - if(chan != NULL) - return NULL; - printf("Allocated session channel\n"); - chan = ssh_channel_new(session); - ssh_callbacks_init(&channel_cb); - ssh_set_channel_callbacks(chan, &channel_cb); - return chan; -} - - -#ifdef HAVE_ARGP_H -const char *argp_program_version = "libssh server example " -SSH_STRINGIFY(LIBSSH_VERSION); -const char *argp_program_bug_address = ""; - -/* Program documentation. */ -static char doc[] = "libssh -- a Secure Shell protocol implementation"; - -/* A description of the arguments we accept. */ -static char args_doc[] = "BINDADDR"; - -/* The options we understand. */ -static struct argp_option options[] = { - { - .name = "port", - .key = 'p', - .arg = "PORT", - .flags = 0, - .doc = "Set the port to bind.", - .group = 0 - }, - { - .name = "hostkey", - .key = 'k', - .arg = "FILE", - .flags = 0, - .doc = "Set the host key.", - .group = 0 - }, - { - .name = "dsakey", - .key = 'd', - .arg = "FILE", - .flags = 0, - .doc = "Set the dsa key.", - .group = 0 - }, - { - .name = "rsakey", - .key = 'r', - .arg = "FILE", - .flags = 0, - .doc = "Set the rsa key.", - .group = 0 - }, - { - .name = "verbose", - .key = 'v', - .arg = NULL, - .flags = 0, - .doc = "Get verbose output.", - .group = 0 - }, - {NULL, 0, NULL, 0, NULL, 0} -}; - -/* Parse a single option. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* Get the input argument from argp_parse, which we - * know is a pointer to our arguments structure. - */ - ssh_bind sshbind = state->input; - - switch (key) { - case 'p': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); - break; - case 'd': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); - break; - case 'k': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); - break; - case 'r': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); - break; - case 'v': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - /* Too many arguments. */ - argp_usage (state); - } - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - /* Not enough arguments. */ - argp_usage (state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; -#endif /* HAVE_ARGP_H */ - -int main(int argc, char **argv){ - ssh_session session; - ssh_bind sshbind; - ssh_event mainloop; - struct ssh_server_callbacks_struct cb = { - .userdata = NULL, - .auth_password_function = auth_password, - .auth_gssapi_mic_function = auth_gssapi_mic, - .channel_open_request_session_function = new_session_channel - }; - - char buf[2048]; - int i; - int r; - - sshbind=ssh_bind_new(); - session=ssh_new(); - - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key"); - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key"); - -#ifdef HAVE_ARGP_H - /* - * Parse our arguments; every option seen by parse_opt will - * be reflected in arguments. - */ - argp_parse (&argp, argc, argv, 0, 0, sshbind); -#else - (void) argc; - (void) argv; -#endif - - if(ssh_bind_listen(sshbind)<0){ - printf("Error listening to socket: %s\n",ssh_get_error(sshbind)); - return 1; - } - r=ssh_bind_accept(sshbind,session); - if(r==SSH_ERROR){ - printf("error accepting a connection : %s\n",ssh_get_error(sshbind)); - return 1; - } - ssh_callbacks_init(&cb); - ssh_set_server_callbacks(session, &cb); - - if (ssh_handle_key_exchange(session)) { - printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); - return 1; - } - ssh_set_auth_methods(session,SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_GSSAPI_MIC); - mainloop = ssh_event_new(); - ssh_event_add_session(mainloop, session); - - while (!(authenticated && chan != NULL)){ - if(error) - break; - r = ssh_event_dopoll(mainloop, -1); - if (r == SSH_ERROR){ - printf("Error : %s\n",ssh_get_error(session)); - ssh_disconnect(session); - return 1; - } - } - if(error){ - printf("Error, exiting loop\n"); - } else - printf("Authenticated and got a channel\n"); - do{ - i=ssh_channel_read(chan,buf, 2048, 0); - if(i>0) { - ssh_channel_write(chan, buf, i); - if (write(1,buf,i) < 0) { - printf("error writing to buffer\n"); - return 1; - } - if (buf[0] == '\x0d') { - if (write(1, "\n", 1) < 0) { - printf("error writing to buffer\n"); - return 1; - } - ssh_channel_write(chan, "\n", 1); - } - } - } while (i>0); - ssh_disconnect(session); - ssh_bind_free(sshbind); - ssh_finalize(); - return 0; -} - diff --git a/libssh/examples/samplesshd-kbdint.c b/libssh/examples/samplesshd-kbdint.c deleted file mode 100644 index 5c2c461e..00000000 --- a/libssh/examples/samplesshd-kbdint.c +++ /dev/null @@ -1,413 +0,0 @@ -/* This is a sample implementation of a libssh based SSH server */ -/* -Copyright 2003-2011 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ - -#include "config.h" - -#include -#include - -#ifdef HAVE_ARGP_H -#include -#endif -#include -#include -#include - -#define SSHD_USER "libssh" -#define SSHD_PASSWORD "libssh" - -#ifndef KEYS_FOLDER -#ifdef _WIN32 -#define KEYS_FOLDER -#else -#define KEYS_FOLDER "/etc/ssh/" -#endif -#endif - -static int port = 22; - -#ifdef WITH_PCAP -static const char *pcap_file = "debug.server.pcap"; -static ssh_pcap_file pcap; - -static void set_pcap(ssh_session session){ - if(!pcap_file) - return; - pcap=ssh_pcap_file_new(); - if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ - printf("Error opening pcap file\n"); - ssh_pcap_file_free(pcap); - pcap=NULL; - return; - } - ssh_set_pcap_file(session,pcap); -} - -static void cleanup_pcap(void) { - ssh_pcap_file_free(pcap); - pcap=NULL; -} -#endif - - -static int auth_password(const char *user, const char *password){ - if(strcmp(user, SSHD_USER)) - return 0; - if(strcmp(password, SSHD_PASSWORD)) - return 0; - return 1; // authenticated -} -#ifdef HAVE_ARGP_H -const char *argp_program_version = "libssh server example " - SSH_STRINGIFY(LIBSSH_VERSION); -const char *argp_program_bug_address = ""; - -/* Program documentation. */ -static char doc[] = "libssh -- a Secure Shell protocol implementation"; - -/* A description of the arguments we accept. */ -static char args_doc[] = "BINDADDR"; - -/* The options we understand. */ -static struct argp_option options[] = { - { - .name = "port", - .key = 'p', - .arg = "PORT", - .flags = 0, - .doc = "Set the port to bind.", - .group = 0 - }, - { - .name = "hostkey", - .key = 'k', - .arg = "FILE", - .flags = 0, - .doc = "Set the host key.", - .group = 0 - }, - { - .name = "dsakey", - .key = 'd', - .arg = "FILE", - .flags = 0, - .doc = "Set the dsa key.", - .group = 0 - }, - { - .name = "rsakey", - .key = 'r', - .arg = "FILE", - .flags = 0, - .doc = "Set the rsa key.", - .group = 0 - }, - { - .name = "verbose", - .key = 'v', - .arg = NULL, - .flags = 0, - .doc = "Get verbose output.", - .group = 0 - }, - {NULL, 0, 0, 0, NULL, 0} -}; - -/* Parse a single option. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* Get the input argument from argp_parse, which we - * know is a pointer to our arguments structure. - */ - ssh_bind sshbind = state->input; - - switch (key) { - case 'p': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); - port = atoi(arg); - break; - case 'd': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); - break; - case 'k': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); - break; - case 'r': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); - break; - case 'v': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "3"); - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - /* Too many arguments. */ - argp_usage (state); - } - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - /* Not enough arguments. */ - argp_usage (state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; -#endif /* HAVE_ARGP_H */ - -static const char *name; -static const char *instruction; -static const char *prompts[2]; -static char echo[] = { 1, 0 }; - -static int kbdint_check_response(ssh_session session) { - int count; - - count = ssh_userauth_kbdint_getnanswers(session); - if(count != 2) { - instruction = "Something weird happened :("; - return 0; - } - if(strcasecmp("Arthur Dent", - ssh_userauth_kbdint_getanswer(session, 0)) != 0) { - instruction = "OK, this is not YOUR name, " - "but it's a reference to the HGTG..."; - prompts[0] = "The main character's full name: "; - return 0; - } - if(strcmp("42", ssh_userauth_kbdint_getanswer(session, 1)) != 0) { - instruction = "Make an effort !!! What is the Answer to the Ultimate " - "Question of Life, the Universe, and Everything ?"; - prompts[1] = "Answer to the Ultimate Question of Life, the Universe, " - "and Everything: "; - return 0; - } - - return 1; -} - -static int authenticate(ssh_session session) { - ssh_message message; - - name = "\n\nKeyboard-Interactive Fancy Authentication\n"; - instruction = "Please enter your real name and your password"; - prompts[0] = "Real name: "; - prompts[1] = "Password: "; - - do { - message=ssh_message_get(session); - if(!message) - break; - switch(ssh_message_type(message)){ - case SSH_REQUEST_AUTH: - switch(ssh_message_subtype(message)){ - case SSH_AUTH_METHOD_PASSWORD: - printf("User %s wants to auth with pass %s\n", - ssh_message_auth_user(message), - ssh_message_auth_password(message)); - if(auth_password(ssh_message_auth_user(message), - ssh_message_auth_password(message))){ - ssh_message_auth_reply_success(message,0); - ssh_message_free(message); - return 1; - } - ssh_message_auth_set_methods(message, - SSH_AUTH_METHOD_PASSWORD | - SSH_AUTH_METHOD_INTERACTIVE); - // not authenticated, send default message - ssh_message_reply_default(message); - break; - - case SSH_AUTH_METHOD_INTERACTIVE: - if(!ssh_message_auth_kbdint_is_response(message)) { - printf("User %s wants to auth with kbdint\n", - ssh_message_auth_user(message)); - ssh_message_auth_interactive_request(message, name, - instruction, 2, prompts, echo); - } else { - if(kbdint_check_response(session)) { - ssh_message_auth_reply_success(message,0); - ssh_message_free(message); - return 1; - } - ssh_message_auth_set_methods(message, - SSH_AUTH_METHOD_PASSWORD | - SSH_AUTH_METHOD_INTERACTIVE); - ssh_message_reply_default(message); - } - break; - case SSH_AUTH_METHOD_NONE: - default: - printf("User %s wants to auth with unknown auth %d\n", - ssh_message_auth_user(message), - ssh_message_subtype(message)); - ssh_message_auth_set_methods(message, - SSH_AUTH_METHOD_PASSWORD | - SSH_AUTH_METHOD_INTERACTIVE); - ssh_message_reply_default(message); - break; - } - break; - default: - ssh_message_auth_set_methods(message, - SSH_AUTH_METHOD_PASSWORD | - SSH_AUTH_METHOD_INTERACTIVE); - ssh_message_reply_default(message); - } - ssh_message_free(message); - } while (1); - return 0; -} - -int main(int argc, char **argv){ - ssh_session session; - ssh_bind sshbind; - ssh_message message; - ssh_channel chan=0; - char buf[2048]; - int auth=0; - int shell=0; - int i; - int r; - - sshbind=ssh_bind_new(); - session=ssh_new(); - - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, - KEYS_FOLDER "ssh_host_dsa_key"); - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, - KEYS_FOLDER "ssh_host_rsa_key"); - -#ifdef HAVE_ARGP_H - /* - * Parse our arguments; every option seen by parse_opt will - * be reflected in arguments. - */ - argp_parse (&argp, argc, argv, 0, 0, sshbind); -#else - (void) argc; - (void) argv; -#endif -#ifdef WITH_PCAP - set_pcap(session); -#endif - - if(ssh_bind_listen(sshbind)<0){ - printf("Error listening to socket: %s\n", ssh_get_error(sshbind)); - return 1; - } - printf("Started sample libssh sshd on port %d\n", port); - printf("You can login as the user %s with the password %s\n", SSHD_USER, - SSHD_PASSWORD); - r = ssh_bind_accept(sshbind, session); - if(r==SSH_ERROR){ - printf("Error accepting a connection: %s\n", ssh_get_error(sshbind)); - return 1; - } - if (ssh_handle_key_exchange(session)) { - printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session)); - return 1; - } - - /* proceed to authentication */ - auth = authenticate(session); - if(!auth){ - printf("Authentication error: %s\n", ssh_get_error(session)); - ssh_disconnect(session); - return 1; - } - - - /* wait for a channel session */ - do { - message = ssh_message_get(session); - if(message){ - if(ssh_message_type(message) == SSH_REQUEST_CHANNEL_OPEN && - ssh_message_subtype(message) == SSH_CHANNEL_SESSION) { - chan = ssh_message_channel_request_open_reply_accept(message); - ssh_message_free(message); - break; - } else { - ssh_message_reply_default(message); - ssh_message_free(message); - } - } else { - break; - } - } while(!chan); - - if(!chan) { - printf("Error: cleint did not ask for a channel session (%s)\n", - ssh_get_error(session)); - ssh_finalize(); - return 1; - } - - - /* wait for a shell */ - do { - message = ssh_message_get(session); - if(message != NULL) { - if(ssh_message_type(message) == SSH_REQUEST_CHANNEL && - ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_SHELL) { - shell = 1; - ssh_message_channel_request_reply_success(message); - ssh_message_free(message); - break; - } - ssh_message_reply_default(message); - ssh_message_free(message); - } else { - break; - } - } while(!shell); - - if(!shell) { - printf("Error: No shell requested (%s)\n", ssh_get_error(session)); - return 1; - } - - - printf("it works !\n"); - do{ - i=ssh_channel_read(chan,buf, 2048, 0); - if(i>0) { - if(*buf == '' || *buf == '') - break; - if(i == 1 && *buf == '\r') - ssh_channel_write(chan, "\r\n", 2); - else - ssh_channel_write(chan, buf, i); - if (write(1,buf,i) < 0) { - printf("error writing to buffer\n"); - return 1; - } - } - } while (i>0); - ssh_channel_close(chan); - ssh_disconnect(session); - ssh_bind_free(sshbind); -#ifdef WITH_PCAP - cleanup_pcap(); -#endif - ssh_finalize(); - return 0; -} - diff --git a/libssh/examples/scp_download.c b/libssh/examples/scp_download.c deleted file mode 100644 index ba8782a7..00000000 --- a/libssh/examples/scp_download.c +++ /dev/null @@ -1,175 +0,0 @@ -/* scp_download.c - * Sample implementation of a tiny SCP downloader client - */ - -/* -Copyright 2009 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. - */ - -#include -#include -#include -#include -#include - -#include -#include "examples_common.h" - -int verbosity=0; -const char *createcommand="rm -fr /tmp/libssh_tests && mkdir /tmp/libssh_tests && cd /tmp/libssh_tests && date > a && date > b && mkdir c && date > d"; -char *host=NULL; -static void usage(const char *argv0){ - fprintf(stderr,"Usage : %s [options] host\n" - "sample tiny scp downloader client - libssh-%s\n" - "This program will create files in /tmp and try to fetch them\n", -// "Options :\n", -// " -r : use RSA to verify host public key\n", - argv0, - ssh_version(0)); - exit(0); -} - -static int opts(int argc, char **argv){ - int i; - while((i=getopt(argc,argv,"v"))!=-1){ - switch(i){ - case 'v': - verbosity++; - break; - default: - fprintf(stderr,"unknown option %c\n",optopt); - usage(argv[0]); - return -1; - } - } - host = argv[optind]; - if(host == NULL) - usage(argv[0]); - return 0; -} - -static void create_files(ssh_session session){ - ssh_channel channel=ssh_channel_new(session); - char buffer[1]; - int rc; - - if(channel == NULL){ - fprintf(stderr,"Error creating channel: %s\n",ssh_get_error(session)); - exit(EXIT_FAILURE); - } - if(ssh_channel_open_session(channel) != SSH_OK){ - fprintf(stderr,"Error creating channel: %s\n",ssh_get_error(session)); - ssh_channel_free(channel); - exit(EXIT_FAILURE); - } - if(ssh_channel_request_exec(channel,createcommand) != SSH_OK){ - fprintf(stderr,"Error executing command: %s\n",ssh_get_error(session)); - ssh_channel_close(channel); - ssh_channel_free(channel); - exit(EXIT_FAILURE); - } - while(!ssh_channel_is_eof(channel)){ - rc = ssh_channel_read(channel,buffer,1,1); - if (rc != 1) { - fprintf(stderr, "Error reading from channel\n"); - ssh_channel_close(channel); - ssh_channel_free(channel); - return; - } - - rc = write(1, buffer, 1); - if (rc < 0) { - fprintf(stderr, "Error writing to buffer\n"); - ssh_channel_close(channel); - ssh_channel_free(channel); - return; - } - } - ssh_channel_close(channel); - ssh_channel_free(channel); -} - - -static int fetch_files(ssh_session session){ - int size; - char buffer[16384]; - int mode; - char *filename; - int r; - ssh_scp scp=ssh_scp_new(session, SSH_SCP_READ | SSH_SCP_RECURSIVE, "/tmp/libssh_tests/*"); - if(ssh_scp_init(scp) != SSH_OK){ - fprintf(stderr,"error initializing scp: %s\n",ssh_get_error(session)); - ssh_scp_free(scp); - return -1; - } - printf("Trying to download 3 files (a,b,d) and 1 directory (c)\n"); - do { - - r=ssh_scp_pull_request(scp); - switch(r){ - case SSH_SCP_REQUEST_NEWFILE: - size=ssh_scp_request_get_size(scp); - filename=strdup(ssh_scp_request_get_filename(scp)); - mode=ssh_scp_request_get_permissions(scp); - printf("downloading file %s, size %d, perms 0%o\n",filename,size,mode); - free(filename); - ssh_scp_accept_request(scp); - r=ssh_scp_read(scp,buffer,sizeof(buffer)); - if(r==SSH_ERROR){ - fprintf(stderr,"Error reading scp: %s\n",ssh_get_error(session)); - ssh_scp_close(scp); - ssh_scp_free(scp); - return -1; - } - printf("done\n"); - break; - case SSH_ERROR: - fprintf(stderr,"Error: %s\n",ssh_get_error(session)); - ssh_scp_close(scp); - ssh_scp_free(scp); - return -1; - case SSH_SCP_REQUEST_WARNING: - fprintf(stderr,"Warning: %s\n",ssh_scp_request_get_warning(scp)); - break; - case SSH_SCP_REQUEST_NEWDIR: - filename=strdup(ssh_scp_request_get_filename(scp)); - mode=ssh_scp_request_get_permissions(scp); - printf("downloading directory %s, perms 0%o\n",filename,mode); - free(filename); - ssh_scp_accept_request(scp); - break; - case SSH_SCP_REQUEST_ENDDIR: - printf("End of directory\n"); - break; - case SSH_SCP_REQUEST_EOF: - printf("End of requests\n"); - goto end; - } - } while (1); - end: - ssh_scp_close(scp); - ssh_scp_free(scp); - return 0; -} - -int main(int argc, char **argv){ - ssh_session session; - if(opts(argc,argv)<0) - return EXIT_FAILURE; - session=connect_ssh(host,NULL,verbosity); - if(session == NULL) - return EXIT_FAILURE; - create_files(session); - fetch_files(session); - ssh_disconnect(session); - ssh_free(session); - ssh_finalize(); - return 0; -} diff --git a/libssh/examples/senddata.c b/libssh/examples/senddata.c deleted file mode 100644 index acc1bebc..00000000 --- a/libssh/examples/senddata.c +++ /dev/null @@ -1,64 +0,0 @@ -#include - -#include -#include "examples_common.h" - -#define LIMIT 0x100000000 - -int main(void) { - ssh_session session; - ssh_channel channel; - char buffer[1024*1024]; - int rc; - uint64_t total=0; - uint64_t lastshown=4096; - session = connect_ssh("localhost", NULL, 0); - if (session == NULL) { - return 1; - } - - channel = ssh_channel_new(session);; - if (channel == NULL) { - ssh_disconnect(session); - return 1; - } - - rc = ssh_channel_open_session(channel); - if (rc < 0) { - ssh_channel_close(channel); - ssh_disconnect(session); - return 1; - } - - rc = ssh_channel_request_exec(channel, "cat > /dev/null"); - if (rc < 0) { - ssh_channel_close(channel); - ssh_disconnect(session); - return 1; - } - - - while ((rc = ssh_channel_write(channel, buffer, sizeof(buffer))) > 0) { - total += rc; - if(total/2 >= lastshown){ - printf("written %llx\n", (long long unsigned int) total); - lastshown=total; - } - if(total > LIMIT) - break; - } - - if (rc < 0) { - printf("error : %s\n",ssh_get_error(session)); - ssh_channel_close(channel); - ssh_disconnect(session); - return 1; - } - - ssh_channel_send_eof(channel); - ssh_channel_close(channel); - - ssh_disconnect(session); - - return 0; -} diff --git a/libssh/examples/ssh_server_fork.c b/libssh/examples/ssh_server_fork.c deleted file mode 100644 index 837db6fe..00000000 --- a/libssh/examples/ssh_server_fork.c +++ /dev/null @@ -1,697 +0,0 @@ -/* This is a sample implementation of a libssh based SSH server */ -/* -Copyright 2014 Audrius Butkevicius - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. -*/ - -#include "config.h" - -#include -#include -#include - -#ifdef HAVE_ARGP_H -#include -#endif -#include -#ifdef HAVE_LIBUTIL_H -#include -#endif -#ifdef HAVE_PTY_H -#include -#endif -#include -#include -#ifdef HAVE_UTMP_H -#include -#endif -#ifdef HAVE_UTIL_H -#include -#endif -#include -#include -#include - -#ifndef KEYS_FOLDER -#ifdef _WIN32 -#define KEYS_FOLDER -#else -#define KEYS_FOLDER "/etc/ssh/" -#endif -#endif - -#define USER "myuser" -#define PASS "mypassword" -#define BUF_SIZE 1048576 -#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR) -#define SFTP_SERVER_PATH "/usr/lib/sftp-server" - -static void set_default_keys(ssh_bind sshbind, - int rsa_already_set, - int dsa_already_set, - int ecdsa_already_set) { - if (!rsa_already_set) { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, - KEYS_FOLDER "ssh_host_rsa_key"); - } - if (!dsa_already_set) { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, - KEYS_FOLDER "ssh_host_dsa_key"); - } - if (!ecdsa_already_set) { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, - KEYS_FOLDER "ssh_host_ecdsa_key"); - } -} - -#ifdef HAVE_ARGP_H -const char *argp_program_version = "libssh server example " -SSH_STRINGIFY(LIBSSH_VERSION); -const char *argp_program_bug_address = ""; - -/* Program documentation. */ -static char doc[] = "libssh -- a Secure Shell protocol implementation"; - -/* A description of the arguments we accept. */ -static char args_doc[] = "BINDADDR"; - -/* The options we understand. */ -static struct argp_option options[] = { - { - .name = "port", - .key = 'p', - .arg = "PORT", - .flags = 0, - .doc = "Set the port to bind.", - .group = 0 - }, - { - .name = "hostkey", - .key = 'k', - .arg = "FILE", - .flags = 0, - .doc = "Set a host key. Can be used multiple times. " - "Implies no default keys.", - .group = 0 - }, - { - .name = "dsakey", - .key = 'd', - .arg = "FILE", - .flags = 0, - .doc = "Set the dsa key.", - .group = 0 - }, - { - .name = "rsakey", - .key = 'r', - .arg = "FILE", - .flags = 0, - .doc = "Set the rsa key.", - .group = 0 - }, - { - .name = "ecdsakey", - .key = 'e', - .arg = "FILE", - .flags = 0, - .doc = "Set the ecdsa key.", - .group = 0 - }, - { - .name = "no-default-keys", - .key = 'n', - .arg = NULL, - .flags = 0, - .doc = "Do not set default key locations.", - .group = 0 - }, - { - .name = "verbose", - .key = 'v', - .arg = NULL, - .flags = 0, - .doc = "Get verbose output.", - .group = 0 - }, - {NULL, 0, NULL, 0, NULL, 0} -}; - -/* Parse a single option. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* Get the input argument from argp_parse, which we - * know is a pointer to our arguments structure. */ - ssh_bind sshbind = state->input; - static int no_default_keys = 0; - static int rsa_already_set = 0, dsa_already_set = 0, ecdsa_already_set = 0; - - switch (key) { - case 'n': - no_default_keys = 1; - break; - case 'p': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg); - break; - case 'd': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg); - dsa_already_set = 1; - break; - case 'k': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg); - /* We can't track the types of keys being added with this - option, so let's ensure we keep the keys we're adding - by just not setting the default keys */ - no_default_keys = 1; - break; - case 'r': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg); - rsa_already_set = 1; - break; - case 'e': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, arg); - ecdsa_already_set = 1; - break; - case 'v': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, - "3"); - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - /* Too many arguments. */ - argp_usage (state); - } - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg); - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - /* Not enough arguments. */ - argp_usage (state); - } - - if (!no_default_keys) { - set_default_keys(sshbind, - rsa_already_set, - dsa_already_set, - ecdsa_already_set); - } - - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; -#endif /* HAVE_ARGP_H */ - -/* A userdata struct for channel. */ -struct channel_data_struct { - /* pid of the child process the channel will spawn. */ - pid_t pid; - /* For PTY allocation */ - socket_t pty_master; - socket_t pty_slave; - /* For communication with the child process. */ - socket_t stdin; - socket_t stdout; - /* Only used for subsystem and exec requests. */ - socket_t stderr; - /* Event which is used to poll the above descriptors. */ - ssh_event event; - /* Terminal size struct. */ - struct winsize *winsize; -}; - -/* A userdata struct for session. */ -struct session_data_struct { - /* Pointer to the channel the session will allocate. */ - ssh_channel channel; - int auth_attempts; - int authenticated; -}; - -static int data_function(ssh_session session, ssh_channel channel, void *data, - uint32_t len, int is_stderr, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - - (void) session; - (void) channel; - (void) is_stderr; - - if (len == 0 || cdata->pid < 1 || kill(cdata->pid, 0) < 0) { - return 0; - } - - return write(cdata->stdin, (char *) data, len); -} - -static int pty_request(ssh_session session, ssh_channel channel, - const char *term, int cols, int rows, int py, int px, - void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *)userdata; - - (void) session; - (void) channel; - (void) term; - - cdata->winsize->ws_row = rows; - cdata->winsize->ws_col = cols; - cdata->winsize->ws_xpixel = px; - cdata->winsize->ws_ypixel = py; - - if (openpty(&cdata->pty_master, &cdata->pty_slave, NULL, NULL, - cdata->winsize) != 0) { - fprintf(stderr, "Failed to open pty\n"); - return SSH_ERROR; - } - return SSH_OK; -} - -static int pty_resize(ssh_session session, ssh_channel channel, int cols, - int rows, int py, int px, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *)userdata; - - (void) session; - (void) channel; - - cdata->winsize->ws_row = rows; - cdata->winsize->ws_col = cols; - cdata->winsize->ws_xpixel = px; - cdata->winsize->ws_ypixel = py; - - if (cdata->pty_master != -1) { - return ioctl(cdata->pty_master, TIOCSWINSZ, cdata->winsize); - } - - return SSH_ERROR; -} - -static int exec_pty(const char *mode, const char *command, - struct channel_data_struct *cdata) { - switch(cdata->pid = fork()) { - case -1: - close(cdata->pty_master); - close(cdata->pty_slave); - fprintf(stderr, "Failed to fork\n"); - return SSH_ERROR; - case 0: - close(cdata->pty_master); - if (login_tty(cdata->pty_slave) != 0) { - exit(1); - } - execl("/bin/sh", "sh", mode, command, NULL); - exit(0); - default: - close(cdata->pty_slave); - /* pty fd is bi-directional */ - cdata->stdout = cdata->stdin = cdata->pty_master; - } - return SSH_OK; -} - -static int exec_nopty(const char *command, struct channel_data_struct *cdata) { - int in[2], out[2], err[2]; - - /* Do the plumbing to be able to talk with the child process. */ - if (pipe(in) != 0) { - goto stdin_failed; - } - if (pipe(out) != 0) { - goto stdout_failed; - } - if (pipe(err) != 0) { - goto stderr_failed; - } - - switch(cdata->pid = fork()) { - case -1: - goto fork_failed; - case 0: - /* Finish the plumbing in the child process. */ - close(in[1]); - close(out[0]); - close(err[0]); - dup2(in[0], STDIN_FILENO); - dup2(out[1], STDOUT_FILENO); - dup2(err[1], STDERR_FILENO); - close(in[0]); - close(out[1]); - close(err[1]); - /* exec the requested command. */ - execl("/bin/sh", "sh", "-c", command, NULL); - exit(0); - } - - close(in[0]); - close(out[1]); - close(err[1]); - - cdata->stdin = in[1]; - cdata->stdout = out[0]; - cdata->stderr = err[0]; - - return SSH_OK; - -fork_failed: - close(err[0]); - close(err[1]); -stderr_failed: - close(out[0]); - close(out[1]); -stdout_failed: - close(in[0]); - close(in[1]); -stdin_failed: - return SSH_ERROR; -} - -static int exec_request(ssh_session session, ssh_channel channel, - const char *command, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - - - (void) session; - (void) channel; - - if(cdata->pid > 0) { - return SSH_ERROR; - } - - if (cdata->pty_master != -1 && cdata->pty_slave != -1) { - return exec_pty("-c", command, cdata); - } - return exec_nopty(command, cdata); -} - -static int shell_request(ssh_session session, ssh_channel channel, - void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - - (void) session; - (void) channel; - - if(cdata->pid > 0) { - return SSH_ERROR; - } - - if (cdata->pty_master != -1 && cdata->pty_slave != -1) { - return exec_pty("-l", NULL, cdata); - } - /* Client requested a shell without a pty, let's pretend we allow that */ - return SSH_OK; -} - -static int subsystem_request(ssh_session session, ssh_channel channel, - const char *subsystem, void *userdata) { - /* subsystem requests behave simillarly to exec requests. */ - if (strcmp(subsystem, "sftp") == 0) { - return exec_request(session, channel, SFTP_SERVER_PATH, userdata); - } - return SSH_ERROR; -} - -static int auth_password(ssh_session session, const char *user, - const char *pass, void *userdata) { - struct session_data_struct *sdata = (struct session_data_struct *) userdata; - - (void) session; - - if (strcmp(user, USER) == 0 && strcmp(pass, PASS) == 0) { - sdata->authenticated = 1; - return SSH_AUTH_SUCCESS; - } - - sdata->auth_attempts++; - return SSH_AUTH_DENIED; -} - -static ssh_channel channel_open(ssh_session session, void *userdata) { - struct session_data_struct *sdata = (struct session_data_struct *) userdata; - - sdata->channel = ssh_channel_new(session); - return sdata->channel; -} - -static int process_stdout(socket_t fd, int revents, void *userdata) { - char buf[BUF_SIZE]; - int n = -1; - ssh_channel channel = (ssh_channel) userdata; - - if (channel != NULL && (revents & POLLIN) != 0) { - n = read(fd, buf, BUF_SIZE); - if (n > 0) { - ssh_channel_write(channel, buf, n); - } - } - - return n; -} - -static int process_stderr(socket_t fd, int revents, void *userdata) { - char buf[BUF_SIZE]; - int n = -1; - ssh_channel channel = (ssh_channel) userdata; - - if (channel != NULL && (revents & POLLIN) != 0) { - n = read(fd, buf, BUF_SIZE); - if (n > 0) { - ssh_channel_write_stderr(channel, buf, n); - } - } - - return n; -} - -static void handle_session(ssh_event event, ssh_session session) { - int n, rc; - - /* Structure for storing the pty size. */ - struct winsize wsize = { - .ws_row = 0, - .ws_col = 0, - .ws_xpixel = 0, - .ws_ypixel = 0 - }; - - /* Our struct holding information about the channel. */ - struct channel_data_struct cdata = { - .pid = 0, - .pty_master = -1, - .pty_slave = -1, - .stdin = -1, - .stdout = -1, - .stderr = -1, - .event = NULL, - .winsize = &wsize - }; - - /* Our struct holding information about the session. */ - struct session_data_struct sdata = { - .channel = NULL, - .auth_attempts = 0, - .authenticated = 0 - }; - - struct ssh_channel_callbacks_struct channel_cb = { - .userdata = &cdata, - .channel_pty_request_function = pty_request, - .channel_pty_window_change_function = pty_resize, - .channel_shell_request_function = shell_request, - .channel_exec_request_function = exec_request, - .channel_data_function = data_function, - .channel_subsystem_request_function = subsystem_request - }; - - struct ssh_server_callbacks_struct server_cb = { - .userdata = &sdata, - .auth_password_function = auth_password, - .channel_open_request_session_function = channel_open, - }; - - ssh_callbacks_init(&server_cb); - ssh_callbacks_init(&channel_cb); - - ssh_set_server_callbacks(session, &server_cb); - - if (ssh_handle_key_exchange(session) != SSH_OK) { - fprintf(stderr, "%s\n", ssh_get_error(session)); - return; - } - - ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD); - ssh_event_add_session(event, session); - - n = 0; - while (sdata.authenticated == 0 || sdata.channel == NULL) { - /* If the user has used up all attempts, or if he hasn't been able to - * authenticate in 10 seconds (n * 100ms), disconnect. */ - if (sdata.auth_attempts >= 3 || n >= 100) { - return; - } - - if (ssh_event_dopoll(event, 100) == SSH_ERROR) { - fprintf(stderr, "%s\n", ssh_get_error(session)); - return; - } - n++; - } - - ssh_set_channel_callbacks(sdata.channel, &channel_cb); - - do { - /* Poll the main event which takes care of the session, the channel and - * even our child process's stdout/stderr (once it's started). */ - if (ssh_event_dopoll(event, -1) == SSH_ERROR) { - ssh_channel_close(sdata.channel); - } - - /* If child process's stdout/stderr has been registered with the event, - * or the child process hasn't started yet, continue. */ - if (cdata.event != NULL || cdata.pid == 0) { - continue; - } - /* Executed only once, once the child process starts. */ - cdata.event = event; - /* If stdout valid, add stdout to be monitored by the poll event. */ - if (cdata.stdout != -1) { - if (ssh_event_add_fd(event, cdata.stdout, POLLIN, process_stdout, - sdata.channel) != SSH_OK) { - fprintf(stderr, "Failed to register stdout to poll context\n"); - ssh_channel_close(sdata.channel); - } - } - - /* If stderr valid, add stderr to be monitored by the poll event. */ - if (cdata.stderr != -1){ - if (ssh_event_add_fd(event, cdata.stderr, POLLIN, process_stderr, - sdata.channel) != SSH_OK) { - fprintf(stderr, "Failed to register stderr to poll context\n"); - ssh_channel_close(sdata.channel); - } - } - } while(ssh_channel_is_open(sdata.channel) && - (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0)); - - close(cdata.pty_master); - close(cdata.stdin); - close(cdata.stdout); - close(cdata.stderr); - - /* Remove the descriptors from the polling context, since they are now - * closed, they will always trigger during the poll calls. */ - ssh_event_remove_fd(event, cdata.stdout); - ssh_event_remove_fd(event, cdata.stderr); - - /* If the child process exited. */ - if (kill(cdata.pid, 0) < 0 && WIFEXITED(rc)) { - rc = WEXITSTATUS(rc); - ssh_channel_request_send_exit_status(sdata.channel, rc); - /* If client terminated the channel or the process did not exit nicely, - * but only if something has been forked. */ - } else if (cdata.pid > 0) { - kill(cdata.pid, SIGKILL); - } - - ssh_channel_send_eof(sdata.channel); - ssh_channel_close(sdata.channel); - - /* Wait up to 5 seconds for the client to terminate the session. */ - for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { - ssh_event_dopoll(event, 100); - } -} - -/* SIGCHLD handler for cleaning up dead children. */ -static void sigchld_handler(int signo) { - (void) signo; - while (waitpid(-1, NULL, WNOHANG) > 0); -} - -int main(int argc, char **argv) { - ssh_bind sshbind; - ssh_session session; - ssh_event event; - struct sigaction sa; - - /* Set up SIGCHLD handler. */ - sa.sa_handler = sigchld_handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; - if (sigaction(SIGCHLD, &sa, NULL) != 0) { - fprintf(stderr, "Failed to register SIGCHLD handler\n"); - return 1; - } - - ssh_init(); - sshbind = ssh_bind_new(); - -#ifdef HAVE_ARGP_H - argp_parse(&argp, argc, argv, 0, 0, sshbind); -#else - (void) argc; - (void) argv; - - set_default_keys(sshbind, 0, 0, 0); -#endif /* HAVE_ARGP_H */ - - if(ssh_bind_listen(sshbind) < 0) { - fprintf(stderr, "%s\n", ssh_get_error(sshbind)); - return 1; - } - - while (1) { - session = ssh_new(); - if (session == NULL) { - fprintf(stderr, "Failed to allocate session\n"); - continue; - } - - /* Blocks until there is a new incoming connection. */ - if(ssh_bind_accept(sshbind, session) != SSH_ERROR) { - switch(fork()) { - case 0: - /* Remove the SIGCHLD handler inherited from parent. */ - sa.sa_handler = SIG_DFL; - sigaction(SIGCHLD, &sa, NULL); - /* Remove socket binding, which allows us to restart the - * parent process, without terminating existing sessions. */ - ssh_bind_free(sshbind); - - event = ssh_event_new(); - if (event != NULL) { - /* Blocks until the SSH session ends by either - * child process exiting, or client disconnecting. */ - handle_session(event, session); - ssh_event_free(event); - } else { - fprintf(stderr, "Could not create polling context\n"); - } - ssh_disconnect(session); - ssh_free(session); - - exit(0); - case -1: - fprintf(stderr, "Failed to fork\n"); - } - } else { - fprintf(stderr, "%s\n", ssh_get_error(sshbind)); - } - /* Since the session has been passed to a child fork, do some cleaning - * up at the parent process. */ - ssh_disconnect(session); - ssh_free(session); - } - - ssh_bind_free(sshbind); - ssh_finalize(); - return 0; -} diff --git a/libssh/examples/sshnetcat.c b/libssh/examples/sshnetcat.c deleted file mode 100644 index 8ac5a212..00000000 --- a/libssh/examples/sshnetcat.c +++ /dev/null @@ -1,265 +0,0 @@ -/* -Copyright 2010 Aris Adamantiadis - -This file is part of the SSH Library - -You are free to copy this file, modify it in any way, consider it being public -domain. This does not apply to the rest of the library though, but it is -allowed to cut-and-paste working code from this file to any license of -program. -The goal is to show the API in action. It's not a reference on how terminal -clients must be made or how a client should react. -*/ - -#include "config.h" -#include -#include -#include -#ifdef HAVE_TERMIOS_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif - -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "examples_common.h" -char *host; -const char *desthost="localhost"; -const char *port="22"; - -#ifdef WITH_PCAP -#include -char *pcap_file=NULL; -#endif - -static void usage(){ - fprintf(stderr,"Usage : sshnetcat [user@]host forwarded_host forwarded_port\n"); - exit(1); -} - -static int opts(int argc, char **argv){ - int i; - while((i=getopt(argc,argv,"P:"))!=-1){ - switch(i){ -#ifdef WITH_PCAP - case 'P': - pcap_file=optarg; - break; -#endif - default: - fprintf(stderr,"unknown option %c\n",optopt); - usage(); - } - } - if(optind < argc) - host=argv[optind++]; - if(optind < argc) - desthost=argv[optind++]; - if(optind < argc) - port=argv[optind++]; - if(host==NULL) - usage(); - return 0; -} - -static void select_loop(ssh_session session,ssh_channel channel){ - fd_set fds; - struct timeval timeout; - char buffer[4096]; - /* channels will be set to the channels to poll. - * outchannels will contain the result of the poll - */ - ssh_channel channels[2], outchannels[2]; - int lus; - int eof=0; - int maxfd; - int ret; - while(channel){ - do{ - int fd; - - FD_ZERO(&fds); - if(!eof) - FD_SET(0,&fds); - timeout.tv_sec=30; - timeout.tv_usec=0; - - fd = ssh_get_fd(session); - if (fd == -1) { - fprintf(stderr, "Error getting the session file descriptor: %s\n", - ssh_get_error(session)); - return; - } - FD_SET(fd, &fds); - maxfd = fd + 1; - - channels[0]=channel; // set the first channel we want to read from - channels[1]=NULL; - ret=ssh_select(channels,outchannels,maxfd,&fds,&timeout); - if(ret==EINTR) - continue; - if(FD_ISSET(0,&fds)){ - lus=read(0,buffer,sizeof(buffer)); - if(lus) - ssh_channel_write(channel,buffer,lus); - else { - eof=1; - ssh_channel_send_eof(channel); - } - } - if(channel && ssh_channel_is_closed(channel)){ - ssh_channel_free(channel); - channel=NULL; - channels[0]=NULL; - } - if(outchannels[0]){ - while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,0)){ - lus = ssh_channel_read(channel,buffer,sizeof(buffer),0); - if(lus==-1){ - fprintf(stderr, "Error reading channel: %s\n", - ssh_get_error(session)); - return; - } - if(lus==0){ - ssh_channel_free(channel); - channel=channels[0]=NULL; - } else { - ret = write(1, buffer, lus); - if (ret < 0) { - fprintf(stderr, "Error writing to stdin: %s", - strerror(errno)); - return; - } - } - } - while(channel && ssh_channel_is_open(channel) && ssh_channel_poll(channel,1)){ /* stderr */ - lus = ssh_channel_read(channel, buffer, sizeof(buffer), 1); - if(lus==-1){ - fprintf(stderr, "Error reading channel: %s\n", - ssh_get_error(session)); - return; - } - if(lus==0){ - ssh_channel_free(channel); - channel=channels[0]=NULL; - } else { - ret = write(2, buffer, lus); - if (ret < 0) { - fprintf(stderr, "Error writing to stderr: %s", - strerror(errno)); - return; - } - } - } - } - if(channel && ssh_channel_is_closed(channel)){ - ssh_channel_free(channel); - channel=NULL; - } - } while (ret==EINTR || ret==SSH_EINTR); - - } -} - -static void forwarding(ssh_session session){ - ssh_channel channel; - int r; - channel = ssh_channel_new(session); - r = ssh_channel_open_forward(channel, desthost, atoi(port), "localhost", 22); - if(r<0) { - printf("error forwarding port : %s\n",ssh_get_error(session)); - return; - } - select_loop(session,channel); -} - -static int client(ssh_session session){ - int auth=0; - char *banner; - int state; - - if (ssh_options_set(session, SSH_OPTIONS_HOST ,host) < 0) - return -1; - ssh_options_parse_config(session, NULL); - - if(ssh_connect(session)){ - fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session)); - return -1; - } - state=verify_knownhost(session); - if (state != 0) - return -1; - ssh_userauth_none(session, NULL); - banner=ssh_get_issue_banner(session); - if(banner){ - printf("%s\n",banner); - free(banner); - } - auth=authenticate_console(session); - if(auth != SSH_AUTH_SUCCESS){ - return -1; - } - forwarding(session); - return 0; -} - -#ifdef WITH_PCAP -ssh_pcap_file pcap; -void set_pcap(ssh_session session); -void set_pcap(ssh_session session){ - if(!pcap_file) - return; - pcap=ssh_pcap_file_new(); - if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){ - printf("Error opening pcap file\n"); - ssh_pcap_file_free(pcap); - pcap=NULL; - return; - } - ssh_set_pcap_file(session,pcap); -} - -void cleanup_pcap(void); -void cleanup_pcap(){ - ssh_pcap_file_free(pcap); - pcap=NULL; -} -#endif - -int main(int argc, char **argv){ - ssh_session session; - - session = ssh_new(); - - if(ssh_options_getopt(session, &argc, argv)) { - fprintf(stderr, "error parsing command line :%s\n", - ssh_get_error(session)); - usage(); - } - opts(argc,argv); -#ifdef WITH_PCAP - set_pcap(session); -#endif - client(session); - - ssh_disconnect(session); - ssh_free(session); -#ifdef WITH_PCAP - cleanup_pcap(); -#endif - - ssh_finalize(); - - return 0; -} diff --git a/libssh/include/CMakeLists.txt b/libssh/include/CMakeLists.txt deleted file mode 100644 index cb3bd962..00000000 --- a/libssh/include/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -project(headers C) - -add_subdirectory(libssh) diff --git a/libssh/include/libssh/CMakeLists.txt b/libssh/include/libssh/CMakeLists.txt deleted file mode 100644 index 78ee1c61..00000000 --- a/libssh/include/libssh/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -project(libssh-headers C) - -set(libssh_HDRS - callbacks.h - libssh.h - ssh2.h - legacy.h -) - -if (WITH_SFTP) - set(libssh_HDRS - ${libssh_HDRS} - sftp.h - ) -endif (WITH_SFTP) - -if (WITH_SSH1) - set(libssh_HDRS - ${libssh_HDRS} - ssh1.h - ) -endif (WITH_SSH1) - -if (WITH_SERVER) - set(libssh_HDRS - ${libssh_HDRS} - server.h - ) -endif (WITH_SERVER) - -install( - FILES - ${libssh_HDRS} - DESTINATION - ${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME} - COMPONENT - headers -) - diff --git a/libssh/include/libssh/agent.h b/libssh/include/libssh/agent.h deleted file mode 100644 index 77209d0f..00000000 --- a/libssh/include/libssh/agent.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2008-2009 Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __AGENT_H -#define __AGENT_H - -#include "libssh/libssh.h" - -/* Messages for the authentication agent connection. */ -#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 -#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 -#define SSH_AGENTC_RSA_CHALLENGE 3 -#define SSH_AGENT_RSA_RESPONSE 4 -#define SSH_AGENT_FAILURE 5 -#define SSH_AGENT_SUCCESS 6 -#define SSH_AGENTC_ADD_RSA_IDENTITY 7 -#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 -#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 - -/* private OpenSSH extensions for SSH2 */ -#define SSH2_AGENTC_REQUEST_IDENTITIES 11 -#define SSH2_AGENT_IDENTITIES_ANSWER 12 -#define SSH2_AGENTC_SIGN_REQUEST 13 -#define SSH2_AGENT_SIGN_RESPONSE 14 -#define SSH2_AGENTC_ADD_IDENTITY 17 -#define SSH2_AGENTC_REMOVE_IDENTITY 18 -#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 - -/* smartcard */ -#define SSH_AGENTC_ADD_SMARTCARD_KEY 20 -#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21 - -/* lock/unlock the agent */ -#define SSH_AGENTC_LOCK 22 -#define SSH_AGENTC_UNLOCK 23 - -/* add key with constraints */ -#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24 -#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 -#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 - -#define SSH_AGENT_CONSTRAIN_LIFETIME 1 -#define SSH_AGENT_CONSTRAIN_CONFIRM 2 - -/* extended failure messages */ -#define SSH2_AGENT_FAILURE 30 - -/* additional error code for ssh.com's ssh-agent2 */ -#define SSH_COM_AGENT2_FAILURE 102 - -#define SSH_AGENT_OLD_SIGNATURE 0x01 - -struct ssh_agent_struct { - struct ssh_socket_struct *sock; - ssh_buffer ident; - unsigned int count; - ssh_channel channel; -}; - -#ifndef _WIN32 -/* agent.c */ -/** - * @brief Create a new ssh agent structure. - * - * @return An allocated ssh agent structure or NULL on error. - */ -struct ssh_agent_struct *agent_new(struct ssh_session_struct *session); - -void agent_close(struct ssh_agent_struct *agent); - -/** - * @brief Free an allocated ssh agent structure. - * - * @param agent The ssh agent structure to free. - */ -void agent_free(struct ssh_agent_struct *agent); - -/** - * @brief Check if the ssh agent is running. - * - * @param session The ssh session to check for the agent. - * - * @return 1 if it is running, 0 if not. - */ -int agent_is_running(struct ssh_session_struct *session); - -int ssh_agent_get_ident_count(struct ssh_session_struct *session); - -ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, - char **comment); - -ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session, - char **comment); - -ssh_string ssh_agent_sign_data(ssh_session session, - const ssh_key pubkey, - struct ssh_buffer_struct *data); -#endif - -#endif /* __AGENT_H */ -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/include/libssh/auth.h b/libssh/include/libssh/auth.h deleted file mode 100644 index 2c0012b0..00000000 --- a/libssh/include/libssh/auth.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef AUTH_H_ -#define AUTH_H_ -#include "config.h" -#include "libssh/callbacks.h" - -SSH_PACKET_CALLBACK(ssh_packet_userauth_banner); -SSH_PACKET_CALLBACK(ssh_packet_userauth_failure); -SSH_PACKET_CALLBACK(ssh_packet_userauth_success); -SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok); -SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request); -SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response); - -/** @internal - * kdbint structure must be shared with message.c - * and server.c - */ -struct ssh_kbdint_struct { - uint32_t nprompts; - uint32_t nanswers; - char *name; - char *instruction; - char **prompts; - unsigned char *echo; /* bool array */ - char **answers; -}; -typedef struct ssh_kbdint_struct* ssh_kbdint; - -ssh_kbdint ssh_kbdint_new(void); -void ssh_kbdint_clean(ssh_kbdint kbd); -void ssh_kbdint_free(ssh_kbdint kbd); - - -#ifdef WITH_SSH1 -void ssh_auth1_handler(ssh_session session, uint8_t type); - -/* auth1.c */ -int ssh_userauth1_none(ssh_session session, const char *username); -int ssh_userauth1_offer_pubkey(ssh_session session, const char *username, - int type, ssh_string pubkey); -int ssh_userauth1_password(ssh_session session, const char *username, - const char *password); - - -#endif - -/** @internal - * States of authentication in the client-side. They describe - * what was the last response from the server - */ -enum ssh_auth_state_e { - /** No authentication asked */ - SSH_AUTH_STATE_NONE=0, - /** Last authentication response was a partial success */ - SSH_AUTH_STATE_PARTIAL, - /** Last authentication response was a success */ - SSH_AUTH_STATE_SUCCESS, - /** Last authentication response was failed */ - SSH_AUTH_STATE_FAILED, - /** Last authentication was erroneous */ - SSH_AUTH_STATE_ERROR, - /** Last state was a keyboard-interactive ask for info */ - SSH_AUTH_STATE_INFO, - /** Last state was a public key accepted for authentication */ - SSH_AUTH_STATE_PK_OK, - /** We asked for a keyboard-interactive authentication */ - SSH_AUTH_STATE_KBDINT_SENT, - /** We have sent an userauth request with gssapi-with-mic */ - SSH_AUTH_STATE_GSSAPI_REQUEST_SENT, - /** We are exchanging tokens until authentication */ - SSH_AUTH_STATE_GSSAPI_TOKEN, - /** We have sent the MIC and expecting to be authenticated */ - SSH_AUTH_STATE_GSSAPI_MIC_SENT, -}; - -/** @internal - * @brief states of the authentication service request - */ -enum ssh_auth_service_state_e { - /** initial state */ - SSH_AUTH_SERVICE_NONE=0, - /** Authentication service request packet sent */ - SSH_AUTH_SERVICE_SENT, - /** Service accepted */ - SSH_AUTH_SERVICE_ACCEPTED, - /** Access to service denied (fatal) */ - SSH_AUTH_SERVICE_DENIED, - /** Specific to SSH1 */ - SSH_AUTH_SERVICE_USER_SENT -}; - -#endif /* AUTH_H_ */ diff --git a/libssh/include/libssh/bignum.h b/libssh/include/libssh/bignum.h deleted file mode 100644 index e5f2a472..00000000 --- a/libssh/include/libssh/bignum.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2014 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef BIGNUM_H_ -#define BIGNUM_H_ - -#include "libssh/libcrypto.h" -#include "libssh/libgcrypt.h" - -bignum make_string_bn(ssh_string string); -ssh_string make_bignum_string(bignum num); -void ssh_print_bignum(const char *which,bignum num); - - -#endif /* BIGNUM_H_ */ diff --git a/libssh/include/libssh/bind.h b/libssh/include/libssh/bind.h deleted file mode 100644 index ced1c494..00000000 --- a/libssh/include/libssh/bind.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef BIND_H_ -#define BIND_H_ - -#include "libssh/priv.h" -#include "libssh/session.h" - -struct ssh_bind_struct { - struct ssh_common_struct common; /* stuff common to ssh_bind and ssh_session */ - struct ssh_bind_callbacks_struct *bind_callbacks; - void *bind_callbacks_userdata; - - struct ssh_poll_handle_struct *poll; - /* options */ - char *wanted_methods[10]; - char *banner; - char *ecdsakey; - char *dsakey; - char *rsakey; - ssh_key ecdsa; - ssh_key dsa; - ssh_key rsa; - char *bindaddr; - socket_t bindfd; - unsigned int bindport; - int blocking; - int toaccept; -}; - -struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct - *sshbind); - - -#endif /* BIND_H_ */ diff --git a/libssh/include/libssh/buffer.h b/libssh/include/libssh/buffer.h deleted file mode 100644 index 2aebe7e7..00000000 --- a/libssh/include/libssh/buffer.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef BUFFER_H_ -#define BUFFER_H_ - -#include - -#include "libssh/libssh.h" -/* - * Describes a buffer state - * [XXXXXXXXXXXXDATA PAYLOAD XXXXXXXXXXXXXXXXXXXXXXXX] - * ^ ^ ^ ^] - * \_data points\_pos points here \_used points here | / - * here Allocated - */ -struct ssh_buffer_struct { - char *data; - uint32_t used; - uint32_t allocated; - uint32_t pos; - int secure; -}; - -#define SSH_BUFFER_PACK_END ((uint32_t) 0x4f65feb3) - -LIBSSH_API void ssh_buffer_free(ssh_buffer buffer); -LIBSSH_API void *ssh_buffer_get_begin(ssh_buffer buffer); -LIBSSH_API uint32_t ssh_buffer_get_len(ssh_buffer buffer); -LIBSSH_API ssh_buffer ssh_buffer_new(void); -void ssh_buffer_set_secure(ssh_buffer buffer); -int buffer_add_ssh_string(ssh_buffer buffer, ssh_string string); -int buffer_add_u8(ssh_buffer buffer, uint8_t data); -int buffer_add_u16(ssh_buffer buffer, uint16_t data); -int buffer_add_u32(ssh_buffer buffer, uint32_t data); -int buffer_add_u64(ssh_buffer buffer, uint64_t data); -int ssh_buffer_add_data(ssh_buffer buffer, const void *data, uint32_t len); -int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer, const char *format, va_list ap); -int _ssh_buffer_pack(struct ssh_buffer_struct *buffer, const char *format, ...); -#define ssh_buffer_pack(buffer, format, ...) _ssh_buffer_pack((buffer),(format), __VA_ARGS__, SSH_BUFFER_PACK_END) -int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer, const char *format, va_list ap); -int _ssh_buffer_unpack(struct ssh_buffer_struct *buffer, const char *format, ...); -#define ssh_buffer_unpack(buffer, format, ...) _ssh_buffer_unpack((buffer),(format), __VA_ARGS__, SSH_BUFFER_PACK_END) - -int buffer_prepend_data(ssh_buffer buffer, const void *data, uint32_t len); -int buffer_add_buffer(ssh_buffer buffer, ssh_buffer source); -int ssh_buffer_reinit(ssh_buffer buffer); - -/* buffer_get_rest returns a pointer to the current position into the buffer */ -void *buffer_get_rest(ssh_buffer buffer); -/* buffer_get_rest_len returns the number of bytes which can be read */ -uint32_t buffer_get_rest_len(ssh_buffer buffer); - -/* buffer_read_*() returns the number of bytes read, except for ssh strings */ -int buffer_get_u8(ssh_buffer buffer, uint8_t *data); -int buffer_get_u32(ssh_buffer buffer, uint32_t *data); -int buffer_get_u64(ssh_buffer buffer, uint64_t *data); - -uint32_t buffer_get_data(ssh_buffer buffer, void *data, uint32_t requestedlen); -/* buffer_get_ssh_string() is an exception. if the String read is too large or invalid, it will answer NULL. */ -ssh_string buffer_get_ssh_string(ssh_buffer buffer); -/* gets a string out of a SSH-1 mpint */ -ssh_string buffer_get_mpint(ssh_buffer buffer); -/* buffer_pass_bytes acts as if len bytes have been read (used for padding) */ -uint32_t buffer_pass_bytes_end(ssh_buffer buffer, uint32_t len); -uint32_t buffer_pass_bytes(ssh_buffer buffer, uint32_t len); - -#endif /* BUFFER_H_ */ diff --git a/libssh/include/libssh/callbacks.h b/libssh/include/libssh/callbacks.h deleted file mode 100644 index 6bd8c573..00000000 --- a/libssh/include/libssh/callbacks.h +++ /dev/null @@ -1,855 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* callback.h - * This file includes the public declarations for the libssh callback mechanism - */ - -#ifndef _SSH_CALLBACK_H -#define _SSH_CALLBACK_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @defgroup libssh_callbacks The libssh callbacks - * @ingroup libssh - * - * Callback which can be replaced in libssh. - * - * @{ - */ - -/** @internal - * @brief callback to process simple codes - * @param code value to transmit - * @param user Userdata to pass in callback - */ -typedef void (*ssh_callback_int) (int code, void *user); - -/** @internal - * @brief callback for data received messages. - * @param data data retrieved from the socket or stream - * @param len number of bytes available from this stream - * @param user user-supplied pointer sent along with all callback messages - * @returns number of bytes processed by the callee. The remaining bytes will - * be sent in the next callback message, when more data is available. - */ -typedef int (*ssh_callback_data) (const void *data, size_t len, void *user); - -typedef void (*ssh_callback_int_int) (int code, int errno_code, void *user); - -typedef int (*ssh_message_callback) (ssh_session, ssh_message message, void *user); -typedef int (*ssh_channel_callback_int) (ssh_channel channel, int code, void *user); -typedef int (*ssh_channel_callback_data) (ssh_channel channel, int code, void *data, size_t len, void *user); - -/** - * @brief SSH log callback. All logging messages will go through this callback - * @param session Current session handler - * @param priority Priority of the log, the smaller being the more important - * @param message the actual message - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_log_callback) (ssh_session session, int priority, - const char *message, void *userdata); - -/** - * @brief SSH log callback. - * - * All logging messages will go through this callback. - * - * @param priority Priority of the log, the smaller being the more important. - * - * @param function The function name calling the the logging fucntions. - * - * @param message The actual message - * - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_logging_callback) (int priority, - const char *function, - const char *buffer, - void *userdata); - -/** - * @brief SSH Connection status callback. - * @param session Current session handler - * @param status Percentage of connection status, going from 0.0 to 1.0 - * once connection is done. - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_status_callback) (ssh_session session, float status, - void *userdata); - -/** - * @brief SSH global request callback. All global request will go through this - * callback. - * @param session Current session handler - * @param message the actual message - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_global_request_callback) (ssh_session session, - ssh_message message, void *userdata); - -/** - * @brief Handles an SSH new channel open X11 request. This happens when the server - * sends back an X11 connection attempt. This is a client-side API - * @param session current session handler - * @param userdata Userdata to be passed to the callback function. - * @returns a valid ssh_channel handle if the request is to be allowed - * @returns NULL if the request should not be allowed - * @warning The channel pointer returned by this callback must be closed by the application. - */ -typedef ssh_channel (*ssh_channel_open_request_x11_callback) (ssh_session session, - const char * originator_address, int originator_port, void *userdata); - -/** - * The structure to replace libssh functions with appropriate callbacks. - */ -struct ssh_callbacks_struct { - /** DON'T SET THIS use ssh_callbacks_init() instead. */ - size_t size; - /** - * User-provided data. User is free to set anything he wants here - */ - void *userdata; - /** - * This functions will be called if e.g. a keyphrase is needed. - */ - ssh_auth_callback auth_function; - /** - * This function will be called each time a loggable event happens. - */ - ssh_log_callback log_function; - /** - * This function gets called during connection time to indicate the - * percentage of connection steps completed. - */ - void (*connect_status_function)(void *userdata, float status); - /** - * This function will be called each time a global request is received. - */ - ssh_global_request_callback global_request_function; - /** This function will be called when an incoming X11 request is received. - */ - ssh_channel_open_request_x11_callback channel_open_request_x11_function; -}; -typedef struct ssh_callbacks_struct *ssh_callbacks; - -/** These are callbacks used specifically in SSH servers. - */ - -/** - * @brief SSH authentication callback. - * @param session Current session handler - * @param user User that wants to authenticate - * @param password Password used for authentication - * @param userdata Userdata to be passed to the callback function. - * @returns SSH_AUTH_SUCCESS Authentication is accepted. - * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. - * @returns SSH_AUTH_DENIED Authentication failed. - */ -typedef int (*ssh_auth_password_callback) (ssh_session session, const char *user, const char *password, - void *userdata); - -/** - * @brief SSH authentication callback. Tries to authenticates user with the "none" method - * which is anonymous or passwordless. - * @param session Current session handler - * @param user User that wants to authenticate - * @param userdata Userdata to be passed to the callback function. - * @returns SSH_AUTH_SUCCESS Authentication is accepted. - * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. - * @returns SSH_AUTH_DENIED Authentication failed. - */ -typedef int (*ssh_auth_none_callback) (ssh_session session, const char *user, void *userdata); - -/** - * @brief SSH authentication callback. Tries to authenticates user with the "gssapi-with-mic" method - * @param session Current session handler - * @param user Username of the user (can be spoofed) - * @param principal Authenticated principal of the user, including realm. - * @param userdata Userdata to be passed to the callback function. - * @returns SSH_AUTH_SUCCESS Authentication is accepted. - * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. - * @returns SSH_AUTH_DENIED Authentication failed. - * @warning Implementations should verify that parameter user matches in some way the principal. - * user and principal can be different. Only the latter is guaranteed to be safe. - */ -typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *user, const char *principal, - void *userdata); - -/** - * @brief SSH authentication callback. - * @param session Current session handler - * @param user User that wants to authenticate - * @param pubkey public key used for authentication - * @param signature_state SSH_PUBLICKEY_STATE_NONE if the key is not signed (simple public key probe), - * SSH_PUBLICKEY_STATE_VALID if the signature is valid. Others values should be - * replied with a SSH_AUTH_DENIED. - * @param userdata Userdata to be passed to the callback function. - * @returns SSH_AUTH_SUCCESS Authentication is accepted. - * @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed. - * @returns SSH_AUTH_DENIED Authentication failed. - */ -typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user, struct ssh_key_struct *pubkey, - char signature_state, void *userdata); - - -/** - * @brief Handles an SSH service request - * @param session current session handler - * @param service name of the service (e.g. "ssh-userauth") requested - * @param userdata Userdata to be passed to the callback function. - * @returns 0 if the request is to be allowed - * @returns -1 if the request should not be allowed - */ - -typedef int (*ssh_service_request_callback) (ssh_session session, const char *service, void *userdata); - -/** - * @brief Handles an SSH new channel open session request - * @param session current session handler - * @param userdata Userdata to be passed to the callback function. - * @returns a valid ssh_channel handle if the request is to be allowed - * @returns NULL if the request should not be allowed - * @warning The channel pointer returned by this callback must be closed by the application. - */ -typedef ssh_channel (*ssh_channel_open_request_session_callback) (ssh_session session, void *userdata); - -/* - * @brief handle the beginning of a GSSAPI authentication, server side. - * @param session current session handler - * @param user the username of the client - * @param n_oid number of available oids - * @param oids OIDs provided by the client - * @returns an ssh_string containing the chosen OID, that's supported by both - * client and server. - * @warning It is not necessary to fill this callback in if libssh is linked - * with libgssapi. - */ -typedef ssh_string (*ssh_gssapi_select_oid_callback) (ssh_session session, const char *user, - int n_oid, ssh_string *oids, void *userdata); - -/* - * @brief handle the negociation of a security context, server side. - * @param session current session handler - * @param[in] input_token input token provided by client - * @param[out] output_token output of the gssapi accept_sec_context method, - * NULL after completion. - * @returns SSH_OK if the token was generated correctly or accept_sec_context - * returned GSS_S_COMPLETE - * @returns SSH_ERROR in case of error - * @warning It is not necessary to fill this callback in if libssh is linked - * with libgssapi. - */ -typedef int (*ssh_gssapi_accept_sec_ctx_callback) (ssh_session session, - ssh_string input_token, ssh_string *output_token, void *userdata); - -/* - * @brief Verify and authenticates a MIC, server side. - * @param session current session handler - * @param[in] mic input mic to be verified provided by client - * @param[in] mic_buffer buffer of data to be signed. - * @param[in] mic_buffer_size size of mic_buffer - * @returns SSH_OK if the MIC was authenticated correctly - * @returns SSH_ERROR in case of error - * @warning It is not necessary to fill this callback in if libssh is linked - * with libgssapi. - */ -typedef int (*ssh_gssapi_verify_mic_callback) (ssh_session session, - ssh_string mic, void *mic_buffer, size_t mic_buffer_size, void *userdata); - - -/** - * This structure can be used to implement a libssh server, with appropriate callbacks. - */ - -struct ssh_server_callbacks_struct { - /** DON'T SET THIS use ssh_callbacks_init() instead. */ - size_t size; - /** - * User-provided data. User is free to set anything he wants here - */ - void *userdata; - /** This function gets called when a client tries to authenticate through - * password method. - */ - ssh_auth_password_callback auth_password_function; - - /** This function gets called when a client tries to authenticate through - * none method. - */ - ssh_auth_none_callback auth_none_function; - - /** This function gets called when a client tries to authenticate through - * gssapi-mic method. - */ - ssh_auth_gssapi_mic_callback auth_gssapi_mic_function; - - /** this function gets called when a client tries to authenticate or offer - * a public key. - */ - ssh_auth_pubkey_callback auth_pubkey_function; - - /** This functions gets called when a service request is issued by the - * client - */ - ssh_service_request_callback service_request_function; - /** This functions gets called when a new channel request is issued by - * the client - */ - ssh_channel_open_request_session_callback channel_open_request_session_function; - /** This function will be called when a new gssapi authentication is attempted. - */ - ssh_gssapi_select_oid_callback gssapi_select_oid_function; - /** This function will be called when a gssapi token comes in. - */ - ssh_gssapi_accept_sec_ctx_callback gssapi_accept_sec_ctx_function; - /* This function will be called when a MIC needs to be verified. - */ - ssh_gssapi_verify_mic_callback gssapi_verify_mic_function; -}; -typedef struct ssh_server_callbacks_struct *ssh_server_callbacks; - -/** - * @brief Set the session server callback functions. - * - * This functions sets the callback structure to use your own callback - * functions for user authentication, new channels and requests. - * - * @code - * struct ssh_server_callbacks_struct cb = { - * .userdata = data, - * .auth_password_function = my_auth_function - * }; - * ssh_callbacks_init(&cb); - * ssh_set_server_callbacks(session, &cb); - * @endcode - * - * @param session The session to set the callback structure. - * - * @param cb The callback structure itself. - * - * @return SSH_OK on success, SSH_ERROR on error. - */ -LIBSSH_API int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb); - -/** - * These are the callbacks exported by the socket structure - * They are called by the socket module when a socket event appears - */ -struct ssh_socket_callbacks_struct { - /** - * User-provided data. User is free to set anything he wants here - */ - void *userdata; - /** - * This function will be called each time data appears on socket. The data - * not consumed will appear on the next data event. - */ - ssh_callback_data data; - /** This function will be called each time a controlflow state changes, i.e. - * the socket is available for reading or writing. - */ - ssh_callback_int controlflow; - /** This function will be called each time an exception appears on socket. An - * exception can be a socket problem (timeout, ...) or an end-of-file. - */ - ssh_callback_int_int exception; - /** This function is called when the ssh_socket_connect was used on the socket - * on nonblocking state, and the connection successed. - */ - ssh_callback_int_int connected; -}; -typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks; - -#define SSH_SOCKET_FLOW_WRITEWILLBLOCK 1 -#define SSH_SOCKET_FLOW_WRITEWONTBLOCK 2 - -#define SSH_SOCKET_EXCEPTION_EOF 1 -#define SSH_SOCKET_EXCEPTION_ERROR 2 - -#define SSH_SOCKET_CONNECTED_OK 1 -#define SSH_SOCKET_CONNECTED_ERROR 2 -#define SSH_SOCKET_CONNECTED_TIMEOUT 3 - -/** - * @brief Initializes an ssh_callbacks_struct - * A call to this macro is mandatory when you have set a new - * ssh_callback_struct structure. Its goal is to maintain the binary - * compatibility with future versions of libssh as the structure - * evolves with time. - */ -#define ssh_callbacks_init(p) do {\ - (p)->size=sizeof(*(p)); \ -} while(0); - -/** - * @internal - * @brief tests if a callback can be called without crash - * verifies that the struct size if big enough - * verifies that the callback pointer exists - * @param p callback pointer - * @param c callback name - * @returns nonzero if callback can be called - */ -#define ssh_callbacks_exists(p,c) (\ - (p != NULL) && ( (char *)&((p)-> c) < (char *)(p) + (p)->size ) && \ - ((p)-> c != NULL) \ - ) - -/** @brief Prototype for a packet callback, to be called when a new packet arrives - * @param session The current session of the packet - * @param type packet type (see ssh2.h) - * @param packet buffer containing the packet, excluding size, type and padding fields - * @param user user argument to the callback - * and are called each time a packet shows up - * @returns SSH_PACKET_USED Packet was parsed and used - * @returns SSH_PACKET_NOT_USED Packet was not used or understood, processing must continue - */ -typedef int (*ssh_packet_callback) (ssh_session session, uint8_t type, ssh_buffer packet, void *user); - -/** return values for a ssh_packet_callback */ -/** Packet was used and should not be parsed by another callback */ -#define SSH_PACKET_USED 1 -/** Packet was not used and should be passed to any other callback - * available */ -#define SSH_PACKET_NOT_USED 2 - - -/** @brief This macro declares a packet callback handler - * @code - * SSH_PACKET_CALLBACK(mycallback){ - * ... - * } - * @endcode - */ -#define SSH_PACKET_CALLBACK(name) \ - int name (ssh_session session, uint8_t type, ssh_buffer packet, void *user) - -struct ssh_packet_callbacks_struct { - /** Index of the first packet type being handled */ - uint8_t start; - /** Number of packets being handled by this callback struct */ - uint8_t n_callbacks; - /** A pointer to n_callbacks packet callbacks */ - ssh_packet_callback *callbacks; - /** - * User-provided data. User is free to set anything he wants here - */ - void *user; -}; - -typedef struct ssh_packet_callbacks_struct *ssh_packet_callbacks; - -/** - * @brief Set the session callback functions. - * - * This functions sets the callback structure to use your own callback - * functions for auth, logging and status. - * - * @code - * struct ssh_callbacks_struct cb = { - * .userdata = data, - * .auth_function = my_auth_function - * }; - * ssh_callbacks_init(&cb); - * ssh_set_callbacks(session, &cb); - * @endcode - * - * @param session The session to set the callback structure. - * - * @param cb The callback structure itself. - * - * @return SSH_OK on success, SSH_ERROR on error. - */ -LIBSSH_API int ssh_set_callbacks(ssh_session session, ssh_callbacks cb); - -/** - * @brief SSH channel data callback. Called when data is available on a channel - * @param session Current session handler - * @param channel the actual channel - * @param data the data that has been read on the channel - * @param len the length of the data - * @param is_stderr is 0 for stdout or 1 for stderr - * @param userdata Userdata to be passed to the callback function. - * @returns number of bytes processed by the callee. The remaining bytes will - * be sent in the next callback message, when more data is available. - */ -typedef int (*ssh_channel_data_callback) (ssh_session session, - ssh_channel channel, - void *data, - uint32_t len, - int is_stderr, - void *userdata); - -/** - * @brief SSH channel eof callback. Called when a channel receives EOF - * @param session Current session handler - * @param channel the actual channel - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_channel_eof_callback) (ssh_session session, - ssh_channel channel, - void *userdata); - -/** - * @brief SSH channel close callback. Called when a channel is closed by remote peer - * @param session Current session handler - * @param channel the actual channel - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_channel_close_callback) (ssh_session session, - ssh_channel channel, - void *userdata); - -/** - * @brief SSH channel signal callback. Called when a channel has received a signal - * @param session Current session handler - * @param channel the actual channel - * @param signal the signal name (without the SIG prefix) - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_channel_signal_callback) (ssh_session session, - ssh_channel channel, - const char *signal, - void *userdata); - -/** - * @brief SSH channel exit status callback. Called when a channel has received an exit status - * @param session Current session handler - * @param channel the actual channel - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_channel_exit_status_callback) (ssh_session session, - ssh_channel channel, - int exit_status, - void *userdata); - -/** - * @brief SSH channel exit signal callback. Called when a channel has received an exit signal - * @param session Current session handler - * @param channel the actual channel - * @param signal the signal name (without the SIG prefix) - * @param core a boolean telling wether a core has been dumped or not - * @param errmsg the description of the exception - * @param lang the language of the description (format: RFC 3066) - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_channel_exit_signal_callback) (ssh_session session, - ssh_channel channel, - const char *signal, - int core, - const char *errmsg, - const char *lang, - void *userdata); - -/** - * @brief SSH channel PTY request from a client. - * @param channel the channel - * @param term The type of terminal emulation - * @param width width of the terminal, in characters - * @param height height of the terminal, in characters - * @param pxwidth width of the terminal, in pixels - * @param pxheight height of the terminal, in pixels - * @param userdata Userdata to be passed to the callback function. - * @returns 0 if the pty request is accepted - * @returns -1 if the request is denied - */ -typedef int (*ssh_channel_pty_request_callback) (ssh_session session, - ssh_channel channel, - const char *term, - int width, int height, - int pxwidth, int pwheight, - void *userdata); - -/** - * @brief SSH channel Shell request from a client. - * @param channel the channel - * @param userdata Userdata to be passed to the callback function. - * @returns 0 if the shell request is accepted - * @returns 1 if the request is denied - */ -typedef int (*ssh_channel_shell_request_callback) (ssh_session session, - ssh_channel channel, - void *userdata); -/** - * @brief SSH auth-agent-request from the client. This request is - * sent by a client when agent forwarding is available. - * Server is free to ignore this callback, no answer is expected. - * @param channel the channel - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_channel_auth_agent_req_callback) (ssh_session session, - ssh_channel channel, - void *userdata); - -/** - * @brief SSH X11 request from the client. This request is - * sent by a client when X11 forwarding is requested(and available). - * Server is free to ignore this callback, no answer is expected. - * @param channel the channel - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_channel_x11_req_callback) (ssh_session session, - ssh_channel channel, - int single_connection, - const char *auth_protocol, - const char *auth_cookie, - uint32_t screen_number, - void *userdata); -/** - * @brief SSH channel PTY windows change (terminal size) from a client. - * @param channel the channel - * @param width width of the terminal, in characters - * @param height height of the terminal, in characters - * @param pxwidth width of the terminal, in pixels - * @param pxheight height of the terminal, in pixels - * @param userdata Userdata to be passed to the callback function. - * @returns 0 if the pty request is accepted - * @returns -1 if the request is denied - */ -typedef int (*ssh_channel_pty_window_change_callback) (ssh_session session, - ssh_channel channel, - int width, int height, - int pxwidth, int pwheight, - void *userdata); - -/** - * @brief SSH channel Exec request from a client. - * @param channel the channel - * @param command the shell command to be executed - * @param userdata Userdata to be passed to the callback function. - * @returns 0 if the exec request is accepted - * @returns 1 if the request is denied - */ -typedef int (*ssh_channel_exec_request_callback) (ssh_session session, - ssh_channel channel, - const char *command, - void *userdata); - -/** - * @brief SSH channel environment request from a client. - * @param channel the channel - * @param env_name name of the environment value to be set - * @param env_value value of the environment value to be set - * @param userdata Userdata to be passed to the callback function. - * @returns 0 if the env request is accepted - * @returns 1 if the request is denied - * @warning some environment variables can be dangerous if changed (e.g. - * LD_PRELOAD) and should not be fulfilled. - */ -typedef int (*ssh_channel_env_request_callback) (ssh_session session, - ssh_channel channel, - const char *env_name, - const char *env_value, - void *userdata); -/** - * @brief SSH channel subsystem request from a client. - * @param channel the channel - * @param subsystem the subsystem required - * @param userdata Userdata to be passed to the callback function. - * @returns 0 if the subsystem request is accepted - * @returns 1 if the request is denied - */ -typedef int (*ssh_channel_subsystem_request_callback) (ssh_session session, - ssh_channel channel, - const char *subsystem, - void *userdata); - - -struct ssh_channel_callbacks_struct { - /** DON'T SET THIS use ssh_callbacks_init() instead. */ - size_t size; - /** - * User-provided data. User is free to set anything he wants here - */ - void *userdata; - /** - * This functions will be called when there is data available. - */ - ssh_channel_data_callback channel_data_function; - /** - * This functions will be called when the channel has received an EOF. - */ - ssh_channel_eof_callback channel_eof_function; - /** - * This functions will be called when the channel has been closed by remote - */ - ssh_channel_close_callback channel_close_function; - /** - * This functions will be called when a signal has been received - */ - ssh_channel_signal_callback channel_signal_function; - /** - * This functions will be called when an exit status has been received - */ - ssh_channel_exit_status_callback channel_exit_status_function; - /** - * This functions will be called when an exit signal has been received - */ - ssh_channel_exit_signal_callback channel_exit_signal_function; - /** - * This function will be called when a client requests a PTY - */ - ssh_channel_pty_request_callback channel_pty_request_function; - /** - * This function will be called when a client requests a shell - */ - ssh_channel_shell_request_callback channel_shell_request_function; - /** This function will be called when a client requests agent - * authentication forwarding. - */ - ssh_channel_auth_agent_req_callback channel_auth_agent_req_function; - /** This function will be called when a client requests X11 - * forwarding. - */ - ssh_channel_x11_req_callback channel_x11_req_function; - /** This function will be called when a client requests a - * window change. - */ - ssh_channel_pty_window_change_callback channel_pty_window_change_function; - /** This function will be called when a client requests a - * command execution. - */ - ssh_channel_exec_request_callback channel_exec_request_function; - /** This function will be called when a client requests an environment - * variable to be set. - */ - ssh_channel_env_request_callback channel_env_request_function; - /** This function will be called when a client requests a subsystem - * (like sftp). - */ - ssh_channel_subsystem_request_callback channel_subsystem_request_function; -}; - -typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks; - -/** - * @brief Set the channel callback functions. - * - * This functions sets the callback structure to use your own callback - * functions for channel data and exceptions - * - * @code - * struct ssh_channel_callbacks_struct cb = { - * .userdata = data, - * .channel_data = my_channel_data_function - * }; - * ssh_callbacks_init(&cb); - * ssh_set_channel_callbacks(channel, &cb); - * @endcode - * - * @param channel The channel to set the callback structure. - * - * @param cb The callback structure itself. - * - * @return SSH_OK on success, SSH_ERROR on error. - */ -LIBSSH_API int ssh_set_channel_callbacks(ssh_channel channel, - ssh_channel_callbacks cb); - -/** @} */ - -/** @group libssh_threads - * @{ - */ - -typedef int (*ssh_thread_callback) (void **lock); - -typedef unsigned long (*ssh_thread_id_callback) (void); -struct ssh_threads_callbacks_struct { - const char *type; - ssh_thread_callback mutex_init; - ssh_thread_callback mutex_destroy; - ssh_thread_callback mutex_lock; - ssh_thread_callback mutex_unlock; - ssh_thread_id_callback thread_id; -}; - -/** - * @brief Set the thread callbacks structure. - * - * This is necessary if your program is using libssh in a multithreaded fashion. - * This function must be called first, outside of any threading context (in your - * main() function for instance), before you call ssh_init(). - * - * @param[in] cb A pointer to a ssh_threads_callbacks_struct structure, which - * contains the different callbacks to be set. - * - * @returns Always returns SSH_OK. - * - * @see ssh_threads_callbacks_struct - * @see SSH_THREADS_PTHREAD - * @bug libgcrypt 1.6 and bigger backend does not support custom callback. - * Using anything else than pthreads here will fail. - */ -LIBSSH_API int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct - *cb); - -/** - * @brief returns a pointer on the pthread threads callbacks, to be used with - * ssh_threads_set_callbacks. - * @warning you have to link with the library ssh_threads. - * @see ssh_threads_set_callbacks - */ -LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void); - -/** - * @brief Get the noop threads callbacks structure - * - * This can be used with ssh_threads_set_callbacks. These callbacks do nothing - * and are being used by default. - * - * @return Always returns a valid pointer to the noop callbacks structure. - * - * @see ssh_threads_set_callbacks - */ -LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void); - -/** - * @brief Set the logging callback function. - * - * @param[in] cb The callback to set. - * - * @return 0 on success, < 0 on errror. - */ -LIBSSH_API int ssh_set_log_callback(ssh_logging_callback cb); - -/** - * @brief Get the pointer to the logging callback function. - * - * @return The pointer the the callback or NULL if none set. - */ -LIBSSH_API ssh_logging_callback ssh_get_log_callback(void); - -/** @} */ -#ifdef __cplusplus -} -#endif - -#endif /*_SSH_CALLBACK_H */ - -/* @} */ diff --git a/libssh/include/libssh/channels.h b/libssh/include/libssh/channels.h deleted file mode 100644 index b05c4c02..00000000 --- a/libssh/include/libssh/channels.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CHANNELS_H_ -#define CHANNELS_H_ -#include "libssh/priv.h" - -/** @internal - * Describes the different possible states in a - * outgoing (client) channel request - */ -enum ssh_channel_request_state_e { - /** No request has been made */ - SSH_CHANNEL_REQ_STATE_NONE = 0, - /** A request has been made and answer is pending */ - SSH_CHANNEL_REQ_STATE_PENDING, - /** A request has been replied and accepted */ - SSH_CHANNEL_REQ_STATE_ACCEPTED, - /** A request has been replied and refused */ - SSH_CHANNEL_REQ_STATE_DENIED, - /** A request has been replied and an error happend */ - SSH_CHANNEL_REQ_STATE_ERROR -}; - -enum ssh_channel_state_e { - SSH_CHANNEL_STATE_NOT_OPEN = 0, - SSH_CHANNEL_STATE_OPENING, - SSH_CHANNEL_STATE_OPEN_DENIED, - SSH_CHANNEL_STATE_OPEN, - SSH_CHANNEL_STATE_CLOSED -}; - -/* The channel has been closed by the remote side */ -#define SSH_CHANNEL_FLAG_CLOSED_REMOTE 0x1 -/* The channel has been freed by the calling program */ -#define SSH_CHANNEL_FLAG_FREED_LOCAL 0x2 -/* the channel has not yet been bound to a remote one */ -#define SSH_CHANNEL_FLAG_NOT_BOUND 0x4 - -struct ssh_channel_struct { - ssh_session session; /* SSH_SESSION pointer */ - uint32_t local_channel; - uint32_t local_window; - int local_eof; - uint32_t local_maxpacket; - - uint32_t remote_channel; - uint32_t remote_window; - int remote_eof; /* end of file received */ - uint32_t remote_maxpacket; - enum ssh_channel_state_e state; - int delayed_close; - int flags; - ssh_buffer stdout_buffer; - ssh_buffer stderr_buffer; - void *userarg; - int version; - int exit_status; - enum ssh_channel_request_state_e request_state; - ssh_channel_callbacks callbacks; - /* counters */ - ssh_counter counter; -}; - -SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf); -SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail); -SSH_PACKET_CALLBACK(ssh_packet_channel_success); -SSH_PACKET_CALLBACK(ssh_packet_channel_failure); -SSH_PACKET_CALLBACK(ssh_request_success); -SSH_PACKET_CALLBACK(ssh_request_denied); - -SSH_PACKET_CALLBACK(channel_rcv_change_window); -SSH_PACKET_CALLBACK(channel_rcv_eof); -SSH_PACKET_CALLBACK(channel_rcv_close); -SSH_PACKET_CALLBACK(channel_rcv_request); -SSH_PACKET_CALLBACK(channel_rcv_data); - -ssh_channel ssh_channel_new(ssh_session session); -int channel_default_bufferize(ssh_channel channel, void *data, int len, - int is_stderr); -int ssh_channel_flush(ssh_channel channel); -uint32_t ssh_channel_new_id(ssh_session session); -ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id); -void ssh_channel_do_free(ssh_channel channel); -#ifdef WITH_SSH1 -SSH_PACKET_CALLBACK(ssh_packet_data1); -SSH_PACKET_CALLBACK(ssh_packet_close1); -SSH_PACKET_CALLBACK(ssh_packet_exist_status1); - -/* channels1.c */ -int channel_open_session1(ssh_channel channel); -int channel_request_pty_size1(ssh_channel channel, const char *terminal, - int cols, int rows); -int channel_change_pty_size1(ssh_channel channel, int cols, int rows); -int channel_request_shell1(ssh_channel channel); -int channel_request_exec1(ssh_channel channel, const char *cmd); -int channel_write1(ssh_channel channel, const void *data, int len); -ssh_channel ssh_get_channel1(ssh_session session); -#endif - -#endif /* CHANNELS_H_ */ diff --git a/libssh/include/libssh/crc32.h b/libssh/include/libssh/crc32.h deleted file mode 100644 index 07c0cafc..00000000 --- a/libssh/include/libssh/crc32.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * crc32.c - simple CRC32 code - * - * This file is part of the SSH Library - * - * Copyright (c) 2005 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _CRC32_H -#define _CRC32_H - -uint32_t ssh_crc32(const char *buf, uint32_t len); - -#endif /* _CRC32_H */ diff --git a/libssh/include/libssh/crypto.h b/libssh/include/libssh/crypto.h deleted file mode 100644 index 61a2b27b..00000000 --- a/libssh/include/libssh/crypto.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * crypto.h is an include file for internal cryptographic structures of libssh - */ - -#ifndef _CRYPTO_H_ -#define _CRYPTO_H_ - -#include "config.h" - -#ifdef HAVE_LIBGCRYPT -#include -#endif -#include "libssh/wrapper.h" - -#ifdef cbc_encrypt -#undef cbc_encrypt -#endif -#ifdef cbc_decrypt -#undef cbc_decrypt -#endif - -#ifdef HAVE_OPENSSL_ECDH_H -#include -#endif -#include "libssh/ecdh.h" -#include "libssh/kex.h" -#include "libssh/curve25519.h" - -#define DIGEST_MAX_LEN 64 - -enum ssh_key_exchange_e { - /* diffie-hellman-group1-sha1 */ - SSH_KEX_DH_GROUP1_SHA1=1, - /* diffie-hellman-group14-sha1 */ - SSH_KEX_DH_GROUP14_SHA1, - /* ecdh-sha2-nistp256 */ - SSH_KEX_ECDH_SHA2_NISTP256, - /* curve25519-sha256@libssh.org */ - SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG -}; - -struct ssh_crypto_struct { - bignum e,f,x,k,y; -#ifdef HAVE_ECDH - EC_KEY *ecdh_privkey; - ssh_string ecdh_client_pubkey; - ssh_string ecdh_server_pubkey; -#endif -#ifdef HAVE_CURVE25519 - ssh_curve25519_privkey curve25519_privkey; - ssh_curve25519_pubkey curve25519_client_pubkey; - ssh_curve25519_pubkey curve25519_server_pubkey; -#endif - ssh_string dh_server_signature; /* information used by dh_handshake. */ - size_t digest_len; /* len of all the fields below */ - unsigned char *session_id; - unsigned char *secret_hash; /* Secret hash is same as session id until re-kex */ - unsigned char *encryptIV; - unsigned char *decryptIV; - unsigned char *decryptkey; - unsigned char *encryptkey; - unsigned char *encryptMAC; - unsigned char *decryptMAC; - unsigned char hmacbuf[DIGEST_MAX_LEN]; - struct ssh_cipher_struct *in_cipher, *out_cipher; /* the cipher structures/objects */ - enum ssh_hmac_e in_hmac, out_hmac; /* the MAC algorithms used */ - - ssh_string server_pubkey; - const char *server_pubkey_type; - int do_compress_out; /* idem */ - int do_compress_in; /* don't set them, set the option instead */ - int delayed_compress_in; /* Use of zlib@openssh.org */ - int delayed_compress_out; - void *compress_out_ctx; /* don't touch it */ - void *compress_in_ctx; /* really, don't */ - /* kex sent by server, client, and mutually elected methods */ - struct ssh_kex_struct server_kex; - struct ssh_kex_struct client_kex; - char *kex_methods[SSH_KEX_METHODS]; - enum ssh_key_exchange_e kex_type; - enum ssh_mac_e mac_type; /* Mac operations to use for key gen */ -}; - -struct ssh_cipher_struct { - const char *name; /* ssh name of the algorithm */ - unsigned int blocksize; /* blocksize of the algo */ - unsigned int keylen; /* length of the key structure */ -#ifdef HAVE_LIBGCRYPT - gcry_cipher_hd_t *key; -#elif defined HAVE_LIBCRYPTO - void *key; /* a key buffer allocated for the algo */ - void *IV; -#endif - unsigned int keysize; /* bytes of key used. != keylen */ - /* sets the new key for immediate use */ - int (*set_encrypt_key)(struct ssh_cipher_struct *cipher, void *key, void *IV); - int (*set_decrypt_key)(struct ssh_cipher_struct *cipher, void *key, void *IV); - void (*encrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len); - void (*decrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len); -}; - -/* vim: set ts=2 sw=2 et cindent: */ -#endif /* _CRYPTO_H_ */ diff --git a/libssh/include/libssh/curve25519.h b/libssh/include/libssh/curve25519.h deleted file mode 100644 index 0406b9ee..00000000 --- a/libssh/include/libssh/curve25519.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2013 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef CURVE25519_H_ -#define CURVE25519_H_ - -#include "config.h" -#include "libssh.h" - -#ifdef WITH_NACL - -#include -#define CURVE25519_PUBKEY_SIZE crypto_scalarmult_curve25519_BYTES -#define CURVE25519_PRIVKEY_SIZE crypto_scalarmult_curve25519_SCALARBYTES -#define crypto_scalarmult_base crypto_scalarmult_curve25519_base -#define crypto_scalarmult crypto_scalarmult_curve25519 -#else - -#define CURVE25519_PUBKEY_SIZE 32 -#define CURVE25519_PRIVKEY_SIZE 32 -int crypto_scalarmult_base(unsigned char *q, const unsigned char *n); -int crypto_scalarmult(unsigned char *q, const unsigned char *n, const unsigned char *p); -#endif /* WITH_NACL */ - -#ifdef HAVE_ECC -#define HAVE_CURVE25519 1 -#endif - -typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE]; -typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE]; - - -int ssh_client_curve25519_init(ssh_session session); -int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet); - -#ifdef WITH_SERVER -int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet); -#endif /* WITH_SERVER */ - -#endif /* CURVE25519_H_ */ diff --git a/libssh/include/libssh/dh.h b/libssh/include/libssh/dh.h deleted file mode 100644 index 95b76cdd..00000000 --- a/libssh/include/libssh/dh.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef DH_H_ -#define DH_H_ - -#include "config.h" - -#include "libssh/crypto.h" - -int dh_generate_e(ssh_session session); -int dh_generate_f(ssh_session session); -int dh_generate_x(ssh_session session); -int dh_generate_y(ssh_session session); - -int ssh_crypto_init(void); -void ssh_crypto_finalize(void); - -ssh_string dh_get_e(ssh_session session); -ssh_string dh_get_f(ssh_session session); -int dh_import_f(ssh_session session,ssh_string f_string); -int dh_import_e(ssh_session session, ssh_string e_string); -void dh_import_pubkey(ssh_session session,ssh_string pubkey_string); -int dh_build_k(ssh_session session); -int ssh_client_dh_init(ssh_session session); -int ssh_client_dh_reply(ssh_session session, ssh_buffer packet); - -int make_sessionid(ssh_session session); -/* add data for the final cookie */ -int hashbufin_add_cookie(ssh_session session, unsigned char *cookie); -int hashbufout_add_cookie(ssh_session session); -int generate_session_keys(ssh_session session); - -#endif /* DH_H_ */ diff --git a/libssh/include/libssh/ecdh.h b/libssh/include/libssh/ecdh.h deleted file mode 100644 index 8d1e7515..00000000 --- a/libssh/include/libssh/ecdh.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2011 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ECDH_H_ -#define ECDH_H_ - -#include "config.h" - -#ifdef HAVE_LIBCRYPTO -#ifdef HAVE_OPENSSL_ECDH_H - -#ifdef HAVE_ECC -#define HAVE_ECDH 1 -#endif - -#endif /* HAVE_OPENSSL_ECDH_H */ -#endif /* HAVE_LIBCRYPTO */ - -int ssh_client_ecdh_init(ssh_session session); -int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet); - -#ifdef WITH_SERVER -int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet); -#endif /* WITH_SERVER */ - -#endif /* ECDH_H_ */ diff --git a/libssh/include/libssh/ed25519.h b/libssh/include/libssh/ed25519.h deleted file mode 100644 index 7b48856c..00000000 --- a/libssh/include/libssh/ed25519.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2014 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef ED25519_H_ -#define ED25519_H_ -#include "libssh/priv.h" - -/** - * @defgroup ed25519 ed25519 API - * @internal - * @brief API for DJB's ed25519 - * - * @{ */ - -#define ED25519_PK_LEN 32 -#define ED25519_SK_LEN 64 -#define ED25519_SIG_LEN 64 - -typedef uint8_t ed25519_pubkey[ED25519_PK_LEN]; -typedef uint8_t ed25519_privkey[ED25519_SK_LEN]; -typedef uint8_t ed25519_signature[ED25519_SIG_LEN]; - -/** @internal - * @brief generate an ed25519 key pair - * @param[out] pk generated public key - * @param[out] sk generated secret key - * @return 0 on success, -1 on error. - * */ -int crypto_sign_ed25519_keypair(ed25519_pubkey pk, ed25519_privkey sk); - -/** @internal - * @brief sign a message with ed25519 - * @param[out] sm location to store the signed message. - * Its length should be mlen + 64. - * @param[out] smlen pointer to the size of the signed message - * @param[in] m message to be signed - * @param[in] mlen length of the message to be signed - * @param[in] sk secret key to sign the message with - * @return 0 on success. - */ -int crypto_sign_ed25519( - unsigned char *sm,unsigned long long *smlen, - const unsigned char *m,unsigned long long mlen, - const ed25519_privkey sk); - -/** @internal - * @brief "open" and verify the signature of a signed message - * @param[out] m location to store the verified message. - * Its length should be equal to smlen. - * @param[out] mlen pointer to the size of the verified message - * @param[in] sm signed message to verify - * @param[in] smlen length of the signed message to verify - * @param[in] pk public key used to sign the message - * @returns 0 on success (supposedly). - */ -int crypto_sign_ed25519_open( - unsigned char *m,unsigned long long *mlen, - const unsigned char *sm,unsigned long long smlen, - const ed25519_pubkey pk); - -/** @} */ -#endif /* ED25519_H_ */ diff --git a/libssh/include/libssh/fe25519.h b/libssh/include/libssh/fe25519.h deleted file mode 100644 index e959912e..00000000 --- a/libssh/include/libssh/fe25519.h +++ /dev/null @@ -1,68 +0,0 @@ -/* $OpenBSD: fe25519.h,v 1.3 2013/12/09 11:03:45 markus Exp $ */ - -/* - * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, - * Peter Schwabe, Bo-Yin Yang. - * Copied from supercop-20130419/crypto_sign/ed25519/ref/fe25519.h - */ - -#ifndef FE25519_H -#define FE25519_H - -#include "libssh/priv.h" - -#define fe25519 crypto_sign_ed25519_ref_fe25519 -#define fe25519_freeze crypto_sign_ed25519_ref_fe25519_freeze -#define fe25519_unpack crypto_sign_ed25519_ref_fe25519_unpack -#define fe25519_pack crypto_sign_ed25519_ref_fe25519_pack -#define fe25519_iszero crypto_sign_ed25519_ref_fe25519_iszero -#define fe25519_iseq_vartime crypto_sign_ed25519_ref_fe25519_iseq_vartime -#define fe25519_cmov crypto_sign_ed25519_ref_fe25519_cmov -#define fe25519_setone crypto_sign_ed25519_ref_fe25519_setone -#define fe25519_setzero crypto_sign_ed25519_ref_fe25519_setzero -#define fe25519_neg crypto_sign_ed25519_ref_fe25519_neg -#define fe25519_getparity crypto_sign_ed25519_ref_fe25519_getparity -#define fe25519_add crypto_sign_ed25519_ref_fe25519_add -#define fe25519_sub crypto_sign_ed25519_ref_fe25519_sub -#define fe25519_mul crypto_sign_ed25519_ref_fe25519_mul -#define fe25519_square crypto_sign_ed25519_ref_fe25519_square -#define fe25519_invert crypto_sign_ed25519_ref_fe25519_invert -#define fe25519_pow2523 crypto_sign_ed25519_ref_fe25519_pow2523 - -typedef struct { - uint32_t v[32]; -} fe25519; - -void fe25519_freeze(fe25519 *r); - -void fe25519_unpack(fe25519 *r, const unsigned char x[32]); - -void fe25519_pack(unsigned char r[32], const fe25519 *x); - -int fe25519_iszero(const fe25519 *x); - -int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y); - -void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b); - -void fe25519_setone(fe25519 *r); - -void fe25519_setzero(fe25519 *r); - -void fe25519_neg(fe25519 *r, const fe25519 *x); - -unsigned char fe25519_getparity(const fe25519 *x); - -void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y); - -void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y); - -void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y); - -void fe25519_square(fe25519 *r, const fe25519 *x); - -void fe25519_invert(fe25519 *r, const fe25519 *x); - -void fe25519_pow2523(fe25519 *r, const fe25519 *x); - -#endif diff --git a/libssh/include/libssh/ge25519.h b/libssh/include/libssh/ge25519.h deleted file mode 100644 index 64f63c6f..00000000 --- a/libssh/include/libssh/ge25519.h +++ /dev/null @@ -1,43 +0,0 @@ -/* $OpenBSD: ge25519.h,v 1.3 2013/12/09 11:03:45 markus Exp $ */ - -/* - * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, - * Peter Schwabe, Bo-Yin Yang. - * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519.h - */ - -#ifndef GE25519_H -#define GE25519_H - -#include "fe25519.h" -#include "sc25519.h" - -#define ge25519 crypto_sign_ed25519_ref_ge25519 -#define ge25519_base crypto_sign_ed25519_ref_ge25519_base -#define ge25519_unpackneg_vartime crypto_sign_ed25519_ref_unpackneg_vartime -#define ge25519_pack crypto_sign_ed25519_ref_pack -#define ge25519_isneutral_vartime crypto_sign_ed25519_ref_isneutral_vartime -#define ge25519_double_scalarmult_vartime crypto_sign_ed25519_ref_double_scalarmult_vartime -#define ge25519_scalarmult_base crypto_sign_ed25519_ref_scalarmult_base - -typedef struct -{ - fe25519 x; - fe25519 y; - fe25519 z; - fe25519 t; -} ge25519; - -const ge25519 ge25519_base; - -int ge25519_unpackneg_vartime(ge25519 *r, const unsigned char p[32]); - -void ge25519_pack(unsigned char r[32], const ge25519 *p); - -int ge25519_isneutral_vartime(const ge25519 *p); - -void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const sc25519 *s1, const ge25519 *p2, const sc25519 *s2); - -void ge25519_scalarmult_base(ge25519 *r, const sc25519 *s); - -#endif diff --git a/libssh/include/libssh/gssapi.h b/libssh/include/libssh/gssapi.h deleted file mode 100644 index ccd83664..00000000 --- a/libssh/include/libssh/gssapi.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2013 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef GSSAPI_H_ -#define GSSAPI_H_ - -#include "config.h" -#include "session.h" - -/* all OID begin with the tag identifier + length */ -#define SSH_OID_TAG 06 - -typedef struct ssh_gssapi_struct *ssh_gssapi; - -#ifdef WITH_SERVER -int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids); -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server); -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic); -#endif /* WITH_SERVER */ - -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token); -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client); -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response); - - -int ssh_gssapi_auth_mic(ssh_session session); - -#endif /* GSSAPI_H */ diff --git a/libssh/include/libssh/kex.h b/libssh/include/libssh/kex.h deleted file mode 100644 index 1a5b6d41..00000000 --- a/libssh/include/libssh/kex.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef KEX_H_ -#define KEX_H_ - -#include "libssh/priv.h" -#include "libssh/callbacks.h" - -#define SSH_KEX_METHODS 10 - -struct ssh_kex_struct { - unsigned char cookie[16]; - char *methods[SSH_KEX_METHODS]; -}; - -SSH_PACKET_CALLBACK(ssh_packet_kexinit); -#ifdef WITH_SSH1 -SSH_PACKET_CALLBACK(ssh_packet_publickey1); -#endif - -int ssh_send_kex(ssh_session session, int server_kex); -void ssh_list_kex(struct ssh_kex_struct *kex); -int set_client_kex(ssh_session session); -int ssh_kex_select_methods(ssh_session session); -int verify_existing_algo(int algo, const char *name); -char **space_tokenize(const char *chain); -int ssh_get_kex1(ssh_session session); -char *ssh_find_matching(const char *in_d, const char *what_d); -const char *ssh_kex_get_supported_method(uint32_t algo); -const char *ssh_kex_get_description(uint32_t algo); - -#endif /* KEX_H_ */ diff --git a/libssh/include/libssh/keys.h b/libssh/include/libssh/keys.h deleted file mode 100644 index 6f08e070..00000000 --- a/libssh/include/libssh/keys.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef KEYS_H_ -#define KEYS_H_ - -#include "config.h" -#include "libssh/libssh.h" -#include "libssh/wrapper.h" - -struct ssh_public_key_struct { - int type; - const char *type_c; /* Don't free it ! it is static */ -#ifdef HAVE_LIBGCRYPT - gcry_sexp_t dsa_pub; - gcry_sexp_t rsa_pub; -#elif HAVE_LIBCRYPTO - DSA *dsa_pub; - RSA *rsa_pub; -#endif -}; - -struct ssh_private_key_struct { - int type; -#ifdef HAVE_LIBGCRYPT - gcry_sexp_t dsa_priv; - gcry_sexp_t rsa_priv; -#elif defined HAVE_LIBCRYPTO - DSA *dsa_priv; - RSA *rsa_priv; -#endif -}; - -const char *ssh_type_to_char(int type); -int ssh_type_from_name(const char *name); - -ssh_public_key publickey_from_string(ssh_session session, ssh_string pubkey_s); - -#endif /* KEYS_H_ */ diff --git a/libssh/include/libssh/knownhosts.h b/libssh/include/libssh/knownhosts.h deleted file mode 100644 index 723c7ebc..00000000 --- a/libssh/include/libssh/knownhosts.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 20014 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef KNOWNHOSTS_H_ -#define KNOWNHOSTS_H_ - -char **ssh_knownhosts_algorithms(ssh_session session); - -#endif /* KNOWNHOSTS_H_ */ diff --git a/libssh/include/libssh/legacy.h b/libssh/include/libssh/legacy.h deleted file mode 100644 index 911173ee..00000000 --- a/libssh/include/libssh/legacy.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* Since libssh.h includes legacy.h, it's important that libssh.h is included - * first. we don't define LEGACY_H now because we want it to be defined when - * included from libssh.h - * All function calls declared in this header are deprecated and meant to be - * removed in future. - */ - -#ifndef LEGACY_H_ -#define LEGACY_H_ - -typedef struct ssh_private_key_struct* ssh_private_key; -typedef struct ssh_public_key_struct* ssh_public_key; - -LIBSSH_API int ssh_auth_list(ssh_session session); -LIBSSH_API int ssh_userauth_offer_pubkey(ssh_session session, const char *username, int type, ssh_string publickey); -LIBSSH_API int ssh_userauth_pubkey(ssh_session session, const char *username, ssh_string publickey, ssh_private_key privatekey); -#ifndef _WIN32 -LIBSSH_API int ssh_userauth_agent_pubkey(ssh_session session, const char *username, - ssh_public_key publickey); -#endif -LIBSSH_API int ssh_userauth_autopubkey(ssh_session session, const char *passphrase); -LIBSSH_API int ssh_userauth_privatekey_file(ssh_session session, const char *username, - const char *filename, const char *passphrase); - -SSH_DEPRECATED LIBSSH_API void buffer_free(ssh_buffer buffer); -SSH_DEPRECATED LIBSSH_API void *buffer_get(ssh_buffer buffer); -SSH_DEPRECATED LIBSSH_API uint32_t buffer_get_len(ssh_buffer buffer); -SSH_DEPRECATED LIBSSH_API ssh_buffer buffer_new(void); - -SSH_DEPRECATED LIBSSH_API ssh_channel channel_accept_x11(ssh_channel channel, int timeout_ms); -SSH_DEPRECATED LIBSSH_API int channel_change_pty_size(ssh_channel channel,int cols,int rows); -SSH_DEPRECATED LIBSSH_API ssh_channel channel_forward_accept(ssh_session session, int timeout_ms); -SSH_DEPRECATED LIBSSH_API int channel_close(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_forward_cancel(ssh_session session, const char *address, int port); -SSH_DEPRECATED LIBSSH_API int channel_forward_listen(ssh_session session, const char *address, int port, int *bound_port); -SSH_DEPRECATED LIBSSH_API void channel_free(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_get_exit_status(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API ssh_session channel_get_session(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_is_closed(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_is_eof(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_is_open(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API ssh_channel channel_new(ssh_session session); -SSH_DEPRECATED LIBSSH_API int channel_open_forward(ssh_channel channel, const char *remotehost, - int remoteport, const char *sourcehost, int localport); -SSH_DEPRECATED LIBSSH_API int channel_open_session(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_poll(ssh_channel channel, int is_stderr); -SSH_DEPRECATED LIBSSH_API int channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); - -SSH_DEPRECATED LIBSSH_API int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, - int is_stderr); - -SSH_DEPRECATED LIBSSH_API int channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, - int is_stderr); -SSH_DEPRECATED LIBSSH_API int channel_request_env(ssh_channel channel, const char *name, const char *value); -SSH_DEPRECATED LIBSSH_API int channel_request_exec(ssh_channel channel, const char *cmd); -SSH_DEPRECATED LIBSSH_API int channel_request_pty(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_request_pty_size(ssh_channel channel, const char *term, - int cols, int rows); -SSH_DEPRECATED LIBSSH_API int channel_request_shell(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_request_send_signal(ssh_channel channel, const char *signum); -SSH_DEPRECATED LIBSSH_API int channel_request_sftp(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_request_subsystem(ssh_channel channel, const char *subsystem); -SSH_DEPRECATED LIBSSH_API int channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, - const char *cookie, int screen_number); -SSH_DEPRECATED LIBSSH_API int channel_send_eof(ssh_channel channel); -SSH_DEPRECATED LIBSSH_API int channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct - timeval * timeout); -SSH_DEPRECATED LIBSSH_API void channel_set_blocking(ssh_channel channel, int blocking); -SSH_DEPRECATED LIBSSH_API int channel_write(ssh_channel channel, const void *data, uint32_t len); - -LIBSSH_API void privatekey_free(ssh_private_key prv); -LIBSSH_API ssh_private_key privatekey_from_file(ssh_session session, const char *filename, - int type, const char *passphrase); -LIBSSH_API void publickey_free(ssh_public_key key); -LIBSSH_API int ssh_publickey_to_file(ssh_session session, const char *file, - ssh_string pubkey, int type); -LIBSSH_API ssh_string publickey_from_file(ssh_session session, const char *filename, - int *type); -LIBSSH_API ssh_public_key publickey_from_privatekey(ssh_private_key prv); -LIBSSH_API ssh_string publickey_to_string(ssh_public_key key); -LIBSSH_API int ssh_try_publickey_from_file(ssh_session session, const char *keyfile, - ssh_string *publickey, int *type); -LIBSSH_API enum ssh_keytypes_e ssh_privatekey_type(ssh_private_key privatekey); - -LIBSSH_API ssh_string ssh_get_pubkey(ssh_session session); - -LIBSSH_API ssh_message ssh_message_retrieve(ssh_session session, uint32_t packettype); -LIBSSH_API ssh_public_key ssh_message_auth_publickey(ssh_message msg); - -SSH_DEPRECATED LIBSSH_API void string_burn(ssh_string str); -SSH_DEPRECATED LIBSSH_API ssh_string string_copy(ssh_string str); -SSH_DEPRECATED LIBSSH_API void *string_data(ssh_string str); -SSH_DEPRECATED LIBSSH_API int string_fill(ssh_string str, const void *data, size_t len); -SSH_DEPRECATED LIBSSH_API void string_free(ssh_string str); -SSH_DEPRECATED LIBSSH_API ssh_string string_from_char(const char *what); -SSH_DEPRECATED LIBSSH_API size_t string_len(ssh_string str); -SSH_DEPRECATED LIBSSH_API ssh_string string_new(size_t size); -SSH_DEPRECATED LIBSSH_API char *string_to_char(ssh_string str); - -#endif /* LEGACY_H_ */ diff --git a/libssh/include/libssh/libcrypto.h b/libssh/include/libssh/libcrypto.h deleted file mode 100644 index c3783880..00000000 --- a/libssh/include/libssh/libcrypto.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBCRYPTO_H_ -#define LIBCRYPTO_H_ - -#include "config.h" - -#ifdef HAVE_LIBCRYPTO - -#include -#include -#include -#include -#include -#ifdef HAVE_OPENSSL_ECC -#include -#endif - -typedef SHA_CTX* SHACTX; -typedef SHA256_CTX* SHA256CTX; -typedef SHA512_CTX* SHA384CTX; -typedef SHA512_CTX* SHA512CTX; -typedef MD5_CTX* MD5CTX; -typedef HMAC_CTX* HMACCTX; -#ifdef HAVE_ECC -typedef EVP_MD_CTX *EVPCTX; -#else -typedef void *EVPCTX; -#endif - -#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH -#define SHA256_DIGEST_LEN SHA256_DIGEST_LENGTH -#define SHA384_DIGEST_LEN SHA384_DIGEST_LENGTH -#define SHA512_DIGEST_LEN SHA512_DIGEST_LENGTH -#ifdef MD5_DIGEST_LEN - #undef MD5_DIGEST_LEN -#endif -#define MD5_DIGEST_LEN MD5_DIGEST_LENGTH - -#ifdef HAVE_OPENSSL_ECC -#define EVP_DIGEST_LEN EVP_MAX_MD_SIZE -#endif - -#include -#include -#define OPENSSL_0_9_7b 0x0090702fL -#if (OPENSSL_VERSION_NUMBER <= OPENSSL_0_9_7b) -#define BROKEN_AES_CTR -#endif -typedef BIGNUM* bignum; -typedef BN_CTX* bignum_CTX; - -#define bignum_new() BN_new() -#define bignum_free(num) BN_clear_free(num) -#define bignum_set_word(bn,n) BN_set_word(bn,n) -#define bignum_bin2bn(bn,datalen,data) BN_bin2bn(bn,datalen,data) -#define bignum_bn2dec(num) BN_bn2dec(num) -#define bignum_dec2bn(bn,data) BN_dec2bn(data,bn) -#define bignum_bn2hex(num) BN_bn2hex(num) -#define bignum_rand(rnd, bits, top, bottom) BN_rand(rnd,bits,top,bottom) -#define bignum_ctx_new() BN_CTX_new() -#define bignum_ctx_free(num) BN_CTX_free(num) -#define bignum_mod_exp(dest,generator,exp,modulo,ctx) BN_mod_exp(dest,generator,exp,modulo,ctx) -#define bignum_num_bytes(num) BN_num_bytes(num) -#define bignum_num_bits(num) BN_num_bits(num) -#define bignum_is_bit_set(num,bit) BN_is_bit_set(num,bit) -#define bignum_bn2bin(num,ptr) BN_bn2bin(num,ptr) -#define bignum_cmp(num1,num2) BN_cmp(num1,num2) - -SHA256CTX sha256_init(void); -void sha256_update(SHA256CTX c, const void *data, unsigned long len); -void sha256_final(unsigned char *md, SHA256CTX c); - -SHA384CTX sha384_init(void); -void sha384_update(SHA384CTX c, const void *data, unsigned long len); -void sha384_final(unsigned char *md, SHA384CTX c); - -SHA512CTX sha512_init(void); -void sha512_update(SHA512CTX c, const void *data, unsigned long len); -void sha512_final(unsigned char *md, SHA512CTX c); - -struct ssh_cipher_struct *ssh_get_ciphertab(void); - -#endif /* HAVE_LIBCRYPTO */ - -#endif /* LIBCRYPTO_H_ */ diff --git a/libssh/include/libssh/libgcrypt.h b/libssh/include/libssh/libgcrypt.h deleted file mode 100644 index 8e52a681..00000000 --- a/libssh/include/libssh/libgcrypt.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBGCRYPT_H_ -#define LIBGCRYPT_H_ - -#include "config.h" - -#ifdef HAVE_LIBGCRYPT - -#include -typedef gcry_md_hd_t SHACTX; -typedef gcry_md_hd_t SHA256CTX; -typedef gcry_md_hd_t SHA384CTX; -typedef gcry_md_hd_t SHA512CTX; -typedef gcry_md_hd_t MD5CTX; -typedef gcry_md_hd_t HMACCTX; -typedef void *EVPCTX; -#define SHA_DIGEST_LENGTH 20 -#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH -#define MD5_DIGEST_LEN 16 -#define SHA256_DIGEST_LENGTH 32 -#define SHA256_DIGEST_LEN SHA256_DIGEST_LENGTH -#define SHA384_DIGEST_LENGTH 48 -#define SHA384_DIGEST_LEN SHA384_DIGEST_LENGTH -#define SHA512_DIGEST_LENGTH 64 -#define SHA512_DIGEST_LEN SHA512_DIGEST_LENGTH - -#ifndef EVP_MAX_MD_SIZE -#define EVP_MAX_MD_SIZE 64 -#endif - -#define EVP_DIGEST_LEN EVP_MAX_MD_SIZE - -typedef gcry_mpi_t bignum; - -/* missing gcrypt functions */ -int my_gcry_dec2bn(bignum *bn, const char *data); -char *my_gcry_bn2dec(bignum bn); - -#define bignum_new() gcry_mpi_new(0) -#define bignum_free(num) gcry_mpi_release(num) -#define bignum_set_word(bn,n) gcry_mpi_set_ui(bn,n) -#define bignum_bin2bn(bn,datalen,data) gcry_mpi_scan(data,GCRYMPI_FMT_USG,bn,datalen,NULL) -#define bignum_bn2dec(num) my_gcry_bn2dec(num) -#define bignum_dec2bn(num, data) my_gcry_dec2bn(data, num) -#define bignum_bn2hex(num,data) gcry_mpi_aprint(GCRYMPI_FMT_HEX,data,NULL,num) -#define bignum_hex2bn(num,datalen,data) gcry_mpi_scan(num,GCRYMPI_FMT_HEX,data,datalen,NULL) -#define bignum_rand(num,bits) gcry_mpi_randomize(num,bits,GCRY_STRONG_RANDOM),gcry_mpi_set_bit(num,bits-1),gcry_mpi_set_bit(num,0) -#define bignum_mod_exp(dest,generator,exp,modulo) gcry_mpi_powm(dest,generator,exp,modulo) -#define bignum_num_bits(num) gcry_mpi_get_nbits(num) -#define bignum_num_bytes(num) ((gcry_mpi_get_nbits(num)+7)/8) -#define bignum_is_bit_set(num,bit) gcry_mpi_test_bit(num,bit) -#define bignum_bn2bin(num,datalen,data) gcry_mpi_print(GCRYMPI_FMT_USG,data,datalen,NULL,num) -#define bignum_cmp(num1,num2) gcry_mpi_cmp(num1,num2) - -#endif /* HAVE_LIBGCRYPT */ - -struct ssh_cipher_struct *ssh_get_ciphertab(void); - -#endif /* LIBGCRYPT_H_ */ diff --git a/libssh/include/libssh/libssh.h b/libssh/include/libssh/libssh.h deleted file mode 100644 index ba4f5f44..00000000 --- a/libssh/include/libssh/libssh.h +++ /dev/null @@ -1,674 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _LIBSSH_H -#define _LIBSSH_H - -#if defined _WIN32 || defined __CYGWIN__ - #ifdef LIBSSH_STATIC - #define LIBSSH_API - #else - #ifdef LIBSSH_EXPORTS - #ifdef __GNUC__ - #define LIBSSH_API __attribute__((dllexport)) - #else - #define LIBSSH_API __declspec(dllexport) - #endif - #else - #ifdef __GNUC__ - #define LIBSSH_API __attribute__((dllimport)) - #else - #define LIBSSH_API __declspec(dllimport) - #endif - #endif - #endif -#else - #if __GNUC__ >= 4 && !defined(__OS2__) - #define LIBSSH_API __attribute__((visibility("default"))) - #else - #define LIBSSH_API - #endif -#endif - -#ifdef _MSC_VER - /* Visual Studio hasn't inttypes.h so it doesn't know uint32_t */ - typedef int int32_t; - typedef unsigned int uint32_t; - typedef unsigned short uint16_t; - typedef unsigned char uint8_t; - typedef unsigned long long uint64_t; - typedef int mode_t; -#else /* _MSC_VER */ - #include - #include -#endif /* _MSC_VER */ - -#ifdef _WIN32 - #include -#else /* _WIN32 */ - #include /* for fd_set * */ - #include -#endif /* _WIN32 */ - -#define SSH_STRINGIFY(s) SSH_TOSTRING(s) -#define SSH_TOSTRING(s) #s - -/* libssh version macros */ -#define SSH_VERSION_INT(a, b, c) ((a) << 16 | (b) << 8 | (c)) -#define SSH_VERSION_DOT(a, b, c) a ##.## b ##.## c -#define SSH_VERSION(a, b, c) SSH_VERSION_DOT(a, b, c) - -/* libssh version */ -#define LIBSSH_VERSION_MAJOR 0 -#define LIBSSH_VERSION_MINOR 6 -#define LIBSSH_VERSION_MICRO 0 - -#define LIBSSH_VERSION_INT SSH_VERSION_INT(LIBSSH_VERSION_MAJOR, \ - LIBSSH_VERSION_MINOR, \ - LIBSSH_VERSION_MICRO) -#define LIBSSH_VERSION SSH_VERSION(LIBSSH_VERSION_MAJOR, \ - LIBSSH_VERSION_MINOR, \ - LIBSSH_VERSION_MICRO) - -/* GCC have printf type attribute check. */ -#ifdef __GNUC__ -#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) -#else -#define PRINTF_ATTRIBUTE(a,b) -#endif /* __GNUC__ */ - -#ifdef __GNUC__ -#define SSH_DEPRECATED __attribute__ ((deprecated)) -#else -#define SSH_DEPRECATED -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -struct ssh_counter_struct { - uint64_t in_bytes; - uint64_t out_bytes; - uint64_t in_packets; - uint64_t out_packets; -}; -typedef struct ssh_counter_struct *ssh_counter; - -typedef struct ssh_agent_struct* ssh_agent; -typedef struct ssh_buffer_struct* ssh_buffer; -typedef struct ssh_channel_struct* ssh_channel; -typedef struct ssh_message_struct* ssh_message; -typedef struct ssh_pcap_file_struct* ssh_pcap_file; -typedef struct ssh_key_struct* ssh_key; -typedef struct ssh_scp_struct* ssh_scp; -typedef struct ssh_session_struct* ssh_session; -typedef struct ssh_string_struct* ssh_string; -typedef struct ssh_event_struct* ssh_event; -typedef void* ssh_gssapi_creds; - -/* Socket type */ -#ifdef _WIN32 -#ifndef socket_t -typedef SOCKET socket_t; -#endif /* socket_t */ -#else /* _WIN32 */ -#ifndef socket_t -typedef int socket_t; -#endif -#endif /* _WIN32 */ - -#define SSH_INVALID_SOCKET ((socket_t) -1) - -/* the offsets of methods */ -enum ssh_kex_types_e { - SSH_KEX=0, - SSH_HOSTKEYS, - SSH_CRYPT_C_S, - SSH_CRYPT_S_C, - SSH_MAC_C_S, - SSH_MAC_S_C, - SSH_COMP_C_S, - SSH_COMP_S_C, - SSH_LANG_C_S, - SSH_LANG_S_C -}; - -#define SSH_CRYPT 2 -#define SSH_MAC 3 -#define SSH_COMP 4 -#define SSH_LANG 5 - -enum ssh_auth_e { - SSH_AUTH_SUCCESS=0, - SSH_AUTH_DENIED, - SSH_AUTH_PARTIAL, - SSH_AUTH_INFO, - SSH_AUTH_AGAIN, - SSH_AUTH_ERROR=-1 -}; - -/* auth flags */ -#define SSH_AUTH_METHOD_UNKNOWN 0 -#define SSH_AUTH_METHOD_NONE 0x0001 -#define SSH_AUTH_METHOD_PASSWORD 0x0002 -#define SSH_AUTH_METHOD_PUBLICKEY 0x0004 -#define SSH_AUTH_METHOD_HOSTBASED 0x0008 -#define SSH_AUTH_METHOD_INTERACTIVE 0x0010 -#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020 - -/* messages */ -enum ssh_requests_e { - SSH_REQUEST_AUTH=1, - SSH_REQUEST_CHANNEL_OPEN, - SSH_REQUEST_CHANNEL, - SSH_REQUEST_SERVICE, - SSH_REQUEST_GLOBAL -}; - -enum ssh_channel_type_e { - SSH_CHANNEL_UNKNOWN=0, - SSH_CHANNEL_SESSION, - SSH_CHANNEL_DIRECT_TCPIP, - SSH_CHANNEL_FORWARDED_TCPIP, - SSH_CHANNEL_X11 -}; - -enum ssh_channel_requests_e { - SSH_CHANNEL_REQUEST_UNKNOWN=0, - SSH_CHANNEL_REQUEST_PTY, - SSH_CHANNEL_REQUEST_EXEC, - SSH_CHANNEL_REQUEST_SHELL, - SSH_CHANNEL_REQUEST_ENV, - SSH_CHANNEL_REQUEST_SUBSYSTEM, - SSH_CHANNEL_REQUEST_WINDOW_CHANGE, - SSH_CHANNEL_REQUEST_X11 -}; - -enum ssh_global_requests_e { - SSH_GLOBAL_REQUEST_UNKNOWN=0, - SSH_GLOBAL_REQUEST_TCPIP_FORWARD, - SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD, -}; - -enum ssh_publickey_state_e { - SSH_PUBLICKEY_STATE_ERROR=-1, - SSH_PUBLICKEY_STATE_NONE=0, - SSH_PUBLICKEY_STATE_VALID=1, - SSH_PUBLICKEY_STATE_WRONG=2 -}; - -/* Status flags */ -/** Socket is closed */ -#define SSH_CLOSED 0x01 -/** Reading to socket won't block */ -#define SSH_READ_PENDING 0x02 -/** Session was closed due to an error */ -#define SSH_CLOSED_ERROR 0x04 -/** Output buffer not empty */ -#define SSH_WRITE_PENDING 0x08 - -enum ssh_server_known_e { - SSH_SERVER_ERROR=-1, - SSH_SERVER_NOT_KNOWN=0, - SSH_SERVER_KNOWN_OK, - SSH_SERVER_KNOWN_CHANGED, - SSH_SERVER_FOUND_OTHER, - SSH_SERVER_FILE_NOT_FOUND -}; - -#ifndef MD5_DIGEST_LEN - #define MD5_DIGEST_LEN 16 -#endif -/* errors */ - -enum ssh_error_types_e { - SSH_NO_ERROR=0, - SSH_REQUEST_DENIED, - SSH_FATAL, - SSH_EINTR -}; - -/* some types for keys */ -enum ssh_keytypes_e{ - SSH_KEYTYPE_UNKNOWN=0, - SSH_KEYTYPE_DSS=1, - SSH_KEYTYPE_RSA, - SSH_KEYTYPE_RSA1, - SSH_KEYTYPE_ECDSA, - SSH_KEYTYPE_ED25519 -}; - -enum ssh_keycmp_e { - SSH_KEY_CMP_PUBLIC = 0, - SSH_KEY_CMP_PRIVATE -}; - -/* Error return codes */ -#define SSH_OK 0 /* No error */ -#define SSH_ERROR -1 /* Error of some kind */ -#define SSH_AGAIN -2 /* The nonblocking call must be repeated */ -#define SSH_EOF -127 /* We have already a eof */ - -/** - * @addtogroup libssh_log - * - * @{ - */ - -enum { - /** No logging at all - */ - SSH_LOG_NOLOG=0, - /** Only warnings - */ - SSH_LOG_WARNING, - /** High level protocol information - */ - SSH_LOG_PROTOCOL, - /** Lower level protocol infomations, packet level - */ - SSH_LOG_PACKET, - /** Every function path - */ - SSH_LOG_FUNCTIONS -}; -/** @} */ -#define SSH_LOG_RARE SSH_LOG_WARNING - -/** - * @name Logging levels - * - * @brief Debug levels for logging. - * @{ - */ - -/** No logging at all */ -#define SSH_LOG_NONE 0 -/** Show only warnings */ -#define SSH_LOG_WARN 1 -/** Get some information what's going on */ -#define SSH_LOG_INFO 2 -/** Get detailed debuging information **/ -#define SSH_LOG_DEBUG 3 -/** Get trace output, packet information, ... */ -#define SSH_LOG_TRACE 4 - -/** @} */ - -enum ssh_options_e { - SSH_OPTIONS_HOST, - SSH_OPTIONS_PORT, - SSH_OPTIONS_PORT_STR, - SSH_OPTIONS_FD, - SSH_OPTIONS_USER, - SSH_OPTIONS_SSH_DIR, - SSH_OPTIONS_IDENTITY, - SSH_OPTIONS_ADD_IDENTITY, - SSH_OPTIONS_KNOWNHOSTS, - SSH_OPTIONS_TIMEOUT, - SSH_OPTIONS_TIMEOUT_USEC, - SSH_OPTIONS_SSH1, - SSH_OPTIONS_SSH2, - SSH_OPTIONS_LOG_VERBOSITY, - SSH_OPTIONS_LOG_VERBOSITY_STR, - SSH_OPTIONS_CIPHERS_C_S, - SSH_OPTIONS_CIPHERS_S_C, - SSH_OPTIONS_COMPRESSION_C_S, - SSH_OPTIONS_COMPRESSION_S_C, - SSH_OPTIONS_PROXYCOMMAND, - SSH_OPTIONS_BINDADDR, - SSH_OPTIONS_STRICTHOSTKEYCHECK, - SSH_OPTIONS_COMPRESSION, - SSH_OPTIONS_COMPRESSION_LEVEL, - SSH_OPTIONS_KEY_EXCHANGE, - SSH_OPTIONS_HOSTKEYS, - SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, - SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, - SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, - SSH_OPTIONS_HMAC_C_S, - SSH_OPTIONS_HMAC_S_C, -}; - -enum { - /** Code is going to write/create remote files */ - SSH_SCP_WRITE, - /** Code is going to read remote files */ - SSH_SCP_READ, - SSH_SCP_RECURSIVE=0x10 -}; - -enum ssh_scp_request_types { - /** A new directory is going to be pulled */ - SSH_SCP_REQUEST_NEWDIR=1, - /** A new file is going to be pulled */ - SSH_SCP_REQUEST_NEWFILE, - /** End of requests */ - SSH_SCP_REQUEST_EOF, - /** End of directory */ - SSH_SCP_REQUEST_ENDDIR, - /** Warning received */ - SSH_SCP_REQUEST_WARNING -}; - -LIBSSH_API int ssh_blocking_flush(ssh_session session, int timeout); -LIBSSH_API ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms); -LIBSSH_API int ssh_channel_change_pty_size(ssh_channel channel,int cols,int rows); -LIBSSH_API int ssh_channel_close(ssh_channel channel); -LIBSSH_API void ssh_channel_free(ssh_channel channel); -LIBSSH_API int ssh_channel_get_exit_status(ssh_channel channel); -LIBSSH_API ssh_session ssh_channel_get_session(ssh_channel channel); -LIBSSH_API int ssh_channel_is_closed(ssh_channel channel); -LIBSSH_API int ssh_channel_is_eof(ssh_channel channel); -LIBSSH_API int ssh_channel_is_open(ssh_channel channel); -LIBSSH_API ssh_channel ssh_channel_new(ssh_session session); -LIBSSH_API int ssh_channel_open_auth_agent(ssh_channel channel); -LIBSSH_API int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, - int remoteport, const char *sourcehost, int localport); -LIBSSH_API int ssh_channel_open_session(ssh_channel channel); -LIBSSH_API int ssh_channel_open_x11(ssh_channel channel, const char *orig_addr, int orig_port); -LIBSSH_API int ssh_channel_poll(ssh_channel channel, int is_stderr); -LIBSSH_API int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr); -LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); -LIBSSH_API int ssh_channel_read_timeout(ssh_channel channel, void *dest, uint32_t count, int is_stderr, int timeout_ms); -LIBSSH_API int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, - int is_stderr); -LIBSSH_API int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value); -LIBSSH_API int ssh_channel_request_exec(ssh_channel channel, const char *cmd); -LIBSSH_API int ssh_channel_request_pty(ssh_channel channel); -LIBSSH_API int ssh_channel_request_pty_size(ssh_channel channel, const char *term, - int cols, int rows); -LIBSSH_API int ssh_channel_request_shell(ssh_channel channel); -LIBSSH_API int ssh_channel_request_send_signal(ssh_channel channel, const char *signum); -LIBSSH_API int ssh_channel_request_sftp(ssh_channel channel); -LIBSSH_API int ssh_channel_request_subsystem(ssh_channel channel, const char *subsystem); -LIBSSH_API int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, - const char *cookie, int screen_number); -LIBSSH_API int ssh_channel_send_eof(ssh_channel channel); -LIBSSH_API int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct - timeval * timeout); -LIBSSH_API void ssh_channel_set_blocking(ssh_channel channel, int blocking); -LIBSSH_API void ssh_channel_set_counter(ssh_channel channel, - ssh_counter counter); -LIBSSH_API int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len); -LIBSSH_API uint32_t ssh_channel_window_size(ssh_channel channel); - -LIBSSH_API char *ssh_basename (const char *path); -LIBSSH_API void ssh_clean_pubkey_hash(unsigned char **hash); -LIBSSH_API int ssh_connect(ssh_session session); -LIBSSH_API const char *ssh_copyright(void); -LIBSSH_API void ssh_disconnect(ssh_session session); -LIBSSH_API char *ssh_dirname (const char *path); -LIBSSH_API int ssh_finalize(void); - -/* REVERSE PORT FORWARDING */ -LIBSSH_API ssh_channel ssh_channel_accept_forward(ssh_session session, - int timeout_ms, - int *destination_port); -LIBSSH_API int ssh_channel_cancel_forward(ssh_session session, - const char *address, - int port); -LIBSSH_API int ssh_channel_listen_forward(ssh_session session, - const char *address, - int port, - int *bound_port); - -LIBSSH_API void ssh_free(ssh_session session); -LIBSSH_API const char *ssh_get_disconnect_message(ssh_session session); -LIBSSH_API const char *ssh_get_error(void *error); -LIBSSH_API int ssh_get_error_code(void *error); -LIBSSH_API socket_t ssh_get_fd(ssh_session session); -LIBSSH_API char *ssh_get_hexa(const unsigned char *what, size_t len); -LIBSSH_API char *ssh_get_issue_banner(ssh_session session); -LIBSSH_API int ssh_get_openssh_version(ssh_session session); - -LIBSSH_API int ssh_get_publickey(ssh_session session, ssh_key *key); - -enum ssh_publickey_hash_type { - SSH_PUBLICKEY_HASH_SHA1, - SSH_PUBLICKEY_HASH_MD5 -}; -LIBSSH_API int ssh_get_publickey_hash(const ssh_key key, - enum ssh_publickey_hash_type type, - unsigned char **hash, - size_t *hlen); - -/* DEPRECATED FUNCTIONS */ -SSH_DEPRECATED LIBSSH_API int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash); -SSH_DEPRECATED LIBSSH_API ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms); -SSH_DEPRECATED LIBSSH_API int ssh_forward_cancel(ssh_session session, const char *address, int port); -SSH_DEPRECATED LIBSSH_API int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port); - - -LIBSSH_API int ssh_get_random(void *where,int len,int strong); -LIBSSH_API int ssh_get_version(ssh_session session); -LIBSSH_API int ssh_get_status(ssh_session session); -LIBSSH_API int ssh_get_poll_flags(ssh_session session); -LIBSSH_API int ssh_init(void); -LIBSSH_API int ssh_is_blocking(ssh_session session); -LIBSSH_API int ssh_is_connected(ssh_session session); -LIBSSH_API int ssh_is_server_known(ssh_session session); - -/* LOGGING */ -LIBSSH_API int ssh_set_log_level(int level); -LIBSSH_API int ssh_get_log_level(void); -LIBSSH_API void *ssh_get_log_userdata(void); -LIBSSH_API int ssh_set_log_userdata(void *data); -LIBSSH_API void _ssh_log(int verbosity, - const char *function, - const char *format, ...) PRINTF_ATTRIBUTE(3, 4); - -/* legacy */ -SSH_DEPRECATED LIBSSH_API void ssh_log(ssh_session session, - int prioriry, - const char *format, ...) PRINTF_ATTRIBUTE(3, 4); - -LIBSSH_API ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg); -LIBSSH_API int ssh_message_channel_request_reply_success(ssh_message msg); -LIBSSH_API void ssh_message_free(ssh_message msg); -LIBSSH_API ssh_message ssh_message_get(ssh_session session); -LIBSSH_API int ssh_message_subtype(ssh_message msg); -LIBSSH_API int ssh_message_type(ssh_message msg); -LIBSSH_API int ssh_mkdir (const char *pathname, mode_t mode); -LIBSSH_API ssh_session ssh_new(void); - -LIBSSH_API int ssh_options_copy(ssh_session src, ssh_session *dest); -LIBSSH_API int ssh_options_getopt(ssh_session session, int *argcptr, char **argv); -LIBSSH_API int ssh_options_parse_config(ssh_session session, const char *filename); -LIBSSH_API int ssh_options_set(ssh_session session, enum ssh_options_e type, - const void *value); -LIBSSH_API int ssh_options_get(ssh_session session, enum ssh_options_e type, - char **value); -LIBSSH_API int ssh_options_get_port(ssh_session session, unsigned int * port_target); -LIBSSH_API int ssh_pcap_file_close(ssh_pcap_file pcap); -LIBSSH_API void ssh_pcap_file_free(ssh_pcap_file pcap); -LIBSSH_API ssh_pcap_file ssh_pcap_file_new(void); -LIBSSH_API int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename); - -/** - * @brief SSH authentication callback. - * - * @param prompt Prompt to be displayed. - * @param buf Buffer to save the password. You should null-terminate it. - * @param len Length of the buffer. - * @param echo Enable or disable the echo of what you type. - * @param verify Should the password be verified? - * @param userdata Userdata to be passed to the callback function. Useful - * for GUI applications. - * - * @return 0 on success, < 0 on error. - */ -typedef int (*ssh_auth_callback) (const char *prompt, char *buf, size_t len, - int echo, int verify, void *userdata); - -LIBSSH_API ssh_key ssh_key_new(void); -LIBSSH_API void ssh_key_free (ssh_key key); -LIBSSH_API enum ssh_keytypes_e ssh_key_type(const ssh_key key); -LIBSSH_API const char *ssh_key_type_to_char(enum ssh_keytypes_e type); -LIBSSH_API enum ssh_keytypes_e ssh_key_type_from_name(const char *name); -LIBSSH_API int ssh_key_is_public(const ssh_key k); -LIBSSH_API int ssh_key_is_private(const ssh_key k); -LIBSSH_API int ssh_key_cmp(const ssh_key k1, - const ssh_key k2, - enum ssh_keycmp_e what); - -LIBSSH_API int ssh_pki_generate(enum ssh_keytypes_e type, int parameter, - ssh_key *pkey); -LIBSSH_API int ssh_pki_import_privkey_base64(const char *b64_key, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data, - ssh_key *pkey); -LIBSSH_API int ssh_pki_import_privkey_file(const char *filename, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data, - ssh_key *pkey); -LIBSSH_API int ssh_pki_export_privkey_file(const ssh_key privkey, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data, - const char *filename); - -LIBSSH_API int ssh_pki_import_pubkey_base64(const char *b64_key, - enum ssh_keytypes_e type, - ssh_key *pkey); -LIBSSH_API int ssh_pki_import_pubkey_file(const char *filename, - ssh_key *pkey); - -LIBSSH_API int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey, - ssh_key *pkey); -LIBSSH_API int ssh_pki_export_pubkey_base64(const ssh_key key, - char **b64_key); -LIBSSH_API int ssh_pki_export_pubkey_file(const ssh_key key, - const char *filename); - -LIBSSH_API const char *ssh_pki_key_ecdsa_name(const ssh_key key); - -LIBSSH_API void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len); -LIBSSH_API int ssh_send_ignore (ssh_session session, const char *data); -LIBSSH_API int ssh_send_debug (ssh_session session, const char *message, int always_display); -LIBSSH_API void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds); -LIBSSH_API int ssh_scp_accept_request(ssh_scp scp); -LIBSSH_API int ssh_scp_close(ssh_scp scp); -LIBSSH_API int ssh_scp_deny_request(ssh_scp scp, const char *reason); -LIBSSH_API void ssh_scp_free(ssh_scp scp); -LIBSSH_API int ssh_scp_init(ssh_scp scp); -LIBSSH_API int ssh_scp_leave_directory(ssh_scp scp); -LIBSSH_API ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location); -LIBSSH_API int ssh_scp_pull_request(ssh_scp scp); -LIBSSH_API int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode); -LIBSSH_API int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int perms); -LIBSSH_API int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int perms); -LIBSSH_API int ssh_scp_read(ssh_scp scp, void *buffer, size_t size); -LIBSSH_API const char *ssh_scp_request_get_filename(ssh_scp scp); -LIBSSH_API int ssh_scp_request_get_permissions(ssh_scp scp); -LIBSSH_API size_t ssh_scp_request_get_size(ssh_scp scp); -LIBSSH_API uint64_t ssh_scp_request_get_size64(ssh_scp scp); -LIBSSH_API const char *ssh_scp_request_get_warning(ssh_scp scp); -LIBSSH_API int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len); -LIBSSH_API int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, - fd_set *readfds, struct timeval *timeout); -LIBSSH_API int ssh_service_request(ssh_session session, const char *service); -LIBSSH_API int ssh_set_agent_channel(ssh_session session, ssh_channel channel); -LIBSSH_API void ssh_set_blocking(ssh_session session, int blocking); -LIBSSH_API void ssh_set_counters(ssh_session session, ssh_counter scounter, - ssh_counter rcounter); -LIBSSH_API void ssh_set_fd_except(ssh_session session); -LIBSSH_API void ssh_set_fd_toread(ssh_session session); -LIBSSH_API void ssh_set_fd_towrite(ssh_session session); -LIBSSH_API void ssh_silent_disconnect(ssh_session session); -LIBSSH_API int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile); - -/* USERAUTH */ -LIBSSH_API int ssh_userauth_none(ssh_session session, const char *username); -LIBSSH_API int ssh_userauth_list(ssh_session session, const char *username); -LIBSSH_API int ssh_userauth_try_publickey(ssh_session session, - const char *username, - const ssh_key pubkey); -LIBSSH_API int ssh_userauth_publickey(ssh_session session, - const char *username, - const ssh_key privkey); -#ifndef _WIN32 -LIBSSH_API int ssh_userauth_agent(ssh_session session, - const char *username); -#endif -LIBSSH_API int ssh_userauth_publickey_auto(ssh_session session, - const char *username, - const char *passphrase); -LIBSSH_API int ssh_userauth_password(ssh_session session, - const char *username, - const char *password); - -LIBSSH_API int ssh_userauth_kbdint(ssh_session session, const char *user, const char *submethods); -LIBSSH_API const char *ssh_userauth_kbdint_getinstruction(ssh_session session); -LIBSSH_API const char *ssh_userauth_kbdint_getname(ssh_session session); -LIBSSH_API int ssh_userauth_kbdint_getnprompts(ssh_session session); -LIBSSH_API const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, char *echo); -LIBSSH_API int ssh_userauth_kbdint_getnanswers(ssh_session session); -LIBSSH_API const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i); -LIBSSH_API int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i, - const char *answer); -LIBSSH_API int ssh_userauth_gssapi(ssh_session session); -LIBSSH_API const char *ssh_version(int req_version); -LIBSSH_API int ssh_write_knownhost(ssh_session session); - -LIBSSH_API void ssh_string_burn(ssh_string str); -LIBSSH_API ssh_string ssh_string_copy(ssh_string str); -LIBSSH_API void *ssh_string_data(ssh_string str); -LIBSSH_API int ssh_string_fill(ssh_string str, const void *data, size_t len); -LIBSSH_API void ssh_string_free(ssh_string str); -LIBSSH_API ssh_string ssh_string_from_char(const char *what); -LIBSSH_API size_t ssh_string_len(ssh_string str); -LIBSSH_API ssh_string ssh_string_new(size_t size); -LIBSSH_API const char *ssh_string_get_char(ssh_string str); -LIBSSH_API char *ssh_string_to_char(ssh_string str); -LIBSSH_API void ssh_string_free_char(char *s); - -LIBSSH_API int ssh_getpass(const char *prompt, char *buf, size_t len, int echo, - int verify); - - -typedef int (*ssh_event_callback)(socket_t fd, int revents, void *userdata); - -LIBSSH_API ssh_event ssh_event_new(void); -LIBSSH_API int ssh_event_add_fd(ssh_event event, socket_t fd, short events, - ssh_event_callback cb, void *userdata); -LIBSSH_API int ssh_event_add_session(ssh_event event, ssh_session session); -LIBSSH_API int ssh_event_dopoll(ssh_event event, int timeout); -LIBSSH_API int ssh_event_remove_fd(ssh_event event, socket_t fd); -LIBSSH_API int ssh_event_remove_session(ssh_event event, ssh_session session); -LIBSSH_API void ssh_event_free(ssh_event event); -LIBSSH_API const char* ssh_get_clientbanner(ssh_session session); -LIBSSH_API const char* ssh_get_serverbanner(ssh_session session); -LIBSSH_API const char* ssh_get_cipher_in(ssh_session session); -LIBSSH_API const char* ssh_get_cipher_out(ssh_session session); -LIBSSH_API const char* ssh_get_hmac_in(ssh_session session); -LIBSSH_API const char* ssh_get_hmac_out(ssh_session session); - -#ifndef LIBSSH_LEGACY_0_4 -#include "libssh/legacy.h" -#endif - -#ifdef __cplusplus -} -#endif -#endif /* _LIBSSH_H */ -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/include/libssh/libsshpp.hpp b/libssh/include/libssh/libsshpp.hpp deleted file mode 100644 index af08a914..00000000 --- a/libssh/include/libssh/libsshpp.hpp +++ /dev/null @@ -1,614 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBSSHPP_HPP_ -#define LIBSSHPP_HPP_ - -/** - * @defgroup ssh_cpp The libssh C++ wrapper - * - * The C++ bindings for libssh are completely embedded in a single .hpp file, and - * this for two reasons: - * - C++ is hard to keep binary compatible, C is easy. We try to keep libssh C version - * as much as possible binary compatible between releases, while this would be hard for - * C++. If you compile your program with these headers, you will only link to the C version - * of libssh which will be kept ABI compatible. No need to recompile your C++ program - * each time a new binary-compatible version of libssh is out - * - Most of the functions in this file are really short and are probably worth the "inline" - * linking mode, which the compiler can decide to do in some case. There would be nearly no - * performance penalty of using the wrapper rather than native calls. - * - * Please visit the documentation of ssh::Session and ssh::Channel - * @see ssh::Session - * @see ssh::Channel - * - * If you wish not to use C++ exceptions, please define SSH_NO_CPP_EXCEPTIONS: - * @code - * #define SSH_NO_CPP_EXCEPTIONS - * #include - * @endcode - * All functions will then return SSH_ERROR in case of error. - * @{ - */ - -/* do not use deprecated functions */ -#define LIBSSH_LEGACY_0_4 - -#include -#include -#include -#include -#include -#include - -namespace ssh { - -class Channel; -/** Some people do not like C++ exceptions. With this define, we give - * the choice to use or not exceptions. - * @brief if defined, disable C++ exceptions for libssh c++ wrapper - */ -#ifndef SSH_NO_CPP_EXCEPTIONS - -/** @brief This class describes a SSH Exception object. This object can be thrown - * by several SSH functions that interact with the network, and may fail because of - * socket, protocol or memory errors. - */ -class SshException{ -public: - SshException(ssh_session csession){ - code=ssh_get_error_code(csession); - description=std::string(ssh_get_error(csession)); - } - SshException(const SshException &e){ - code=e.code; - description=e.description; - } - /** @brief returns the Error code - * @returns SSH_FATAL Fatal error happened (not recoverable) - * @returns SSH_REQUEST_DENIED Request was denied by remote host - * @see ssh_get_error_code - */ - int getCode(){ - return code; - } - /** @brief returns the error message of the last exception - * @returns pointer to a c string containing the description of error - * @see ssh_get_error - */ - std::string getError(){ - return description; - } -private: - int code; - std::string description; -}; - -/** @internal - * @brief Macro to throw exception if there was an error - */ -#define ssh_throw(x) if((x)==SSH_ERROR) throw SshException(getCSession()) -#define ssh_throw_null(CSession,x) if((x)==NULL) throw SshException(CSession) -#define void_throwable void -#define return_throwable return - -#else - -/* No exception at all. All functions will return an error code instead - * of an exception - */ -#define ssh_throw(x) if((x)==SSH_ERROR) return SSH_ERROR -#define ssh_throw_null(CSession,x) if((x)==NULL) return NULL -#define void_throwable int -#define return_throwable return SSH_OK -#endif - -/** - * The ssh::Session class contains the state of a SSH connection. - */ -class Session { - friend class Channel; -public: - Session(){ - c_session=ssh_new(); - } - ~Session(){ - ssh_free(c_session); - c_session=NULL; - } - /** @brief sets an SSH session options - * @param type Type of option - * @param option cstring containing the value of option - * @throws SshException on error - * @see ssh_options_set - */ - void_throwable setOption(enum ssh_options_e type, const char *option){ - ssh_throw(ssh_options_set(c_session,type,option)); - return_throwable; - } - /** @brief sets an SSH session options - * @param type Type of option - * @param option long integer containing the value of option - * @throws SshException on error - * @see ssh_options_set - */ - void_throwable setOption(enum ssh_options_e type, long int option){ - ssh_throw(ssh_options_set(c_session,type,&option)); - return_throwable; - } - /** @brief sets an SSH session options - * @param type Type of option - * @param option void pointer containing the value of option - * @throws SshException on error - * @see ssh_options_set - */ - void_throwable setOption(enum ssh_options_e type, void *option){ - ssh_throw(ssh_options_set(c_session,type,option)); - return_throwable; - } - /** @brief connects to the remote host - * @throws SshException on error - * @see ssh_connect - */ - void_throwable connect(){ - int ret=ssh_connect(c_session); - ssh_throw(ret); - return_throwable; - } - /** @brief Authenticates automatically using public key - * @throws SshException on error - * @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED - * @see ssh_userauth_autopubkey - */ - int userauthPublickeyAuto(void){ - int ret=ssh_userauth_publickey_auto(c_session, NULL, NULL); - ssh_throw(ret); - return ret; - } - /** @brief Authenticates using the "none" method. Prefer using autopubkey if - * possible. - * @throws SshException on error - * @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED - * @see ssh_userauth_none - * @see Session::userauthAutoPubkey - */ - int userauthNone(){ - int ret=ssh_userauth_none(c_session,NULL); - ssh_throw(ret); - return ret; - } - /** @brief Authenticates using the password method. - * @param[in] password password to use for authentication - * @throws SshException on error - * @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED - * @see ssh_userauth_password - */ - int userauthPassword(const char *password){ - int ret=ssh_userauth_password(c_session,NULL,password); - ssh_throw(ret); - return ret; - } - /** @brief Try to authenticate using the publickey method. - * @param[in] pubkey public key to use for authentication - * @throws SshException on error - * @returns SSH_AUTH_SUCCESS if the pubkey is accepted, - * @returns SSH_AUTH_DENIED if the pubkey is denied - * @see ssh_userauth_try_pubkey - */ - int userauthTryPublickey(ssh_key pubkey){ - int ret=ssh_userauth_try_publickey(c_session, NULL, pubkey); - ssh_throw(ret); - return ret; - } - /** @brief Authenticates using the publickey method. - * @param[in] privkey private key to use for authentication - * @throws SshException on error - * @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED - * @see ssh_userauth_pubkey - */ - int userauthPublickey(ssh_key privkey){ - int ret=ssh_userauth_publickey(c_session, NULL, privkey); - ssh_throw(ret); - return ret; - } - int userauthPrivatekeyFile(const char *filename, - const char *passphrase); - /** @brief Returns the available authentication methods from the server - * @throws SshException on error - * @returns Bitfield of available methods. - * @see ssh_userauth_list - */ - int getAuthList(){ - int ret=ssh_userauth_list(c_session, NULL); - ssh_throw(ret); - return ret; - } - /** @brief Disconnects from the SSH server and closes connection - * @see ssh_disconnect - */ - void disconnect(){ - ssh_disconnect(c_session); - } - /** @brief Returns the disconnect message from the server, if any - * @returns pointer to the message, or NULL. Do not attempt to free - * the pointer. - */ - const char *getDisconnectMessage(){ - const char *msg=ssh_get_disconnect_message(c_session); - return msg; - } - /** @internal - * @brief gets error message - */ - const char *getError(){ - return ssh_get_error(c_session); - } - /** @internal - * @brief returns error code - */ - int getErrorCode(){ - return ssh_get_error_code(c_session); - } - /** @brief returns the file descriptor used for the communication - * @returns the file descriptor - * @warning if a proxycommand is used, this function will only return - * one of the two file descriptors being used - * @see ssh_get_fd - */ - socket_t getSocket(){ - return ssh_get_fd(c_session); - } - /** @brief gets the Issue banner from the ssh server - * @returns the issue banner. This is generally a MOTD from server - * @see ssh_get_issue_banner - */ - std::string getIssueBanner(){ - char *banner=ssh_get_issue_banner(c_session); - std::string ret= std::string(banner); - ::free(banner); - return ret; - } - /** @brief returns the OpenSSH version (server) if possible - * @returns openssh version code - * @see ssh_get_openssh_version - */ - int getOpensshVersion(){ - return ssh_get_openssh_version(c_session); - } - /** @brief returns the version of the SSH protocol being used - * @returns the SSH protocol version - * @see ssh_get_version - */ - int getVersion(){ - return ssh_get_version(c_session); - } - /** @brief verifies that the server is known - * @throws SshException on error - * @returns Integer value depending on the knowledge of the - * server key - * @see ssh_is_server_known - */ - int isServerKnown(){ - int ret=ssh_is_server_known(c_session); - ssh_throw(ret); - return ret; - } - void log(int priority, const char *format, ...){ - char buffer[1024]; - va_list va; - - va_start(va, format); - vsnprintf(buffer, sizeof(buffer), format, va); - va_end(va); - _ssh_log(priority, "libsshpp", "%s", buffer); - } - - /** @brief copies options from a session to another - * @throws SshException on error - * @see ssh_options_copy - */ - void_throwable optionsCopy(const Session &source){ - ssh_throw(ssh_options_copy(source.c_session,&c_session)); - return_throwable; - } - /** @brief parses a configuration file for options - * @throws SshException on error - * @param[in] file configuration file name - * @see ssh_options_parse_config - */ - void_throwable optionsParseConfig(const char *file){ - ssh_throw(ssh_options_parse_config(c_session,file)); - return_throwable; - } - /** @brief silently disconnect from remote host - * @see ssh_silent_disconnect - */ - void silentDisconnect(){ - ssh_silent_disconnect(c_session); - } - /** @brief Writes the known host file with current - * host key - * @throws SshException on error - * @see ssh_write_knownhost - */ - int writeKnownhost(){ - int ret = ssh_write_knownhost(c_session); - ssh_throw(ret); - return ret; - } - - /** @brief accept an incoming forward connection - * @param[in] timeout_ms timeout for waiting, in ms - * @returns new Channel pointer on the forward connection - * @returns NULL in case of error - * @warning you have to delete this pointer after use - * @see ssh_channel_forward_accept - * @see Session::listenForward - */ - inline Channel *acceptForward(int timeout_ms); - /* implemented outside the class due Channel references */ - - void_throwable cancelForward(const char *address, int port){ - int err=ssh_channel_cancel_forward(c_session, address, port); - ssh_throw(err); - return_throwable; - } - - void_throwable listenForward(const char *address, int port, - int &boundport){ - int err=ssh_channel_listen_forward(c_session, address, port, &boundport); - ssh_throw(err); - return_throwable; - } - -private: - ssh_session c_session; - ssh_session getCSession(){ - return c_session; - } - /* No copy constructor, no = operator */ - Session(const Session &); - Session& operator=(const Session &); -}; - -/** @brief the ssh::Channel class describes the state of an SSH - * channel. - * @see ssh_channel - */ -class Channel { - friend class Session; -public: - Channel(Session &session){ - channel=ssh_channel_new(session.getCSession()); - this->session=&session; - } - ~Channel(){ - ssh_channel_free(channel); - channel=NULL; - } - - /** @brief accept an incoming X11 connection - * @param[in] timeout_ms timeout for waiting, in ms - * @returns new Channel pointer on the X11 connection - * @returns NULL in case of error - * @warning you have to delete this pointer after use - * @see ssh_channel_accept_x11 - * @see Channel::requestX11 - */ - Channel *acceptX11(int timeout_ms){ - ssh_channel x11chan = ssh_channel_accept_x11(channel,timeout_ms); - ssh_throw_null(getCSession(),x11chan); - Channel *newchan = new Channel(getSession(),x11chan); - return newchan; - } - /** @brief change the size of a pseudoterminal - * @param[in] cols number of columns - * @param[in] rows number of rows - * @throws SshException on error - * @see ssh_channel_change_pty_size - */ - void_throwable changePtySize(int cols, int rows){ - int err=ssh_channel_change_pty_size(channel,cols,rows); - ssh_throw(err); - return_throwable; - } - - /** @brief closes a channel - * @throws SshException on error - * @see ssh_channel_close - */ - void_throwable close(){ - ssh_throw(ssh_channel_close(channel)); - return_throwable; - } - - int getExitStatus(){ - return ssh_channel_get_exit_status(channel); - } - Session &getSession(){ - return *session; - } - /** @brief returns true if channel is in closed state - * @see ssh_channel_is_closed - */ - bool isClosed(){ - return ssh_channel_is_closed(channel) != 0; - } - /** @brief returns true if channel is in EOF state - * @see ssh_channel_is_eof - */ - bool isEof(){ - return ssh_channel_is_eof(channel) != 0; - } - /** @brief returns true if channel is in open state - * @see ssh_channel_is_open - */ - bool isOpen(){ - return ssh_channel_is_open(channel) != 0; - } - int openForward(const char *remotehost, int remoteport, - const char *sourcehost=NULL, int localport=0){ - int err=ssh_channel_open_forward(channel,remotehost,remoteport, - sourcehost, localport); - ssh_throw(err); - return err; - } - /* TODO: completely remove this ? */ - void_throwable openSession(){ - int err=ssh_channel_open_session(channel); - ssh_throw(err); - return_throwable; - } - int poll(bool is_stderr=false){ - int err=ssh_channel_poll(channel,is_stderr); - ssh_throw(err); - return err; - } - int read(void *dest, size_t count, bool is_stderr){ - int err; - /* handle int overflow */ - if(count > 0x7fffffff) - count = 0x7fffffff; - err=ssh_channel_read_timeout(channel,dest,count,is_stderr,-1); - ssh_throw(err); - return err; - } - int read(void *dest, size_t count, int timeout){ - int err; - /* handle int overflow */ - if(count > 0x7fffffff) - count = 0x7fffffff; - err=ssh_channel_read_timeout(channel,dest,count,false,timeout); - ssh_throw(err); - return err; - } - int read(void *dest, size_t count, bool is_stderr=false, int timeout=-1){ - int err; - /* handle int overflow */ - if(count > 0x7fffffff) - count = 0x7fffffff; - err=ssh_channel_read_timeout(channel,dest,count,is_stderr,timeout); - ssh_throw(err); - return err; - } - int readNonblocking(void *dest, size_t count, bool is_stderr=false){ - int err; - /* handle int overflow */ - if(count > 0x7fffffff) - count = 0x7fffffff; - err=ssh_channel_read_nonblocking(channel,dest,count,is_stderr); - ssh_throw(err); - return err; - } - void_throwable requestEnv(const char *name, const char *value){ - int err=ssh_channel_request_env(channel,name,value); - ssh_throw(err); - return_throwable; - } - - void_throwable requestExec(const char *cmd){ - int err=ssh_channel_request_exec(channel,cmd); - ssh_throw(err); - return_throwable; - } - void_throwable requestPty(const char *term=NULL, int cols=0, int rows=0){ - int err; - if(term != NULL && cols != 0 && rows != 0) - err=ssh_channel_request_pty_size(channel,term,cols,rows); - else - err=ssh_channel_request_pty(channel); - ssh_throw(err); - return_throwable; - } - - void_throwable requestShell(){ - int err=ssh_channel_request_shell(channel); - ssh_throw(err); - return_throwable; - } - void_throwable requestSendSignal(const char *signum){ - int err=ssh_channel_request_send_signal(channel, signum); - ssh_throw(err); - return_throwable; - } - void_throwable requestSubsystem(const char *subsystem){ - int err=ssh_channel_request_subsystem(channel,subsystem); - ssh_throw(err); - return_throwable; - } - int requestX11(bool single_connection, - const char *protocol, const char *cookie, int screen_number){ - int err=ssh_channel_request_x11(channel,single_connection, - protocol, cookie, screen_number); - ssh_throw(err); - return err; - } - void_throwable sendEof(){ - int err=ssh_channel_send_eof(channel); - ssh_throw(err); - return_throwable; - } - /** @brief Writes on a channel - * @param data data to write. - * @param len number of bytes to write. - * @param is_stderr write should be done on the stderr channel (server only) - * @returns number of bytes written - * @throws SshException in case of error - * @see channel_write - * @see channel_write_stderr - */ - int write(const void *data, size_t len, bool is_stderr=false){ - int ret; - if(is_stderr){ - ret=ssh_channel_write_stderr(channel,data,len); - } else { - ret=ssh_channel_write(channel,data,len); - } - ssh_throw(ret); - return ret; - } -private: - ssh_session getCSession(){ - return session->getCSession(); - } - Channel (Session &session, ssh_channel c_channel){ - this->channel=c_channel; - this->session=&session; - } - Session *session; - ssh_channel channel; - /* No copy and no = operator */ - Channel(const Channel &); - Channel &operator=(const Channel &); -}; - - -inline Channel *Session::acceptForward(int timeout_ms){ - ssh_channel forward = - ssh_channel_accept_forward(c_session, timeout_ms, NULL); - ssh_throw_null(c_session,forward); - Channel *newchan = new Channel(*this,forward); - return newchan; - } - -} // namespace ssh - -/** @} */ -#endif /* LIBSSHPP_HPP_ */ diff --git a/libssh/include/libssh/messages.h b/libssh/include/libssh/messages.h deleted file mode 100644 index e766d08b..00000000 --- a/libssh/include/libssh/messages.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MESSAGES_H_ -#define MESSAGES_H_ - -#include "config.h" - -struct ssh_auth_request { - char *username; - int method; - char *password; - struct ssh_key_struct *pubkey; - char signature_state; - char kbdint_response; -}; - -struct ssh_channel_request_open { - int type; - uint32_t sender; - uint32_t window; - uint32_t packet_size; - char *originator; - uint16_t originator_port; - char *destination; - uint16_t destination_port; -}; - -struct ssh_service_request { - char *service; -}; - -struct ssh_global_request { - int type; - uint8_t want_reply; - char *bind_address; - uint16_t bind_port; -}; - -struct ssh_channel_request { - int type; - ssh_channel channel; - uint8_t want_reply; - /* pty-req type specifics */ - char *TERM; - uint32_t width; - uint32_t height; - uint32_t pxwidth; - uint32_t pxheight; - ssh_string modes; - - /* env type request */ - char *var_name; - char *var_value; - /* exec type request */ - char *command; - /* subsystem */ - char *subsystem; - - /* X11 */ - uint8_t x11_single_connection; - const char *x11_auth_protocol; - const char *x11_auth_cookie; - uint32_t x11_screen_number; -}; - -struct ssh_message_struct { - ssh_session session; - int type; - struct ssh_auth_request auth_request; - struct ssh_channel_request_open channel_request_open; - struct ssh_channel_request channel_request; - struct ssh_service_request service_request; - struct ssh_global_request global_request; -}; - -SSH_PACKET_CALLBACK(ssh_packet_channel_open); -SSH_PACKET_CALLBACK(ssh_packet_global_request); - -#ifdef WITH_SERVER -SSH_PACKET_CALLBACK(ssh_packet_service_request); -SSH_PACKET_CALLBACK(ssh_packet_userauth_request); -#endif /* WITH_SERVER */ - -int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet, - const char *request, uint8_t want_reply); -void ssh_message_queue(ssh_session session, ssh_message message); -ssh_message ssh_message_pop_head(ssh_session session); -int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_channel chan); - -#endif /* MESSAGES_H_ */ diff --git a/libssh/include/libssh/misc.h b/libssh/include/libssh/misc.h deleted file mode 100644 index 1e69b069..00000000 --- a/libssh/include/libssh/misc.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef MISC_H_ -#define MISC_H_ - -/* in misc.c */ -/* gets the user home dir. */ -char *ssh_get_user_home_dir(void); -char *ssh_get_local_username(void); -int ssh_file_readaccess_ok(const char *file); - -char *ssh_path_expand_tilde(const char *d); -char *ssh_path_expand_escape(ssh_session session, const char *s); -int ssh_analyze_banner(ssh_session session, int server, int *ssh1, int *ssh2); -int ssh_is_ipaddr_v4(const char *str); -int ssh_is_ipaddr(const char *str); - -#ifndef HAVE_NTOHLL -/* macro for byte ordering */ -uint64_t ntohll(uint64_t); -#endif - -#ifndef HAVE_HTONLL -#define htonll(x) ntohll((x)) -#endif - -/* list processing */ - -struct ssh_list { - struct ssh_iterator *root; - struct ssh_iterator *end; -}; - -struct ssh_iterator { - struct ssh_iterator *next; - const void *data; -}; - -struct ssh_timestamp { - long seconds; - long useconds; -}; - -struct ssh_list *ssh_list_new(void); -void ssh_list_free(struct ssh_list *list); -struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list); -struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value); -int ssh_list_append(struct ssh_list *list, const void *data); -int ssh_list_prepend(struct ssh_list *list, const void *data); -void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator); -char *ssh_lowercase(const char* str); -char *ssh_hostport(const char *host, int port); - -const void *_ssh_list_pop_head(struct ssh_list *list); - -#define ssh_iterator_value(type, iterator)\ - ((type)((iterator)->data)) - -/** @brief fetch the head element of a list and remove it from list - * @param type type of the element to return - * @param list the ssh_list to use - * @return the first element of the list, or NULL if the list is empty - */ -#define ssh_list_pop_head(type, ssh_list)\ - ((type)_ssh_list_pop_head(ssh_list)) - -int ssh_make_milliseconds(long sec, long usec); -void ssh_timestamp_init(struct ssh_timestamp *ts); -int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout); -int ssh_timeout_update(struct ssh_timestamp *ts, int timeout); - -int ssh_match_group(const char *group, const char *object); - -#endif /* MISC_H_ */ diff --git a/libssh/include/libssh/options.h b/libssh/include/libssh/options.h deleted file mode 100644 index 4078ad08..00000000 --- a/libssh/include/libssh/options.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2011 Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _OPTIONS_H -#define _OPTIONS_H - -int ssh_config_parse_file(ssh_session session, const char *filename); -int ssh_options_set_algo(ssh_session session, int algo, const char *list); -int ssh_options_apply(ssh_session session); - -#endif /* _OPTIONS_H */ diff --git a/libssh/include/libssh/packet.h b/libssh/include/libssh/packet.h deleted file mode 100644 index d8ef35bb..00000000 --- a/libssh/include/libssh/packet.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PACKET_H_ -#define PACKET_H_ - -#include "libssh/wrapper.h" - -struct ssh_socket_struct; - -/* this structure should go someday */ -typedef struct packet_struct { - int valid; - uint32_t len; - uint8_t type; -} PACKET; - -/** different state of packet reading. */ -enum ssh_packet_state_e { - /** Packet not initialized, must read the size of packet */ - PACKET_STATE_INIT, - /** Size was read, waiting for the rest of data */ - PACKET_STATE_SIZEREAD, - /** Full packet was read and callbacks are being called. Future packets - * should wait for the end of the callback. */ - PACKET_STATE_PROCESSING -}; - -int packet_send(ssh_session session); - -#ifdef WITH_SSH1 -int packet_send1(ssh_session session) ; -void ssh_packet_set_default_callbacks1(ssh_session session); - -SSH_PACKET_CALLBACK(ssh_packet_disconnect1); -SSH_PACKET_CALLBACK(ssh_packet_smsg_success1); -SSH_PACKET_CALLBACK(ssh_packet_smsg_failure1); -int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user); - -#endif - -SSH_PACKET_CALLBACK(ssh_packet_unimplemented); -SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback); -SSH_PACKET_CALLBACK(ssh_packet_ignore_callback); -SSH_PACKET_CALLBACK(ssh_packet_dh_reply); -SSH_PACKET_CALLBACK(ssh_packet_newkeys); -SSH_PACKET_CALLBACK(ssh_packet_service_accept); - -#ifdef WITH_SERVER -SSH_PACKET_CALLBACK(ssh_packet_kexdh_init); -#endif - -int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum); -int ssh_packet_parse_type(ssh_session session); -//int packet_flush(ssh_session session, int enforce_blocking); - -int ssh_packet_socket_callback(const void *data, size_t len, void *user); -void ssh_packet_register_socket_callback(ssh_session session, struct ssh_socket_struct *s); -void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks); -void ssh_packet_set_default_callbacks(ssh_session session); -void ssh_packet_process(ssh_session session, uint8_t type); - -/* PACKET CRYPT */ -uint32_t packet_decrypt_len(ssh_session session, char *crypted); -int packet_decrypt(ssh_session session, void *packet, unsigned int len); -unsigned char *packet_encrypt(ssh_session session, - void *packet, - unsigned int len); -int packet_hmac_verify(ssh_session session,ssh_buffer buffer, - unsigned char *mac, enum ssh_hmac_e type); - -#endif /* PACKET_H_ */ diff --git a/libssh/include/libssh/pcap.h b/libssh/include/libssh/pcap.h deleted file mode 100644 index ca57156b..00000000 --- a/libssh/include/libssh/pcap.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PCAP_H_ -#define PCAP_H_ - -#include "config.h" -#include "libssh/libssh.h" - -#ifdef WITH_PCAP -typedef struct ssh_pcap_context_struct* ssh_pcap_context; - -int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len); - -ssh_pcap_context ssh_pcap_context_new(ssh_session session); -void ssh_pcap_context_free(ssh_pcap_context ctx); - -enum ssh_pcap_direction{ - SSH_PCAP_DIR_IN, - SSH_PCAP_DIR_OUT -}; -void ssh_pcap_context_set_file(ssh_pcap_context, ssh_pcap_file); -int ssh_pcap_context_write(ssh_pcap_context,enum ssh_pcap_direction direction, void *data, - uint32_t len, uint32_t origlen); - - -#endif /* WITH_PCAP */ -#endif /* PCAP_H_ */ -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/include/libssh/pki.h b/libssh/include/libssh/pki.h deleted file mode 100644 index 9f9ddf4a..00000000 --- a/libssh/include/libssh/pki.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PKI_H_ -#define PKI_H_ - -#include "libssh/priv.h" -#ifdef HAVE_OPENSSL_EC_H -#include -#endif -#ifdef HAVE_OPENSSL_ECDSA_H -#include -#endif - -#include "libssh/crypto.h" -#include "libssh/ed25519.h" - -#define MAX_PUBKEY_SIZE 0x100000 /* 1M */ -#define MAX_PRIVKEY_SIZE 0x400000 /* 4M */ - -#define SSH_KEY_FLAG_EMPTY 0x0 -#define SSH_KEY_FLAG_PUBLIC 0x0001 -#define SSH_KEY_FLAG_PRIVATE 0x0002 - -struct ssh_key_struct { - enum ssh_keytypes_e type; - int flags; - const char *type_c; /* Don't free it ! it is static */ - int ecdsa_nid; -#ifdef HAVE_LIBGCRYPT - gcry_sexp_t dsa; - gcry_sexp_t rsa; - void *ecdsa; -#elif HAVE_LIBCRYPTO - DSA *dsa; - RSA *rsa; -#ifdef HAVE_OPENSSL_ECC - EC_KEY *ecdsa; -#else - void *ecdsa; -#endif /* HAVE_OPENSSL_EC_H */ -#endif - ed25519_pubkey *ed25519_pubkey; - ed25519_privkey *ed25519_privkey; - void *cert; -}; - -struct ssh_signature_struct { - enum ssh_keytypes_e type; - const char *type_c; -#ifdef HAVE_LIBGCRYPT - gcry_sexp_t dsa_sig; - gcry_sexp_t rsa_sig; - void *ecdsa_sig; -#elif defined HAVE_LIBCRYPTO - DSA_SIG *dsa_sig; - ssh_string rsa_sig; -# ifdef HAVE_OPENSSL_ECC - ECDSA_SIG *ecdsa_sig; -# else - void *ecdsa_sig; -# endif -#endif - ed25519_signature *ed25519_sig; -}; - -typedef struct ssh_signature_struct *ssh_signature; - -/* SSH Key Functions */ -ssh_key ssh_key_dup(const ssh_key key); -void ssh_key_clean (ssh_key key); - -/* SSH Signature Functions */ -ssh_signature ssh_signature_new(void); -void ssh_signature_free(ssh_signature sign); - -int ssh_pki_export_signature_blob(const ssh_signature sign, - ssh_string *sign_blob); -int ssh_pki_import_signature_blob(const ssh_string sig_blob, - const ssh_key pubkey, - ssh_signature *psig); -int ssh_pki_signature_verify_blob(ssh_session session, - ssh_string sig_blob, - const ssh_key key, - unsigned char *digest, - size_t dlen); - -/* SSH Public Key Functions */ -int ssh_pki_export_pubkey_blob(const ssh_key key, - ssh_string *pblob); -int ssh_pki_import_pubkey_blob(const ssh_string key_blob, - ssh_key *pkey); -int ssh_pki_export_pubkey_rsa1(const ssh_key key, - const char *host, - char *rsa1, - size_t rsa1_len); - -/* SSH Signing Functions */ -ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf, - const ssh_key privatekey); -ssh_string ssh_pki_do_sign_agent(ssh_session session, - struct ssh_buffer_struct *buf, - const ssh_key pubkey); -ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, - const ssh_key privkey); - -/* Temporary functions, to be removed after migration to ssh_key */ -ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key); -ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key); - -#endif /* PKI_H_ */ diff --git a/libssh/include/libssh/pki_priv.h b/libssh/include/libssh/pki_priv.h deleted file mode 100644 index 0aaadb60..00000000 --- a/libssh/include/libssh/pki_priv.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef PKI_PRIV_H_ -#define PKI_PRIV_H_ - -#define RSA_HEADER_BEGIN "-----BEGIN RSA PRIVATE KEY-----" -#define RSA_HEADER_END "-----END RSA PRIVATE KEY-----" -#define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----" -#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----" -#define ECDSA_HEADER_BEGIN "-----BEGIN EC PRIVATE KEY-----" -#define ECDSA_HEADER_END "-----END EC PRIVATE KEY-----" - -#define ssh_pki_log(...) \ - _ssh_pki_log(__FUNCTION__, __VA_ARGS__) -void _ssh_pki_log(const char *function, - const char *format, ...) PRINTF_ATTRIBUTE(2, 3); - -int pki_key_ecdsa_nid_from_name(const char *name); -const char *pki_key_ecdsa_nid_to_name(int nid); - -/* SSH Key Functions */ -ssh_key pki_key_dup(const ssh_key key, int demote); -int pki_key_generate_rsa(ssh_key key, int parameter); -int pki_key_generate_dss(ssh_key key, int parameter); -int pki_key_generate_ecdsa(ssh_key key, int parameter); -int pki_key_generate_ed25519(ssh_key key); - -int pki_key_compare(const ssh_key k1, - const ssh_key k2, - enum ssh_keycmp_e what); - -/* SSH Private Key Functions */ -enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey); -ssh_key pki_private_key_from_base64(const char *b64_key, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data); - -ssh_string pki_private_key_to_pem(const ssh_key key, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data); - -/* SSH Public Key Functions */ -int pki_pubkey_build_dss(ssh_key key, - ssh_string p, - ssh_string q, - ssh_string g, - ssh_string pubkey); -int pki_pubkey_build_rsa(ssh_key key, - ssh_string e, - ssh_string n); -int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e); -ssh_string pki_publickey_to_blob(const ssh_key key); -int pki_export_pubkey_rsa1(const ssh_key key, - const char *host, - char *rsa1, - size_t rsa1_len); - -/* SSH Signature Functions */ -ssh_string pki_signature_to_blob(const ssh_signature sign); -ssh_signature pki_signature_from_blob(const ssh_key pubkey, - const ssh_string sig_blob, - enum ssh_keytypes_e type); -int pki_signature_verify(ssh_session session, - const ssh_signature sig, - const ssh_key key, - const unsigned char *hash, - size_t hlen); - -/* SSH Signing Functions */ -ssh_signature pki_do_sign(const ssh_key privkey, - const unsigned char *hash, - size_t hlen); -ssh_signature pki_do_sign_sessionid(const ssh_key key, - const unsigned char *hash, - size_t hlen); -int pki_ed25519_sign(const ssh_key privkey, ssh_signature sig, - const unsigned char *hash, size_t hlen); -int pki_ed25519_verify(const ssh_key pubkey, ssh_signature sig, - const unsigned char *hash, size_t hlen); -int pki_ed25519_key_cmp(const ssh_key k1, - const ssh_key k2, - enum ssh_keycmp_e what); -int pki_ed25519_key_dup(ssh_key new, const ssh_key key); -int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key); -ssh_string pki_ed25519_sig_to_blob(ssh_signature sig); -int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob); - -#endif /* PKI_PRIV_H_ */ diff --git a/libssh/include/libssh/poll.h b/libssh/include/libssh/poll.h deleted file mode 100644 index bbc03a95..00000000 --- a/libssh/include/libssh/poll.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef POLL_H_ -#define POLL_H_ - -#include "config.h" - -#ifdef HAVE_POLL - -#include -typedef struct pollfd ssh_pollfd_t; - -#else /* HAVE_POLL */ - -/* poll emulation support */ - -typedef struct ssh_pollfd_struct { - socket_t fd; /* file descriptor */ - short events; /* requested events */ - short revents; /* returned events */ -} ssh_pollfd_t; - -typedef unsigned long int nfds_t; - -#ifdef _WIN32 - -#ifndef POLLRDNORM -#define POLLRDNORM 0x0100 -#endif -#ifndef POLLRDBAND -#define POLLRDBAND 0x0200 -#endif -#ifndef POLLIN -#define POLLIN (POLLRDNORM | POLLRDBAND) -#endif -#ifndef POLLPRI -#define POLLPRI 0x0400 -#endif - -#ifndef POLLWRNORM -#define POLLWRNORM 0x0010 -#endif -#ifndef POLLOUT -#define POLLOUT (POLLWRNORM) -#endif -#ifndef POLLWRBAND -#define POLLWRBAND 0x0020 -#endif - -#ifndef POLLERR -#define POLLERR 0x0001 -#endif -#ifndef POLLHUP -#define POLLHUP 0x0002 -#endif -#ifndef POLLNVAL -#define POLLNVAL 0x0004 -#endif - -#else /* _WIN32 */ - -/* poll.c */ -#ifndef POLLIN -#define POLLIN 0x001 /* There is data to read. */ -#endif -#ifndef POLLPRI -#define POLLPRI 0x002 /* There is urgent data to read. */ -#endif -#ifndef POLLOUT -#define POLLOUT 0x004 /* Writing now will not block. */ -#endif - -#ifndef POLLERR -#define POLLERR 0x008 /* Error condition. */ -#endif -#ifndef POLLHUP -#define POLLHUP 0x010 /* Hung up. */ -#endif -#ifndef POLLNVAL -#define POLLNVAL 0x020 /* Invalid polling request. */ -#endif - -#ifndef POLLRDNORM -#define POLLRDNORM 0x040 /* mapped to read fds_set */ -#endif -#ifndef POLLRDBAND -#define POLLRDBAND 0x080 /* mapped to exception fds_set */ -#endif -#ifndef POLLWRNORM -#define POLLWRNORM 0x100 /* mapped to write fds_set */ -#endif -#ifndef POLLWRBAND -#define POLLWRBAND 0x200 /* mapped to write fds_set */ -#endif - -#endif /* WIN32 */ -#endif /* HAVE_POLL */ - -void ssh_poll_init(void); -void ssh_poll_cleanup(void); -int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout); -typedef struct ssh_poll_ctx_struct *ssh_poll_ctx; -typedef struct ssh_poll_handle_struct *ssh_poll_handle; - -/** - * @brief SSH poll callback. This callback will be used when an event - * caught on the socket. - * - * @param p Poll object this callback belongs to. - * @param fd The raw socket. - * @param revents The current poll events on the socket. - * @param userdata Userdata to be passed to the callback function. - * - * @return 0 on success, < 0 if you removed the poll object from - * its poll context. - */ -typedef int (*ssh_poll_callback)(ssh_poll_handle p, socket_t fd, int revents, - void *userdata); - -struct ssh_socket_struct; - -ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb, - void *userdata); -void ssh_poll_free(ssh_poll_handle p); -ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p); -short ssh_poll_get_events(ssh_poll_handle p); -void ssh_poll_set_events(ssh_poll_handle p, short events); -void ssh_poll_add_events(ssh_poll_handle p, short events); -void ssh_poll_remove_events(ssh_poll_handle p, short events); -socket_t ssh_poll_get_fd(ssh_poll_handle p); -void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd); -void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata); -ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size); -void ssh_poll_ctx_free(ssh_poll_ctx ctx); -int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p); -int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, struct ssh_socket_struct *s); -void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p); -int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout); -ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session); - -#endif /* POLL_H_ */ diff --git a/libssh/include/libssh/priv.h b/libssh/include/libssh/priv.h deleted file mode 100644 index 0e3bab5b..00000000 --- a/libssh/include/libssh/priv.h +++ /dev/null @@ -1,305 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * priv.h file - * This include file contains everything you shouldn't deal with in - * user programs. Consider that anything in this file might change - * without notice; libssh.h file will keep backward compatibility - * on binary & source - */ - -#ifndef _LIBSSH_PRIV_H -#define _LIBSSH_PRIV_H - -#include "config.h" - -#if !defined(HAVE_STRTOULL) -# if defined(HAVE___STRTOULL) -# define strtoull __strtoull -# elif defined(HAVE__STRTOUI64) -# define strtoull _strtoui64 -# elif defined(__hpux) && defined(__LP64__) -# define strtoull strtoul -# else -# error "no strtoull function found" -# endif -#endif /* !defined(HAVE_STRTOULL) */ - -#ifdef _WIN32 - -/* Imitate define of inttypes.h */ -# ifndef PRIdS -# define PRIdS "Id" -# endif - -# ifndef PRIu64 -# if __WORDSIZE == 64 -# define PRIu64 "lu" -# else -# define PRIu64 "llu" -# endif /* __WORDSIZE */ -# endif /* PRIu64 */ - -# ifdef _MSC_VER -# include - -/* On Microsoft compilers define inline to __inline on all others use inline */ -# undef inline -# define inline __inline - -# define strcasecmp _stricmp -# define strncasecmp _strnicmp -# if ! defined(HAVE_ISBLANK) -# define isblank(ch) ((ch) == ' ' || (ch) == '\t' || (ch) == '\n' || (ch) == '\r') -# endif - -# define usleep(X) Sleep(((X)+1000)/1000) - -# undef strtok_r -# define strtok_r strtok_s - -# if defined(HAVE__SNPRINTF_S) -# undef snprintf -# define snprintf(d, n, ...) _snprintf_s((d), (n), _TRUNCATE, __VA_ARGS__) -# else /* HAVE__SNPRINTF_S */ -# if defined(HAVE__SNPRINTF) -# undef snprintf -# define snprintf _snprintf -# else /* HAVE__SNPRINTF */ -# if !defined(HAVE_SNPRINTF) -# error "no snprintf compatible function found" -# endif /* HAVE_SNPRINTF */ -# endif /* HAVE__SNPRINTF */ -# endif /* HAVE__SNPRINTF_S */ - -# if defined(HAVE__VSNPRINTF_S) -# undef vsnprintf -# define vsnprintf(s, n, f, v) _vsnprintf_s((s), (n), _TRUNCATE, (f), (v)) -# else /* HAVE__VSNPRINTF_S */ -# if defined(HAVE__VSNPRINTF) -# undef vsnprintf -# define vsnprintf _vsnprintf -# else -# if !defined(HAVE_VSNPRINTF) -# error "No vsnprintf compatible function found" -# endif /* HAVE_VSNPRINTF */ -# endif /* HAVE__VSNPRINTF */ -# endif /* HAVE__VSNPRINTF_S */ - -# endif /* _MSC_VER */ - -struct timeval; -int gettimeofday(struct timeval *__p, void *__t); - -#else /* _WIN32 */ - -#include -#define PRIdS "zd" - -#endif /* _WIN32 */ - -#include "libssh/libssh.h" -#include "libssh/callbacks.h" - -/* some constants */ -#ifndef MAX_PACKAT_LEN -#define MAX_PACKET_LEN 262144 -#endif -#ifndef ERROR_BUFFERLEN -#define ERROR_BUFFERLEN 1024 -#endif -#ifndef CLIENTBANNER1 -#define CLIENTBANNER1 "SSH-1.5-libssh-" SSH_STRINGIFY(LIBSSH_VERSION) -#endif -#ifndef CLIENTBANNER2 -#define CLIENTBANNER2 "SSH-2.0-libssh-" SSH_STRINGIFY(LIBSSH_VERSION) -#endif -#ifndef KBDINT_MAX_PROMPT -#define KBDINT_MAX_PROMPT 256 /* more than openssh's :) */ -#endif -#ifndef MAX_BUF_SIZE -#define MAX_BUF_SIZE 4096 -#endif - -#ifndef __FUNCTION__ -#if defined(__SUNPRO_C) -#define __FUNCTION__ __func__ -#endif -#endif - -#if defined(HAVE_GCC_THREAD_LOCAL_STORAGE) -# define LIBSSH_THREAD __thread -#elif defined(HAVE_MSC_THREAD_LOCAL_STORAGE) -# define LIBSSH_THREAD __declspec(thread) -#else -# define LIBSSH_THREAD -#endif - -/* - * This makes sure that the compiler doesn't optimize out the code - * - * Use it in a macro where the provided variable is 'x'. - */ -#if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION) -# define LIBSSH_MEM_PROTECTION __asm__ volatile("" : : "r"(&(x)) : "memory") -#else -# define LIBSSH_MEM_PROTECTION -#endif - -#ifdef HAVE_SYS_TIME_H -#include -#endif - -/* forward declarations */ -struct ssh_common_struct; -struct ssh_kex_struct; - -int ssh_get_key_params(ssh_session session, ssh_key *privkey); - -/* LOGGING */ -void ssh_log_function(int verbosity, - const char *function, - const char *buffer); -#define SSH_LOG(priority, ...) \ - _ssh_log(priority, __FUNCTION__, __VA_ARGS__) - -/* LEGACY */ -void ssh_log_common(struct ssh_common_struct *common, - int verbosity, - const char *function, - const char *format, ...) PRINTF_ATTRIBUTE(4, 5); - - -/* ERROR HANDLING */ - -/* error handling structure */ -struct error_struct { - int error_code; - char error_buffer[ERROR_BUFFERLEN]; -}; - -#define ssh_set_error(error, code, ...) \ - _ssh_set_error(error, code, __FUNCTION__, __VA_ARGS__) -void _ssh_set_error(void *error, - int code, - const char *function, - const char *descr, ...) PRINTF_ATTRIBUTE(4, 5); - -#define ssh_set_error_oom(error) \ - _ssh_set_error_oom(error, __FUNCTION__) -void _ssh_set_error_oom(void *error, const char *function); - -#define ssh_set_error_invalid(error) \ - _ssh_set_error_invalid(error, __FUNCTION__) -void _ssh_set_error_invalid(void *error, const char *function); - - -/* server.c */ -#ifdef WITH_SERVER -int ssh_auth_reply_default(ssh_session session,int partial); -int ssh_auth_reply_success(ssh_session session, int partial); -#endif -/* client.c */ - -int ssh_send_banner(ssh_session session, int is_server); - -/* connect.c */ -socket_t ssh_connect_host(ssh_session session, const char *host,const char - *bind_addr, int port, long timeout, long usec); -socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, - const char *bind_addr, int port); - -/* in base64.c */ -ssh_buffer base64_to_bin(const char *source); -unsigned char *bin_to_base64(const unsigned char *source, int len); - -/* gzip.c */ -int compress_buffer(ssh_session session,ssh_buffer buf); -int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen); - -/* match.c */ -int match_hostname(const char *host, const char *pattern, unsigned int len); - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - -/** Free memory space */ -#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0) - -/** Zero a structure */ -#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) - -/** Zero a structure given a pointer to the structure */ -#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0) - -/** Get the size of an array */ -#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) - -/* - * See http://llvm.org/bugs/show_bug.cgi?id=15495 - */ -#if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION) -/** Overwrite a string with '\0' */ -# define BURN_STRING(x) do { \ - if ((x) != NULL) \ - memset((x), '\0', strlen((x))); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ - } while(0) - -/** Overwrite the buffer with '\0' */ -# define BURN_BUFFER(x, size) do { \ - if ((x) != NULL) \ - memset((x), '\0', (size)); __asm__ volatile("" : : "r"(&(x)) : "memory"); \ - } while(0) -#else /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */ -/** Overwrite a string with '\0' */ -# define BURN_STRING(x) do { \ - if ((x) != NULL) memset((x), '\0', strlen((x))); \ - } while(0) - -/** Overwrite the buffer with '\0' */ -# define BURN_BUFFER(x, size) do { \ - if ((x) != NULL) \ - memset((x), '\0', (size)); \ - } while(0) -#endif /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */ - -/** - * This is a hack to fix warnings. The idea is to use this everywhere that we - * get the "discarding const" warning by the compiler. That doesn't actually - * fix the real issue, but marks the place and you can search the code for - * discard_const. - * - * Please use this macro only when there is no other way to fix the warning. - * We should use this function in only in a very few places. - * - * Also, please call this via the discard_const_p() macro interface, as that - * makes the return type safe. - */ -#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) - -/** - * Type-safe version of discard_const - */ -#define discard_const_p(type, ptr) ((type *)discard_const(ptr)) - -#endif /* _LIBSSH_PRIV_H */ -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/include/libssh/sc25519.h b/libssh/include/libssh/sc25519.h deleted file mode 100644 index 5a2c1b85..00000000 --- a/libssh/include/libssh/sc25519.h +++ /dev/null @@ -1,74 +0,0 @@ -/* $OpenBSD: sc25519.h,v 1.3 2013/12/09 11:03:45 markus Exp $ */ - -/* - * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, - * Peter Schwabe, Bo-Yin Yang. - * Copied from supercop-20130419/crypto_sign/ed25519/ref/sc25519.h - */ - -#ifndef SC25519_H -#define SC25519_H - -#define sc25519 crypto_sign_ed25519_ref_sc25519 -#define shortsc25519 crypto_sign_ed25519_ref_shortsc25519 -#define sc25519_from32bytes crypto_sign_ed25519_ref_sc25519_from32bytes -#define shortsc25519_from16bytes crypto_sign_ed25519_ref_shortsc25519_from16bytes -#define sc25519_from64bytes crypto_sign_ed25519_ref_sc25519_from64bytes -#define sc25519_from_shortsc crypto_sign_ed25519_ref_sc25519_from_shortsc -#define sc25519_to32bytes crypto_sign_ed25519_ref_sc25519_to32bytes -#define sc25519_iszero_vartime crypto_sign_ed25519_ref_sc25519_iszero_vartime -#define sc25519_isshort_vartime crypto_sign_ed25519_ref_sc25519_isshort_vartime -#define sc25519_lt_vartime crypto_sign_ed25519_ref_sc25519_lt_vartime -#define sc25519_add crypto_sign_ed25519_ref_sc25519_add -#define sc25519_sub_nored crypto_sign_ed25519_ref_sc25519_sub_nored -#define sc25519_mul crypto_sign_ed25519_ref_sc25519_mul -#define sc25519_mul_shortsc crypto_sign_ed25519_ref_sc25519_mul_shortsc -#define sc25519_window3 crypto_sign_ed25519_ref_sc25519_window3 -#define sc25519_window5 crypto_sign_ed25519_ref_sc25519_window5 -#define sc25519_2interleave2 crypto_sign_ed25519_ref_sc25519_2interleave2 - -typedef struct { - uint32_t v[32]; -} sc25519; - -typedef struct { - uint32_t v[16]; -} shortsc25519; - -void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]); - -void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16]); - -void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]); - -void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x); - -void sc25519_to32bytes(unsigned char r[32], const sc25519 *x); - -int sc25519_iszero_vartime(const sc25519 *x); - -int sc25519_isshort_vartime(const sc25519 *x); - -int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y); - -void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y); - -void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y); - -void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y); - -void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y); - -/* Convert s into a representation of the form \sum_{i=0}^{84}r[i]2^3 - * with r[i] in {-4,...,3} - */ -void sc25519_window3(signed char r[85], const sc25519 *s); - -/* Convert s into a representation of the form \sum_{i=0}^{50}r[i]2^5 - * with r[i] in {-16,...,15} - */ -void sc25519_window5(signed char r[51], const sc25519 *s); - -void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2); - -#endif diff --git a/libssh/include/libssh/scp.h b/libssh/include/libssh/scp.h deleted file mode 100644 index d356d89b..00000000 --- a/libssh/include/libssh/scp.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _SCP_H -#define _SCP_H - -enum ssh_scp_states { - SSH_SCP_NEW, //Data structure just created - SSH_SCP_WRITE_INITED, //Gave our intention to write - SSH_SCP_WRITE_WRITING,//File was opened and currently writing - SSH_SCP_READ_INITED, //Gave our intention to read - SSH_SCP_READ_REQUESTED, //We got a read request - SSH_SCP_READ_READING, //File is opened and reading - SSH_SCP_ERROR, //Something bad happened - SSH_SCP_TERMINATED //Transfer finished -}; - -struct ssh_scp_struct { - ssh_session session; - int mode; - int recursive; - ssh_channel channel; - char *location; - enum ssh_scp_states state; - uint64_t filelen; - uint64_t processed; - enum ssh_scp_request_types request_type; - char *request_name; - char *warning; - int request_mode; -}; - -int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len); -int ssh_scp_integer_mode(const char *mode); -char *ssh_scp_string_mode(int mode); -int ssh_scp_response(ssh_scp scp, char **response); - -#endif diff --git a/libssh/include/libssh/server.h b/libssh/include/libssh/server.h deleted file mode 100644 index 385a10a7..00000000 --- a/libssh/include/libssh/server.h +++ /dev/null @@ -1,336 +0,0 @@ -/* Public include file for server support */ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * @defgroup libssh_server The libssh server API - * - * @{ - */ - -#ifndef SERVER_H -#define SERVER_H - -#include "libssh/libssh.h" -#define SERVERBANNER CLIENTBANNER - -#ifdef __cplusplus -extern "C" { -#endif - -enum ssh_bind_options_e { - SSH_BIND_OPTIONS_BINDADDR, - SSH_BIND_OPTIONS_BINDPORT, - SSH_BIND_OPTIONS_BINDPORT_STR, - SSH_BIND_OPTIONS_HOSTKEY, - SSH_BIND_OPTIONS_DSAKEY, - SSH_BIND_OPTIONS_RSAKEY, - SSH_BIND_OPTIONS_BANNER, - SSH_BIND_OPTIONS_LOG_VERBOSITY, - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, - SSH_BIND_OPTIONS_ECDSAKEY -}; - -typedef struct ssh_bind_struct* ssh_bind; - -/* Callback functions */ - -/** - * @brief Incoming connection callback. This callback is called when a ssh_bind - * has a new incoming connection. - * @param sshbind Current sshbind session handler - * @param userdata Userdata to be passed to the callback function. - */ -typedef void (*ssh_bind_incoming_connection_callback) (ssh_bind sshbind, - void *userdata); - -/** - * @brief These are the callbacks exported by the ssh_bind structure. - * - * They are called by the server module when events appear on the network. - */ -struct ssh_bind_callbacks_struct { - /** DON'T SET THIS use ssh_callbacks_init() instead. */ - size_t size; - /** A new connection is available. */ - ssh_bind_incoming_connection_callback incoming_connection; -}; -typedef struct ssh_bind_callbacks_struct *ssh_bind_callbacks; - -/** - * @brief Creates a new SSH server bind. - * - * @return A newly allocated ssh_bind session pointer. - */ -LIBSSH_API ssh_bind ssh_bind_new(void); - -LIBSSH_API int ssh_bind_options_set(ssh_bind sshbind, - enum ssh_bind_options_e type, const void *value); - -/** - * @brief Start listening to the socket. - * - * @param ssh_bind_o The ssh server bind to use. - * - * @return 0 on success, < 0 on error. - */ -LIBSSH_API int ssh_bind_listen(ssh_bind ssh_bind_o); - -/** - * @brief Set the callback for this bind. - * - * @param[in] sshbind The bind to set the callback on. - * - * @param[in] callbacks An already set up ssh_bind_callbacks instance. - * - * @param[in] userdata A pointer to private data to pass to the callbacks. - * - * @return SSH_OK on success, SSH_ERROR if an error occured. - * - * @code - * struct ssh_callbacks_struct cb = { - * .userdata = data, - * .auth_function = my_auth_function - * }; - * ssh_callbacks_init(&cb); - * ssh_bind_set_callbacks(session, &cb); - * @endcode - */ -LIBSSH_API int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, - void *userdata); - -/** - * @brief Set the session to blocking/nonblocking mode. - * - * @param ssh_bind_o The ssh server bind to use. - * - * @param blocking Zero for nonblocking mode. - */ -LIBSSH_API void ssh_bind_set_blocking(ssh_bind ssh_bind_o, int blocking); - -/** - * @brief Recover the file descriptor from the session. - * - * @param ssh_bind_o The ssh server bind to get the fd from. - * - * @return The file descriptor. - */ -LIBSSH_API socket_t ssh_bind_get_fd(ssh_bind ssh_bind_o); - -/** - * @brief Set the file descriptor for a session. - * - * @param ssh_bind_o The ssh server bind to set the fd. - * - * @param fd The file descriptssh_bind B - */ -LIBSSH_API void ssh_bind_set_fd(ssh_bind ssh_bind_o, socket_t fd); - -/** - * @brief Allow the file descriptor to accept new sessions. - * - * @param ssh_bind_o The ssh server bind to use. - */ -LIBSSH_API void ssh_bind_fd_toaccept(ssh_bind ssh_bind_o); - -/** - * @brief Accept an incoming ssh connection and initialize the session. - * - * @param ssh_bind_o The ssh server bind to accept a connection. - * @param session A preallocated ssh session - * @see ssh_new - * @return SSH_OK when a connection is established - */ -LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session); - -/** - * @brief Accept an incoming ssh connection on the given file descriptor - * and initialize the session. - * - * @param ssh_bind_o The ssh server bind to accept a connection. - * @param session A preallocated ssh session - * @param fd A file descriptor of an already established TCP - * inbound connection - * @see ssh_new - * @see ssh_bind_accept - * @return SSH_OK when a connection is established - */ -LIBSSH_API int ssh_bind_accept_fd(ssh_bind ssh_bind_o, ssh_session session, - socket_t fd); - -LIBSSH_API ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session); - -/** - * @brief Handles the key exchange and set up encryption - * - * @param session A connected ssh session - * @see ssh_bind_accept - * @return SSH_OK if the key exchange was successful - */ -LIBSSH_API int ssh_handle_key_exchange(ssh_session session); - -/** - * @brief Free a ssh servers bind. - * - * @param ssh_bind_o The ssh server bind to free. - */ -LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o); - -LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods); - -/********************************************************** - * SERVER MESSAGING - **********************************************************/ - -/** - * @brief Reply with a standard reject message. - * - * Use this function if you don't know what to respond or if you want to reject - * a request. - * - * @param[in] msg The message to use for the reply. - * - * @return 0 on success, -1 on error. - * - * @see ssh_message_get() - */ -LIBSSH_API int ssh_message_reply_default(ssh_message msg); - -/** - * @brief Get the name of the authenticated user. - * - * @param[in] msg The message to get the username from. - * - * @return The username or NULL if an error occured. - * - * @see ssh_message_get() - * @see ssh_message_type() - */ -LIBSSH_API const char *ssh_message_auth_user(ssh_message msg); - -/** - * @brief Get the password of the authenticated user. - * - * @param[in] msg The message to get the password from. - * - * @return The username or NULL if an error occured. - * - * @see ssh_message_get() - * @see ssh_message_type() - */ -LIBSSH_API const char *ssh_message_auth_password(ssh_message msg); - -/** - * @brief Get the publickey of the authenticated user. - * - * If you need the key for later user you should duplicate it. - * - * @param[in] msg The message to get the public key from. - * - * @return The public key or NULL. - * - * @see ssh_key_dup() - * @see ssh_key_cmp() - * @see ssh_message_get() - * @see ssh_message_type() - */ -LIBSSH_API ssh_key ssh_message_auth_pubkey(ssh_message msg); - -LIBSSH_API int ssh_message_auth_kbdint_is_response(ssh_message msg); -LIBSSH_API enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg); -LIBSSH_API int ssh_message_auth_reply_success(ssh_message msg,int partial); -LIBSSH_API int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey); -LIBSSH_API int ssh_message_auth_reply_pk_ok_simple(ssh_message msg); - -LIBSSH_API int ssh_message_auth_set_methods(ssh_message msg, int methods); - -LIBSSH_API int ssh_message_auth_interactive_request(ssh_message msg, - const char *name, const char *instruction, - unsigned int num_prompts, const char **prompts, char *echo); - -LIBSSH_API int ssh_message_service_reply_success(ssh_message msg); -LIBSSH_API const char *ssh_message_service_service(ssh_message msg); - -LIBSSH_API int ssh_message_global_request_reply_success(ssh_message msg, - uint16_t bound_port); - -LIBSSH_API void ssh_set_message_callback(ssh_session session, - int(*ssh_bind_message_callback)(ssh_session session, ssh_message msg, void *data), - void *data); -LIBSSH_API int ssh_execute_message_callbacks(ssh_session session); - -LIBSSH_API const char *ssh_message_channel_request_open_originator(ssh_message msg); -LIBSSH_API int ssh_message_channel_request_open_originator_port(ssh_message msg); -LIBSSH_API const char *ssh_message_channel_request_open_destination(ssh_message msg); -LIBSSH_API int ssh_message_channel_request_open_destination_port(ssh_message msg); - -LIBSSH_API ssh_channel ssh_message_channel_request_channel(ssh_message msg); - -LIBSSH_API const char *ssh_message_channel_request_pty_term(ssh_message msg); -LIBSSH_API int ssh_message_channel_request_pty_width(ssh_message msg); -LIBSSH_API int ssh_message_channel_request_pty_height(ssh_message msg); -LIBSSH_API int ssh_message_channel_request_pty_pxwidth(ssh_message msg); -LIBSSH_API int ssh_message_channel_request_pty_pxheight(ssh_message msg); - -LIBSSH_API const char *ssh_message_channel_request_env_name(ssh_message msg); -LIBSSH_API const char *ssh_message_channel_request_env_value(ssh_message msg); - -LIBSSH_API const char *ssh_message_channel_request_command(ssh_message msg); - -LIBSSH_API const char *ssh_message_channel_request_subsystem(ssh_message msg); - -LIBSSH_API int ssh_message_channel_request_x11_single_connection(ssh_message msg); -LIBSSH_API const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg); -LIBSSH_API const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg); -LIBSSH_API int ssh_message_channel_request_x11_screen_number(ssh_message msg); - -LIBSSH_API const char *ssh_message_global_request_address(ssh_message msg); -LIBSSH_API int ssh_message_global_request_port(ssh_message msg); - -LIBSSH_API int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost, - int remoteport, const char *sourcehost, int localport); -LIBSSH_API int ssh_channel_open_x11(ssh_channel channel, - const char *orig_addr, int orig_port); - -LIBSSH_API int ssh_channel_request_send_exit_status(ssh_channel channel, - int exit_status); -LIBSSH_API int ssh_channel_request_send_exit_signal(ssh_channel channel, - const char *signum, - int core, - const char *errmsg, - const char *lang); -LIBSSH_API int ssh_channel_write_stderr(ssh_channel channel, - const void *data, - uint32_t len); - -LIBSSH_API int ssh_send_keepalive(ssh_session session); - -/* deprecated functions */ -SSH_DEPRECATED LIBSSH_API int ssh_accept(ssh_session session); -SSH_DEPRECATED LIBSSH_API int channel_write_stderr(ssh_channel channel, - const void *data, uint32_t len); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* SERVER_H */ - -/** @} */ diff --git a/libssh/include/libssh/session.h b/libssh/include/libssh/session.h deleted file mode 100644 index 29bdd60b..00000000 --- a/libssh/include/libssh/session.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SESSION_H_ -#define SESSION_H_ -#include "libssh/priv.h" -#include "libssh/kex.h" -#include "libssh/packet.h" -#include "libssh/pcap.h" -#include "libssh/auth.h" -#include "libssh/channels.h" -#include "libssh/poll.h" - -/* These are the different states a SSH session can be into its life */ -enum ssh_session_state_e { - SSH_SESSION_STATE_NONE=0, - SSH_SESSION_STATE_CONNECTING, - SSH_SESSION_STATE_SOCKET_CONNECTED, - SSH_SESSION_STATE_BANNER_RECEIVED, - SSH_SESSION_STATE_INITIAL_KEX, - SSH_SESSION_STATE_KEXINIT_RECEIVED, - SSH_SESSION_STATE_DH, - SSH_SESSION_STATE_AUTHENTICATING, - SSH_SESSION_STATE_AUTHENTICATED, - SSH_SESSION_STATE_ERROR, - SSH_SESSION_STATE_DISCONNECTED -}; - -enum ssh_dh_state_e { - DH_STATE_INIT=0, - DH_STATE_INIT_SENT, - DH_STATE_NEWKEYS_SENT, - DH_STATE_FINISHED -}; - -enum ssh_pending_call_e { - SSH_PENDING_CALL_NONE = 0, - SSH_PENDING_CALL_CONNECT, - SSH_PENDING_CALL_AUTH_NONE, - SSH_PENDING_CALL_AUTH_PASSWORD, - SSH_PENDING_CALL_AUTH_OFFER_PUBKEY, - SSH_PENDING_CALL_AUTH_PUBKEY, - SSH_PENDING_CALL_AUTH_AGENT, - SSH_PENDING_CALL_AUTH_KBDINT_INIT, - SSH_PENDING_CALL_AUTH_KBDINT_SEND, - SSH_PENDING_CALL_AUTH_GSSAPI_MIC -}; - -/* libssh calls may block an undefined amount of time */ -#define SSH_SESSION_FLAG_BLOCKING 1 - -/* Client successfully authenticated */ -#define SSH_SESSION_FLAG_AUTHENTICATED 2 - -/* codes to use with ssh_handle_packets*() */ -/* Infinite timeout */ -#define SSH_TIMEOUT_INFINITE -1 -/* Use the timeout defined by user if any. Mostly used with new connections */ -#define SSH_TIMEOUT_USER -2 -/* Use the default timeout, depending on ssh_is_blocking() */ -#define SSH_TIMEOUT_DEFAULT -3 -/* Don't block at all */ -#define SSH_TIMEOUT_NONBLOCKING 0 - -/* members that are common to ssh_session and ssh_bind */ -struct ssh_common_struct { - struct error_struct error; - ssh_callbacks callbacks; /* Callbacks to user functions */ - int log_verbosity; /* verbosity of the log functions */ -}; - -struct ssh_session_struct { - struct ssh_common_struct common; - struct ssh_socket_struct *socket; - char *serverbanner; - char *clientbanner; - int protoversion; - int server; - int client; - int openssh; - uint32_t send_seq; - uint32_t recv_seq; - - int connected; - /* !=0 when the user got a session handle */ - int alive; - /* two previous are deprecated */ - /* int auth_service_asked; */ - - /* session flags (SSH_SESSION_FLAG_*) */ - int flags; - - ssh_string banner; /* that's the issue banner from - the server */ - char *discon_msg; /* disconnect message from - the remote host */ - ssh_buffer in_buffer; - PACKET in_packet; - ssh_buffer out_buffer; - - /* the states are used by the nonblocking stuff to remember */ - /* where it was before being interrupted */ - enum ssh_pending_call_e pending_call_state; - enum ssh_session_state_e session_state; - int packet_state; - enum ssh_dh_state_e dh_handshake_state; - enum ssh_auth_service_state_e auth_service_state; - enum ssh_auth_state_e auth_state; - enum ssh_channel_request_state_e global_req_state; - struct ssh_agent_state_struct *agent_state; - struct ssh_auth_auto_state_struct *auth_auto_state; - - /* - * RFC 4253, 7.1: if the first_kex_packet_follows flag was set in - * the received SSH_MSG_KEXINIT, but the guess was wrong, this - * field will be set such that the following guessed packet will - * be ignored. Once that packet has been received and ignored, - * this field is cleared. - */ - int first_kex_follows_guess_wrong; - - ssh_buffer in_hashbuf; - ssh_buffer out_hashbuf; - struct ssh_crypto_struct *current_crypto; - struct ssh_crypto_struct *next_crypto; /* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */ - - struct ssh_list *channels; /* linked list of channels */ - int maxchannel; - int exec_channel_opened; /* version 1 only. more - info in channels1.c */ - ssh_agent agent; /* ssh agent */ - -/* keyb interactive data */ - struct ssh_kbdint_struct *kbdint; - struct ssh_gssapi_struct *gssapi; - int version; /* 1 or 2 */ - /* server host keys */ - struct { - ssh_key rsa_key; - ssh_key dsa_key; - ssh_key ecdsa_key; - - /* The type of host key wanted by client */ - enum ssh_keytypes_e hostkey; - } srv; - /* auths accepted by server */ - int auth_methods; - struct ssh_list *ssh_message_list; /* list of delayed SSH messages */ - int (*ssh_message_callback)( struct ssh_session_struct *session, ssh_message msg, void *userdata); - void *ssh_message_callback_data; - ssh_server_callbacks server_callbacks; - void (*ssh_connection_callback)( struct ssh_session_struct *session); - struct ssh_packet_callbacks_struct default_packet_callbacks; - struct ssh_list *packet_callbacks; - struct ssh_socket_callbacks_struct socket_callbacks; - ssh_poll_ctx default_poll_ctx; - /* options */ -#ifdef WITH_PCAP - ssh_pcap_context pcap_ctx; /* pcap debugging context */ -#endif - struct { - struct ssh_list *identity; - char *username; - char *host; - char *bindaddr; /* bind the client to an ip addr */ - char *sshdir; - char *knownhosts; - char *wanted_methods[10]; - char *ProxyCommand; - char *custombanner; - unsigned long timeout; /* seconds */ - unsigned long timeout_usec; - unsigned int port; - socket_t fd; - int StrictHostKeyChecking; - int ssh2; - int ssh1; - char compressionlevel; - char *gss_server_identity; - char *gss_client_identity; - int gss_delegate_creds; - } opts; - /* counters */ - ssh_counter socket_counter; - ssh_counter raw_counter; -}; - -/** @internal - * @brief a termination function evaluates the status of an object - * @param user[in] object to evaluate - * @returns 1 if the polling routine should terminate, 0 instead - */ -typedef int (*ssh_termination_function)(void *user); -int ssh_handle_packets(ssh_session session, int timeout); -int ssh_handle_packets_termination(ssh_session session, int timeout, - ssh_termination_function fct, void *user); -void ssh_socket_exception_callback(int code, int errno_code, void *user); - -#endif /* SESSION_H_ */ diff --git a/libssh/include/libssh/sftp.h b/libssh/include/libssh/sftp.h deleted file mode 100644 index 8fb8f116..00000000 --- a/libssh/include/libssh/sftp.h +++ /dev/null @@ -1,1000 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * @defgroup libssh_sftp The libssh SFTP API - * - * @brief SFTP handling functions - * - * SFTP commands are channeled by the ssh sftp subsystem. Every packet is - * sent/read using a sftp_packet type structure. Related to these packets, - * most of the server answers are messages having an ID and a message - * specific part. It is described by sftp_message when reading a message, - * the sftp system puts it into the queue, so the process having asked for - * it can fetch it, while continuing to read for other messages (it is - * unspecified in which order messages may be sent back to the client - * - * @{ - */ - -#ifndef SFTP_H -#define SFTP_H - -#include - -#include "libssh.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef _WIN32 -#ifndef uid_t - typedef uint32_t uid_t; -#endif /* uid_t */ -#ifndef gid_t - typedef uint32_t gid_t; -#endif /* gid_t */ -#ifdef _MSC_VER -#ifndef ssize_t - typedef _W64 SSIZE_T ssize_t; -#endif /* ssize_t */ -#endif /* _MSC_VER */ -#endif /* _WIN32 */ - -#define LIBSFTP_VERSION 3 - -typedef struct sftp_attributes_struct* sftp_attributes; -typedef struct sftp_client_message_struct* sftp_client_message; -typedef struct sftp_dir_struct* sftp_dir; -typedef struct sftp_ext_struct *sftp_ext; -typedef struct sftp_file_struct* sftp_file; -typedef struct sftp_message_struct* sftp_message; -typedef struct sftp_packet_struct* sftp_packet; -typedef struct sftp_request_queue_struct* sftp_request_queue; -typedef struct sftp_session_struct* sftp_session; -typedef struct sftp_status_message_struct* sftp_status_message; -typedef struct sftp_statvfs_struct* sftp_statvfs_t; - -struct sftp_session_struct { - ssh_session session; - ssh_channel channel; - int server_version; - int client_version; - int version; - sftp_request_queue queue; - uint32_t id_counter; - int errnum; - void **handles; - sftp_ext ext; -}; - -struct sftp_packet_struct { - sftp_session sftp; - uint8_t type; - ssh_buffer payload; -}; - -/* file handler */ -struct sftp_file_struct { - sftp_session sftp; - char *name; - uint64_t offset; - ssh_string handle; - int eof; - int nonblocking; -}; - -struct sftp_dir_struct { - sftp_session sftp; - char *name; - ssh_string handle; /* handle to directory */ - ssh_buffer buffer; /* contains raw attributes from server which haven't been parsed */ - uint32_t count; /* counts the number of following attributes structures into buffer */ - int eof; /* end of directory listing */ -}; - -struct sftp_message_struct { - sftp_session sftp; - uint8_t packet_type; - ssh_buffer payload; - uint32_t id; -}; - -/* this is a bunch of all data that could be into a message */ -struct sftp_client_message_struct { - sftp_session sftp; - uint8_t type; - uint32_t id; - char *filename; /* can be "path" */ - uint32_t flags; - sftp_attributes attr; - ssh_string handle; - uint64_t offset; - uint32_t len; - int attr_num; - ssh_buffer attrbuf; /* used by sftp_reply_attrs */ - ssh_string data; /* can be newpath of rename() */ - ssh_buffer complete_message; /* complete message in case of retransmission*/ - char *str_data; /* cstring version of data */ -}; - -struct sftp_request_queue_struct { - sftp_request_queue next; - sftp_message message; -}; - -/* SSH_FXP_MESSAGE described into .7 page 26 */ -struct sftp_status_message_struct { - uint32_t id; - uint32_t status; - ssh_string error_unused; /* not used anymore */ - ssh_string lang_unused; /* not used anymore */ - char *errormsg; - char *langmsg; -}; - -struct sftp_attributes_struct { - char *name; - char *longname; /* ls -l output on openssh, not reliable else */ - uint32_t flags; - uint8_t type; - uint64_t size; - uint32_t uid; - uint32_t gid; - char *owner; /* set if openssh and version 4 */ - char *group; /* set if openssh and version 4 */ - uint32_t permissions; - uint64_t atime64; - uint32_t atime; - uint32_t atime_nseconds; - uint64_t createtime; - uint32_t createtime_nseconds; - uint64_t mtime64; - uint32_t mtime; - uint32_t mtime_nseconds; - ssh_string acl; - uint32_t extended_count; - ssh_string extended_type; - ssh_string extended_data; -}; - -/** - * @brief SFTP statvfs structure. - */ -struct sftp_statvfs_struct { - uint64_t f_bsize; /** file system block size */ - uint64_t f_frsize; /** fundamental fs block size */ - uint64_t f_blocks; /** number of blocks (unit f_frsize) */ - uint64_t f_bfree; /** free blocks in file system */ - uint64_t f_bavail; /** free blocks for non-root */ - uint64_t f_files; /** total file inodes */ - uint64_t f_ffree; /** free file inodes */ - uint64_t f_favail; /** free file inodes for to non-root */ - uint64_t f_fsid; /** file system id */ - uint64_t f_flag; /** bit mask of f_flag values */ - uint64_t f_namemax; /** maximum filename length */ -}; - -/** - * @brief Start a new sftp session. - * - * @param session The ssh session to use. - * - * @return A new sftp session or NULL on error. - * - * @see sftp_free() - */ -LIBSSH_API sftp_session sftp_new(ssh_session session); - -/** - * @brief Start a new sftp session with an existing channel. - * - * @param session The ssh session to use. - * @param channel An open session channel with subsystem already allocated - * - * @return A new sftp session or NULL on error. - * - * @see sftp_free() - */ -LIBSSH_API sftp_session sftp_new_channel(ssh_session session, ssh_channel channel); - - -/** - * @brief Close and deallocate a sftp session. - * - * @param sftp The sftp session handle to free. - */ -LIBSSH_API void sftp_free(sftp_session sftp); - -/** - * @brief Initialize the sftp session with the server. - * - * @param sftp The sftp session to initialize. - * - * @return 0 on success, < 0 on error with ssh error set. - * - * @see sftp_new() - */ -LIBSSH_API int sftp_init(sftp_session sftp); - -/** - * @brief Get the last sftp error. - * - * Use this function to get the latest error set by a posix like sftp function. - * - * @param sftp The sftp session where the error is saved. - * - * @return The saved error (see server responses), < 0 if an error - * in the function occured. - * - * @see Server responses - */ -LIBSSH_API int sftp_get_error(sftp_session sftp); - -/** - * @brief Get the count of extensions provided by the server. - * - * @param sftp The sftp session to use. - * - * @return The count of extensions provided by the server, 0 on error or - * not available. - */ -LIBSSH_API unsigned int sftp_extensions_get_count(sftp_session sftp); - -/** - * @brief Get the name of the extension provided by the server. - * - * @param sftp The sftp session to use. - * - * @param indexn The index number of the extension name you want. - * - * @return The name of the extension. - */ -LIBSSH_API const char *sftp_extensions_get_name(sftp_session sftp, unsigned int indexn); - -/** - * @brief Get the data of the extension provided by the server. - * - * This is normally the version number of the extension. - * - * @param sftp The sftp session to use. - * - * @param indexn The index number of the extension data you want. - * - * @return The data of the extension. - */ -LIBSSH_API const char *sftp_extensions_get_data(sftp_session sftp, unsigned int indexn); - -/** - * @brief Check if the given extension is supported. - * - * @param sftp The sftp session to use. - * - * @param name The name of the extension. - * - * @param data The data of the extension. - * - * @return 1 if supported, 0 if not. - * - * Example: - * - * @code - * sftp_extension_supported(sftp, "statvfs@openssh.com", "2"); - * @endcode - */ -LIBSSH_API int sftp_extension_supported(sftp_session sftp, const char *name, - const char *data); - -/** - * @brief Open a directory used to obtain directory entries. - * - * @param session The sftp session handle to open the directory. - * @param path The path of the directory to open. - * - * @return A sftp directory handle or NULL on error with ssh and - * sftp error set. - * - * @see sftp_readdir - * @see sftp_closedir - */ -LIBSSH_API sftp_dir sftp_opendir(sftp_session session, const char *path); - -/** - * @brief Get a single file attributes structure of a directory. - * - * @param session The sftp session handle to read the directory entry. - * @param dir The opened sftp directory handle to read from. - * - * @return A file attribute structure or NULL at the end of the - * directory. - * - * @see sftp_opendir() - * @see sftp_attribute_free() - * @see sftp_closedir() - */ -LIBSSH_API sftp_attributes sftp_readdir(sftp_session session, sftp_dir dir); - -/** - * @brief Tell if the directory has reached EOF (End Of File). - * - * @param dir The sftp directory handle. - * - * @return 1 if the directory is EOF, 0 if not. - * - * @see sftp_readdir() - */ -LIBSSH_API int sftp_dir_eof(sftp_dir dir); - -/** - * @brief Get information about a file or directory. - * - * @param session The sftp session handle. - * @param path The path to the file or directory to obtain the - * information. - * - * @return The sftp attributes structure of the file or directory, - * NULL on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API sftp_attributes sftp_stat(sftp_session session, const char *path); - -/** - * @brief Get information about a file or directory. - * - * Identical to sftp_stat, but if the file or directory is a symbolic link, - * then the link itself is stated, not the file that it refers to. - * - * @param session The sftp session handle. - * @param path The path to the file or directory to obtain the - * information. - * - * @return The sftp attributes structure of the file or directory, - * NULL on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API sftp_attributes sftp_lstat(sftp_session session, const char *path); - -/** - * @brief Get information about a file or directory from a file handle. - * - * @param file The sftp file handle to get the stat information. - * - * @return The sftp attributes structure of the file or directory, - * NULL on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API sftp_attributes sftp_fstat(sftp_file file); - -/** - * @brief Free a sftp attribute structure. - * - * @param file The sftp attribute structure to free. - */ -LIBSSH_API void sftp_attributes_free(sftp_attributes file); - -/** - * @brief Close a directory handle opened by sftp_opendir(). - * - * @param dir The sftp directory handle to close. - * - * @return Returns SSH_NO_ERROR or SSH_ERROR if an error occured. - */ -LIBSSH_API int sftp_closedir(sftp_dir dir); - -/** - * @brief Close an open file handle. - * - * @param file The open sftp file handle to close. - * - * @return Returns SSH_NO_ERROR or SSH_ERROR if an error occured. - * - * @see sftp_open() - */ -LIBSSH_API int sftp_close(sftp_file file); - -/** - * @brief Open a file on the server. - * - * @param session The sftp session handle. - * - * @param file The file to be opened. - * - * @param accesstype Is one of O_RDONLY, O_WRONLY or O_RDWR which request - * opening the file read-only,write-only or read/write. - * Acesss may also be bitwise-or'd with one or more of - * the following: - * O_CREAT - If the file does not exist it will be - * created. - * O_EXCL - When used with O_CREAT, if the file already - * exists it is an error and the open will fail. - * O_TRUNC - If the file already exists it will be - * truncated. - * - * @param mode Mode specifies the permissions to use if a new file is - * created. It is modified by the process's umask in - * the usual way: The permissions of the created file are - * (mode & ~umask) - * - * @return A sftp file handle, NULL on error with ssh and sftp - * error set. - * - * @see sftp_get_error() - */ -LIBSSH_API sftp_file sftp_open(sftp_session session, const char *file, int accesstype, - mode_t mode); - -/** - * @brief Make the sftp communication for this file handle non blocking. - * - * @param[in] handle The file handle to set non blocking. - */ -LIBSSH_API void sftp_file_set_nonblocking(sftp_file handle); - -/** - * @brief Make the sftp communication for this file handle blocking. - * - * @param[in] handle The file handle to set blocking. - */ -LIBSSH_API void sftp_file_set_blocking(sftp_file handle); - -/** - * @brief Read from a file using an opened sftp file handle. - * - * @param file The opened sftp file handle to be read from. - * - * @param buf Pointer to buffer to recieve read data. - * - * @param count Size of the buffer in bytes. - * - * @return Number of bytes written, < 0 on error with ssh and sftp - * error set. - * - * @see sftp_get_error() - */ -LIBSSH_API ssize_t sftp_read(sftp_file file, void *buf, size_t count); - -/** - * @brief Start an asynchronous read from a file using an opened sftp file handle. - * - * Its goal is to avoid the slowdowns related to the request/response pattern - * of a synchronous read. To do so, you must call 2 functions: - * - * sftp_async_read_begin() and sftp_async_read(). - * - * The first step is to call sftp_async_read_begin(). This function returns a - * request identifier. The second step is to call sftp_async_read() using the - * returned identifier. - * - * @param file The opened sftp file handle to be read from. - * - * @param len Size to read in bytes. - * - * @return An identifier corresponding to the sent request, < 0 on - * error. - * - * @warning When calling this function, the internal offset is - * updated corresponding to the len parameter. - * - * @warning A call to sftp_async_read_begin() sends a request to - * the server. When the server answers, libssh allocates - * memory to store it until sftp_async_read() is called. - * Not calling sftp_async_read() will lead to memory - * leaks. - * - * @see sftp_async_read() - * @see sftp_open() - */ -LIBSSH_API int sftp_async_read_begin(sftp_file file, uint32_t len); - -/** - * @brief Wait for an asynchronous read to complete and save the data. - * - * @param file The opened sftp file handle to be read from. - * - * @param data Pointer to buffer to recieve read data. - * - * @param len Size of the buffer in bytes. It should be bigger or - * equal to the length parameter of the - * sftp_async_read_begin() call. - * - * @param id The identifier returned by the sftp_async_read_begin() - * function. - * - * @return Number of bytes read, 0 on EOF, SSH_ERROR if an error - * occured, SSH_AGAIN if the file is opened in nonblocking - * mode and the request hasn't been executed yet. - * - * @warning A call to this function with an invalid identifier - * will never return. - * - * @see sftp_async_read_begin() - */ -LIBSSH_API int sftp_async_read(sftp_file file, void *data, uint32_t len, uint32_t id); - -/** - * @brief Write to a file using an opened sftp file handle. - * - * @param file Open sftp file handle to write to. - * - * @param buf Pointer to buffer to write data. - * - * @param count Size of buffer in bytes. - * - * @return Number of bytes written, < 0 on error with ssh and sftp - * error set. - * - * @see sftp_open() - * @see sftp_read() - * @see sftp_close() - */ -LIBSSH_API ssize_t sftp_write(sftp_file file, const void *buf, size_t count); - -/** - * @brief Seek to a specific location in a file. - * - * @param file Open sftp file handle to seek in. - * - * @param new_offset Offset in bytes to seek. - * - * @return 0 on success, < 0 on error. - */ -LIBSSH_API int sftp_seek(sftp_file file, uint32_t new_offset); - -/** - * @brief Seek to a specific location in a file. This is the - * 64bit version. - * - * @param file Open sftp file handle to seek in. - * - * @param new_offset Offset in bytes to seek. - * - * @return 0 on success, < 0 on error. - */ -LIBSSH_API int sftp_seek64(sftp_file file, uint64_t new_offset); - -/** - * @brief Report current byte position in file. - * - * @param file Open sftp file handle. - * - * @return The offset of the current byte relative to the beginning - * of the file associated with the file descriptor. < 0 on - * error. - */ -LIBSSH_API unsigned long sftp_tell(sftp_file file); - -/** - * @brief Report current byte position in file. - * - * @param file Open sftp file handle. - * - * @return The offset of the current byte relative to the beginning - * of the file associated with the file descriptor. < 0 on - * error. - */ -LIBSSH_API uint64_t sftp_tell64(sftp_file file); - -/** - * @brief Rewinds the position of the file pointer to the beginning of the - * file. - * - * @param file Open sftp file handle. - */ -LIBSSH_API void sftp_rewind(sftp_file file); - -/** - * @brief Unlink (delete) a file. - * - * @param sftp The sftp session handle. - * - * @param file The file to unlink/delete. - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_unlink(sftp_session sftp, const char *file); - -/** - * @brief Remove a directoy. - * - * @param sftp The sftp session handle. - * - * @param directory The directory to remove. - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_rmdir(sftp_session sftp, const char *directory); - -/** - * @brief Create a directory. - * - * @param sftp The sftp session handle. - * - * @param directory The directory to create. - * - * @param mode Specifies the permissions to use. It is modified by the - * process's umask in the usual way: - * The permissions of the created file are (mode & ~umask) - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode); - -/** - * @brief Rename or move a file or directory. - * - * @param sftp The sftp session handle. - * - * @param original The original url (source url) of file or directory to - * be moved. - * - * @param newname The new url (destination url) of the file or directory - * after the move. - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_rename(sftp_session sftp, const char *original, const char *newname); - -/** - * @brief Set file attributes on a file, directory or symbolic link. - * - * @param sftp The sftp session handle. - * - * @param file The file which attributes should be changed. - * - * @param attr The file attributes structure with the attributes set - * which should be changed. - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr); - -/** - * @brief Change the file owner and group - * - * @param sftp The sftp session handle. - * - * @param file The file which owner and group should be changed. - * - * @param owner The new owner which should be set. - * - * @param group The new group which should be set. - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_chown(sftp_session sftp, const char *file, uid_t owner, gid_t group); - -/** - * @brief Change permissions of a file - * - * @param sftp The sftp session handle. - * - * @param file The file which owner and group should be changed. - * - * @param mode Specifies the permissions to use. It is modified by the - * process's umask in the usual way: - * The permissions of the created file are (mode & ~umask) - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_chmod(sftp_session sftp, const char *file, mode_t mode); - -/** - * @brief Change the last modification and access time of a file. - * - * @param sftp The sftp session handle. - * - * @param file The file which owner and group should be changed. - * - * @param times A timeval structure which contains the desired access - * and modification time. - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_utimes(sftp_session sftp, const char *file, const struct timeval *times); - -/** - * @brief Create a symbolic link. - * - * @param sftp The sftp session handle. - * - * @param target Specifies the target of the symlink. - * - * @param dest Specifies the path name of the symlink to be created. - * - * @return 0 on success, < 0 on error with ssh and sftp error set. - * - * @see sftp_get_error() - */ -LIBSSH_API int sftp_symlink(sftp_session sftp, const char *target, const char *dest); - -/** - * @brief Read the value of a symbolic link. - * - * @param sftp The sftp session handle. - * - * @param path Specifies the path name of the symlink to be read. - * - * @return The target of the link, NULL on error. - * - * @see sftp_get_error() - */ -LIBSSH_API char *sftp_readlink(sftp_session sftp, const char *path); - -/** - * @brief Get information about a mounted file system. - * - * @param sftp The sftp session handle. - * - * @param path The pathname of any file within the mounted file system. - * - * @return A statvfs structure or NULL on error. - * - * @see sftp_get_error() - */ -LIBSSH_API sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path); - -/** - * @brief Get information about a mounted file system. - * - * @param file An opened file. - * - * @return A statvfs structure or NULL on error. - * - * @see sftp_get_error() - */ -LIBSSH_API sftp_statvfs_t sftp_fstatvfs(sftp_file file); - -/** - * @brief Free the memory of an allocated statvfs. - * - * @param statvfs_o The statvfs to free. - */ -LIBSSH_API void sftp_statvfs_free(sftp_statvfs_t statvfs_o); - -/** - * @brief Canonicalize a sftp path. - * - * @param sftp The sftp session handle. - * - * @param path The path to be canonicalized. - * - * @return The canonicalize path, NULL on error. - */ -LIBSSH_API char *sftp_canonicalize_path(sftp_session sftp, const char *path); - -/** - * @brief Get the version of the SFTP protocol supported by the server - * - * @param sftp The sftp session handle. - * - * @return The server version. - */ -LIBSSH_API int sftp_server_version(sftp_session sftp); - -#ifdef WITH_SERVER -/** - * @brief Create a new sftp server session. - * - * @param session The ssh session to use. - * - * @param chan The ssh channel to use. - * - * @return A new sftp server session. - */ -LIBSSH_API sftp_session sftp_server_new(ssh_session session, ssh_channel chan); - -/** - * @brief Intialize the sftp server. - * - * @param sftp The sftp session to init. - * - * @return 0 on success, < 0 on error. - */ -LIBSSH_API int sftp_server_init(sftp_session sftp); -#endif /* WITH_SERVER */ - -/* this is not a public interface */ -#define SFTP_HANDLES 256 -sftp_packet sftp_packet_read(sftp_session sftp); -int sftp_packet_write(sftp_session sftp,uint8_t type, ssh_buffer payload); -void sftp_packet_free(sftp_packet packet); -int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr); -sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf,int expectname); -/* sftpserver.c */ - -LIBSSH_API sftp_client_message sftp_get_client_message(sftp_session sftp); -LIBSSH_API void sftp_client_message_free(sftp_client_message msg); -LIBSSH_API uint8_t sftp_client_message_get_type(sftp_client_message msg); -LIBSSH_API const char *sftp_client_message_get_filename(sftp_client_message msg); -LIBSSH_API void sftp_client_message_set_filename(sftp_client_message msg, const char *newname); -LIBSSH_API const char *sftp_client_message_get_data(sftp_client_message msg); -LIBSSH_API uint32_t sftp_client_message_get_flags(sftp_client_message msg); -LIBSSH_API int sftp_send_client_message(sftp_session sftp, sftp_client_message msg); -int sftp_reply_name(sftp_client_message msg, const char *name, - sftp_attributes attr); -int sftp_reply_handle(sftp_client_message msg, ssh_string handle); -ssh_string sftp_handle_alloc(sftp_session sftp, void *info); -int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr); -void *sftp_handle(sftp_session sftp, ssh_string handle); -int sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message); -int sftp_reply_names_add(sftp_client_message msg, const char *file, - const char *longname, sftp_attributes attr); -int sftp_reply_names(sftp_client_message msg); -int sftp_reply_data(sftp_client_message msg, const void *data, int len); -void sftp_handle_remove(sftp_session sftp, void *handle); - -/* SFTP commands and constants */ -#define SSH_FXP_INIT 1 -#define SSH_FXP_VERSION 2 -#define SSH_FXP_OPEN 3 -#define SSH_FXP_CLOSE 4 -#define SSH_FXP_READ 5 -#define SSH_FXP_WRITE 6 -#define SSH_FXP_LSTAT 7 -#define SSH_FXP_FSTAT 8 -#define SSH_FXP_SETSTAT 9 -#define SSH_FXP_FSETSTAT 10 -#define SSH_FXP_OPENDIR 11 -#define SSH_FXP_READDIR 12 -#define SSH_FXP_REMOVE 13 -#define SSH_FXP_MKDIR 14 -#define SSH_FXP_RMDIR 15 -#define SSH_FXP_REALPATH 16 -#define SSH_FXP_STAT 17 -#define SSH_FXP_RENAME 18 -#define SSH_FXP_READLINK 19 -#define SSH_FXP_SYMLINK 20 - -#define SSH_FXP_STATUS 101 -#define SSH_FXP_HANDLE 102 -#define SSH_FXP_DATA 103 -#define SSH_FXP_NAME 104 -#define SSH_FXP_ATTRS 105 - -#define SSH_FXP_EXTENDED 200 -#define SSH_FXP_EXTENDED_REPLY 201 - -/* attributes */ -/* sftp draft is completely braindead : version 3 and 4 have different flags for same constants */ -/* and even worst, version 4 has same flag for 2 different constants */ -/* follow up : i won't develop any sftp4 compliant library before having a clarification */ - -#define SSH_FILEXFER_ATTR_SIZE 0x00000001 -#define SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004 -#define SSH_FILEXFER_ATTR_ACCESSTIME 0x00000008 -#define SSH_FILEXFER_ATTR_ACMODTIME 0x00000008 -#define SSH_FILEXFER_ATTR_CREATETIME 0x00000010 -#define SSH_FILEXFER_ATTR_MODIFYTIME 0x00000020 -#define SSH_FILEXFER_ATTR_ACL 0x00000040 -#define SSH_FILEXFER_ATTR_OWNERGROUP 0x00000080 -#define SSH_FILEXFER_ATTR_SUBSECOND_TIMES 0x00000100 -#define SSH_FILEXFER_ATTR_EXTENDED 0x80000000 -#define SSH_FILEXFER_ATTR_UIDGID 0x00000002 - -/* types */ -#define SSH_FILEXFER_TYPE_REGULAR 1 -#define SSH_FILEXFER_TYPE_DIRECTORY 2 -#define SSH_FILEXFER_TYPE_SYMLINK 3 -#define SSH_FILEXFER_TYPE_SPECIAL 4 -#define SSH_FILEXFER_TYPE_UNKNOWN 5 - -/** - * @name Server responses - * - * @brief Responses returned by the sftp server. - * @{ - */ - -/** No error */ -#define SSH_FX_OK 0 -/** End-of-file encountered */ -#define SSH_FX_EOF 1 -/** File doesn't exist */ -#define SSH_FX_NO_SUCH_FILE 2 -/** Permission denied */ -#define SSH_FX_PERMISSION_DENIED 3 -/** Generic failure */ -#define SSH_FX_FAILURE 4 -/** Garbage received from server */ -#define SSH_FX_BAD_MESSAGE 5 -/** No connection has been set up */ -#define SSH_FX_NO_CONNECTION 6 -/** There was a connection, but we lost it */ -#define SSH_FX_CONNECTION_LOST 7 -/** Operation not supported by the server */ -#define SSH_FX_OP_UNSUPPORTED 8 -/** Invalid file handle */ -#define SSH_FX_INVALID_HANDLE 9 -/** No such file or directory path exists */ -#define SSH_FX_NO_SUCH_PATH 10 -/** An attempt to create an already existing file or directory has been made */ -#define SSH_FX_FILE_ALREADY_EXISTS 11 -/** We are trying to write on a write-protected filesystem */ -#define SSH_FX_WRITE_PROTECT 12 -/** No media in remote drive */ -#define SSH_FX_NO_MEDIA 13 - -/** @} */ - -/* file flags */ -#define SSH_FXF_READ 0x01 -#define SSH_FXF_WRITE 0x02 -#define SSH_FXF_APPEND 0x04 -#define SSH_FXF_CREAT 0x08 -#define SSH_FXF_TRUNC 0x10 -#define SSH_FXF_EXCL 0x20 -#define SSH_FXF_TEXT 0x40 - -/* rename flags */ -#define SSH_FXF_RENAME_OVERWRITE 0x00000001 -#define SSH_FXF_RENAME_ATOMIC 0x00000002 -#define SSH_FXF_RENAME_NATIVE 0x00000004 - -#define SFTP_OPEN SSH_FXP_OPEN -#define SFTP_CLOSE SSH_FXP_CLOSE -#define SFTP_READ SSH_FXP_READ -#define SFTP_WRITE SSH_FXP_WRITE -#define SFTP_LSTAT SSH_FXP_LSTAT -#define SFTP_FSTAT SSH_FXP_FSTAT -#define SFTP_SETSTAT SSH_FXP_SETSTAT -#define SFTP_FSETSTAT SSH_FXP_FSETSTAT -#define SFTP_OPENDIR SSH_FXP_OPENDIR -#define SFTP_READDIR SSH_FXP_READDIR -#define SFTP_REMOVE SSH_FXP_REMOVE -#define SFTP_MKDIR SSH_FXP_MKDIR -#define SFTP_RMDIR SSH_FXP_RMDIR -#define SFTP_REALPATH SSH_FXP_REALPATH -#define SFTP_STAT SSH_FXP_STAT -#define SFTP_RENAME SSH_FXP_RENAME -#define SFTP_READLINK SSH_FXP_READLINK -#define SFTP_SYMLINK SSH_FXP_SYMLINK - -/* openssh flags */ -#define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */ -#define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */ - -#ifdef __cplusplus -} ; -#endif - -#endif /* SFTP_H */ - -/** @} */ -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/include/libssh/socket.h b/libssh/include/libssh/socket.h deleted file mode 100644 index 8e1eac21..00000000 --- a/libssh/include/libssh/socket.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef SOCKET_H_ -#define SOCKET_H_ - -#include "libssh/callbacks.h" -struct ssh_poll_handle_struct; -/* socket.c */ - -struct ssh_socket_struct; -typedef struct ssh_socket_struct* ssh_socket; - -int ssh_socket_init(void); -void ssh_socket_cleanup(void); -ssh_socket ssh_socket_new(ssh_session session); -void ssh_socket_reset(ssh_socket s); -void ssh_socket_free(ssh_socket s); -void ssh_socket_set_fd(ssh_socket s, socket_t fd); -socket_t ssh_socket_get_fd_in(ssh_socket s); -#ifndef _WIN32 -int ssh_socket_unix(ssh_socket s, const char *path); -void ssh_execute_command(const char *command, socket_t in, socket_t out); -int ssh_socket_connect_proxycommand(ssh_socket s, const char *command); -#endif -void ssh_socket_close(ssh_socket s); -int ssh_socket_write(ssh_socket s,const void *buffer, int len); -int ssh_socket_is_open(ssh_socket s); -int ssh_socket_fd_isset(ssh_socket s, fd_set *set); -void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd); -void ssh_socket_set_fd_in(ssh_socket s, socket_t fd); -void ssh_socket_set_fd_out(ssh_socket s, socket_t fd); -int ssh_socket_nonblocking_flush(ssh_socket s); -void ssh_socket_set_write_wontblock(ssh_socket s); -void ssh_socket_set_read_wontblock(ssh_socket s); -void ssh_socket_set_except(ssh_socket s); -int ssh_socket_get_status(ssh_socket s); -int ssh_socket_get_poll_flags(ssh_socket s); -int ssh_socket_buffered_write_bytes(ssh_socket s); -int ssh_socket_data_available(ssh_socket s); -int ssh_socket_data_writable(ssh_socket s); -int ssh_socket_set_nonblocking(socket_t fd); -int ssh_socket_set_blocking(socket_t fd); - -void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks); -int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s); -struct ssh_poll_handle_struct * ssh_socket_get_poll_handle_in(ssh_socket s); -struct ssh_poll_handle_struct * ssh_socket_get_poll_handle_out(ssh_socket s); - -int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr); - -#endif /* SOCKET_H_ */ diff --git a/libssh/include/libssh/ssh1.h b/libssh/include/libssh/ssh1.h deleted file mode 100644 index ce67f20b..00000000 --- a/libssh/include/libssh/ssh1.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef __SSH1_H -#define __SSH1_H - -#define SSH_MSG_NONE 0 /* no message */ -#define SSH_MSG_DISCONNECT 1 /* cause (string) */ -#define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ -#define SSH_CMSG_SESSION_KEY 3 /* key (BIGNUM) */ -#define SSH_CMSG_USER 4 /* user (string) */ -#define SSH_CMSG_AUTH_RHOSTS 5 /* user (string) */ -#define SSH_CMSG_AUTH_RSA 6 /* modulus (BIGNUM) */ -#define SSH_SMSG_AUTH_RSA_CHALLENGE 7 /* int (BIGNUM) */ -#define SSH_CMSG_AUTH_RSA_RESPONSE 8 /* int (BIGNUM) */ -#define SSH_CMSG_AUTH_PASSWORD 9 /* pass (string) */ -#define SSH_CMSG_REQUEST_PTY 10 /* TERM, tty modes */ -#define SSH_CMSG_WINDOW_SIZE 11 /* row,col,xpix,ypix */ -#define SSH_CMSG_EXEC_SHELL 12 /* */ -#define SSH_CMSG_EXEC_CMD 13 /* cmd (string) */ -#define SSH_SMSG_SUCCESS 14 /* */ -#define SSH_SMSG_FAILURE 15 /* */ -#define SSH_CMSG_STDIN_DATA 16 /* data (string) */ -#define SSH_SMSG_STDOUT_DATA 17 /* data (string) */ -#define SSH_SMSG_STDERR_DATA 18 /* data (string) */ -#define SSH_CMSG_EOF 19 /* */ -#define SSH_SMSG_EXITSTATUS 20 /* status (int) */ -#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* channel (int) */ -#define SSH_MSG_CHANNEL_OPEN_FAILURE 22 /* channel (int) */ -#define SSH_MSG_CHANNEL_DATA 23 /* ch,data (int,str) */ -#define SSH_MSG_CHANNEL_CLOSE 24 /* channel (int) */ -#define SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* channel (int) */ -/* SSH_CMSG_X11_REQUEST_FORWARDING 26 OBSOLETE */ -#define SSH_SMSG_X11_OPEN 27 /* channel (int) */ -#define SSH_CMSG_PORT_FORWARD_REQUEST 28 /* p,host,hp (i,s,i) */ -#define SSH_MSG_PORT_OPEN 29 /* ch,h,p (i,s,i) */ -#define SSH_CMSG_AGENT_REQUEST_FORWARDING 30 /* */ -#define SSH_SMSG_AGENT_OPEN 31 /* port (int) */ -#define SSH_MSG_IGNORE 32 /* string */ -#define SSH_CMSG_EXIT_CONFIRMATION 33 /* */ -#define SSH_CMSG_X11_REQUEST_FORWARDING 34 /* proto,data (s,s) */ -#define SSH_CMSG_AUTH_RHOSTS_RSA 35 /* user,mod (s,mpi) */ -#define SSH_MSG_DEBUG 36 /* string */ -#define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */ -#define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k (int) */ -#define SSH_CMSG_AUTH_TIS 39 /* we use this for s/key */ -#define SSH_SMSG_AUTH_TIS_CHALLENGE 40 /* challenge (string) */ -#define SSH_CMSG_AUTH_TIS_RESPONSE 41 /* response (string) */ -#define SSH_CMSG_AUTH_KERBEROS 42 /* (KTEXT) */ -#define SSH_SMSG_AUTH_KERBEROS_RESPONSE 43 /* (KTEXT) */ -#define SSH_CMSG_HAVE_KERBEROS_TGT 44 /* credentials (s) */ -#define SSH_CMSG_HAVE_AFS_TOKEN 65 /* token (s) */ - -/* protocol version 1.5 overloads some version 1.3 message types */ -#define SSH_MSG_CHANNEL_INPUT_EOF SSH_MSG_CHANNEL_CLOSE -#define SSH_MSG_CHANNEL_OUTPUT_CLOSE SSH_MSG_CHANNEL_CLOSE_CONFIRMATION - -/* - * Authentication methods. New types can be added, but old types should not - * be removed for compatibility. The maximum allowed value is 31. - */ -#define SSH_AUTH_RHOSTS 1 -#define SSH_AUTH_RSA 2 -#define SSH_AUTH_PASSWORD 3 -#define SSH_AUTH_RHOSTS_RSA 4 -#define SSH_AUTH_TIS 5 -#define SSH_AUTH_KERBEROS 6 -#define SSH_PASS_KERBEROS_TGT 7 - /* 8 to 15 are reserved */ -#define SSH_PASS_AFS_TOKEN 21 - -/* Protocol flags. These are bit masks. */ -#define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */ -#define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */ - -/* cipher flags. they are bit numbers */ -#define SSH_CIPHER_NONE 0 /* No encryption */ -#define SSH_CIPHER_IDEA 1 /* IDEA in CFB mode */ -#define SSH_CIPHER_DES 2 /* DES in CBC mode */ -#define SSH_CIPHER_3DES 3 /* Triple-DES in CBC mode */ -#define SSH_CIPHER_RC4 5 /* RC4 */ -#define SSH_CIPHER_BLOWFISH 6 - -#endif - diff --git a/libssh/include/libssh/ssh2.h b/libssh/include/libssh/ssh2.h deleted file mode 100644 index 8b39b9a6..00000000 --- a/libssh/include/libssh/ssh2.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef __SSH2_H -#define __SSH2_H - -#define SSH2_MSG_DISCONNECT 1 -#define SSH2_MSG_IGNORE 2 -#define SSH2_MSG_UNIMPLEMENTED 3 -#define SSH2_MSG_DEBUG 4 -#define SSH2_MSG_SERVICE_REQUEST 5 -#define SSH2_MSG_SERVICE_ACCEPT 6 - -#define SSH2_MSG_KEXINIT 20 -#define SSH2_MSG_NEWKEYS 21 - -#define SSH2_MSG_KEXDH_INIT 30 -#define SSH2_MSG_KEXDH_REPLY 31 -#define SSH2_MSG_KEX_ECDH_INIT 30 -#define SSH2_MSG_KEX_ECDH_REPLY 31 -#define SSH2_MSG_ECMQV_INIT 30 -#define SSH2_MSG_ECMQV_REPLY 31 - -#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 -#define SSH2_MSG_KEX_DH_GEX_GROUP 31 -#define SSH2_MSG_KEX_DH_GEX_INIT 32 -#define SSH2_MSG_KEX_DH_GEX_REPLY 33 -#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 -#define SSH2_MSG_USERAUTH_REQUEST 50 -#define SSH2_MSG_USERAUTH_FAILURE 51 -#define SSH2_MSG_USERAUTH_SUCCESS 52 -#define SSH2_MSG_USERAUTH_BANNER 53 -#define SSH2_MSG_USERAUTH_PK_OK 60 -#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 -#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 -#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 -#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 -#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 -#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 -#define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 -#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 -#define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 - -#define SSH2_MSG_GLOBAL_REQUEST 80 -#define SSH2_MSG_REQUEST_SUCCESS 81 -#define SSH2_MSG_REQUEST_FAILURE 82 -#define SSH2_MSG_CHANNEL_OPEN 90 -#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 -#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 -#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 -#define SSH2_MSG_CHANNEL_DATA 94 -#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 -#define SSH2_MSG_CHANNEL_EOF 96 -#define SSH2_MSG_CHANNEL_CLOSE 97 -#define SSH2_MSG_CHANNEL_REQUEST 98 -#define SSH2_MSG_CHANNEL_SUCCESS 99 -#define SSH2_MSG_CHANNEL_FAILURE 100 - -#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 -#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 -#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 -#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 -#define SSH2_DISCONNECT_RESERVED 4 -#define SSH2_DISCONNECT_MAC_ERROR 5 -#define SSH2_DISCONNECT_COMPRESSION_ERROR 6 -#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 -#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 -#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 -#define SSH2_DISCONNECT_CONNECTION_LOST 10 -#define SSH2_DISCONNECT_BY_APPLICATION 11 -#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 -#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 -#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 -#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 - -#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 -#define SSH2_OPEN_CONNECT_FAILED 2 -#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 -#define SSH2_OPEN_RESOURCE_SHORTAGE 4 - -#define SSH2_EXTENDED_DATA_STDERR 1 - -#endif diff --git a/libssh/include/libssh/string.h b/libssh/include/libssh/string.h deleted file mode 100644 index 8c7db1df..00000000 --- a/libssh/include/libssh/string.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef STRING_H_ -#define STRING_H_ -#include "libssh/priv.h" - -/* must be 32 bits number + immediately our data */ -#ifdef _MSC_VER -#pragma pack(1) -#endif -struct ssh_string_struct { - uint32_t size; - unsigned char data[1]; -} -#if defined(__GNUC__) -__attribute__ ((packed)) -#endif -#ifdef _MSC_VER -#pragma pack() -#endif -; - -#endif /* STRING_H_ */ diff --git a/libssh/include/libssh/threads.h b/libssh/include/libssh/threads.h deleted file mode 100644 index 70072648..00000000 --- a/libssh/include/libssh/threads.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef THREADS_H_ -#define THREADS_H_ - -#include -#include - -int ssh_threads_init(void); -void ssh_threads_finalize(void); -const char *ssh_threads_get_type(void); - -#endif /* THREADS_H_ */ diff --git a/libssh/include/libssh/wrapper.h b/libssh/include/libssh/wrapper.h deleted file mode 100644 index a327e188..00000000 --- a/libssh/include/libssh/wrapper.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef WRAPPER_H_ -#define WRAPPER_H_ - -#include "config.h" -#include "libssh/libssh.h" -#include "libssh/libcrypto.h" -#include "libssh/libgcrypt.h" - -enum ssh_mac_e { - SSH_MAC_SHA1=1, - SSH_MAC_SHA256, - SSH_MAC_SHA384, - SSH_MAC_SHA512 -}; - -enum ssh_hmac_e { - SSH_HMAC_SHA1 = 1, - SSH_HMAC_SHA256, - SSH_HMAC_SHA384, - SSH_HMAC_SHA512, - SSH_HMAC_MD5 -}; - -enum ssh_des_e { - SSH_3DES, - SSH_DES -}; - -struct ssh_hmac_struct { - const char* name; - enum ssh_hmac_e hmac_type; -}; - -typedef struct ssh_mac_ctx_struct *ssh_mac_ctx; -MD5CTX md5_init(void); -void md5_update(MD5CTX c, const void *data, unsigned long len); -void md5_final(unsigned char *md,MD5CTX c); - -SHACTX sha1_init(void); -void sha1_update(SHACTX c, const void *data, unsigned long len); -void sha1_final(unsigned char *md,SHACTX c); -void sha1(unsigned char *digest,int len,unsigned char *hash); - -SHA256CTX sha256_init(void); -void sha256_update(SHA256CTX c, const void *data, unsigned long len); -void sha256_final(unsigned char *md,SHA256CTX c); -void sha256(unsigned char *digest, int len, unsigned char *hash); - -SHA384CTX sha384_init(void); -void sha384_update(SHA384CTX c, const void *data, unsigned long len); -void sha384_final(unsigned char *md,SHA384CTX c); -void sha384(unsigned char *digest, int len, unsigned char *hash); - -SHA512CTX sha512_init(void); -void sha512_update(SHA512CTX c, const void *data, unsigned long len); -void sha512_final(unsigned char *md,SHA512CTX c); -void sha512(unsigned char *digest, int len, unsigned char *hash); - -void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen); -EVPCTX evp_init(int nid); -void evp_update(EVPCTX ctx, const void *data, unsigned long len); -void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen); - -ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type); -void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len); -void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx); - -HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type); -void hmac_update(HMACCTX c, const void *data, unsigned long len); -void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len); -size_t hmac_digest_len(enum ssh_hmac_e type); - -int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type); -int crypt_set_algorithms_server(ssh_session session); -struct ssh_crypto_struct *crypto_new(void); -void crypto_free(struct ssh_crypto_struct *crypto); - -void ssh_reseed(void); - -struct ssh_hmac_struct *ssh_get_hmactab(void); -const char *ssh_hmac_type_to_string(enum ssh_hmac_e hmac_type); - -#endif /* WRAPPER_H_ */ diff --git a/libssh/libssh-build-tree-settings.cmake.in b/libssh/libssh-build-tree-settings.cmake.in deleted file mode 100644 index 16b406aa..00000000 --- a/libssh/libssh-build-tree-settings.cmake.in +++ /dev/null @@ -1 +0,0 @@ -set(LIBSSH_INLUDE_DIR @PROJECT_SOURCE_DIR@/include) diff --git a/libssh/libssh-config-version.cmake.in b/libssh/libssh-config-version.cmake.in deleted file mode 100644 index 98f292c0..00000000 --- a/libssh/libssh-config-version.cmake.in +++ /dev/null @@ -1,11 +0,0 @@ -set(PACKAGE_VERSION @APPLICATION_VERSION@) - -# Check whether the requested PACKAGE_FIND_VERSION is compatible -if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_COMPATIBLE FALSE) -else() - set(PACKAGE_VERSION_COMPATIBLE TRUE) - if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_EXACT TRUE) - endif() -endif() diff --git a/libssh/libssh-config.cmake.in b/libssh/libssh-config.cmake.in deleted file mode 100644 index fa9cecf8..00000000 --- a/libssh/libssh-config.cmake.in +++ /dev/null @@ -1,13 +0,0 @@ -get_filename_component(LIBSSH_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -if (EXISTS "${LIBSSH_CMAKE_DIR}/CMakeCache.txt") - # In build tree - include(${LIBSSH_CMAKE_DIR}/libssh-build-tree-settings.cmake) -else() - set(LIBSSH_INCLUDE_DIR @INCLUDE_INSTALL_DIR@) -endif() - -set(LIBSSH_LIBRARY @LIB_INSTALL_DIR@/@LIBSSH_LIBRARY_NAME@) -set(LIBSSH_LIBRARIES @LIB_INSTALL_DIR@/@LIBSSH_LIBRARY_NAME@) - -set(LIBSSH_THREADS_LIBRARY @LIB_INSTALL_DIR@/@LIBSSH_THREADS_LIBRARY_NAME@) diff --git a/libssh/libssh.pc.cmake b/libssh/libssh.pc.cmake deleted file mode 100644 index 3b1cb8dc..00000000 --- a/libssh/libssh.pc.cmake +++ /dev/null @@ -1,6 +0,0 @@ -Name: ${APPLICATION_NAME} -Description: The SSH Library -Version: ${APPLICATION_VERSION} -Libs: -L${LIB_INSTALL_DIR} -lssh -Cflags: -I${INCLUDE_INSTALL_DIR} - diff --git a/libssh/libssh_threads.pc.cmake b/libssh/libssh_threads.pc.cmake deleted file mode 100644 index 5479c34f..00000000 --- a/libssh/libssh_threads.pc.cmake +++ /dev/null @@ -1,6 +0,0 @@ -Name: ${APPLICATION_NAME}_threads -Description: The SSH Library Thread Extension -Version: ${APPLICATION_VERSION} -Libs: -L${LIB_INSTALL_DIR} -lssh_threads -Cflags: -I${INCLUDE_INSTALL_DIR} - diff --git a/libssh/obj/build_make.sh b/libssh/obj/build_make.sh deleted file mode 100755 index 2f2e4a6c..00000000 --- a/libssh/obj/build_make.sh +++ /dev/null @@ -1,197 +0,0 @@ -#!/bin/bash -# -# Last Change: 2008-06-18 14:13:46 -# -# Script to build libssh on UNIX. -# -# Copyright (c) 2006-2007 Andreas Schneider -# - -SOURCE_DIR=".." - -LANG=C -export LANG - -SCRIPT="$0" -COUNT=0 -while [ -L "${SCRIPT}" ] -do - SCRIPT=$(readlink ${SCRIPT}) - COUNT=$(expr ${COUNT} + 1) - if [ ${COUNT} -gt 100 ]; then - echo "Too many symbolic links" - exit 1 - fi -done -BUILDDIR=$(dirname ${SCRIPT}) - -cleanup_and_exit () { - if test "$1" = 0 -o -z "$1" ; then - exit 0 - else - exit $1 - fi -} - -function configure() { - if [ -n "${CMAKEDIR}" ]; then - ${CMAKEDIR}/bin/cmake "$@" ${SOURCE_DIR} || cleanup_and_exit $? - else - cmake "$@" ${SOURCE_DIR} || cleanup_and_exit $? - fi -} - -function compile() { - if [ -f /proc/cpuinfo ]; then - CPUCOUNT=$(grep -c processor /proc/cpuinfo) - elif test `uname` = "SunOS" ; then - CPUCOUNT=$(psrinfo -p) - else - CPUCOUNT="1" - fi - - if [ "${CPUCOUNT}" -gt "1" ]; then - ${MAKE} -j${CPUCOUNT} $1 || cleanup_and_exit $? - else - ${MAKE} $1 || exit $? - fi -} - -function clean_build_dir() { - find ! -path "*.svn*" ! -name "*.bat" ! -name "*.sh" ! -name "." -print0 | xargs -0 rm -rf -} - -function usage () { -echo "Usage: `basename $0` [--prefix /install_prefix|--build [debug|final]|--clean|--verbose|--libsuffix (32|64)|--help|--clang|--cmakedir /directory|--make -(gmake|make)|--ccompiler (gcc|cc)|--withstaticlib|--unittesting|--clientunittesting|--withssh1|--withserver]" - cleanup_and_exit -} - -cd ${BUILDDIR} - -# the default CMake options: -OPTIONS="--graphviz=${BUILDDIR}/libssh.dot" - -# the default 'make' utility: -MAKE="make" - -while test -n "$1"; do - PARAM="$1" - ARG="$2" - shift - case ${PARAM} in - *-*=*) - ARG=${PARAM#*=} - PARAM=${PARAM%%=*} - set -- "----noarg=${PARAM}" "$@" - esac - case ${PARAM} in - *-help|-h) - #echo_help - usage - cleanup_and_exit - ;; - *-build) - DOMAKE="1" - BUILD_TYPE="${ARG}" - test -n "${BUILD_TYPE}" && shift - ;; - *-clean) - clean_build_dir - cleanup_and_exit - ;; - *-clang) - OPTIONS="${OPTIONS} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++" - ;; - *-verbose) - DOVERBOSE="1" - ;; - *-memtest) - OPTIONS="${OPTIONS} -DMEM_NULL_TESTS=ON" - ;; - *-libsuffix) - OPTIONS="${OPTIONS} -DLIB_SUFFIX=${ARG}" - shift - ;; - *-prefix) - OPTIONS="${OPTIONS} -DCMAKE_INSTALL_PREFIX=${ARG}" - shift - ;; - *-sysconfdir) - OPTIONS="${OPTIONS} -DSYSCONF_INSTALL_DIR=${ARG}" - shift - ;; - *-cmakedir) - CMAKEDIR="${ARG}" - shift - ;; - *-make) - MAKE="${ARG}" - shift - ;; - *-ccompiler) - OPTIONS="${OPTIONS} -DCMAKE_C_COMPILER=${ARG}" - shift - ;; - *-withstaticlib) - OPTIONS="${OPTIONS} -DWITH_STATIC_LIB=ON" - ;; - *-unittesting) - OPTIONS="${OPTIONS} -DWITH_TESTING=ON" - ;; - *-clientunittesting) - OPTIONS="${OPTIONS} -DWITH_CLIENT_TESTING=ON" - ;; - *-withssh1) - OPTIONS="${OPTIONS} -DWITH_SSH1=ON" - ;; - *-withserver) - OPTIONS="${OPTIONS} -DWITH_SERVER=ON" - ;; - ----noarg) - echo "$ARG does not take an argument" - cleanup_and_exit - ;; - -*) - echo Unknown Option "$PARAM". Exit. - cleanup_and_exit 1 - ;; - *) - usage - ;; - esac -done - -if [ "${DOMAKE}" == "1" ]; then - OPTIONS="${OPTIONS} -DCMAKE_BUILD_TYPE=${BUILD_TYPE}" -fi - -if [ -n "${DOVERBOSE}" ]; then - OPTIONS="${OPTIONS} -DCMAKE_VERBOSE_MAKEFILE=1" -else - OPTIONS="${OPTIONS} -DCMAKE_VERBOSE_MAKEFILE=0" -fi - -test -f "${BUILDDIR}/.build.log" && rm -f ${BUILDDIR}/.build.log -touch ${BUILDDIR}/.build.log -# log everything from here to .build.log -exec 1> >(exec -a 'build logging tee' tee -a ${BUILDDIR}/.build.log) 2>&1 -echo "${HOST} started build at $(date)." -echo - -configure ${OPTIONS} "$@" - -if [ -n "${DOMAKE}" ]; then - test -n "${DOVERBOSE}" && compile VERBOSE=1 || compile -fi - -DOT=$(which dot 2>/dev/null) -if [ -n "${DOT}" ]; then - ${DOT} -Tpng -o${BUILDDIR}/libssh.png ${BUILDDIR}/libssh.dot - ${DOT} -Tsvg -o${BUILDDIR}/libssh.svg ${BUILDDIR}/libssh.dot -fi - -exec >&0 2>&0 # so that the logging tee finishes -sleep 1 # wait till tee terminates - -cleanup_and_exit 0 diff --git a/libssh/src/CMakeLists.txt b/libssh/src/CMakeLists.txt deleted file mode 100644 index a4bc8595..00000000 --- a/libssh/src/CMakeLists.txt +++ /dev/null @@ -1,299 +0,0 @@ -project(libssh-library C) - -set(LIBSSH_PUBLIC_INCLUDE_DIRS - ${CMAKE_SOURCE_DIR}/include - CACHE INTERNAL "libssh public include directories" -) - -set(LIBSSH_PRIVATE_INCLUDE_DIRS - ${CMAKE_BINARY_DIR} - ${OPENSSL_INCLUDE_DIRS} -) - -set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_REQUIRED_LIBRARIES} -) - -if (WIN32) - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - ws2_32 - ) -endif (WIN32) - -if (HAVE_LIBSOCKET) - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - socket - ) -endif (HAVE_LIBSOCKET) - -if (OPENSSL_CRYPTO_LIBRARIES) - set(LIBSSH_PRIVATE_INCLUDE_DIRS - ${LIBSSH_PRIVATE_INCLUDE_DIRS} - ${OPENSSL_INCLUDE_DIRS} - ) - - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - ${OPENSSL_CRYPTO_LIBRARIES} - ) -endif (OPENSSL_CRYPTO_LIBRARIES) - -if (GCRYPT_LIBRARY) - set(LIBSSH_PRIVATE_INCLUDE_DIRS - ${LIBSSH_PRIVATE_INCLUDE_DIRS} - ${GCRYPT_INCLUDE_DIR} - ) - - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - ${GCRYPT_LIBRARY} - ) -endif (GCRYPT_LIBRARY) - -if (WITH_ZLIB) - set(LIBSSH_PRIVATE_INCLUDE_DIRS - ${LIBSSH_PRIVATE_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS} - ) - - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - ${ZLIB_LIBRARY} - ) -endif (WITH_ZLIB) - -if (WITH_GSSAPI AND GSSAPI_FOUND) - set(LIBSSH_PRIVATE_INCLUDE_DIRS - ${LIBSSH_PRIVATE_INCLUDE_DIRS} - ${GSSAPI_INCLUDE_DIR} - ) - - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - ${GSSAPI_LIBRARIES} - ) -endif (WITH_GSSAPI AND GSSAPI_FOUND) - -if (WITH_NACL AND NACL_FOUND) - set(LIBSSH_PRIVATE_INCLUDE_DIRS - ${LIBSSH_PRIVATE_INCLUDE_DIRS} - ${NACL_INCLUDE_DIR} - ) - - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - ${NACL_LIBRARY} - ) -endif (WITH_NACL AND NACL_FOUND) - -set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - CACHE INTERNAL "libssh link libraries" -) - -set(LIBSSH_SHARED_LIBRARY - ssh_shared - CACHE INTERNAL "libssh shared library" -) - -if (WITH_STATIC_LIB) - set(LIBSSH_STATIC_LIBRARY - ssh_static - CACHE INTERNAL "libssh static library" - ) -endif (WITH_STATIC_LIB) - -set(libssh_SRCS - agent.c - auth.c - base64.c - bignum.c - buffer.c - callbacks.c - channels.c - client.c - config.c - connect.c - curve25519.c - dh.c - ecdh.c - ed25519.c - error.c - fe25519.c - ge25519.c - getpass.c - init.c - kex.c - known_hosts.c - legacy.c - libcrypto.c - log.c - match.c - messages.c - misc.c - options.c - packet.c - packet_cb.c - packet_crypt.c - pcap.c - pki.c - pki_ed25519.c - poll.c - session.c - sc25519.c - scp.c - socket.c - string.c - threads.c - wrapper.c -) - -if (WITH_GCRYPT) - set(libssh_SRCS - ${libssh_SRCS} - libgcrypt.c - gcrypt_missing.c - pki_gcrypt.c - ) -else (WITH_GCRYPT) - set(libssh_SRCS - ${libssh_SRCS} - pki_crypto.c - ) -endif (WITH_GCRYPT) - -if (WITH_SFTP) - set(libssh_SRCS - ${libssh_SRCS} - sftp.c - ) - - if (WITH_SERVER) - set(libssh_SRCS - ${libssh_SRCS} - sftpserver.c - ) - endif (WITH_SERVER) -endif (WITH_SFTP) - -if (WITH_SSH1) - set(libssh_SRCS - ${libssh_SRCS} - auth1.c - channels1.c - crc32.c - kex1.c - packet1.c - ) -endif (WITH_SSH1) - -if (WITH_SERVER) - set(libssh_SRCS - ${libssh_SRCS} - server.c - bind.c - ) -endif (WITH_SERVER) - -if (WITH_ZLIB) - set(libssh_SRCS - ${libssh_SRCS} - gzip.c - ) -endif(WITH_ZLIB) - -if (WITH_GSSAPI AND GSSAPI_FOUND) - set(libssh_SRCS - ${libssh_SRCS} - gssapi.c - ) -endif (WITH_GSSAPI AND GSSAPI_FOUND) - -if (NOT WITH_NACL) - set(libssh_SRCS - ${libssh_SRCS} - curve25519_ref.c - ) -endif (NOT WITH_NACL) - -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${LIBSSH_PRIVATE_INCLUDE_DIRS} -) - -add_library(${LIBSSH_SHARED_LIBRARY} SHARED ${libssh_SRCS}) - -target_link_libraries(${LIBSSH_SHARED_LIBRARY} ${LIBSSH_LINK_LIBRARIES}) - -set_target_properties( - ${LIBSSH_SHARED_LIBRARY} - PROPERTIES - VERSION - ${LIBRARY_VERSION} - SOVERSION - ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh - DEFINE_SYMBOL - LIBSSH_EXPORTS -) - -if (WITH_VISIBILITY_HIDDEN) - set_target_properties(${LIBSSH_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") -endif (WITH_VISIBILITY_HIDDEN) - - -install( - TARGETS - ${LIBSSH_SHARED_LIBRARY} - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - COMPONENT libraries -) - -if (WITH_STATIC_LIB) - add_library(${LIBSSH_STATIC_LIBRARY} STATIC ${libssh_SRCS}) - - if (MSVC) - set(OUTPUT_SUFFIX static) - else (MSVC) - set(OUTPUT_SUFFIX ) - endif (MSVC) - set_target_properties( - ${LIBSSH_STATIC_LIBRARY} - PROPERTIES - VERSION - ${LIBRARY_VERSION} - SOVERSION - ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh - ARCHIVE_OUTPUT_DIRECTORY - ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX} - ) - - if (WIN32) - set_target_properties( - ${LIBSSH_STATIC_LIBRARY} - PROPERTIES - COMPILE_FLAGS - "-DLIBSSH_STATIC" - ) - endif (WIN32) - - install( - TARGETS - ${LIBSSH_STATIC_LIBRARY} - DESTINATION - ${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX} - COMPONENT - libraries - ) -endif (WITH_STATIC_LIB) - -if (Threads_FOUND) - add_subdirectory(threads) -endif (Threads_FOUND) diff --git a/libssh/src/agent.c b/libssh/src/agent.c deleted file mode 100644 index d5257604..00000000 --- a/libssh/src/agent.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - * agent.c - ssh agent functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2008-2013 by Andreas Schneider - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* This file is based on authfd.c from OpenSSH */ - -/* - * How does the ssh-agent work? - * - * a) client sends a request to get a list of all keys - * the agent returns the count and all public keys - * b) iterate over them to check if the server likes one - * c) the client sends a sign request to the agent - * type, pubkey as blob, data to sign, flags - * the agent returns the signed data - */ - -#ifndef _WIN32 - -#include "config.h" - -#include -#include -#include -#include - -#ifdef HAVE_UNISTD_H -#include -#endif - -#include -#include - -#include "libssh/agent.h" -#include "libssh/priv.h" -#include "libssh/socket.h" -#include "libssh/buffer.h" -#include "libssh/session.h" -#include "libssh/poll.h" -#include "libssh/pki.h" - -/* macro to check for "agent failure" message */ -#define agent_failed(x) \ - (((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \ - ((x) == SSH2_AGENT_FAILURE)) - -static uint32_t agent_get_u32(const void *vp) { - const uint8_t *p = (const uint8_t *)vp; - uint32_t v; - - v = (uint32_t)p[0] << 24; - v |= (uint32_t)p[1] << 16; - v |= (uint32_t)p[2] << 8; - v |= (uint32_t)p[3]; - - return v; -} - -static void agent_put_u32(void *vp, uint32_t v) { - uint8_t *p = (uint8_t *)vp; - - p[0] = (uint8_t)(v >> 24) & 0xff; - p[1] = (uint8_t)(v >> 16) & 0xff; - p[2] = (uint8_t)(v >> 8) & 0xff; - p[3] = (uint8_t)v & 0xff; -} - -static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int do_read) { - char *b = buf; - size_t pos = 0; - ssize_t res; - ssh_pollfd_t pfd; - ssh_channel channel = agent->channel; - socket_t fd; - - /* Using a socket ? */ - if (channel == NULL) { - fd = ssh_socket_get_fd_in(agent->sock); - pfd.fd = fd; - pfd.events = do_read ? POLLIN : POLLOUT; - - while (n > pos) { - if (do_read) { - res = read(fd, b + pos, n - pos); - } else { - res = write(fd, b + pos, n - pos); - } - switch (res) { - case -1: - if (errno == EINTR) { - continue; - } -#ifdef EWOULDBLOCK - if (errno == EAGAIN || errno == EWOULDBLOCK) { -#else - if (errno == EAGAIN) { -#endif - (void) ssh_poll(&pfd, 1, -1); - continue; - } - return 0; - case 0: - /* read returns 0 on end-of-file */ - errno = do_read ? 0 : EPIPE; - return pos; - default: - pos += (size_t) res; - } - } - return pos; - } else { - /* using an SSH channel */ - while (n > pos){ - if (do_read) - res = ssh_channel_read(channel,b + pos, n-pos, 0); - else - res = ssh_channel_write(channel, b+pos, n-pos); - if (res == SSH_AGAIN) - continue; - if (res == SSH_ERROR) - return 0; - pos += (size_t)res; - } - return pos; - } -} - -ssh_agent agent_new(struct ssh_session_struct *session) { - ssh_agent agent = NULL; - - agent = malloc(sizeof(struct ssh_agent_struct)); - if (agent == NULL) { - return NULL; - } - ZERO_STRUCTP(agent); - - agent->count = 0; - agent->sock = ssh_socket_new(session); - if (agent->sock == NULL) { - SAFE_FREE(agent); - return NULL; - } - agent->channel = NULL; - return agent; -} - -static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channel){ - agent->channel = channel; -} - -/** @brief sets the SSH agent channel. - * The SSH agent channel will be used to authenticate this client using - * an agent through a channel, from another session. The most likely use - * is to implement SSH Agent forwarding into a SSH proxy. - * @param[in] channel a SSH channel from another session. - * @returns SSH_OK in case of success - * SSH_ERROR in case of an error - */ -int ssh_set_agent_channel(ssh_session session, ssh_channel channel){ - if (!session) - return SSH_ERROR; - if (!session->agent){ - ssh_set_error(session, SSH_REQUEST_DENIED, "Session has no active agent"); - return SSH_ERROR; - } - agent_set_channel(session->agent, channel); - return SSH_OK; -} - - -void agent_close(struct ssh_agent_struct *agent) { - if (agent == NULL) { - return; - } - - if (getenv("SSH_AUTH_SOCK")) { - ssh_socket_close(agent->sock); - } -} - -void agent_free(ssh_agent agent) { - if (agent) { - if (agent->ident) { - ssh_buffer_free(agent->ident); - } - if (agent->sock) { - agent_close(agent); - ssh_socket_free(agent->sock); - } - SAFE_FREE(agent); - } -} - -static int agent_connect(ssh_session session) { - const char *auth_sock = NULL; - - if (session == NULL || session->agent == NULL) { - return -1; - } - - if (session->agent->channel != NULL) - return 0; - - auth_sock = getenv("SSH_AUTH_SOCK"); - - if (auth_sock && *auth_sock) { - if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) { - return -1; - } - return 0; - } - - return -1; -} - -#if 0 -static int agent_decode_reply(struct ssh_session_struct *session, int type) { - switch (type) { - case SSH_AGENT_FAILURE: - case SSH2_AGENT_FAILURE: - case SSH_COM_AGENT2_FAILURE: - ssh_log(session, SSH_LOG_RARE, "SSH_AGENT_FAILURE"); - return 0; - case SSH_AGENT_SUCCESS: - return 1; - default: - ssh_set_error(session, SSH_FATAL, - "Bad response from authentication agent: %d", type); - break; - } - - return -1; -} -#endif - -static int agent_talk(struct ssh_session_struct *session, - struct ssh_buffer_struct *request, struct ssh_buffer_struct *reply) { - uint32_t len = 0; - uint8_t payload[1024] = {0}; - - len = buffer_get_rest_len(request); - SSH_LOG(SSH_LOG_TRACE, "Request length: %u", len); - agent_put_u32(payload, len); - - /* send length and then the request packet */ - if (atomicio(session->agent, payload, 4, 0) == 4) { - if (atomicio(session->agent, buffer_get_rest(request), len, 0) - != len) { - SSH_LOG(SSH_LOG_WARN, "atomicio sending request failed: %s", - strerror(errno)); - return -1; - } - } else { - SSH_LOG(SSH_LOG_WARN, - "atomicio sending request length failed: %s", - strerror(errno)); - return -1; - } - - /* wait for response, read the length of the response packet */ - if (atomicio(session->agent, payload, 4, 1) != 4) { - SSH_LOG(SSH_LOG_WARN, "atomicio read response length failed: %s", - strerror(errno)); - return -1; - } - - len = agent_get_u32(payload); - if (len > 256 * 1024) { - ssh_set_error(session, SSH_FATAL, - "Authentication response too long: %u", len); - return -1; - } - SSH_LOG(SSH_LOG_TRACE, "Response length: %u", len); - - while (len > 0) { - size_t n = len; - if (n > sizeof(payload)) { - n = sizeof(payload); - } - if (atomicio(session->agent, payload, n, 1) != n) { - SSH_LOG(SSH_LOG_WARN, - "Error reading response from authentication socket."); - return -1; - } - if (ssh_buffer_add_data(reply, payload, n) < 0) { - SSH_LOG(SSH_LOG_WARN, "Not enough space"); - return -1; - } - len -= n; - } - - return 0; -} - -int ssh_agent_get_ident_count(struct ssh_session_struct *session) { - ssh_buffer request = NULL; - ssh_buffer reply = NULL; - unsigned int type = 0; - unsigned int c1 = 0, c2 = 0; - uint8_t buf[4] = {0}; - int rc; - - switch (session->version) { - case 1: - c1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; - c2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; - break; - case 2: - c1 = SSH2_AGENTC_REQUEST_IDENTITIES; - c2 = SSH2_AGENT_IDENTITIES_ANSWER; - break; - default: - return 0; - } - - /* send message to the agent requesting the list of identities */ - request = ssh_buffer_new(); - if (request == NULL) { - ssh_set_error_oom(session); - return -1; - } - if (buffer_add_u8(request, c1) < 0) { - ssh_set_error_oom(session); - ssh_buffer_free(request); - return -1; - } - - reply = ssh_buffer_new(); - if (reply == NULL) { - ssh_buffer_free(request); - ssh_set_error(session, SSH_FATAL, "Not enough space"); - return -1; - } - - if (agent_talk(session, request, reply) < 0) { - ssh_buffer_free(request); - ssh_buffer_free(reply); - return 0; - } - ssh_buffer_free(request); - - /* get message type and verify the answer */ - rc = buffer_get_u8(reply, (uint8_t *) &type); - if (rc != sizeof(uint8_t)) { - ssh_set_error(session, SSH_FATAL, - "Bad authentication reply size: %d", rc); - ssh_buffer_free(reply); - return -1; - } - - SSH_LOG(SSH_LOG_WARN, - "Answer type: %d, expected answer: %d", - type, c2); - - if (agent_failed(type)) { - ssh_buffer_free(reply); - return 0; - } else if (type != c2) { - ssh_set_error(session, SSH_FATAL, - "Bad authentication reply message type: %d", type); - ssh_buffer_free(reply); - return -1; - } - - buffer_get_u32(reply, (uint32_t *) buf); - session->agent->count = agent_get_u32(buf); - SSH_LOG(SSH_LOG_DEBUG, "Agent count: %d", - session->agent->count); - if (session->agent->count > 1024) { - ssh_set_error(session, SSH_FATAL, - "Too many identities in authentication reply: %d", - session->agent->count); - ssh_buffer_free(reply); - return -1; - } - - if (session->agent->ident) { - ssh_buffer_reinit(session->agent->ident); - } - session->agent->ident = reply; - - return session->agent->count; -} - -/* caller has to free commment */ -ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session, - char **comment) { - if (ssh_agent_get_ident_count(session) > 0) { - return ssh_agent_get_next_ident(session, comment); - } - - return NULL; -} - -/* caller has to free commment */ -ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session, - char **comment) { - struct ssh_key_struct *key; - struct ssh_string_struct *blob = NULL; - struct ssh_string_struct *tmp = NULL; - int rc; - - if (session->agent->count == 0) { - return NULL; - } - - switch(session->version) { - case 1: - return NULL; - case 2: - /* get the blob */ - blob = buffer_get_ssh_string(session->agent->ident); - if (blob == NULL) { - return NULL; - } - - /* get the comment */ - tmp = buffer_get_ssh_string(session->agent->ident); - if (tmp == NULL) { - ssh_string_free(blob); - - return NULL; - } - - if (comment) { - *comment = ssh_string_to_char(tmp); - } else { - ssh_string_free(blob); - ssh_string_free(tmp); - - return NULL; - } - ssh_string_free(tmp); - - /* get key from blob */ - rc = ssh_pki_import_pubkey_blob(blob, &key); - ssh_string_free(blob); - if (rc == SSH_ERROR) { - return NULL; - } - break; - default: - return NULL; - } - - return key; -} - -int agent_is_running(ssh_session session) { - if (session == NULL || session->agent == NULL) { - return 0; - } - - if (ssh_socket_is_open(session->agent->sock)) { - return 1; - } else { - if (agent_connect(session) < 0) { - return 0; - } else { - return 1; - } - } - - return 0; -} - -ssh_string ssh_agent_sign_data(ssh_session session, - const ssh_key pubkey, - struct ssh_buffer_struct *data) -{ - ssh_buffer request; - ssh_buffer reply; - ssh_string key_blob; - ssh_string sig_blob; - int type = SSH2_AGENT_FAILURE; - int flags = 0; - uint32_t dlen; - int rc; - - request = ssh_buffer_new(); - if (request == NULL) { - return NULL; - } - - /* create request */ - if (buffer_add_u8(request, SSH2_AGENTC_SIGN_REQUEST) < 0) { - ssh_buffer_free(request); - return NULL; - } - - rc = ssh_pki_export_pubkey_blob(pubkey, &key_blob); - if (rc < 0) { - ssh_buffer_free(request); - return NULL; - } - - /* adds len + blob */ - rc = buffer_add_ssh_string(request, key_blob); - ssh_string_free(key_blob); - if (rc < 0) { - ssh_buffer_free(request); - return NULL; - } - - /* Add data */ - dlen = buffer_get_rest_len(data); - if (buffer_add_u32(request, htonl(dlen)) < 0) { - ssh_buffer_free(request); - return NULL; - } - if (ssh_buffer_add_data(request, buffer_get_rest(data), dlen) < 0) { - ssh_buffer_free(request); - return NULL; - } - - if (buffer_add_u32(request, htonl(flags)) < 0) { - ssh_buffer_free(request); - return NULL; - } - - reply = ssh_buffer_new(); - if (reply == NULL) { - ssh_buffer_free(request); - return NULL; - } - - /* send the request */ - if (agent_talk(session, request, reply) < 0) { - ssh_buffer_free(request); - ssh_buffer_free(reply); - return NULL; - } - ssh_buffer_free(request); - - /* check if reply is valid */ - if (buffer_get_u8(reply, (uint8_t *) &type) != sizeof(uint8_t)) { - ssh_buffer_free(reply); - return NULL; - } - - if (agent_failed(type)) { - SSH_LOG(SSH_LOG_WARN, "Agent reports failure in signing the key"); - ssh_buffer_free(reply); - return NULL; - } else if (type != SSH2_AGENT_SIGN_RESPONSE) { - ssh_set_error(session, SSH_FATAL, "Bad authentication response: %d", type); - ssh_buffer_free(reply); - return NULL; - } - - sig_blob = buffer_get_ssh_string(reply); - ssh_buffer_free(reply); - - return sig_blob; -} - -#endif /* _WIN32 */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/auth.c b/libssh/src/auth.c deleted file mode 100644 index d56111d2..00000000 --- a/libssh/src/auth.c +++ /dev/null @@ -1,1810 +0,0 @@ -/* - * auth.c - Authentication with SSH protocols - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis - * Copyright (c) 2008-2013 Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/priv.h" -#include "libssh/crypto.h" -#include "libssh/ssh2.h" -#include "libssh/buffer.h" -#include "libssh/agent.h" -#include "libssh/misc.h" -#include "libssh/packet.h" -#include "libssh/session.h" -#include "libssh/keys.h" -#include "libssh/auth.h" -#include "libssh/pki.h" -#include "libssh/gssapi.h" -#include "libssh/legacy.h" - -/** - * @defgroup libssh_auth The SSH authentication functions. - * @ingroup libssh - * - * Functions to authenticate with a server. - * - * @{ - */ - -/** - * @internal - * - * @brief Ask access to the ssh-userauth service. - * - * @param[in] session The SSH session handle. - * - * @returns SSH_OK on success, SSH_ERROR on error. - * @returns SSH_AGAIN on nonblocking mode, if calling that function - * again is necessary - */ -static int ssh_userauth_request_service(ssh_session session) { - int rc; - - rc = ssh_service_request(session, "ssh-userauth"); - if (rc != SSH_OK) { - SSH_LOG(SSH_LOG_WARN, - "Failed to request \"ssh-userauth\" service"); - } - - return rc; -} - -static int ssh_auth_response_termination(void *user){ - ssh_session session=(ssh_session)user; - switch(session->auth_state){ - case SSH_AUTH_STATE_NONE: - case SSH_AUTH_STATE_KBDINT_SENT: - case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT: - case SSH_AUTH_STATE_GSSAPI_TOKEN: - case SSH_AUTH_STATE_GSSAPI_MIC_SENT: - return 0; - default: - return 1; - } -} - -/** - * @internal - * @brief Wait for a response of an authentication function. - * - * @param[in] session The SSH session. - * - * @returns SSH_AUTH_SUCCESS Authentication success, or pubkey accepted - * SSH_AUTH_PARTIAL Authentication succeeded but another mean - * of authentication is needed. - * SSH_AUTH_INFO Data for keyboard-interactive - * SSH_AUTH_AGAIN In nonblocking mode, call has to be made again - * SSH_AUTH_ERROR Error during the process. - */ -static int ssh_userauth_get_response(ssh_session session) { - int rc = SSH_AUTH_ERROR; - - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, - ssh_auth_response_termination, session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - if (!ssh_auth_response_termination(session)){ - return SSH_AUTH_AGAIN; - } - - switch(session->auth_state) { - case SSH_AUTH_STATE_ERROR: - rc = SSH_AUTH_ERROR; - break; - case SSH_AUTH_STATE_FAILED: - rc = SSH_AUTH_DENIED; - break; - case SSH_AUTH_STATE_INFO: - rc = SSH_AUTH_INFO; - break; - case SSH_AUTH_STATE_PARTIAL: - rc = SSH_AUTH_PARTIAL; - break; - case SSH_AUTH_STATE_PK_OK: - case SSH_AUTH_STATE_SUCCESS: - rc = SSH_AUTH_SUCCESS; - break; - case SSH_AUTH_STATE_KBDINT_SENT: - case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT: - case SSH_AUTH_STATE_GSSAPI_TOKEN: - case SSH_AUTH_STATE_GSSAPI_MIC_SENT: - case SSH_AUTH_STATE_NONE: - /* not reached */ - rc = SSH_AUTH_ERROR; - break; - } - - return rc; -} - -/** - * @internal - * - * @brief Handles a SSH_USERAUTH_BANNER packet. - * - * This banner should be shown to user prior to authentication - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_banner){ - ssh_string banner; - (void)type; - (void)user; - - banner = buffer_get_ssh_string(packet); - if (banner == NULL) { - SSH_LOG(SSH_LOG_WARN, - "Invalid SSH_USERAUTH_BANNER packet"); - } else { - SSH_LOG(SSH_LOG_DEBUG, - "Received SSH_USERAUTH_BANNER packet"); - if(session->banner != NULL) - ssh_string_free(session->banner); - session->banner = banner; - } - - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handles a SSH_USERAUTH_FAILURE packet. - * - * This handles the complete or partial authentication failure. - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_failure){ - char *auth_methods = NULL; - uint8_t partial = 0; - int rc; - (void) type; - (void) user; - - rc = ssh_buffer_unpack(packet, "sb", &auth_methods, &partial); - if (rc != SSH_OK) { - ssh_set_error(session, SSH_FATAL, - "Invalid SSH_MSG_USERAUTH_FAILURE message"); - session->auth_state=SSH_AUTH_STATE_ERROR; - goto end; - } - - if (partial) { - session->auth_state=SSH_AUTH_STATE_PARTIAL; - SSH_LOG(SSH_LOG_INFO, - "Partial success. Authentication that can continue: %s", - auth_methods); - } else { - session->auth_state=SSH_AUTH_STATE_FAILED; - SSH_LOG(SSH_LOG_INFO, - "Access denied. Authentication that can continue: %s", - auth_methods); - ssh_set_error(session, SSH_REQUEST_DENIED, - "Access denied. Authentication that can continue: %s", - auth_methods); - - session->auth_methods = 0; - } - if (strstr(auth_methods, "password") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_PASSWORD; - } - if (strstr(auth_methods, "keyboard-interactive") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; - } - if (strstr(auth_methods, "publickey") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; - } - if (strstr(auth_methods, "hostbased") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_HOSTBASED; - } - if (strstr(auth_methods, "gssapi-with-mic") != NULL) { - session->auth_methods |= SSH_AUTH_METHOD_GSSAPI_MIC; - } - -end: - SAFE_FREE(auth_methods); - - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handles a SSH_USERAUTH_SUCCESS packet. - * - * It is also used to communicate the new to the upper levels. - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_success){ - (void)packet; - (void)type; - (void)user; - - SSH_LOG(SSH_LOG_DEBUG, "Authentication successful"); - SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_SUCCESS"); - - session->auth_state=SSH_AUTH_STATE_SUCCESS; - session->session_state=SSH_SESSION_STATE_AUTHENTICATED; - session->flags |= SSH_SESSION_FLAG_AUTHENTICATED; - - if(session->current_crypto && session->current_crypto->delayed_compress_out){ - SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression OUT"); - session->current_crypto->do_compress_out=1; - } - if(session->current_crypto && session->current_crypto->delayed_compress_in){ - SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression IN"); - session->current_crypto->do_compress_in=1; - } - - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handles a SSH_USERAUTH_PK_OK or SSH_USERAUTH_INFO_REQUEST packet. - * - * Since the two types of packets share the same code, additional work is done - * to understand if we are in a public key or keyboard-interactive context. - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok){ - int rc; - - SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_PK_OK/INFO_REQUEST/GSSAPI_RESPONSE"); - - if(session->auth_state==SSH_AUTH_STATE_KBDINT_SENT){ - /* Assuming we are in keyboard-interactive context */ - SSH_LOG(SSH_LOG_TRACE, - "keyboard-interactive context, assuming SSH_USERAUTH_INFO_REQUEST"); - rc=ssh_packet_userauth_info_request(session,type,packet,user); -#ifdef WITH_GSSAPI - } else if (session->auth_state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT){ - rc = ssh_packet_userauth_gssapi_response(session, type, packet, user); -#endif - } else { - session->auth_state=SSH_AUTH_STATE_PK_OK; - SSH_LOG(SSH_LOG_TRACE, "Assuming SSH_USERAUTH_PK_OK"); - rc=SSH_PACKET_USED; - } - - return rc; -} - -/** - * @brief Get available authentication methods from the server. - * - * This requires the function ssh_userauth_none() to be called before the - * methods are available. The server MAY return a list of methods that may - * continue. - * - * @param[in] session The SSH session. - * - * @param[in] username Deprecated, set to NULL. - * - * @returns A bitfield of the fllowing values: - * - SSH_AUTH_METHOD_PASSWORD - * - SSH_AUTH_METHOD_PUBLICKEY - * - SSH_AUTH_METHOD_HOSTBASED - * - SSH_AUTH_METHOD_INTERACTIVE - * - * @warning Other reserved flags may appear in future versions. - * @see ssh_userauth_none() - */ -int ssh_userauth_list(ssh_session session, const char *username) -{ - (void) username; /* unused */ - - if (session == NULL) { - return 0; - } - -#ifdef WITH_SSH1 - if(session->version == 1) { - return SSH_AUTH_METHOD_PASSWORD; - } -#endif - - return session->auth_methods; -} - -/** - * @brief Try to authenticate through the "none" method. - * - * @param[in] session The ssh session to use. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @returns SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: Authentication failed: use another method\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method\n - * SSH_AUTH_SUCCESS: Authentication success\n - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_none(ssh_session session, const char *username) { - int rc; - -#ifdef WITH_SSH1 - if (session->version == 1) { - return ssh_userauth1_none(session, username); - } -#endif - - switch(session->pending_call_state){ - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_NONE: - goto pending; - default: - ssh_set_error(session, SSH_FATAL, - "Wrong state during pending SSH call"); - return SSH_AUTH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsss", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "none" - ); - if (rc < 0) { - goto fail; - } - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_NONE; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -/** - * @brief Try to authenticate with the given public key. - * - * To avoid unnecessary processing and user interaction, the following method - * is provided for querying whether authentication using the 'pubkey' would - * be possible. - * - * @param[in] session The SSH session. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @param[in] pubkey The public key to try. - * - * @return SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: The server doesn't accept that public key as an - * authentication token. Try another key or another - * method.\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method.\n - * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use - * ssh_userauth_pubkey(). - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_try_publickey(ssh_session session, - const char *username, - const ssh_key pubkey) -{ - ssh_string pubkey_s = NULL; - int rc; - - if (session == NULL) { - return SSH_AUTH_ERROR; - } - - if (pubkey == NULL || !ssh_key_is_public(pubkey)) { - ssh_set_error(session, SSH_FATAL, "Invalid pubkey"); - return SSH_AUTH_ERROR; - } - -#ifdef WITH_SSH1 - if (session->version == 1) { - return SSH_AUTH_DENIED; - } -#endif - - switch(session->pending_call_state) { - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY: - goto pending; - default: - ssh_set_error(session, - SSH_FATAL, - "Wrong state during pending SSH call"); - return SSH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - /* public key */ - rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_s); - if (rc < 0) { - goto fail; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "publickey", - 0, /* private key ? */ - pubkey->type_c, /* algo */ - pubkey_s /* public key */ - ); - if (rc < 0) { - goto fail; - } - - ssh_string_free(pubkey_s); - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_OFFER_PUBKEY; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_string_free(pubkey_s); - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -/** - * @brief Authenticate with public/private key. - * - * @param[in] session The SSH session. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @param[in] privkey The private key for authentication. - * - * @return SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: The server doesn't accept that public key as an - * authentication token. Try another key or another - * method.\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method.\n - * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use - * ssh_userauth_pubkey(). - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_publickey(ssh_session session, - const char *username, - const ssh_key privkey) -{ - ssh_string str = NULL; - int rc; - - if (session == NULL) { - return SSH_AUTH_ERROR; - } - - if (privkey == NULL || !ssh_key_is_private(privkey)) { - ssh_set_error(session, SSH_FATAL, "Invalid private key"); - return SSH_AUTH_ERROR; - } - -#ifdef WITH_SSH1 - if (session->version == 1) { - return SSH_AUTH_DENIED; - } -#endif - - switch(session->pending_call_state) { - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_PUBKEY: - goto pending; - default: - ssh_set_error(session, - SSH_FATAL, - "Bad call during pending SSH call in ssh_userauth_try_pubkey"); - return SSH_AUTH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - /* public key */ - rc = ssh_pki_export_pubkey_blob(privkey, &str); - if (rc < 0) { - goto fail; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "publickey", - 1, /* private key */ - privkey->type_c, /* algo */ - str /* public key */ - ); - if (rc < 0) { - goto fail; - } - ssh_string_free(str); - - /* sign the buffer with the private key */ - str = ssh_pki_do_sign(session, session->out_buffer, privkey); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - str = NULL; - if (rc < 0) { - goto fail; - } - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_PUBKEY; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_string_free(str); - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -#ifndef _WIN32 -static int ssh_userauth_agent_publickey(ssh_session session, - const char *username, - ssh_key pubkey) -{ - ssh_string str = NULL; - int rc; - - switch(session->pending_call_state) { - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_AGENT: - goto pending; - default: - ssh_set_error(session, - SSH_FATAL, - "Bad call during pending SSH call in ssh_userauth_try_pubkey"); - return SSH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - - /* public key */ - rc = ssh_pki_export_pubkey_blob(pubkey, &str); - if (rc < 0) { - goto fail; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsssbsS", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "publickey", - 1, /* private key */ - pubkey->type_c, /* algo */ - str /* public key */ - ); - if (rc < 0) { - goto fail; - } - - ssh_string_free(str); - - /* sign the buffer with the private key */ - str = ssh_pki_do_sign_agent(session, session->out_buffer, pubkey); - if (str == NULL) { - goto fail; - } - - rc = buffer_add_ssh_string(session->out_buffer, str); - ssh_string_free(str); - if (rc < 0) { - goto fail; - } - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_AGENT; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - ssh_string_free(str); - - return SSH_AUTH_ERROR; -} - -enum ssh_agent_state_e { - SSH_AGENT_STATE_NONE = 0, - SSH_AGENT_STATE_PUBKEY, - SSH_AGENT_STATE_AUTH -}; - -struct ssh_agent_state_struct { - enum ssh_agent_state_e state; - ssh_key pubkey; - char *comment; -}; - - -/** - * @brief Try to do public key authentication with ssh agent. - * - * @param[in] session The ssh session to use. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @return SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: The server doesn't accept that public key as an - * authentication token. Try another key or another - * method.\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method.\n - * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use - * ssh_userauth_pubkey(). - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_agent(ssh_session session, - const char *username) { - int rc = SSH_AUTH_ERROR; - struct ssh_agent_state_struct *state; - if (session == NULL) { - return SSH_AUTH_ERROR; - } - - if (!agent_is_running(session)) { - return SSH_AUTH_DENIED; - } - if (!session->agent_state){ - session->agent_state = malloc(sizeof(struct ssh_agent_state_struct)); - if (!session->agent_state){ - ssh_set_error_oom(session); - return SSH_AUTH_ERROR; - } - ZERO_STRUCTP(session->agent_state); - session->agent_state->state=SSH_AGENT_STATE_NONE; - } - state = session->agent_state; - if (state->pubkey == NULL) - state->pubkey = ssh_agent_get_first_ident(session, &state->comment); - while (state->pubkey != NULL) { - if(state->state == SSH_AGENT_STATE_NONE){ - SSH_LOG(SSH_LOG_DEBUG, - "Trying identity %s", state->comment); - } - if(state->state == SSH_AGENT_STATE_NONE || - state->state == SSH_AGENT_STATE_PUBKEY){ - rc = ssh_userauth_try_publickey(session, username, state->pubkey); - if (rc == SSH_AUTH_ERROR) { - ssh_string_free_char(state->comment); - ssh_key_free(state->pubkey); - SAFE_FREE(session->agent_state); - return rc; - } else if (rc == SSH_AUTH_AGAIN) { - state->state = SSH_AGENT_STATE_PUBKEY; - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(SSH_LOG_DEBUG, - "Public key of %s refused by server", state->comment); - ssh_string_free_char(state->comment); - ssh_key_free(state->pubkey); - state->pubkey = ssh_agent_get_next_ident(session, &state->comment); - state->state = SSH_AGENT_STATE_NONE; - continue; - } - - SSH_LOG(SSH_LOG_DEBUG, - "Public key of %s accepted by server", state->comment); - state->state = SSH_AGENT_STATE_AUTH; - } - if (state->state == SSH_AGENT_STATE_AUTH){ - rc = ssh_userauth_agent_publickey(session, username, state->pubkey); - if (rc == SSH_AUTH_AGAIN) - return rc; - ssh_string_free_char(state->comment); - ssh_key_free(state->pubkey); - if (rc == SSH_AUTH_ERROR) { - SAFE_FREE(session->agent_state); - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(SSH_LOG_INFO, - "Server accepted public key but refused the signature"); - state->pubkey = ssh_agent_get_next_ident(session, &state->comment); - state->state = SSH_AGENT_STATE_NONE; - continue; - } - SAFE_FREE(session->agent_state); - return SSH_AUTH_SUCCESS; - } - } - - SAFE_FREE(session->agent_state); - return rc; -} -#endif - -enum ssh_auth_auto_state_e { - SSH_AUTH_AUTO_STATE_NONE=0, - SSH_AUTH_AUTO_STATE_PUBKEY, - SSH_AUTH_AUTO_STATE_KEY_IMPORTED, - SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED -}; - -struct ssh_auth_auto_state_struct { - enum ssh_auth_auto_state_e state; - struct ssh_iterator *it; - ssh_key privkey; - ssh_key pubkey; -}; - -/** - * @brief Tries to automatically authenticate with public key and "none" - * - * It may fail, for instance it doesn't ask for a password and uses a default - * asker for passphrases (in case the private key is encrypted). - * - * @param[in] session The SSH session. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @param[in] passphrase Use this passphrase to unlock the privatekey. Use NULL - * if you don't want to use a passphrase or the user - * should be asked. - * - * @return SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: The server doesn't accept that public key as an - * authentication token. Try another key or another - * method.\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method.\n - * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use - * ssh_userauth_pubkey(). - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - */ -int ssh_userauth_publickey_auto(ssh_session session, - const char *username, - const char *passphrase) -{ - ssh_auth_callback auth_fn = NULL; - void *auth_data = NULL; - struct ssh_auth_auto_state_struct *state; - int rc; - - if (session == NULL) { - return SSH_AUTH_ERROR; - } - - if (session->common.callbacks) { - auth_fn = session->common.callbacks->auth_function; - auth_data = session->common.callbacks->userdata; - } - if (!session->auth_auto_state){ - session->auth_auto_state = - malloc(sizeof(struct ssh_auth_auto_state_struct)); - if (!session->auth_auto_state){ - ssh_set_error_oom(session); - return SSH_AUTH_ERROR; - } - ZERO_STRUCTP(session->auth_auto_state); - } - state = session->auth_auto_state; - if (state->state == SSH_AUTH_AUTO_STATE_NONE) { -#ifndef _WIN32 - /* Try authentication with ssh-agent first */ - rc = ssh_userauth_agent(session, username); - if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_AGAIN) { - return rc; - } -#endif - state->state = SSH_AUTH_AUTO_STATE_PUBKEY; - } - if (state->it == NULL) { - state->it = ssh_list_get_iterator(session->opts.identity); - } - - while (state->it != NULL){ - const char *privkey_file = state->it->data; - char pubkey_file[1024] = {0}; - if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY){ - SSH_LOG(SSH_LOG_DEBUG, - "Trying to authenticate with %s", privkey_file); - state->privkey = NULL; - state->pubkey = NULL; - snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file); - - rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey); - if (rc == SSH_ERROR) { - ssh_set_error(session, - SSH_FATAL, - "Failed to import public key: %s", - pubkey_file); - SAFE_FREE(session->auth_auto_state); - return SSH_AUTH_ERROR; - } else if (rc == SSH_EOF) { - /* Read the private key and save the public key to file */ - rc = ssh_pki_import_privkey_file(privkey_file, - passphrase, - auth_fn, - auth_data, - &state->privkey); - if (rc == SSH_ERROR) { - ssh_set_error(session, - SSH_FATAL, - "Failed to read private key: %s", - privkey_file); - state->it=state->it->next; - continue; - } else if (rc == SSH_EOF) { - /* If the file doesn't exist, continue */ - SSH_LOG(SSH_LOG_DEBUG, - "Private key %s doesn't exist.", - privkey_file); - state->it=state->it->next; - continue; - } - - rc = ssh_pki_export_privkey_to_pubkey(state->privkey, &state->pubkey); - if (rc == SSH_ERROR) { - ssh_key_free(state->privkey); - SAFE_FREE(session->auth_auto_state); - return SSH_AUTH_ERROR; - } - - rc = ssh_pki_export_pubkey_file(state->pubkey, pubkey_file); - if (rc == SSH_ERROR) { - SSH_LOG(SSH_LOG_WARN, - "Could not write public key to file: %s", - pubkey_file); - } - } - state->state = SSH_AUTH_AUTO_STATE_KEY_IMPORTED; - } - if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED){ - rc = ssh_userauth_try_publickey(session, username, state->pubkey); - if (rc == SSH_AUTH_ERROR) { - SSH_LOG(SSH_LOG_WARN, - "Public key authentication error for %s", - privkey_file); - ssh_key_free(state->privkey); - ssh_key_free(state->pubkey); - SAFE_FREE(session->auth_auto_state); - return rc; - } else if (rc == SSH_AUTH_AGAIN){ - return rc; - } else if (rc != SSH_AUTH_SUCCESS) { - SSH_LOG(SSH_LOG_DEBUG, - "Public key for %s refused by server", - privkey_file); - ssh_key_free(state->privkey); - state->privkey = NULL; - ssh_key_free(state->pubkey); - state->pubkey = NULL; - state->it=state->it->next; - state->state = SSH_AUTH_AUTO_STATE_PUBKEY; - continue; - } - state->state = SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED; - } - if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED){ - /* Public key has been accepted by the server */ - if (state->privkey == NULL) { - rc = ssh_pki_import_privkey_file(privkey_file, - passphrase, - auth_fn, - auth_data, - &state->privkey); - if (rc == SSH_ERROR) { - ssh_key_free(state->pubkey); - state->pubkey=NULL; - ssh_set_error(session, - SSH_FATAL, - "Failed to read private key: %s", - privkey_file); - state->it=state->it->next; - state->state = SSH_AUTH_AUTO_STATE_PUBKEY; - continue; - } else if (rc == SSH_EOF) { - /* If the file doesn't exist, continue */ - ssh_key_free(state->pubkey); - state->pubkey=NULL; - SSH_LOG(SSH_LOG_INFO, - "Private key %s doesn't exist.", - privkey_file); - state->it=state->it->next; - state->state = SSH_AUTH_AUTO_STATE_PUBKEY; - continue; - } - } - - rc = ssh_userauth_publickey(session, username, state->privkey); - if (rc != SSH_AUTH_AGAIN && rc != SSH_AUTH_DENIED) { - ssh_key_free(state->privkey); - ssh_key_free(state->pubkey); - SAFE_FREE(session->auth_auto_state); - } - if (rc == SSH_AUTH_ERROR) { - return rc; - } else if (rc == SSH_AUTH_SUCCESS) { - SSH_LOG(SSH_LOG_INFO, - "Successfully authenticated using %s", - privkey_file); - return rc; - } else if (rc == SSH_AUTH_AGAIN){ - return rc; - } - - SSH_LOG(SSH_LOG_WARN, - "The server accepted the public key but refused the signature"); - state->it=state->it->next; - state->state=SSH_AUTH_AUTO_STATE_PUBKEY; - /* continue */ - } - } - SSH_LOG(SSH_LOG_INFO, - "Tried every public key, none matched"); - SAFE_FREE(session->auth_auto_state); - return SSH_AUTH_DENIED; -} - -/** - * @brief Try to authenticate by password. - * - * This authentication method is normally disabled on SSHv2 server. You should - * use keyboard-interactive mode. - * - * The 'password' value MUST be encoded UTF-8. It is up to the server how to - * interpret the password and validate it against the password database. - * However, if you read the password in some other encoding, you MUST convert - * the password to UTF-8. - * - * @param[in] session The ssh session to use. - * - * @param[in] username The username, this SHOULD be NULL. - * - * @param[in] password The password to authenticate in UTF-8. - * - * @returns SSH_AUTH_ERROR: A serious error happened.\n - * SSH_AUTH_DENIED: Authentication failed: use another method\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method\n - * SSH_AUTH_SUCCESS: Authentication success\n - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @note Most server implementations do not permit changing the username during - * authentication. The username should only be set with ssh_options_set() only - * before you connect to the server. - * - * @see ssh_userauth_none() - * @see ssh_userauth_kbdint() - */ -int ssh_userauth_password(ssh_session session, - const char *username, - const char *password) { - int rc; - -#ifdef WITH_SSH1 - if (session->version == 1) { - rc = ssh_userauth1_password(session, username, password); - return rc; - } -#endif - - switch(session->pending_call_state) { - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_OFFER_PUBKEY: - goto pending; - default: - ssh_set_error(session, - SSH_FATAL, - "Wrong state during pending SSH call"); - return SSH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsssbs", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "password", - 0, /* false */ - password - ); - if (rc < 0) { - goto fail; - } - - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_OFFER_PUBKEY; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } - - return rc; -fail: - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -#ifndef _WIN32 -/* LEGACY */ -int ssh_userauth_agent_pubkey(ssh_session session, - const char *username, - ssh_public_key publickey) -{ - ssh_key key; - int rc; - - key = ssh_key_new(); - if (key == NULL) { - return SSH_AUTH_ERROR; - } - - key->type = publickey->type; - key->type_c = ssh_key_type_to_char(key->type); - key->flags = SSH_KEY_FLAG_PUBLIC; - key->dsa = publickey->dsa_pub; - key->rsa = publickey->rsa_pub; - - rc = ssh_userauth_agent_publickey(session, username, key); - - key->dsa = NULL; - key->rsa = NULL; - ssh_key_free(key); - - return rc; -} -#endif /* _WIN32 */ - -ssh_kbdint ssh_kbdint_new(void) { - ssh_kbdint kbd; - - kbd = malloc(sizeof(struct ssh_kbdint_struct)); - if (kbd == NULL) { - return NULL; - } - ZERO_STRUCTP(kbd); - - return kbd; -} - - -void ssh_kbdint_free(ssh_kbdint kbd) { - int i, n; - - if (kbd == NULL) { - return; - } - - SAFE_FREE(kbd->name); - SAFE_FREE(kbd->instruction); - SAFE_FREE(kbd->echo); - - n = kbd->nprompts; - if (kbd->prompts) { - for (i = 0; i < n; i++) { - BURN_STRING(kbd->prompts[i]); - SAFE_FREE(kbd->prompts[i]); - } - SAFE_FREE(kbd->prompts); - } - - n = kbd->nanswers; - if (kbd->answers) { - for (i = 0; i < n; i++) { - BURN_STRING(kbd->answers[i]); - SAFE_FREE(kbd->answers[i]); - } - SAFE_FREE(kbd->answers); - } - - SAFE_FREE(kbd); -} - -void ssh_kbdint_clean(ssh_kbdint kbd) { - int i, n; - - if (kbd == NULL) { - return; - } - - SAFE_FREE(kbd->name); - SAFE_FREE(kbd->instruction); - SAFE_FREE(kbd->echo); - - n = kbd->nprompts; - if (kbd->prompts) { - for (i = 0; i < n; i++) { - BURN_STRING(kbd->prompts[i]); - SAFE_FREE(kbd->prompts[i]); - } - SAFE_FREE(kbd->prompts); - } - - n = kbd->nanswers; - - if (kbd->answers) { - for (i = 0; i < n; i++) { - BURN_STRING(kbd->answers[i]); - SAFE_FREE(kbd->answers[i]); - } - SAFE_FREE(kbd->answers); - } - - kbd->nprompts = 0; - kbd->nanswers = 0; -} - -/* - * This function sends the first packet as explained in RFC 3066 section 3.1. - */ -static int ssh_userauth_kbdint_init(ssh_session session, - const char *username, - const char *submethods) -{ - int rc; - if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_INIT) - goto pending; - if (session->pending_call_state != SSH_PENDING_CALL_NONE){ - ssh_set_error_invalid(session); - return SSH_ERROR; - } - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) - return SSH_AUTH_AGAIN; - if (rc != SSH_OK) { - return SSH_AUTH_ERROR; - } - - /* request */ - rc = ssh_buffer_pack(session->out_buffer, "bsssss", - SSH2_MSG_USERAUTH_REQUEST, - username ? username : session->opts.username, - "ssh-connection", - "keyboard-interactive", - "", /* lang (ignore it) */ - submethods ? submethods : "" - ); - if (rc < 0) { - goto fail; - } - - - session->auth_state = SSH_AUTH_STATE_KBDINT_SENT; - session->pending_call_state = SSH_PENDING_CALL_AUTH_KBDINT_INIT; - - SSH_LOG(SSH_LOG_DEBUG, - "Sending keyboard-interactive init request"); - - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) - session->pending_call_state = SSH_PENDING_CALL_NONE; - return rc; -fail: - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -/** - * @internal - * - * @brief Send the current challenge response and wait for a reply from the - * server. - * - * @returns SSH_AUTH_INFO if more info is needed - * @returns SSH_AUTH_SUCCESS - * @returns SSH_AUTH_FAILURE - * @returns SSH_AUTH_PARTIAL - */ -static int ssh_userauth_kbdint_send(ssh_session session) -{ - uint32_t i; - int rc; - if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_SEND) - goto pending; - if (session->pending_call_state != SSH_PENDING_CALL_NONE){ - ssh_set_error_invalid(session); - return SSH_ERROR; - } - rc = ssh_buffer_pack(session->out_buffer, "bd", - SSH2_MSG_USERAUTH_INFO_RESPONSE, - session->kbdint->nprompts); - if (rc < 0) { - goto fail; - } - - for (i = 0; i < session->kbdint->nprompts; i++) { - rc = ssh_buffer_pack(session->out_buffer, "s", - session->kbdint->answers && session->kbdint->answers[i] ? - session->kbdint->answers[i]:""); - if (rc < 0) { - goto fail; - } - } - - session->auth_state = SSH_AUTH_STATE_KBDINT_SENT; - session->pending_call_state = SSH_PENDING_CALL_AUTH_KBDINT_SEND; - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - SSH_LOG(SSH_LOG_DEBUG, - "Sending keyboard-interactive response packet"); - - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) - session->pending_call_state = SSH_PENDING_CALL_NONE; - return rc; -fail: - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - - return SSH_AUTH_ERROR; -} - -/** - * @internal - * @brief handles a SSH_USERAUTH_INFO_REQUEST packet, as used in - * keyboard-interactive authentication, and changes the - * authentication state. - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) { - ssh_string tmp = NULL; - uint32_t nprompts; - uint32_t i; - int rc; - (void)user; - (void)type; - - - if (session->kbdint == NULL) { - session->kbdint = ssh_kbdint_new(); - if (session->kbdint == NULL) { - ssh_set_error_oom(session); - return SSH_PACKET_USED; - } - } else { - ssh_kbdint_clean(session->kbdint); - } - - rc = ssh_buffer_unpack(packet, "ssSd", - &session->kbdint->name, /* name of the "asking" window shown to client */ - &session->kbdint->instruction, - &tmp, /* to ignore */ - &nprompts - ); - - /* We don't care about tmp */ - ssh_string_free(tmp); - - if (rc != SSH_OK) { - ssh_set_error(session, SSH_FATAL, "Invalid USERAUTH_INFO_REQUEST msg"); - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_DEBUG, - "%d keyboard-interactive prompts", nprompts); - if (nprompts > KBDINT_MAX_PROMPT) { - ssh_set_error(session, SSH_FATAL, - "Too much prompts requested by the server: %u (0x%.4x)", - nprompts, nprompts); - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - return SSH_PACKET_USED; - } - - session->kbdint->nprompts = nprompts; - session->kbdint->nanswers = nprompts; - session->kbdint->prompts = malloc(nprompts * sizeof(char *)); - if (session->kbdint->prompts == NULL) { - session->kbdint->nprompts = 0; - ssh_set_error_oom(session); - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - return SSH_PACKET_USED; - } - memset(session->kbdint->prompts, 0, nprompts * sizeof(char *)); - - session->kbdint->echo = malloc(nprompts); - if (session->kbdint->echo == NULL) { - session->kbdint->nprompts = 0; - ssh_set_error_oom(session); - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - return SSH_PACKET_USED; - } - memset(session->kbdint->echo, 0, nprompts); - - for (i = 0; i < nprompts; i++) { - rc = ssh_buffer_unpack(packet, "sb", - &session->kbdint->prompts[i], - &session->kbdint->echo[i]); - if (rc == SSH_ERROR) { - ssh_set_error(session, SSH_FATAL, "Short INFO_REQUEST packet"); - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - return SSH_PACKET_USED; - } - } - session->auth_state=SSH_AUTH_STATE_INFO; - - return SSH_PACKET_USED; -} - -/** - * @brief Try to authenticate through the "keyboard-interactive" method. - * - * @param[in] session The ssh session to use. - * - * @param[in] user The username to authenticate. You can specify NULL if - * ssh_option_set_username() has been used. You cannot try - * two different logins in a row. - * - * @param[in] submethods Undocumented. Set it to NULL. - * - * @returns SSH_AUTH_ERROR: A serious error happened\n - * SSH_AUTH_DENIED: Authentication failed : use another method\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method\n - * SSH_AUTH_SUCCESS: Authentication success\n - * SSH_AUTH_INFO: The server asked some questions. Use - * ssh_userauth_kbdint_getnprompts() and such.\n - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - * - * @see ssh_userauth_kbdint_getnprompts() - * @see ssh_userauth_kbdint_getname() - * @see ssh_userauth_kbdint_getinstruction() - * @see ssh_userauth_kbdint_getprompt() - * @see ssh_userauth_kbdint_setanswer() - */ -int ssh_userauth_kbdint(ssh_session session, const char *user, - const char *submethods) { - int rc = SSH_AUTH_ERROR; - - if (session == NULL) { - return SSH_AUTH_ERROR; - } - -#ifdef WITH_SSH1 - if (session->version == 1) { - return SSH_AUTH_DENIED; - } -#endif - if ((session->pending_call_state == SSH_PENDING_CALL_NONE && session->kbdint == NULL) || - session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_INIT) - rc = ssh_userauth_kbdint_init(session, user, submethods); - else if (session->pending_call_state == SSH_PENDING_CALL_AUTH_KBDINT_SEND || - session->kbdint != NULL) { - /* - * If we are at this point, it is because session->kbdint exists. - * It means the user has set some information there we need to send - * the server and then we need to ack the status (new questions or ok - * pass in). - * It is possible that session->kbdint is NULL while we're waiting for - * a reply, hence the test for the pending call. - */ - rc = ssh_userauth_kbdint_send(session); - } else { - /* We are here because session->kbdint == NULL & state != NONE. - * This should not happen - */ - rc = SSH_AUTH_ERROR; - ssh_set_error(session,SSH_FATAL,"Invalid state in %s", __FUNCTION__); - } - return rc; -} - -/** - * @brief Get the number of prompts (questions) the server has given. - * - * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return - * code, this function can be used to retrieve information about the keyboard - * interactive authentication questions sent by the remote host. - * - * @param[in] session The ssh session to use. - * - * @returns The number of prompts. - */ -int ssh_userauth_kbdint_getnprompts(ssh_session session) { - if(session==NULL) - return SSH_ERROR; - if(session->kbdint == NULL) { - ssh_set_error_invalid(session); - return SSH_ERROR; - } - return session->kbdint->nprompts; -} - -/** - * @brief Get the "name" of the message block. - * - * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return - * code, this function can be used to retrieve information about the keyboard - * interactive authentication questions sent by the remote host. - * - * @param[in] session The ssh session to use. - * - * @returns The name of the message block. Do not free it. - */ -const char *ssh_userauth_kbdint_getname(ssh_session session) { - if(session==NULL) - return NULL; - if(session->kbdint == NULL) { - ssh_set_error_invalid(session); - return NULL; - } - return session->kbdint->name; -} - -/** - * @brief Get the "instruction" of the message block. - * - * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return - * code, this function can be used to retrieve information about the keyboard - * interactive authentication questions sent by the remote host. - * - * @param[in] session The ssh session to use. - * - * @returns The instruction of the message block. - */ - -const char *ssh_userauth_kbdint_getinstruction(ssh_session session) { - if(session==NULL) - return NULL; - if(session->kbdint == NULL) { - ssh_set_error_invalid(session); - return NULL; - } - return session->kbdint->instruction; -} - -/** - * @brief Get a prompt from a message block. - * - * Once you have called ssh_userauth_kbdint() and received SSH_AUTH_INFO return - * code, this function can be used to retrieve information about the keyboard - * interactive authentication questions sent by the remote host. - * - * @param[in] session The ssh session to use. - * - * @param[in] i The index number of the i'th prompt. - * - * @param[out] echo This is an optional variable. You can obtain a - * boolean if the user input should be echoed or - * hidden. For passwords it is usually hidden. - * - * @returns A pointer to the prompt. Do not free it. - * - * @code - * const char prompt; - * char echo; - * - * prompt = ssh_userauth_kbdint_getprompt(session, 0, &echo); - * if (echo) ... - * @endcode - */ -const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, - char *echo) { - if(session==NULL) - return NULL; - if(session->kbdint == NULL) { - ssh_set_error_invalid(session); - return NULL; - } - if (i > session->kbdint->nprompts) { - ssh_set_error_invalid(session); - return NULL; - } - - if (echo) { - *echo = session->kbdint->echo[i]; - } - - return session->kbdint->prompts[i]; -} - -#ifdef WITH_SERVER -/** - * @brief Get the number of answers the client has given. - * - * @param[in] session The ssh session to use. - * - * @returns The number of answers. - */ -int ssh_userauth_kbdint_getnanswers(ssh_session session) { - if(session==NULL || session->kbdint == NULL) - return SSH_ERROR; - return session->kbdint->nanswers; -} - -/** - * @brief Get the answer for a question from a message block. - * - * @param[in] session The ssh session to use. - * - * @param[in] i index The number of the ith answer. - * - * @return 0 on success, < 0 on error. - */ -const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) { - if(session==NULL || session->kbdint == NULL - || session->kbdint->answers == NULL) { - return NULL; - } - if (i >= session->kbdint->nanswers) { - return NULL; - } - - return session->kbdint->answers[i]; -} -#endif - -/** - * @brief Set the answer for a question from a message block. - * - * If you have called ssh_userauth_kbdint() and got SSH_AUTH_INFO, this - * function returns the questions from the server. - * - * @param[in] session The ssh session to use. - * - * @param[in] i index The number of the ith prompt. - * - * @param[in] answer The answer to give to the server. The answer MUST be - * encoded UTF-8. It is up to the server how to interpret - * the value and validate it. However, if you read the - * answer in some other encoding, you MUST convert it to - * UTF-8. - * - * @return 0 on success, < 0 on error. - */ -int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i, - const char *answer) { - if (session == NULL) - return -1; - if (answer == NULL || session->kbdint == NULL || - i >= session->kbdint->nprompts) { - ssh_set_error_invalid(session); - return -1; - } - - if (session->kbdint->answers == NULL) { - session->kbdint->answers = malloc(sizeof(char*) * session->kbdint->nprompts); - if (session->kbdint->answers == NULL) { - ssh_set_error_oom(session); - return -1; - } - memset(session->kbdint->answers, 0, sizeof(char *) * session->kbdint->nprompts); - } - - if (session->kbdint->answers[i]) { - BURN_STRING(session->kbdint->answers[i]); - SAFE_FREE(session->kbdint->answers[i]); - } - - session->kbdint->answers[i] = strdup(answer); - if (session->kbdint->answers[i] == NULL) { - ssh_set_error_oom(session); - return -1; - } - - return 0; -} - -/** - * @brief Try to authenticate through the "gssapi-with-mic" method. - * - * @param[in] session The ssh session to use. - * - * @returns SSH_AUTH_ERROR: A serious error happened\n - * SSH_AUTH_DENIED: Authentication failed : use another method\n - * SSH_AUTH_PARTIAL: You've been partially authenticated, you still - * have to use another method\n - * SSH_AUTH_SUCCESS: Authentication success\n - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - */ -int ssh_userauth_gssapi(ssh_session session) { - int rc = SSH_AUTH_DENIED; -#ifdef WITH_GSSAPI - switch(session->pending_call_state) { - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_AUTH_GSSAPI_MIC: - goto pending; - default: - ssh_set_error(session, - SSH_FATAL, - "Wrong state during pending SSH call"); - return SSH_ERROR; - } - - rc = ssh_userauth_request_service(session); - if (rc == SSH_AGAIN) { - return SSH_AUTH_AGAIN; - } else if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi-with-mic"); - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_GSSAPI_MIC; - rc = ssh_gssapi_auth_mic(session); - - if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_DENIED) { - session->auth_state = SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_NONE; - return rc; - } - -pending: - rc = ssh_userauth_get_response(session); - if (rc != SSH_AUTH_AGAIN) { - session->pending_call_state = SSH_PENDING_CALL_NONE; - } -#else - (void) session; /* unused */ -#endif - return rc; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/auth1.c b/libssh/src/auth1.c deleted file mode 100644 index a65c4475..00000000 --- a/libssh/src/auth1.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * auth1.c - authentication with SSH-1 protocol - * - * This file is part of the SSH Library - * - * Copyright (c) 2005-2008 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include - -#include "libssh/priv.h" -#include "libssh/ssh1.h" -#include "libssh/buffer.h" -#include "libssh/packet.h" -#include "libssh/session.h" -#include "libssh/string.h" - -#ifdef WITH_SSH1 - -static int ssh_auth_status_termination(void *s){ - ssh_session session=s; - if(session->auth_state != SSH_AUTH_STATE_NONE || - session->session_state == SSH_SESSION_STATE_ERROR) - return 1; - return 0; -} - -static int wait_auth1_status(ssh_session session) { - /* wait for a packet */ - if (ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, - ssh_auth_status_termination, session) != SSH_OK){ - - return SSH_AUTH_ERROR; - } - SSH_LOG(SSH_LOG_PROTOCOL,"Auth state : %d",session->auth_state); - - switch(session->auth_state) { - case SSH_AUTH_STATE_SUCCESS: - return SSH_AUTH_SUCCESS; - case SSH_AUTH_STATE_FAILED: - return SSH_AUTH_DENIED; - default: - return SSH_AUTH_AGAIN; - } - return SSH_AUTH_ERROR; -} - -void ssh_auth1_handler(ssh_session session, uint8_t type){ - if(session->session_state != SSH_SESSION_STATE_AUTHENTICATING){ - ssh_set_error(session,SSH_FATAL,"SSH_SMSG_SUCCESS or FAILED received in wrong state"); - return; - } - if(type==SSH_SMSG_SUCCESS){ - session->auth_state=SSH_AUTH_STATE_SUCCESS; - session->session_state=SSH_SESSION_STATE_AUTHENTICATED; - } else if(type==SSH_SMSG_FAILURE) - session->auth_state=SSH_AUTH_STATE_FAILED; -} - -static int send_username(ssh_session session, const char *username) { - ssh_string user = NULL; - int rc; - /* returns SSH_AUTH_SUCCESS or SSH_AUTH_DENIED */ - if(session->auth_service_state == SSH_AUTH_SERVICE_USER_SENT) { - if(session->auth_state == SSH_AUTH_STATE_FAILED) - return SSH_AUTH_DENIED; - if(session->auth_state == SSH_AUTH_STATE_SUCCESS) - return SSH_AUTH_SUCCESS; - return SSH_AUTH_ERROR; - } - if (session->auth_service_state == SSH_AUTH_SERVICE_SENT) - goto pending; - if (!username) { - if(!(username = session->opts.username)) { - if (ssh_options_set(session, SSH_OPTIONS_USER, NULL) < 0) { - session->auth_service_state = SSH_AUTH_SERVICE_DENIED; - return SSH_ERROR; - } else { - username = session->opts.username; - } - } - } - user = ssh_string_from_char(username); - if (user == NULL) { - return SSH_AUTH_ERROR; - } - - if (buffer_add_u8(session->out_buffer, SSH_CMSG_USER) < 0) { - ssh_string_free(user); - return SSH_AUTH_ERROR; - } - if (buffer_add_ssh_string(session->out_buffer, user) < 0) { - ssh_string_free(user); - return SSH_AUTH_ERROR; - } - ssh_string_free(user); - session->auth_state=SSH_AUTH_STATE_NONE; - session->auth_service_state = SSH_AUTH_SERVICE_SENT; - if (packet_send(session) == SSH_ERROR) { - return SSH_AUTH_ERROR; - } -pending: - rc = wait_auth1_status(session); - switch (rc){ - case SSH_AUTH_SUCCESS: - session->auth_service_state=SSH_AUTH_SERVICE_USER_SENT; - session->auth_state=SSH_AUTH_STATE_SUCCESS; - ssh_set_error(session, SSH_NO_ERROR, "Authentication successful"); - return SSH_AUTH_SUCCESS; - case SSH_AUTH_DENIED: - session->auth_service_state=SSH_AUTH_SERVICE_USER_SENT; - ssh_set_error(session,SSH_REQUEST_DENIED,"Password authentication necessary for user %s",username); - return SSH_AUTH_DENIED; - case SSH_AUTH_AGAIN: - return SSH_AUTH_AGAIN; - default: - session->auth_service_state = SSH_AUTH_SERVICE_NONE; - session->auth_state=SSH_AUTH_STATE_ERROR; - return SSH_AUTH_ERROR; - } -} - -/* use the "none" authentication question */ -int ssh_userauth1_none(ssh_session session, const char *username){ - return send_username(session, username); -} - -/** \internal - * \todo implement ssh1 public key - */ -int ssh_userauth1_offer_pubkey(ssh_session session, const char *username, - int type, ssh_string pubkey) { - (void) session; - (void) username; - (void) type; - (void) pubkey; - - return SSH_AUTH_DENIED; -} - -int ssh_userauth1_password(ssh_session session, const char *username, - const char *password) { - ssh_string pwd = NULL; - int rc; - - rc = send_username(session, username); - if (rc != SSH_AUTH_DENIED) { - return rc; - } - if (session->pending_call_state == SSH_PENDING_CALL_AUTH_PASSWORD) - goto pending; - /* we trick a bit here. A known flaw in SSH1 protocol is that it's - * easy to guess password sizes. - * not that sure ... - */ - - /* XXX fix me here ! */ - /* cisco IOS doesn't like when a password is followed by zeroes and random pad. */ - if(1 || strlen(password) >= 128) { - /* not risky to disclose the size of such a big password .. */ - pwd = ssh_string_from_char(password); - if (pwd == NULL) { - return SSH_AUTH_ERROR; - } - } else { - char buf[128] = {0}; - /* fill the password string from random things. the strcpy - * ensure there is at least a nul byte after the password. - * most implementation won't see the garbage at end. - * why garbage ? because nul bytes will be compressed by - * gzip and disclose password len. - */ - pwd = ssh_string_new(sizeof(buf)); - if (pwd == NULL) { - return SSH_AUTH_ERROR; - } - ssh_get_random(buf, sizeof(buf), 0); - strcpy(buf, password); - ssh_string_fill(pwd, buf, sizeof(buf)); - } - - if (buffer_add_u8(session->out_buffer, SSH_CMSG_AUTH_PASSWORD) < 0) { - ssh_string_burn(pwd); - ssh_string_free(pwd); - - return SSH_AUTH_ERROR; - } - if (buffer_add_ssh_string(session->out_buffer, pwd) < 0) { - ssh_string_burn(pwd); - ssh_string_free(pwd); - - return SSH_AUTH_ERROR; - } - - ssh_string_burn(pwd); - ssh_string_free(pwd); - session->auth_state=SSH_AUTH_STATE_NONE; - session->pending_call_state = SSH_PENDING_CALL_AUTH_PASSWORD; - if (packet_send(session) == SSH_ERROR) { - return SSH_AUTH_ERROR; - } -pending: - rc = wait_auth1_status(session); - if (rc != SSH_AUTH_AGAIN) - session->pending_call_state = SSH_PENDING_CALL_NONE; - - return rc; -} - -#endif /* WITH_SSH1 */ -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/base64.c b/libssh/src/base64.c deleted file mode 100644 index 2a162d0b..00000000 --- a/libssh/src/base64.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * base64.c - support for base64 alphabet system, described in RFC1521 - * - * This file is part of the SSH Library - * - * Copyright (c) 2005-2005 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/* just the dirtiest part of code i ever made */ -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/buffer.h" - -static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - -/* Transformations */ -#define SET_A(n, i) do { (n) |= ((i) & 63) <<18; } while (0) -#define SET_B(n, i) do { (n) |= ((i) & 63) <<12; } while (0) -#define SET_C(n, i) do { (n) |= ((i) & 63) << 6; } while (0) -#define SET_D(n, i) do { (n) |= ((i) & 63); } while (0) - -#define GET_A(n) (unsigned char) (((n) & 0xff0000) >> 16) -#define GET_B(n) (unsigned char) (((n) & 0xff00) >> 8) -#define GET_C(n) (unsigned char) ((n) & 0xff) - -static int _base64_to_bin(unsigned char dest[3], const char *source, int num); -static int get_equals(char *string); - -/* First part: base64 to binary */ - -/** - * @internal - * - * @brief Translates a base64 string into a binary one. - * - * @returns A buffer containing the decoded string, NULL if something went - * wrong (e.g. incorrect char). - */ -ssh_buffer base64_to_bin(const char *source) { - ssh_buffer buffer = NULL; - unsigned char block[3]; - char *base64; - char *ptr; - size_t len; - int equals; - - base64 = strdup(source); - if (base64 == NULL) { - return NULL; - } - ptr = base64; - - /* Get the number of equals signs, which mirrors the padding */ - equals = get_equals(ptr); - if (equals > 2) { - SAFE_FREE(base64); - return NULL; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - SAFE_FREE(base64); - return NULL; - } - /* - * The base64 buffer often contains sensitive data. Make sure we don't leak - * sensitive data - */ - ssh_buffer_set_secure(buffer); - - len = strlen(ptr); - while (len > 4) { - if (_base64_to_bin(block, ptr, 3) < 0) { - goto error; - } - if (ssh_buffer_add_data(buffer, block, 3) < 0) { - goto error; - } - len -= 4; - ptr += 4; - } - - /* - * Depending on the number of bytes resting, there are 3 possibilities - * from the RFC. - */ - switch (len) { - /* - * (1) The final quantum of encoding input is an integral multiple of - * 24 bits. Here, the final unit of encoded output will be an integral - * multiple of 4 characters with no "=" padding - */ - case 4: - if (equals != 0) { - goto error; - } - if (_base64_to_bin(block, ptr, 3) < 0) { - goto error; - } - if (ssh_buffer_add_data(buffer, block, 3) < 0) { - goto error; - } - SAFE_FREE(base64); - - return buffer; - /* - * (2) The final quantum of encoding input is exactly 8 bits; here, the - * final unit of encoded output will be two characters followed by - * two "=" padding characters. - */ - case 2: - if (equals != 2){ - goto error; - } - - if (_base64_to_bin(block, ptr, 1) < 0) { - goto error; - } - if (ssh_buffer_add_data(buffer, block, 1) < 0) { - goto error; - } - SAFE_FREE(base64); - - return buffer; - /* - * The final quantum of encoding input is exactly 16 bits. Here, the final - * unit of encoded output will be three characters followed by one "=" - * padding character. - */ - case 3: - if (equals != 1) { - goto error; - } - if (_base64_to_bin(block, ptr, 2) < 0) { - goto error; - } - if (ssh_buffer_add_data(buffer,block,2) < 0) { - goto error; - } - SAFE_FREE(base64); - - return buffer; - default: - /* 4,3,2 are the only padding size allowed */ - goto error; - } - -error: - SAFE_FREE(base64); - ssh_buffer_free(buffer); - return NULL; -} - -#define BLOCK(letter, n) do {ptr = strchr(alphabet, source[n]); \ - if(!ptr) return -1; \ - i = ptr - alphabet; \ - SET_##letter(*block, i); \ - } while(0) - -/* Returns 0 if ok, -1 if not (ie invalid char into the stuff) */ -static int to_block4(unsigned long *block, const char *source, int num) { - char *ptr; - unsigned int i; - - *block = 0; - if (num < 1) { - return 0; - } - - BLOCK(A, 0); /* 6 bit */ - BLOCK(B,1); /* 12 bit */ - - if (num < 2) { - return 0; - } - - BLOCK(C, 2); /* 18 bit */ - - if (num < 3) { - return 0; - } - - BLOCK(D, 3); /* 24 bit */ - - return 0; -} - -/* num = numbers of final bytes to be decoded */ -static int _base64_to_bin(unsigned char dest[3], const char *source, int num) { - unsigned long block; - - if (to_block4(&block, source, num) < 0) { - return -1; - } - dest[0] = GET_A(block); - dest[1] = GET_B(block); - dest[2] = GET_C(block); - - return 0; -} - -/* Count the number of "=" signs and replace them by zeroes */ -static int get_equals(char *string) { - char *ptr = string; - int num = 0; - - while ((ptr=strchr(ptr,'=')) != NULL) { - num++; - *ptr = '\0'; - ptr++; - } - - return num; -} - -/* thanks sysk for debugging my mess :) */ -#define BITS(n) ((1 << (n)) - 1) -static void _bin_to_base64(unsigned char *dest, const unsigned char source[3], - int len) { - switch (len) { - case 1: - dest[0] = alphabet[(source[0] >> 2)]; - dest[1] = alphabet[((source[0] & BITS(2)) << 4)]; - dest[2] = '='; - dest[3] = '='; - break; - case 2: - dest[0] = alphabet[source[0] >> 2]; - dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)]; - dest[2] = alphabet[(source[1] & BITS(4)) << 2]; - dest[3] = '='; - break; - case 3: - dest[0] = alphabet[(source[0] >> 2)]; - dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)]; - dest[2] = alphabet[ (source[2] >> 6) | (source[1] & BITS(4)) << 2]; - dest[3] = alphabet[source[2] & BITS(6)]; - break; - } -} - -/** - * @internal - * - * @brief Converts binary data to a base64 string. - * - * @returns the converted string - */ -unsigned char *bin_to_base64(const unsigned char *source, int len) { - unsigned char *base64; - unsigned char *ptr; - int flen = len + (3 - (len % 3)); /* round to upper 3 multiple */ - flen = (4 * flen) / 3 + 1; - - base64 = malloc(flen); - if (base64 == NULL) { - return NULL; - } - ptr = base64; - - while(len > 0){ - _bin_to_base64(ptr, source, len > 3 ? 3 : len); - ptr += 4; - source += 3; - len -= 3; - } - ptr[0] = '\0'; - - return base64; -} - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/bignum.c b/libssh/src/bignum.c deleted file mode 100644 index 14b5aa54..00000000 --- a/libssh/src/bignum.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2014 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include - -#include "libssh/priv.h" -#include "libssh/bignum.h" -#include "libssh/string.h" - -ssh_string make_bignum_string(bignum num) { - ssh_string ptr = NULL; - int pad = 0; - unsigned int len = bignum_num_bytes(num); - unsigned int bits = bignum_num_bits(num); - - if (len == 0) { - return NULL; - } - - /* If the first bit is set we have a negative number */ - if (!(bits % 8) && bignum_is_bit_set(num, bits - 1)) { - pad++; - } - -#ifdef DEBUG_CRYPTO - fprintf(stderr, "%d bits, %d bytes, %d padding\n", bits, len, pad); -#endif /* DEBUG_CRYPTO */ - - ptr = ssh_string_new(len + pad); - if (ptr == NULL) { - return NULL; - } - - /* We have a negative number so we need a leading zero */ - if (pad) { - ptr->data[0] = 0; - } - -#ifdef HAVE_LIBGCRYPT - bignum_bn2bin(num, len, ptr->data + pad); -#elif HAVE_LIBCRYPTO - bignum_bn2bin(num, ptr->data + pad); -#endif - - return ptr; -} - -bignum make_string_bn(ssh_string string){ - bignum bn = NULL; - unsigned int len = ssh_string_len(string); - -#ifdef DEBUG_CRYPTO - fprintf(stderr, "Importing a %d bits, %d bytes object ...\n", - len * 8, len); -#endif /* DEBUG_CRYPTO */ - -#ifdef HAVE_LIBGCRYPT - bignum_bin2bn(string->data, len, &bn); -#elif defined HAVE_LIBCRYPTO - bn = bignum_bin2bn(string->data, len, NULL); -#endif - - return bn; -} - -/* prints the bignum on stderr */ -void ssh_print_bignum(const char *which, bignum num) { -#ifdef HAVE_LIBGCRYPT - unsigned char *hex = NULL; - bignum_bn2hex(num, &hex); -#elif defined HAVE_LIBCRYPTO - char *hex = NULL; - hex = bignum_bn2hex(num); -#endif - fprintf(stderr, "%s value: ", which); - fprintf(stderr, "%s\n", (hex == NULL) ? "(null)" : (char *) hex); - SAFE_FREE(hex); -} diff --git a/libssh/src/bind.c b/libssh/src/bind.c deleted file mode 100644 index b3239462..00000000 --- a/libssh/src/bind.c +++ /dev/null @@ -1,504 +0,0 @@ -/* - * bind.c : all ssh_bind functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2004-2005 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/bind.h" -#include "libssh/libssh.h" -#include "libssh/server.h" -#include "libssh/pki.h" -#include "libssh/buffer.h" -#include "libssh/socket.h" -#include "libssh/session.h" - -/** - * @addtogroup libssh_server - * - * @{ - */ - - -#ifdef _WIN32 -#include -#include -#include - -/* - * is necessary for getaddrinfo before Windows XP, but it isn't - * available on some platforms like MinGW. - */ -#ifdef HAVE_WSPIAPI_H -# include -#endif - -#define SOCKOPT_TYPE_ARG4 char - -#else /* _WIN32 */ - -#include -#include -#include -#define SOCKOPT_TYPE_ARG4 int - -#endif /* _WIN32 */ - -static socket_t bind_socket(ssh_bind sshbind, const char *hostname, - int port) { - char port_c[6]; - struct addrinfo *ai; - struct addrinfo hints; - int opt = 1; - socket_t s; - int rc; - - ZERO_STRUCT(hints); - - hints.ai_flags = AI_PASSIVE; - hints.ai_socktype = SOCK_STREAM; - - snprintf(port_c, 6, "%d", port); - rc = getaddrinfo(hostname, port_c, &hints, &ai); - if (rc != 0) { - ssh_set_error(sshbind, - SSH_FATAL, - "Resolving %s: %s", hostname, gai_strerror(rc)); - return -1; - } - - s = socket (ai->ai_family, - ai->ai_socktype, - ai->ai_protocol); - if (s == SSH_INVALID_SOCKET) { - ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno)); - freeaddrinfo (ai); - return -1; - } - - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, - (char *)&opt, sizeof(opt)) < 0) { - ssh_set_error(sshbind, - SSH_FATAL, - "Setting socket options failed: %s", - strerror(errno)); - freeaddrinfo (ai); - close(s); - return -1; - } - - if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) { - ssh_set_error(sshbind, - SSH_FATAL, - "Binding to %s:%d: %s", - hostname, - port, - strerror(errno)); - freeaddrinfo (ai); - close(s); - return -1; - } - - freeaddrinfo (ai); - return s; -} - -ssh_bind ssh_bind_new(void) { - ssh_bind ptr; - - ptr = malloc(sizeof(struct ssh_bind_struct)); - if (ptr == NULL) { - return NULL; - } - ZERO_STRUCTP(ptr); - ptr->bindfd = SSH_INVALID_SOCKET; - ptr->bindport= 22; - ptr->common.log_verbosity = 0; - - return ptr; -} - -static int ssh_bind_import_keys(ssh_bind sshbind) { - int rc; - - if (sshbind->ecdsakey == NULL && - sshbind->dsakey == NULL && - sshbind->rsakey == NULL) { - ssh_set_error(sshbind, SSH_FATAL, - "ECDSA, DSA, or RSA host key file must be set"); - return SSH_ERROR; - } - -#ifdef HAVE_ECC - if (sshbind->ecdsa == NULL && sshbind->ecdsakey != NULL) { - rc = ssh_pki_import_privkey_file(sshbind->ecdsakey, - NULL, - NULL, - NULL, - &sshbind->ecdsa); - if (rc == SSH_ERROR || rc == SSH_EOF) { - ssh_set_error(sshbind, SSH_FATAL, - "Failed to import private ECDSA host key"); - return SSH_ERROR; - } - - if (ssh_key_type(sshbind->ecdsa) != SSH_KEYTYPE_ECDSA) { - ssh_set_error(sshbind, SSH_FATAL, - "The ECDSA host key has the wrong type"); - ssh_key_free(sshbind->ecdsa); - sshbind->ecdsa = NULL; - return SSH_ERROR; - } - } -#endif - - if (sshbind->dsa == NULL && sshbind->dsakey != NULL) { - rc = ssh_pki_import_privkey_file(sshbind->dsakey, - NULL, - NULL, - NULL, - &sshbind->dsa); - if (rc == SSH_ERROR || rc == SSH_EOF) { - ssh_set_error(sshbind, SSH_FATAL, - "Failed to import private DSA host key"); - return SSH_ERROR; - } - - if (ssh_key_type(sshbind->dsa) != SSH_KEYTYPE_DSS) { - ssh_set_error(sshbind, SSH_FATAL, - "The DSA host key has the wrong type: %d", - ssh_key_type(sshbind->dsa)); - ssh_key_free(sshbind->dsa); - sshbind->dsa = NULL; - return SSH_ERROR; - } - } - - if (sshbind->rsa == NULL && sshbind->rsakey != NULL) { - rc = ssh_pki_import_privkey_file(sshbind->rsakey, - NULL, - NULL, - NULL, - &sshbind->rsa); - if (rc == SSH_ERROR || rc == SSH_EOF) { - ssh_set_error(sshbind, SSH_FATAL, - "Failed to import private RSA host key"); - return SSH_ERROR; - } - - if (ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA && - ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA1) { - ssh_set_error(sshbind, SSH_FATAL, - "The RSA host key has the wrong type"); - ssh_key_free(sshbind->rsa); - sshbind->rsa = NULL; - return SSH_ERROR; - } - } - - return SSH_OK; -} - -int ssh_bind_listen(ssh_bind sshbind) { - const char *host; - socket_t fd; - int rc; - - if (ssh_init() < 0) { - ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed"); - return -1; - } - - rc = ssh_bind_import_keys(sshbind); - if (rc != SSH_OK) { - return SSH_ERROR; - } - - if (sshbind->bindfd == SSH_INVALID_SOCKET) { - host = sshbind->bindaddr; - if (host == NULL) { - host = "0.0.0.0"; - } - - fd = bind_socket(sshbind, host, sshbind->bindport); - if (fd == SSH_INVALID_SOCKET) { - ssh_key_free(sshbind->dsa); - sshbind->dsa = NULL; - ssh_key_free(sshbind->rsa); - sshbind->rsa = NULL; - return -1; - } - - if (listen(fd, 10) < 0) { - ssh_set_error(sshbind, SSH_FATAL, - "Listening to socket %d: %s", - fd, strerror(errno)); - close(fd); - ssh_key_free(sshbind->dsa); - sshbind->dsa = NULL; - ssh_key_free(sshbind->rsa); - sshbind->rsa = NULL; - return -1; - } - - sshbind->bindfd = fd; - } else { - SSH_LOG(SSH_LOG_INFO, "Using app-provided bind socket"); - } - return 0; -} - -int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, - void *userdata){ - if (sshbind == NULL) { - return SSH_ERROR; - } - if (callbacks == NULL) { - ssh_set_error_invalid(sshbind); - return SSH_ERROR; - } - if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){ - ssh_set_error(sshbind,SSH_FATAL, - "Invalid callback passed in (badly initialized)"); - return SSH_ERROR; - } - sshbind->bind_callbacks = callbacks; - sshbind->bind_callbacks_userdata=userdata; - return 0; -} - -/** @internal - * @brief callback being called by poll when an event happens - * - */ -static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, - socket_t fd, int revents, void *user){ - ssh_bind sshbind=(ssh_bind)user; - (void)sshpoll; - (void)fd; - - if(revents & POLLIN){ - /* new incoming connection */ - if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){ - sshbind->bind_callbacks->incoming_connection(sshbind, - sshbind->bind_callbacks_userdata); - } - } - return 0; -} - -/** @internal - * @brief returns the current poll handle, or create it - * @param sshbind the ssh_bind object - * @returns a ssh_poll handle suitable for operation - */ -ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind){ - if(sshbind->poll) - return sshbind->poll; - sshbind->poll=ssh_poll_new(sshbind->bindfd,POLLIN, - ssh_bind_poll_callback,sshbind); - return sshbind->poll; -} - -void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) { - sshbind->blocking = blocking ? 1 : 0; -} - -socket_t ssh_bind_get_fd(ssh_bind sshbind) { - return sshbind->bindfd; -} - -void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) { - sshbind->bindfd = fd; -} - -void ssh_bind_fd_toaccept(ssh_bind sshbind) { - sshbind->toaccept = 1; -} - -void ssh_bind_free(ssh_bind sshbind){ - int i; - - if (sshbind == NULL) { - return; - } - - if (sshbind->bindfd >= 0) { -#ifdef _WIN32 - closesocket(sshbind->bindfd); -#else - close(sshbind->bindfd); -#endif - } - sshbind->bindfd = SSH_INVALID_SOCKET; - - /* options */ - SAFE_FREE(sshbind->banner); - SAFE_FREE(sshbind->bindaddr); - - SAFE_FREE(sshbind->dsakey); - SAFE_FREE(sshbind->rsakey); - SAFE_FREE(sshbind->ecdsakey); - - ssh_key_free(sshbind->dsa); - sshbind->dsa = NULL; - ssh_key_free(sshbind->rsa); - sshbind->rsa = NULL; - ssh_key_free(sshbind->ecdsa); - sshbind->ecdsa = NULL; - - for (i = 0; i < 10; i++) { - if (sshbind->wanted_methods[i]) { - SAFE_FREE(sshbind->wanted_methods[i]); - } - } - - SAFE_FREE(sshbind); -} - -int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ - int i, rc; - - if (session == NULL){ - ssh_set_error(sshbind, SSH_FATAL,"session is null"); - return SSH_ERROR; - } - - session->server = 1; - session->version = 2; - - /* copy options */ - for (i = 0; i < 10; i++) { - if (sshbind->wanted_methods[i]) { - session->opts.wanted_methods[i] = strdup(sshbind->wanted_methods[i]); - if (session->opts.wanted_methods[i] == NULL) { - return SSH_ERROR; - } - } - } - - if (sshbind->bindaddr == NULL) - session->opts.bindaddr = NULL; - else { - SAFE_FREE(session->opts.bindaddr); - session->opts.bindaddr = strdup(sshbind->bindaddr); - if (session->opts.bindaddr == NULL) { - return SSH_ERROR; - } - } - - session->common.log_verbosity = sshbind->common.log_verbosity; - if(sshbind->banner != NULL) - session->opts.custombanner = strdup(sshbind->banner); - ssh_socket_free(session->socket); - session->socket = ssh_socket_new(session); - if (session->socket == NULL) { - /* perhaps it may be better to copy the error from session to sshbind */ - ssh_set_error_oom(sshbind); - return SSH_ERROR; - } - ssh_socket_set_fd(session->socket, fd); - ssh_socket_get_poll_handle_out(session->socket); - - /* We must try to import any keys that could be imported in case - * we are not using ssh_bind_listen (which is the other place - * where keys can be imported) on this ssh_bind and are instead - * only using ssh_bind_accept_fd to manage sockets ourselves. - */ - rc = ssh_bind_import_keys(sshbind); - if (rc != SSH_OK) { - return SSH_ERROR; - } - -#ifdef HAVE_ECC - if (sshbind->ecdsa) { - session->srv.ecdsa_key = ssh_key_dup(sshbind->ecdsa); - if (session->srv.ecdsa_key == NULL) { - ssh_set_error_oom(sshbind); - return SSH_ERROR; - } - } -#endif - if (sshbind->dsa) { - session->srv.dsa_key = ssh_key_dup(sshbind->dsa); - if (session->srv.dsa_key == NULL) { - ssh_set_error_oom(sshbind); - return SSH_ERROR; - } - } - if (sshbind->rsa) { - session->srv.rsa_key = ssh_key_dup(sshbind->rsa); - if (session->srv.rsa_key == NULL) { - ssh_set_error_oom(sshbind); - return SSH_ERROR; - } - } - /* force PRNG to change state in case we fork after ssh_bind_accept */ - ssh_reseed(); - return SSH_OK; -} - -int ssh_bind_accept(ssh_bind sshbind, ssh_session session) { - socket_t fd = SSH_INVALID_SOCKET; - int rc; - if (sshbind->bindfd == SSH_INVALID_SOCKET) { - ssh_set_error(sshbind, SSH_FATAL, - "Can't accept new clients on a not bound socket."); - return SSH_ERROR; - } - - if (session == NULL){ - ssh_set_error(sshbind, SSH_FATAL,"session is null"); - return SSH_ERROR; - } - - fd = accept(sshbind->bindfd, NULL, NULL); - if (fd == SSH_INVALID_SOCKET) { - ssh_set_error(sshbind, SSH_FATAL, - "Accepting a new connection: %s", - strerror(errno)); - return SSH_ERROR; - } - rc = ssh_bind_accept_fd(sshbind, session, fd); - - if(rc == SSH_ERROR){ -#ifdef _WIN32 - closesocket(fd); -#else - close(fd); -#endif - ssh_socket_free(session->socket); - } - return rc; -} - - -/** - * @} - */ diff --git a/libssh/src/buffer.c b/libssh/src/buffer.c deleted file mode 100644 index be25a32f..00000000 --- a/libssh/src/buffer.c +++ /dev/null @@ -1,996 +0,0 @@ -/* - * buffer.c - buffer functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/priv.h" -#include "libssh/buffer.h" -#include "libssh/misc.h" -#include "libssh/bignum.h" - -/** - * @defgroup libssh_buffer The SSH buffer functions. - * @ingroup libssh - * - * Functions to handle SSH buffers. - * - * @{ - */ - - -#ifdef DEBUG_BUFFER -/** - * @internal - * - * @brief Check that preconditions and postconditions are valid. - * - * @param[in] buf The buffer to check. - */ -static void buffer_verify(ssh_buffer buf){ - int doabort=0; - if(buf->data == NULL) - return; - if(buf->used > buf->allocated){ - fprintf(stderr,"Buffer error : allocated %u, used %u\n",buf->allocated, buf->used); - doabort=1; - } - if(buf->pos > buf->used){ - fprintf(stderr,"Buffer error : position %u, used %u\n",buf->pos, buf->used); - doabort=1; - } - if(buf->pos > buf->allocated){ - fprintf(stderr,"Buffer error : position %u, allocated %u\n",buf->pos, buf->allocated); - doabort=1; - } - if(doabort) - abort(); -} - -#else -#define buffer_verify(x) -#endif - -/** - * @brief Create a new SSH buffer. - * - * @return A newly initialized SSH buffer, NULL on error. - */ -struct ssh_buffer_struct *ssh_buffer_new(void) { - struct ssh_buffer_struct *buf = malloc(sizeof(struct ssh_buffer_struct)); - - if (buf == NULL) { - return NULL; - } - memset(buf, 0, sizeof(struct ssh_buffer_struct)); - buffer_verify(buf); - return buf; -} - -/** - * @brief Deallocate a SSH buffer. - * - * \param[in] buffer The buffer to free. - */ -void ssh_buffer_free(struct ssh_buffer_struct *buffer) { - if (buffer == NULL) { - return; - } - buffer_verify(buffer); - - if (buffer->data) { - /* burn the data */ - BURN_BUFFER(buffer->data, buffer->allocated); - SAFE_FREE(buffer->data); - } - BURN_BUFFER(buffer, sizeof(struct ssh_buffer_struct)); - SAFE_FREE(buffer); -} - -/** - * @brief Sets the buffer as secure. - * - * A secure buffer will never leave cleartext data in the heap - * after being reallocated or freed. - * - * @param[in] buffer buffer to set secure. - */ -void ssh_buffer_set_secure(ssh_buffer buffer){ - buffer->secure = 1; -} - -static int realloc_buffer(struct ssh_buffer_struct *buffer, size_t needed) { - size_t smallest = 1; - char *new; - - buffer_verify(buffer); - - /* Find the smallest power of two which is greater or equal to needed */ - while(smallest <= needed) { - if (smallest == 0) { - return -1; - } - smallest <<= 1; - } - needed = smallest; - if (buffer->secure){ - new = malloc(needed); - if (new == NULL) { - return -1; - } - memcpy(new, buffer->data,buffer->used); - BURN_BUFFER(buffer->data, buffer->used); - SAFE_FREE(buffer->data); - } else { - new = realloc(buffer->data, needed); - if (new == NULL) { - buffer->data = NULL; - return -1; - } - } - buffer->data = new; - buffer->allocated = needed; - buffer_verify(buffer); - return 0; -} - -/** @internal - * @brief shifts a buffer to remove unused data in the beginning - * @param buffer SSH buffer - */ -static void buffer_shift(ssh_buffer buffer){ - uint32_t burn_pos = buffer->pos; - - buffer_verify(buffer); - if(buffer->pos==0) - return; - memmove(buffer->data, buffer->data + buffer->pos, buffer->used - buffer->pos); - buffer->used -= buffer->pos; - buffer->pos=0; - - if (buffer->secure){ - void *ptr = buffer->data + buffer->used; - BURN_BUFFER(ptr, burn_pos); - } - - buffer_verify(buffer); -} - -/** - * @internal - * - * @brief Reinitialize a SSH buffer. - * - * @param[in] buffer The buffer to reinitialize. - * - * @return 0 on success, < 0 on error. - */ -int ssh_buffer_reinit(struct ssh_buffer_struct *buffer) -{ - buffer_verify(buffer); - BURN_BUFFER(buffer->data, buffer->used); - buffer->used = 0; - buffer->pos = 0; - if(buffer->allocated > 127) { - if (realloc_buffer(buffer, 127) < 0) { - return -1; - } - } - buffer_verify(buffer); - return 0; -} - -/** - * @internal - * - * @brief Add data at the tail of a buffer. - * - * @param[in] buffer The buffer to add the data. - * - * @param[in] data A pointer to the data to add. - * - * @param[in] len The length of the data to add. - * - * @return 0 on success, < 0 on error. - */ -int ssh_buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint32_t len) -{ - buffer_verify(buffer); - - if (buffer->used + len < len) { - return -1; - } - - if (buffer->allocated < (buffer->used + len)) { - if(buffer->pos > 0) - buffer_shift(buffer); - if (realloc_buffer(buffer, buffer->used + len) < 0) { - return -1; - } - } - - memcpy(buffer->data+buffer->used, data, len); - buffer->used+=len; - buffer_verify(buffer); - return 0; -} - -/** - * @internal - * - * @brief Add a SSH string to the tail of a buffer. - * - * @param[in] buffer The buffer to add the string. - * - * @param[in] string The SSH String to add. - * - * @return 0 on success, < 0 on error. - */ -int buffer_add_ssh_string(struct ssh_buffer_struct *buffer, - struct ssh_string_struct *string) { - uint32_t len = 0; - - len = ssh_string_len(string); - if (ssh_buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) { - return -1; - } - - return 0; -} - -/** - * @internal - * - * @brief Add a 32 bits unsigned integer to the tail of a buffer. - * - * @param[in] buffer The buffer to add the integer. - * - * @param[in] data The 32 bits integer to add. - * - * @return 0 on success, -1 on error. - */ -int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data) -{ - int rc; - - rc = ssh_buffer_add_data(buffer, &data, sizeof(data)); - if (rc < 0) { - return -1; - } - - return 0; -} - -/** - * @internal - * - * @brief Add a 16 bits unsigned integer to the tail of a buffer. - * - * @param[in] buffer The buffer to add the integer. - * - * @param[in] data The 16 bits integer to add. - * - * @return 0 on success, -1 on error. - */ -int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data) -{ - int rc; - - rc = ssh_buffer_add_data(buffer, &data, sizeof(data)); - if (rc < 0) { - return -1; - } - - return 0; -} - -/** - * @internal - * - * @brief Add a 64 bits unsigned integer to the tail of a buffer. - * - * @param[in] buffer The buffer to add the integer. - * - * @param[in] data The 64 bits integer to add. - * - * @return 0 on success, -1 on error. - */ -int buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data) -{ - int rc; - - rc = ssh_buffer_add_data(buffer, &data, sizeof(data)); - if (rc < 0) { - return -1; - } - - return 0; -} - -/** - * @internal - * - * @brief Add a 8 bits unsigned integer to the tail of a buffer. - * - * @param[in] buffer The buffer to add the integer. - * - * @param[in] data The 8 bits integer to add. - * - * @return 0 on success, -1 on error. - */ -int buffer_add_u8(struct ssh_buffer_struct *buffer,uint8_t data) -{ - int rc; - - rc = ssh_buffer_add_data(buffer, &data, sizeof(uint8_t)); - if (rc < 0) { - return -1; - } - - return 0; -} - -/** - * @internal - * - * @brief Add data at the head of a buffer. - * - * @param[in] buffer The buffer to add the data. - * - * @param[in] data The data to prepend. - * - * @param[in] len The length of data to prepend. - * - * @return 0 on success, -1 on error. - */ -int buffer_prepend_data(struct ssh_buffer_struct *buffer, const void *data, - uint32_t len) { - buffer_verify(buffer); - - if(len <= buffer->pos){ - /* It's possible to insert data between begin and pos */ - memcpy(buffer->data + (buffer->pos - len), data, len); - buffer->pos -= len; - buffer_verify(buffer); - return 0; - } - /* pos isn't high enough */ - if (buffer->used - buffer->pos + len < len) { - return -1; - } - - if (buffer->allocated < (buffer->used - buffer->pos + len)) { - if (realloc_buffer(buffer, buffer->used - buffer->pos + len) < 0) { - return -1; - } - } - memmove(buffer->data + len, buffer->data + buffer->pos, buffer->used - buffer->pos); - memcpy(buffer->data, data, len); - buffer->used += len - buffer->pos; - buffer->pos = 0; - buffer_verify(buffer); - return 0; -} - -/** - * @internal - * - * @brief Append data from a buffer to the tail of another buffer. - * - * @param[in] buffer The destination buffer. - * - * @param[in] source The source buffer to append. It doesn't take the - * position of the buffer into account. - * - * @return 0 on success, -1 on error. - */ -int buffer_add_buffer(struct ssh_buffer_struct *buffer, - struct ssh_buffer_struct *source) -{ - int rc; - - rc = ssh_buffer_add_data(buffer, - buffer_get_rest(source), - buffer_get_rest_len(source)); - if (rc < 0) { - return -1; - } - - return 0; -} - -/** - * @brief Get a pointer on the head of a buffer. - * - * @param[in] buffer The buffer to get the head pointer. - * - * @return A data pointer on the head. It doesn't take the position - * into account. - * - * @warning Don't expect data to be nul-terminated. - * - * @see buffer_get_rest() - * @see buffer_get_len() - */ -void *ssh_buffer_get_begin(struct ssh_buffer_struct *buffer){ - return buffer->data; -} - -/** - * @internal - * - * @brief Get a pointer to the head of a buffer at the current position. - * - * @param[in] buffer The buffer to get the head pointer. - * - * @return A pointer to the data from current position. - * - * @see buffer_get_rest_len() - * @see buffer_get() - */ -void *buffer_get_rest(struct ssh_buffer_struct *buffer){ - return buffer->data + buffer->pos; -} - -/** - * @brief Get the length of the buffer, not counting position. - * - * @param[in] buffer The buffer to get the length from. - * - * @return The length of the buffer. - * - * @see buffer_get() - */ -uint32_t ssh_buffer_get_len(struct ssh_buffer_struct *buffer){ - return buffer->used; -} - -/** - * @internal - * - * @brief Get the length of the buffer from the current position. - * - * @param[in] buffer The buffer to get the length from. - * - * @return The length of the buffer. - * - * @see buffer_get_rest() - */ -uint32_t buffer_get_rest_len(struct ssh_buffer_struct *buffer){ - buffer_verify(buffer); - return buffer->used - buffer->pos; -} - -/** - * @internal - * - * @brief Advance the position in the buffer. - * - * This has effect to "eat" bytes at head of the buffer. - * - * @param[in] buffer The buffer to advance the position. - * - * @param[in] len The number of bytes to eat. - * - * @return The new size of the buffer. - */ -uint32_t buffer_pass_bytes(struct ssh_buffer_struct *buffer, uint32_t len){ - buffer_verify(buffer); - - if (buffer->pos + len < len || buffer->used < buffer->pos + len) { - return 0; - } - - buffer->pos+=len; - /* if the buffer is empty after having passed the whole bytes into it, we can clean it */ - if(buffer->pos==buffer->used){ - buffer->pos=0; - buffer->used=0; - } - buffer_verify(buffer); - return len; -} - -/** - * @internal - * - * @brief Cut the end of the buffer. - * - * @param[in] buffer The buffer to cut. - * - * @param[in] len The number of bytes to remove from the tail. - * - * @return The new size of the buffer. - */ -uint32_t buffer_pass_bytes_end(struct ssh_buffer_struct *buffer, uint32_t len){ - buffer_verify(buffer); - - if (buffer->used < len) { - return 0; - } - - buffer->used-=len; - buffer_verify(buffer); - return len; -} - -/** - * @internal - * - * @brief Get the remaining data out of the buffer and adjust the read pointer. - * - * @param[in] buffer The buffer to read. - * - * @param[in] data The data buffer where to store the data. - * - * @param[in] len The length to read from the buffer. - * - * @returns 0 if there is not enough data in buffer, len otherwise. - */ -uint32_t buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint32_t len){ - /* - * Check for a integer overflow first, then check if not enough data is in - * the buffer. - */ - if (buffer->pos + len < len || buffer->pos + len > buffer->used) { - return 0; - } - memcpy(data,buffer->data+buffer->pos,len); - buffer->pos+=len; - return len; /* no yet support for partial reads (is it really needed ?? ) */ -} - -/** - * @internal - * - * @brief Get a 8 bits unsigned int out of the buffer and adjusts the read - * pointer. - * - * @param[in] buffer The buffer to read. - * - * @param[in] data A pointer to a uint8_t where to store the data. - * - * @returns 0 if there is not enough data in buffer, 1 otherwise. - */ -int buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){ - return buffer_get_data(buffer,data,sizeof(uint8_t)); -} - -/** \internal - * \brief gets a 32 bits unsigned int out of the buffer. Adjusts the read pointer. - * \param buffer Buffer to read - * \param data pointer to a uint32_t where to store the data - * \returns 0 if there is not enough data in buffer - * \returns 4 otherwise. - */ -int buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){ - return buffer_get_data(buffer,data,sizeof(uint32_t)); -} -/** - * @internal - * - * @brief Get a 64 bits unsigned int out of the buffer and adjusts the read - * pointer. - * - * @param[in] buffer The buffer to read. - * - * @param[in] data A pointer to a uint64_t where to store the data. - * - * @returns 0 if there is not enough data in buffer, 8 otherwise. - */ -int buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){ - return buffer_get_data(buffer,data,sizeof(uint64_t)); -} - -/** - * @internal - * - * @brief Get a SSH String out of the buffer and adjusts the read pointer. - * - * @param[in] buffer The buffer to read. - * - * @returns The SSH String, NULL on error. - */ -struct ssh_string_struct *buffer_get_ssh_string(struct ssh_buffer_struct *buffer) { - uint32_t stringlen; - uint32_t hostlen; - struct ssh_string_struct *str = NULL; - - if (buffer_get_u32(buffer, &stringlen) == 0) { - return NULL; - } - hostlen = ntohl(stringlen); - /* verify if there is enough space in buffer to get it */ - if (buffer->pos + hostlen < hostlen || buffer->pos + hostlen > buffer->used) { - return NULL; /* it is indeed */ - } - str = ssh_string_new(hostlen); - if (str == NULL) { - return NULL; - } - if (buffer_get_data(buffer, ssh_string_data(str), hostlen) != hostlen) { - /* should never happen */ - SAFE_FREE(str); - return NULL; - } - - return str; -} - -/** - * @internal - * - * @brief Get a mpint out of the buffer and adjusts the read pointer. - * - * @note This function is SSH-1 only. - * - * @param[in] buffer The buffer to read. - * - * @returns The SSH String containing the mpint, NULL on error. - */ -struct ssh_string_struct *buffer_get_mpint(struct ssh_buffer_struct *buffer) { - uint16_t bits; - uint32_t len; - struct ssh_string_struct *str = NULL; - - if (buffer_get_data(buffer, &bits, sizeof(uint16_t)) != sizeof(uint16_t)) { - return NULL; - } - bits = ntohs(bits); - len = (bits + 7) / 8; - if (buffer->pos + len < len || buffer->pos + len > buffer->used) { - return NULL; - } - str = ssh_string_new(len); - if (str == NULL) { - return NULL; - } - if (buffer_get_data(buffer, ssh_string_data(str), len) != len) { - SAFE_FREE(str); - return NULL; - } - return str; -} - -/** @internal - * @brief Add multiple values in a buffer on a single function call - * @param[in] buffer The buffer to add to - * @param[in] format A format string of arguments. - * @param[in] ap A va_list of arguments. - * @returns SSH_OK on success - * SSH_ERROR on error - * @see ssh_buffer_add_format() for format list values. - */ -int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer, const char *format, va_list ap){ - int rc = SSH_ERROR; - const char *p; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - ssh_string string; - void *data; - } o; - char *cstring; - bignum b; - size_t len; - - for (p = format; *p != '\0'; p++) { - switch(*p) { - case 'b': - o.byte = (uint8_t)va_arg(ap, unsigned int); - rc = buffer_add_u8(buffer, o.byte); - break; - case 'w': - o.word = (uint16_t)va_arg(ap, unsigned int); - o.word = htons(o.word); - rc = buffer_add_u16(buffer, o.word); - break; - case 'd': - o.dword = va_arg(ap, uint32_t); - o.dword = htonl(o.dword); - rc = buffer_add_u32(buffer, o.dword); - break; - case 'q': - o.qword = va_arg(ap, uint64_t); - o.qword = htonll(o.qword); - rc = buffer_add_u64(buffer, o.qword); - break; - case 'S': - o.string = va_arg(ap, ssh_string); - rc = buffer_add_ssh_string(buffer, o.string); - o.string = NULL; - break; - case 's': - cstring = va_arg(ap, char *); - len = strlen(cstring); - rc = buffer_add_u32(buffer, htonl(len)); - if (rc == SSH_OK){ - rc = ssh_buffer_add_data(buffer, cstring, len); - } - cstring = NULL; - break; - case 'P': - len = va_arg(ap, size_t); - o.data = va_arg(ap, void *); - rc = ssh_buffer_add_data(buffer, o.data, len); - o.data = NULL; - break; - case 'B': - b = va_arg(ap, bignum); - o.string = make_bignum_string(b); - if(o.string == NULL){ - rc = SSH_ERROR; - break; - } - rc = buffer_add_ssh_string(buffer, o.string); - SAFE_FREE(o.string); - break; - case 't': - cstring = va_arg(ap, char *); - len = strlen(cstring); - rc = ssh_buffer_add_data(buffer, cstring, len); - cstring = NULL; - break; - default: - SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p); - rc = SSH_ERROR; - } - if (rc != SSH_OK){ - break; - } - } - - if (rc != SSH_ERROR){ - /* verify that the last hidden argument is correct */ - o.dword = va_arg(ap, uint32_t); - if (o.dword != SSH_BUFFER_PACK_END){ - rc = SSH_ERROR; - } - } - return rc; -} - -/** @internal - * @brief Add multiple values in a buffer on a single function call - * @param[in] buffer The buffer to add to - * @param[in] format A format string of arguments. This string contains single - * letters describing the order and type of arguments: - * 'b': uint8_t (pushed in network byte order) - * 'w': uint16_t (pushed in network byte order) - * 'd': uint32_t (pushed in network byte order) - * 'q': uint64_t (pushed in network byte order) - * 'S': ssh_string - * 's': char * (C string, pushed as SSH string) - * 't': char * (C string, pushed as free text) - * 'P': size_t, void * (len of data, pointer to data) - * only pushes data. - * 'B': bignum (pushed as SSH string) - * @returns SSH_OK on success - * SSH_ERROR on error - * @warning when using 'P' with a constant size (e.g. 8), do not - * forget to cast to (size_t). - */ -int _ssh_buffer_pack(struct ssh_buffer_struct *buffer, const char *format, ...){ - va_list ap; - int rc; - - va_start(ap, format); - rc = ssh_buffer_pack_va(buffer, format, ap); - va_end(ap); - return rc; -} - -/** @internal - * @brief Get multiple values from a buffer on a single function call - * @param[in] buffer The buffer to get from - * @param[in] format A format string of arguments. - * @param[in] ap A va_list of arguments. - * @returns SSH_OK on success - * SSH_ERROR on error - * @see ssh_buffer_get_format() for format list values. - */ -int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer, const char *format, va_list ap){ - int rc = SSH_ERROR; - const char *p, *last; - union { - uint8_t *byte; - uint16_t *word; - uint32_t *dword; - uint64_t *qword; - ssh_string *string; - char **cstring; - void **data; - } o; - size_t len, rlen; - uint32_t u32len; - va_list ap_copy; - - /* copy the argument list in case a rollback is needed */ - va_copy(ap_copy, ap); - - for (p = format; *p != '\0'; p++) { - switch (*p) { - case 'b': - o.byte = va_arg(ap, uint8_t *); - rlen = buffer_get_u8(buffer, o.byte); - rc = rlen==1 ? SSH_OK : SSH_ERROR; - break; - case 'w': - o.word = va_arg(ap, uint16_t *); - rlen = buffer_get_data(buffer, o.word, sizeof(uint16_t)); - *o.word = ntohs(*o.word); - rc = rlen==2 ? SSH_OK : SSH_ERROR; - break; - case 'd': - o.dword = va_arg(ap, uint32_t *); - rlen = buffer_get_u32(buffer, o.dword); - *o.dword = ntohl(*o.dword); - rc = rlen==4 ? SSH_OK : SSH_ERROR; - break; - case 'q': - o.qword = va_arg(ap, uint64_t*); - rlen = buffer_get_u64(buffer, o.qword); - *o.qword = ntohll(*o.qword); - rc = rlen==8 ? SSH_OK : SSH_ERROR; - break; - case 'S': - o.string = va_arg(ap, ssh_string *); - *o.string = buffer_get_ssh_string(buffer); - rc = *o.string != NULL ? SSH_OK : SSH_ERROR; - o.string = NULL; - break; - case 's': - o.cstring = va_arg(ap, char **); - *o.cstring = NULL; - rc = buffer_get_u32(buffer, &u32len); - if (rc != 4){ - rc = SSH_ERROR; - break; - } - len = ntohl(u32len); - if (len > UINT_MAX - 1){ - rc = SSH_ERROR; - break; - } - *o.cstring = malloc(len + 1); - if (*o.cstring == NULL){ - rc = SSH_ERROR; - break; - } - rlen = buffer_get_data(buffer, *o.cstring, len); - if (rlen != len){ - SAFE_FREE(*o.cstring); - rc = SSH_ERROR; - break; - } - (*o.cstring)[len] = '\0'; - o.cstring = NULL; - rc = SSH_OK; - break; - case 'P': - len = va_arg(ap, size_t); - o.data = va_arg(ap, void **); - *o.data = malloc(len); - if(*o.data == NULL){ - rc = SSH_ERROR; - break; - } - rlen = buffer_get_data(buffer, *o.data, len); - if (rlen != len){ - SAFE_FREE(*o.data); - rc = SSH_ERROR; - break; - } - o.data = NULL; - rc = SSH_OK; - break; - default: - SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p); - rc = SSH_ERROR; - } - if (rc != SSH_OK) { - break; - } - } - if (rc != SSH_ERROR){ - /* verify that the last hidden argument is correct */ - uint32_t canary = va_arg(ap, uint32_t); - if (canary != SSH_BUFFER_PACK_END){ - rc = SSH_ERROR; - } - } - if (rc != SSH_OK){ - /* Reset the format string and erase everything that was allocated */ - last = p; - for(p=format;p - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include "libssh/callbacks.h" -#include "libssh/session.h" - - -/* LEGACY */ -static void ssh_legacy_log_callback(int priority, - const char *function, - const char *buffer, - void *userdata) -{ - ssh_session session = (ssh_session)userdata; - ssh_log_callback log_fn = session->common.callbacks->log_function; - void *log_data = session->common.callbacks->userdata; - - (void)function; /* unused */ - - log_fn(session, priority, buffer, log_data); -} - -int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) { - if (session == NULL || cb == NULL) { - return SSH_ERROR; - } - - if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ - ssh_set_error(session,SSH_FATAL, - "Invalid callback passed in (badly initialized)"); - - return SSH_ERROR; - } - session->common.callbacks = cb; - - /* LEGACY */ - if (ssh_get_log_callback() == NULL && cb->log_function) { - ssh_set_log_callback(ssh_legacy_log_callback); - ssh_set_log_userdata(session); - } - - return 0; -} - -int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb) { - ssh_session session = NULL; - if (channel == NULL || cb == NULL) { - return SSH_ERROR; - } - session = channel->session; - - if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ - ssh_set_error(session,SSH_FATAL, - "Invalid channel callback passed in (badly initialized)"); - - return SSH_ERROR; - } - channel->callbacks = cb; - - return 0; -} - -int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb){ - if (session == NULL || cb == NULL) { - return SSH_ERROR; - } - - if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){ - ssh_set_error(session,SSH_FATAL, - "Invalid callback passed in (badly initialized)"); - - return SSH_ERROR; - } - session->server_callbacks = cb; - - return 0; -} diff --git a/libssh/src/channels.c b/libssh/src/channels.c deleted file mode 100644 index 7a4e71fe..00000000 --- a/libssh/src/channels.c +++ /dev/null @@ -1,3418 +0,0 @@ -/* - * channels.c - SSH channel functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis - * Copyright (c) 2009-2013 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/priv.h" -#include "libssh/ssh2.h" -#include "libssh/buffer.h" -#include "libssh/packet.h" -#include "libssh/socket.h" -#include "libssh/channels.h" -#include "libssh/session.h" -#include "libssh/misc.h" -#include "libssh/messages.h" -#if WITH_SERVER -#include "libssh/server.h" -#endif - -#define WINDOWBASE 1280000 -#define WINDOWLIMIT (WINDOWBASE/2) - -/* - * All implementations MUST be able to process packets with an - * uncompressed payload length of 32768 bytes or less and a total packet - * size of 35000 bytes or less. - */ -#define CHANNEL_MAX_PACKET 32768 -#define CHANNEL_INITIAL_WINDOW 64000 - -/** - * @defgroup libssh_channel The SSH channel functions - * @ingroup libssh - * - * Functions that manage a SSH channel. - * - * @{ - */ - -static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet); - -/** - * @brief Allocate a new channel. - * - * @param[in] session The ssh session to use. - * - * @return A pointer to a newly allocated channel, NULL on error. - */ -ssh_channel ssh_channel_new(ssh_session session) { - ssh_channel channel = NULL; - - if(session == NULL) { - return NULL; - } - - channel = malloc(sizeof(struct ssh_channel_struct)); - if (channel == NULL) { - ssh_set_error_oom(session); - return NULL; - } - memset(channel,0,sizeof(struct ssh_channel_struct)); - - channel->stdout_buffer = ssh_buffer_new(); - if (channel->stdout_buffer == NULL) { - ssh_set_error_oom(session); - SAFE_FREE(channel); - return NULL; - } - - channel->stderr_buffer = ssh_buffer_new(); - if (channel->stderr_buffer == NULL) { - ssh_set_error_oom(session); - ssh_buffer_free(channel->stdout_buffer); - SAFE_FREE(channel); - return NULL; - } - - channel->session = session; - channel->version = session->version; - channel->exit_status = -1; - channel->flags = SSH_CHANNEL_FLAG_NOT_BOUND; - - if(session->channels == NULL) { - session->channels = ssh_list_new(); - } - ssh_list_prepend(session->channels, channel); - return channel; -} - -/** - * @internal - * - * @brief Create a new channel identifier. - * - * @param[in] session The SSH session to use. - * - * @return The new channel identifier. - */ -uint32_t ssh_channel_new_id(ssh_session session) { - return ++(session->maxchannel); -} - -/** - * @internal - * - * @brief Handle a SSH_PACKET_CHANNEL_OPEN_CONFIRMATION packet. - * - * Constructs the channel object. - */ -SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ - uint32_t channelid=0; - ssh_channel channel; - int rc; - (void)type; - (void)user; - - SSH_LOG(SSH_LOG_PACKET,"Received SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"); - - rc = ssh_buffer_unpack(packet, "d", &channelid); - if (rc != SSH_OK) - goto error; - channel=ssh_channel_from_local(session,channelid); - if(channel==NULL){ - ssh_set_error(session, SSH_FATAL, - "Unknown channel id %lu", - (long unsigned int) channelid); - /* TODO: Set error marking in channel object */ - - return SSH_PACKET_USED; - } - - rc = ssh_buffer_unpack(packet, "ddd", - &channel->remote_channel, - &channel->remote_window, - &channel->remote_maxpacket); - if (rc != SSH_OK) - goto error; - - SSH_LOG(SSH_LOG_PROTOCOL, - "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d", - channel->local_channel, - channel->remote_channel); - SSH_LOG(SSH_LOG_PROTOCOL, - "Remote window : %lu, maxpacket : %lu", - (long unsigned int) channel->remote_window, - (long unsigned int) channel->remote_maxpacket); - - channel->state = SSH_CHANNEL_STATE_OPEN; - channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND; - return SSH_PACKET_USED; - -error: - ssh_set_error(session, SSH_FATAL, "Invalid packet"); - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handle a SSH_CHANNEL_OPEN_FAILURE and set the state of the channel. - */ -SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){ - - ssh_channel channel; - char *error = NULL; - uint32_t code; - int rc; - (void)user; - (void)type; - - channel=channel_from_msg(session,packet); - if(channel==NULL){ - SSH_LOG(SSH_LOG_RARE,"Invalid channel in packet"); - return SSH_PACKET_USED; - } - - rc = ssh_buffer_unpack(packet, "ds", &code, &error); - if (rc != SSH_OK){ - ssh_set_error(session, SSH_FATAL, "Invalid packet"); - return SSH_PACKET_USED; - } - - ssh_set_error(session, SSH_REQUEST_DENIED, - "Channel opening failure: channel %u error (%lu) %s", - channel->local_channel, - (long unsigned int) code, - error); - SAFE_FREE(error); - channel->state=SSH_CHANNEL_STATE_OPEN_DENIED; - return SSH_PACKET_USED; -} - -static int ssh_channel_open_termination(void *c){ - ssh_channel channel = (ssh_channel) c; - if (channel->state != SSH_CHANNEL_STATE_OPENING || - channel->session->session_state == SSH_SESSION_STATE_ERROR) - return 1; - else - return 0; -} - -/** - * @internal - * - * @brief Open a channel by sending a SSH_OPEN_CHANNEL message and - * wait for the reply. - * - * @param[in] channel The current channel. - * - * @param[in] type A C string describing the kind of channel (e.g. "exec"). - * - * @param[in] window The receiving window of the channel. The window is the - * maximum size of data that can stay in buffers and - * network. - * - * @param[in] maxpacket The maximum packet size allowed (like MTU). - * - * @param[in] payload The buffer containing additional payload for the query. - */ -static int channel_open(ssh_channel channel, const char *type, int window, - int maxpacket, ssh_buffer payload) { - ssh_session session = channel->session; - int err=SSH_ERROR; - int rc; - - switch(channel->state){ - case SSH_CHANNEL_STATE_NOT_OPEN: - break; - case SSH_CHANNEL_STATE_OPENING: - goto pending; - case SSH_CHANNEL_STATE_OPEN: - case SSH_CHANNEL_STATE_CLOSED: - case SSH_CHANNEL_STATE_OPEN_DENIED: - goto end; - default: - ssh_set_error(session,SSH_FATAL,"Bad state in channel_open: %d",channel->state); - } - channel->local_channel = ssh_channel_new_id(session); - channel->local_maxpacket = maxpacket; - channel->local_window = window; - - SSH_LOG(SSH_LOG_PROTOCOL, - "Creating a channel %d with %d window and %d max packet", - channel->local_channel, window, maxpacket); - - rc = ssh_buffer_pack(session->out_buffer, - "bsddd", - SSH2_MSG_CHANNEL_OPEN, - type, - channel->local_channel, - channel->local_window, - channel->local_maxpacket); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - return err; - } - - if (payload != NULL) { - if (buffer_add_buffer(session->out_buffer, payload) < 0) { - ssh_set_error_oom(session); - - return err; - } - } - channel->state = SSH_CHANNEL_STATE_OPENING; - if (packet_send(session) == SSH_ERROR) { - - return err; - } - - SSH_LOG(SSH_LOG_PACKET, - "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d", - type, channel->local_channel); -pending: - /* wait until channel is opened by server */ - err = ssh_handle_packets_termination(session, - SSH_TIMEOUT_DEFAULT, - ssh_channel_open_termination, - channel); - - if (session->session_state == SSH_SESSION_STATE_ERROR) - err = SSH_ERROR; -end: - if(channel->state == SSH_CHANNEL_STATE_OPEN) - err=SSH_OK; - - return err; -} - -/* return channel with corresponding local id, or NULL if not found */ -ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) { - struct ssh_iterator *it; - ssh_channel channel; - - for (it = ssh_list_get_iterator(session->channels); it != NULL ; it=it->next) { - channel = ssh_iterator_value(ssh_channel, it); - if (channel == NULL) { - continue; - } - if (channel->local_channel == id) { - return channel; - } - } - - return NULL; -} - -/** - * @internal - * @brief grows the local window and send a packet to the other party - * @param session SSH session - * @param channel SSH channel - * @param minimumsize The minimum acceptable size for the new window. - */ -static int grow_window(ssh_session session, ssh_channel channel, int minimumsize) { - uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE; - int rc; - -#ifdef WITH_SSH1 - if (session->version == 1){ - channel->remote_window = new_window; - - return SSH_OK; - } -#endif - if(new_window <= channel->local_window){ - SSH_LOG(SSH_LOG_PROTOCOL, - "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)", - channel->local_channel, channel->remote_channel, new_window, - channel->local_window); - - return SSH_OK; - } - /* WINDOW_ADJUST packet needs a relative increment rather than an absolute - * value, so we give here the missing bytes needed to reach new_window - */ - rc = ssh_buffer_pack(session->out_buffer, - "bdd", - SSH2_MSG_CHANNEL_WINDOW_ADJUST, - channel->remote_channel, - new_window - channel->local_window); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - - if (packet_send(session) == SSH_ERROR) { - goto error; - } - - SSH_LOG(SSH_LOG_PROTOCOL, - "growing window (channel %d:%d) to %d bytes", - channel->local_channel, - channel->remote_channel, - new_window); - - channel->local_window = new_window; - - return SSH_OK; -error: - ssh_buffer_reinit(session->out_buffer); - - return SSH_ERROR; -} - -/** - * @internal - * - * @brief Parse a channel-related packet to resolve it to a ssh_channel. - * - * This works on SSH1 sessions too. - * - * @param[in] session The current SSH session. - * - * @param[in] packet The buffer to parse packet from. The read pointer will - * be moved after the call. - * - * @returns The related ssh_channel, or NULL if the channel is - * unknown or the packet is invalid. - */ -static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) { - ssh_channel channel; - uint32_t chan; - int rc; -#ifdef WITH_SSH1 - /* With SSH1, the channel is always the first one */ - if(session->version==1) - return ssh_get_channel1(session); -#endif - rc = ssh_buffer_unpack(packet,"d",&chan); - if (rc != SSH_OK) { - ssh_set_error(session, SSH_FATAL, - "Getting channel from message: short read"); - return NULL; - } - - channel = ssh_channel_from_local(session, chan); - if (channel == NULL) { - ssh_set_error(session, SSH_FATAL, - "Server specified invalid channel %lu", - (long unsigned int) chan); - } - - return channel; -} - -SSH_PACKET_CALLBACK(channel_rcv_change_window) { - ssh_channel channel; - uint32_t bytes; - int rc; - (void)user; - (void)type; - - channel = channel_from_msg(session,packet); - if (channel == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - } - - rc = ssh_buffer_unpack(packet, "d", &bytes); - if (channel == NULL || rc != SSH_OK) { - SSH_LOG(SSH_LOG_PACKET, - "Error getting a window adjust message: invalid packet"); - - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_PROTOCOL, - "Adding %d bytes to channel (%d:%d) (from %d bytes)", - bytes, - channel->local_channel, - channel->remote_channel, - channel->remote_window); - - channel->remote_window += bytes; - - return SSH_PACKET_USED; -} - -/* is_stderr is set to 1 if the data are extended, ie stderr */ -SSH_PACKET_CALLBACK(channel_rcv_data){ - ssh_channel channel; - ssh_string str; - ssh_buffer buf; - size_t len; - int is_stderr; - int rest; - (void)user; - - if(type==SSH2_MSG_CHANNEL_DATA) - is_stderr=0; - else - is_stderr=1; - - channel = channel_from_msg(session,packet); - if (channel == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, - "%s", ssh_get_error(session)); - - return SSH_PACKET_USED; - } - - if (is_stderr) { - uint32_t ignore; - /* uint32 data type code. we can ignore it */ - buffer_get_u32(packet, &ignore); - } - - str = buffer_get_ssh_string(packet); - if (str == NULL) { - SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!"); - - return SSH_PACKET_USED; - } - len = ssh_string_len(str); - - SSH_LOG(SSH_LOG_PACKET, - "Channel receiving %" PRIdS " bytes data in %d (local win=%d remote win=%d)", - len, - is_stderr, - channel->local_window, - channel->remote_window); - - /* What shall we do in this case? Let's accept it anyway */ - if (len > channel->local_window) { - SSH_LOG(SSH_LOG_RARE, - "Data packet too big for our window(%" PRIdS " vs %d)", - len, - channel->local_window); - } - - if (channel_default_bufferize(channel, ssh_string_data(str), len, - is_stderr) < 0) { - ssh_string_free(str); - - return SSH_PACKET_USED; - } - - if (len <= channel->local_window) { - channel->local_window -= len; - } else { - channel->local_window = 0; /* buggy remote */ - } - - SSH_LOG(SSH_LOG_PACKET, - "Channel windows are now (local win=%d remote win=%d)", - channel->local_window, - channel->remote_window); - - ssh_string_free(str); - - if(ssh_callbacks_exists(channel->callbacks, channel_data_function)) { - if(is_stderr) { - buf = channel->stderr_buffer; - } else { - buf = channel->stdout_buffer; - } - rest = channel->callbacks->channel_data_function(channel->session, - channel, - buffer_get_rest(buf), - buffer_get_rest_len(buf), - is_stderr, - channel->callbacks->userdata); - if(rest > 0) { - if (channel->counter != NULL) { - channel->counter->in_bytes += rest; - } - buffer_pass_bytes(buf, rest); - } - if (channel->local_window + buffer_get_rest_len(buf) < WINDOWLIMIT) { - if (grow_window(session, channel, 0) < 0) { - return -1; - } - } - } - - return SSH_PACKET_USED; -} - -SSH_PACKET_CALLBACK(channel_rcv_eof) { - ssh_channel channel; - (void)user; - (void)type; - - channel = channel_from_msg(session,packet); - if (channel == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_PACKET, - "Received eof on channel (%d:%d)", - channel->local_channel, - channel->remote_channel); - /* channel->remote_window = 0; */ - channel->remote_eof = 1; - - if(ssh_callbacks_exists(channel->callbacks, channel_eof_function)) { - channel->callbacks->channel_eof_function(channel->session, - channel, - channel->callbacks->userdata); - } - - return SSH_PACKET_USED; -} - -SSH_PACKET_CALLBACK(channel_rcv_close) { - ssh_channel channel; - (void)user; - (void)type; - - channel = channel_from_msg(session,packet); - if (channel == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_PACKET, - "Received close on channel (%d:%d)", - channel->local_channel, - channel->remote_channel); - - if ((channel->stdout_buffer && - buffer_get_rest_len(channel->stdout_buffer) > 0) || - (channel->stderr_buffer && - buffer_get_rest_len(channel->stderr_buffer) > 0)) { - channel->delayed_close = 1; - } else { - channel->state = SSH_CHANNEL_STATE_CLOSED; - } - if (channel->remote_eof == 0) { - SSH_LOG(SSH_LOG_PACKET, - "Remote host not polite enough to send an eof before close"); - } - channel->remote_eof = 1; - /* - * The remote eof doesn't break things if there was still data into read - * buffer because the eof is ignored until the buffer is empty. - */ - - if(ssh_callbacks_exists(channel->callbacks, channel_close_function)) { - channel->callbacks->channel_close_function(channel->session, - channel, - channel->callbacks->userdata); - } - channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE; - if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL) - ssh_channel_do_free(channel); - - return SSH_PACKET_USED; -} - -SSH_PACKET_CALLBACK(channel_rcv_request) { - ssh_channel channel; - char *request=NULL; - uint8_t status; - int rc; - (void)user; - (void)type; - - channel = channel_from_msg(session,packet); - if (channel == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS,"%s", ssh_get_error(session)); - return SSH_PACKET_USED; - } - - rc = ssh_buffer_unpack(packet, "sb", - &request, - &status); - if (rc != SSH_OK) { - SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - return SSH_PACKET_USED; - } - - if (strcmp(request,"exit-status") == 0) { - uint32_t exit_status = 0; - - SAFE_FREE(request); - rc = ssh_buffer_unpack(packet, "d", &exit_status); - SSH_LOG(SSH_LOG_PACKET, "received exit-status %d", channel->exit_status); - - if(ssh_callbacks_exists(channel->callbacks, channel_exit_status_function)) { - channel->callbacks->channel_exit_status_function(channel->session, - channel, - channel->exit_status, - channel->callbacks->userdata); - } - - return SSH_PACKET_USED; - } - - if (strcmp(request,"signal") == 0) { - char *sig = NULL; - - SAFE_FREE(request); - SSH_LOG(SSH_LOG_PACKET, "received signal"); - - rc = ssh_buffer_unpack(packet, "s", &sig); - if (rc != SSH_OK) { - SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_PACKET, - "Remote connection sent a signal SIG %s", sig); - if(ssh_callbacks_exists(channel->callbacks, channel_signal_function)) { - channel->callbacks->channel_signal_function(channel->session, - channel, - sig, - channel->callbacks->userdata); - } - SAFE_FREE(sig); - - return SSH_PACKET_USED; - } - - if (strcmp(request, "exit-signal") == 0) { - const char *core = "(core dumped)"; - char *sig = NULL; - char *errmsg = NULL; - char *lang = NULL; - uint8_t core_dumped; - - SAFE_FREE(request); - - rc = ssh_buffer_unpack(packet, "sbs", - &sig, /* signal name */ - &core_dumped, /* core dumped */ - &errmsg, /* error message */ - &lang); - if (rc != SSH_OK) { - SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST"); - return SSH_PACKET_USED; - } - - if (core_dumped == 0) { - core = ""; - } - - SSH_LOG(SSH_LOG_PACKET, - "Remote connection closed by signal SIG %s %s", sig, core); - if(ssh_callbacks_exists(channel->callbacks, channel_exit_signal_function)) { - channel->callbacks->channel_exit_signal_function(channel->session, - channel, - sig, core_dumped, errmsg, lang, - channel->callbacks->userdata); - } - - SAFE_FREE(lang); - SAFE_FREE(errmsg); - SAFE_FREE(sig); - - return SSH_PACKET_USED; - } - if(strcmp(request,"keepalive@openssh.com")==0){ - SAFE_FREE(request); - SSH_LOG(SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive"); - - rc = ssh_buffer_pack(session->out_buffer, - "bd", - SSH2_MSG_CHANNEL_FAILURE, - channel->remote_channel); - if (rc != SSH_OK) { - return SSH_PACKET_USED; - } - packet_send(session); - - return SSH_PACKET_USED; - } - - if (strcmp(request, "auth-agent-req@openssh.com") == 0) { - SAFE_FREE(request); - SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request"); - if(ssh_callbacks_exists(channel->callbacks, channel_auth_agent_req_function)) { - channel->callbacks->channel_auth_agent_req_function(channel->session, channel, - channel->callbacks->userdata); - } - - return SSH_PACKET_USED; - } -#ifdef WITH_SERVER - /* If we are here, that means we have a request that is not in the understood - * client requests. That means we need to create a ssh message to be passed - * to the user code handling ssh messages - */ - ssh_message_handle_channel_request(session,channel,packet,request,status); -#else - SSH_LOG(SSH_LOG_WARNING, "Unhandled channel request %s", request); -#endif - - SAFE_FREE(request); - - return SSH_PACKET_USED; -} - -/* - * When data has been received from the ssh server, it can be applied to the - * known user function, with help of the callback, or inserted here - * - * FIXME is the window changed? - */ -int channel_default_bufferize(ssh_channel channel, void *data, int len, - int is_stderr) { - ssh_session session; - - if(channel == NULL) { - return -1; - } - - session = channel->session; - - if(data == NULL) { - ssh_set_error_invalid(session); - return -1; - } - - SSH_LOG(SSH_LOG_PACKET, - "placing %d bytes into channel buffer (stderr=%d)", len, is_stderr); - if (is_stderr == 0) { - /* stdout */ - if (channel->stdout_buffer == NULL) { - channel->stdout_buffer = ssh_buffer_new(); - if (channel->stdout_buffer == NULL) { - ssh_set_error_oom(session); - return -1; - } - } - - if (ssh_buffer_add_data(channel->stdout_buffer, data, len) < 0) { - ssh_set_error_oom(session); - ssh_buffer_free(channel->stdout_buffer); - channel->stdout_buffer = NULL; - return -1; - } - } else { - /* stderr */ - if (channel->stderr_buffer == NULL) { - channel->stderr_buffer = ssh_buffer_new(); - if (channel->stderr_buffer == NULL) { - ssh_set_error_oom(session); - return -1; - } - } - - if (ssh_buffer_add_data(channel->stderr_buffer, data, len) < 0) { - ssh_set_error_oom(session); - ssh_buffer_free(channel->stderr_buffer); - channel->stderr_buffer = NULL; - return -1; - } - } - - return 0; -} - -/** - * @brief Open a session channel (suited for a shell, not TCP forwarding). - * - * @param[in] channel An allocated channel. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * - * @see ssh_channel_open_forward() - * @see ssh_channel_request_env() - * @see ssh_channel_request_shell() - * @see ssh_channel_request_exec() - */ -int ssh_channel_open_session(ssh_channel channel) { - if(channel == NULL) { - return SSH_ERROR; - } - -#ifdef WITH_SSH1 - if (channel->session->version == 1) { - return channel_open_session1(channel); - } -#endif - - return channel_open(channel, - "session", - CHANNEL_INITIAL_WINDOW, - CHANNEL_MAX_PACKET, - NULL); -} - -/** - * @brief Open an agent authentication forwarding channel. This type of channel - * can be opened by a server towards a client in order to provide SSH-Agent services - * to the server-side process. This channel can only be opened if the client - * claimed support by sending a channel request beforehand. - * - * @param[in] channel An allocated channel. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * - * @see ssh_channel_open_forward() - */ -int ssh_channel_open_auth_agent(ssh_channel channel){ - if(channel == NULL) { - return SSH_ERROR; - } - -#ifdef WITH_SSH1 - if (channel->session->version == 1) { - return SSH_ERROR; - } -#endif - - return channel_open(channel, - "auth-agent@openssh.com", - CHANNEL_INITIAL_WINDOW, - CHANNEL_MAX_PACKET, - NULL); -} - - -/** - * @brief Open a TCP/IP forwarding channel. - * - * @param[in] channel An allocated channel. - * - * @param[in] remotehost The remote host to connected (host name or IP). - * - * @param[in] remoteport The remote port. - * - * @param[in] sourcehost The numeric IP address of the machine from where the - * connection request originates. This is mostly for - * logging purposes. - * - * @param[in] localport The port on the host from where the connection - * originated. This is mostly for logging purposes. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * - * @warning This function does not bind the local port and does not automatically - * forward the content of a socket to the channel. You still have to - * use channel_read and channel_write for this. - */ -int ssh_channel_open_forward(ssh_channel channel, const char *remotehost, - int remoteport, const char *sourcehost, int localport) { - ssh_session session; - ssh_buffer payload = NULL; - ssh_string str = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return rc; - } - - session = channel->session; - - if(remotehost == NULL || sourcehost == NULL) { - ssh_set_error_invalid(session); - return rc; - } - - payload = ssh_buffer_new(); - if (payload == NULL) { - ssh_set_error_oom(session); - goto error; - } - - rc = ssh_buffer_pack(payload, - "sdsd", - remotehost, - remoteport, - sourcehost, - localport); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - - rc = channel_open(channel, - "direct-tcpip", - CHANNEL_INITIAL_WINDOW, - CHANNEL_MAX_PACKET, - payload); - -error: - ssh_buffer_free(payload); - ssh_string_free(str); - - return rc; -} - - -/** - * @brief Close and free a channel. - * - * @param[in] channel The channel to free. - * - * @warning Any data unread on this channel will be lost. - */ -void ssh_channel_free(ssh_channel channel) { - ssh_session session; - - if (channel == NULL) { - return; - } - - session = channel->session; - if (session->alive && channel->state == SSH_CHANNEL_STATE_OPEN) { - ssh_channel_close(channel); - } - channel->flags |= SSH_CHANNEL_FLAG_FREED_LOCAL; - - /* The idea behind the flags is the following : it is well possible - * that a client closes a channel that stills exists on the server side. - * We definitively close the channel when we receive a close message *and* - * the user closed it. - */ - if((channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) - || (channel->flags & SSH_CHANNEL_FLAG_NOT_BOUND)){ - ssh_channel_do_free(channel); - } -} - -/** - * @internal - * @brief Effectively free a channel, without caring about flags - */ - -void ssh_channel_do_free(ssh_channel channel){ - struct ssh_iterator *it; - ssh_session session = channel->session; - it = ssh_list_find(session->channels, channel); - if(it != NULL){ - ssh_list_remove(session->channels, it); - } - ssh_buffer_free(channel->stdout_buffer); - ssh_buffer_free(channel->stderr_buffer); - - /* debug trick to catch use after frees */ - memset(channel, 'X', sizeof(struct ssh_channel_struct)); - SAFE_FREE(channel); -} - -/** - * @brief Send an end of file on the channel. - * - * This doesn't close the channel. You may still read from it but not write. - * - * @param[in] channel The channel to send the eof to. - * - * @return SSH_OK on success, SSH_ERROR if an error occurred. - * - * Example: -@code - rc = ssh_channel_send_eof(channel); - if (rc == SSH_ERROR) { - return -1; - } - while(!ssh_channel_is_eof(channel)) { - rc = ssh_channel_read(channel, buf, sizeof(buf), 0); - if (rc == SSH_ERROR) { - return -1; - } - } - ssh_channel_close(channel); -@endcode - * - * @see ssh_channel_close() - * @see ssh_channel_free() - * @see ssh_channel_is_eof() - */ -int ssh_channel_send_eof(ssh_channel channel){ - ssh_session session; - int rc = SSH_ERROR; - int err; - - if(channel == NULL) { - return rc; - } - - session = channel->session; - - err = ssh_buffer_pack(session->out_buffer, - "bd", - SSH2_MSG_CHANNEL_EOF, - channel->remote_channel); - if (err != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - - rc = packet_send(session); - SSH_LOG(SSH_LOG_PACKET, - "Sent a EOF on client channel (%d:%d)", - channel->local_channel, - channel->remote_channel); - - rc = ssh_channel_flush(channel); - if(rc == SSH_ERROR) - goto error; - - channel->local_eof = 1; - - return rc; -error: - ssh_buffer_reinit(session->out_buffer); - - return rc; -} - -/** - * @brief Close a channel. - * - * This sends an end of file and then closes the channel. You won't be able - * to recover any data the server was going to send or was in buffers. - * - * @param[in] channel The channel to close. - * - * @return SSH_OK on success, SSH_ERROR if an error occurred. - * - * @see ssh_channel_free() - * @see ssh_channel_is_eof() - */ -int ssh_channel_close(ssh_channel channel){ - ssh_session session; - int rc = 0; - - if(channel == NULL) { - return SSH_ERROR; - } - - session = channel->session; - - if (channel->local_eof == 0) { - rc = ssh_channel_send_eof(channel); - } - - if (rc != SSH_OK) { - return rc; - } - - rc = ssh_buffer_pack(session->out_buffer, - "bd", - SSH2_MSG_CHANNEL_CLOSE, - channel->remote_channel); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - - rc = packet_send(session); - SSH_LOG(SSH_LOG_PACKET, - "Sent a close on client channel (%d:%d)", - channel->local_channel, - channel->remote_channel); - - if(rc == SSH_OK) { - channel->state=SSH_CHANNEL_STATE_CLOSED; - } - - rc = ssh_channel_flush(channel); - if(rc == SSH_ERROR) - goto error; - - return rc; -error: - ssh_buffer_reinit(session->out_buffer); - - return rc; -} - -/* this termination function waits for a window growing condition */ -static int ssh_channel_waitwindow_termination(void *c){ - ssh_channel channel = (ssh_channel) c; - if (channel->remote_window > 0 || - channel->session->session_state == SSH_SESSION_STATE_ERROR || - channel->state == SSH_CHANNEL_STATE_CLOSED) - return 1; - else - return 0; -} - -/* This termination function waits until the session is not in blocked status - * anymore, e.g. because of a key re-exchange. - */ -static int ssh_waitsession_unblocked(void *s){ - ssh_session session = (ssh_session)s; - switch (session->session_state){ - case SSH_SESSION_STATE_DH: - case SSH_SESSION_STATE_INITIAL_KEX: - case SSH_SESSION_STATE_KEXINIT_RECEIVED: - return 0; - default: - return 1; - } -} -/** - * @internal - * @brief Flushes a channel (and its session) until the output buffer - * is empty, or timeout elapsed. - * @param channel SSH channel - * @returns SSH_OK On success, - * SSH_ERROR on error - * SSH_AGAIN Timeout elapsed (or in nonblocking mode) - */ -int ssh_channel_flush(ssh_channel channel){ - return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT); -} - -static int channel_write_common(ssh_channel channel, - const void *data, - uint32_t len, int is_stderr) -{ - ssh_session session; - uint32_t origlen = len; - size_t effectivelen; - size_t maxpacketlen; - int rc; - - if(channel == NULL) { - return -1; - } - session = channel->session; - if(data == NULL) { - ssh_set_error_invalid(session); - return -1; - } - - if (len > INT_MAX) { - SSH_LOG(SSH_LOG_PROTOCOL, - "Length (%u) is bigger than INT_MAX", len); - return SSH_ERROR; - } - - /* - * Handle the max packet len from remote side, be nice - * 10 bytes for the headers - */ - maxpacketlen = channel->remote_maxpacket - 10; - - if (channel->local_eof) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "Can't write to channel %d:%d after EOF was sent", - channel->local_channel, - channel->remote_channel); - return -1; - } - - if (channel->state != SSH_CHANNEL_STATE_OPEN || channel->delayed_close != 0) { - ssh_set_error(session, SSH_REQUEST_DENIED, "Remote channel is closed"); - - return -1; - } - - if (channel->session->session_state == SSH_SESSION_STATE_ERROR) { - return SSH_ERROR; - } -#ifdef WITH_SSH1 - if (channel->version == 1) { - rc = channel_write1(channel, data, len); - - return rc; - } -#endif - if (ssh_waitsession_unblocked(session) == 0){ - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT, - ssh_waitsession_unblocked, session); - if (rc == SSH_ERROR || !ssh_waitsession_unblocked(session)) - goto out; - } - while (len > 0) { - if (channel->remote_window < len) { - SSH_LOG(SSH_LOG_PROTOCOL, - "Remote window is %d bytes. going to write %d bytes", - channel->remote_window, - len); - /* What happens when the channel window is zero? */ - if(channel->remote_window == 0) { - /* nothing can be written */ - SSH_LOG(SSH_LOG_PROTOCOL, - "Wait for a growing window message..."); - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT, - ssh_channel_waitwindow_termination,channel); - if (rc == SSH_ERROR || - !ssh_channel_waitwindow_termination(channel) || - channel->session->session_state == SSH_SESSION_STATE_ERROR || - channel->state == SSH_CHANNEL_STATE_CLOSED) - goto out; - continue; - } - effectivelen = MIN(len, channel->remote_window); - } else { - effectivelen = len; - } - - effectivelen = MIN(effectivelen, maxpacketlen);; - - rc = ssh_buffer_pack(session->out_buffer, - "bd", - is_stderr ? SSH2_MSG_CHANNEL_EXTENDED_DATA : SSH2_MSG_CHANNEL_DATA, - channel->remote_channel); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - - /* stderr message has an extra field */ - if (is_stderr) { - rc = ssh_buffer_pack(session->out_buffer, - "d", - SSH2_EXTENDED_DATA_STDERR); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - } - - /* append payload data */ - rc = ssh_buffer_pack(session->out_buffer, - "dP", - effectivelen, - (size_t)effectivelen, data); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_PACKET, - "channel_write wrote %ld bytes", (long int) effectivelen); - - channel->remote_window -= effectivelen; - len -= effectivelen; - data = ((uint8_t*)data + effectivelen); - if (channel->counter != NULL) { - channel->counter->out_bytes += effectivelen; - } - } - - /* it's a good idea to flush the socket now */ - rc = ssh_channel_flush(channel); - if (rc == SSH_ERROR) { - goto error; - } - -out: - return (int)(origlen - len); - -error: - ssh_buffer_reinit(session->out_buffer); - - return SSH_ERROR; -} - -uint32_t ssh_channel_window_size(ssh_channel channel) { - return channel->remote_window; -} - -/** - * @brief Blocking write on a channel. - * - * @param[in] channel The channel to write to. - * - * @param[in] data A pointer to the data to write. - * - * @param[in] len The length of the buffer to write to. - * - * @return The number of bytes written, SSH_ERROR on error. - * - * @see ssh_channel_read() - */ -int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) { - return channel_write_common(channel, data, len, 0); -} - -/** - * @brief Check if the channel is open or not. - * - * @param[in] channel The channel to check. - * - * @return 0 if channel is closed, nonzero otherwise. - * - * @see ssh_channel_is_closed() - */ -int ssh_channel_is_open(ssh_channel channel) { - if(channel == NULL) { - return 0; - } - return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0); -} - -/** - * @brief Check if the channel is closed or not. - * - * @param[in] channel The channel to check. - * - * @return 0 if channel is opened, nonzero otherwise. - * - * @see ssh_channel_is_open() - */ -int ssh_channel_is_closed(ssh_channel channel) { - if(channel == NULL) { - return SSH_ERROR; - } - return (channel->state != SSH_CHANNEL_STATE_OPEN || channel->session->alive == 0); -} - -/** - * @brief Check if remote has sent an EOF. - * - * @param[in] channel The channel to check. - * - * @return 0 if there is no EOF, nonzero otherwise. - */ -int ssh_channel_is_eof(ssh_channel channel) { - if(channel == NULL) { - return SSH_ERROR; - } - if ((channel->stdout_buffer && - buffer_get_rest_len(channel->stdout_buffer) > 0) || - (channel->stderr_buffer && - buffer_get_rest_len(channel->stderr_buffer) > 0)) { - return 0; - } - - return (channel->remote_eof != 0); -} - -/** - * @brief Put the channel into blocking or nonblocking mode. - * - * @param[in] channel The channel to use. - * - * @param[in] blocking A boolean for blocking or nonblocking. - * - * @warning A side-effect of this is to put the whole session - * in non-blocking mode. - * @see ssh_set_blocking() - */ -void ssh_channel_set_blocking(ssh_channel channel, int blocking) { - if(channel == NULL) { - return; - } - ssh_set_blocking(channel->session,blocking); -} - -/** - * @internal - * - * @brief handle a SSH_CHANNEL_SUCCESS packet and set the channel state. - * - * This works on SSH1 sessions too. - */ -SSH_PACKET_CALLBACK(ssh_packet_channel_success){ - ssh_channel channel; - (void)type; - (void)user; - - channel=channel_from_msg(session,packet); - if (channel == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_PACKET, - "Received SSH_CHANNEL_SUCCESS on channel (%d:%d)", - channel->local_channel, - channel->remote_channel); - if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){ - SSH_LOG(SSH_LOG_RARE, "SSH_CHANNEL_SUCCESS received in incorrect state %d", - channel->request_state); - } else { - channel->request_state=SSH_CHANNEL_REQ_STATE_ACCEPTED; - } - - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handle a SSH_CHANNEL_FAILURE packet and set the channel state. - * - * This works on SSH1 sessions too. - */ -SSH_PACKET_CALLBACK(ssh_packet_channel_failure){ - ssh_channel channel; - (void)type; - (void)user; - - channel=channel_from_msg(session,packet); - if (channel == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session)); - - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_PACKET, - "Received SSH_CHANNEL_FAILURE on channel (%d:%d)", - channel->local_channel, - channel->remote_channel); - if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){ - SSH_LOG(SSH_LOG_RARE, "SSH_CHANNEL_FAILURE received in incorrect state %d", - channel->request_state); - } else { - channel->request_state=SSH_CHANNEL_REQ_STATE_DENIED; - } - - return SSH_PACKET_USED; -} - -static int ssh_channel_request_termination(void *c){ - ssh_channel channel = (ssh_channel)c; - if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING || - channel->session->session_state == SSH_SESSION_STATE_ERROR) - return 1; - else - return 0; -} - -static int channel_request(ssh_channel channel, const char *request, - ssh_buffer buffer, int reply) { - ssh_session session = channel->session; - int rc = SSH_ERROR; - int ret; - - switch(channel->request_state){ - case SSH_CHANNEL_REQ_STATE_NONE: - break; - default: - goto pending; - } - - ret = ssh_buffer_pack(session->out_buffer, - "bdsb", - SSH2_MSG_CHANNEL_REQUEST, - channel->remote_channel, - request, - reply == 0 ? 0 : 1); - if (ret != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - - if (buffer != NULL) { - if (ssh_buffer_add_data(session->out_buffer, buffer_get_rest(buffer), - buffer_get_rest_len(buffer)) < 0) { - ssh_set_error_oom(session); - goto error; - } - } - channel->request_state = SSH_CHANNEL_REQ_STATE_PENDING; - if (packet_send(session) == SSH_ERROR) { - return rc; - } - - SSH_LOG(SSH_LOG_PACKET, - "Sent a SSH_MSG_CHANNEL_REQUEST %s", request); - if (reply == 0) { - channel->request_state = SSH_CHANNEL_REQ_STATE_NONE; - return SSH_OK; - } -pending: - rc = ssh_handle_packets_termination(session, - SSH_TIMEOUT_DEFAULT, - ssh_channel_request_termination, - channel); - - if(session->session_state == SSH_SESSION_STATE_ERROR || rc == SSH_ERROR) { - channel->request_state = SSH_CHANNEL_REQ_STATE_ERROR; - } - /* we received something */ - switch (channel->request_state){ - case SSH_CHANNEL_REQ_STATE_ERROR: - rc=SSH_ERROR; - break; - case SSH_CHANNEL_REQ_STATE_DENIED: - ssh_set_error(session, SSH_REQUEST_DENIED, - "Channel request %s failed", request); - rc=SSH_ERROR; - break; - case SSH_CHANNEL_REQ_STATE_ACCEPTED: - SSH_LOG(SSH_LOG_PROTOCOL, - "Channel request %s success",request); - rc=SSH_OK; - break; - case SSH_CHANNEL_REQ_STATE_PENDING: - rc = SSH_AGAIN; - return rc; - case SSH_CHANNEL_REQ_STATE_NONE: - /* Never reached */ - ssh_set_error(session, SSH_FATAL, "Invalid state in channel_request()"); - rc=SSH_ERROR; - break; - } - channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - - return rc; -error: - ssh_buffer_reinit(session->out_buffer); - - return rc; -} - -/** - * @brief Request a pty with a specific type and size. - * - * @param[in] channel The channel to sent the request. - * - * @param[in] terminal The terminal type ("vt100, xterm,..."). - * - * @param[in] col The number of columns. - * - * @param[in] row The number of rows. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - */ -int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, - int col, int row) { - ssh_session session; - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return SSH_ERROR; - } - session = channel->session; - - if(terminal == NULL) { - ssh_set_error_invalid(channel->session); - return rc; - } - -#ifdef WITH_SSH1 - if (channel->version==1) { - rc = channel_request_pty_size1(channel,terminal, col, row); - - return rc; - } -#endif - switch(channel->request_state){ - case SSH_CHANNEL_REQ_STATE_NONE: - break; - default: - goto pending; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(session); - goto error; - } - - rc = ssh_buffer_pack(buffer, - "sdddddb", - terminal, - col, - row, - 0, /* pix */ - 0, /* pix */ - 1, /* add a 0byte string */ - 0); - - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } -pending: - rc = channel_request(channel, "pty-req", buffer, 1); -error: - ssh_buffer_free(buffer); - - return rc; -} - -/** - * @brief Request a PTY. - * - * @param[in] channel The channel to send the request. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * - * @see ssh_channel_request_pty_size() - */ -int ssh_channel_request_pty(ssh_channel channel) { - return ssh_channel_request_pty_size(channel, "xterm", 80, 24); -} - -/** - * @brief Change the size of the terminal associated to a channel. - * - * @param[in] channel The channel to change the size. - * - * @param[in] cols The new number of columns. - * - * @param[in] rows The new number of rows. - * - * @return SSH_OK on success, SSH_ERROR if an error occurred. - * - * @warning Do not call it from a signal handler if you are not sure any other - * libssh function using the same channel/session is running at same - * time (not 100% threadsafe). - */ -int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) { - ssh_session session = channel->session; - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - -#ifdef WITH_SSH1 - if (channel->version == 1) { - rc = channel_change_pty_size1(channel,cols,rows); - - return rc; - } -#endif - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(session); - goto error; - } - - rc = ssh_buffer_pack(buffer, - "dddd", - cols, - rows, - 0, /* pix */ - 0 /* pix */); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - - rc = channel_request(channel, "window-change", buffer, 0); -error: - ssh_buffer_free(buffer); - - return rc; -} - -/** - * @brief Request a shell. - * - * @param[in] channel The channel to send the request. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - */ -int ssh_channel_request_shell(ssh_channel channel) { - if(channel == NULL) { - return SSH_ERROR; - } -#ifdef WITH_SSH1 - if (channel->version == 1) { - return channel_request_shell1(channel); - } -#endif - return channel_request(channel, "shell", NULL, 1); -} - -/** - * @brief Request a subsystem (for example "sftp"). - * - * @param[in] channel The channel to send the request. - * - * @param[in] subsys The subsystem to request (for example "sftp"). - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * - * @warning You normally don't have to call it for sftp, see sftp_new(). - */ -int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) { - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return SSH_ERROR; - } - if(subsys == NULL) { - ssh_set_error_invalid(channel->session); - return rc; - } - switch(channel->request_state){ - case SSH_CHANNEL_REQ_STATE_NONE: - break; - default: - goto pending; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = ssh_buffer_pack(buffer, "s", subsys); - if (rc != SSH_OK) { - ssh_set_error_oom(channel->session); - goto error; - } -pending: - rc = channel_request(channel, "subsystem", buffer, 1); -error: - ssh_buffer_free(buffer); - - return rc; -} - -int ssh_channel_request_sftp( ssh_channel channel){ - if(channel == NULL) { - return SSH_ERROR; - } - return ssh_channel_request_subsystem(channel, "sftp"); -} - -static char *generate_cookie(void) { - static const char *hex = "0123456789abcdef"; - char s[36]; - unsigned char rnd[16]; - int i; - - ssh_get_random(rnd,sizeof(rnd),0); - for (i = 0; i < 16; i++) { - s[i*2] = hex[rnd[i] & 0x0f]; - s[i*2+1] = hex[rnd[i] >> 4]; - } - s[32] = '\0'; - return strdup(s); -} - -/** - * @brief Sends the "x11-req" channel request over an existing session channel. - * - * This will enable redirecting the display of the remote X11 applications to - * local X server over an secure tunnel. - * - * @param[in] channel An existing session channel where the remote X11 - * applications are going to be executed. - * - * @param[in] single_connection A boolean to mark only one X11 app will be - * redirected. - * - * @param[in] protocol A x11 authentication protocol. Pass NULL to use the - * default value MIT-MAGIC-COOKIE-1. - * - * @param[in] cookie A x11 authentication cookie. Pass NULL to generate - * a random cookie. - * - * @param[in] screen_number The screen number. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - */ -int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, - const char *cookie, int screen_number) { - ssh_buffer buffer = NULL; - char *c = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return SSH_ERROR; - } - switch(channel->request_state){ - case SSH_CHANNEL_REQ_STATE_NONE: - break; - default: - goto pending; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - if (cookie == NULL) { - c = generate_cookie(); - if (c == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - } - - rc = ssh_buffer_pack(buffer, - "bssd", - single_connection == 0 ? 0 : 1, - protocol ? protocol : "MIT-MAGIC-COOKIE-1", - cookie ? cookie : c, - screen_number); - if (c != NULL){ - SAFE_FREE(c); - } - if (rc != SSH_OK) { - ssh_set_error_oom(channel->session); - goto error; - } -pending: - rc = channel_request(channel, "x11-req", buffer, 1); - -error: - ssh_buffer_free(buffer); - return rc; -} - -static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, - int timeout_ms, int *destination_port) { -#ifndef _WIN32 - static const struct timespec ts = { - .tv_sec = 0, - .tv_nsec = 50000000 /* 50ms */ - }; -#endif - ssh_message msg = NULL; - ssh_channel channel = NULL; - struct ssh_iterator *iterator; - int t; - - /* - * We sleep for 50 ms in ssh_handle_packets() and later sleep for - * 50 ms. So we need to decrement by 100 ms. - */ - for (t = timeout_ms; t >= 0; t -= 100) { - if (timeout_ms == 0) { - ssh_handle_packets(session, 0); - } else { - ssh_handle_packets(session, 50); - } - - if (session->ssh_message_list) { - iterator = ssh_list_get_iterator(session->ssh_message_list); - while (iterator) { - msg = (ssh_message)iterator->data; - if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL_OPEN && - ssh_message_subtype(msg) == channeltype) { - ssh_list_remove(session->ssh_message_list, iterator); - channel = ssh_message_channel_request_open_reply_accept(msg); - if(destination_port) { - *destination_port=msg->channel_request_open.destination_port; - } - - ssh_message_free(msg); - return channel; - } - iterator = iterator->next; - } - } - if(t>0){ -#ifdef _WIN32 - Sleep(50); /* 50ms */ -#else - nanosleep(&ts, NULL); -#endif - } - } - - ssh_set_error(session, SSH_NO_ERROR, "No channel request of this type from server"); - return NULL; -} - -/** - * @brief Accept an X11 forwarding channel. - * - * @param[in] channel An x11-enabled session channel. - * - * @param[in] timeout_ms Timeout in milliseconds. - * - * @return A newly created channel, or NULL if no X11 request from - * the server. - */ -ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) { - return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL); -} - -/** - * @internal - * - * @brief Handle a SSH_REQUEST_SUCCESS packet normally sent after a global - * request. - */ -SSH_PACKET_CALLBACK(ssh_request_success){ - (void)type; - (void)user; - (void)packet; - - SSH_LOG(SSH_LOG_PACKET, - "Received SSH_REQUEST_SUCCESS"); - if(session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING){ - SSH_LOG(SSH_LOG_RARE, "SSH_REQUEST_SUCCESS received in incorrect state %d", - session->global_req_state); - } else { - session->global_req_state=SSH_CHANNEL_REQ_STATE_ACCEPTED; - } - - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handle a SSH_REQUEST_DENIED packet normally sent after a global - * request. - */ -SSH_PACKET_CALLBACK(ssh_request_denied){ - (void)type; - (void)user; - (void)packet; - - SSH_LOG(SSH_LOG_PACKET, - "Received SSH_REQUEST_FAILURE"); - if(session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING){ - SSH_LOG(SSH_LOG_RARE, "SSH_REQUEST_DENIED received in incorrect state %d", - session->global_req_state); - } else { - session->global_req_state=SSH_CHANNEL_REQ_STATE_DENIED; - } - - return SSH_PACKET_USED; - -} - -static int ssh_global_request_termination(void *s){ - ssh_session session = (ssh_session) s; - if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING || - session->session_state == SSH_SESSION_STATE_ERROR) - return 1; - else - return 0; -} - -/** - * @internal - * - * @brief Send a global request (needed for forward listening) and wait for the - * result. - * - * @param[in] session The SSH session handle. - * - * @param[in] request The type of request (defined in RFC). - * - * @param[in] buffer Additional data to put in packet. - * - * @param[in] reply Set if you expect a reply from server. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - */ -static int global_request(ssh_session session, const char *request, - ssh_buffer buffer, int reply) { - int rc; - - switch (session->global_req_state) { - case SSH_CHANNEL_REQ_STATE_NONE: - break; - default: - goto pending; - } - - rc = ssh_buffer_pack(session->out_buffer, - "bsb", - SSH2_MSG_GLOBAL_REQUEST, - request, - reply == 0 ? 0 : 1); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - rc = SSH_ERROR; - goto error; - } - - if (buffer != NULL) { - rc = ssh_buffer_add_data(session->out_buffer, - buffer_get_rest(buffer), - buffer_get_rest_len(buffer)); - if (rc < 0) { - ssh_set_error_oom(session); - rc = SSH_ERROR; - goto error; - } - } - - session->global_req_state = SSH_CHANNEL_REQ_STATE_PENDING; - rc = packet_send(session); - if (rc == SSH_ERROR) { - return rc; - } - - SSH_LOG(SSH_LOG_PACKET, - "Sent a SSH_MSG_GLOBAL_REQUEST %s", request); - - if (reply == 0) { - session->global_req_state = SSH_CHANNEL_REQ_STATE_NONE; - - return SSH_OK; - } -pending: - rc = ssh_handle_packets_termination(session, - SSH_TIMEOUT_DEFAULT, - ssh_global_request_termination, - session); - - if(rc==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){ - session->global_req_state = SSH_CHANNEL_REQ_STATE_ERROR; - } - switch(session->global_req_state){ - case SSH_CHANNEL_REQ_STATE_ACCEPTED: - SSH_LOG(SSH_LOG_PROTOCOL, "Global request %s success",request); - rc=SSH_OK; - break; - case SSH_CHANNEL_REQ_STATE_DENIED: - SSH_LOG(SSH_LOG_PACKET, - "Global request %s failed", request); - ssh_set_error(session, SSH_REQUEST_DENIED, - "Global request %s failed", request); - rc=SSH_ERROR; - break; - case SSH_CHANNEL_REQ_STATE_ERROR: - case SSH_CHANNEL_REQ_STATE_NONE: - rc = SSH_ERROR; - break; - case SSH_CHANNEL_REQ_STATE_PENDING: - return SSH_AGAIN; - } - session->global_req_state = SSH_CHANNEL_REQ_STATE_NONE; - - return rc; -error: - ssh_buffer_reinit(session->out_buffer); - - return rc; -} - -/** - * @brief Sends the "tcpip-forward" global request to ask the server to begin - * listening for inbound connections. - * - * @param[in] session The ssh session to send the request. - * - * @param[in] address The address to bind to on the server. Pass NULL to bind - * to all available addresses on all protocol families - * supported by the server. - * - * @param[in] port The port to bind to on the server. Pass 0 to ask the - * server to allocate the next available unprivileged port - * number - * - * @param[in] bound_port The pointer to get actual bound port. Pass NULL to - * ignore. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - **/ -int ssh_channel_listen_forward(ssh_session session, - const char *address, - int port, - int *bound_port) -{ - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) - goto pending; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(session); - goto error; - } - - rc = ssh_buffer_pack(buffer, - "sd", - address ? address : "", - port); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - goto error; - } -pending: - rc = global_request(session, "tcpip-forward", buffer, 1); - - /* TODO: FIXME no guarantee the last packet we received contains - * that info */ - if (rc == SSH_OK && port == 0 && bound_port != NULL) { - rc = ssh_buffer_unpack(session->in_buffer, "d", bound_port); - if (rc != SSH_OK) - *bound_port = 0; - } - -error: - ssh_buffer_free(buffer); - return rc; -} - -/* DEPRECATED */ -ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) { - return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL); -} - -/** - * @brief Accept an incoming TCP/IP forwarding channel and get information - * about incomming connection - * @param[in] session The ssh session to use. - * - * @param[in] timeout_ms A timeout in milliseconds. - * - * @param[in] destination_port A pointer to destination port or NULL. - * - * @return Newly created channel, or NULL if no incoming channel request from - * the server - */ -ssh_channel ssh_channel_accept_forward(ssh_session session, int timeout_ms, int* destination_port) { - return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port); -} - -/** - * @brief Sends the "cancel-tcpip-forward" global request to ask the server to - * cancel the tcpip-forward request. - * - * @param[in] session The ssh session to send the request. - * - * @param[in] address The bound address on the server. - * - * @param[in] port The bound port on the server. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - */ -int ssh_channel_cancel_forward(ssh_session session, - const char *address, - int port) -{ - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(session->global_req_state != SSH_CHANNEL_REQ_STATE_NONE) - goto pending; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(session); - goto error; - } - - rc = ssh_buffer_pack(buffer, "sd", - address ? address : "", - port); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - goto error; - } -pending: - rc = global_request(session, "cancel-tcpip-forward", buffer, 1); - -error: - ssh_buffer_free(buffer); - return rc; -} - -int ssh_forward_cancel(ssh_session session, const char *address, int port) { - return ssh_channel_cancel_forward(session, address, port); -} - -/** - * @brief Set environment variables. - * - * @param[in] channel The channel to set the environment variables. - * - * @param[in] name The name of the variable. - * - * @param[in] value The value to set. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * @warning Some environment variables may be refused by security reasons. - */ -int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value) { - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return SSH_ERROR; - } - if(name == NULL || value == NULL) { - ssh_set_error_invalid(channel->session); - return rc; - } - switch(channel->request_state){ - case SSH_CHANNEL_REQ_STATE_NONE: - break; - default: - goto pending; - } - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = ssh_buffer_pack(buffer, - "ss", - name, - value); - if (rc != SSH_OK){ - ssh_set_error_oom(channel->session); - goto error; - } -pending: - rc = channel_request(channel, "env", buffer,1); -error: - ssh_buffer_free(buffer); - - return rc; -} - -/** - * @brief Run a shell command without an interactive shell. - * - * This is similar to 'sh -c command'. - * - * @param[in] channel The channel to execute the command. - * - * @param[in] cmd The command to execute - * (e.g. "ls ~/ -al | grep -i reports"). - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * - * Example: -@code - rc = channel_request_exec(channel, "ps aux"); - if (rc > 0) { - return -1; - } - - while ((rc = channel_read(channel, buffer, sizeof(buffer), 0)) > 0) { - if (fwrite(buffer, 1, rc, stdout) != (unsigned int) rc) { - return -1; - } - } -@endcode - * - * @see ssh_channel_request_shell() - */ -int ssh_channel_request_exec(ssh_channel channel, const char *cmd) { - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return SSH_ERROR; - } - if(cmd == NULL) { - ssh_set_error_invalid(channel->session); - return rc; - } - -#ifdef WITH_SSH1 - if (channel->version == 1) { - return channel_request_exec1(channel, cmd); - } -#endif - switch(channel->request_state){ - case SSH_CHANNEL_REQ_STATE_NONE: - break; - default: - goto pending; - } - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = ssh_buffer_pack(buffer, "s", cmd); - - if (rc != SSH_OK) { - ssh_set_error_oom(channel->session); - goto error; - } -pending: - rc = channel_request(channel, "exec", buffer, 1); -error: - ssh_buffer_free(buffer); - return rc; -} - - -/** - * @brief Send a signal to remote process (as described in RFC 4254, section 6.9). - * - * Sends a signal 'sig' to the remote process. - * Note, that remote system may not support signals concept. - * In such a case this request will be silently ignored. - * Only SSH-v2 is supported (I'm not sure about SSH-v1). - * - * OpenSSH doesn't support signals yet, see: - * https://bugzilla.mindrot.org/show_bug.cgi?id=1424 - * - * @param[in] channel The channel to send signal. - * - * @param[in] sig The signal to send (without SIG prefix) - * \n\n - * SIGABRT -> ABRT \n - * SIGALRM -> ALRM \n - * SIGFPE -> FPE \n - * SIGHUP -> HUP \n - * SIGILL -> ILL \n - * SIGINT -> INT \n - * SIGKILL -> KILL \n - * SIGPIPE -> PIPE \n - * SIGQUIT -> QUIT \n - * SIGSEGV -> SEGV \n - * SIGTERM -> TERM \n - * SIGUSR1 -> USR1 \n - * SIGUSR2 -> USR2 \n - * - * @return SSH_OK on success, SSH_ERROR if an error occurred - * (including attempts to send signal via SSH-v1 session). - */ -int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) { - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return SSH_ERROR; - } - if(sig == NULL) { - ssh_set_error_invalid(channel->session); - return rc; - } - -#ifdef WITH_SSH1 - if (channel->version == 1) { - return SSH_ERROR; // TODO: Add support for SSH-v1 if possible. - } -#endif - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = ssh_buffer_pack(buffer, "s", sig); - if (rc != SSH_OK) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = channel_request(channel, "signal", buffer, 0); -error: - ssh_buffer_free(buffer); - return rc; -} - - -/** - * @brief Read data from a channel into a buffer. - * - * @param[in] channel The channel to read from. - * - * @param[in] buffer The buffer which will get the data. - * - * @param[in] count The count of bytes to be read. If it is bigger than 0, - * the exact size will be read, else (bytes=0) it will - * return once anything is available. - * - * @param is_stderr A boolean value to mark reading from the stderr stream. - * - * @return The number of bytes read, 0 on end of file or SSH_ERROR - * on error. - * @deprecated Please use ssh_channel_read instead - * @warning This function doesn't work in nonblocking/timeout mode - * @see ssh_channel_read - */ -int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, - int is_stderr) { - ssh_session session; - char buffer_tmp[8192]; - int r; - uint32_t total=0; - - if(channel == NULL) { - return SSH_ERROR; - } - session = channel->session; - - if(buffer == NULL) { - ssh_set_error_invalid(channel->session); - return SSH_ERROR; - } - - ssh_buffer_reinit(buffer); - if(count==0){ - do { - r=ssh_channel_poll(channel, is_stderr); - if(r < 0){ - return r; - } - if(r > 0){ - r=ssh_channel_read(channel, buffer_tmp, r, is_stderr); - if(r < 0){ - return r; - } - if(ssh_buffer_add_data(buffer,buffer_tmp,r) < 0){ - ssh_set_error_oom(session); - r = SSH_ERROR; - } - - return r; - } - if(ssh_channel_is_eof(channel)){ - return 0; - } - ssh_handle_packets(channel->session, SSH_TIMEOUT_INFINITE); - } while (r == 0); - } - while(total < count){ - r=ssh_channel_read(channel, buffer_tmp, sizeof(buffer_tmp), is_stderr); - if(r<0){ - return r; - } - if(r==0){ - return total; - } - if (ssh_buffer_add_data(buffer,buffer_tmp,r) < 0) { - ssh_set_error_oom(session); - - return SSH_ERROR; - } - total += r; - } - - return total; -} - -struct ssh_channel_read_termination_struct { - ssh_channel channel; - uint32_t count; - ssh_buffer buffer; -}; - -static int ssh_channel_read_termination(void *s){ - struct ssh_channel_read_termination_struct *ctx = s; - if (buffer_get_rest_len(ctx->buffer) >= ctx->count || - ctx->channel->remote_eof || - ctx->channel->session->session_state == SSH_SESSION_STATE_ERROR) - return 1; - else - return 0; -} - -/* TODO FIXME Fix the delayed close thing */ -/* TODO FIXME Fix the blocking behaviours */ - -/** - * @brief Reads data from a channel. - * - * @param[in] channel The channel to read from. - * - * @param[in] dest The destination buffer which will get the data. - * - * @param[in] count The count of bytes to be read. - * - * @param[in] is_stderr A boolean value to mark reading from the stderr flow. - * - * @return The number of bytes read, 0 on end of file or SSH_ERROR - * on error. In nonblocking mode it Can return 0 if no data - * is available or SSH_AGAIN. - * - * @warning This function may return less than count bytes of data, and won't - * block until count bytes have been read. - * @warning The read function using a buffer has been renamed to - * channel_read_buffer(). - */ -int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr) -{ - return ssh_channel_read_timeout(channel, dest, count, is_stderr, -1); -} - -/** - * @brief Reads data from a channel. - * - * @param[in] channel The channel to read from. - * - * @param[in] dest The destination buffer which will get the data. - * - * @param[in] count The count of bytes to be read. - * - * @param[in] is_stderr A boolean value to mark reading from the stderr flow. - * - * @param[in] timeout_ms A timeout in milliseconds. A value of -1 means - * infinite timeout. - * - * @return The number of bytes read, 0 on end of file or SSH_ERROR - * on error. In nonblocking mode it Can return 0 if no data - * is available or SSH_AGAIN. - * - * @warning This function may return less than count bytes of data, and won't - * block until count bytes have been read. - * @warning The read function using a buffer has been renamed to - * channel_read_buffer(). - */ -int ssh_channel_read_timeout(ssh_channel channel, - void *dest, - uint32_t count, - int is_stderr, - int timeout) -{ - ssh_session session; - ssh_buffer stdbuf; - uint32_t len; - struct ssh_channel_read_termination_struct ctx; - int rc; - - if(channel == NULL) { - return SSH_ERROR; - } - if(dest == NULL) { - ssh_set_error_invalid(channel->session); - return SSH_ERROR; - } - - session = channel->session; - stdbuf = channel->stdout_buffer; - - if (count == 0) { - return 0; - } - - if (is_stderr) { - stdbuf=channel->stderr_buffer; - } - - /* - * We may have problem if the window is too small to accept as much data - * as asked - */ - SSH_LOG(SSH_LOG_PACKET, - "Read (%d) buffered : %d bytes. Window: %d", - count, - buffer_get_rest_len(stdbuf), - channel->local_window); - - if (count > buffer_get_rest_len(stdbuf) + channel->local_window) { - if (grow_window(session, channel, count - buffer_get_rest_len(stdbuf)) < 0) { - return -1; - } - } - - /* block reading until at least one byte has been read - * and ignore the trivial case count=0 - */ - ctx.channel = channel; - ctx.buffer = stdbuf; - ctx.count = 1; - - if (timeout < 0) { - timeout = SSH_TIMEOUT_DEFAULT; - } - - rc = ssh_handle_packets_termination(session, - timeout, - ssh_channel_read_termination, - &ctx); - if (rc == SSH_ERROR){ - return rc; - } - if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ - return SSH_ERROR; - } - if (channel->remote_eof && buffer_get_rest_len(stdbuf) == 0) { - return 0; - } - len = buffer_get_rest_len(stdbuf); - /* Read count bytes if len is greater, everything otherwise */ - len = (len > count ? count : len); - memcpy(dest, buffer_get_rest(stdbuf), len); - buffer_pass_bytes(stdbuf,len); - if (channel->counter != NULL) { - channel->counter->in_bytes += len; - } - /* Authorize some buffering while userapp is busy */ - if (channel->local_window < WINDOWLIMIT) { - if (grow_window(session, channel, 0) < 0) { - return -1; - } - } - - return len; -} - -/** - * @brief Do a nonblocking read on the channel. - * - * A nonblocking read on the specified channel. it will return <= count bytes of - * data read atomically. - * - * @param[in] channel The channel to read from. - * - * @param[in] dest A pointer to a destination buffer. - * - * @param[in] count The count of bytes of data to be read. - * - * @param[in] is_stderr A boolean to select the stderr stream. - * - * @return The number of bytes read, 0 if nothing is available or - * SSH_ERROR on error. - * - * @warning Don't forget to check for EOF as it would return 0 here. - * - * @see ssh_channel_is_eof() - */ -int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, - int is_stderr) { - ssh_session session; - int to_read; - int rc; - int blocking; - - if(channel == NULL) { - return SSH_ERROR; - } - if(dest == NULL) { - ssh_set_error_invalid(channel->session); - return SSH_ERROR; - } - - session = channel->session; - - to_read = ssh_channel_poll(channel, is_stderr); - - if (to_read <= 0) { - if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ - return SSH_ERROR; - } - - return to_read; /* may be an error code */ - } - - if (to_read > (int)count) { - to_read = (int)count; - } - blocking = ssh_is_blocking(session); - ssh_set_blocking(session, 0); - rc = ssh_channel_read(channel, dest, to_read, is_stderr); - ssh_set_blocking(session,blocking); - - return rc; -} - -/** - * @brief Polls a channel for data to read. - * - * @param[in] channel The channel to poll. - * - * @param[in] is_stderr A boolean to select the stderr stream. - * - * @return The number of bytes available for reading, 0 if nothing - * is available or SSH_ERROR on error. - * - * @warning When the channel is in EOF state, the function returns SSH_EOF. - * - * @see ssh_channel_is_eof() - */ -int ssh_channel_poll(ssh_channel channel, int is_stderr){ - ssh_buffer stdbuf; - - if(channel == NULL) { - return SSH_ERROR; - } - - stdbuf = channel->stdout_buffer; - - if (is_stderr) { - stdbuf = channel->stderr_buffer; - } - - if (buffer_get_rest_len(stdbuf) == 0 && channel->remote_eof == 0) { - if (channel->session->session_state == SSH_SESSION_STATE_ERROR){ - return SSH_ERROR; - } - if (ssh_handle_packets(channel->session, SSH_TIMEOUT_NONBLOCKING)==SSH_ERROR) { - return SSH_ERROR; - } - } - - if (buffer_get_rest_len(stdbuf) > 0){ - return buffer_get_rest_len(stdbuf); - } - - if (channel->remote_eof) { - return SSH_EOF; - } - - return buffer_get_rest_len(stdbuf); -} - -/** - * @brief Polls a channel for data to read, waiting for a certain timeout. - * - * @param[in] channel The channel to poll. - * @param[in] timeout Set an upper limit on the time for which this function - * will block, in milliseconds. Specifying a negative value - * means an infinite timeout. This parameter is passed to - * the poll() function. - * @param[in] is_stderr A boolean to select the stderr stream. - * - * @return The number of bytes available for reading, - * 0 if nothing is available (timeout elapsed), - * SSH_EOF on end of file, - * SSH_ERROR on error. - * - * @warning When the channel is in EOF state, the function returns SSH_EOF. - * - * @see ssh_channel_is_eof() - */ -int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){ - ssh_session session; - ssh_buffer stdbuf; - struct ssh_channel_read_termination_struct ctx; - int rc; - - if(channel == NULL) { - return SSH_ERROR; - } - - session = channel->session; - stdbuf = channel->stdout_buffer; - - if (is_stderr) { - stdbuf = channel->stderr_buffer; - } - ctx.buffer = stdbuf; - ctx.channel = channel; - ctx.count = 1; - rc = ssh_handle_packets_termination(channel->session, timeout, - ssh_channel_read_termination, &ctx); - if(rc ==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){ - rc = SSH_ERROR; - goto end; - } - rc = buffer_get_rest_len(stdbuf); - if(rc > 0) - goto end; - if (channel->remote_eof) - rc = SSH_EOF; -end: - return rc; -} - -/** - * @brief Recover the session in which belongs a channel. - * - * @param[in] channel The channel to recover the session from. - * - * @return The session pointer. - */ -ssh_session ssh_channel_get_session(ssh_channel channel) { - if(channel == NULL) { - return NULL; - } - - return channel->session; -} - -static int ssh_channel_exit_status_termination(void *c){ - ssh_channel channel = c; - if(channel->exit_status != -1 || - /* When a channel is closed, no exit status message can - * come anymore */ - (channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) || - channel->session->session_state == SSH_SESSION_STATE_ERROR) - return 1; - else - return 0; -} - -/** - * @brief Get the exit status of the channel (error code from the executed - * instruction). - * - * @param[in] channel The channel to get the status from. - * - * @returns The exit status, -1 if no exit status has been returned - * (yet). - * @warning This function may block until a timeout (or never) - * if the other side is not willing to close the channel. - * - * If you're looking for an async handling of this register a callback for the - * exit status. - * - * @see ssh_channel_exit_status_callback - */ -int ssh_channel_get_exit_status(ssh_channel channel) { - int rc; - if(channel == NULL) { - return SSH_ERROR; - } - rc = ssh_handle_packets_termination(channel->session, - SSH_TIMEOUT_DEFAULT, - ssh_channel_exit_status_termination, - channel); - if (rc == SSH_ERROR || channel->session->session_state == - SSH_SESSION_STATE_ERROR) - return SSH_ERROR; - return channel->exit_status; -} - -/* - * This function acts as a meta select. - * - * First, channels are analyzed to seek potential can-write or can-read ones, - * then if no channel has been elected, it goes in a loop with the posix - * select(2). - * This is made in two parts: protocol select and network select. The protocol - * select does not use the network functions at all - */ -static int channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans, - ssh_channel *echans, ssh_channel *rout, ssh_channel *wout, ssh_channel *eout) { - ssh_channel chan; - int i; - int j = 0; - - for (i = 0; rchans[i] != NULL; i++) { - chan = rchans[i]; - - while (ssh_channel_is_open(chan) && ssh_socket_data_available(chan->session->socket)) { - ssh_handle_packets(chan->session, SSH_TIMEOUT_NONBLOCKING); - } - - if ((chan->stdout_buffer && buffer_get_rest_len(chan->stdout_buffer) > 0) || - (chan->stderr_buffer && buffer_get_rest_len(chan->stderr_buffer) > 0) || - chan->remote_eof) { - rout[j] = chan; - j++; - } - } - rout[j] = NULL; - - j = 0; - for(i = 0; wchans[i] != NULL; i++) { - chan = wchans[i]; - /* It's not our business to seek if the file descriptor is writable */ - if (ssh_socket_data_writable(chan->session->socket) && - ssh_channel_is_open(chan) && (chan->remote_window > 0)) { - wout[j] = chan; - j++; - } - } - wout[j] = NULL; - - j = 0; - for (i = 0; echans[i] != NULL; i++) { - chan = echans[i]; - - if (!ssh_socket_is_open(chan->session->socket) || ssh_channel_is_closed(chan)) { - eout[j] = chan; - j++; - } - } - eout[j] = NULL; - - return 0; -} - -/* Just count number of pointers in the array */ -static int count_ptrs(ssh_channel *ptrs) { - int c; - for (c = 0; ptrs[c] != NULL; c++) - ; - - return c; -} - -/** - * @brief Act like the standard select(2) on channels. - * - * The list of pointers are then actualized and will only contain pointers to - * channels that are respectively readable, writable or have an exception to - * trap. - * - * @param[in] readchans A NULL pointer or an array of channel pointers, - * terminated by a NULL. - * - * @param[in] writechans A NULL pointer or an array of channel pointers, - * terminated by a NULL. - * - * @param[in] exceptchans A NULL pointer or an array of channel pointers, - * terminated by a NULL. - * - * @param[in] timeout Timeout as defined by select(2). - * - * @return SSH_OK on a successful operation, SSH_EINTR if the - * select(2) syscall was interrupted, then relaunch the - * function. - */ -int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, - ssh_channel *exceptchans, struct timeval * timeout) { - ssh_channel *rchans, *wchans, *echans; - ssh_channel dummy = NULL; - ssh_event event = NULL; - int rc; - int i; - int tm, tm_base; - int firstround=1; - struct ssh_timestamp ts; - - if (timeout != NULL) - tm_base = timeout->tv_sec * 1000 + timeout->tv_usec/1000; - else - tm_base = SSH_TIMEOUT_INFINITE; - ssh_timestamp_init(&ts); - tm = tm_base; - /* don't allow NULL pointers */ - if (readchans == NULL) { - readchans = &dummy; - } - - if (writechans == NULL) { - writechans = &dummy; - } - - if (exceptchans == NULL) { - exceptchans = &dummy; - } - - if (readchans[0] == NULL && writechans[0] == NULL && exceptchans[0] == NULL) { - /* No channel to poll?? Go away! */ - return 0; - } - - /* Prepare the outgoing temporary arrays */ - rchans = malloc(sizeof(ssh_channel ) * (count_ptrs(readchans) + 1)); - if (rchans == NULL) { - return SSH_ERROR; - } - - wchans = malloc(sizeof(ssh_channel ) * (count_ptrs(writechans) + 1)); - if (wchans == NULL) { - SAFE_FREE(rchans); - return SSH_ERROR; - } - - echans = malloc(sizeof(ssh_channel ) * (count_ptrs(exceptchans) + 1)); - if (echans == NULL) { - SAFE_FREE(rchans); - SAFE_FREE(wchans); - return SSH_ERROR; - } - - /* - * First, try without doing network stuff then, use the ssh_poll - * infrastructure to poll on all sessions. - */ - do { - channel_protocol_select(readchans, writechans, exceptchans, - rchans, wchans, echans); - if (rchans[0] != NULL || wchans[0] != NULL || echans[0] != NULL) { - /* At least one channel has an event */ - break; - } - /* Add all channels' sessions right into an event object */ - if (event == NULL) { - event = ssh_event_new(); - if (event == NULL) { - SAFE_FREE(rchans); - SAFE_FREE(wchans); - SAFE_FREE(echans); - - return SSH_ERROR; - } - for (i = 0; readchans[i] != NULL; i++) { - ssh_poll_get_default_ctx(readchans[i]->session); - ssh_event_add_session(event, readchans[i]->session); - } - for (i = 0; writechans[i] != NULL; i++) { - ssh_poll_get_default_ctx(writechans[i]->session); - ssh_event_add_session(event, writechans[i]->session); - } - for (i = 0; exceptchans[i] != NULL; i++) { - ssh_poll_get_default_ctx(exceptchans[i]->session); - ssh_event_add_session(event, exceptchans[i]->session); - } - } - /* Get out if the timeout has elapsed */ - if (!firstround && ssh_timeout_elapsed(&ts, tm_base)){ - break; - } - /* Here we go */ - rc = ssh_event_dopoll(event,tm); - if (rc != SSH_OK){ - SAFE_FREE(rchans); - SAFE_FREE(wchans); - SAFE_FREE(echans); - ssh_event_free(event); - return rc; - } - tm = ssh_timeout_update(&ts, tm_base); - firstround=0; - } while(1); - - memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel )); - memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel )); - memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel )); - SAFE_FREE(rchans); - SAFE_FREE(wchans); - SAFE_FREE(echans); - if(event) - ssh_event_free(event); - return 0; -} - -/** - * @brief Set the channel data counter. - * - * @code - * struct ssh_counter_struct counter = { - * .in_bytes = 0, - * .out_bytes = 0, - * .in_packets = 0, - * .out_packets = 0 - * }; - * - * ssh_channel_set_counter(channel, &counter); - * @endcode - * - * @param[in] channel The SSH channel. - * - * @param[in] counter Counter for bytes handled by the channel. - */ -void ssh_channel_set_counter(ssh_channel channel, - ssh_counter counter) { - if (channel != NULL) { - channel->counter = counter; - } -} - -#if WITH_SERVER -/** - * @brief Blocking write on a channel stderr. - * - * @param[in] channel The channel to write to. - * - * @param[in] data A pointer to the data to write. - * - * @param[in] len The length of the buffer to write to. - * - * @return The number of bytes written, SSH_ERROR on error. - * - * @see ssh_channel_read() - */ -int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) { - return channel_write_common(channel, data, len, 1); -} - -/** - * @brief Open a TCP/IP reverse forwarding channel. - * - * @param[in] channel An allocated channel. - * - * @param[in] remotehost The remote host to connected (host name or IP). - * - * @param[in] remoteport The remote port. - * - * @param[in] sourcehost The source host (your local computer). It's optional - * and for logging purpose. - * - * @param[in] localport The source port (your local computer). It's optional - * and for logging purpose. - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * - * @warning This function does not bind the local port and does not automatically - * forward the content of a socket to the channel. You still have to - * use channel_read and channel_write for this. - */ -int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost, - int remoteport, const char *sourcehost, int localport) { - ssh_session session; - ssh_buffer payload = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return rc; - } - if(remotehost == NULL || sourcehost == NULL) { - ssh_set_error_invalid(channel->session); - return rc; - } - - session = channel->session; - - if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN) - goto pending; - payload = ssh_buffer_new(); - if (payload == NULL) { - ssh_set_error_oom(session); - goto error; - } - rc = ssh_buffer_pack(payload, - "sdsd", - remotehost, - remoteport, - sourcehost, - localport); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - goto error; - } -pending: - rc = channel_open(channel, - "forwarded-tcpip", - CHANNEL_INITIAL_WINDOW, - CHANNEL_MAX_PACKET, - payload); - -error: - ssh_buffer_free(payload); - - return rc; -} - -/** - * @brief Open a X11 channel. - * - * @param[in] channel An allocated channel. - * - * @param[in] orig_addr The source host (the local server). - * - * @param[in] orig_port The source port (the local server). - * - * @return SSH_OK on success, - * SSH_ERROR if an error occurred, - * SSH_AGAIN if in nonblocking mode and call has - * to be done again. - * @warning This function does not bind the local port and does not automatically - * forward the content of a socket to the channel. You still have to - * use channel_read and channel_write for this. - */ -int ssh_channel_open_x11(ssh_channel channel, - const char *orig_addr, int orig_port) { - ssh_session session; - ssh_buffer payload = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return rc; - } - if(orig_addr == NULL) { - ssh_set_error_invalid(channel->session); - return rc; - } - session = channel->session; - - if(channel->state != SSH_CHANNEL_STATE_NOT_OPEN) - goto pending; - - payload = ssh_buffer_new(); - if (payload == NULL) { - ssh_set_error_oom(session); - goto error; - } - - rc = ssh_buffer_pack(payload, - "sd", - orig_addr, - orig_port); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } -pending: - rc = channel_open(channel, - "x11", - CHANNEL_INITIAL_WINDOW, - CHANNEL_MAX_PACKET, - payload); - -error: - ssh_buffer_free(payload); - - return rc; -} - -/** - * @brief Send the exit status to the remote process - * - * Sends the exit status to the remote process (as described in RFC 4254, - * section 6.10). - * Only SSH-v2 is supported (I'm not sure about SSH-v1). - * - * @param[in] channel The channel to send exit status. - * - * @param[in] exit_status The exit status to send - * - * @return SSH_OK on success, SSH_ERROR if an error occurred. - * (including attempts to send exit status via SSH-v1 session). - */ -int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) { - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return SSH_ERROR; - } - -#ifdef WITH_SSH1 - if (channel->version == 1) { - return SSH_ERROR; // TODO: Add support for SSH-v1 if possible. - } -#endif - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = ssh_buffer_pack(buffer, "d", exit_status); - if (rc != SSH_OK) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = channel_request(channel, "exit-status", buffer, 0); -error: - ssh_buffer_free(buffer); - return rc; -} - -/** - * @brief Send an exit signal to remote process (RFC 4254, section 6.10). - * - * This sends the exit status of the remote process. - * Note, that remote system may not support signals concept. - * In such a case this request will be silently ignored. - * Only SSH-v2 is supported (I'm not sure about SSH-v1). - * - * @param[in] channel The channel to send signal. - * - * @param[in] sig The signal to send (without SIG prefix) - * (e.g. "TERM" or "KILL"). - * @param[in] core A boolean to tell if a core was dumped - * @param[in] errmsg A CRLF explanation text about the error condition - * @param[in] lang The language used in the message (format: RFC 3066) - * - * @return SSH_OK on success, SSH_ERROR if an error occurred - * (including attempts to send signal via SSH-v1 session). - */ -int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig, - int core, const char *errmsg, const char *lang) { - ssh_buffer buffer = NULL; - int rc = SSH_ERROR; - - if(channel == NULL) { - return rc; - } - if(sig == NULL || errmsg == NULL || lang == NULL) { - ssh_set_error_invalid(channel->session); - return rc; - } -#ifdef WITH_SSH1 - if (channel->version == 1) { - return SSH_ERROR; // TODO: Add support for SSH-v1 if possible. - } -#endif - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = ssh_buffer_pack(buffer, - "sbss", - sig, - core ? 1 : 0, - errmsg, - lang); - if (rc != SSH_OK) { - ssh_set_error_oom(channel->session); - goto error; - } - - rc = channel_request(channel, "exit-signal", buffer, 0); -error: - ssh_buffer_free(buffer); - return rc; -} - -#endif - -/* @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/channels1.c b/libssh/src/channels1.c deleted file mode 100644 index 4d82c636..00000000 --- a/libssh/src/channels1.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * channels1.c - Support for SSH-1 type channels - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include -#ifndef _WIN32 -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif - -#include "libssh/priv.h" -#include "libssh/ssh1.h" -#include "libssh/buffer.h" -#include "libssh/packet.h" -#include "libssh/channels.h" -#include "libssh/session.h" -#include "libssh/misc.h" - -#ifdef WITH_SSH1 - -/* - * This is a big hack. In fact, SSH1 doesn't make a clever use of channels. - * The whole packets concerning shells are sent outside of a channel. - * Thus, an inside limitation of this behavior is that you can't only - * request one shell. - * The question is still how they managed to embed two "channel" into one - * protocol. - */ - -int channel_open_session1(ssh_channel chan) { - ssh_session session; - - if (chan == NULL) { - return -1; - } - session = chan->session; - - /* - * We guess we are requesting an *exec* channel. It can only have one exec - * channel. So we abort with an error if we need more than one. - */ - if (session->exec_channel_opened) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "SSH1 supports only one execution channel. " - "One has already been opened"); - return -1; - } - session->exec_channel_opened = 1; - chan->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED; - chan->state = SSH_CHANNEL_STATE_OPEN; - chan->local_maxpacket = 32000; - chan->local_window = 64000; - SSH_LOG(SSH_LOG_PACKET, "Opened a SSH1 channel session"); - - return 0; -} - -/* 10 SSH_CMSG_REQUEST_PTY - * - * string TERM environment variable value (e.g. vt100) - * 32-bit int terminal height, rows (e.g., 24) - * 32-bit int terminal width, columns (e.g., 80) - * 32-bit int terminal width, pixels (0 if no graphics) (e.g., 480) - * 32-bit int terminal height, pixels (0 if no graphics) (e.g., 640) - * n bytes tty modes encoded in binary - * Some day, someone should have a look at that nasty tty encoded. It's - * much simplier under ssh2. I just hope the defaults values are ok ... - */ - -int channel_request_pty_size1(ssh_channel channel, const char *terminal, int col, - int row) { - ssh_session session; - ssh_string str = NULL; - - if (channel == NULL) { - return SSH_ERROR; - } - session = channel->session; - - if(channel->request_state != SSH_CHANNEL_REQ_STATE_NONE){ - ssh_set_error(session,SSH_REQUEST_DENIED,"Wrong request state"); - return SSH_ERROR; - } - str = ssh_string_from_char(terminal); - if (str == NULL) { - ssh_set_error_oom(session); - return -1; - } - - if (buffer_add_u8(session->out_buffer, SSH_CMSG_REQUEST_PTY) < 0 || - buffer_add_ssh_string(session->out_buffer, str) < 0) { - ssh_string_free(str); - return -1; - } - ssh_string_free(str); - - if (buffer_add_u32(session->out_buffer, ntohl(row)) < 0 || - buffer_add_u32(session->out_buffer, ntohl(col)) < 0 || - buffer_add_u32(session->out_buffer, 0) < 0 || /* x */ - buffer_add_u32(session->out_buffer, 0) < 0 || /* y */ - buffer_add_u8(session->out_buffer, 0) < 0) { /* tty things */ - return -1; - } - - SSH_LOG(SSH_LOG_FUNCTIONS, "Opening a ssh1 pty"); - channel->request_state = SSH_CHANNEL_REQ_STATE_PENDING; - if (packet_send(session) == SSH_ERROR) { - return -1; - } - - while (channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING) { - ssh_handle_packets(session, SSH_TIMEOUT_INFINITE); - } - - switch(channel->request_state){ - case SSH_CHANNEL_REQ_STATE_ERROR: - case SSH_CHANNEL_REQ_STATE_PENDING: - case SSH_CHANNEL_REQ_STATE_NONE: - channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - return SSH_ERROR; - case SSH_CHANNEL_REQ_STATE_ACCEPTED: - channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - SSH_LOG(SSH_LOG_RARE, "PTY: Success"); - return SSH_OK; - case SSH_CHANNEL_REQ_STATE_DENIED: - channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - ssh_set_error(session, SSH_REQUEST_DENIED, - "Server denied PTY allocation"); - SSH_LOG(SSH_LOG_RARE, "PTY: denied\n"); - return SSH_ERROR; - } - // Not reached - return SSH_ERROR; -} - -int channel_change_pty_size1(ssh_channel channel, int cols, int rows) { - ssh_session session; - - if (channel == NULL) { - return SSH_ERROR; - } - session = channel->session; - - if(channel->request_state != SSH_CHANNEL_REQ_STATE_NONE){ - ssh_set_error(session,SSH_REQUEST_DENIED,"Wrong request state"); - return SSH_ERROR; - } - if (buffer_add_u8(session->out_buffer, SSH_CMSG_WINDOW_SIZE) < 0 || - buffer_add_u32(session->out_buffer, ntohl(rows)) < 0 || - buffer_add_u32(session->out_buffer, ntohl(cols)) < 0 || - buffer_add_u32(session->out_buffer, 0) < 0 || - buffer_add_u32(session->out_buffer, 0) < 0) { - return SSH_ERROR; - } - channel->request_state=SSH_CHANNEL_REQ_STATE_PENDING; - if (packet_send(session) == SSH_ERROR) { - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_PROTOCOL, "Change pty size send"); - while(channel->request_state==SSH_CHANNEL_REQ_STATE_PENDING){ - ssh_handle_packets(session, SSH_TIMEOUT_INFINITE); - } - switch(channel->request_state){ - case SSH_CHANNEL_REQ_STATE_ERROR: - case SSH_CHANNEL_REQ_STATE_PENDING: - case SSH_CHANNEL_REQ_STATE_NONE: - channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - return SSH_ERROR; - case SSH_CHANNEL_REQ_STATE_ACCEPTED: - channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - SSH_LOG(SSH_LOG_PROTOCOL, "pty size changed"); - return SSH_OK; - case SSH_CHANNEL_REQ_STATE_DENIED: - channel->request_state=SSH_CHANNEL_REQ_STATE_NONE; - SSH_LOG(SSH_LOG_RARE, "pty size change denied"); - ssh_set_error(session, SSH_REQUEST_DENIED, "pty size change denied"); - return SSH_ERROR; - } - // Not reached - return SSH_ERROR; - -} - -int channel_request_shell1(ssh_channel channel) { - ssh_session session; - - if (channel == NULL) { - return -1; - } - session = channel->session; - - if (buffer_add_u8(session->out_buffer,SSH_CMSG_EXEC_SHELL) < 0) { - return -1; - } - - if (packet_send(session) == SSH_ERROR) { - return -1; - } - - SSH_LOG(SSH_LOG_RARE, "Launched a shell"); - - return 0; -} - -int channel_request_exec1(ssh_channel channel, const char *cmd) { - ssh_session session; - ssh_string command = NULL; - - if (channel == NULL) { - return -1; - } - session = channel->session; - - command = ssh_string_from_char(cmd); - if (command == NULL) { - return -1; - } - - if (buffer_add_u8(session->out_buffer, SSH_CMSG_EXEC_CMD) < 0 || - buffer_add_ssh_string(session->out_buffer, command) < 0) { - ssh_string_free(command); - return -1; - } - ssh_string_free(command); - - if(packet_send(session) == SSH_ERROR) { - return -1; - } - - SSH_LOG(SSH_LOG_RARE, "Executing %s ...", cmd); - - return 0; -} - -SSH_PACKET_CALLBACK(ssh_packet_data1){ - ssh_channel channel = ssh_get_channel1(session); - ssh_string str = NULL; - int is_stderr=(type==SSH_SMSG_STDOUT_DATA ? 0 : 1); - (void)user; - - if (channel == NULL) { - return SSH_PACKET_NOT_USED; - } - - str = buffer_get_ssh_string(packet); - if (str == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, "Invalid data packet !\n"); - return SSH_PACKET_USED; - } - - SSH_LOG(SSH_LOG_PROTOCOL, - "Adding %" PRIdS " bytes data in %d", - ssh_string_len(str), is_stderr); - - if (channel_default_bufferize(channel, ssh_string_data(str), ssh_string_len(str), - is_stderr) < 0) { - ssh_string_free(str); - return SSH_PACKET_USED; - } - ssh_string_free(str); - - return SSH_PACKET_USED; -} - -SSH_PACKET_CALLBACK(ssh_packet_close1){ - ssh_channel channel = ssh_get_channel1(session); - uint32_t status; - int rc; - - (void)type; - (void)user; - - if (channel == NULL) { - return SSH_PACKET_NOT_USED; - } - - buffer_get_u32(packet, &status); - /* - * It's much more than a channel closing. spec says it's the last - * message sent by server (strange) - */ - - /* actually status is lost somewhere */ - channel->state = SSH_CHANNEL_STATE_CLOSED; - channel->remote_eof = 1; - - rc = buffer_add_u8(session->out_buffer, SSH_CMSG_EXIT_CONFIRMATION); - if (rc < 0) { - return SSH_PACKET_NOT_USED; - } - packet_send(session); - - return SSH_PACKET_USED; -} - -SSH_PACKET_CALLBACK(ssh_packet_exist_status1){ - ssh_channel channel = ssh_get_channel1(session); - uint32_t status; - (void)type; - (void)user; - - if (channel == NULL) { - return SSH_PACKET_NOT_USED; - } - - buffer_get_u32(packet, &status); - channel->state = SSH_CHANNEL_STATE_CLOSED; - channel->remote_eof = 1; - channel->exit_status = ntohl(status); - - return SSH_PACKET_USED; -} - - -int channel_write1(ssh_channel channel, const void *data, int len) { - ssh_session session; - int origlen = len; - int effectivelen; - const unsigned char *ptr=data; - - if (channel == NULL) { - return -1; - } - session = channel->session; - - while (len > 0) { - if (buffer_add_u8(session->out_buffer, SSH_CMSG_STDIN_DATA) < 0) { - return -1; - } - - effectivelen = len > 32000 ? 32000 : len; - - if (buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 || - ssh_buffer_add_data(session->out_buffer, ptr, effectivelen) < 0) { - return -1; - } - - ptr += effectivelen; - len -= effectivelen; - - if (packet_send(session) == SSH_ERROR) { - return -1; - } - ssh_handle_packets(session, SSH_TIMEOUT_NONBLOCKING); - if (channel->counter != NULL) { - channel->counter->out_bytes += effectivelen; - } - } - if (ssh_blocking_flush(session,SSH_TIMEOUT_USER) == SSH_ERROR) - return -1; - return origlen; -} - -ssh_channel ssh_get_channel1(ssh_session session){ - struct ssh_iterator *it; - - if (session == NULL) { - return NULL; - } - - /* With SSH1, the channel is always the first one */ - if(session->channels != NULL){ - it = ssh_list_get_iterator(session->channels); - if(it) - return ssh_iterator_value(ssh_channel, it); - } - return NULL; -} -#endif /* WITH_SSH1 */ -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/client.c b/libssh/src/client.c deleted file mode 100644 index 0a45944c..00000000 --- a/libssh/src/client.c +++ /dev/null @@ -1,704 +0,0 @@ -/* - * client.c - SSH client functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/priv.h" -#include "libssh/ssh2.h" -#include "libssh/buffer.h" -#include "libssh/packet.h" -#include "libssh/options.h" -#include "libssh/socket.h" -#include "libssh/session.h" -#include "libssh/dh.h" -#include "libssh/ecdh.h" -#include "libssh/threads.h" -#include "libssh/misc.h" -#include "libssh/pki.h" -#include "libssh/kex.h" - -#define set_status(session, status) do {\ - if (session->common.callbacks && session->common.callbacks->connect_status_function) \ - session->common.callbacks->connect_status_function(session->common.callbacks->userdata, status); \ - } while (0) - -/** - * @internal - * @brief Callback to be called when the socket is connected or had a - * connection error. Changes the state of the session and updates the error - * message. - * @param code one of SSH_SOCKET_CONNECTED_OK or SSH_SOCKET_CONNECTED_ERROR - * @param user is a pointer to session - */ -static void socket_callback_connected(int code, int errno_code, void *user){ - ssh_session session=(ssh_session)user; - - if (session->session_state != SSH_SESSION_STATE_CONNECTING && - session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED) - { - ssh_set_error(session,SSH_FATAL, "Wrong state in socket_callback_connected : %d", - session->session_state); - - return; - } - - SSH_LOG(SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code); - if(code == SSH_SOCKET_CONNECTED_OK) - session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED; - else { - session->session_state=SSH_SESSION_STATE_ERROR; - ssh_set_error(session,SSH_FATAL,"%s",strerror(errno_code)); - } - session->ssh_connection_callback(session); -} - -/** - * @internal - * - * @brief Gets the banner from socket and saves it in session. - * Updates the session state - * - * @param data pointer to the beginning of header - * @param len size of the banner - * @param user is a pointer to session - * @returns Number of bytes processed, or zero if the banner is not complete. - */ -static int callback_receive_banner(const void *data, size_t len, void *user) { - char *buffer = (char *)data; - ssh_session session=(ssh_session) user; - char *str = NULL; - size_t i; - int ret=0; - - if(session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED){ - ssh_set_error(session,SSH_FATAL,"Wrong state in callback_receive_banner : %d",session->session_state); - - return SSH_ERROR; - } - for(i=0;ipcap_ctx && buffer[i] == '\n'){ - ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_IN,buffer,i+1,i+1); - } -#endif - if(buffer[i]=='\r') { - buffer[i]='\0'; - } - if (buffer[i]=='\n') { - buffer[i] = '\0'; - str = strdup(buffer); - if (str == NULL) { - return SSH_ERROR; - } - /* number of bytes read */ - ret = i + 1; - session->serverbanner = str; - session->session_state=SSH_SESSION_STATE_BANNER_RECEIVED; - SSH_LOG(SSH_LOG_PACKET,"Received banner: %s",str); - session->ssh_connection_callback(session); - - return ret; - } - if(i>127){ - /* Too big banner */ - session->session_state=SSH_SESSION_STATE_ERROR; - ssh_set_error(session,SSH_FATAL,"Receiving banner: too large banner"); - - return 0; - } - } - - return ret; -} - -/** @internal - * @brief Sends a SSH banner to the server. - * - * @param session The SSH session to use. - * - * @param server Send client or server banner. - * - * @return 0 on success, < 0 on error. - */ -int ssh_send_banner(ssh_session session, int server) { - const char *banner = NULL; - char buffer[128] = {0}; - int err=SSH_ERROR; - - banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2; - - if (server) { - if(session->opts.custombanner == NULL){ - session->serverbanner = strdup(banner); - } else { - session->serverbanner = malloc(strlen(session->opts.custombanner) + 9); - if(!session->serverbanner) - goto end; - strcpy(session->serverbanner, "SSH-2.0-"); - strcat(session->serverbanner, session->opts.custombanner); - } - if (session->serverbanner == NULL) { - goto end; - } - snprintf(buffer, 128, "%s\n", session->serverbanner); - } else { - session->clientbanner = strdup(banner); - if (session->clientbanner == NULL) { - goto end; - } - snprintf(buffer, 128, "%s\n", session->clientbanner); - } - - if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERROR) { - goto end; - } -#ifdef WITH_PCAP - if(session->pcap_ctx) - ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT,buffer,strlen(buffer),strlen(buffer)); -#endif - err=SSH_OK; -end: - - return err; -} - -/** @internal - * @brief launches the DH handshake state machine - * @param session session handle - * @returns SSH_OK or SSH_ERROR - * @warning this function returning is no proof that DH handshake is - * completed - */ -static int dh_handshake(ssh_session session) { - - int rc = SSH_AGAIN; - - switch (session->dh_handshake_state) { - case DH_STATE_INIT: - switch(session->next_crypto->kex_type){ - case SSH_KEX_DH_GROUP1_SHA1: - case SSH_KEX_DH_GROUP14_SHA1: - rc = ssh_client_dh_init(session); - break; -#ifdef HAVE_ECDH - case SSH_KEX_ECDH_SHA2_NISTP256: - rc = ssh_client_ecdh_init(session); - break; -#endif -#ifdef HAVE_CURVE25519 - case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: - rc = ssh_client_curve25519_init(session); - break; -#endif - default: - rc = SSH_ERROR; - } - - if (rc == SSH_ERROR) { - return SSH_ERROR; - } - - session->dh_handshake_state = DH_STATE_INIT_SENT; - case DH_STATE_INIT_SENT: - /* wait until ssh_packet_dh_reply is called */ - break; - case DH_STATE_NEWKEYS_SENT: - /* wait until ssh_packet_newkeys is called */ - break; - case DH_STATE_FINISHED: - return SSH_OK; - default: - ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d", - session->dh_handshake_state); - - return SSH_ERROR; - } - - return rc; -} - -static int ssh_service_request_termination(void *s){ - ssh_session session = (ssh_session)s; - if(session->session_state == SSH_SESSION_STATE_ERROR || - session->auth_service_state != SSH_AUTH_SERVICE_SENT) - return 1; - else - return 0; -} - -/** - * @internal - * - * @brief Request a service from the SSH server. - * - * Service requests are for example: ssh-userauth, ssh-connection, etc. - * - * @param session The session to use to ask for a service request. - * @param service The service request. - * - * @return SSH_OK on success - * @return SSH_ERROR on error - * @return SSH_AGAIN No response received yet - * @bug actually only works with ssh-userauth - */ -int ssh_service_request(ssh_session session, const char *service) { - int rc=SSH_ERROR; - - if(session->auth_service_state != SSH_AUTH_SERVICE_NONE) - goto pending; - - rc = ssh_buffer_pack(session->out_buffer, - "bs", - SSH2_MSG_SERVICE_REQUEST, - service); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - return SSH_ERROR; - } - session->auth_service_state=SSH_AUTH_SERVICE_SENT; - if (packet_send(session) == SSH_ERROR) { - ssh_set_error(session, SSH_FATAL, - "Sending SSH2_MSG_SERVICE_REQUEST failed."); - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_PACKET, - "Sent SSH_MSG_SERVICE_REQUEST (service %s)", service); -pending: - rc=ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, - ssh_service_request_termination, session); - if (rc == SSH_ERROR) { - return SSH_ERROR; - } - switch(session->auth_service_state){ - case SSH_AUTH_SERVICE_DENIED: - ssh_set_error(session,SSH_FATAL,"ssh_auth_service request denied"); - break; - case SSH_AUTH_SERVICE_ACCEPTED: - rc=SSH_OK; - break; - case SSH_AUTH_SERVICE_SENT: - rc=SSH_AGAIN; - break; - case SSH_AUTH_SERVICE_NONE: - case SSH_AUTH_SERVICE_USER_SENT: - /* Invalid state, SSH1 specific */ - rc=SSH_ERROR; - break; - } - - return rc; -} - -/** - * @addtogroup libssh_session - * - * @{ - */ - -/** - * @internal - * - * @brief A function to be called each time a step has been done in the - * connection. - */ -static void ssh_client_connection_callback(ssh_session session){ - int ssh1,ssh2; - - switch(session->session_state){ - case SSH_SESSION_STATE_NONE: - case SSH_SESSION_STATE_CONNECTING: - case SSH_SESSION_STATE_SOCKET_CONNECTED: - break; - case SSH_SESSION_STATE_BANNER_RECEIVED: - if (session->serverbanner == NULL) { - goto error; - } - set_status(session, 0.4f); - SSH_LOG(SSH_LOG_RARE, - "SSH server banner: %s", session->serverbanner); - - /* Here we analyze the different protocols the server allows. */ - if (ssh_analyze_banner(session, 0, &ssh1, &ssh2) < 0) { - goto error; - } - /* Here we decide which version of the protocol to use. */ - if (ssh2 && session->opts.ssh2) { - session->version = 2; -#ifdef WITH_SSH1 - } else if(ssh1 && session->opts.ssh1) { - session->version = 1; -#endif - } else if(ssh1 && !session->opts.ssh1){ -#ifdef WITH_SSH1 - ssh_set_error(session, SSH_FATAL, - "SSH-1 protocol not available (configure session to allow SSH-1)"); - goto error; -#else - ssh_set_error(session, SSH_FATAL, - "SSH-1 protocol not available (libssh compiled without SSH-1 support)"); - goto error; -#endif - } else { - ssh_set_error(session, SSH_FATAL, - "No version of SSH protocol usable (banner: %s)", - session->serverbanner); - goto error; - } - /* from now, the packet layer is handling incoming packets */ - if(session->version==2) - session->socket_callbacks.data=ssh_packet_socket_callback; -#ifdef WITH_SSH1 - else - session->socket_callbacks.data=ssh_packet_socket_callback1; -#endif - ssh_packet_set_default_callbacks(session); - session->session_state=SSH_SESSION_STATE_INITIAL_KEX; - ssh_send_banner(session, 0); - set_status(session, 0.5f); - break; - case SSH_SESSION_STATE_INITIAL_KEX: - /* TODO: This state should disappear in favor of get_key handle */ -#ifdef WITH_SSH1 - if(session->version==1){ - if (ssh_get_kex1(session) < 0) - goto error; - set_status(session,0.6f); - session->connected = 1; - break; - } -#endif - break; - case SSH_SESSION_STATE_KEXINIT_RECEIVED: - set_status(session,0.6f); - ssh_list_kex(&session->next_crypto->server_kex); - if (set_client_kex(session) < 0) { - goto error; - } - if (ssh_kex_select_methods(session) == SSH_ERROR) - goto error; - if (ssh_send_kex(session, 0) < 0) { - goto error; - } - set_status(session,0.8f); - session->session_state=SSH_SESSION_STATE_DH; - if (dh_handshake(session) == SSH_ERROR) { - goto error; - } - /* FALL THROUGH */ - case SSH_SESSION_STATE_DH: - if(session->dh_handshake_state==DH_STATE_FINISHED){ - set_status(session,1.0f); - session->connected = 1; - if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) - session->session_state = SSH_SESSION_STATE_AUTHENTICATED; - else - session->session_state=SSH_SESSION_STATE_AUTHENTICATING; - } - break; - case SSH_SESSION_STATE_AUTHENTICATING: - break; - case SSH_SESSION_STATE_ERROR: - goto error; - default: - ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); - } - - return; -error: - ssh_socket_close(session->socket); - session->alive = 0; - session->session_state=SSH_SESSION_STATE_ERROR; - -} - -/** @internal - * @brief describe under which conditions the ssh_connect function may stop - */ -static int ssh_connect_termination(void *user){ - ssh_session session = (ssh_session)user; - switch(session->session_state){ - case SSH_SESSION_STATE_ERROR: - case SSH_SESSION_STATE_AUTHENTICATING: - case SSH_SESSION_STATE_DISCONNECTED: - return 1; - default: - return 0; - } -} - -/** - * @brief Connect to the ssh server. - * - * @param[in] session The ssh session to connect. - * - * @returns SSH_OK on success, SSH_ERROR on error. - * @returns SSH_AGAIN, if the session is in nonblocking mode, - * and call must be done again. - * - * @see ssh_new() - * @see ssh_disconnect() - */ -int ssh_connect(ssh_session session) { - int ret; - - if (session == NULL) { - return SSH_ERROR; - } - - switch(session->pending_call_state){ - case SSH_PENDING_CALL_NONE: - break; - case SSH_PENDING_CALL_CONNECT: - goto pending; - default: - ssh_set_error(session,SSH_FATAL,"Bad call during pending SSH call in ssh_connect"); - - return SSH_ERROR; - } - session->alive = 0; - session->client = 1; - - if (ssh_init() < 0) { - return SSH_ERROR; - } - if (session->opts.fd == SSH_INVALID_SOCKET && - session->opts.host == NULL && - session->opts.ProxyCommand == NULL) { - ssh_set_error(session, SSH_FATAL, "Hostname required"); - return SSH_ERROR; - } - - ret = ssh_options_apply(session); - if (ret < 0) { - ssh_set_error(session, SSH_FATAL, "Couldn't apply options"); - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_PROTOCOL, - "libssh %s, using threading %s", - ssh_copyright(), - ssh_threads_get_type()); - - session->ssh_connection_callback = ssh_client_connection_callback; - session->session_state=SSH_SESSION_STATE_CONNECTING; - ssh_socket_set_callbacks(session->socket,&session->socket_callbacks); - session->socket_callbacks.connected=socket_callback_connected; - session->socket_callbacks.data=callback_receive_banner; - session->socket_callbacks.exception=ssh_socket_exception_callback; - session->socket_callbacks.userdata=session; - if (session->opts.fd != SSH_INVALID_SOCKET) { - session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED; - ssh_socket_set_fd(session->socket, session->opts.fd); - ret=SSH_OK; -#ifndef _WIN32 - } else if (session->opts.ProxyCommand != NULL){ - ret = ssh_socket_connect_proxycommand(session->socket, - session->opts.ProxyCommand); -#endif - } else { - ret=ssh_socket_connect(session->socket, - session->opts.host, - session->opts.port, - session->opts.bindaddr); - } - if (ret == SSH_ERROR) { - return SSH_ERROR; - } - - set_status(session, 0.2f); - - session->alive = 1; - SSH_LOG(SSH_LOG_PROTOCOL,"Socket connecting, now waiting for the callbacks to work"); -pending: - session->pending_call_state=SSH_PENDING_CALL_CONNECT; - if(ssh_is_blocking(session)) { - int timeout = (session->opts.timeout * 1000) + - (session->opts.timeout_usec / 1000); - if (timeout == 0) { - timeout = 10 * 1000; - } - SSH_LOG(SSH_LOG_PACKET,"Actual timeout : %d", timeout); - ret = ssh_handle_packets_termination(session, timeout, ssh_connect_termination, session); - if (session->session_state != SSH_SESSION_STATE_ERROR && - (ret == SSH_ERROR || !ssh_connect_termination(session))) { - ssh_set_error(session, SSH_FATAL, - "Timeout connecting to %s", session->opts.host); - session->session_state = SSH_SESSION_STATE_ERROR; - } - } - else { - ret = ssh_handle_packets_termination(session, - SSH_TIMEOUT_NONBLOCKING, - ssh_connect_termination, - session); - if (ret == SSH_ERROR) { - session->session_state = SSH_SESSION_STATE_ERROR; - } - } - SSH_LOG(SSH_LOG_PACKET,"current state : %d",session->session_state); - if(!ssh_is_blocking(session) && !ssh_connect_termination(session)){ - return SSH_AGAIN; - } - - session->pending_call_state=SSH_PENDING_CALL_NONE; - if(session->session_state == SSH_SESSION_STATE_ERROR || session->session_state == SSH_SESSION_STATE_DISCONNECTED) - return SSH_ERROR; - return SSH_OK; -} - -/** - * @brief Get the issue banner from the server. - * - * This is the banner showing a disclaimer to users who log in, - * typically their right or the fact that they will be monitored. - * - * @param[in] session The SSH session to use. - * - * @return A newly allocated string with the banner, NULL on error. - */ -char *ssh_get_issue_banner(ssh_session session) { - if (session == NULL || session->banner == NULL) { - return NULL; - } - - return ssh_string_to_char(session->banner); -} - -/** - * @brief Get the version of the OpenSSH server, if it is not an OpenSSH server - * then 0 will be returned. - * - * You can use the SSH_VERSION_INT macro to compare version numbers. - * - * @param[in] session The SSH session to use. - * - * @return The version number if available, 0 otherwise. - * - * @code - * int openssh = ssh_get_openssh_version(); - * - * if (openssh == SSH_INT_VERSION(6, 1, 0)) { - * printf("Version match!\m"); - * } - * @endcode - */ -int ssh_get_openssh_version(ssh_session session) { - if (session == NULL) { - return 0; - } - - return session->openssh; -} - -/** - * @brief Disconnect from a session (client or server). - * The session can then be reused to open a new session. - * - * @param[in] session The SSH session to use. - */ -void ssh_disconnect(ssh_session session) { - struct ssh_iterator *it; - int rc; - - if (session == NULL) { - return; - } - - if (session->socket != NULL && ssh_socket_is_open(session->socket)) { - rc = ssh_buffer_pack(session->out_buffer, - "bds", - SSH2_MSG_DISCONNECT, - SSH2_DISCONNECT_BY_APPLICATION, - "Bye Bye"); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - goto error; - } - - packet_send(session); - ssh_socket_close(session->socket); - } -error: - session->alive = 0; - if (session->socket != NULL){ - ssh_socket_reset(session->socket); - } - session->opts.fd = SSH_INVALID_SOCKET; - session->session_state=SSH_SESSION_STATE_DISCONNECTED; - - while ((it=ssh_list_get_iterator(session->channels)) != NULL) { - ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); - ssh_list_remove(session->channels, it); - } - if(session->current_crypto){ - crypto_free(session->current_crypto); - session->current_crypto=NULL; - } - if (session->in_buffer) { - ssh_buffer_reinit(session->in_buffer); - } - if (session->out_buffer) { - ssh_buffer_reinit(session->out_buffer); - } - if (session->in_hashbuf) { - ssh_buffer_reinit(session->in_hashbuf); - } - if (session->out_hashbuf) { - ssh_buffer_reinit(session->out_hashbuf); - } - session->auth_methods = 0; - SAFE_FREE(session->serverbanner); - SAFE_FREE(session->clientbanner); - - if(session->ssh_message_list){ - ssh_message msg; - while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list)) - != NULL){ - ssh_message_free(msg); - } - ssh_list_free(session->ssh_message_list); - session->ssh_message_list=NULL; - } - - if (session->packet_callbacks){ - ssh_list_free(session->packet_callbacks); - session->packet_callbacks=NULL; - } -} - -const char *ssh_copyright(void) { - return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2014 Aris Adamantiadis, Andreas Schneider, " - "and libssh contributors. Distributed under the LGPL, please refer to COPYING " - "file for information about your rights"; -} -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/config.c b/libssh/src/config.c deleted file mode 100644 index 4c966ed3..00000000 --- a/libssh/src/config.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * config.c - parse the ssh config file - * - * This file is part of the SSH Library - * - * Copyright (c) 2009-2013 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/session.h" -#include "libssh/misc.h" -#include "libssh/options.h" - -enum ssh_config_opcode_e { - SOC_UNSUPPORTED = -1, - SOC_HOST, - SOC_HOSTNAME, - SOC_PORT, - SOC_USERNAME, - SOC_IDENTITY, - SOC_CIPHERS, - SOC_COMPRESSION, - SOC_TIMEOUT, - SOC_PROTOCOL, - SOC_STRICTHOSTKEYCHECK, - SOC_KNOWNHOSTS, - SOC_PROXYCOMMAND, - SOC_GSSAPISERVERIDENTITY, - SOC_GSSAPICLIENTIDENTITY, - SOC_GSSAPIDELEGATECREDENTIALS, -}; - -struct ssh_config_keyword_table_s { - const char *name; - enum ssh_config_opcode_e opcode; -}; - -static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = { - { "host", SOC_HOST }, - { "hostname", SOC_HOSTNAME }, - { "port", SOC_PORT }, - { "user", SOC_USERNAME }, - { "identityfile", SOC_IDENTITY }, - { "ciphers", SOC_CIPHERS }, - { "compression", SOC_COMPRESSION }, - { "connecttimeout", SOC_TIMEOUT }, - { "protocol", SOC_PROTOCOL }, - { "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK }, - { "userknownhostsfile", SOC_KNOWNHOSTS }, - { "proxycommand", SOC_PROXYCOMMAND }, - { "gssapiserveridentity", SOC_GSSAPISERVERIDENTITY }, - { "gssapiserveridentity", SOC_GSSAPICLIENTIDENTITY }, - { "gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS }, - { NULL, SOC_UNSUPPORTED } -}; - -static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) { - int i; - - for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) { - if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) { - return ssh_config_keyword_table[i].opcode; - } - } - - return SOC_UNSUPPORTED; -} - -static char *ssh_config_get_cmd(char **str) { - register char *c; - char *r; - - /* Ignore leading spaces */ - for (c = *str; *c; c++) { - if (! isblank(*c)) { - break; - } - } - - if (*c == '\"') { - for (r = ++c; *c; c++) { - if (*c == '\"') { - *c = '\0'; - goto out; - } - } - } - - for (r = c; *c; c++) { - if (*c == '\n') { - *c = '\0'; - goto out; - } - } - -out: - *str = c + 1; - - return r; -} - -static char *ssh_config_get_token(char **str) { - register char *c; - char *r; - - c = ssh_config_get_cmd(str); - - for (r = c; *c; c++) { - if (isblank(*c)) { - *c = '\0'; - goto out; - } - } - -out: - *str = c + 1; - - return r; -} - -static int ssh_config_get_int(char **str, int notfound) { - char *p, *endp; - int i; - - p = ssh_config_get_token(str); - if (p && *p) { - i = strtol(p, &endp, 10); - if (p == endp) { - return notfound; - } - return i; - } - - return notfound; -} - -static const char *ssh_config_get_str_tok(char **str, const char *def) { - char *p; - - p = ssh_config_get_token(str); - if (p && *p) { - return p; - } - - return def; -} - -static int ssh_config_get_yesno(char **str, int notfound) { - const char *p; - - p = ssh_config_get_str_tok(str, NULL); - if (p == NULL) { - return notfound; - } - - if (strncasecmp(p, "yes", 3) == 0) { - return 1; - } else if (strncasecmp(p, "no", 2) == 0) { - return 0; - } - - return notfound; -} - -static int ssh_config_parse_line(ssh_session session, const char *line, - unsigned int count, int *parsing) { - enum ssh_config_opcode_e opcode; - const char *p; - char *s, *x; - char *keyword; - char *lowerhost; - size_t len; - int i; - - x = s = strdup(line); - if (s == NULL) { - ssh_set_error_oom(session); - return -1; - } - - /* Remove trailing spaces */ - for (len = strlen(s) - 1; len > 0; len--) { - if (! isspace(s[len])) { - break; - } - s[len] = '\0'; - } - - keyword = ssh_config_get_token(&s); - if (keyword == NULL || *keyword == '#' || - *keyword == '\0' || *keyword == '\n') { - SAFE_FREE(x); - return 0; - } - - opcode = ssh_config_get_opcode(keyword); - - switch (opcode) { - case SOC_HOST: - *parsing = 0; - lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL; - for (p = ssh_config_get_str_tok(&s, NULL); - p != NULL && p[0] != '\0'; - p = ssh_config_get_str_tok(&s, NULL)) { - char *z = ssh_path_expand_escape(session, p); - int ok; - - if (z == NULL) { - z = strdup(p); - } - ok = match_hostname(lowerhost, z, strlen(z)); - if (ok) { - *parsing = 1; - } - free(z); - } - SAFE_FREE(lowerhost); - break; - case SOC_HOSTNAME: - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_HOST, p); - } - break; - case SOC_PORT: - if (session->opts.port == 22) { - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_PORT_STR, p); - } - } - break; - case SOC_USERNAME: - if (session->opts.username == NULL) { - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_USER, p); - } - } - break; - case SOC_IDENTITY: - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p); - } - break; - case SOC_CIPHERS: - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p); - ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p); - } - break; - case SOC_COMPRESSION: - i = ssh_config_get_yesno(&s, -1); - if (i >= 0 && *parsing) { - if (i) { - ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); - } else { - ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no"); - } - } - break; - case SOC_PROTOCOL: - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - char *a, *b; - b = strdup(p); - if (b == NULL) { - SAFE_FREE(x); - ssh_set_error_oom(session); - return -1; - } - i = 0; - ssh_options_set(session, SSH_OPTIONS_SSH1, &i); - ssh_options_set(session, SSH_OPTIONS_SSH2, &i); - - for (a = strtok(b, ","); a; a = strtok(NULL, ",")) { - switch (atoi(a)) { - case 1: - i = 1; - ssh_options_set(session, SSH_OPTIONS_SSH1, &i); - break; - case 2: - i = 1; - ssh_options_set(session, SSH_OPTIONS_SSH2, &i); - break; - default: - break; - } - } - SAFE_FREE(b); - } - break; - case SOC_TIMEOUT: - i = ssh_config_get_int(&s, -1); - if (i >= 0 && *parsing) { - ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &i); - } - break; - case SOC_STRICTHOSTKEYCHECK: - i = ssh_config_get_yesno(&s, -1); - if (i >= 0 && *parsing) { - ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i); - } - break; - case SOC_KNOWNHOSTS: - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p); - } - break; - case SOC_PROXYCOMMAND: - p = ssh_config_get_cmd(&s); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p); - } - break; - case SOC_GSSAPISERVERIDENTITY: - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p); - } - break; - case SOC_GSSAPICLIENTIDENTITY: - p = ssh_config_get_str_tok(&s, NULL); - if (p && *parsing) { - ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p); - } - break; - case SOC_GSSAPIDELEGATECREDENTIALS: - i = ssh_config_get_yesno(&s, -1); - if (i >=0 && *parsing) { - ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i); - } - break; - case SOC_UNSUPPORTED: - SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d\n", - keyword, count); - break; - default: - ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d\n", - opcode); - SAFE_FREE(x); - return -1; - break; - } - - SAFE_FREE(x); - return 0; -} - -/* ssh_config_parse_file */ -int ssh_config_parse_file(ssh_session session, const char *filename) { - char line[1024] = {0}; - unsigned int count = 0; - FILE *f; - int parsing; - - if ((f = fopen(filename, "r")) == NULL) { - return 0; - } - - SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename); - - parsing = 1; - while (fgets(line, sizeof(line), f)) { - count++; - if (ssh_config_parse_line(session, line, count, &parsing) < 0) { - fclose(f); - return -1; - } - } - - fclose(f); - return 0; -} diff --git a/libssh/src/connect.c b/libssh/src/connect.c deleted file mode 100644 index 4ef85bc4..00000000 --- a/libssh/src/connect.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * connect.c - handles connections to ssh servers - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "libssh/libssh.h" -#include "libssh/misc.h" - -#ifdef _WIN32 -/* - * Only use Windows API functions available on Windows 2000 SP4 or later. - * The available constants are in . - * http://msdn.microsoft.com/en-us/library/aa383745.aspx - * http://blogs.msdn.com/oldnewthing/archive/2007/04/11/2079137.aspx - */ -#undef _WIN32_WINNT -#ifdef HAVE_WSPIAPI_H -#define _WIN32_WINNT 0x0500 /* _WIN32_WINNT_WIN2K */ -#undef NTDDI_VERSION -#define NTDDI_VERSION 0x05000400 /* NTDDI_WIN2KSP4 */ -#else -#define _WIN32_WINNT 0x0501 /* _WIN32_WINNT_WINXP */ -#undef NTDDI_VERSION -#define NTDDI_VERSION 0x05010000 /* NTDDI_WINXP */ -#endif - -#if _MSC_VER >= 1400 -#include -#undef close -#define close _close -#endif /* _MSC_VER */ -#include -#include - -/* is necessary for getaddrinfo before Windows XP, but it isn't - * available on some platforms like MinGW. */ -#ifdef HAVE_WSPIAPI_H -#include -#endif - -#else /* _WIN32 */ - -#include -#include -#include -#include - -#endif /* _WIN32 */ - -#include "libssh/priv.h" -#include "libssh/socket.h" -#include "libssh/channels.h" -#include "libssh/session.h" -#include "libssh/poll.h" - -#ifndef HAVE_GETADDRINFO -#error "Your system must have getaddrinfo()" -#endif - -#ifdef _WIN32 -#ifndef gai_strerror -char WSAAPI *gai_strerrorA(int code) { - static char buf[256]; - - snprintf(buf, sizeof(buf), "Undetermined error code (%d)", code); - - return buf; -} -#endif /* gai_strerror */ -#endif /* _WIN32 */ - -static int ssh_connect_socket_close(socket_t s){ -#ifdef _WIN32 - return closesocket(s); -#else - return close(s); -#endif -} - - -static int getai(const char *host, int port, struct addrinfo **ai) { - const char *service = NULL; - struct addrinfo hints; - char s_port[10]; - - ZERO_STRUCT(hints); - - hints.ai_protocol = IPPROTO_TCP; - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if (port == 0) { - hints.ai_flags = AI_PASSIVE; - } else { - snprintf(s_port, sizeof(s_port), "%hu", (unsigned short)port); - service = s_port; -#ifdef AI_NUMERICSERV - hints.ai_flags=AI_NUMERICSERV; -#endif - } - - if (ssh_is_ipaddr(host)) { - /* this is an IP address */ - SSH_LOG(SSH_LOG_PACKET,"host %s matches an IP address",host); - hints.ai_flags |= AI_NUMERICHOST; - } - - return getaddrinfo(host, service, &hints, ai); -} - -static int ssh_connect_ai_timeout(ssh_session session, const char *host, - int port, struct addrinfo *ai, long timeout, long usec, socket_t s) { - int timeout_ms; - ssh_pollfd_t fds; - int rc = 0; - int ret; - socklen_t len = sizeof(rc); - - /* I know we're losing some precision. But it's not like poll-like family - * type of mechanisms are precise up to the microsecond. - */ - timeout_ms=timeout * 1000 + usec / 1000; - - rc = ssh_socket_set_nonblocking(s); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, - "Failed to set socket non-blocking for %s:%d", host, port); - ssh_connect_socket_close(s); - return -1; - } - - SSH_LOG(SSH_LOG_RARE, "Trying to connect to host: %s:%d with " - "timeout %d ms", host, port, timeout_ms); - - /* The return value is checked later */ - connect(s, ai->ai_addr, ai->ai_addrlen); - freeaddrinfo(ai); - - fds.fd=s; - fds.revents=0; - fds.events=POLLOUT; -#ifdef _WIN32 - fds.events |= POLLWRNORM; -#endif - rc = ssh_poll(&fds,1,timeout_ms); - - if (rc == 0) { - /* timeout */ - ssh_set_error(session, SSH_FATAL, - "Timeout while connecting to %s:%d", host, port); - ssh_connect_socket_close(s); - - return -1; - } - - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, - "poll error: %s", strerror(errno)); - ssh_connect_socket_close(s); - - return -1; - } - rc = -1; - - /* Get connect(2) return code. Zero means no error */ - ret = getsockopt(s, SOL_SOCKET, SO_ERROR,(char *) &rc, &len); - if (ret < 0 || rc != 0) { - ssh_set_error(session, SSH_FATAL, - "Connect to %s:%d failed: %s", host, port, strerror(rc)); - ssh_connect_socket_close(s); - - return -1; - } - - /* s is connected ? */ - SSH_LOG(SSH_LOG_PACKET, "Socket connected with timeout\n"); - ret = ssh_socket_set_blocking(s); - if (ret < 0) { - ssh_set_error(session, SSH_FATAL, - "Failed to set socket as blocking connecting to %s:%d failed: %s", - host, port, strerror(errno)); - ssh_connect_socket_close(s); - return -1; - } - - return s; -} - -/** - * @internal - * - * @brief Connect to an IPv4 or IPv6 host specified by its IP address or - * hostname. - * - * @returns A file descriptor, < 0 on error. - */ -socket_t ssh_connect_host(ssh_session session, const char *host, - const char *bind_addr, int port, long timeout, long usec) { - socket_t s = -1; - int rc; - struct addrinfo *ai; - struct addrinfo *itr; - - rc = getai(host, port, &ai); - if (rc != 0) { - ssh_set_error(session, SSH_FATAL, - "Failed to resolve hostname %s (%s)", host, gai_strerror(rc)); - - return -1; - } - - for (itr = ai; itr != NULL; itr = itr->ai_next){ - /* create socket */ - s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol); - if (s < 0) { - ssh_set_error(session, SSH_FATAL, - "Socket create failed: %s", strerror(errno)); - continue; - } - - if (bind_addr) { - struct addrinfo *bind_ai; - struct addrinfo *bind_itr; - - SSH_LOG(SSH_LOG_PACKET, "Resolving %s\n", bind_addr); - - rc = getai(bind_addr, 0, &bind_ai); - if (rc != 0) { - ssh_set_error(session, SSH_FATAL, - "Failed to resolve bind address %s (%s)", - bind_addr, - gai_strerror(rc)); - freeaddrinfo(ai); - close(s); - - return -1; - } - - for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) { - if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) { - ssh_set_error(session, SSH_FATAL, - "Binding local address: %s", strerror(errno)); - continue; - } else { - break; - } - } - freeaddrinfo(bind_ai); - - /* Cannot bind to any local addresses */ - if (bind_itr == NULL) { - ssh_connect_socket_close(s); - s = -1; - continue; - } - } - - if (timeout || usec) { - socket_t ret = ssh_connect_ai_timeout(session, host, port, itr, - timeout, usec, s); - - return ret; - } - - if (connect(s, itr->ai_addr, itr->ai_addrlen) < 0) { - ssh_set_error(session, SSH_FATAL, "Connect failed: %s", strerror(errno)); - ssh_connect_socket_close(s); - s = -1; - continue; - } else { - /* We are connected */ - break; - } - } - - freeaddrinfo(ai); - - return s; -} - -/** - * @internal - * - * @brief Launches a nonblocking connect to an IPv4 or IPv6 host - * specified by its IP address or hostname. - * - * @returns A file descriptor, < 0 on error. - * @warning very ugly !!! - */ -socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host, - const char *bind_addr, int port) { - socket_t s = -1; - int rc; - struct addrinfo *ai; - struct addrinfo *itr; - - rc = getai(host, port, &ai); - if (rc != 0) { - ssh_set_error(session, SSH_FATAL, - "Failed to resolve hostname %s (%s)", host, gai_strerror(rc)); - - return -1; - } - - for (itr = ai; itr != NULL; itr = itr->ai_next){ - /* create socket */ - s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol); - if (s < 0) { - ssh_set_error(session, SSH_FATAL, - "Socket create failed: %s", strerror(errno)); - continue; - } - - if (bind_addr) { - struct addrinfo *bind_ai; - struct addrinfo *bind_itr; - - SSH_LOG(SSH_LOG_PACKET, "Resolving %s\n", bind_addr); - - rc = getai(bind_addr, 0, &bind_ai); - if (rc != 0) { - ssh_set_error(session, SSH_FATAL, - "Failed to resolve bind address %s (%s)", - bind_addr, - gai_strerror(rc)); - ssh_connect_socket_close(s); - s=-1; - break; - } - - for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) { - if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) { - ssh_set_error(session, SSH_FATAL, - "Binding local address: %s", strerror(errno)); - continue; - } else { - break; - } - } - freeaddrinfo(bind_ai); - - /* Cannot bind to any local addresses */ - if (bind_itr == NULL) { - ssh_connect_socket_close(s); - s = -1; - continue; - } - } - - rc = ssh_socket_set_nonblocking(s); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, - "Failed to set socket non-blocking for %s:%d", host, port); - ssh_connect_socket_close(s); - s = -1; - continue; - } - - rc = connect(s, itr->ai_addr, itr->ai_addrlen); - if (rc == -1 && (errno != EINPROGRESS)) { - ssh_set_error(session, SSH_FATAL, - "Failed to connect: %s", strerror(errno)); - ssh_connect_socket_close(s); - s = -1; - continue; - } - - break; - } - - freeaddrinfo(ai); - - return s; -} - -/** - * @addtogroup libssh_session - * - * @{ - */ - -static int ssh_select_cb (socket_t fd, int revents, void *userdata){ - fd_set *set = (fd_set *)userdata; - if(revents & POLLIN) - FD_SET(fd, set); - return 0; -} - -/** - * @brief A wrapper for the select syscall - * - * This functions acts more or less like the select(2) syscall.\n - * There is no support for writing or exceptions.\n - * - * @param[in] channels Arrays of channels pointers terminated by a NULL. - * It is never rewritten. - * - * @param[out] outchannels Arrays of same size that "channels", there is no need - * to initialize it. - * - * @param[in] maxfd Maximum +1 file descriptor from readfds. - * - * @param[in] readfds A fd_set of file descriptors to be select'ed for - * reading. - * - * @param[in] timeout The timeout in milliseconds. - * - * @return SSH_OK on success, - * SSH_ERROR on error, - * SSH_EINTR if it was interrupted. In that case, - * just restart it. - * - * @warning libssh is not reentrant here. That means that if a signal is caught - * during the processing of this function, you cannot call libssh - * functions on sessions that are busy with ssh_select(). - * - * @see select(2) - */ -int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, - fd_set *readfds, struct timeval *timeout) { - fd_set origfds; - socket_t fd; - int i,j; - int rc; - int base_tm, tm; - struct ssh_timestamp ts; - ssh_event event = ssh_event_new(); - int firstround=1; - - base_tm = tm=timeout->tv_sec * 1000 + timeout->tv_usec/1000; - for (i=0 ; channels[i] != NULL; ++i){ - ssh_event_add_session(event, channels[i]->session); - } - - FD_ZERO(&origfds); - for (fd = 0; fd < maxfd ; fd++) { - if (FD_ISSET(fd, readfds)) { - ssh_event_add_fd(event, fd, POLLIN, ssh_select_cb, readfds); - FD_SET(fd, &origfds); - } - } - outchannels[0] = NULL; - FD_ZERO(readfds); - ssh_timestamp_init(&ts); - do { - /* Poll every channel */ - j = 0; - for (i = 0; channels[i]; i++) { - if(ssh_channel_poll(channels[i], 0) != 0) { - outchannels[j] = channels[i]; - j++; - } else if(ssh_channel_poll(channels[i], 1) != 0) { - outchannels[j] = channels[i]; - j++; - } - } - outchannels[j] = NULL; - if(j != 0) - break; - /* watch if a user socket was triggered */ - for (fd = 0; fd < maxfd; fd++) { - if (FD_ISSET(fd, readfds)) { - goto out; - } - } - - /* If the timeout is elapsed, we should go out */ - if(!firstround && ssh_timeout_elapsed(&ts, base_tm)) - goto out; - /* since there's nothing, let's fire the polling */ - rc = ssh_event_dopoll(event,tm); - if (rc == SSH_ERROR){ - goto out; - } - tm = ssh_timeout_update(&ts, base_tm); - firstround=0; - } while (1); -out: - for (fd = 0; fd < maxfd; fd++) { - if (FD_ISSET(fd, &origfds)) { - ssh_event_remove_fd(event, fd); - } - } - ssh_event_free(event); - return SSH_OK; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/crc32.c b/libssh/src/crc32.c deleted file mode 100644 index cb4e931d..00000000 --- a/libssh/src/crc32.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * crc32.c - simple CRC32 code - * - * This file is part of the SSH Library - * - * Copyright (c) 2005 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include "libssh/priv.h" -#include "libssh/crc32.h" - -static uint32_t crc_table[] = { - 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, - 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, - 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, - 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, - 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, - 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, - 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, - 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, - 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, - 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, - 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, - 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, - 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, - 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, - 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, - 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, - 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, - 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, - 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, - 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, - 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, - 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, - 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, - 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, - 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, - 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, - 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, - 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, - 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, - 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, - 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, - 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, - 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, - 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, - 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, - 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, - 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, - 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, - 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, - 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, - 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, - 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, - 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, - 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, - 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, - 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, - 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, - 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, - 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, - 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, - 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, - 0x2d02ef8dUL -}; - -uint32_t ssh_crc32(const char *buf, uint32_t len) { - uint32_t ret = 0; - while(len > 0) { - ret = crc_table[(ret ^ *buf) & 0xff] ^ (ret >> 8); - --len; - ++buf; - } - - return ret; -} - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/curve25519.c b/libssh/src/curve25519.c deleted file mode 100644 index 99d7145b..00000000 --- a/libssh/src/curve25519.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * curve25519.c - Curve25519 ECDH functions for key exchange - * curve25519-sha256@libssh.org - * - * This file is part of the SSH Library - * - * Copyright (c) 2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 2.1 of the License. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include "libssh/curve25519.h" -#ifdef HAVE_CURVE25519 - -#ifdef WITH_NACL -#include "nacl/crypto_scalarmult_curve25519.h" -#endif - -#include "libssh/ssh2.h" -#include "libssh/buffer.h" -#include "libssh/priv.h" -#include "libssh/session.h" -#include "libssh/crypto.h" -#include "libssh/dh.h" -#include "libssh/pki.h" -#include "libssh/bignum.h" - -/** @internal - * @brief Starts curve25519-sha256@libssh.org key exchange - */ -int ssh_client_curve25519_init(ssh_session session){ - int rc; - - rc = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); - if (rc == 0){ - ssh_set_error(session, SSH_FATAL, "PRNG error"); - return SSH_ERROR; - } - - crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey, - session->next_crypto->curve25519_privkey); - - rc = ssh_buffer_pack(session->out_buffer, - "bdP", - SSH2_MSG_KEX_ECDH_INIT, - CURVE25519_PUBKEY_SIZE, - (size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_client_pubkey); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - - rc = packet_send(session); - - return rc; -} - -static int ssh_curve25519_build_k(ssh_session session) { - ssh_curve25519_pubkey k; - session->next_crypto->k = bignum_new(); - - if (session->next_crypto->k == NULL) { - return SSH_ERROR; - } - - if (session->server) - crypto_scalarmult(k, session->next_crypto->curve25519_privkey, - session->next_crypto->curve25519_client_pubkey); - else - crypto_scalarmult(k, session->next_crypto->curve25519_privkey, - session->next_crypto->curve25519_server_pubkey); - - bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Session server cookie", - session->next_crypto->server_kex.cookie, 16); - ssh_print_hexa("Session client cookie", - session->next_crypto->client_kex.cookie, 16); - ssh_print_bignum("Shared secret key", session->next_crypto->k); -#endif - - return 0; -} - -/** @internal - * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back - * a SSH_MSG_NEWKEYS - */ -int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet){ - ssh_string q_s_string = NULL; - ssh_string pubkey = NULL; - ssh_string signature = NULL; - int rc; - pubkey = buffer_get_ssh_string(packet); - if (pubkey == NULL){ - ssh_set_error(session,SSH_FATAL, "No public key in packet"); - goto error; - } - /* this is the server host key */ - session->next_crypto->server_pubkey = pubkey; - pubkey = NULL; - - q_s_string = buffer_get_ssh_string(packet); - if (q_s_string == NULL) { - ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet"); - goto error; - } - if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){ - ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", - (int)ssh_string_len(q_s_string)); - ssh_string_free(q_s_string); - goto error; - } - memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE); - ssh_string_free(q_s_string); - - signature = buffer_get_ssh_string(packet); - if (signature == NULL) { - ssh_set_error(session, SSH_FATAL, "No signature in packet"); - goto error; - } - session->next_crypto->dh_server_signature = signature; - signature=NULL; /* ownership changed */ - /* TODO: verify signature now instead of waiting for NEWKEYS */ - if (ssh_curve25519_build_k(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - goto error; - } - - /* Send the MSG_NEWKEYS */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; - } - - rc=packet_send(session); - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); - return rc; -error: - return SSH_ERROR; -} - -#ifdef WITH_SERVER - -/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a - * SSH_MSG_KEXDH_REPLY - */ -int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ - /* ECDH keys */ - ssh_string q_c_string; - ssh_string q_s_string; - - /* SSH host keys (rsa,dsa,ecdsa) */ - ssh_key privkey; - ssh_string sig_blob = NULL; - int rc; - - /* Extract the client pubkey from the init packet */ - q_c_string = buffer_get_ssh_string(packet); - if (q_c_string == NULL) { - ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); - return SSH_ERROR; - } - if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE){ - ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", - (int)ssh_string_len(q_c_string)); - ssh_string_free(q_c_string); - return SSH_ERROR; - } - - memcpy(session->next_crypto->curve25519_client_pubkey, - ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE); - ssh_string_free(q_c_string); - /* Build server's keypair */ - - rc = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); - if (rc == 0){ - ssh_set_error(session, SSH_FATAL, "PRNG error"); - return SSH_ERROR; - } - - crypto_scalarmult_base(session->next_crypto->curve25519_server_pubkey, - session->next_crypto->curve25519_privkey); - - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_REPLY); - if (rc < 0) { - ssh_set_error_oom(session); - goto error; - } - - /* build k and session_id */ - rc = ssh_curve25519_build_k(session); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - goto error; - } - - /* privkey is not allocated */ - rc = ssh_get_key_params(session, &privkey); - if (rc == SSH_ERROR) { - goto error; - } - - rc = make_sessionid(session); - if (rc != SSH_OK) { - ssh_set_error(session, SSH_FATAL, "Could not create a session id"); - goto error; - } - - /* add host's public key */ - rc = buffer_add_ssh_string(session->out_buffer, - session->next_crypto->server_pubkey); - if (rc < 0) { - ssh_set_error_oom(session); - goto error; - } - - /* add ecdh public key */ - q_s_string = ssh_string_new(CURVE25519_PUBKEY_SIZE); - if (q_s_string == NULL) { - goto error; - } - - ssh_string_fill(q_s_string, - session->next_crypto->curve25519_server_pubkey, - CURVE25519_PUBKEY_SIZE); - - rc = buffer_add_ssh_string(session->out_buffer, q_s_string); - ssh_string_free(q_s_string); - if (rc < 0) { - ssh_set_error_oom(session); - goto error; - } - /* add signature blob */ - sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); - if (sig_blob == NULL) { - ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); - goto error; - } - - rc = buffer_add_ssh_string(session->out_buffer, sig_blob); - ssh_string_free(sig_blob); - if (rc < 0) { - ssh_set_error_oom(session); - goto error; - } - - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_ECDH_REPLY sent"); - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_ERROR; - } - - /* Send the MSG_NEWKEYS */ - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); - if (rc < 0) { - goto error; - } - - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - rc = packet_send(session); - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); - - return rc; -error: - ssh_buffer_reinit(session->out_buffer); - return SSH_ERROR; -} - -#endif /* WITH_SERVER */ - -#endif /* HAVE_CURVE25519 */ diff --git a/libssh/src/curve25519_ref.c b/libssh/src/curve25519_ref.c deleted file mode 100644 index aa4cfa2b..00000000 --- a/libssh/src/curve25519_ref.c +++ /dev/null @@ -1,272 +0,0 @@ -/* -version 20081011 -Matthew Dempsky -Public domain. -Derived from public domain code by D. J. Bernstein. -*/ - -#include "libssh/curve25519.h" -static const unsigned char base[32] = {9}; - -int crypto_scalarmult_base(unsigned char *q, - const unsigned char *n) -{ - return crypto_scalarmult(q,n,base); -} - -static void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) -{ - unsigned int j; - unsigned int u; - u = 0; - for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; } - u += a[31] + b[31]; out[31] = u; -} - -static void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) -{ - unsigned int j; - unsigned int u; - u = 218; - for (j = 0;j < 31;++j) { - u += a[j] + 65280 - b[j]; - out[j] = u & 255; - u >>= 8; - } - u += a[31] - b[31]; - out[31] = u; -} - -static void squeeze(unsigned int a[32]) -{ - unsigned int j; - unsigned int u; - u = 0; - for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } - u += a[31]; a[31] = u & 127; - u = 19 * (u >> 7); - for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } - u += a[31]; a[31] = u; -} - -static const unsigned int minusp[32] = { - 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 -} ; - -static void freeze(unsigned int a[32]) -{ - unsigned int aorig[32]; - unsigned int j; - unsigned int negative; - - for (j = 0;j < 32;++j) aorig[j] = a[j]; - add(a,a,minusp); - negative = -((a[31] >> 7) & 1); - for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]); -} - -static void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) -{ - unsigned int i; - unsigned int j; - unsigned int u; - - for (i = 0;i < 32;++i) { - u = 0; - for (j = 0;j <= i;++j) u += a[j] * b[i - j]; - for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j]; - out[i] = u; - } - squeeze(out); -} - -static void mult121665(unsigned int out[32],const unsigned int a[32]) -{ - unsigned int j; - unsigned int u; - - u = 0; - for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; } - u += 121665 * a[31]; out[31] = u & 127; - u = 19 * (u >> 7); - for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; } - u += out[j]; out[j] = u; -} - -static void square(unsigned int out[32],const unsigned int a[32]) -{ - unsigned int i; - unsigned int j; - unsigned int u; - - for (i = 0;i < 32;++i) { - u = 0; - for (j = 0;j < i - j;++j) u += a[j] * a[i - j]; - for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j]; - u *= 2; - if ((i & 1) == 0) { - u += a[i / 2] * a[i / 2]; - u += 38 * a[i / 2 + 16] * a[i / 2 + 16]; - } - out[i] = u; - } - squeeze(out); -} - -static void c_select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b) -{ - unsigned int j; - unsigned int t; - unsigned int bminus1; - - bminus1 = b - 1; - for (j = 0;j < 64;++j) { - t = bminus1 & (r[j] ^ s[j]); - p[j] = s[j] ^ t; - q[j] = r[j] ^ t; - } -} - -static void mainloop(unsigned int work[64],const unsigned char e[32]) -{ - unsigned int xzm1[64]; - unsigned int xzm[64]; - unsigned int xzmb[64]; - unsigned int xzm1b[64]; - unsigned int xznb[64]; - unsigned int xzn1b[64]; - unsigned int a0[64]; - unsigned int a1[64]; - unsigned int b0[64]; - unsigned int b1[64]; - unsigned int c1[64]; - unsigned int r[32]; - unsigned int s[32]; - unsigned int t[32]; - unsigned int u[32]; - unsigned int j; - unsigned int b; - int pos; - - for (j = 0;j < 32;++j) xzm1[j] = work[j]; - xzm1[32] = 1; - for (j = 33;j < 64;++j) xzm1[j] = 0; - - xzm[0] = 1; - for (j = 1;j < 64;++j) xzm[j] = 0; - - for (pos = 254;pos >= 0;--pos) { - b = e[pos / 8] >> (pos & 7); - b &= 1; - c_select(xzmb,xzm1b,xzm,xzm1,b); - add(a0,xzmb,xzmb + 32); - sub(a0 + 32,xzmb,xzmb + 32); - add(a1,xzm1b,xzm1b + 32); - sub(a1 + 32,xzm1b,xzm1b + 32); - square(b0,a0); - square(b0 + 32,a0 + 32); - mult(b1,a1,a0 + 32); - mult(b1 + 32,a1 + 32,a0); - add(c1,b1,b1 + 32); - sub(c1 + 32,b1,b1 + 32); - square(r,c1 + 32); - sub(s,b0,b0 + 32); - mult121665(t,s); - add(u,t,b0); - mult(xznb,b0,b0 + 32); - mult(xznb + 32,s,u); - square(xzn1b,c1); - mult(xzn1b + 32,r,work); - c_select(xzm,xzm1,xznb,xzn1b,b); - } - - for (j = 0;j < 64;++j) work[j] = xzm[j]; -} - -static void recip(unsigned int out[32],const unsigned int z[32]) -{ - unsigned int z2[32]; - unsigned int z9[32]; - unsigned int z11[32]; - unsigned int z2_5_0[32]; - unsigned int z2_10_0[32]; - unsigned int z2_20_0[32]; - unsigned int z2_50_0[32]; - unsigned int z2_100_0[32]; - unsigned int t0[32]; - unsigned int t1[32]; - int i; - - /* 2 */ square(z2,z); - /* 4 */ square(t1,z2); - /* 8 */ square(t0,t1); - /* 9 */ mult(z9,t0,z); - /* 11 */ mult(z11,z9,z2); - /* 22 */ square(t0,z11); - /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9); - - /* 2^6 - 2^1 */ square(t0,z2_5_0); - /* 2^7 - 2^2 */ square(t1,t0); - /* 2^8 - 2^3 */ square(t0,t1); - /* 2^9 - 2^4 */ square(t1,t0); - /* 2^10 - 2^5 */ square(t0,t1); - /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0); - - /* 2^11 - 2^1 */ square(t0,z2_10_0); - /* 2^12 - 2^2 */ square(t1,t0); - /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); } - /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0); - - /* 2^21 - 2^1 */ square(t0,z2_20_0); - /* 2^22 - 2^2 */ square(t1,t0); - /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); } - /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0); - - /* 2^41 - 2^1 */ square(t1,t0); - /* 2^42 - 2^2 */ square(t0,t1); - /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); } - /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0); - - /* 2^51 - 2^1 */ square(t0,z2_50_0); - /* 2^52 - 2^2 */ square(t1,t0); - /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } - /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0); - - /* 2^101 - 2^1 */ square(t1,z2_100_0); - /* 2^102 - 2^2 */ square(t0,t1); - /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); } - /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0); - - /* 2^201 - 2^1 */ square(t0,t1); - /* 2^202 - 2^2 */ square(t1,t0); - /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } - /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0); - - /* 2^251 - 2^1 */ square(t1,t0); - /* 2^252 - 2^2 */ square(t0,t1); - /* 2^253 - 2^3 */ square(t1,t0); - /* 2^254 - 2^4 */ square(t0,t1); - /* 2^255 - 2^5 */ square(t1,t0); - /* 2^255 - 21 */ mult(out,t1,z11); -} - -int crypto_scalarmult(unsigned char *q, - const unsigned char *n, - const unsigned char *p) -{ - unsigned int work[96]; - unsigned char e[32]; - unsigned int i; - for (i = 0;i < 32;++i) e[i] = n[i]; - e[0] &= 248; - e[31] &= 127; - e[31] |= 64; - for (i = 0;i < 32;++i) work[i] = p[i]; - mainloop(work,e); - recip(work + 32,work + 32); - mult(work + 64,work,work + 32); - freeze(work + 64); - for (i = 0;i < 32;++i) q[i] = work[64 + i]; - return 0; -} - diff --git a/libssh/src/dh.c b/libssh/src/dh.c deleted file mode 100644 index e489a1d5..00000000 --- a/libssh/src/dh.c +++ /dev/null @@ -1,1124 +0,0 @@ -/* - * dh.c - Diffie-Helman algorithm code against SSH 2 - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis - * Copyright (c) 2009-2013 by Andreas Schneider - * Copyright (c) 2012 by Dmitriy Kuznetsov - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/* - * Let us resume the dh protocol. - * Each side computes a private prime number, x at client side, y at server - * side. - * g and n are two numbers common to every ssh software. - * client's public key (e) is calculated by doing: - * e = g^x mod p - * client sends e to the server. - * the server computes his own public key, f - * f = g^y mod p - * it sends it to the client - * the common key K is calculated by the client by doing - * k = f^x mod p - * the server does the same with the client public key e - * k' = e^y mod p - * if everything went correctly, k and k' are equal - */ - -#include "config.h" -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/priv.h" -#include "libssh/crypto.h" -#include "libssh/buffer.h" -#include "libssh/session.h" -#include "libssh/misc.h" -#include "libssh/dh.h" -#include "libssh/ssh2.h" -#include "libssh/pki.h" -#include "libssh/bignum.h" - -/* todo: remove it */ -#include "libssh/string.h" -#ifdef HAVE_LIBCRYPTO -#include -#include -#include -#endif - -static unsigned char p_group1_value[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, - 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, - 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, - 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, - 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, - 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, - 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -#define P_GROUP1_LEN 128 /* Size in bytes of the p number */ - - -static unsigned char p_group14_value[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, - 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, - 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, - 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, - 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, - 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, - 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, - 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, - 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, - 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, - 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, - 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, - 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, - 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, - 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, - 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, - 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, - 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, - 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF}; - -#define P_GROUP14_LEN 256 /* Size in bytes of the p number for group 14 */ - -static unsigned long g_int = 2 ; /* G is defined as 2 by the ssh2 standards */ -static bignum g; -static bignum p_group1; -static bignum p_group14; -static int ssh_crypto_initialized; - -static bignum select_p(enum ssh_key_exchange_e type) { - return type == SSH_KEX_DH_GROUP14_SHA1 ? p_group14 : p_group1; -} - -int ssh_get_random(void *where, int len, int strong){ - -#ifdef HAVE_LIBGCRYPT - /* variable not used in gcrypt */ - (void) strong; - /* not using GCRY_VERY_STRONG_RANDOM which is a bit overkill */ - gcry_randomize(where,len,GCRY_STRONG_RANDOM); - - return 1; -#elif defined HAVE_LIBCRYPTO - if (strong) { - return RAND_bytes(where,len); - } else { - return RAND_pseudo_bytes(where,len); - } -#endif - - /* never reached */ - return 1; -} - - -/* - * This inits the values g and p which are used for DH key agreement - * FIXME: Make the function thread safe by adding a semaphore or mutex. - */ -int ssh_crypto_init(void) { - if (ssh_crypto_initialized == 0) { -#ifdef HAVE_LIBGCRYPT - gcry_check_version(NULL); - if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P,0)) { - gcry_control(GCRYCTL_INIT_SECMEM, 4096); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED,0); - } -#endif - - g = bignum_new(); - if (g == NULL) { - return -1; - } - bignum_set_word(g,g_int); - -#ifdef HAVE_LIBGCRYPT - bignum_bin2bn(p_group1_value, P_GROUP1_LEN, &p_group1); - if (p_group1 == NULL) { - bignum_free(g); - g = NULL; - return -1; - } - bignum_bin2bn(p_group14_value, P_GROUP14_LEN, &p_group14); - if (p_group14 == NULL) { - bignum_free(g); - bignum_free(p_group1); - g = NULL; - p_group1 = NULL; - return -1; - } - -#elif defined HAVE_LIBCRYPTO - p_group1 = bignum_new(); - if (p_group1 == NULL) { - bignum_free(g); - g = NULL; - return -1; - } - bignum_bin2bn(p_group1_value, P_GROUP1_LEN, p_group1); - - p_group14 = bignum_new(); - if (p_group14 == NULL) { - bignum_free(g); - bignum_free(p_group1); - g = NULL; - p_group1 = NULL; - return -1; - } - bignum_bin2bn(p_group14_value, P_GROUP14_LEN, p_group14); - - OpenSSL_add_all_algorithms(); - -#endif - - ssh_crypto_initialized = 1; - } - - return 0; -} - -void ssh_crypto_finalize(void) { - if (ssh_crypto_initialized) { - bignum_free(g); - g = NULL; - bignum_free(p_group1); - p_group1 = NULL; - bignum_free(p_group14); - p_group14 = NULL; -#ifdef HAVE_LIBGCRYPT - gcry_control(GCRYCTL_TERM_SECMEM); -#elif defined HAVE_LIBCRYPTO - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); -#endif - ssh_crypto_initialized=0; - } -} - -int dh_generate_x(ssh_session session) { - session->next_crypto->x = bignum_new(); - if (session->next_crypto->x == NULL) { - return -1; - } - -#ifdef HAVE_LIBGCRYPT - bignum_rand(session->next_crypto->x, 128); -#elif defined HAVE_LIBCRYPTO - bignum_rand(session->next_crypto->x, 128, 0, -1); -#endif - - /* not harder than this */ -#ifdef DEBUG_CRYPTO - ssh_print_bignum("x", session->next_crypto->x); -#endif - - return 0; -} - -/* used by server */ -int dh_generate_y(ssh_session session) { - session->next_crypto->y = bignum_new(); - if (session->next_crypto->y == NULL) { - return -1; - } - -#ifdef HAVE_LIBGCRYPT - bignum_rand(session->next_crypto->y, 128); -#elif defined HAVE_LIBCRYPTO - bignum_rand(session->next_crypto->y, 128, 0, -1); -#endif - - /* not harder than this */ -#ifdef DEBUG_CRYPTO - ssh_print_bignum("y", session->next_crypto->y); -#endif - - return 0; -} - -/* used by server */ -int dh_generate_e(ssh_session session) { -#ifdef HAVE_LIBCRYPTO - bignum_CTX ctx = bignum_ctx_new(); - if (ctx == NULL) { - return -1; - } -#endif - - session->next_crypto->e = bignum_new(); - if (session->next_crypto->e == NULL) { -#ifdef HAVE_LIBCRYPTO - bignum_ctx_free(ctx); -#endif - return -1; - } - -#ifdef HAVE_LIBGCRYPT - bignum_mod_exp(session->next_crypto->e, g, session->next_crypto->x, - select_p(session->next_crypto->kex_type)); -#elif defined HAVE_LIBCRYPTO - bignum_mod_exp(session->next_crypto->e, g, session->next_crypto->x, - select_p(session->next_crypto->kex_type), ctx); -#endif - -#ifdef DEBUG_CRYPTO - ssh_print_bignum("e", session->next_crypto->e); -#endif - -#ifdef HAVE_LIBCRYPTO - bignum_ctx_free(ctx); -#endif - - return 0; -} - -int dh_generate_f(ssh_session session) { -#ifdef HAVE_LIBCRYPTO - bignum_CTX ctx = bignum_ctx_new(); - if (ctx == NULL) { - return -1; - } -#endif - - session->next_crypto->f = bignum_new(); - if (session->next_crypto->f == NULL) { -#ifdef HAVE_LIBCRYPTO - bignum_ctx_free(ctx); -#endif - return -1; - } - -#ifdef HAVE_LIBGCRYPT - bignum_mod_exp(session->next_crypto->f, g, session->next_crypto->y, - select_p(session->next_crypto->kex_type)); -#elif defined HAVE_LIBCRYPTO - bignum_mod_exp(session->next_crypto->f, g, session->next_crypto->y, - select_p(session->next_crypto->kex_type), ctx); -#endif - -#ifdef DEBUG_CRYPTO - ssh_print_bignum("f", session->next_crypto->f); -#endif - -#ifdef HAVE_LIBCRYPTO - bignum_ctx_free(ctx); -#endif - - return 0; -} - -ssh_string dh_get_e(ssh_session session) { - return make_bignum_string(session->next_crypto->e); -} - -/* used by server */ -ssh_string dh_get_f(ssh_session session) { - return make_bignum_string(session->next_crypto->f); -} - -void dh_import_pubkey(ssh_session session, ssh_string pubkey_string) { - session->next_crypto->server_pubkey = pubkey_string; -} - -int dh_import_f(ssh_session session, ssh_string f_string) { - session->next_crypto->f = make_string_bn(f_string); - if (session->next_crypto->f == NULL) { - return -1; - } - -#ifdef DEBUG_CRYPTO - ssh_print_bignum("f",session->next_crypto->f); -#endif - - return 0; -} - -/* used by the server implementation */ -int dh_import_e(ssh_session session, ssh_string e_string) { - session->next_crypto->e = make_string_bn(e_string); - if (session->next_crypto->e == NULL) { - return -1; - } - -#ifdef DEBUG_CRYPTO - ssh_print_bignum("e",session->next_crypto->e); -#endif - - return 0; -} - -int dh_build_k(ssh_session session) { -#ifdef HAVE_LIBCRYPTO - bignum_CTX ctx = bignum_ctx_new(); - if (ctx == NULL) { - return -1; - } -#endif - - session->next_crypto->k = bignum_new(); - if (session->next_crypto->k == NULL) { -#ifdef HAVE_LIBCRYPTO - bignum_ctx_free(ctx); -#endif - return -1; - } - - /* the server and clients don't use the same numbers */ -#ifdef HAVE_LIBGCRYPT - if(session->client) { - bignum_mod_exp(session->next_crypto->k, session->next_crypto->f, - session->next_crypto->x, select_p(session->next_crypto->kex_type)); - } else { - bignum_mod_exp(session->next_crypto->k, session->next_crypto->e, - session->next_crypto->y, select_p(session->next_crypto->kex_type)); - } -#elif defined HAVE_LIBCRYPTO - if (session->client) { - bignum_mod_exp(session->next_crypto->k, session->next_crypto->f, - session->next_crypto->x, select_p(session->next_crypto->kex_type), ctx); - } else { - bignum_mod_exp(session->next_crypto->k, session->next_crypto->e, - session->next_crypto->y, select_p(session->next_crypto->kex_type), ctx); - } -#endif - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Session server cookie", - session->next_crypto->server_kex.cookie, 16); - ssh_print_hexa("Session client cookie", - session->next_crypto->client_kex.cookie, 16); - ssh_print_bignum("Shared secret key", session->next_crypto->k); -#endif - -#ifdef HAVE_LIBCRYPTO - bignum_ctx_free(ctx); -#endif - - return 0; -} - -/** @internal - * @brief Starts diffie-hellman-group1 key exchange - */ -int ssh_client_dh_init(ssh_session session){ - ssh_string e = NULL; - int rc; - - if (dh_generate_x(session) < 0) { - goto error; - } - if (dh_generate_e(session) < 0) { - goto error; - } - - e = dh_get_e(session); - if (e == NULL) { - goto error; - } - - rc = ssh_buffer_pack(session->out_buffer, "bS", SSH2_MSG_KEXDH_INIT, e); - if (rc != SSH_OK) { - goto error; - } - - ssh_string_burn(e); - ssh_string_free(e); - e=NULL; - - rc = packet_send(session); - return rc; - error: - if(e != NULL){ - ssh_string_burn(e); - ssh_string_free(e); - } - - return SSH_ERROR; -} - -int ssh_client_dh_reply(ssh_session session, ssh_buffer packet){ - ssh_string f; - ssh_string pubkey = NULL; - ssh_string signature = NULL; - int rc; - pubkey = buffer_get_ssh_string(packet); - if (pubkey == NULL){ - ssh_set_error(session,SSH_FATAL, "No public key in packet"); - goto error; - } - dh_import_pubkey(session, pubkey); - - f = buffer_get_ssh_string(packet); - if (f == NULL) { - ssh_set_error(session,SSH_FATAL, "No F number in packet"); - goto error; - } - rc = dh_import_f(session, f); - ssh_string_burn(f); - ssh_string_free(f); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot import f number"); - goto error; - } - - signature = buffer_get_ssh_string(packet); - if (signature == NULL) { - ssh_set_error(session, SSH_FATAL, "No signature in packet"); - goto error; - } - session->next_crypto->dh_server_signature = signature; - signature=NULL; /* ownership changed */ - if (dh_build_k(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - goto error; - } - - /* Send the MSG_NEWKEYS */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; - } - - rc=packet_send(session); - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); - return rc; -error: - return SSH_ERROR; -} - -int make_sessionid(ssh_session session) { - ssh_string num = NULL; - ssh_buffer server_hash = NULL; - ssh_buffer client_hash = NULL; - ssh_buffer buf = NULL; - int rc = SSH_ERROR; - - buf = ssh_buffer_new(); - if (buf == NULL) { - return rc; - } - - rc = ssh_buffer_pack(buf, - "ss", - session->clientbanner, - session->serverbanner); - if (rc == SSH_ERROR) { - goto error; - } - - if (session->client) { - server_hash = session->in_hashbuf; - client_hash = session->out_hashbuf; - } else { - server_hash = session->out_hashbuf; - client_hash = session->in_hashbuf; - } - - /* - * Handle the two final fields for the KEXINIT message (RFC 4253 7.1): - * - * boolean first_kex_packet_follows - * uint32 0 (reserved for future extension) - */ - rc = buffer_add_u8(server_hash, 0); - if (rc < 0) { - goto error; - } - rc = buffer_add_u32(server_hash, 0); - if (rc < 0) { - goto error; - } - - /* These fields are handled for the server case in ssh_packet_kexinit. */ - if (session->client) { - rc = buffer_add_u8(client_hash, 0); - if (rc < 0) { - goto error; - } - rc = buffer_add_u32(client_hash, 0); - if (rc < 0) { - goto error; - } - } - - rc = ssh_buffer_pack(buf, - "dPdPS", - buffer_get_rest_len(client_hash), - buffer_get_rest_len(client_hash), - buffer_get_rest(client_hash), - buffer_get_rest_len(server_hash), - buffer_get_rest_len(server_hash), - buffer_get_rest(server_hash), - session->next_crypto->server_pubkey); - - if(rc != SSH_OK){ - goto error; - } - - if (session->next_crypto->kex_type == SSH_KEX_DH_GROUP1_SHA1 || - session->next_crypto->kex_type == SSH_KEX_DH_GROUP14_SHA1) { - rc = ssh_buffer_pack(buf, - "BB", - session->next_crypto->e, - session->next_crypto->f); - if (rc != SSH_OK) { - goto error; - } - -#ifdef HAVE_ECDH - } else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256) { - if (session->next_crypto->ecdh_client_pubkey == NULL || - session->next_crypto->ecdh_server_pubkey == NULL) { - SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing"); - goto error; - } - rc = ssh_buffer_pack(buf, - "SS", - session->next_crypto->ecdh_client_pubkey, - session->next_crypto->ecdh_server_pubkey); - if (rc != SSH_OK) { - goto error; - } -#endif -#ifdef HAVE_CURVE25519 - } else if (session->next_crypto->kex_type == SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG) { - rc = ssh_buffer_pack(buf, - "dPdP", - CURVE25519_PUBKEY_SIZE, - (size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_client_pubkey, - CURVE25519_PUBKEY_SIZE, - (size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_server_pubkey); - - if (rc != SSH_OK) { - goto error; - } -#endif - } - rc = ssh_buffer_pack(buf, "B", session->next_crypto->k); - if (rc != SSH_OK) { - goto error; - } - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("hash buffer", ssh_buffer_get_begin(buf), ssh_buffer_get_len(buf)); -#endif - - switch (session->next_crypto->kex_type) { - case SSH_KEX_DH_GROUP1_SHA1: - case SSH_KEX_DH_GROUP14_SHA1: - session->next_crypto->digest_len = SHA_DIGEST_LENGTH; - session->next_crypto->mac_type = SSH_MAC_SHA1; - session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); - if (session->next_crypto->secret_hash == NULL) { - ssh_set_error_oom(session); - goto error; - } - sha1(buffer_get_rest(buf), buffer_get_rest_len(buf), - session->next_crypto->secret_hash); - break; - case SSH_KEX_ECDH_SHA2_NISTP256: - case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: - session->next_crypto->digest_len = SHA256_DIGEST_LENGTH; - session->next_crypto->mac_type = SSH_MAC_SHA256; - session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); - if (session->next_crypto->secret_hash == NULL) { - ssh_set_error_oom(session); - goto error; - } - sha256(buffer_get_rest(buf), buffer_get_rest_len(buf), - session->next_crypto->secret_hash); - break; - } - /* During the first kex, secret hash and session ID are equal. However, after - * a key re-exchange, a new secret hash is calculated. This hash will not replace - * but complement existing session id. - */ - if (!session->next_crypto->session_id) { - session->next_crypto->session_id = malloc(session->next_crypto->digest_len); - if (session->next_crypto->session_id == NULL) { - ssh_set_error_oom(session); - goto error; - } - memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash, - session->next_crypto->digest_len); - } -#ifdef DEBUG_CRYPTO - printf("Session hash: \n"); - ssh_print_hexa("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len); - ssh_print_hexa("session id", session->next_crypto->session_id, session->next_crypto->digest_len); -#endif - - rc = SSH_OK; -error: - ssh_buffer_free(buf); - ssh_buffer_free(client_hash); - ssh_buffer_free(server_hash); - - session->in_hashbuf = NULL; - session->out_hashbuf = NULL; - - ssh_string_free(num); - - return rc; -} - -int hashbufout_add_cookie(ssh_session session) { - session->out_hashbuf = ssh_buffer_new(); - if (session->out_hashbuf == NULL) { - return -1; - } - - if (buffer_add_u8(session->out_hashbuf, 20) < 0) { - ssh_buffer_reinit(session->out_hashbuf); - return -1; - } - - if (session->server) { - if (ssh_buffer_add_data(session->out_hashbuf, - session->next_crypto->server_kex.cookie, 16) < 0) { - ssh_buffer_reinit(session->out_hashbuf); - return -1; - } - } else { - if (ssh_buffer_add_data(session->out_hashbuf, - session->next_crypto->client_kex.cookie, 16) < 0) { - ssh_buffer_reinit(session->out_hashbuf); - return -1; - } - } - - return 0; -} - -int hashbufin_add_cookie(ssh_session session, unsigned char *cookie) { - session->in_hashbuf = ssh_buffer_new(); - if (session->in_hashbuf == NULL) { - return -1; - } - - if (buffer_add_u8(session->in_hashbuf, 20) < 0) { - ssh_buffer_reinit(session->in_hashbuf); - return -1; - } - if (ssh_buffer_add_data(session->in_hashbuf,cookie, 16) < 0) { - ssh_buffer_reinit(session->in_hashbuf); - return -1; - } - - return 0; -} - -static int generate_one_key(ssh_string k, - struct ssh_crypto_struct *crypto, unsigned char **output, char letter, size_t requested_size) { - ssh_mac_ctx ctx; - unsigned char *tmp; - size_t size = crypto->digest_len; - ctx=ssh_mac_ctx_init(crypto->mac_type); - - if (ctx == NULL) { - return -1; - } - - ssh_mac_update(ctx, k, ssh_string_len(k) + 4); - ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); - ssh_mac_update(ctx, &letter, 1); - ssh_mac_update(ctx, crypto->session_id, crypto->digest_len); - ssh_mac_final(*output, ctx); - - while(requested_size > size) { - tmp = realloc(*output, size + crypto->digest_len); - if (tmp == NULL) { - return -1; - } - *output = tmp; - - ctx = ssh_mac_ctx_init(crypto->mac_type); - if (ctx == NULL) { - return -1; - } - ssh_mac_update(ctx, k, ssh_string_len(k) + 4); - ssh_mac_update(ctx, crypto->secret_hash, - crypto->digest_len); - ssh_mac_update(ctx, tmp, size); - ssh_mac_final(tmp + size, ctx); - size += crypto->digest_len; - } - - return 0; -} - -int generate_session_keys(ssh_session session) { - ssh_string k_string = NULL; - struct ssh_crypto_struct *crypto = session->next_crypto; - int rc = -1; - - k_string = make_bignum_string(crypto->k); - if (k_string == NULL) { - ssh_set_error_oom(session); - goto error; - } - - crypto->encryptIV = malloc(crypto->digest_len); - crypto->decryptIV = malloc(crypto->digest_len); - crypto->encryptkey = malloc(crypto->digest_len); - crypto->decryptkey = malloc(crypto->digest_len); - crypto->encryptMAC = malloc(crypto->digest_len); - crypto->decryptMAC = malloc(crypto->digest_len); - if(crypto->encryptIV == NULL || crypto->decryptIV == NULL || - crypto->encryptkey == NULL || crypto->decryptkey == NULL || - crypto->encryptMAC == NULL || crypto->decryptMAC == NULL){ - ssh_set_error_oom(session); - goto error; - } - - /* IV */ - if (session->client) { - rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'A', crypto->digest_len); - if (rc < 0) { - goto error; - } - rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'B', crypto->digest_len); - if (rc < 0) { - goto error; - } - } else { - rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'A', crypto->digest_len); - if (rc < 0) { - goto error; - } - rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'B', crypto->digest_len); - if (rc < 0) { - goto error; - } - } - if (session->client) { - rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'C', crypto->out_cipher->keysize / 8); - if (rc < 0) { - goto error; - } - rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'D', crypto->in_cipher->keysize / 8); - if (rc < 0) { - goto error; - } - } else { - rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'C', crypto->in_cipher->keysize / 8); - if (rc < 0) { - goto error; - } - rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'D', crypto->out_cipher->keysize / 8); - if (rc < 0) { - goto error; - } - } - - if(session->client) { - rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'E', hmac_digest_len(crypto->out_hmac)); - if (rc < 0) { - goto error; - } - rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'F', hmac_digest_len(crypto->in_hmac)); - if (rc < 0) { - goto error; - } - } else { - rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'E', hmac_digest_len(crypto->in_hmac)); - if (rc < 0) { - goto error; - } - rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'F', hmac_digest_len(crypto->out_hmac)); - if (rc < 0) { - goto error; - } - } - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Encrypt IV", crypto->encryptIV, crypto->digest_len); - ssh_print_hexa("Decrypt IV", crypto->decryptIV, crypto->digest_len); - ssh_print_hexa("Encryption key", crypto->encryptkey, crypto->out_cipher->keysize / 8); - ssh_print_hexa("Decryption key", crypto->decryptkey, crypto->in_cipher->keysize / 8); - ssh_print_hexa("Encryption MAC", crypto->encryptMAC, hmac_digest_len(crypto->out_hmac)); - ssh_print_hexa("Decryption MAC", crypto->decryptMAC, hmac_digest_len(crypto->in_hmac)); -#endif - - rc = 0; -error: - ssh_string_free(k_string); - - return rc; -} - -/** - * @addtogroup libssh_session - * - * @{ - */ - -/** - * @deprecated Use ssh_get_publickey_hash() - */ -int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash) { - ssh_string pubkey; - MD5CTX ctx; - unsigned char *h; - - if (session == NULL || hash == NULL) { - return SSH_ERROR; - } - *hash = NULL; - if (session->current_crypto == NULL || - session->current_crypto->server_pubkey == NULL){ - ssh_set_error(session,SSH_FATAL,"No current cryptographic context"); - return SSH_ERROR; - } - - h = malloc(sizeof(unsigned char) * MD5_DIGEST_LEN); - if (h == NULL) { - return SSH_ERROR; - } - - ctx = md5_init(); - if (ctx == NULL) { - SAFE_FREE(h); - return SSH_ERROR; - } - - pubkey = session->current_crypto->server_pubkey; - - md5_update(ctx, ssh_string_data(pubkey), ssh_string_len(pubkey)); - md5_final(h, ctx); - - *hash = h; - - return MD5_DIGEST_LEN; -} - -/** - * @brief Deallocate the hash obtained by ssh_get_pubkey_hash. - * - * This is required under Microsoft platform as this library might use a - * different C library than your software, hence a different heap. - * - * @param[in] hash The buffer to deallocate. - * - * @see ssh_get_pubkey_hash() - */ -void ssh_clean_pubkey_hash(unsigned char **hash) { - SAFE_FREE(*hash); - *hash = NULL; -} - -/** - * @brief Get the server public key from a session. - * - * @param[in] session The session to get the key from. - * - * @param[out] key A pointer to store the allocated key. You need to free - * the key. - * - * @return SSH_OK on success, SSH_ERROR on errror. - * - * @see ssh_key_free() - */ -int ssh_get_publickey(ssh_session session, ssh_key *key) -{ - if (session==NULL || - session->current_crypto ==NULL || - session->current_crypto->server_pubkey == NULL) { - return SSH_ERROR; - } - - return ssh_pki_import_pubkey_blob(session->current_crypto->server_pubkey, - key); -} - -/** - * @brief Allocates a buffer with the hash of the public key. - * - * This function allows you to get a hash of the public key. You can then - * print this hash in a human-readable form to the user so that he is able to - * verify it. Use ssh_get_hexa() or ssh_print_hexa() to display it. - * - * @param[in] key The public key to create the hash for. - * - * @param[in] type The type of the hash you want. - * - * @param[in] hash A pointer to store the allocated buffer. It can be - * freed using ssh_clean_pubkey_hash(). - * - * @param[in] hlen The length of the hash. - * - * @return 0 on success, -1 if an error occured. - * - * @warning It is very important that you verify at some moment that the hash - * matches a known server. If you don't do it, cryptography wont help - * you at making things secure. - * OpenSSH uses SHA1 to print public key digests. - * - * @see ssh_is_server_known() - * @see ssh_get_hexa() - * @see ssh_print_hexa() - * @see ssh_clean_pubkey_hash() - */ -int ssh_get_publickey_hash(const ssh_key key, - enum ssh_publickey_hash_type type, - unsigned char **hash, - size_t *hlen) -{ - ssh_string blob; - unsigned char *h; - int rc; - - rc = ssh_pki_export_pubkey_blob(key, &blob); - if (rc < 0) { - return rc; - } - - switch (type) { - case SSH_PUBLICKEY_HASH_SHA1: - { - SHACTX ctx; - - h = malloc(SHA_DIGEST_LEN); - if (h == NULL) { - rc = -1; - goto out; - } - - ctx = sha1_init(); - if (ctx == NULL) { - free(h); - rc = -1; - goto out; - } - - sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); - sha1_final(h, ctx); - - *hlen = SHA_DIGEST_LEN; - } - break; - case SSH_PUBLICKEY_HASH_MD5: - { - MD5CTX ctx; - - h = malloc(MD5_DIGEST_LEN); - if (h == NULL) { - rc = -1; - goto out; - } - - ctx = md5_init(); - if (ctx == NULL) { - free(h); - rc = -1; - goto out; - } - - md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); - md5_final(h, ctx); - - *hlen = MD5_DIGEST_LEN; - } - break; - default: - rc = -1; - goto out; - } - - *hash = h; - rc = 0; -out: - ssh_string_free(blob); - return rc; -} - -/** - * @brief Convert a buffer into a colon separated hex string. - * The caller has to free the memory. - * - * @param what What should be converted to a hex string. - * - * @param len Length of the buffer to convert. - * - * @return The hex string or NULL on error. - * - * @see ssh_string_free_char() - */ -char *ssh_get_hexa(const unsigned char *what, size_t len) { - const char h[] = "0123456789abcdef"; - char *hexa; - size_t i; - size_t hlen = len * 3; - - if (len > (UINT_MAX - 1) / 3) { - return NULL; - } - - hexa = malloc(hlen + 1); - if (hexa == NULL) { - return NULL; - } - - for (i = 0; i < len; i++) { - hexa[i * 3] = h[(what[i] >> 4) & 0xF]; - hexa[i * 3 + 1] = h[what[i] & 0xF]; - hexa[i * 3 + 2] = ':'; - } - hexa[hlen - 1] = '\0'; - - return hexa; -} - -/** - * @brief Print a buffer as colon separated hex string. - * - * @param descr Description printed in front of the hex string. - * - * @param what What should be converted to a hex string. - * - * @param len Length of the buffer to convert. - */ -void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) { - char *hexa = ssh_get_hexa(what, len); - - if (hexa == NULL) { - return; - } - printf("%s: %s\n", descr, hexa); - - free(hexa); -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/ecdh.c b/libssh/src/ecdh.c deleted file mode 100644 index e6310b4c..00000000 --- a/libssh/src/ecdh.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2011-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" -#include "libssh/session.h" -#include "libssh/ecdh.h" -#include "libssh/dh.h" -#include "libssh/buffer.h" -#include "libssh/ssh2.h" -#include "libssh/pki.h" -#include "libssh/bignum.h" - -#ifdef HAVE_ECDH -#include - -#define NISTP256 NID_X9_62_prime256v1 -#define NISTP384 NID_secp384r1 -#define NISTP521 NID_secp521r1 - -/** @internal - * @brief Starts ecdh-sha2-nistp256 key exchange - */ -int ssh_client_ecdh_init(ssh_session session){ - EC_KEY *key; - const EC_GROUP *group; - const EC_POINT *pubkey; - ssh_string client_pubkey; - int len; - int rc; - bignum_CTX ctx = BN_CTX_new(); - - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT); - if (rc < 0) { - BN_CTX_free(ctx); - return SSH_ERROR; - } - - key = EC_KEY_new_by_curve_name(NISTP256); - if (key == NULL) { - BN_CTX_free(ctx); - return SSH_ERROR; - } - group = EC_KEY_get0_group(key); - - EC_KEY_generate_key(key); - - pubkey=EC_KEY_get0_public_key(key); - len = EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED, - NULL,0,ctx); - - client_pubkey = ssh_string_new(len); - if (client_pubkey == NULL) { - BN_CTX_free(ctx); - EC_KEY_free(key); - return SSH_ERROR; - } - - EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED, - ssh_string_data(client_pubkey),len,ctx); - BN_CTX_free(ctx); - - rc = buffer_add_ssh_string(session->out_buffer,client_pubkey); - if (rc < 0) { - EC_KEY_free(key); - ssh_string_free(client_pubkey); - return SSH_ERROR; - } - - session->next_crypto->ecdh_privkey = key; - session->next_crypto->ecdh_client_pubkey = client_pubkey; - - rc = packet_send(session); - - return rc; -} - -static void ecdh_import_pubkey(ssh_session session, ssh_string pubkey_string) { - session->next_crypto->server_pubkey = pubkey_string; -} - -static int ecdh_build_k(ssh_session session) { - const EC_GROUP *group = EC_KEY_get0_group(session->next_crypto->ecdh_privkey); - EC_POINT *pubkey; - void *buffer; - int rc; - int len = (EC_GROUP_get_degree(group) + 7) / 8; - bignum_CTX ctx = bignum_ctx_new(); - if (ctx == NULL) { - return -1; - } - - session->next_crypto->k = bignum_new(); - if (session->next_crypto->k == NULL) { - bignum_ctx_free(ctx); - return -1; - } - - pubkey = EC_POINT_new(group); - if (pubkey == NULL) { - bignum_ctx_free(ctx); - return -1; - } - - if (session->server) { - rc = EC_POINT_oct2point(group, - pubkey, - ssh_string_data(session->next_crypto->ecdh_client_pubkey), - ssh_string_len(session->next_crypto->ecdh_client_pubkey), - ctx); - } else { - rc = EC_POINT_oct2point(group, - pubkey, - ssh_string_data(session->next_crypto->ecdh_server_pubkey), - ssh_string_len(session->next_crypto->ecdh_server_pubkey), - ctx); - } - bignum_ctx_free(ctx); - if (rc <= 0) { - EC_POINT_clear_free(pubkey); - return -1; - } - - buffer = malloc(len); - if (buffer == NULL) { - EC_POINT_clear_free(pubkey); - return -1; - } - - rc = ECDH_compute_key(buffer, - len, - pubkey, - session->next_crypto->ecdh_privkey, - NULL); - EC_POINT_clear_free(pubkey); - if (rc <= 0) { - free(buffer); - return -1; - } - - bignum_bin2bn(buffer, len, session->next_crypto->k); - free(buffer); - - EC_KEY_free(session->next_crypto->ecdh_privkey); - session->next_crypto->ecdh_privkey = NULL; - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Session server cookie", - session->next_crypto->server_kex.cookie, 16); - ssh_print_hexa("Session client cookie", - session->next_crypto->client_kex.cookie, 16); - ssh_print_bignum("Shared secret key", session->next_crypto->k); -#endif - - return 0; -} - -/** @internal - * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back - * a SSH_MSG_NEWKEYS - */ -int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet){ - ssh_string q_s_string = NULL; - ssh_string pubkey = NULL; - ssh_string signature = NULL; - int rc; - pubkey = buffer_get_ssh_string(packet); - if (pubkey == NULL){ - ssh_set_error(session,SSH_FATAL, "No public key in packet"); - goto error; - } - ecdh_import_pubkey(session, pubkey); - - q_s_string = buffer_get_ssh_string(packet); - if (q_s_string == NULL) { - ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet"); - goto error; - } - session->next_crypto->ecdh_server_pubkey = q_s_string; - signature = buffer_get_ssh_string(packet); - if (signature == NULL) { - ssh_set_error(session, SSH_FATAL, "No signature in packet"); - goto error; - } - session->next_crypto->dh_server_signature = signature; - signature=NULL; /* ownership changed */ - /* TODO: verify signature now instead of waiting for NEWKEYS */ - if (ecdh_build_k(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - goto error; - } - - /* Send the MSG_NEWKEYS */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; - } - - rc=packet_send(session); - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); - return rc; -error: - return SSH_ERROR; -} - -#ifdef WITH_SERVER - -/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a - * SSH_MSG_KEXDH_REPLY - */ - -int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){ - /* ECDH keys */ - ssh_string q_c_string; - ssh_string q_s_string; - EC_KEY *ecdh_key; - const EC_GROUP *group; - const EC_POINT *ecdh_pubkey; - bignum_CTX ctx; - /* SSH host keys (rsa,dsa,ecdsa) */ - ssh_key privkey; - ssh_string sig_blob = NULL; - int len; - int rc; - - /* Extract the client pubkey from the init packet */ - q_c_string = buffer_get_ssh_string(packet); - if (q_c_string == NULL) { - ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); - return SSH_ERROR; - } - session->next_crypto->ecdh_client_pubkey = q_c_string; - - /* Build server's keypair */ - - ctx = BN_CTX_new(); - ecdh_key = EC_KEY_new_by_curve_name(NISTP256); - if (ecdh_key == NULL) { - ssh_set_error_oom(session); - BN_CTX_free(ctx); - return SSH_ERROR; - } - - group = EC_KEY_get0_group(ecdh_key); - EC_KEY_generate_key(ecdh_key); - - ecdh_pubkey = EC_KEY_get0_public_key(ecdh_key); - len = EC_POINT_point2oct(group, - ecdh_pubkey, - POINT_CONVERSION_UNCOMPRESSED, - NULL, - 0, - ctx); - - q_s_string = ssh_string_new(len); - if (q_s_string == NULL) { - EC_KEY_free(ecdh_key); - BN_CTX_free(ctx); - return SSH_ERROR; - } - - EC_POINT_point2oct(group, - ecdh_pubkey, - POINT_CONVERSION_UNCOMPRESSED, - ssh_string_data(q_s_string), - len, - ctx); - BN_CTX_free(ctx); - - session->next_crypto->ecdh_privkey = ecdh_key; - session->next_crypto->ecdh_server_pubkey = q_s_string; - - /* build k and session_id */ - rc = ecdh_build_k(session); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - return SSH_ERROR; - } - - /* privkey is not allocated */ - rc = ssh_get_key_params(session, &privkey); - if (rc == SSH_ERROR) { - return SSH_ERROR; - } - - rc = make_sessionid(session); - if (rc != SSH_OK) { - ssh_set_error(session, SSH_FATAL, "Could not create a session id"); - return SSH_ERROR; - } - - sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); - if (sig_blob == NULL) { - ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); - return SSH_ERROR; - } - - rc = ssh_buffer_pack(session->out_buffer, - "bSSS", - SSH2_MSG_KEXDH_REPLY, - session->next_crypto->server_pubkey, /* host's pubkey */ - q_s_string, /* ecdh public key */ - sig_blob); /* signature blob */ - - ssh_string_free(sig_blob); - - if (rc != SSH_OK) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent"); - rc = packet_send(session); - if (rc == SSH_ERROR) { - return SSH_ERROR; - } - - /* Send the MSG_NEWKEYS */ - rc = buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); - if (rc < 0) { - return SSH_ERROR;; - } - - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - rc = packet_send(session); - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); - - return rc; -} - -#endif /* WITH_SERVER */ - -#endif /* HAVE_ECDH */ diff --git a/libssh/src/ed25519.c b/libssh/src/ed25519.c deleted file mode 100644 index 2ae0ef4e..00000000 --- a/libssh/src/ed25519.c +++ /dev/null @@ -1,222 +0,0 @@ -/* $OpenBSD: ed25519.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ - -/* - * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, - * Peter Schwabe, Bo-Yin Yang. - * Copied from supercop-20130419/crypto_sign/ed25519/ref/ed25519.c - */ - -#include "config.h" - -#include "libssh/libcrypto.h" -#include "libssh/wrapper.h" -#include "libssh/ge25519.h" -#include "libssh/sc25519.h" -#include "libssh/ed25519.h" - -/* - * Public Domain, Author: Daniel J. Bernstein - * Copied from nacl-20110221/crypto_verify/32/ref/verify.c - */ - -static int crypto_verify_32(const unsigned char *x,const unsigned char *y) -{ - unsigned int differentbits = 0; -#define F(i) differentbits |= x[i] ^ y[i]; - F(0) - F(1) - F(2) - F(3) - F(4) - F(5) - F(6) - F(7) - F(8) - F(9) - F(10) - F(11) - F(12) - F(13) - F(14) - F(15) - F(16) - F(17) - F(18) - F(19) - F(20) - F(21) - F(22) - F(23) - F(24) - F(25) - F(26) - F(27) - F(28) - F(29) - F(30) - F(31) - - return (1 & ((differentbits - 1) >> 8)) - 1; -} - -static void get_hram(unsigned char *hram, - const unsigned char *sm, - const unsigned char *pk, - unsigned char *playground, - unsigned long long smlen) -{ - unsigned long long i; - SHA512CTX ctx; - for (i = 0;i < 32;++i) playground[i] = sm[i]; - for (i = 32;i < 64;++i) playground[i] = pk[i-32]; - for (i = 64;i < smlen;++i) playground[i] = sm[i]; - - ctx = sha512_init(); - sha512_update(ctx, playground, smlen); - sha512_final(hram, ctx); -} - - -int crypto_sign_ed25519_keypair(unsigned char *pk, - unsigned char *sk) -{ - sc25519 scsk; - ge25519 gepk; - SHA512CTX ctx; - unsigned char extsk[64]; - int i; - int rc; - - rc = ssh_get_random(sk, 32, 0); - if (rc < 0){ - return -1; - } - - ctx = sha512_init(); - sha512_update(ctx, sk, 32); - sha512_final(extsk, ctx); - extsk[0] &= 248; - extsk[31] &= 127; - extsk[31] |= 64; - - sc25519_from32bytes(&scsk,extsk); - - ge25519_scalarmult_base(&gepk, &scsk); - ge25519_pack(pk, &gepk); - for(i=0;i<32;i++) { - sk[32 + i] = pk[i]; - } - - return 0; -} - -int crypto_sign_ed25519(unsigned char *sm, - unsigned long long *smlen, - const unsigned char *m, - unsigned long long mlen, - const unsigned char *sk) -{ - sc25519 sck, scs, scsk; - ge25519 ger; - SHA512CTX ctx; - unsigned char r[32]; - unsigned char s[32]; - unsigned char extsk[64]; - unsigned long long i; - unsigned char hmg[SHA512_DIGEST_LEN]; - unsigned char hram[SHA512_DIGEST_LEN]; - - ctx = sha512_init(); - sha512_update(ctx, sk, 32); - sha512_final(extsk, ctx); - - extsk[0] &= 248; - extsk[31] &= 127; - extsk[31] |= 64; - - *smlen = mlen + 64; - for (i = 0;i < mlen; i++) { - sm[64 + i] = m[i]; - } - for (i = 0;i < 32; i++) { - sm[32 + i] = extsk[32+i]; - } - - /* Generate k as h(extsk[32],...,extsk[63],m) */ - ctx = sha512_init(); - sha512_update(ctx, sm + 32, mlen + 32); - sha512_final(hmg, ctx); - - /* Computation of R */ - sc25519_from64bytes(&sck, hmg); - ge25519_scalarmult_base(&ger, &sck); - ge25519_pack(r, &ger); - - /* Computation of s */ - for (i = 0; i < 32; i++) { - sm[i] = r[i]; - } - - get_hram(hram, sm, sk+32, sm, mlen+64); - - sc25519_from64bytes(&scs, hram); - sc25519_from32bytes(&scsk, extsk); - sc25519_mul(&scs, &scs, &scsk); - - sc25519_add(&scs, &scs, &sck); - - sc25519_to32bytes(s,&scs); /* cat s */ - for (i = 0;i < 32; i++) { - sm[32 + i] = s[i]; - } - - return 0; -} - -int crypto_sign_ed25519_open(unsigned char *m, - unsigned long long *mlen, - const unsigned char *sm, - unsigned long long smlen, - const unsigned char *pk) -{ - unsigned int i; - int ret; - unsigned char t2[32]; - ge25519 get1, get2; - sc25519 schram, scs; - unsigned char hram[SHA512_DIGEST_LEN]; - - *mlen = (unsigned long long) -1; - if (smlen < 64) return -1; - - if (ge25519_unpackneg_vartime(&get1, pk)) { - return -1; - } - - get_hram(hram,sm,pk,m,smlen); - - sc25519_from64bytes(&schram, hram); - - sc25519_from32bytes(&scs, sm+32); - - ge25519_double_scalarmult_vartime(&get2, - &get1, - &schram, - &ge25519_base, - &scs); - ge25519_pack(t2, &get2); - - ret = crypto_verify_32(sm, t2); - if (ret != 0) { - for (i = 0; i < smlen - 64; i++) { - m[i] = sm[i + 64]; - } - *mlen = smlen-64; - } else { - for (i = 0; i < smlen - 64; i++) { - m[i] = 0; - } - } - - return ret; -} diff --git a/libssh/src/error.c b/libssh/src/error.c deleted file mode 100644 index bd755c4f..00000000 --- a/libssh/src/error.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * error.c - functions for ssh error handling - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include "libssh/priv.h" -#include "libssh/session.h" - -/** - * @defgroup libssh_error The SSH error functions. - * @ingroup libssh - * - * Functions for error handling. - * - * @{ - */ - -/** - * @internal - * - * @brief Registers an error with a description. - * - * @param error The place to store the error. - * - * @param code The class of error. - * - * @param descr The description, which can be a format string. - * - * @param ... The arguments for the format string. - */ -void _ssh_set_error(void *error, - int code, - const char *function, - const char *descr, ...) -{ - struct ssh_common_struct *err = error; - va_list va; - - va_start(va, descr); - vsnprintf(err->error.error_buffer, ERROR_BUFFERLEN, descr, va); - va_end(va); - - err->error.error_code = code; - if (ssh_get_log_level() >= SSH_LOG_WARN) { - ssh_log_function(SSH_LOG_WARN, - function, - err->error.error_buffer); - } -} - -/** - * @internal - * - * @brief Registers an out of memory error - * - * @param error The place to store the error. - * - */ -void _ssh_set_error_oom(void *error, const char *function) -{ - struct error_struct *err = error; - - snprintf(err->error_buffer, sizeof(err->error_buffer), - "%s: Out of memory", function); - err->error_code = SSH_FATAL; -} - -/** - * @internal - * - * @brief Registers an invalid argument error - * - * @param error The place to store the error. - * - * @param function The function the error happened in. - * - */ -void _ssh_set_error_invalid(void *error, const char *function) -{ - _ssh_set_error(error, SSH_FATAL, function, - "Invalid argument in %s", function); -} - -/** - * @brief Retrieve the error text message from the last error. - * - * @param error An ssh_session or ssh_bind. - * - * @return A static string describing the error. - */ -const char *ssh_get_error(void *error) { - struct error_struct *err = error; - - return err->error_buffer; -} - -/** - * @brief Retrieve the error code from the last error. - * - * @param error An ssh_session or ssh_bind. - * - * \return SSH_NO_ERROR No error occurred\n - * SSH_REQUEST_DENIED The last request was denied but situation is - * recoverable\n - * SSH_FATAL A fatal error occurred. This could be an unexpected - * disconnection\n - * - * Other error codes are internal but can be considered same than - * SSH_FATAL. - */ -int ssh_get_error_code(void *error) { - struct error_struct *err = error; - - return err->error_code; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/fe25519.c b/libssh/src/fe25519.c deleted file mode 100644 index 0cedd89d..00000000 --- a/libssh/src/fe25519.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, - * Peter Schwabe, Bo-Yin Yang. - * Copied from supercop-20130419/crypto_sign/ed25519/ref/fe25519.c - */ - -#define WINDOWSIZE 1 /* Should be 1,2, or 4 */ -#define WINDOWMASK ((1<>= 31; /* 1: yes; 0: no */ - return x; -} - -static uint32_t ge(uint32_t a,uint32_t b) /* 16-bit inputs */ -{ - unsigned int x = a; - - x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */ - x >>= 31; /* 0: yes; 1: no */ - x ^= 1; /* 1: yes; 0: no */ - - return x; -} - -static uint32_t times19(uint32_t a) -{ - return (a << 4) + (a << 1) + a; -} - -static uint32_t times38(uint32_t a) -{ - return (a << 5) + (a << 2) + (a << 1); -} - -static void reduce_add_sub(fe25519 *r) -{ - uint32_t t; - int i,rep; - - for(rep = 0; rep < 4; rep++) { - t = r->v[31] >> 7; - r->v[31] &= 127; - t = times19(t); - r->v[0] += t; - for(i = 0; i < 31; i++) { - t = r->v[i] >> 8; - r->v[i+1] += t; - r->v[i] &= 255; - } - } -} - -static void reduce_mul(fe25519 *r) -{ - uint32_t t; - int i,rep; - - for(rep = 0; rep < 2; rep++) { - t = r->v[31] >> 7; - r->v[31] &= 127; - t = times19(t); - r->v[0] += t; - for(i = 0; i < 31; i++) { - t = r->v[i] >> 8; - r->v[i+1] += t; - r->v[i] &= 255; - } - } -} - -/* reduction modulo 2^255-19 */ -void fe25519_freeze(fe25519 *r) -{ - int i; - uint32_t m = equal(r->v[31],127); - - for (i = 30; i > 0; i--) { - m &= equal(r->v[i],255); - } - m &= ge(r->v[0],237); - - m = -m; - - r->v[31] -= m&127; - for (i = 30; i > 0; i--) { - r->v[i] -= m&255; - } - r->v[0] -= m&237; -} - -void fe25519_unpack(fe25519 *r, const unsigned char x[32]) -{ - int i; - - for (i = 0;i < 32; i++) { - r->v[i] = x[i]; - } - - r->v[31] &= 127; -} - -/* Assumes input x being reduced below 2^255 */ -void fe25519_pack(unsigned char r[32], const fe25519 *x) -{ - int i; - - fe25519 y = *x; - fe25519_freeze(&y); - - for (i = 0; i < 32; i++) { - r[i] = y.v[i]; - } -} - -int fe25519_iszero(const fe25519 *x) -{ - int i; - int r; - - fe25519 t = *x; - fe25519_freeze(&t); - - r = equal(t.v[0],0); - for (i = 1; i < 32; i++) { - r &= equal(t.v[i],0); - } - - return r; -} - -int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y) -{ - int i; - - fe25519 t1 = *x; - fe25519 t2 = *y; - fe25519_freeze(&t1); - fe25519_freeze(&t2); - - for (i = 0; i < 32; i++) { - if(t1.v[i] != t2.v[i]) { - return 0; - } - } - - return 1; -} - -void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b) -{ - int i; - uint32_t mask = b; - - mask = -mask; - - for (i = 0; i < 32; i++) { - r->v[i] ^= mask & (x->v[i] ^ r->v[i]); - } -} - -unsigned char fe25519_getparity(const fe25519 *x) -{ - fe25519 t = *x; - fe25519_freeze(&t); - - return t.v[0] & 1; -} - -void fe25519_setone(fe25519 *r) -{ - int i; - - r->v[0] = 1; - for (i = 1; i < 32; i++) { - r->v[i]=0; - } -} - -void fe25519_setzero(fe25519 *r) -{ - int i; - - for (i = 0; i < 32; i++) { - r->v[i]=0; - } -} - -void fe25519_neg(fe25519 *r, const fe25519 *x) -{ - fe25519 t; - int i; - - for (i = 0; i < 32; i++) { - t.v[i]=x->v[i]; - } - - fe25519_setzero(r); - fe25519_sub(r, r, &t); -} - -void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y) -{ - int i; - - for (i = 0; i < 32; i++) { - r->v[i] = x->v[i] + y->v[i]; - } - - reduce_add_sub(r); -} - -void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y) -{ - int i; - uint32_t t[32]; - - t[0] = x->v[0] + 0x1da; - t[31] = x->v[31] + 0xfe; - - for (i = 1; i < 31; i++) { - t[i] = x->v[i] + 0x1fe; - } - - for (i = 0; i < 32; i++) { - r->v[i] = t[i] - y->v[i]; - } - - reduce_add_sub(r); -} - -void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y) -{ - int i,j; - uint32_t t[63]; - - for (i = 0; i < 63; i++) { - t[i] = 0; - } - - for (i = 0; i < 32; i++) { - for (j = 0; j < 32; j++) { - t[i+j] += x->v[i] * y->v[j]; - } - } - - for (i = 32; i < 63; i++) { - r->v[i-32] = t[i-32] + times38(t[i]); - } - r->v[31] = t[31]; /* result now in r[0]...r[31] */ - - reduce_mul(r); -} - -void fe25519_square(fe25519 *r, const fe25519 *x) -{ - fe25519_mul(r, x, x); -} - -void fe25519_invert(fe25519 *r, const fe25519 *x) -{ - fe25519 z2; - fe25519 z9; - fe25519 z11; - fe25519 z2_5_0; - fe25519 z2_10_0; - fe25519 z2_20_0; - fe25519 z2_50_0; - fe25519 z2_100_0; - fe25519 t0; - fe25519 t1; - int i; - - /* 2 */ fe25519_square(&z2, x); - /* 4 */ fe25519_square(&t1, &z2); - /* 8 */ fe25519_square(&t0, &t1); - /* 9 */ fe25519_mul(&z9, &t0, x); - /* 11 */ fe25519_mul(&z11, &z9, &z2); - /* 22 */ fe25519_square(&t0, &z11); - /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0, &t0, &z9); - - /* 2^6 - 2^1 */ fe25519_square(&t0, &z2_5_0); - /* 2^7 - 2^2 */ fe25519_square(&t1, &t0); - /* 2^8 - 2^3 */ fe25519_square(&t0, &t1); - /* 2^9 - 2^4 */ fe25519_square(&t1, &t0); - /* 2^10 - 2^5 */ fe25519_square(&t0, &t1); - /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0, &t0, &z2_5_0); - - /* 2^11 - 2^1 */ fe25519_square(&t0, &z2_10_0); - /* 2^12 - 2^2 */ fe25519_square(&t1, &t0); - /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { - fe25519_square(&t0, &t1); - fe25519_square(&t1, &t0); - } - /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0, &t1, &z2_10_0); - - /* 2^21 - 2^1 */ fe25519_square(&t0, &z2_20_0); - /* 2^22 - 2^2 */ fe25519_square(&t1, &t0); - /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { - fe25519_square(&t0, &t1); - fe25519_square(&t1,&t0); - } - /* 2^40 - 2^0 */ fe25519_mul(&t0, &t1, &z2_20_0); - - /* 2^41 - 2^1 */ fe25519_square(&t1, &t0); - /* 2^42 - 2^2 */ fe25519_square(&t0, &t1); - /* 2^50 - 2^10 */ for (i = 2; i < 10;i += 2) { - fe25519_square(&t1, &t0); - fe25519_square(&t0, &t1); - } - /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0); - - /* 2^51 - 2^1 */ fe25519_square(&t0, &z2_50_0); - /* 2^52 - 2^2 */ fe25519_square(&t1, &t0); - /* 2^100 - 2^50 */ for (i = 2; i < 50; i += 2) { - fe25519_square(&t0, &t1); - fe25519_square(&t1,&t0); - } - /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0, &t1, &z2_50_0); - - /* 2^101 - 2^1 */ fe25519_square(&t1, &z2_100_0); - /* 2^102 - 2^2 */ fe25519_square(&t0, &t1); - /* 2^200 - 2^100 */ for (i = 2; i < 100; i += 2) { - fe25519_square(&t1, &t0); - fe25519_square(&t0,&t1); - } - /* 2^200 - 2^0 */ fe25519_mul(&t1, &t0, &z2_100_0); - - /* 2^201 - 2^1 */ fe25519_square(&t0, &t1); - /* 2^202 - 2^2 */ fe25519_square(&t1, &t0); - /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { - fe25519_square(&t0, &t1); - fe25519_square(&t1,&t0); - } - /* 2^250 - 2^0 */ fe25519_mul(&t0, &t1, &z2_50_0); - - /* 2^251 - 2^1 */ fe25519_square(&t1, &t0); - /* 2^252 - 2^2 */ fe25519_square(&t0, &t1); - /* 2^253 - 2^3 */ fe25519_square(&t1, &t0); - /* 2^254 - 2^4 */ fe25519_square(&t0, &t1); - /* 2^255 - 2^5 */ fe25519_square(&t1, &t0); - /* 2^255 - 21 */ fe25519_mul(r, &t1, &z11); -} - -void fe25519_pow2523(fe25519 *r, const fe25519 *x) -{ - fe25519 z2; - fe25519 z9; - fe25519 z11; - fe25519 z2_5_0; - fe25519 z2_10_0; - fe25519 z2_20_0; - fe25519 z2_50_0; - fe25519 z2_100_0; - fe25519 t; - int i; - - /* 2 */ fe25519_square(&z2, x); - /* 4 */ fe25519_square(&t, &z2); - /* 8 */ fe25519_square(&t, &t); - /* 9 */ fe25519_mul(&z9, &t, x); - /* 11 */ fe25519_mul(&z11, &z9, &z2); - /* 22 */ fe25519_square(&t, &z11); - /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0, &t, &z9); - - /* 2^6 - 2^1 */ fe25519_square(&t, &z2_5_0); - /* 2^10 - 2^5 */ for (i = 1; i < 5; i++) { - fe25519_square(&t,&t); - } - /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0, &t, &z2_5_0); - - /* 2^11 - 2^1 */ fe25519_square(&t, &z2_10_0); - /* 2^20 - 2^10 */ for (i = 1; i < 10; i++) { - fe25519_square(&t, &t); - } - /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0, &t, &z2_10_0); - - /* 2^21 - 2^1 */ fe25519_square(&t, &z2_20_0); - /* 2^40 - 2^20 */ for (i = 1; i < 20; i++) { - fe25519_square(&t,&t); - } - /* 2^40 - 2^0 */ fe25519_mul(&t, &t, &z2_20_0); - - /* 2^41 - 2^1 */ fe25519_square(&t, &t); - /* 2^50 - 2^10 */ for (i = 1; i < 10; i++) { - fe25519_square(&t,&t); - } - /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0, &t, &z2_10_0); - - /* 2^51 - 2^1 */ fe25519_square(&t, &z2_50_0); - /* 2^100 - 2^50 */ for (i = 1; i < 50; i++) { - fe25519_square(&t, &t); - } - /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0, &t, &z2_50_0); - - /* 2^101 - 2^1 */ fe25519_square(&t, &z2_100_0); - /* 2^200 - 2^100 */ for (i = 1; i < 100; i++) { - fe25519_square(&t, &t); - } - /* 2^200 - 2^0 */ fe25519_mul(&t, &t, &z2_100_0); - - /* 2^201 - 2^1 */ fe25519_square(&t, &t); - /* 2^250 - 2^50 */ for (i = 1; i < 50; i++) { - fe25519_square(&t, &t); - } - /* 2^250 - 2^0 */ fe25519_mul(&t, &t, &z2_50_0); - - /* 2^251 - 2^1 */ fe25519_square(&t, &t); - /* 2^252 - 2^2 */ fe25519_square(&t, &t); - /* 2^252 - 3 */ fe25519_mul(r, &t, x); -} diff --git a/libssh/src/gcrypt_missing.c b/libssh/src/gcrypt_missing.c deleted file mode 100644 index b21e5f30..00000000 --- a/libssh/src/gcrypt_missing.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * gcrypt_missing.c - routines that are in OpenSSL but not in libgcrypt. - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2006 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include - -#include "libssh/priv.h" -#include "libssh/libgcrypt.h" - -#ifdef HAVE_LIBGCRYPT -int my_gcry_dec2bn(bignum *bn, const char *data) { - int count; - - *bn = bignum_new(); - if (*bn == NULL) { - return 0; - } - gcry_mpi_set_ui(*bn, 0); - for (count = 0; data[count]; count++) { - gcry_mpi_mul_ui(*bn, *bn, 10); - gcry_mpi_add_ui(*bn, *bn, data[count] - '0'); - } - - return count; -} - -char *my_gcry_bn2dec(bignum bn) { - bignum bndup, num, ten; - char *ret; - int count, count2; - int size, rsize; - char decnum; - - size = gcry_mpi_get_nbits(bn) * 3; - rsize = size / 10 + size / 1000 + 2; - - ret = malloc(rsize + 1); - if (ret == NULL) { - return NULL; - } - - if (!gcry_mpi_cmp_ui(bn, 0)) { - strcpy(ret, "0"); - } else { - ten = bignum_new(); - if (ten == NULL) { - SAFE_FREE(ret); - return NULL; - } - - num = bignum_new(); - if (num == NULL) { - SAFE_FREE(ret); - bignum_free(ten); - return NULL; - } - - for (bndup = gcry_mpi_copy(bn), bignum_set_word(ten, 10), count = rsize; - count; count--) { - gcry_mpi_div(bndup, num, bndup, ten, 0); - for (decnum = 0, count2 = gcry_mpi_get_nbits(num); count2; - decnum *= 2, decnum += (gcry_mpi_test_bit(num, count2 - 1) ? 1 : 0), - count2--) - ; - ret[count - 1] = decnum + '0'; - } - for (count = 0; count < rsize && ret[count] == '0'; count++) - ; - for (count2 = 0; count2 < rsize - count; ++count2) { - ret[count2] = ret[count2 + count]; - } - ret[count2] = 0; - bignum_free(num); - bignum_free(bndup); - bignum_free(ten); - } - - return ret; -} - -#endif -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/ge25519.c b/libssh/src/ge25519.c deleted file mode 100644 index 20a33d1e..00000000 --- a/libssh/src/ge25519.c +++ /dev/null @@ -1,367 +0,0 @@ -/* $OpenBSD: ge25519.c,v 1.3 2013/12/09 11:03:45 markus Exp $ */ - -/* - * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, - * Peter Schwabe, Bo-Yin Yang. - * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519.c - */ - -#include "libssh/fe25519.h" -#include "libssh/sc25519.h" -#include "libssh/ge25519.h" - -/* - * Arithmetic on the twisted Edwards curve -x^2 + y^2 = 1 + dx^2y^2 - * with d = -(121665/121666) = 37095705934669439343138083508754565189542113879843219016388785533085940283555 - * Base point: (15112221349535400772501151409588531511454012693041857206046113283949847762202,46316835694926478169428394003475163141307993866256225615783033603165251855960); - */ - -/* d */ -static const fe25519 ge25519_ecd = { - {0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, - 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00, - 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, - 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52} -}; - -/* 2*d */ -static const fe25519 ge25519_ec2d = { - {0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, - 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00, - 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, - 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24} -}; - -/* sqrt(-1) */ -static const fe25519 ge25519_sqrtm1 = { - {0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, - 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F, - 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, - 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B} -}; - -#define ge25519_p3 ge25519 - -typedef struct { - fe25519 x; - fe25519 z; - fe25519 y; - fe25519 t; -} ge25519_p1p1; - -typedef struct { - fe25519 x; - fe25519 y; - fe25519 z; -} ge25519_p2; - -typedef struct { - fe25519 x; - fe25519 y; -} ge25519_aff; - - -/* Packed coordinates of the base point */ -const ge25519 ge25519_base = { - {{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, - 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69, - 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, - 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}}, - {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, - 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, - 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, - 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, - 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20, - 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, - 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}} -}; - -/* Multiples of the base point in affine representation */ -static const ge25519_aff ge25519_base_multiples_affine[425] = { -#include "ge25519_base.data" -}; - -static void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p) -{ - fe25519_mul(&r->x, &p->x, &p->t); - fe25519_mul(&r->y, &p->y, &p->z); - fe25519_mul(&r->z, &p->z, &p->t); -} - -static void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p) -{ - p1p1_to_p2((ge25519_p2 *)r, p); - fe25519_mul(&r->t, &p->x, &p->y); -} - -static void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q) -{ - fe25519 a,b,t1,t2,c,d,e,f,g,h,qt; - fe25519_mul(&qt, &q->x, &q->y); - fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */ - fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */ - fe25519_sub(&t1, &q->y, &q->x); - fe25519_add(&t2, &q->y, &q->x); - fe25519_mul(&a, &a, &t1); - fe25519_mul(&b, &b, &t2); - fe25519_sub(&e, &b, &a); /* E = B-A */ - fe25519_add(&h, &b, &a); /* H = B+A */ - fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */ - fe25519_mul(&c, &c, &ge25519_ec2d); - fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */ - fe25519_sub(&f, &d, &c); /* F = D-C */ - fe25519_add(&g, &d, &c); /* G = D+C */ - fe25519_mul(&r->x, &e, &f); - fe25519_mul(&r->y, &h, &g); - fe25519_mul(&r->z, &g, &f); - fe25519_mul(&r->t, &e, &h); -} - -static void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q) -{ - fe25519 a, b, c, d, t; - - fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */ - fe25519_sub(&t, &q->y, &q->x); - fe25519_mul(&a, &a, &t); - fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */ - fe25519_add(&t, &q->x, &q->y); - fe25519_mul(&b, &b, &t); - fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */ - fe25519_mul(&c, &c, &ge25519_ec2d); - fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */ - fe25519_add(&d, &d, &d); - fe25519_sub(&r->x, &b, &a); /* E = B-A */ - fe25519_sub(&r->t, &d, &c); /* F = D-C */ - fe25519_add(&r->z, &d, &c); /* G = D+C */ - fe25519_add(&r->y, &b, &a); /* H = B+A */ -} - -/* See http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd */ -static void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p) -{ - fe25519 a,b,c,d; - fe25519_square(&a, &p->x); - fe25519_square(&b, &p->y); - fe25519_square(&c, &p->z); - fe25519_add(&c, &c, &c); - fe25519_neg(&d, &a); - - fe25519_add(&r->x, &p->x, &p->y); - fe25519_square(&r->x, &r->x); - fe25519_sub(&r->x, &r->x, &a); - fe25519_sub(&r->x, &r->x, &b); - fe25519_add(&r->z, &d, &b); - fe25519_sub(&r->t, &r->z, &c); - fe25519_sub(&r->y, &d, &b); -} - -/* Constant-time version of: if(b) r = p */ -static void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b) -{ - fe25519_cmov(&r->x, &p->x, b); - fe25519_cmov(&r->y, &p->y, b); -} - -static unsigned char equal(signed char b,signed char c) -{ - unsigned char ub = b; - unsigned char uc = c; - unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ - uint32_t y = x; /* 0: yes; 1..255: no */ - - y -= 1; /* 4294967295: yes; 0..254: no */ - y >>= 31; /* 1: yes; 0: no */ - - return y; -} - -static unsigned char negative(signed char b) -{ - unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ - - x >>= 63; /* 1: yes; 0: no */ - - return x; -} - -static void choose_t(ge25519_aff *t, unsigned long long pos, signed char b) -{ - /* constant time */ - fe25519 v; - - *t = ge25519_base_multiples_affine[5 * pos + 0]; - - cmov_aff(t, &ge25519_base_multiples_affine[5 * pos + 1], - equal(b,1) | equal(b,-1)); - cmov_aff(t, &ge25519_base_multiples_affine[5 * pos + 2], - equal(b,2) | equal(b,-2)); - cmov_aff(t, &ge25519_base_multiples_affine[5 * pos + 3], - equal(b,3) | equal(b,-3)); - cmov_aff(t, &ge25519_base_multiples_affine[5 * pos + 4], - equal(b,-4)); - - fe25519_neg(&v, &t->x); - fe25519_cmov(&t->x, &v, negative(b)); -} - -static void setneutral(ge25519 *r) -{ - fe25519_setzero(&r->x); - fe25519_setone(&r->y); - fe25519_setone(&r->z); - fe25519_setzero(&r->t); -} - -/* ******************************************************************** - * EXPORTED FUNCTIONS - ******************************************************************** */ - -/* return 0 on success, -1 otherwise */ -int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32]) -{ - unsigned char par; - - fe25519 t, chk, num, den, den2, den4, den6; - fe25519_setone(&r->z); - par = p[31] >> 7; - fe25519_unpack(&r->y, p); - fe25519_square(&num, &r->y); /* x = y^2 */ - fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */ - fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */ - fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */ - - /* Computation of sqrt(num/den) */ - /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */ - fe25519_square(&den2, &den); - fe25519_square(&den4, &den2); - fe25519_mul(&den6, &den4, &den2); - fe25519_mul(&t, &den6, &num); - fe25519_mul(&t, &t, &den); - - fe25519_pow2523(&t, &t); - /* 2. computation of r->x = t * num * den^3 */ - fe25519_mul(&t, &t, &num); - fe25519_mul(&t, &t, &den); - fe25519_mul(&t, &t, &den); - fe25519_mul(&r->x, &t, &den); - - /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */ - fe25519_square(&chk, &r->x); - fe25519_mul(&chk, &chk, &den); - if (!fe25519_iseq_vartime(&chk, &num)) { - fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1); - } - - /* 4. Now we have one of the two square roots, except if input was not a square */ - fe25519_square(&chk, &r->x); - fe25519_mul(&chk, &chk, &den); - if (!fe25519_iseq_vartime(&chk, &num)) { - return -1; - } - - /* 5. Choose the desired square root according to parity: */ - if(fe25519_getparity(&r->x) != (1-par)) { - fe25519_neg(&r->x, &r->x); - } - - fe25519_mul(&r->t, &r->x, &r->y); - - return 0; -} - -void ge25519_pack(unsigned char r[32], const ge25519_p3 *p) -{ - fe25519 tx, ty, zi; - - fe25519_invert(&zi, &p->z); - fe25519_mul(&tx, &p->x, &zi); - fe25519_mul(&ty, &p->y, &zi); - fe25519_pack(r, &ty); - - r[31] ^= fe25519_getparity(&tx) << 7; -} - -int ge25519_isneutral_vartime(const ge25519_p3 *p) -{ - int ret = 1; - - if (!fe25519_iszero(&p->x)) { - ret = 0; - } - - if (!fe25519_iseq_vartime(&p->y, &p->z)) { - ret = 0; - } - - return ret; -} - -/* computes [s1]p1 + [s2]p2 */ -void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2) -{ - ge25519_p1p1 tp1p1; - ge25519_p3 pre[16]; - unsigned char b[127]; - int i; - - /* precomputation s2 s1 */ - setneutral(pre); /* 00 00 */ - pre[1] = *p1; /* 00 01 */ - dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */ - add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */ - pre[4] = *p2; /* 01 00 */ - add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */ - add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */ - add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */ - dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */ - add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */ - dbl_p1p1(&tp1p1,(ge25519_p2 *)&pre[5]); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */ - add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */ - add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */ - add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */ - add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */ - add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */ - - sc25519_2interleave2(b,s1,s2); - - /* scalar multiplication */ - *r = pre[b[126]]; - - for (i = 125; i >= 0; i--) { - dbl_p1p1(&tp1p1, (ge25519_p2 *)r); - p1p1_to_p2((ge25519_p2 *) r, &tp1p1); - dbl_p1p1(&tp1p1, (ge25519_p2 *)r); - if(b[i] != 0) { - p1p1_to_p3(r, &tp1p1); - add_p1p1(&tp1p1, r, &pre[b[i]]); - } - if (i != 0) { - p1p1_to_p2((ge25519_p2 *)r, &tp1p1); - } else { - p1p1_to_p3(r, &tp1p1); - } - } -} - -void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s) -{ - signed char b[85]; - int i; - ge25519_aff t; - - sc25519_window3(b,s); - - choose_t((ge25519_aff *)r, 0, b[0]); - fe25519_setone(&r->z); - fe25519_mul(&r->t, &r->x, &r->y); - for (i = 1; i < 85; i++) { - choose_t(&t, (unsigned long long) i, b[i]); - ge25519_mixadd2(r, &t); - } -} diff --git a/libssh/src/ge25519_base.data b/libssh/src/ge25519_base.data deleted file mode 100644 index 66fb1b61..00000000 --- a/libssh/src/ge25519_base.data +++ /dev/null @@ -1,858 +0,0 @@ -/* $OpenBSD: ge25519_base.data,v 1.3 2013/12/09 11:03:45 markus Exp $ */ - -/* - * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, - * Peter Schwabe, Bo-Yin Yang. - * Copied from supercop-20130419/crypto_sign/ed25519/ref/ge25519_base.data - */ - -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21}} , - {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}}, -{{{0x0e, 0xce, 0x43, 0x28, 0x4e, 0xa1, 0xc5, 0x83, 0x5f, 0xa4, 0xd7, 0x15, 0x45, 0x8e, 0x0d, 0x08, 0xac, 0xe7, 0x33, 0x18, 0x7d, 0x3b, 0x04, 0x3d, 0x6c, 0x04, 0x5a, 0x9f, 0x4c, 0x38, 0xab, 0x36}} , - {{0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0x0e, 0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39, 0x97, 0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d, 0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd, 0x60, 0x22}}}, -{{{0x5c, 0xe2, 0xf8, 0xd3, 0x5f, 0x48, 0x62, 0xac, 0x86, 0x48, 0x62, 0x81, 0x19, 0x98, 0x43, 0x63, 0x3a, 0xc8, 0xda, 0x3e, 0x74, 0xae, 0xf4, 0x1f, 0x49, 0x8f, 0x92, 0x22, 0x4a, 0x9c, 0xae, 0x67}} , - {{0xd4, 0xb4, 0xf5, 0x78, 0x48, 0x68, 0xc3, 0x02, 0x04, 0x03, 0x24, 0x67, 0x17, 0xec, 0x16, 0x9f, 0xf7, 0x9e, 0x26, 0x60, 0x8e, 0xa1, 0x26, 0xa1, 0xab, 0x69, 0xee, 0x77, 0xd1, 0xb1, 0x67, 0x12}}}, -{{{0x70, 0xf8, 0xc9, 0xc4, 0x57, 0xa6, 0x3a, 0x49, 0x47, 0x15, 0xce, 0x93, 0xc1, 0x9e, 0x73, 0x1a, 0xf9, 0x20, 0x35, 0x7a, 0xb8, 0xd4, 0x25, 0x83, 0x46, 0xf1, 0xcf, 0x56, 0xdb, 0xa8, 0x3d, 0x20}} , - {{0x2f, 0x11, 0x32, 0xca, 0x61, 0xab, 0x38, 0xdf, 0xf0, 0x0f, 0x2f, 0xea, 0x32, 0x28, 0xf2, 0x4c, 0x6c, 0x71, 0xd5, 0x80, 0x85, 0xb8, 0x0e, 0x47, 0xe1, 0x95, 0x15, 0xcb, 0x27, 0xe8, 0xd0, 0x47}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xc8, 0x84, 0xa5, 0x08, 0xbc, 0xfd, 0x87, 0x3b, 0x99, 0x8b, 0x69, 0x80, 0x7b, 0xc6, 0x3a, 0xeb, 0x93, 0xcf, 0x4e, 0xf8, 0x5c, 0x2d, 0x86, 0x42, 0xb6, 0x71, 0xd7, 0x97, 0x5f, 0xe1, 0x42, 0x67}} , - {{0xb4, 0xb9, 0x37, 0xfc, 0xa9, 0x5b, 0x2f, 0x1e, 0x93, 0xe4, 0x1e, 0x62, 0xfc, 0x3c, 0x78, 0x81, 0x8f, 0xf3, 0x8a, 0x66, 0x09, 0x6f, 0xad, 0x6e, 0x79, 0x73, 0xe5, 0xc9, 0x00, 0x06, 0xd3, 0x21}}}, -{{{0xf8, 0xf9, 0x28, 0x6c, 0x6d, 0x59, 0xb2, 0x59, 0x74, 0x23, 0xbf, 0xe7, 0x33, 0x8d, 0x57, 0x09, 0x91, 0x9c, 0x24, 0x08, 0x15, 0x2b, 0xe2, 0xb8, 0xee, 0x3a, 0xe5, 0x27, 0x06, 0x86, 0xa4, 0x23}} , - {{0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8, 0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a, 0xb0, 0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f, 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96, 0x96, 0x70}}}, -{{{0x81, 0x6b, 0x88, 0xe8, 0x1e, 0xc7, 0x77, 0x96, 0x0e, 0xa1, 0xa9, 0x52, 0xe0, 0xd8, 0x0e, 0x61, 0x9e, 0x79, 0x2d, 0x95, 0x9c, 0x8d, 0x96, 0xe0, 0x06, 0x40, 0x5d, 0x87, 0x28, 0x5f, 0x98, 0x70}} , - {{0xf1, 0x79, 0x7b, 0xed, 0x4f, 0x44, 0xb2, 0xe7, 0x08, 0x0d, 0xc2, 0x08, 0x12, 0xd2, 0x9f, 0xdf, 0xcd, 0x93, 0x20, 0x8a, 0xcf, 0x33, 0xca, 0x6d, 0x89, 0xb9, 0x77, 0xc8, 0x93, 0x1b, 0x4e, 0x60}}}, -{{{0x26, 0x4f, 0x7e, 0x97, 0xf6, 0x40, 0xdd, 0x4f, 0xfc, 0x52, 0x78, 0xf9, 0x90, 0x31, 0x03, 0xe6, 0x7d, 0x56, 0x39, 0x0b, 0x1d, 0x56, 0x82, 0x85, 0xf9, 0x1a, 0x42, 0x17, 0x69, 0x6c, 0xcf, 0x39}} , - {{0x69, 0xd2, 0x06, 0x3a, 0x4f, 0x39, 0x2d, 0xf9, 0x38, 0x40, 0x8c, 0x4c, 0xe7, 0x05, 0x12, 0xb4, 0x78, 0x8b, 0xf8, 0xc0, 0xec, 0x93, 0xde, 0x7a, 0x6b, 0xce, 0x2c, 0xe1, 0x0e, 0xa9, 0x34, 0x44}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x0b, 0xa4, 0x3c, 0xb0, 0x0f, 0x7a, 0x51, 0xf1, 0x78, 0xd6, 0xd9, 0x6a, 0xfd, 0x46, 0xe8, 0xb8, 0xa8, 0x79, 0x1d, 0x87, 0xf9, 0x90, 0xf2, 0x9c, 0x13, 0x29, 0xf8, 0x0b, 0x20, 0x64, 0xfa, 0x05}} , - {{0x26, 0x09, 0xda, 0x17, 0xaf, 0x95, 0xd6, 0xfb, 0x6a, 0x19, 0x0d, 0x6e, 0x5e, 0x12, 0xf1, 0x99, 0x4c, 0xaa, 0xa8, 0x6f, 0x79, 0x86, 0xf4, 0x72, 0x28, 0x00, 0x26, 0xf9, 0xea, 0x9e, 0x19, 0x3d}}}, -{{{0x87, 0xdd, 0xcf, 0xf0, 0x5b, 0x49, 0xa2, 0x5d, 0x40, 0x7a, 0x23, 0x26, 0xa4, 0x7a, 0x83, 0x8a, 0xb7, 0x8b, 0xd2, 0x1a, 0xbf, 0xea, 0x02, 0x24, 0x08, 0x5f, 0x7b, 0xa9, 0xb1, 0xbe, 0x9d, 0x37}} , - {{0xfc, 0x86, 0x4b, 0x08, 0xee, 0xe7, 0xa0, 0xfd, 0x21, 0x45, 0x09, 0x34, 0xc1, 0x61, 0x32, 0x23, 0xfc, 0x9b, 0x55, 0x48, 0x53, 0x99, 0xf7, 0x63, 0xd0, 0x99, 0xce, 0x01, 0xe0, 0x9f, 0xeb, 0x28}}}, -{{{0x47, 0xfc, 0xab, 0x5a, 0x17, 0xf0, 0x85, 0x56, 0x3a, 0x30, 0x86, 0x20, 0x28, 0x4b, 0x8e, 0x44, 0x74, 0x3a, 0x6e, 0x02, 0xf1, 0x32, 0x8f, 0x9f, 0x3f, 0x08, 0x35, 0xe9, 0xca, 0x16, 0x5f, 0x6e}} , - {{0x1c, 0x59, 0x1c, 0x65, 0x5d, 0x34, 0xa4, 0x09, 0xcd, 0x13, 0x9c, 0x70, 0x7d, 0xb1, 0x2a, 0xc5, 0x88, 0xaf, 0x0b, 0x60, 0xc7, 0x9f, 0x34, 0x8d, 0xd6, 0xb7, 0x7f, 0xea, 0x78, 0x65, 0x8d, 0x77}}}, -{{{0x56, 0xa5, 0xc2, 0x0c, 0xdd, 0xbc, 0xb8, 0x20, 0x6d, 0x57, 0x61, 0xb5, 0xfb, 0x78, 0xb5, 0xd4, 0x49, 0x54, 0x90, 0x26, 0xc1, 0xcb, 0xe9, 0xe6, 0xbf, 0xec, 0x1d, 0x4e, 0xed, 0x07, 0x7e, 0x5e}} , - {{0xc7, 0xf6, 0x6c, 0x56, 0x31, 0x20, 0x14, 0x0e, 0xa8, 0xd9, 0x27, 0xc1, 0x9a, 0x3d, 0x1b, 0x7d, 0x0e, 0x26, 0xd3, 0x81, 0xaa, 0xeb, 0xf5, 0x6b, 0x79, 0x02, 0xf1, 0x51, 0x5c, 0x75, 0x55, 0x0f}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x0a, 0x34, 0xcd, 0x82, 0x3c, 0x33, 0x09, 0x54, 0xd2, 0x61, 0x39, 0x30, 0x9b, 0xfd, 0xef, 0x21, 0x26, 0xd4, 0x70, 0xfa, 0xee, 0xf9, 0x31, 0x33, 0x73, 0x84, 0xd0, 0xb3, 0x81, 0xbf, 0xec, 0x2e}} , - {{0xe8, 0x93, 0x8b, 0x00, 0x64, 0xf7, 0x9c, 0xb8, 0x74, 0xe0, 0xe6, 0x49, 0x48, 0x4d, 0x4d, 0x48, 0xb6, 0x19, 0xa1, 0x40, 0xb7, 0xd9, 0x32, 0x41, 0x7c, 0x82, 0x37, 0xa1, 0x2d, 0xdc, 0xd2, 0x54}}}, -{{{0x68, 0x2b, 0x4a, 0x5b, 0xd5, 0xc7, 0x51, 0x91, 0x1d, 0xe1, 0x2a, 0x4b, 0xc4, 0x47, 0xf1, 0xbc, 0x7a, 0xb3, 0xcb, 0xc8, 0xb6, 0x7c, 0xac, 0x90, 0x05, 0xfd, 0xf3, 0xf9, 0x52, 0x3a, 0x11, 0x6b}} , - {{0x3d, 0xc1, 0x27, 0xf3, 0x59, 0x43, 0x95, 0x90, 0xc5, 0x96, 0x79, 0xf5, 0xf4, 0x95, 0x65, 0x29, 0x06, 0x9c, 0x51, 0x05, 0x18, 0xda, 0xb8, 0x2e, 0x79, 0x7e, 0x69, 0x59, 0x71, 0x01, 0xeb, 0x1a}}}, -{{{0x15, 0x06, 0x49, 0xb6, 0x8a, 0x3c, 0xea, 0x2f, 0x34, 0x20, 0x14, 0xc3, 0xaa, 0xd6, 0xaf, 0x2c, 0x3e, 0xbd, 0x65, 0x20, 0xe2, 0x4d, 0x4b, 0x3b, 0xeb, 0x9f, 0x4a, 0xc3, 0xad, 0xa4, 0x3b, 0x60}} , - {{0xbc, 0x58, 0xe6, 0xc0, 0x95, 0x2a, 0x2a, 0x81, 0x9a, 0x7a, 0xf3, 0xd2, 0x06, 0xbe, 0x48, 0xbc, 0x0c, 0xc5, 0x46, 0xe0, 0x6a, 0xd4, 0xac, 0x0f, 0xd9, 0xcc, 0x82, 0x34, 0x2c, 0xaf, 0xdb, 0x1f}}}, -{{{0xf7, 0x17, 0x13, 0xbd, 0xfb, 0xbc, 0xd2, 0xec, 0x45, 0xb3, 0x15, 0x31, 0xe9, 0xaf, 0x82, 0x84, 0x3d, 0x28, 0xc6, 0xfc, 0x11, 0xf5, 0x41, 0xb5, 0x8b, 0xd3, 0x12, 0x76, 0x52, 0xe7, 0x1a, 0x3c}} , - {{0x4e, 0x36, 0x11, 0x07, 0xa2, 0x15, 0x20, 0x51, 0xc4, 0x2a, 0xc3, 0x62, 0x8b, 0x5e, 0x7f, 0xa6, 0x0f, 0xf9, 0x45, 0x85, 0x6c, 0x11, 0x86, 0xb7, 0x7e, 0xe5, 0xd7, 0xf9, 0xc3, 0x91, 0x1c, 0x05}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xea, 0xd6, 0xde, 0x29, 0x3a, 0x00, 0xb9, 0x02, 0x59, 0xcb, 0x26, 0xc4, 0xba, 0x99, 0xb1, 0x97, 0x2f, 0x8e, 0x00, 0x92, 0x26, 0x4f, 0x52, 0xeb, 0x47, 0x1b, 0x89, 0x8b, 0x24, 0xc0, 0x13, 0x7d}} , - {{0xd5, 0x20, 0x5b, 0x80, 0xa6, 0x80, 0x20, 0x95, 0xc3, 0xe9, 0x9f, 0x8e, 0x87, 0x9e, 0x1e, 0x9e, 0x7a, 0xc7, 0xcc, 0x75, 0x6c, 0xa5, 0xf1, 0x91, 0x1a, 0xa8, 0x01, 0x2c, 0xab, 0x76, 0xa9, 0x59}}}, -{{{0xde, 0xc9, 0xb1, 0x31, 0x10, 0x16, 0xaa, 0x35, 0x14, 0x6a, 0xd4, 0xb5, 0x34, 0x82, 0x71, 0xd2, 0x4a, 0x5d, 0x9a, 0x1f, 0x53, 0x26, 0x3c, 0xe5, 0x8e, 0x8d, 0x33, 0x7f, 0xff, 0xa9, 0xd5, 0x17}} , - {{0x89, 0xaf, 0xf6, 0xa4, 0x64, 0xd5, 0x10, 0xe0, 0x1d, 0xad, 0xef, 0x44, 0xbd, 0xda, 0x83, 0xac, 0x7a, 0xa8, 0xf0, 0x1c, 0x07, 0xf9, 0xc3, 0x43, 0x6c, 0x3f, 0xb7, 0xd3, 0x87, 0x22, 0x02, 0x73}}}, -{{{0x64, 0x1d, 0x49, 0x13, 0x2f, 0x71, 0xec, 0x69, 0x87, 0xd0, 0x42, 0xee, 0x13, 0xec, 0xe3, 0xed, 0x56, 0x7b, 0xbf, 0xbd, 0x8c, 0x2f, 0x7d, 0x7b, 0x9d, 0x28, 0xec, 0x8e, 0x76, 0x2f, 0x6f, 0x08}} , - {{0x22, 0xf5, 0x5f, 0x4d, 0x15, 0xef, 0xfc, 0x4e, 0x57, 0x03, 0x36, 0x89, 0xf0, 0xeb, 0x5b, 0x91, 0xd6, 0xe2, 0xca, 0x01, 0xa5, 0xee, 0x52, 0xec, 0xa0, 0x3c, 0x8f, 0x33, 0x90, 0x5a, 0x94, 0x72}}}, -{{{0x8a, 0x4b, 0xe7, 0x38, 0xbc, 0xda, 0xc2, 0xb0, 0x85, 0xe1, 0x4a, 0xfe, 0x2d, 0x44, 0x84, 0xcb, 0x20, 0x6b, 0x2d, 0xbf, 0x11, 0x9c, 0xd7, 0xbe, 0xd3, 0x3e, 0x5f, 0xbf, 0x68, 0xbc, 0xa8, 0x07}} , - {{0x01, 0x89, 0x28, 0x22, 0x6a, 0x78, 0xaa, 0x29, 0x03, 0xc8, 0x74, 0x95, 0x03, 0x3e, 0xdc, 0xbd, 0x07, 0x13, 0xa8, 0xa2, 0x20, 0x2d, 0xb3, 0x18, 0x70, 0x42, 0xfd, 0x7a, 0xc4, 0xd7, 0x49, 0x72}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x02, 0xff, 0x32, 0x2b, 0x5c, 0x93, 0x54, 0x32, 0xe8, 0x57, 0x54, 0x1a, 0x8b, 0x33, 0x60, 0x65, 0xd3, 0x67, 0xa4, 0xc1, 0x26, 0xc4, 0xa4, 0x34, 0x1f, 0x9b, 0xa7, 0xa9, 0xf4, 0xd9, 0x4f, 0x5b}} , - {{0x46, 0x8d, 0xb0, 0x33, 0x54, 0x26, 0x5b, 0x68, 0xdf, 0xbb, 0xc5, 0xec, 0xc2, 0xf9, 0x3c, 0x5a, 0x37, 0xc1, 0x8e, 0x27, 0x47, 0xaa, 0x49, 0x5a, 0xf8, 0xfb, 0x68, 0x04, 0x23, 0xd1, 0xeb, 0x40}}}, -{{{0x65, 0xa5, 0x11, 0x84, 0x8a, 0x67, 0x9d, 0x9e, 0xd1, 0x44, 0x68, 0x7a, 0x34, 0xe1, 0x9f, 0xa3, 0x54, 0xcd, 0x07, 0xca, 0x79, 0x1f, 0x54, 0x2f, 0x13, 0x70, 0x4e, 0xee, 0xa2, 0xfa, 0xe7, 0x5d}} , - {{0x36, 0xec, 0x54, 0xf8, 0xce, 0xe4, 0x85, 0xdf, 0xf6, 0x6f, 0x1d, 0x90, 0x08, 0xbc, 0xe8, 0xc0, 0x92, 0x2d, 0x43, 0x6b, 0x92, 0xa9, 0x8e, 0xab, 0x0a, 0x2e, 0x1c, 0x1e, 0x64, 0x23, 0x9f, 0x2c}}}, -{{{0xa7, 0xd6, 0x2e, 0xd5, 0xcc, 0xd4, 0xcb, 0x5a, 0x3b, 0xa7, 0xf9, 0x46, 0x03, 0x1d, 0xad, 0x2b, 0x34, 0x31, 0x90, 0x00, 0x46, 0x08, 0x82, 0x14, 0xc4, 0xe0, 0x9c, 0xf0, 0xe3, 0x55, 0x43, 0x31}} , - {{0x60, 0xd6, 0xdd, 0x78, 0xe6, 0xd4, 0x22, 0x42, 0x1f, 0x00, 0xf9, 0xb1, 0x6a, 0x63, 0xe2, 0x92, 0x59, 0xd1, 0x1a, 0xb7, 0x00, 0x54, 0x29, 0xc9, 0xc1, 0xf6, 0x6f, 0x7a, 0xc5, 0x3c, 0x5f, 0x65}}}, -{{{0x27, 0x4f, 0xd0, 0x72, 0xb1, 0x11, 0x14, 0x27, 0x15, 0x94, 0x48, 0x81, 0x7e, 0x74, 0xd8, 0x32, 0xd5, 0xd1, 0x11, 0x28, 0x60, 0x63, 0x36, 0x32, 0x37, 0xb5, 0x13, 0x1c, 0xa0, 0x37, 0xe3, 0x74}} , - {{0xf1, 0x25, 0x4e, 0x11, 0x96, 0x67, 0xe6, 0x1c, 0xc2, 0xb2, 0x53, 0xe2, 0xda, 0x85, 0xee, 0xb2, 0x9f, 0x59, 0xf3, 0xba, 0xbd, 0xfa, 0xcf, 0x6e, 0xf9, 0xda, 0xa4, 0xb3, 0x02, 0x8f, 0x64, 0x08}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x34, 0x94, 0xf2, 0x64, 0x54, 0x47, 0x37, 0x07, 0x40, 0x8a, 0x20, 0xba, 0x4a, 0x55, 0xd7, 0x3f, 0x47, 0xba, 0x25, 0x23, 0x14, 0xb0, 0x2c, 0xe8, 0x55, 0xa8, 0xa6, 0xef, 0x51, 0xbd, 0x6f, 0x6a}} , - {{0x71, 0xd6, 0x16, 0x76, 0xb2, 0x06, 0xea, 0x79, 0xf5, 0xc4, 0xc3, 0x52, 0x7e, 0x61, 0xd1, 0xe1, 0xad, 0x70, 0x78, 0x1d, 0x16, 0x11, 0xf8, 0x7c, 0x2b, 0xfc, 0x55, 0x9f, 0x52, 0xf8, 0xf5, 0x16}}}, -{{{0x34, 0x96, 0x9a, 0xf6, 0xc5, 0xe0, 0x14, 0x03, 0x24, 0x0e, 0x4c, 0xad, 0x9e, 0x9a, 0x70, 0x23, 0x96, 0xb2, 0xf1, 0x2e, 0x9d, 0xc3, 0x32, 0x9b, 0x54, 0xa5, 0x73, 0xde, 0x88, 0xb1, 0x3e, 0x24}} , - {{0xf6, 0xe2, 0x4c, 0x1f, 0x5b, 0xb2, 0xaf, 0x82, 0xa5, 0xcf, 0x81, 0x10, 0x04, 0xef, 0xdb, 0xa2, 0xcc, 0x24, 0xb2, 0x7e, 0x0b, 0x7a, 0xeb, 0x01, 0xd8, 0x52, 0xf4, 0x51, 0x89, 0x29, 0x79, 0x37}}}, -{{{0x74, 0xde, 0x12, 0xf3, 0x68, 0xb7, 0x66, 0xc3, 0xee, 0x68, 0xdc, 0x81, 0xb5, 0x55, 0x99, 0xab, 0xd9, 0x28, 0x63, 0x6d, 0x8b, 0x40, 0x69, 0x75, 0x6c, 0xcd, 0x5c, 0x2a, 0x7e, 0x32, 0x7b, 0x29}} , - {{0x02, 0xcc, 0x22, 0x74, 0x4d, 0x19, 0x07, 0xc0, 0xda, 0xb5, 0x76, 0x51, 0x2a, 0xaa, 0xa6, 0x0a, 0x5f, 0x26, 0xd4, 0xbc, 0xaf, 0x48, 0x88, 0x7f, 0x02, 0xbc, 0xf2, 0xe1, 0xcf, 0xe9, 0xdd, 0x15}}}, -{{{0xed, 0xb5, 0x9a, 0x8c, 0x9a, 0xdd, 0x27, 0xf4, 0x7f, 0x47, 0xd9, 0x52, 0xa7, 0xcd, 0x65, 0xa5, 0x31, 0x22, 0xed, 0xa6, 0x63, 0x5b, 0x80, 0x4a, 0xad, 0x4d, 0xed, 0xbf, 0xee, 0x49, 0xb3, 0x06}} , - {{0xf8, 0x64, 0x8b, 0x60, 0x90, 0xe9, 0xde, 0x44, 0x77, 0xb9, 0x07, 0x36, 0x32, 0xc2, 0x50, 0xf5, 0x65, 0xdf, 0x48, 0x4c, 0x37, 0xaa, 0x68, 0xab, 0x9a, 0x1f, 0x3e, 0xff, 0x89, 0x92, 0xa0, 0x07}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x7d, 0x4f, 0x9c, 0x19, 0xc0, 0x4a, 0x31, 0xec, 0xf9, 0xaa, 0xeb, 0xb2, 0x16, 0x9c, 0xa3, 0x66, 0x5f, 0xd1, 0xd4, 0xed, 0xb8, 0x92, 0x1c, 0xab, 0xda, 0xea, 0xd9, 0x57, 0xdf, 0x4c, 0x2a, 0x48}} , - {{0x4b, 0xb0, 0x4e, 0x6e, 0x11, 0x3b, 0x51, 0xbd, 0x6a, 0xfd, 0xe4, 0x25, 0xa5, 0x5f, 0x11, 0x3f, 0x98, 0x92, 0x51, 0x14, 0xc6, 0x5f, 0x3c, 0x0b, 0xa8, 0xf7, 0xc2, 0x81, 0x43, 0xde, 0x91, 0x73}}}, -{{{0x3c, 0x8f, 0x9f, 0x33, 0x2a, 0x1f, 0x43, 0x33, 0x8f, 0x68, 0xff, 0x1f, 0x3d, 0x73, 0x6b, 0xbf, 0x68, 0xcc, 0x7d, 0x13, 0x6c, 0x24, 0x4b, 0xcc, 0x4d, 0x24, 0x0d, 0xfe, 0xde, 0x86, 0xad, 0x3b}} , - {{0x79, 0x51, 0x81, 0x01, 0xdc, 0x73, 0x53, 0xe0, 0x6e, 0x9b, 0xea, 0x68, 0x3f, 0x5c, 0x14, 0x84, 0x53, 0x8d, 0x4b, 0xc0, 0x9f, 0x9f, 0x89, 0x2b, 0x8c, 0xba, 0x86, 0xfa, 0xf2, 0xcd, 0xe3, 0x2d}}}, -{{{0x06, 0xf9, 0x29, 0x5a, 0xdb, 0x3d, 0x84, 0x52, 0xab, 0xcc, 0x6b, 0x60, 0x9d, 0xb7, 0x4a, 0x0e, 0x36, 0x63, 0x91, 0xad, 0xa0, 0x95, 0xb0, 0x97, 0x89, 0x4e, 0xcf, 0x7d, 0x3c, 0xe5, 0x7c, 0x28}} , - {{0x2e, 0x69, 0x98, 0xfd, 0xc6, 0xbd, 0xcc, 0xca, 0xdf, 0x9a, 0x44, 0x7e, 0x9d, 0xca, 0x89, 0x6d, 0xbf, 0x27, 0xc2, 0xf8, 0xcd, 0x46, 0x00, 0x2b, 0xb5, 0x58, 0x4e, 0xb7, 0x89, 0x09, 0xe9, 0x2d}}}, -{{{0x54, 0xbe, 0x75, 0xcb, 0x05, 0xb0, 0x54, 0xb7, 0xe7, 0x26, 0x86, 0x4a, 0xfc, 0x19, 0xcf, 0x27, 0x46, 0xd4, 0x22, 0x96, 0x5a, 0x11, 0xe8, 0xd5, 0x1b, 0xed, 0x71, 0xc5, 0x5d, 0xc8, 0xaf, 0x45}} , - {{0x40, 0x7b, 0x77, 0x57, 0x49, 0x9e, 0x80, 0x39, 0x23, 0xee, 0x81, 0x0b, 0x22, 0xcf, 0xdb, 0x7a, 0x2f, 0x14, 0xb8, 0x57, 0x8f, 0xa1, 0x39, 0x1e, 0x77, 0xfc, 0x0b, 0xa6, 0xbf, 0x8a, 0x0c, 0x6c}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x77, 0x3a, 0xd4, 0xd8, 0x27, 0xcf, 0xe8, 0xa1, 0x72, 0x9d, 0xca, 0xdd, 0x0d, 0x96, 0xda, 0x79, 0xed, 0x56, 0x42, 0x15, 0x60, 0xc7, 0x1c, 0x6b, 0x26, 0x30, 0xf6, 0x6a, 0x95, 0x67, 0xf3, 0x0a}} , - {{0xc5, 0x08, 0xa4, 0x2b, 0x2f, 0xbd, 0x31, 0x81, 0x2a, 0xa6, 0xb6, 0xe4, 0x00, 0x91, 0xda, 0x3d, 0xb2, 0xb0, 0x96, 0xce, 0x8a, 0xd2, 0x8d, 0x70, 0xb3, 0xd3, 0x34, 0x01, 0x90, 0x8d, 0x10, 0x21}}}, -{{{0x33, 0x0d, 0xe7, 0xba, 0x4f, 0x07, 0xdf, 0x8d, 0xea, 0x7d, 0xa0, 0xc5, 0xd6, 0xb1, 0xb0, 0xe5, 0x57, 0x1b, 0x5b, 0xf5, 0x45, 0x13, 0x14, 0x64, 0x5a, 0xeb, 0x5c, 0xfc, 0x54, 0x01, 0x76, 0x2b}} , - {{0x02, 0x0c, 0xc2, 0xaf, 0x96, 0x36, 0xfe, 0x4a, 0xe2, 0x54, 0x20, 0x6a, 0xeb, 0xb2, 0x9f, 0x62, 0xd7, 0xce, 0xa2, 0x3f, 0x20, 0x11, 0x34, 0x37, 0xe0, 0x42, 0xed, 0x6f, 0xf9, 0x1a, 0xc8, 0x7d}}}, -{{{0xd8, 0xb9, 0x11, 0xe8, 0x36, 0x3f, 0x42, 0xc1, 0xca, 0xdc, 0xd3, 0xf1, 0xc8, 0x23, 0x3d, 0x4f, 0x51, 0x7b, 0x9d, 0x8d, 0xd8, 0xe4, 0xa0, 0xaa, 0xf3, 0x04, 0xd6, 0x11, 0x93, 0xc8, 0x35, 0x45}} , - {{0x61, 0x36, 0xd6, 0x08, 0x90, 0xbf, 0xa7, 0x7a, 0x97, 0x6c, 0x0f, 0x84, 0xd5, 0x33, 0x2d, 0x37, 0xc9, 0x6a, 0x80, 0x90, 0x3d, 0x0a, 0xa2, 0xaa, 0xe1, 0xb8, 0x84, 0xba, 0x61, 0x36, 0xdd, 0x69}}}, -{{{0x6b, 0xdb, 0x5b, 0x9c, 0xc6, 0x92, 0xbc, 0x23, 0xaf, 0xc5, 0xb8, 0x75, 0xf8, 0x42, 0xfa, 0xd6, 0xb6, 0x84, 0x94, 0x63, 0x98, 0x93, 0x48, 0x78, 0x38, 0xcd, 0xbb, 0x18, 0x34, 0xc3, 0xdb, 0x67}} , - {{0x96, 0xf3, 0x3a, 0x09, 0x56, 0xb0, 0x6f, 0x7c, 0x51, 0x1e, 0x1b, 0x39, 0x48, 0xea, 0xc9, 0x0c, 0x25, 0xa2, 0x7a, 0xca, 0xe7, 0x92, 0xfc, 0x59, 0x30, 0xa3, 0x89, 0x85, 0xdf, 0x6f, 0x43, 0x38}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x79, 0x84, 0x44, 0x19, 0xbd, 0xe9, 0x54, 0xc4, 0xc0, 0x6e, 0x2a, 0xa8, 0xa8, 0x9b, 0x43, 0xd5, 0x71, 0x22, 0x5f, 0xdc, 0x01, 0xfa, 0xdf, 0xb3, 0xb8, 0x47, 0x4b, 0x0a, 0xa5, 0x44, 0xea, 0x29}} , - {{0x05, 0x90, 0x50, 0xaf, 0x63, 0x5f, 0x9d, 0x9e, 0xe1, 0x9d, 0x38, 0x97, 0x1f, 0x6c, 0xac, 0x30, 0x46, 0xb2, 0x6a, 0x19, 0xd1, 0x4b, 0xdb, 0xbb, 0x8c, 0xda, 0x2e, 0xab, 0xc8, 0x5a, 0x77, 0x6c}}}, -{{{0x2b, 0xbe, 0xaf, 0xa1, 0x6d, 0x2f, 0x0b, 0xb1, 0x8f, 0xe3, 0xe0, 0x38, 0xcd, 0x0b, 0x41, 0x1b, 0x4a, 0x15, 0x07, 0xf3, 0x6f, 0xdc, 0xb8, 0xe9, 0xde, 0xb2, 0xa3, 0x40, 0x01, 0xa6, 0x45, 0x1e}} , - {{0x76, 0x0a, 0xda, 0x8d, 0x2c, 0x07, 0x3f, 0x89, 0x7d, 0x04, 0xad, 0x43, 0x50, 0x6e, 0xd2, 0x47, 0xcb, 0x8a, 0xe6, 0x85, 0x1a, 0x24, 0xf3, 0xd2, 0x60, 0xfd, 0xdf, 0x73, 0xa4, 0x0d, 0x73, 0x0e}}}, -{{{0xfd, 0x67, 0x6b, 0x71, 0x9b, 0x81, 0x53, 0x39, 0x39, 0xf4, 0xb8, 0xd5, 0xc3, 0x30, 0x9b, 0x3b, 0x7c, 0xa3, 0xf0, 0xd0, 0x84, 0x21, 0xd6, 0xbf, 0xb7, 0x4c, 0x87, 0x13, 0x45, 0x2d, 0xa7, 0x55}} , - {{0x5d, 0x04, 0xb3, 0x40, 0x28, 0x95, 0x2d, 0x30, 0x83, 0xec, 0x5e, 0xe4, 0xff, 0x75, 0xfe, 0x79, 0x26, 0x9d, 0x1d, 0x36, 0xcd, 0x0a, 0x15, 0xd2, 0x24, 0x14, 0x77, 0x71, 0xd7, 0x8a, 0x1b, 0x04}}}, -{{{0x5d, 0x93, 0xc9, 0xbe, 0xaa, 0x90, 0xcd, 0x9b, 0xfb, 0x73, 0x7e, 0xb0, 0x64, 0x98, 0x57, 0x44, 0x42, 0x41, 0xb1, 0xaf, 0xea, 0xc1, 0xc3, 0x22, 0xff, 0x60, 0x46, 0xcb, 0x61, 0x81, 0x70, 0x61}} , - {{0x0d, 0x82, 0xb9, 0xfe, 0x21, 0xcd, 0xc4, 0xf5, 0x98, 0x0c, 0x4e, 0x72, 0xee, 0x87, 0x49, 0xf8, 0xa1, 0x95, 0xdf, 0x8f, 0x2d, 0xbd, 0x21, 0x06, 0x7c, 0x15, 0xe8, 0x12, 0x6d, 0x93, 0xd6, 0x38}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x91, 0xf7, 0x51, 0xd9, 0xef, 0x7d, 0x42, 0x01, 0x13, 0xe9, 0xb8, 0x7f, 0xa6, 0x49, 0x17, 0x64, 0x21, 0x80, 0x83, 0x2c, 0x63, 0x4c, 0x60, 0x09, 0x59, 0x91, 0x92, 0x77, 0x39, 0x51, 0xf4, 0x48}} , - {{0x60, 0xd5, 0x22, 0x83, 0x08, 0x2f, 0xff, 0x99, 0x3e, 0x69, 0x6d, 0x88, 0xda, 0xe7, 0x5b, 0x52, 0x26, 0x31, 0x2a, 0xe5, 0x89, 0xde, 0x68, 0x90, 0xb6, 0x22, 0x5a, 0xbd, 0xd3, 0x85, 0x53, 0x31}}}, -{{{0xd8, 0xce, 0xdc, 0xf9, 0x3c, 0x4b, 0xa2, 0x1d, 0x2c, 0x2f, 0x36, 0xbe, 0x7a, 0xfc, 0xcd, 0xbc, 0xdc, 0xf9, 0x30, 0xbd, 0xff, 0x05, 0xc7, 0xe4, 0x8e, 0x17, 0x62, 0xf8, 0x4d, 0xa0, 0x56, 0x79}} , - {{0x82, 0xe7, 0xf6, 0xba, 0x53, 0x84, 0x0a, 0xa3, 0x34, 0xff, 0x3c, 0xa3, 0x6a, 0xa1, 0x37, 0xea, 0xdd, 0xb6, 0x95, 0xb3, 0x78, 0x19, 0x76, 0x1e, 0x55, 0x2f, 0x77, 0x2e, 0x7f, 0xc1, 0xea, 0x5e}}}, -{{{0x83, 0xe1, 0x6e, 0xa9, 0x07, 0x33, 0x3e, 0x83, 0xff, 0xcb, 0x1c, 0x9f, 0xb1, 0xa3, 0xb4, 0xc9, 0xe1, 0x07, 0x97, 0xff, 0xf8, 0x23, 0x8f, 0xce, 0x40, 0xfd, 0x2e, 0x5e, 0xdb, 0x16, 0x43, 0x2d}} , - {{0xba, 0x38, 0x02, 0xf7, 0x81, 0x43, 0x83, 0xa3, 0x20, 0x4f, 0x01, 0x3b, 0x8a, 0x04, 0x38, 0x31, 0xc6, 0x0f, 0xc8, 0xdf, 0xd7, 0xfa, 0x2f, 0x88, 0x3f, 0xfc, 0x0c, 0x76, 0xc4, 0xa6, 0x45, 0x72}}}, -{{{0xbb, 0x0c, 0xbc, 0x6a, 0xa4, 0x97, 0x17, 0x93, 0x2d, 0x6f, 0xde, 0x72, 0x10, 0x1c, 0x08, 0x2c, 0x0f, 0x80, 0x32, 0x68, 0x27, 0xd4, 0xab, 0xdd, 0xc5, 0x58, 0x61, 0x13, 0x6d, 0x11, 0x1e, 0x4d}} , - {{0x1a, 0xb9, 0xc9, 0x10, 0xfb, 0x1e, 0x4e, 0xf4, 0x84, 0x4b, 0x8a, 0x5e, 0x7b, 0x4b, 0xe8, 0x43, 0x8c, 0x8f, 0x00, 0xb5, 0x54, 0x13, 0xc5, 0x5c, 0xb6, 0x35, 0x4e, 0x9d, 0xe4, 0x5b, 0x41, 0x6d}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x15, 0x7d, 0x12, 0x48, 0x82, 0x14, 0x42, 0xcd, 0x32, 0xd4, 0x4b, 0xc1, 0x72, 0x61, 0x2a, 0x8c, 0xec, 0xe2, 0xf8, 0x24, 0x45, 0x94, 0xe3, 0xbe, 0xdd, 0x67, 0xa8, 0x77, 0x5a, 0xae, 0x5b, 0x4b}} , - {{0xcb, 0x77, 0x9a, 0x20, 0xde, 0xb8, 0x23, 0xd9, 0xa0, 0x0f, 0x8c, 0x7b, 0xa5, 0xcb, 0xae, 0xb6, 0xec, 0x42, 0x67, 0x0e, 0x58, 0xa4, 0x75, 0x98, 0x21, 0x71, 0x84, 0xb3, 0xe0, 0x76, 0x94, 0x73}}}, -{{{0xdf, 0xfc, 0x69, 0x28, 0x23, 0x3f, 0x5b, 0xf8, 0x3b, 0x24, 0x37, 0xf3, 0x1d, 0xd5, 0x22, 0x6b, 0xd0, 0x98, 0xa8, 0x6c, 0xcf, 0xff, 0x06, 0xe1, 0x13, 0xdf, 0xb9, 0xc1, 0x0c, 0xa9, 0xbf, 0x33}} , - {{0xd9, 0x81, 0xda, 0xb2, 0x4f, 0x82, 0x9d, 0x43, 0x81, 0x09, 0xf1, 0xd2, 0x01, 0xef, 0xac, 0xf4, 0x2d, 0x7d, 0x01, 0x09, 0xf1, 0xff, 0xa5, 0x9f, 0xe5, 0xca, 0x27, 0x63, 0xdb, 0x20, 0xb1, 0x53}}}, -{{{0x67, 0x02, 0xe8, 0xad, 0xa9, 0x34, 0xd4, 0xf0, 0x15, 0x81, 0xaa, 0xc7, 0x4d, 0x87, 0x94, 0xea, 0x75, 0xe7, 0x4c, 0x94, 0x04, 0x0e, 0x69, 0x87, 0xe7, 0x51, 0x91, 0x10, 0x03, 0xc7, 0xbe, 0x56}} , - {{0x32, 0xfb, 0x86, 0xec, 0x33, 0x6b, 0x2e, 0x51, 0x2b, 0xc8, 0xfa, 0x6c, 0x70, 0x47, 0x7e, 0xce, 0x05, 0x0c, 0x71, 0xf3, 0xb4, 0x56, 0xa6, 0xdc, 0xcc, 0x78, 0x07, 0x75, 0xd0, 0xdd, 0xb2, 0x6a}}}, -{{{0xc6, 0xef, 0xb9, 0xc0, 0x2b, 0x22, 0x08, 0x1e, 0x71, 0x70, 0xb3, 0x35, 0x9c, 0x7a, 0x01, 0x92, 0x44, 0x9a, 0xf6, 0xb0, 0x58, 0x95, 0xc1, 0x9b, 0x02, 0xed, 0x2d, 0x7c, 0x34, 0x29, 0x49, 0x44}} , - {{0x45, 0x62, 0x1d, 0x2e, 0xff, 0x2a, 0x1c, 0x21, 0xa4, 0x25, 0x7b, 0x0d, 0x8c, 0x15, 0x39, 0xfc, 0x8f, 0x7c, 0xa5, 0x7d, 0x1e, 0x25, 0xa3, 0x45, 0xd6, 0xab, 0xbd, 0xcb, 0xc5, 0x5e, 0x78, 0x77}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xd0, 0xd3, 0x42, 0xed, 0x1d, 0x00, 0x3c, 0x15, 0x2c, 0x9c, 0x77, 0x81, 0xd2, 0x73, 0xd1, 0x06, 0xd5, 0xc4, 0x7f, 0x94, 0xbb, 0x92, 0x2d, 0x2c, 0x4b, 0x45, 0x4b, 0xe9, 0x2a, 0x89, 0x6b, 0x2b}} , - {{0xd2, 0x0c, 0x88, 0xc5, 0x48, 0x4d, 0xea, 0x0d, 0x4a, 0xc9, 0x52, 0x6a, 0x61, 0x79, 0xe9, 0x76, 0xf3, 0x85, 0x52, 0x5c, 0x1b, 0x2c, 0xe1, 0xd6, 0xc4, 0x0f, 0x18, 0x0e, 0x4e, 0xf6, 0x1c, 0x7f}}}, -{{{0xb4, 0x04, 0x2e, 0x42, 0xcb, 0x1f, 0x2b, 0x11, 0x51, 0x7b, 0x08, 0xac, 0xaa, 0x3e, 0x9e, 0x52, 0x60, 0xb7, 0xc2, 0x61, 0x57, 0x8c, 0x84, 0xd5, 0x18, 0xa6, 0x19, 0xfc, 0xb7, 0x75, 0x91, 0x1b}} , - {{0xe8, 0x68, 0xca, 0x44, 0xc8, 0x38, 0x38, 0xcc, 0x53, 0x0a, 0x32, 0x35, 0xcc, 0x52, 0xcb, 0x0e, 0xf7, 0xc5, 0xe7, 0xec, 0x3d, 0x85, 0xcc, 0x58, 0xe2, 0x17, 0x47, 0xff, 0x9f, 0xa5, 0x30, 0x17}}}, -{{{0xe3, 0xae, 0xc8, 0xc1, 0x71, 0x75, 0x31, 0x00, 0x37, 0x41, 0x5c, 0x0e, 0x39, 0xda, 0x73, 0xa0, 0xc7, 0x97, 0x36, 0x6c, 0x5b, 0xf2, 0xee, 0x64, 0x0a, 0x3d, 0x89, 0x1e, 0x1d, 0x49, 0x8c, 0x37}} , - {{0x4c, 0xe6, 0xb0, 0xc1, 0xa5, 0x2a, 0x82, 0x09, 0x08, 0xad, 0x79, 0x9c, 0x56, 0xf6, 0xf9, 0xc1, 0xd7, 0x7c, 0x39, 0x7f, 0x93, 0xca, 0x11, 0x55, 0xbf, 0x07, 0x1b, 0x82, 0x29, 0x69, 0x95, 0x5c}}}, -{{{0x87, 0xee, 0xa6, 0x56, 0x9e, 0xc2, 0x9a, 0x56, 0x24, 0x42, 0x85, 0x4d, 0x98, 0x31, 0x1e, 0x60, 0x4d, 0x87, 0x85, 0x04, 0xae, 0x46, 0x12, 0xf9, 0x8e, 0x7f, 0xe4, 0x7f, 0xf6, 0x1c, 0x37, 0x01}} , - {{0x73, 0x4c, 0xb6, 0xc5, 0xc4, 0xe9, 0x6c, 0x85, 0x48, 0x4a, 0x5a, 0xac, 0xd9, 0x1f, 0x43, 0xf8, 0x62, 0x5b, 0xee, 0x98, 0x2a, 0x33, 0x8e, 0x79, 0xce, 0x61, 0x06, 0x35, 0xd8, 0xd7, 0xca, 0x71}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x72, 0xd3, 0xae, 0xa6, 0xca, 0x8f, 0xcd, 0xcc, 0x78, 0x8e, 0x19, 0x4d, 0xa7, 0xd2, 0x27, 0xe9, 0xa4, 0x3c, 0x16, 0x5b, 0x84, 0x80, 0xf9, 0xd0, 0xcc, 0x6a, 0x1e, 0xca, 0x1e, 0x67, 0xbd, 0x63}} , - {{0x7b, 0x6e, 0x2a, 0xd2, 0x87, 0x48, 0xff, 0xa1, 0xca, 0xe9, 0x15, 0x85, 0xdc, 0xdb, 0x2c, 0x39, 0x12, 0x91, 0xa9, 0x20, 0xaa, 0x4f, 0x29, 0xf4, 0x15, 0x7a, 0xd2, 0xf5, 0x32, 0xcc, 0x60, 0x04}}}, -{{{0xe5, 0x10, 0x47, 0x3b, 0xfa, 0x90, 0xfc, 0x30, 0xb5, 0xea, 0x6f, 0x56, 0x8f, 0xfb, 0x0e, 0xa7, 0x3b, 0xc8, 0xb2, 0xff, 0x02, 0x7a, 0x33, 0x94, 0x93, 0x2a, 0x03, 0xe0, 0x96, 0x3a, 0x6c, 0x0f}} , - {{0x5a, 0x63, 0x67, 0xe1, 0x9b, 0x47, 0x78, 0x9f, 0x38, 0x79, 0xac, 0x97, 0x66, 0x1d, 0x5e, 0x51, 0xee, 0x24, 0x42, 0xe8, 0x58, 0x4b, 0x8a, 0x03, 0x75, 0x86, 0x37, 0x86, 0xe2, 0x97, 0x4e, 0x3d}}}, -{{{0x3f, 0x75, 0x8e, 0xb4, 0xff, 0xd8, 0xdd, 0xd6, 0x37, 0x57, 0x9d, 0x6d, 0x3b, 0xbd, 0xd5, 0x60, 0x88, 0x65, 0x9a, 0xb9, 0x4a, 0x68, 0x84, 0xa2, 0x67, 0xdd, 0x17, 0x25, 0x97, 0x04, 0x8b, 0x5e}} , - {{0xbb, 0x40, 0x5e, 0xbc, 0x16, 0x92, 0x05, 0xc4, 0xc0, 0x4e, 0x72, 0x90, 0x0e, 0xab, 0xcf, 0x8a, 0xed, 0xef, 0xb9, 0x2d, 0x3b, 0xf8, 0x43, 0x5b, 0xba, 0x2d, 0xeb, 0x2f, 0x52, 0xd2, 0xd1, 0x5a}}}, -{{{0x40, 0xb4, 0xab, 0xe6, 0xad, 0x9f, 0x46, 0x69, 0x4a, 0xb3, 0x8e, 0xaa, 0xea, 0x9c, 0x8a, 0x20, 0x16, 0x5d, 0x8c, 0x13, 0xbd, 0xf6, 0x1d, 0xc5, 0x24, 0xbd, 0x90, 0x2a, 0x1c, 0xc7, 0x13, 0x3b}} , - {{0x54, 0xdc, 0x16, 0x0d, 0x18, 0xbe, 0x35, 0x64, 0x61, 0x52, 0x02, 0x80, 0xaf, 0x05, 0xf7, 0xa6, 0x42, 0xd3, 0x8f, 0x2e, 0x79, 0x26, 0xa8, 0xbb, 0xb2, 0x17, 0x48, 0xb2, 0x7a, 0x0a, 0x89, 0x14}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x20, 0xa8, 0x88, 0xe3, 0x91, 0xc0, 0x6e, 0xbb, 0x8a, 0x27, 0x82, 0x51, 0x83, 0xb2, 0x28, 0xa9, 0x83, 0xeb, 0xa6, 0xa9, 0x4d, 0x17, 0x59, 0x22, 0x54, 0x00, 0x50, 0x45, 0xcb, 0x48, 0x4b, 0x18}} , - {{0x33, 0x7c, 0xe7, 0x26, 0xba, 0x4d, 0x32, 0xfe, 0x53, 0xf4, 0xfa, 0x83, 0xe3, 0xa5, 0x79, 0x66, 0x73, 0xef, 0x80, 0x23, 0x68, 0xc2, 0x60, 0xdd, 0xa9, 0x33, 0xdc, 0x03, 0x7a, 0xe0, 0xe0, 0x3e}}}, -{{{0x34, 0x5c, 0x13, 0xfb, 0xc0, 0xe3, 0x78, 0x2b, 0x54, 0x58, 0x22, 0x9b, 0x76, 0x81, 0x7f, 0x93, 0x9c, 0x25, 0x3c, 0xd2, 0xe9, 0x96, 0x21, 0x26, 0x08, 0xf5, 0xed, 0x95, 0x11, 0xae, 0x04, 0x5a}} , - {{0xb9, 0xe8, 0xc5, 0x12, 0x97, 0x1f, 0x83, 0xfe, 0x3e, 0x94, 0x99, 0xd4, 0x2d, 0xf9, 0x52, 0x59, 0x5c, 0x82, 0xa6, 0xf0, 0x75, 0x7e, 0xe8, 0xec, 0xcc, 0xac, 0x18, 0x21, 0x09, 0x67, 0x66, 0x67}}}, -{{{0xb3, 0x40, 0x29, 0xd1, 0xcb, 0x1b, 0x08, 0x9e, 0x9c, 0xb7, 0x53, 0xb9, 0x3b, 0x71, 0x08, 0x95, 0x12, 0x1a, 0x58, 0xaf, 0x7e, 0x82, 0x52, 0x43, 0x4f, 0x11, 0x39, 0xf4, 0x93, 0x1a, 0x26, 0x05}} , - {{0x6e, 0x44, 0xa3, 0xf9, 0x64, 0xaf, 0xe7, 0x6d, 0x7d, 0xdf, 0x1e, 0xac, 0x04, 0xea, 0x3b, 0x5f, 0x9b, 0xe8, 0x24, 0x9d, 0x0e, 0xe5, 0x2e, 0x3e, 0xdf, 0xa9, 0xf7, 0xd4, 0x50, 0x71, 0xf0, 0x78}}}, -{{{0x3e, 0xa8, 0x38, 0xc2, 0x57, 0x56, 0x42, 0x9a, 0xb1, 0xe2, 0xf8, 0x45, 0xaa, 0x11, 0x48, 0x5f, 0x17, 0xc4, 0x54, 0x27, 0xdc, 0x5d, 0xaa, 0xdd, 0x41, 0xbc, 0xdf, 0x81, 0xb9, 0x53, 0xee, 0x52}} , - {{0xc3, 0xf1, 0xa7, 0x6d, 0xb3, 0x5f, 0x92, 0x6f, 0xcc, 0x91, 0xb8, 0x95, 0x05, 0xdf, 0x3c, 0x64, 0x57, 0x39, 0x61, 0x51, 0xad, 0x8c, 0x38, 0x7b, 0xc8, 0xde, 0x00, 0x34, 0xbe, 0xa1, 0xb0, 0x7e}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x25, 0x24, 0x1d, 0x8a, 0x67, 0x20, 0xee, 0x42, 0xeb, 0x38, 0xed, 0x0b, 0x8b, 0xcd, 0x46, 0x9d, 0x5e, 0x6b, 0x1e, 0x24, 0x9d, 0x12, 0x05, 0x1a, 0xcc, 0x05, 0x4e, 0x92, 0x38, 0xe1, 0x1f, 0x50}} , - {{0x4e, 0xee, 0x1c, 0x91, 0xe6, 0x11, 0xbd, 0x8e, 0x55, 0x1a, 0x18, 0x75, 0x66, 0xaf, 0x4d, 0x7b, 0x0f, 0xae, 0x6d, 0x85, 0xca, 0x82, 0x58, 0x21, 0x9c, 0x18, 0xe0, 0xed, 0xec, 0x22, 0x80, 0x2f}}}, -{{{0x68, 0x3b, 0x0a, 0x39, 0x1d, 0x6a, 0x15, 0x57, 0xfc, 0xf0, 0x63, 0x54, 0xdb, 0x39, 0xdb, 0xe8, 0x5c, 0x64, 0xff, 0xa0, 0x09, 0x4f, 0x3b, 0xb7, 0x32, 0x60, 0x99, 0x94, 0xfd, 0x94, 0x82, 0x2d}} , - {{0x24, 0xf6, 0x5a, 0x44, 0xf1, 0x55, 0x2c, 0xdb, 0xea, 0x7c, 0x84, 0x7c, 0x01, 0xac, 0xe3, 0xfd, 0xc9, 0x27, 0xc1, 0x5a, 0xb9, 0xde, 0x4f, 0x5a, 0x90, 0xdd, 0xc6, 0x67, 0xaa, 0x6f, 0x8a, 0x3a}}}, -{{{0x78, 0x52, 0x87, 0xc9, 0x97, 0x63, 0xb1, 0xdd, 0x54, 0x5f, 0xc1, 0xf8, 0xf1, 0x06, 0xa6, 0xa8, 0xa3, 0x88, 0x82, 0xd4, 0xcb, 0xa6, 0x19, 0xdd, 0xd1, 0x11, 0x87, 0x08, 0x17, 0x4c, 0x37, 0x2a}} , - {{0xa1, 0x0c, 0xf3, 0x08, 0x43, 0xd9, 0x24, 0x1e, 0x83, 0xa7, 0xdf, 0x91, 0xca, 0xbd, 0x69, 0x47, 0x8d, 0x1b, 0xe2, 0xb9, 0x4e, 0xb5, 0xe1, 0x76, 0xb3, 0x1c, 0x93, 0x03, 0xce, 0x5f, 0xb3, 0x5a}}}, -{{{0x1d, 0xda, 0xe4, 0x61, 0x03, 0x50, 0xa9, 0x8b, 0x68, 0x18, 0xef, 0xb2, 0x1c, 0x84, 0x3b, 0xa2, 0x44, 0x95, 0xa3, 0x04, 0x3b, 0xd6, 0x99, 0x00, 0xaf, 0x76, 0x42, 0x67, 0x02, 0x7d, 0x85, 0x56}} , - {{0xce, 0x72, 0x0e, 0x29, 0x84, 0xb2, 0x7d, 0xd2, 0x45, 0xbe, 0x57, 0x06, 0xed, 0x7f, 0xcf, 0xed, 0xcd, 0xef, 0x19, 0xd6, 0xbc, 0x15, 0x79, 0x64, 0xd2, 0x18, 0xe3, 0x20, 0x67, 0x3a, 0x54, 0x0b}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x52, 0xfd, 0x04, 0xc5, 0xfb, 0x99, 0xe7, 0xe8, 0xfb, 0x8c, 0xe1, 0x42, 0x03, 0xef, 0x9d, 0xd9, 0x9e, 0x4d, 0xf7, 0x80, 0xcf, 0x2e, 0xcc, 0x9b, 0x45, 0xc9, 0x7b, 0x7a, 0xbc, 0x37, 0xa8, 0x52}} , - {{0x96, 0x11, 0x41, 0x8a, 0x47, 0x91, 0xfe, 0xb6, 0xda, 0x7a, 0x54, 0x63, 0xd1, 0x14, 0x35, 0x05, 0x86, 0x8c, 0xa9, 0x36, 0x3f, 0xf2, 0x85, 0x54, 0x4e, 0x92, 0xd8, 0x85, 0x01, 0x46, 0xd6, 0x50}}}, -{{{0x53, 0xcd, 0xf3, 0x86, 0x40, 0xe6, 0x39, 0x42, 0x95, 0xd6, 0xcb, 0x45, 0x1a, 0x20, 0xc8, 0x45, 0x4b, 0x32, 0x69, 0x04, 0xb1, 0xaf, 0x20, 0x46, 0xc7, 0x6b, 0x23, 0x5b, 0x69, 0xee, 0x30, 0x3f}} , - {{0x70, 0x83, 0x47, 0xc0, 0xdb, 0x55, 0x08, 0xa8, 0x7b, 0x18, 0x6d, 0xf5, 0x04, 0x5a, 0x20, 0x0c, 0x4a, 0x8c, 0x60, 0xae, 0xae, 0x0f, 0x64, 0x55, 0x55, 0x2e, 0xd5, 0x1d, 0x53, 0x31, 0x42, 0x41}}}, -{{{0xca, 0xfc, 0x88, 0x6b, 0x96, 0x78, 0x0a, 0x8b, 0x83, 0xdc, 0xbc, 0xaf, 0x40, 0xb6, 0x8d, 0x7f, 0xef, 0xb4, 0xd1, 0x3f, 0xcc, 0xa2, 0x74, 0xc9, 0xc2, 0x92, 0x55, 0x00, 0xab, 0xdb, 0xbf, 0x4f}} , - {{0x93, 0x1c, 0x06, 0x2d, 0x66, 0x65, 0x02, 0xa4, 0x97, 0x18, 0xfd, 0x00, 0xe7, 0xab, 0x03, 0xec, 0xce, 0xc1, 0xbf, 0x37, 0xf8, 0x13, 0x53, 0xa5, 0xe5, 0x0c, 0x3a, 0xa8, 0x55, 0xb9, 0xff, 0x68}}}, -{{{0xe4, 0xe6, 0x6d, 0x30, 0x7d, 0x30, 0x35, 0xc2, 0x78, 0x87, 0xf9, 0xfc, 0x6b, 0x5a, 0xc3, 0xb7, 0x65, 0xd8, 0x2e, 0xc7, 0xa5, 0x0c, 0xc6, 0xdc, 0x12, 0xaa, 0xd6, 0x4f, 0xc5, 0x38, 0xbc, 0x0e}} , - {{0xe2, 0x3c, 0x76, 0x86, 0x38, 0xf2, 0x7b, 0x2c, 0x16, 0x78, 0x8d, 0xf5, 0xa4, 0x15, 0xda, 0xdb, 0x26, 0x85, 0xa0, 0x56, 0xdd, 0x1d, 0xe3, 0xb3, 0xfd, 0x40, 0xef, 0xf2, 0xd9, 0xa1, 0xb3, 0x04}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xdb, 0x49, 0x0e, 0xe6, 0x58, 0x10, 0x7a, 0x52, 0xda, 0xb5, 0x7d, 0x37, 0x6a, 0x3e, 0xa1, 0x78, 0xce, 0xc7, 0x1c, 0x24, 0x23, 0xdb, 0x7d, 0xfb, 0x8c, 0x8d, 0xdc, 0x30, 0x67, 0x69, 0x75, 0x3b}} , - {{0xa9, 0xea, 0x6d, 0x16, 0x16, 0x60, 0xf4, 0x60, 0x87, 0x19, 0x44, 0x8c, 0x4a, 0x8b, 0x3e, 0xfb, 0x16, 0x00, 0x00, 0x54, 0xa6, 0x9e, 0x9f, 0xef, 0xcf, 0xd9, 0xd2, 0x4c, 0x74, 0x31, 0xd0, 0x34}}}, -{{{0xa4, 0xeb, 0x04, 0xa4, 0x8c, 0x8f, 0x71, 0x27, 0x95, 0x85, 0x5d, 0x55, 0x4b, 0xb1, 0x26, 0x26, 0xc8, 0xae, 0x6a, 0x7d, 0xa2, 0x21, 0xca, 0xce, 0x38, 0xab, 0x0f, 0xd0, 0xd5, 0x2b, 0x6b, 0x00}} , - {{0xe5, 0x67, 0x0c, 0xf1, 0x3a, 0x9a, 0xea, 0x09, 0x39, 0xef, 0xd1, 0x30, 0xbc, 0x33, 0xba, 0xb1, 0x6a, 0xc5, 0x27, 0x08, 0x7f, 0x54, 0x80, 0x3d, 0xab, 0xf6, 0x15, 0x7a, 0xc2, 0x40, 0x73, 0x72}}}, -{{{0x84, 0x56, 0x82, 0xb6, 0x12, 0x70, 0x7f, 0xf7, 0xf0, 0xbd, 0x5b, 0xa9, 0xd5, 0xc5, 0x5f, 0x59, 0xbf, 0x7f, 0xb3, 0x55, 0x22, 0x02, 0xc9, 0x44, 0x55, 0x87, 0x8f, 0x96, 0x98, 0x64, 0x6d, 0x15}} , - {{0xb0, 0x8b, 0xaa, 0x1e, 0xec, 0xc7, 0xa5, 0x8f, 0x1f, 0x92, 0x04, 0xc6, 0x05, 0xf6, 0xdf, 0xa1, 0xcc, 0x1f, 0x81, 0xf5, 0x0e, 0x9c, 0x57, 0xdc, 0xe3, 0xbb, 0x06, 0x87, 0x1e, 0xfe, 0x23, 0x6c}}}, -{{{0xd8, 0x2b, 0x5b, 0x16, 0xea, 0x20, 0xf1, 0xd3, 0x68, 0x8f, 0xae, 0x5b, 0xd0, 0xa9, 0x1a, 0x19, 0xa8, 0x36, 0xfb, 0x2b, 0x57, 0x88, 0x7d, 0x90, 0xd5, 0xa6, 0xf3, 0xdc, 0x38, 0x89, 0x4e, 0x1f}} , - {{0xcc, 0x19, 0xda, 0x9b, 0x3b, 0x43, 0x48, 0x21, 0x2e, 0x23, 0x4d, 0x3d, 0xae, 0xf8, 0x8c, 0xfc, 0xdd, 0xa6, 0x74, 0x37, 0x65, 0xca, 0xee, 0x1a, 0x19, 0x8e, 0x9f, 0x64, 0x6f, 0x0c, 0x8b, 0x5a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x25, 0xb9, 0xc2, 0xf0, 0x72, 0xb8, 0x15, 0x16, 0xcc, 0x8d, 0x3c, 0x6f, 0x25, 0xed, 0xf4, 0x46, 0x2e, 0x0c, 0x60, 0x0f, 0xe2, 0x84, 0x34, 0x55, 0x89, 0x59, 0x34, 0x1b, 0xf5, 0x8d, 0xfe, 0x08}} , - {{0xf8, 0xab, 0x93, 0xbc, 0x44, 0xba, 0x1b, 0x75, 0x4b, 0x49, 0x6f, 0xd0, 0x54, 0x2e, 0x63, 0xba, 0xb5, 0xea, 0xed, 0x32, 0x14, 0xc9, 0x94, 0xd8, 0xc5, 0xce, 0xf4, 0x10, 0x68, 0xe0, 0x38, 0x27}}}, -{{{0x74, 0x1c, 0x14, 0x9b, 0xd4, 0x64, 0x61, 0x71, 0x5a, 0xb6, 0x21, 0x33, 0x4f, 0xf7, 0x8e, 0xba, 0xa5, 0x48, 0x9a, 0xc7, 0xfa, 0x9a, 0xf0, 0xb4, 0x62, 0xad, 0xf2, 0x5e, 0xcc, 0x03, 0x24, 0x1a}} , - {{0xf5, 0x76, 0xfd, 0xe4, 0xaf, 0xb9, 0x03, 0x59, 0xce, 0x63, 0xd2, 0x3b, 0x1f, 0xcd, 0x21, 0x0c, 0xad, 0x44, 0xa5, 0x97, 0xac, 0x80, 0x11, 0x02, 0x9b, 0x0c, 0xe5, 0x8b, 0xcd, 0xfb, 0x79, 0x77}}}, -{{{0x15, 0xbe, 0x9a, 0x0d, 0xba, 0x38, 0x72, 0x20, 0x8a, 0xf5, 0xbe, 0x59, 0x93, 0x79, 0xb7, 0xf6, 0x6a, 0x0c, 0x38, 0x27, 0x1a, 0x60, 0xf4, 0x86, 0x3b, 0xab, 0x5a, 0x00, 0xa0, 0xce, 0x21, 0x7d}} , - {{0x6c, 0xba, 0x14, 0xc5, 0xea, 0x12, 0x9e, 0x2e, 0x82, 0x63, 0xce, 0x9b, 0x4a, 0xe7, 0x1d, 0xec, 0xf1, 0x2e, 0x51, 0x1c, 0xf4, 0xd0, 0x69, 0x15, 0x42, 0x9d, 0xa3, 0x3f, 0x0e, 0xbf, 0xe9, 0x5c}}}, -{{{0xe4, 0x0d, 0xf4, 0xbd, 0xee, 0x31, 0x10, 0xed, 0xcb, 0x12, 0x86, 0xad, 0xd4, 0x2f, 0x90, 0x37, 0x32, 0xc3, 0x0b, 0x73, 0xec, 0x97, 0x85, 0xa4, 0x01, 0x1c, 0x76, 0x35, 0xfe, 0x75, 0xdd, 0x71}} , - {{0x11, 0xa4, 0x88, 0x9f, 0x3e, 0x53, 0x69, 0x3b, 0x1b, 0xe0, 0xf7, 0xba, 0x9b, 0xad, 0x4e, 0x81, 0x5f, 0xb5, 0x5c, 0xae, 0xbe, 0x67, 0x86, 0x37, 0x34, 0x8e, 0x07, 0x32, 0x45, 0x4a, 0x67, 0x39}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x90, 0x70, 0x58, 0x20, 0x03, 0x1e, 0x67, 0xb2, 0xc8, 0x9b, 0x58, 0xc5, 0xb1, 0xeb, 0x2d, 0x4a, 0xde, 0x82, 0x8c, 0xf2, 0xd2, 0x14, 0xb8, 0x70, 0x61, 0x4e, 0x73, 0xd6, 0x0b, 0x6b, 0x0d, 0x30}} , - {{0x81, 0xfc, 0x55, 0x5c, 0xbf, 0xa7, 0xc4, 0xbd, 0xe2, 0xf0, 0x4b, 0x8f, 0xe9, 0x7d, 0x99, 0xfa, 0xd3, 0xab, 0xbc, 0xc7, 0x83, 0x2b, 0x04, 0x7f, 0x0c, 0x19, 0x43, 0x03, 0x3d, 0x07, 0xca, 0x40}}}, -{{{0xf9, 0xc8, 0xbe, 0x8c, 0x16, 0x81, 0x39, 0x96, 0xf6, 0x17, 0x58, 0xc8, 0x30, 0x58, 0xfb, 0xc2, 0x03, 0x45, 0xd2, 0x52, 0x76, 0xe0, 0x6a, 0x26, 0x28, 0x5c, 0x88, 0x59, 0x6a, 0x5a, 0x54, 0x42}} , - {{0x07, 0xb5, 0x2e, 0x2c, 0x67, 0x15, 0x9b, 0xfb, 0x83, 0x69, 0x1e, 0x0f, 0xda, 0xd6, 0x29, 0xb1, 0x60, 0xe0, 0xb2, 0xba, 0x69, 0xa2, 0x9e, 0xbd, 0xbd, 0xe0, 0x1c, 0xbd, 0xcd, 0x06, 0x64, 0x70}}}, -{{{0x41, 0xfa, 0x8c, 0xe1, 0x89, 0x8f, 0x27, 0xc8, 0x25, 0x8f, 0x6f, 0x5f, 0x55, 0xf8, 0xde, 0x95, 0x6d, 0x2f, 0x75, 0x16, 0x2b, 0x4e, 0x44, 0xfd, 0x86, 0x6e, 0xe9, 0x70, 0x39, 0x76, 0x97, 0x7e}} , - {{0x17, 0x62, 0x6b, 0x14, 0xa1, 0x7c, 0xd0, 0x79, 0x6e, 0xd8, 0x8a, 0xa5, 0x6d, 0x8c, 0x93, 0xd2, 0x3f, 0xec, 0x44, 0x8d, 0x6e, 0x91, 0x01, 0x8c, 0x8f, 0xee, 0x01, 0x8f, 0xc0, 0xb4, 0x85, 0x0e}}}, -{{{0x02, 0x3a, 0x70, 0x41, 0xe4, 0x11, 0x57, 0x23, 0xac, 0xe6, 0xfc, 0x54, 0x7e, 0xcd, 0xd7, 0x22, 0xcb, 0x76, 0x9f, 0x20, 0xce, 0xa0, 0x73, 0x76, 0x51, 0x3b, 0xa4, 0xf8, 0xe3, 0x62, 0x12, 0x6c}} , - {{0x7f, 0x00, 0x9c, 0x26, 0x0d, 0x6f, 0x48, 0x7f, 0x3a, 0x01, 0xed, 0xc5, 0x96, 0xb0, 0x1f, 0x4f, 0xa8, 0x02, 0x62, 0x27, 0x8a, 0x50, 0x8d, 0x9a, 0x8b, 0x52, 0x0f, 0x1e, 0xcf, 0x41, 0x38, 0x19}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xf5, 0x6c, 0xd4, 0x2f, 0x0f, 0x69, 0x0f, 0x87, 0x3f, 0x61, 0x65, 0x1e, 0x35, 0x34, 0x85, 0xba, 0x02, 0x30, 0xac, 0x25, 0x3d, 0xe2, 0x62, 0xf1, 0xcc, 0xe9, 0x1b, 0xc2, 0xef, 0x6a, 0x42, 0x57}} , - {{0x34, 0x1f, 0x2e, 0xac, 0xd1, 0xc7, 0x04, 0x52, 0x32, 0x66, 0xb2, 0x33, 0x73, 0x21, 0x34, 0x54, 0xf7, 0x71, 0xed, 0x06, 0xb0, 0xff, 0xa6, 0x59, 0x6f, 0x8a, 0x4e, 0xfb, 0x02, 0xb0, 0x45, 0x6b}}}, -{{{0xf5, 0x48, 0x0b, 0x03, 0xc5, 0x22, 0x7d, 0x80, 0x08, 0x53, 0xfe, 0x32, 0xb1, 0xa1, 0x8a, 0x74, 0x6f, 0xbd, 0x3f, 0x85, 0xf4, 0xcf, 0xf5, 0x60, 0xaf, 0x41, 0x7e, 0x3e, 0x46, 0xa3, 0x5a, 0x20}} , - {{0xaa, 0x35, 0x87, 0x44, 0x63, 0x66, 0x97, 0xf8, 0x6e, 0x55, 0x0c, 0x04, 0x3e, 0x35, 0x50, 0xbf, 0x93, 0x69, 0xd2, 0x8b, 0x05, 0x55, 0x99, 0xbe, 0xe2, 0x53, 0x61, 0xec, 0xe8, 0x08, 0x0b, 0x32}}}, -{{{0xb3, 0x10, 0x45, 0x02, 0x69, 0x59, 0x2e, 0x97, 0xd9, 0x64, 0xf8, 0xdb, 0x25, 0x80, 0xdc, 0xc4, 0xd5, 0x62, 0x3c, 0xed, 0x65, 0x91, 0xad, 0xd1, 0x57, 0x81, 0x94, 0xaa, 0xa1, 0x29, 0xfc, 0x68}} , - {{0xdd, 0xb5, 0x7d, 0xab, 0x5a, 0x21, 0x41, 0x53, 0xbb, 0x17, 0x79, 0x0d, 0xd1, 0xa8, 0x0c, 0x0c, 0x20, 0x88, 0x09, 0xe9, 0x84, 0xe8, 0x25, 0x11, 0x67, 0x7a, 0x8b, 0x1a, 0xe4, 0x5d, 0xe1, 0x5d}}}, -{{{0x37, 0xea, 0xfe, 0x65, 0x3b, 0x25, 0xe8, 0xe1, 0xc2, 0xc5, 0x02, 0xa4, 0xbe, 0x98, 0x0a, 0x2b, 0x61, 0xc1, 0x9b, 0xe2, 0xd5, 0x92, 0xe6, 0x9e, 0x7d, 0x1f, 0xca, 0x43, 0x88, 0x8b, 0x2c, 0x59}} , - {{0xe0, 0xb5, 0x00, 0x1d, 0x2a, 0x6f, 0xaf, 0x79, 0x86, 0x2f, 0xa6, 0x5a, 0x93, 0xd1, 0xfe, 0xae, 0x3a, 0xee, 0xdb, 0x7c, 0x61, 0xbe, 0x7c, 0x01, 0xf9, 0xfe, 0x52, 0xdc, 0xd8, 0x52, 0xa3, 0x42}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x22, 0xaf, 0x13, 0x37, 0xbd, 0x37, 0x71, 0xac, 0x04, 0x46, 0x63, 0xac, 0xa4, 0x77, 0xed, 0x25, 0x38, 0xe0, 0x15, 0xa8, 0x64, 0x00, 0x0d, 0xce, 0x51, 0x01, 0xa9, 0xbc, 0x0f, 0x03, 0x1c, 0x04}} , - {{0x89, 0xf9, 0x80, 0x07, 0xcf, 0x3f, 0xb3, 0xe9, 0xe7, 0x45, 0x44, 0x3d, 0x2a, 0x7c, 0xe9, 0xe4, 0x16, 0x5c, 0x5e, 0x65, 0x1c, 0xc7, 0x7d, 0xc6, 0x7a, 0xfb, 0x43, 0xee, 0x25, 0x76, 0x46, 0x72}}}, -{{{0x02, 0xa2, 0xed, 0xf4, 0x8f, 0x6b, 0x0b, 0x3e, 0xeb, 0x35, 0x1a, 0xd5, 0x7e, 0xdb, 0x78, 0x00, 0x96, 0x8a, 0xa0, 0xb4, 0xcf, 0x60, 0x4b, 0xd4, 0xd5, 0xf9, 0x2d, 0xbf, 0x88, 0xbd, 0x22, 0x62}} , - {{0x13, 0x53, 0xe4, 0x82, 0x57, 0xfa, 0x1e, 0x8f, 0x06, 0x2b, 0x90, 0xba, 0x08, 0xb6, 0x10, 0x54, 0x4f, 0x7c, 0x1b, 0x26, 0xed, 0xda, 0x6b, 0xdd, 0x25, 0xd0, 0x4e, 0xea, 0x42, 0xbb, 0x25, 0x03}}}, -{{{0x51, 0x16, 0x50, 0x7c, 0xd5, 0x5d, 0xf6, 0x99, 0xe8, 0x77, 0x72, 0x4e, 0xfa, 0x62, 0xcb, 0x76, 0x75, 0x0c, 0xe2, 0x71, 0x98, 0x92, 0xd5, 0xfa, 0x45, 0xdf, 0x5c, 0x6f, 0x1e, 0x9e, 0x28, 0x69}} , - {{0x0d, 0xac, 0x66, 0x6d, 0xc3, 0x8b, 0xba, 0x16, 0xb5, 0xe2, 0xa0, 0x0d, 0x0c, 0xbd, 0xa4, 0x8e, 0x18, 0x6c, 0xf2, 0xdc, 0xf9, 0xdc, 0x4a, 0x86, 0x25, 0x95, 0x14, 0xcb, 0xd8, 0x1a, 0x04, 0x0f}}}, -{{{0x97, 0xa5, 0xdb, 0x8b, 0x2d, 0xaa, 0x42, 0x11, 0x09, 0xf2, 0x93, 0xbb, 0xd9, 0x06, 0x84, 0x4e, 0x11, 0xa8, 0xa0, 0x25, 0x2b, 0xa6, 0x5f, 0xae, 0xc4, 0xb4, 0x4c, 0xc8, 0xab, 0xc7, 0x3b, 0x02}} , - {{0xee, 0xc9, 0x29, 0x0f, 0xdf, 0x11, 0x85, 0xed, 0xce, 0x0d, 0x62, 0x2c, 0x8f, 0x4b, 0xf9, 0x04, 0xe9, 0x06, 0x72, 0x1d, 0x37, 0x20, 0x50, 0xc9, 0x14, 0xeb, 0xec, 0x39, 0xa7, 0x97, 0x2b, 0x4d}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x69, 0xd1, 0x39, 0xbd, 0xfb, 0x33, 0xbe, 0xc4, 0xf0, 0x5c, 0xef, 0xf0, 0x56, 0x68, 0xfc, 0x97, 0x47, 0xc8, 0x72, 0xb6, 0x53, 0xa4, 0x0a, 0x98, 0xa5, 0xb4, 0x37, 0x71, 0xcf, 0x66, 0x50, 0x6d}} , - {{0x17, 0xa4, 0x19, 0x52, 0x11, 0x47, 0xb3, 0x5c, 0x5b, 0xa9, 0x2e, 0x22, 0xb4, 0x00, 0x52, 0xf9, 0x57, 0x18, 0xb8, 0xbe, 0x5a, 0xe3, 0xab, 0x83, 0xc8, 0x87, 0x0a, 0x2a, 0xd8, 0x8c, 0xbb, 0x54}}}, -{{{0xa9, 0x62, 0x93, 0x85, 0xbe, 0xe8, 0x73, 0x4a, 0x0e, 0xb0, 0xb5, 0x2d, 0x94, 0x50, 0xaa, 0xd3, 0xb2, 0xea, 0x9d, 0x62, 0x76, 0x3b, 0x07, 0x34, 0x4e, 0x2d, 0x70, 0xc8, 0x9a, 0x15, 0x66, 0x6b}} , - {{0xc5, 0x96, 0xca, 0xc8, 0x22, 0x1a, 0xee, 0x5f, 0xe7, 0x31, 0x60, 0x22, 0x83, 0x08, 0x63, 0xce, 0xb9, 0x32, 0x44, 0x58, 0x5d, 0x3a, 0x9b, 0xe4, 0x04, 0xd5, 0xef, 0x38, 0xef, 0x4b, 0xdd, 0x19}}}, -{{{0x4d, 0xc2, 0x17, 0x75, 0xa1, 0x68, 0xcd, 0xc3, 0xc6, 0x03, 0x44, 0xe3, 0x78, 0x09, 0x91, 0x47, 0x3f, 0x0f, 0xe4, 0x92, 0x58, 0xfa, 0x7d, 0x1f, 0x20, 0x94, 0x58, 0x5e, 0xbc, 0x19, 0x02, 0x6f}} , - {{0x20, 0xd6, 0xd8, 0x91, 0x54, 0xa7, 0xf3, 0x20, 0x4b, 0x34, 0x06, 0xfa, 0x30, 0xc8, 0x6f, 0x14, 0x10, 0x65, 0x74, 0x13, 0x4e, 0xf0, 0x69, 0x26, 0xce, 0xcf, 0x90, 0xf4, 0xd0, 0xc5, 0xc8, 0x64}}}, -{{{0x26, 0xa2, 0x50, 0x02, 0x24, 0x72, 0xf1, 0xf0, 0x4e, 0x2d, 0x93, 0xd5, 0x08, 0xe7, 0xae, 0x38, 0xf7, 0x18, 0xa5, 0x32, 0x34, 0xc2, 0xf0, 0xa6, 0xec, 0xb9, 0x61, 0x7b, 0x64, 0x99, 0xac, 0x71}} , - {{0x25, 0xcf, 0x74, 0x55, 0x1b, 0xaa, 0xa9, 0x38, 0x41, 0x40, 0xd5, 0x95, 0x95, 0xab, 0x1c, 0x5e, 0xbc, 0x41, 0x7e, 0x14, 0x30, 0xbe, 0x13, 0x89, 0xf4, 0xe5, 0xeb, 0x28, 0xc0, 0xc2, 0x96, 0x3a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x2b, 0x77, 0x45, 0xec, 0x67, 0x76, 0x32, 0x4c, 0xb9, 0xdf, 0x25, 0x32, 0x6b, 0xcb, 0xe7, 0x14, 0x61, 0x43, 0xee, 0xba, 0x9b, 0x71, 0xef, 0xd2, 0x48, 0x65, 0xbb, 0x1b, 0x8a, 0x13, 0x1b, 0x22}} , - {{0x84, 0xad, 0x0c, 0x18, 0x38, 0x5a, 0xba, 0xd0, 0x98, 0x59, 0xbf, 0x37, 0xb0, 0x4f, 0x97, 0x60, 0x20, 0xb3, 0x9b, 0x97, 0xf6, 0x08, 0x6c, 0xa4, 0xff, 0xfb, 0xb7, 0xfa, 0x95, 0xb2, 0x51, 0x79}}}, -{{{0x28, 0x5c, 0x3f, 0xdb, 0x6b, 0x18, 0x3b, 0x5c, 0xd1, 0x04, 0x28, 0xde, 0x85, 0x52, 0x31, 0xb5, 0xbb, 0xf6, 0xa9, 0xed, 0xbe, 0x28, 0x4f, 0xb3, 0x7e, 0x05, 0x6a, 0xdb, 0x95, 0x0d, 0x1b, 0x1c}} , - {{0xd5, 0xc5, 0xc3, 0x9a, 0x0a, 0xd0, 0x31, 0x3e, 0x07, 0x36, 0x8e, 0xc0, 0x8a, 0x62, 0xb1, 0xca, 0xd6, 0x0e, 0x1e, 0x9d, 0xef, 0xab, 0x98, 0x4d, 0xbb, 0x6c, 0x05, 0xe0, 0xe4, 0x5d, 0xbd, 0x57}}}, -{{{0xcc, 0x21, 0x27, 0xce, 0xfd, 0xa9, 0x94, 0x8e, 0xe1, 0xab, 0x49, 0xe0, 0x46, 0x26, 0xa1, 0xa8, 0x8c, 0xa1, 0x99, 0x1d, 0xb4, 0x27, 0x6d, 0x2d, 0xc8, 0x39, 0x30, 0x5e, 0x37, 0x52, 0xc4, 0x6e}} , - {{0xa9, 0x85, 0xf4, 0xe7, 0xb0, 0x15, 0x33, 0x84, 0x1b, 0x14, 0x1a, 0x02, 0xd9, 0x3b, 0xad, 0x0f, 0x43, 0x6c, 0xea, 0x3e, 0x0f, 0x7e, 0xda, 0xdd, 0x6b, 0x4c, 0x7f, 0x6e, 0xd4, 0x6b, 0xbf, 0x0f}}}, -{{{0x47, 0x9f, 0x7c, 0x56, 0x7c, 0x43, 0x91, 0x1c, 0xbb, 0x4e, 0x72, 0x3e, 0x64, 0xab, 0xa0, 0xa0, 0xdf, 0xb4, 0xd8, 0x87, 0x3a, 0xbd, 0xa8, 0x48, 0xc9, 0xb8, 0xef, 0x2e, 0xad, 0x6f, 0x84, 0x4f}} , - {{0x2d, 0x2d, 0xf0, 0x1b, 0x7e, 0x2a, 0x6c, 0xf8, 0xa9, 0x6a, 0xe1, 0xf0, 0x99, 0xa1, 0x67, 0x9a, 0xd4, 0x13, 0xca, 0xca, 0xba, 0x27, 0x92, 0xaa, 0xa1, 0x5d, 0x50, 0xde, 0xcc, 0x40, 0x26, 0x0a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x9f, 0x3e, 0xf2, 0xb2, 0x90, 0xce, 0xdb, 0x64, 0x3e, 0x03, 0xdd, 0x37, 0x36, 0x54, 0x70, 0x76, 0x24, 0xb5, 0x69, 0x03, 0xfc, 0xa0, 0x2b, 0x74, 0xb2, 0x05, 0x0e, 0xcc, 0xd8, 0x1f, 0x6a, 0x1f}} , - {{0x19, 0x5e, 0x60, 0x69, 0x58, 0x86, 0xa0, 0x31, 0xbd, 0x32, 0xe9, 0x2c, 0x5c, 0xd2, 0x85, 0xba, 0x40, 0x64, 0xa8, 0x74, 0xf8, 0x0e, 0x1c, 0xb3, 0xa9, 0x69, 0xe8, 0x1e, 0x40, 0x64, 0x99, 0x77}}}, -{{{0x6c, 0x32, 0x4f, 0xfd, 0xbb, 0x5c, 0xbb, 0x8d, 0x64, 0x66, 0x4a, 0x71, 0x1f, 0x79, 0xa3, 0xad, 0x8d, 0xf9, 0xd4, 0xec, 0xcf, 0x67, 0x70, 0xfa, 0x05, 0x4a, 0x0f, 0x6e, 0xaf, 0x87, 0x0a, 0x6f}} , - {{0xc6, 0x36, 0x6e, 0x6c, 0x8c, 0x24, 0x09, 0x60, 0xbe, 0x26, 0xd2, 0x4c, 0x5e, 0x17, 0xca, 0x5f, 0x1d, 0xcc, 0x87, 0xe8, 0x42, 0x6a, 0xcb, 0xcb, 0x7d, 0x92, 0x05, 0x35, 0x81, 0x13, 0x60, 0x6b}}}, -{{{0xf4, 0x15, 0xcd, 0x0f, 0x0a, 0xaf, 0x4e, 0x6b, 0x51, 0xfd, 0x14, 0xc4, 0x2e, 0x13, 0x86, 0x74, 0x44, 0xcb, 0x66, 0x6b, 0xb6, 0x9d, 0x74, 0x56, 0x32, 0xac, 0x8d, 0x8e, 0x8c, 0x8c, 0x8c, 0x39}} , - {{0xca, 0x59, 0x74, 0x1a, 0x11, 0xef, 0x6d, 0xf7, 0x39, 0x5c, 0x3b, 0x1f, 0xfa, 0xe3, 0x40, 0x41, 0x23, 0x9e, 0xf6, 0xd1, 0x21, 0xa2, 0xbf, 0xad, 0x65, 0x42, 0x6b, 0x59, 0x8a, 0xe8, 0xc5, 0x7f}}}, -{{{0x64, 0x05, 0x7a, 0x84, 0x4a, 0x13, 0xc3, 0xf6, 0xb0, 0x6e, 0x9a, 0x6b, 0x53, 0x6b, 0x32, 0xda, 0xd9, 0x74, 0x75, 0xc4, 0xba, 0x64, 0x3d, 0x3b, 0x08, 0xdd, 0x10, 0x46, 0xef, 0xc7, 0x90, 0x1f}} , - {{0x7b, 0x2f, 0x3a, 0xce, 0xc8, 0xa1, 0x79, 0x3c, 0x30, 0x12, 0x44, 0x28, 0xf6, 0xbc, 0xff, 0xfd, 0xf4, 0xc0, 0x97, 0xb0, 0xcc, 0xc3, 0x13, 0x7a, 0xb9, 0x9a, 0x16, 0xe4, 0xcb, 0x4c, 0x34, 0x63}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x07, 0x4e, 0xd3, 0x2d, 0x09, 0x33, 0x0e, 0xd2, 0x0d, 0xbe, 0x3e, 0xe7, 0xe4, 0xaa, 0xb7, 0x00, 0x8b, 0xe8, 0xad, 0xaa, 0x7a, 0x8d, 0x34, 0x28, 0xa9, 0x81, 0x94, 0xc5, 0xe7, 0x42, 0xac, 0x47}} , - {{0x24, 0x89, 0x7a, 0x8f, 0xb5, 0x9b, 0xf0, 0xc2, 0x03, 0x64, 0xd0, 0x1e, 0xf5, 0xa4, 0xb2, 0xf3, 0x74, 0xe9, 0x1a, 0x16, 0xfd, 0xcb, 0x15, 0xea, 0xeb, 0x10, 0x6c, 0x35, 0xd1, 0xc1, 0xa6, 0x28}}}, -{{{0xcc, 0xd5, 0x39, 0xfc, 0xa5, 0xa4, 0xad, 0x32, 0x15, 0xce, 0x19, 0xe8, 0x34, 0x2b, 0x1c, 0x60, 0x91, 0xfc, 0x05, 0xa9, 0xb3, 0xdc, 0x80, 0x29, 0xc4, 0x20, 0x79, 0x06, 0x39, 0xc0, 0xe2, 0x22}} , - {{0xbb, 0xa8, 0xe1, 0x89, 0x70, 0x57, 0x18, 0x54, 0x3c, 0xf6, 0x0d, 0x82, 0x12, 0x05, 0x87, 0x96, 0x06, 0x39, 0xe3, 0xf8, 0xb3, 0x95, 0xe5, 0xd7, 0x26, 0xbf, 0x09, 0x5a, 0x94, 0xf9, 0x1c, 0x63}}}, -{{{0x2b, 0x8c, 0x2d, 0x9a, 0x8b, 0x84, 0xf2, 0x56, 0xfb, 0xad, 0x2e, 0x7f, 0xb7, 0xfc, 0x30, 0xe1, 0x35, 0x89, 0xba, 0x4d, 0xa8, 0x6d, 0xce, 0x8c, 0x8b, 0x30, 0xe0, 0xda, 0x29, 0x18, 0x11, 0x17}} , - {{0x19, 0xa6, 0x5a, 0x65, 0x93, 0xc3, 0xb5, 0x31, 0x22, 0x4f, 0xf3, 0xf6, 0x0f, 0xeb, 0x28, 0xc3, 0x7c, 0xeb, 0xce, 0x86, 0xec, 0x67, 0x76, 0x6e, 0x35, 0x45, 0x7b, 0xd8, 0x6b, 0x92, 0x01, 0x65}}}, -{{{0x3d, 0xd5, 0x9a, 0x64, 0x73, 0x36, 0xb1, 0xd6, 0x86, 0x98, 0x42, 0x3f, 0x8a, 0xf1, 0xc7, 0xf5, 0x42, 0xa8, 0x9c, 0x52, 0xa8, 0xdc, 0xf9, 0x24, 0x3f, 0x4a, 0xa1, 0xa4, 0x5b, 0xe8, 0x62, 0x1a}} , - {{0xc5, 0xbd, 0xc8, 0x14, 0xd5, 0x0d, 0xeb, 0xe1, 0xa5, 0xe6, 0x83, 0x11, 0x09, 0x00, 0x1d, 0x55, 0x83, 0x51, 0x7e, 0x75, 0x00, 0x81, 0xb9, 0xcb, 0xd8, 0xc5, 0xe5, 0xa1, 0xd9, 0x17, 0x6d, 0x1f}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xea, 0xf9, 0xe4, 0xe9, 0xe1, 0x52, 0x3f, 0x51, 0x19, 0x0d, 0xdd, 0xd9, 0x9d, 0x93, 0x31, 0x87, 0x23, 0x09, 0xd5, 0x83, 0xeb, 0x92, 0x09, 0x76, 0x6e, 0xe3, 0xf8, 0xc0, 0xa2, 0x66, 0xb5, 0x36}} , - {{0x3a, 0xbb, 0x39, 0xed, 0x32, 0x02, 0xe7, 0x43, 0x7a, 0x38, 0x14, 0x84, 0xe3, 0x44, 0xd2, 0x5e, 0x94, 0xdd, 0x78, 0x89, 0x55, 0x4c, 0x73, 0x9e, 0xe1, 0xe4, 0x3e, 0x43, 0xd0, 0x4a, 0xde, 0x1b}}}, -{{{0xb2, 0xe7, 0x8f, 0xe3, 0xa3, 0xc5, 0xcb, 0x72, 0xee, 0x79, 0x41, 0xf8, 0xdf, 0xee, 0x65, 0xc5, 0x45, 0x77, 0x27, 0x3c, 0xbd, 0x58, 0xd3, 0x75, 0xe2, 0x04, 0x4b, 0xbb, 0x65, 0xf3, 0xc8, 0x0f}} , - {{0x24, 0x7b, 0x93, 0x34, 0xb5, 0xe2, 0x74, 0x48, 0xcd, 0xa0, 0x0b, 0x92, 0x97, 0x66, 0x39, 0xf4, 0xb0, 0xe2, 0x5d, 0x39, 0x6a, 0x5b, 0x45, 0x17, 0x78, 0x1e, 0xdb, 0x91, 0x81, 0x1c, 0xf9, 0x16}}}, -{{{0x16, 0xdf, 0xd1, 0x5a, 0xd5, 0xe9, 0x4e, 0x58, 0x95, 0x93, 0x5f, 0x51, 0x09, 0xc3, 0x2a, 0xc9, 0xd4, 0x55, 0x48, 0x79, 0xa4, 0xa3, 0xb2, 0xc3, 0x62, 0xaa, 0x8c, 0xe8, 0xad, 0x47, 0x39, 0x1b}} , - {{0x46, 0xda, 0x9e, 0x51, 0x3a, 0xe6, 0xd1, 0xa6, 0xbb, 0x4d, 0x7b, 0x08, 0xbe, 0x8c, 0xd5, 0xf3, 0x3f, 0xfd, 0xf7, 0x44, 0x80, 0x2d, 0x53, 0x4b, 0xd0, 0x87, 0x68, 0xc1, 0xb5, 0xd8, 0xf7, 0x07}}}, -{{{0xf4, 0x10, 0x46, 0xbe, 0xb7, 0xd2, 0xd1, 0xce, 0x5e, 0x76, 0xa2, 0xd7, 0x03, 0xdc, 0xe4, 0x81, 0x5a, 0xf6, 0x3c, 0xde, 0xae, 0x7a, 0x9d, 0x21, 0x34, 0xa5, 0xf6, 0xa9, 0x73, 0xe2, 0x8d, 0x60}} , - {{0xfa, 0x44, 0x71, 0xf6, 0x41, 0xd8, 0xc6, 0x58, 0x13, 0x37, 0xeb, 0x84, 0x0f, 0x96, 0xc7, 0xdc, 0xc8, 0xa9, 0x7a, 0x83, 0xb2, 0x2f, 0x31, 0xb1, 0x1a, 0xd8, 0x98, 0x3f, 0x11, 0xd0, 0x31, 0x3b}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x81, 0xd5, 0x34, 0x16, 0x01, 0xa3, 0x93, 0xea, 0x52, 0x94, 0xec, 0x93, 0xb7, 0x81, 0x11, 0x2d, 0x58, 0xf9, 0xb5, 0x0a, 0xaa, 0x4f, 0xf6, 0x2e, 0x3f, 0x36, 0xbf, 0x33, 0x5a, 0xe7, 0xd1, 0x08}} , - {{0x1a, 0xcf, 0x42, 0xae, 0xcc, 0xb5, 0x77, 0x39, 0xc4, 0x5b, 0x5b, 0xd0, 0x26, 0x59, 0x27, 0xd0, 0x55, 0x71, 0x12, 0x9d, 0x88, 0x3d, 0x9c, 0xea, 0x41, 0x6a, 0xf0, 0x50, 0x93, 0x93, 0xdd, 0x47}}}, -{{{0x6f, 0xc9, 0x51, 0x6d, 0x1c, 0xaa, 0xf5, 0xa5, 0x90, 0x3f, 0x14, 0xe2, 0x6e, 0x8e, 0x64, 0xfd, 0xac, 0xe0, 0x4e, 0x22, 0xe5, 0xc1, 0xbc, 0x29, 0x0a, 0x6a, 0x9e, 0xa1, 0x60, 0xcb, 0x2f, 0x0b}} , - {{0xdc, 0x39, 0x32, 0xf3, 0xa1, 0x44, 0xe9, 0xc5, 0xc3, 0x78, 0xfb, 0x95, 0x47, 0x34, 0x35, 0x34, 0xe8, 0x25, 0xde, 0x93, 0xc6, 0xb4, 0x76, 0x6d, 0x86, 0x13, 0xc6, 0xe9, 0x68, 0xb5, 0x01, 0x63}}}, -{{{0x1f, 0x9a, 0x52, 0x64, 0x97, 0xd9, 0x1c, 0x08, 0x51, 0x6f, 0x26, 0x9d, 0xaa, 0x93, 0x33, 0x43, 0xfa, 0x77, 0xe9, 0x62, 0x9b, 0x5d, 0x18, 0x75, 0xeb, 0x78, 0xf7, 0x87, 0x8f, 0x41, 0xb4, 0x4d}} , - {{0x13, 0xa8, 0x82, 0x3e, 0xe9, 0x13, 0xad, 0xeb, 0x01, 0xca, 0xcf, 0xda, 0xcd, 0xf7, 0x6c, 0xc7, 0x7a, 0xdc, 0x1e, 0x6e, 0xc8, 0x4e, 0x55, 0x62, 0x80, 0xea, 0x78, 0x0c, 0x86, 0xb9, 0x40, 0x51}}}, -{{{0x27, 0xae, 0xd3, 0x0d, 0x4c, 0x8f, 0x34, 0xea, 0x7d, 0x3c, 0xe5, 0x8a, 0xcf, 0x5b, 0x92, 0xd8, 0x30, 0x16, 0xb4, 0xa3, 0x75, 0xff, 0xeb, 0x27, 0xc8, 0x5c, 0x6c, 0xc2, 0xee, 0x6c, 0x21, 0x0b}} , - {{0xc3, 0xba, 0x12, 0x53, 0x2a, 0xaa, 0x77, 0xad, 0x19, 0x78, 0x55, 0x8a, 0x2e, 0x60, 0x87, 0xc2, 0x6e, 0x91, 0x38, 0x91, 0x3f, 0x7a, 0xc5, 0x24, 0x8f, 0x51, 0xc5, 0xde, 0xb0, 0x53, 0x30, 0x56}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x02, 0xfe, 0x54, 0x12, 0x18, 0xca, 0x7d, 0xa5, 0x68, 0x43, 0xa3, 0x6d, 0x14, 0x2a, 0x6a, 0xa5, 0x8e, 0x32, 0xe7, 0x63, 0x4f, 0xe3, 0xc6, 0x44, 0x3e, 0xab, 0x63, 0xca, 0x17, 0x86, 0x74, 0x3f}} , - {{0x1e, 0x64, 0xc1, 0x7d, 0x52, 0xdc, 0x13, 0x5a, 0xa1, 0x9c, 0x4e, 0xee, 0x99, 0x28, 0xbb, 0x4c, 0xee, 0xac, 0xa9, 0x1b, 0x89, 0xa2, 0x38, 0x39, 0x7b, 0xc4, 0x0f, 0x42, 0xe6, 0x89, 0xed, 0x0f}}}, -{{{0xf3, 0x3c, 0x8c, 0x80, 0x83, 0x10, 0x8a, 0x37, 0x50, 0x9c, 0xb4, 0xdf, 0x3f, 0x8c, 0xf7, 0x23, 0x07, 0xd6, 0xff, 0xa0, 0x82, 0x6c, 0x75, 0x3b, 0xe4, 0xb5, 0xbb, 0xe4, 0xe6, 0x50, 0xf0, 0x08}} , - {{0x62, 0xee, 0x75, 0x48, 0x92, 0x33, 0xf2, 0xf4, 0xad, 0x15, 0x7a, 0xa1, 0x01, 0x46, 0xa9, 0x32, 0x06, 0x88, 0xb6, 0x36, 0x47, 0x35, 0xb9, 0xb4, 0x42, 0x85, 0x76, 0xf0, 0x48, 0x00, 0x90, 0x38}}}, -{{{0x51, 0x15, 0x9d, 0xc3, 0x95, 0xd1, 0x39, 0xbb, 0x64, 0x9d, 0x15, 0x81, 0xc1, 0x68, 0xd0, 0xb6, 0xa4, 0x2c, 0x7d, 0x5e, 0x02, 0x39, 0x00, 0xe0, 0x3b, 0xa4, 0xcc, 0xca, 0x1d, 0x81, 0x24, 0x10}} , - {{0xe7, 0x29, 0xf9, 0x37, 0xd9, 0x46, 0x5a, 0xcd, 0x70, 0xfe, 0x4d, 0x5b, 0xbf, 0xa5, 0xcf, 0x91, 0xf4, 0xef, 0xee, 0x8a, 0x29, 0xd0, 0xe7, 0xc4, 0x25, 0x92, 0x8a, 0xff, 0x36, 0xfc, 0xe4, 0x49}}}, -{{{0xbd, 0x00, 0xb9, 0x04, 0x7d, 0x35, 0xfc, 0xeb, 0xd0, 0x0b, 0x05, 0x32, 0x52, 0x7a, 0x89, 0x24, 0x75, 0x50, 0xe1, 0x63, 0x02, 0x82, 0x8e, 0xe7, 0x85, 0x0c, 0xf2, 0x56, 0x44, 0x37, 0x83, 0x25}} , - {{0x8f, 0xa1, 0xce, 0xcb, 0x60, 0xda, 0x12, 0x02, 0x1e, 0x29, 0x39, 0x2a, 0x03, 0xb7, 0xeb, 0x77, 0x40, 0xea, 0xc9, 0x2b, 0x2c, 0xd5, 0x7d, 0x7e, 0x2c, 0xc7, 0x5a, 0xfd, 0xff, 0xc4, 0xd1, 0x62}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x1d, 0x88, 0x98, 0x5b, 0x4e, 0xfc, 0x41, 0x24, 0x05, 0xe6, 0x50, 0x2b, 0xae, 0x96, 0x51, 0xd9, 0x6b, 0x72, 0xb2, 0x33, 0x42, 0x98, 0x68, 0xbb, 0x10, 0x5a, 0x7a, 0x8c, 0x9d, 0x07, 0xb4, 0x05}} , - {{0x2f, 0x61, 0x9f, 0xd7, 0xa8, 0x3f, 0x83, 0x8c, 0x10, 0x69, 0x90, 0xe6, 0xcf, 0xd2, 0x63, 0xa3, 0xe4, 0x54, 0x7e, 0xe5, 0x69, 0x13, 0x1c, 0x90, 0x57, 0xaa, 0xe9, 0x53, 0x22, 0x43, 0x29, 0x23}}}, -{{{0xe5, 0x1c, 0xf8, 0x0a, 0xfd, 0x2d, 0x7e, 0xf5, 0xf5, 0x70, 0x7d, 0x41, 0x6b, 0x11, 0xfe, 0xbe, 0x99, 0xd1, 0x55, 0x29, 0x31, 0xbf, 0xc0, 0x97, 0x6c, 0xd5, 0x35, 0xcc, 0x5e, 0x8b, 0xd9, 0x69}} , - {{0x8e, 0x4e, 0x9f, 0x25, 0xf8, 0x81, 0x54, 0x2d, 0x0e, 0xd5, 0x54, 0x81, 0x9b, 0xa6, 0x92, 0xce, 0x4b, 0xe9, 0x8f, 0x24, 0x3b, 0xca, 0xe0, 0x44, 0xab, 0x36, 0xfe, 0xfb, 0x87, 0xd4, 0x26, 0x3e}}}, -{{{0x0f, 0x93, 0x9c, 0x11, 0xe7, 0xdb, 0xf1, 0xf0, 0x85, 0x43, 0x28, 0x15, 0x37, 0xdd, 0xde, 0x27, 0xdf, 0xad, 0x3e, 0x49, 0x4f, 0xe0, 0x5b, 0xf6, 0x80, 0x59, 0x15, 0x3c, 0x85, 0xb7, 0x3e, 0x12}} , - {{0xf5, 0xff, 0xcc, 0xf0, 0xb4, 0x12, 0x03, 0x5f, 0xc9, 0x84, 0xcb, 0x1d, 0x17, 0xe0, 0xbc, 0xcc, 0x03, 0x62, 0xa9, 0x8b, 0x94, 0xa6, 0xaa, 0x18, 0xcb, 0x27, 0x8d, 0x49, 0xa6, 0x17, 0x15, 0x07}}}, -{{{0xd9, 0xb6, 0xd4, 0x9d, 0xd4, 0x6a, 0xaf, 0x70, 0x07, 0x2c, 0x10, 0x9e, 0xbd, 0x11, 0xad, 0xe4, 0x26, 0x33, 0x70, 0x92, 0x78, 0x1c, 0x74, 0x9f, 0x75, 0x60, 0x56, 0xf4, 0x39, 0xa8, 0xa8, 0x62}} , - {{0x3b, 0xbf, 0x55, 0x35, 0x61, 0x8b, 0x44, 0x97, 0xe8, 0x3a, 0x55, 0xc1, 0xc8, 0x3b, 0xfd, 0x95, 0x29, 0x11, 0x60, 0x96, 0x1e, 0xcb, 0x11, 0x9d, 0xc2, 0x03, 0x8a, 0x1b, 0xc6, 0xd6, 0x45, 0x3d}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x7e, 0x0e, 0x50, 0xb2, 0xcc, 0x0d, 0x6b, 0xa6, 0x71, 0x5b, 0x42, 0xed, 0xbd, 0xaf, 0xac, 0xf0, 0xfc, 0x12, 0xa2, 0x3f, 0x4e, 0xda, 0xe8, 0x11, 0xf3, 0x23, 0xe1, 0x04, 0x62, 0x03, 0x1c, 0x4e}} , - {{0xc8, 0xb1, 0x1b, 0x6f, 0x73, 0x61, 0x3d, 0x27, 0x0d, 0x7d, 0x7a, 0x25, 0x5f, 0x73, 0x0e, 0x2f, 0x93, 0xf6, 0x24, 0xd8, 0x4f, 0x90, 0xac, 0xa2, 0x62, 0x0a, 0xf0, 0x61, 0xd9, 0x08, 0x59, 0x6a}}}, -{{{0x6f, 0x2d, 0x55, 0xf8, 0x2f, 0x8e, 0xf0, 0x18, 0x3b, 0xea, 0xdd, 0x26, 0x72, 0xd1, 0xf5, 0xfe, 0xe5, 0xb8, 0xe6, 0xd3, 0x10, 0x48, 0x46, 0x49, 0x3a, 0x9f, 0x5e, 0x45, 0x6b, 0x90, 0xe8, 0x7f}} , - {{0xd3, 0x76, 0x69, 0x33, 0x7b, 0xb9, 0x40, 0x70, 0xee, 0xa6, 0x29, 0x6b, 0xdd, 0xd0, 0x5d, 0x8d, 0xc1, 0x3e, 0x4a, 0xea, 0x37, 0xb1, 0x03, 0x02, 0x03, 0x35, 0xf1, 0x28, 0x9d, 0xff, 0x00, 0x13}}}, -{{{0x7a, 0xdb, 0x12, 0xd2, 0x8a, 0x82, 0x03, 0x1b, 0x1e, 0xaf, 0xf9, 0x4b, 0x9c, 0xbe, 0xae, 0x7c, 0xe4, 0x94, 0x2a, 0x23, 0xb3, 0x62, 0x86, 0xe7, 0xfd, 0x23, 0xaa, 0x99, 0xbd, 0x2b, 0x11, 0x6c}} , - {{0x8d, 0xa6, 0xd5, 0xac, 0x9d, 0xcc, 0x68, 0x75, 0x7f, 0xc3, 0x4d, 0x4b, 0xdd, 0x6c, 0xbb, 0x11, 0x5a, 0x60, 0xe5, 0xbd, 0x7d, 0x27, 0x8b, 0xda, 0xb4, 0x95, 0xf6, 0x03, 0x27, 0xa4, 0x92, 0x3f}}}, -{{{0x22, 0xd6, 0xb5, 0x17, 0x84, 0xbf, 0x12, 0xcc, 0x23, 0x14, 0x4a, 0xdf, 0x14, 0x31, 0xbc, 0xa1, 0xac, 0x6e, 0xab, 0xfa, 0x57, 0x11, 0x53, 0xb3, 0x27, 0xe6, 0xf9, 0x47, 0x33, 0x44, 0x34, 0x1e}} , - {{0x79, 0xfc, 0xa6, 0xb4, 0x0b, 0x35, 0x20, 0xc9, 0x4d, 0x22, 0x84, 0xc4, 0xa9, 0x20, 0xec, 0x89, 0x94, 0xba, 0x66, 0x56, 0x48, 0xb9, 0x87, 0x7f, 0xca, 0x1e, 0x06, 0xed, 0xa5, 0x55, 0x59, 0x29}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x56, 0xe1, 0xf5, 0xf1, 0xd5, 0xab, 0xa8, 0x2b, 0xae, 0x89, 0xf3, 0xcf, 0x56, 0x9f, 0xf2, 0x4b, 0x31, 0xbc, 0x18, 0xa9, 0x06, 0x5b, 0xbe, 0xb4, 0x61, 0xf8, 0xb2, 0x06, 0x9c, 0x81, 0xab, 0x4c}} , - {{0x1f, 0x68, 0x76, 0x01, 0x16, 0x38, 0x2b, 0x0f, 0x77, 0x97, 0x92, 0x67, 0x4e, 0x86, 0x6a, 0x8b, 0xe5, 0xe8, 0x0c, 0xf7, 0x36, 0x39, 0xb5, 0x33, 0xe6, 0xcf, 0x5e, 0xbd, 0x18, 0xfb, 0x10, 0x1f}}}, -{{{0x83, 0xf0, 0x0d, 0x63, 0xef, 0x53, 0x6b, 0xb5, 0x6b, 0xf9, 0x83, 0xcf, 0xde, 0x04, 0x22, 0x9b, 0x2c, 0x0a, 0xe0, 0xa5, 0xd8, 0xc7, 0x9c, 0xa5, 0xa3, 0xf6, 0x6f, 0xcf, 0x90, 0x6b, 0x68, 0x7c}} , - {{0x33, 0x15, 0xd7, 0x7f, 0x1a, 0xd5, 0x21, 0x58, 0xc4, 0x18, 0xa5, 0xf0, 0xcc, 0x73, 0xa8, 0xfd, 0xfa, 0x18, 0xd1, 0x03, 0x91, 0x8d, 0x52, 0xd2, 0xa3, 0xa4, 0xd3, 0xb1, 0xea, 0x1d, 0x0f, 0x00}}}, -{{{0xcc, 0x48, 0x83, 0x90, 0xe5, 0xfd, 0x3f, 0x84, 0xaa, 0xf9, 0x8b, 0x82, 0x59, 0x24, 0x34, 0x68, 0x4f, 0x1c, 0x23, 0xd9, 0xcc, 0x71, 0xe1, 0x7f, 0x8c, 0xaf, 0xf1, 0xee, 0x00, 0xb6, 0xa0, 0x77}} , - {{0xf5, 0x1a, 0x61, 0xf7, 0x37, 0x9d, 0x00, 0xf4, 0xf2, 0x69, 0x6f, 0x4b, 0x01, 0x85, 0x19, 0x45, 0x4d, 0x7f, 0x02, 0x7c, 0x6a, 0x05, 0x47, 0x6c, 0x1f, 0x81, 0x20, 0xd4, 0xe8, 0x50, 0x27, 0x72}}}, -{{{0x2c, 0x3a, 0xe5, 0xad, 0xf4, 0xdd, 0x2d, 0xf7, 0x5c, 0x44, 0xb5, 0x5b, 0x21, 0xa3, 0x89, 0x5f, 0x96, 0x45, 0xca, 0x4d, 0xa4, 0x21, 0x99, 0x70, 0xda, 0xc4, 0xc4, 0xa0, 0xe5, 0xf4, 0xec, 0x0a}} , - {{0x07, 0x68, 0x21, 0x65, 0xe9, 0x08, 0xa0, 0x0b, 0x6a, 0x4a, 0xba, 0xb5, 0x80, 0xaf, 0xd0, 0x1b, 0xc5, 0xf5, 0x4b, 0x73, 0x50, 0x60, 0x2d, 0x71, 0x69, 0x61, 0x0e, 0xc0, 0x20, 0x40, 0x30, 0x19}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xd0, 0x75, 0x57, 0x3b, 0xeb, 0x5c, 0x14, 0x56, 0x50, 0xc9, 0x4f, 0xb8, 0xb8, 0x1e, 0xa3, 0xf4, 0xab, 0xf5, 0xa9, 0x20, 0x15, 0x94, 0x82, 0xda, 0x96, 0x1c, 0x9b, 0x59, 0x8c, 0xff, 0xf4, 0x51}} , - {{0xc1, 0x3a, 0x86, 0xd7, 0xb0, 0x06, 0x84, 0x7f, 0x1b, 0xbd, 0xd4, 0x07, 0x78, 0x80, 0x2e, 0xb1, 0xb4, 0xee, 0x52, 0x38, 0xee, 0x9a, 0xf9, 0xf6, 0xf3, 0x41, 0x6e, 0xd4, 0x88, 0x95, 0xac, 0x35}}}, -{{{0x41, 0x97, 0xbf, 0x71, 0x6a, 0x9b, 0x72, 0xec, 0xf3, 0xf8, 0x6b, 0xe6, 0x0e, 0x6c, 0x69, 0xa5, 0x2f, 0x68, 0x52, 0xd8, 0x61, 0x81, 0xc0, 0x63, 0x3f, 0xa6, 0x3c, 0x13, 0x90, 0xe6, 0x8d, 0x56}} , - {{0xe8, 0x39, 0x30, 0x77, 0x23, 0xb1, 0xfd, 0x1b, 0x3d, 0x3e, 0x74, 0x4d, 0x7f, 0xae, 0x5b, 0x3a, 0xb4, 0x65, 0x0e, 0x3a, 0x43, 0xdc, 0xdc, 0x41, 0x47, 0xe6, 0xe8, 0x92, 0x09, 0x22, 0x48, 0x4c}}}, -{{{0x85, 0x57, 0x9f, 0xb5, 0xc8, 0x06, 0xb2, 0x9f, 0x47, 0x3f, 0xf0, 0xfa, 0xe6, 0xa9, 0xb1, 0x9b, 0x6f, 0x96, 0x7d, 0xf9, 0xa4, 0x65, 0x09, 0x75, 0x32, 0xa6, 0x6c, 0x7f, 0x47, 0x4b, 0x2f, 0x4f}} , - {{0x34, 0xe9, 0x59, 0x93, 0x9d, 0x26, 0x80, 0x54, 0xf2, 0xcc, 0x3c, 0xc2, 0x25, 0x85, 0xe3, 0x6a, 0xc1, 0x62, 0x04, 0xa7, 0x08, 0x32, 0x6d, 0xa1, 0x39, 0x84, 0x8a, 0x3b, 0x87, 0x5f, 0x11, 0x13}}}, -{{{0xda, 0x03, 0x34, 0x66, 0xc4, 0x0c, 0x73, 0x6e, 0xbc, 0x24, 0xb5, 0xf9, 0x70, 0x81, 0x52, 0xe9, 0xf4, 0x7c, 0x23, 0xdd, 0x9f, 0xb8, 0x46, 0xef, 0x1d, 0x22, 0x55, 0x7d, 0x71, 0xc4, 0x42, 0x33}} , - {{0xc5, 0x37, 0x69, 0x5b, 0xa8, 0xc6, 0x9d, 0xa4, 0xfc, 0x61, 0x6e, 0x68, 0x46, 0xea, 0xd7, 0x1c, 0x67, 0xd2, 0x7d, 0xfa, 0xf1, 0xcc, 0x54, 0x8d, 0x36, 0x35, 0xc9, 0x00, 0xdf, 0x6c, 0x67, 0x50}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x9a, 0x4d, 0x42, 0x29, 0x5d, 0xa4, 0x6b, 0x6f, 0xa8, 0x8a, 0x4d, 0x91, 0x7b, 0xd2, 0xdf, 0x36, 0xef, 0x01, 0x22, 0xc5, 0xcc, 0x8d, 0xeb, 0x58, 0x3d, 0xb3, 0x50, 0xfc, 0x8b, 0x97, 0x96, 0x33}} , - {{0x93, 0x33, 0x07, 0xc8, 0x4a, 0xca, 0xd0, 0xb1, 0xab, 0xbd, 0xdd, 0xa7, 0x7c, 0xac, 0x3e, 0x45, 0xcb, 0xcc, 0x07, 0x91, 0xbf, 0x35, 0x9d, 0xcb, 0x7d, 0x12, 0x3c, 0x11, 0x59, 0x13, 0xcf, 0x5c}}}, -{{{0x45, 0xb8, 0x41, 0xd7, 0xab, 0x07, 0x15, 0x00, 0x8e, 0xce, 0xdf, 0xb2, 0x43, 0x5c, 0x01, 0xdc, 0xf4, 0x01, 0x51, 0x95, 0x10, 0x5a, 0xf6, 0x24, 0x24, 0xa0, 0x19, 0x3a, 0x09, 0x2a, 0xaa, 0x3f}} , - {{0xdc, 0x8e, 0xeb, 0xc6, 0xbf, 0xdd, 0x11, 0x7b, 0xe7, 0x47, 0xe6, 0xce, 0xe7, 0xb6, 0xc5, 0xe8, 0x8a, 0xdc, 0x4b, 0x57, 0x15, 0x3b, 0x66, 0xca, 0x89, 0xa3, 0xfd, 0xac, 0x0d, 0xe1, 0x1d, 0x7a}}}, -{{{0x89, 0xef, 0xbf, 0x03, 0x75, 0xd0, 0x29, 0x50, 0xcb, 0x7d, 0xd6, 0xbe, 0xad, 0x5f, 0x7b, 0x00, 0x32, 0xaa, 0x98, 0xed, 0x3f, 0x8f, 0x92, 0xcb, 0x81, 0x56, 0x01, 0x63, 0x64, 0xa3, 0x38, 0x39}} , - {{0x8b, 0xa4, 0xd6, 0x50, 0xb4, 0xaa, 0x5d, 0x64, 0x64, 0x76, 0x2e, 0xa1, 0xa6, 0xb3, 0xb8, 0x7c, 0x7a, 0x56, 0xf5, 0x5c, 0x4e, 0x84, 0x5c, 0xfb, 0xdd, 0xca, 0x48, 0x8b, 0x48, 0xb9, 0xba, 0x34}}}, -{{{0xc5, 0xe3, 0xe8, 0xae, 0x17, 0x27, 0xe3, 0x64, 0x60, 0x71, 0x47, 0x29, 0x02, 0x0f, 0x92, 0x5d, 0x10, 0x93, 0xc8, 0x0e, 0xa1, 0xed, 0xba, 0xa9, 0x96, 0x1c, 0xc5, 0x76, 0x30, 0xcd, 0xf9, 0x30}} , - {{0x95, 0xb0, 0xbd, 0x8c, 0xbc, 0xa7, 0x4f, 0x7e, 0xfd, 0x4e, 0x3a, 0xbf, 0x5f, 0x04, 0x79, 0x80, 0x2b, 0x5a, 0x9f, 0x4f, 0x68, 0x21, 0x19, 0x71, 0xc6, 0x20, 0x01, 0x42, 0xaa, 0xdf, 0xae, 0x2c}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x90, 0x6e, 0x7e, 0x4b, 0x71, 0x93, 0xc0, 0x72, 0xed, 0xeb, 0x71, 0x24, 0x97, 0x26, 0x9c, 0xfe, 0xcb, 0x3e, 0x59, 0x19, 0xa8, 0x0f, 0x75, 0x7d, 0xbe, 0x18, 0xe6, 0x96, 0x1e, 0x95, 0x70, 0x60}} , - {{0x89, 0x66, 0x3e, 0x1d, 0x4c, 0x5f, 0xfe, 0xc0, 0x04, 0x43, 0xd6, 0x44, 0x19, 0xb5, 0xad, 0xc7, 0x22, 0xdc, 0x71, 0x28, 0x64, 0xde, 0x41, 0x38, 0x27, 0x8f, 0x2c, 0x6b, 0x08, 0xb8, 0xb8, 0x7b}}}, -{{{0x3d, 0x70, 0x27, 0x9d, 0xd9, 0xaf, 0xb1, 0x27, 0xaf, 0xe3, 0x5d, 0x1e, 0x3a, 0x30, 0x54, 0x61, 0x60, 0xe8, 0xc3, 0x26, 0x3a, 0xbc, 0x7e, 0xf5, 0x81, 0xdd, 0x64, 0x01, 0x04, 0xeb, 0xc0, 0x1e}} , - {{0xda, 0x2c, 0xa4, 0xd1, 0xa1, 0xc3, 0x5c, 0x6e, 0x32, 0x07, 0x1f, 0xb8, 0x0e, 0x19, 0x9e, 0x99, 0x29, 0x33, 0x9a, 0xae, 0x7a, 0xed, 0x68, 0x42, 0x69, 0x7c, 0x07, 0xb3, 0x38, 0x2c, 0xf6, 0x3d}}}, -{{{0x64, 0xaa, 0xb5, 0x88, 0x79, 0x65, 0x38, 0x8c, 0x94, 0xd6, 0x62, 0x37, 0x7d, 0x64, 0xcd, 0x3a, 0xeb, 0xff, 0xe8, 0x81, 0x09, 0xc7, 0x6a, 0x50, 0x09, 0x0d, 0x28, 0x03, 0x0d, 0x9a, 0x93, 0x0a}} , - {{0x42, 0xa3, 0xf1, 0xc5, 0xb4, 0x0f, 0xd8, 0xc8, 0x8d, 0x15, 0x31, 0xbd, 0xf8, 0x07, 0x8b, 0xcd, 0x08, 0x8a, 0xfb, 0x18, 0x07, 0xfe, 0x8e, 0x52, 0x86, 0xef, 0xbe, 0xec, 0x49, 0x52, 0x99, 0x08}}}, -{{{0x0f, 0xa9, 0xd5, 0x01, 0xaa, 0x48, 0x4f, 0x28, 0x66, 0x32, 0x1a, 0xba, 0x7c, 0xea, 0x11, 0x80, 0x17, 0x18, 0x9b, 0x56, 0x88, 0x25, 0x06, 0x69, 0x12, 0x2c, 0xea, 0x56, 0x69, 0x41, 0x24, 0x19}} , - {{0xde, 0x21, 0xf0, 0xda, 0x8a, 0xfb, 0xb1, 0xb8, 0xcd, 0xc8, 0x6a, 0x82, 0x19, 0x73, 0xdb, 0xc7, 0xcf, 0x88, 0xeb, 0x96, 0xee, 0x6f, 0xfb, 0x06, 0xd2, 0xcd, 0x7d, 0x7b, 0x12, 0x28, 0x8e, 0x0c}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x93, 0x44, 0x97, 0xce, 0x28, 0xff, 0x3a, 0x40, 0xc4, 0xf5, 0xf6, 0x9b, 0xf4, 0x6b, 0x07, 0x84, 0xfb, 0x98, 0xd8, 0xec, 0x8c, 0x03, 0x57, 0xec, 0x49, 0xed, 0x63, 0xb6, 0xaa, 0xff, 0x98, 0x28}} , - {{0x3d, 0x16, 0x35, 0xf3, 0x46, 0xbc, 0xb3, 0xf4, 0xc6, 0xb6, 0x4f, 0xfa, 0xf4, 0xa0, 0x13, 0xe6, 0x57, 0x45, 0x93, 0xb9, 0xbc, 0xd6, 0x59, 0xe7, 0x77, 0x94, 0x6c, 0xab, 0x96, 0x3b, 0x4f, 0x09}}}, -{{{0x5a, 0xf7, 0x6b, 0x01, 0x12, 0x4f, 0x51, 0xc1, 0x70, 0x84, 0x94, 0x47, 0xb2, 0x01, 0x6c, 0x71, 0xd7, 0xcc, 0x17, 0x66, 0x0f, 0x59, 0x5d, 0x5d, 0x10, 0x01, 0x57, 0x11, 0xf5, 0xdd, 0xe2, 0x34}} , - {{0x26, 0xd9, 0x1f, 0x5c, 0x58, 0xac, 0x8b, 0x03, 0xd2, 0xc3, 0x85, 0x0f, 0x3a, 0xc3, 0x7f, 0x6d, 0x8e, 0x86, 0xcd, 0x52, 0x74, 0x8f, 0x55, 0x77, 0x17, 0xb7, 0x8e, 0xb7, 0x88, 0xea, 0xda, 0x1b}}}, -{{{0xb6, 0xea, 0x0e, 0x40, 0x93, 0x20, 0x79, 0x35, 0x6a, 0x61, 0x84, 0x5a, 0x07, 0x6d, 0xf9, 0x77, 0x6f, 0xed, 0x69, 0x1c, 0x0d, 0x25, 0x76, 0xcc, 0xf0, 0xdb, 0xbb, 0xc5, 0xad, 0xe2, 0x26, 0x57}} , - {{0xcf, 0xe8, 0x0e, 0x6b, 0x96, 0x7d, 0xed, 0x27, 0xd1, 0x3c, 0xa9, 0xd9, 0x50, 0xa9, 0x98, 0x84, 0x5e, 0x86, 0xef, 0xd6, 0xf0, 0xf8, 0x0e, 0x89, 0x05, 0x2f, 0xd9, 0x5f, 0x15, 0x5f, 0x73, 0x79}}}, -{{{0xc8, 0x5c, 0x16, 0xfe, 0xed, 0x9f, 0x26, 0x56, 0xf6, 0x4b, 0x9f, 0xa7, 0x0a, 0x85, 0xfe, 0xa5, 0x8c, 0x87, 0xdd, 0x98, 0xce, 0x4e, 0xc3, 0x58, 0x55, 0xb2, 0x7b, 0x3d, 0xd8, 0x6b, 0xb5, 0x4c}} , - {{0x65, 0x38, 0xa0, 0x15, 0xfa, 0xa7, 0xb4, 0x8f, 0xeb, 0xc4, 0x86, 0x9b, 0x30, 0xa5, 0x5e, 0x4d, 0xea, 0x8a, 0x9a, 0x9f, 0x1a, 0xd8, 0x5b, 0x53, 0x14, 0x19, 0x25, 0x63, 0xb4, 0x6f, 0x1f, 0x5d}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xac, 0x8f, 0xbc, 0x1e, 0x7d, 0x8b, 0x5a, 0x0b, 0x8d, 0xaf, 0x76, 0x2e, 0x71, 0xe3, 0x3b, 0x6f, 0x53, 0x2f, 0x3e, 0x90, 0x95, 0xd4, 0x35, 0x14, 0x4f, 0x8c, 0x3c, 0xce, 0x57, 0x1c, 0x76, 0x49}} , - {{0xa8, 0x50, 0xe1, 0x61, 0x6b, 0x57, 0x35, 0xeb, 0x44, 0x0b, 0x0c, 0x6e, 0xf9, 0x25, 0x80, 0x74, 0xf2, 0x8f, 0x6f, 0x7a, 0x3e, 0x7f, 0x2d, 0xf3, 0x4e, 0x09, 0x65, 0x10, 0x5e, 0x03, 0x25, 0x32}}}, -{{{0xa9, 0x60, 0xdc, 0x0f, 0x64, 0xe5, 0x1d, 0xe2, 0x8d, 0x4f, 0x79, 0x2f, 0x0e, 0x24, 0x02, 0x00, 0x05, 0x77, 0x43, 0x25, 0x3d, 0x6a, 0xc7, 0xb7, 0xbf, 0x04, 0x08, 0x65, 0xf4, 0x39, 0x4b, 0x65}} , - {{0x96, 0x19, 0x12, 0x6b, 0x6a, 0xb7, 0xe3, 0xdc, 0x45, 0x9b, 0xdb, 0xb4, 0xa8, 0xae, 0xdc, 0xa8, 0x14, 0x44, 0x65, 0x62, 0xce, 0x34, 0x9a, 0x84, 0x18, 0x12, 0x01, 0xf1, 0xe2, 0x7b, 0xce, 0x50}}}, -{{{0x41, 0x21, 0x30, 0x53, 0x1b, 0x47, 0x01, 0xb7, 0x18, 0xd8, 0x82, 0x57, 0xbd, 0xa3, 0x60, 0xf0, 0x32, 0xf6, 0x5b, 0xf0, 0x30, 0x88, 0x91, 0x59, 0xfd, 0x90, 0xa2, 0xb9, 0x55, 0x93, 0x21, 0x34}} , - {{0x97, 0x67, 0x9e, 0xeb, 0x6a, 0xf9, 0x6e, 0xd6, 0x73, 0xe8, 0x6b, 0x29, 0xec, 0x63, 0x82, 0x00, 0xa8, 0x99, 0x1c, 0x1d, 0x30, 0xc8, 0x90, 0x52, 0x90, 0xb6, 0x6a, 0x80, 0x4e, 0xff, 0x4b, 0x51}}}, -{{{0x0f, 0x7d, 0x63, 0x8c, 0x6e, 0x5c, 0xde, 0x30, 0xdf, 0x65, 0xfa, 0x2e, 0xb0, 0xa3, 0x25, 0x05, 0x54, 0xbd, 0x25, 0xba, 0x06, 0xae, 0xdf, 0x8b, 0xd9, 0x1b, 0xea, 0x38, 0xb3, 0x05, 0x16, 0x09}} , - {{0xc7, 0x8c, 0xbf, 0x64, 0x28, 0xad, 0xf8, 0xa5, 0x5a, 0x6f, 0xc9, 0xba, 0xd5, 0x7f, 0xd5, 0xd6, 0xbd, 0x66, 0x2f, 0x3d, 0xaa, 0x54, 0xf6, 0xba, 0x32, 0x22, 0x9a, 0x1e, 0x52, 0x05, 0xf4, 0x1d}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xaa, 0x1f, 0xbb, 0xeb, 0xfe, 0xe4, 0x87, 0xfc, 0xb1, 0x2c, 0xb7, 0x88, 0xf4, 0xc6, 0xb9, 0xf5, 0x24, 0x46, 0xf2, 0xa5, 0x9f, 0x8f, 0x8a, 0x93, 0x70, 0x69, 0xd4, 0x56, 0xec, 0xfd, 0x06, 0x46}} , - {{0x4e, 0x66, 0xcf, 0x4e, 0x34, 0xce, 0x0c, 0xd9, 0xa6, 0x50, 0xd6, 0x5e, 0x95, 0xaf, 0xe9, 0x58, 0xfa, 0xee, 0x9b, 0xb8, 0xa5, 0x0f, 0x35, 0xe0, 0x43, 0x82, 0x6d, 0x65, 0xe6, 0xd9, 0x00, 0x0f}}}, -{{{0x7b, 0x75, 0x3a, 0xfc, 0x64, 0xd3, 0x29, 0x7e, 0xdd, 0x49, 0x9a, 0x59, 0x53, 0xbf, 0xb4, 0xa7, 0x52, 0xb3, 0x05, 0xab, 0xc3, 0xaf, 0x16, 0x1a, 0x85, 0x42, 0x32, 0xa2, 0x86, 0xfa, 0x39, 0x43}} , - {{0x0e, 0x4b, 0xa3, 0x63, 0x8a, 0xfe, 0xa5, 0x58, 0xf1, 0x13, 0xbd, 0x9d, 0xaa, 0x7f, 0x76, 0x40, 0x70, 0x81, 0x10, 0x75, 0x99, 0xbb, 0xbe, 0x0b, 0x16, 0xe9, 0xba, 0x62, 0x34, 0xcc, 0x07, 0x6d}}}, -{{{0xc3, 0xf1, 0xc6, 0x93, 0x65, 0xee, 0x0b, 0xbc, 0xea, 0x14, 0xf0, 0xc1, 0xf8, 0x84, 0x89, 0xc2, 0xc9, 0xd7, 0xea, 0x34, 0xca, 0xa7, 0xc4, 0x99, 0xd5, 0x50, 0x69, 0xcb, 0xd6, 0x21, 0x63, 0x7c}} , - {{0x99, 0xeb, 0x7c, 0x31, 0x73, 0x64, 0x67, 0x7f, 0x0c, 0x66, 0xaa, 0x8c, 0x69, 0x91, 0xe2, 0x26, 0xd3, 0x23, 0xe2, 0x76, 0x5d, 0x32, 0x52, 0xdf, 0x5d, 0xc5, 0x8f, 0xb7, 0x7c, 0x84, 0xb3, 0x70}}}, -{{{0xeb, 0x01, 0xc7, 0x36, 0x97, 0x4e, 0xb6, 0xab, 0x5f, 0x0d, 0x2c, 0xba, 0x67, 0x64, 0x55, 0xde, 0xbc, 0xff, 0xa6, 0xec, 0x04, 0xd3, 0x8d, 0x39, 0x56, 0x5e, 0xee, 0xf8, 0xe4, 0x2e, 0x33, 0x62}} , - {{0x65, 0xef, 0xb8, 0x9f, 0xc8, 0x4b, 0xa7, 0xfd, 0x21, 0x49, 0x9b, 0x92, 0x35, 0x82, 0xd6, 0x0a, 0x9b, 0xf2, 0x79, 0xf1, 0x47, 0x2f, 0x6a, 0x7e, 0x9f, 0xcf, 0x18, 0x02, 0x3c, 0xfb, 0x1b, 0x3e}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x2f, 0x8b, 0xc8, 0x40, 0x51, 0xd1, 0xac, 0x1a, 0x0b, 0xe4, 0xa9, 0xa2, 0x42, 0x21, 0x19, 0x2f, 0x7b, 0x97, 0xbf, 0xf7, 0x57, 0x6d, 0x3f, 0x3d, 0x4f, 0x0f, 0xe2, 0xb2, 0x81, 0x00, 0x9e, 0x7b}} , - {{0x8c, 0x85, 0x2b, 0xc4, 0xfc, 0xf1, 0xab, 0xe8, 0x79, 0x22, 0xc4, 0x84, 0x17, 0x3a, 0xfa, 0x86, 0xa6, 0x7d, 0xf9, 0xf3, 0x6f, 0x03, 0x57, 0x20, 0x4d, 0x79, 0xf9, 0x6e, 0x71, 0x54, 0x38, 0x09}}}, -{{{0x40, 0x29, 0x74, 0xa8, 0x2f, 0x5e, 0xf9, 0x79, 0xa4, 0xf3, 0x3e, 0xb9, 0xfd, 0x33, 0x31, 0xac, 0x9a, 0x69, 0x88, 0x1e, 0x77, 0x21, 0x2d, 0xf3, 0x91, 0x52, 0x26, 0x15, 0xb2, 0xa6, 0xcf, 0x7e}} , - {{0xc6, 0x20, 0x47, 0x6c, 0xa4, 0x7d, 0xcb, 0x63, 0xea, 0x5b, 0x03, 0xdf, 0x3e, 0x88, 0x81, 0x6d, 0xce, 0x07, 0x42, 0x18, 0x60, 0x7e, 0x7b, 0x55, 0xfe, 0x6a, 0xf3, 0xda, 0x5c, 0x8b, 0x95, 0x10}}}, -{{{0x62, 0xe4, 0x0d, 0x03, 0xb4, 0xd7, 0xcd, 0xfa, 0xbd, 0x46, 0xdf, 0x93, 0x71, 0x10, 0x2c, 0xa8, 0x3b, 0xb6, 0x09, 0x05, 0x70, 0x84, 0x43, 0x29, 0xa8, 0x59, 0xf5, 0x8e, 0x10, 0xe4, 0xd7, 0x20}} , - {{0x57, 0x82, 0x1c, 0xab, 0xbf, 0x62, 0x70, 0xe8, 0xc4, 0xcf, 0xf0, 0x28, 0x6e, 0x16, 0x3c, 0x08, 0x78, 0x89, 0x85, 0x46, 0x0f, 0xf6, 0x7f, 0xcf, 0xcb, 0x7e, 0xb8, 0x25, 0xe9, 0x5a, 0xfa, 0x03}}}, -{{{0xfb, 0x95, 0x92, 0x63, 0x50, 0xfc, 0x62, 0xf0, 0xa4, 0x5e, 0x8c, 0x18, 0xc2, 0x17, 0x24, 0xb7, 0x78, 0xc2, 0xa9, 0xe7, 0x6a, 0x32, 0xd6, 0x29, 0x85, 0xaf, 0xcb, 0x8d, 0x91, 0x13, 0xda, 0x6b}} , - {{0x36, 0x0a, 0xc2, 0xb6, 0x4b, 0xa5, 0x5d, 0x07, 0x17, 0x41, 0x31, 0x5f, 0x62, 0x46, 0xf8, 0x92, 0xf9, 0x66, 0x48, 0x73, 0xa6, 0x97, 0x0d, 0x7d, 0x88, 0xee, 0x62, 0xb1, 0x03, 0xa8, 0x3f, 0x2c}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x4a, 0xb1, 0x70, 0x8a, 0xa9, 0xe8, 0x63, 0x79, 0x00, 0xe2, 0x25, 0x16, 0xca, 0x4b, 0x0f, 0xa4, 0x66, 0xad, 0x19, 0x9f, 0x88, 0x67, 0x0c, 0x8b, 0xc2, 0x4a, 0x5b, 0x2b, 0x6d, 0x95, 0xaf, 0x19}} , - {{0x8b, 0x9d, 0xb6, 0xcc, 0x60, 0xb4, 0x72, 0x4f, 0x17, 0x69, 0x5a, 0x4a, 0x68, 0x34, 0xab, 0xa1, 0x45, 0x32, 0x3c, 0x83, 0x87, 0x72, 0x30, 0x54, 0x77, 0x68, 0xae, 0xfb, 0xb5, 0x8b, 0x22, 0x5e}}}, -{{{0xf1, 0xb9, 0x87, 0x35, 0xc5, 0xbb, 0xb9, 0xcf, 0xf5, 0xd6, 0xcd, 0xd5, 0x0c, 0x7c, 0x0e, 0xe6, 0x90, 0x34, 0xfb, 0x51, 0x42, 0x1e, 0x6d, 0xac, 0x9a, 0x46, 0xc4, 0x97, 0x29, 0x32, 0xbf, 0x45}} , - {{0x66, 0x9e, 0xc6, 0x24, 0xc0, 0xed, 0xa5, 0x5d, 0x88, 0xd4, 0xf0, 0x73, 0x97, 0x7b, 0xea, 0x7f, 0x42, 0xff, 0x21, 0xa0, 0x9b, 0x2f, 0x9a, 0xfd, 0x53, 0x57, 0x07, 0x84, 0x48, 0x88, 0x9d, 0x52}}}, -{{{0xc6, 0x96, 0x48, 0x34, 0x2a, 0x06, 0xaf, 0x94, 0x3d, 0xf4, 0x1a, 0xcf, 0xf2, 0xc0, 0x21, 0xc2, 0x42, 0x5e, 0xc8, 0x2f, 0x35, 0xa2, 0x3e, 0x29, 0xfa, 0x0c, 0x84, 0xe5, 0x89, 0x72, 0x7c, 0x06}} , - {{0x32, 0x65, 0x03, 0xe5, 0x89, 0xa6, 0x6e, 0xb3, 0x5b, 0x8e, 0xca, 0xeb, 0xfe, 0x22, 0x56, 0x8b, 0x5d, 0x14, 0x4b, 0x4d, 0xf9, 0xbe, 0xb5, 0xf5, 0xe6, 0x5c, 0x7b, 0x8b, 0xf4, 0x13, 0x11, 0x34}}}, -{{{0x07, 0xc6, 0x22, 0x15, 0xe2, 0x9c, 0x60, 0xa2, 0x19, 0xd9, 0x27, 0xae, 0x37, 0x4e, 0xa6, 0xc9, 0x80, 0xa6, 0x91, 0x8f, 0x12, 0x49, 0xe5, 0x00, 0x18, 0x47, 0xd1, 0xd7, 0x28, 0x22, 0x63, 0x39}} , - {{0xe8, 0xe2, 0x00, 0x7e, 0xf2, 0x9e, 0x1e, 0x99, 0x39, 0x95, 0x04, 0xbd, 0x1e, 0x67, 0x7b, 0xb2, 0x26, 0xac, 0xe6, 0xaa, 0xe2, 0x46, 0xd5, 0xe4, 0xe8, 0x86, 0xbd, 0xab, 0x7c, 0x55, 0x59, 0x6f}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x24, 0x64, 0x6e, 0x9b, 0x35, 0x71, 0x78, 0xce, 0x33, 0x03, 0x21, 0x33, 0x36, 0xf1, 0x73, 0x9b, 0xb9, 0x15, 0x8b, 0x2c, 0x69, 0xcf, 0x4d, 0xed, 0x4f, 0x4d, 0x57, 0x14, 0x13, 0x82, 0xa4, 0x4d}} , - {{0x65, 0x6e, 0x0a, 0xa4, 0x59, 0x07, 0x17, 0xf2, 0x6b, 0x4a, 0x1f, 0x6e, 0xf6, 0xb5, 0xbc, 0x62, 0xe4, 0xb6, 0xda, 0xa2, 0x93, 0xbc, 0x29, 0x05, 0xd2, 0xd2, 0x73, 0x46, 0x03, 0x16, 0x40, 0x31}}}, -{{{0x4c, 0x73, 0x6d, 0x15, 0xbd, 0xa1, 0x4d, 0x5c, 0x13, 0x0b, 0x24, 0x06, 0x98, 0x78, 0x1c, 0x5b, 0xeb, 0x1f, 0x18, 0x54, 0x43, 0xd9, 0x55, 0x66, 0xda, 0x29, 0x21, 0xe8, 0xb8, 0x3c, 0x42, 0x22}} , - {{0xb4, 0xcd, 0x08, 0x6f, 0x15, 0x23, 0x1a, 0x0b, 0x22, 0xed, 0xd1, 0xf1, 0xa7, 0xc7, 0x73, 0x45, 0xf3, 0x9e, 0xce, 0x76, 0xb7, 0xf6, 0x39, 0xb6, 0x8e, 0x79, 0xbe, 0xe9, 0x9b, 0xcf, 0x7d, 0x62}}}, -{{{0x92, 0x5b, 0xfc, 0x72, 0xfd, 0xba, 0xf1, 0xfd, 0xa6, 0x7c, 0x95, 0xe3, 0x61, 0x3f, 0xe9, 0x03, 0xd4, 0x2b, 0xd4, 0x20, 0xd9, 0xdb, 0x4d, 0x32, 0x3e, 0xf5, 0x11, 0x64, 0xe3, 0xb4, 0xbe, 0x32}} , - {{0x86, 0x17, 0x90, 0xe7, 0xc9, 0x1f, 0x10, 0xa5, 0x6a, 0x2d, 0x39, 0xd0, 0x3b, 0xc4, 0xa6, 0xe9, 0x59, 0x13, 0xda, 0x1a, 0xe6, 0xa0, 0xb9, 0x3c, 0x50, 0xb8, 0x40, 0x7c, 0x15, 0x36, 0x5a, 0x42}}}, -{{{0xb4, 0x0b, 0x32, 0xab, 0xdc, 0x04, 0x51, 0x55, 0x21, 0x1e, 0x0b, 0x75, 0x99, 0x89, 0x73, 0x35, 0x3a, 0x91, 0x2b, 0xfe, 0xe7, 0x49, 0xea, 0x76, 0xc1, 0xf9, 0x46, 0xb9, 0x53, 0x02, 0x23, 0x04}} , - {{0xfc, 0x5a, 0x1e, 0x1d, 0x74, 0x58, 0x95, 0xa6, 0x8f, 0x7b, 0x97, 0x3e, 0x17, 0x3b, 0x79, 0x2d, 0xa6, 0x57, 0xef, 0x45, 0x02, 0x0b, 0x4d, 0x6e, 0x9e, 0x93, 0x8d, 0x2f, 0xd9, 0x9d, 0xdb, 0x04}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xc0, 0xd7, 0x56, 0x97, 0x58, 0x91, 0xde, 0x09, 0x4f, 0x9f, 0xbe, 0x63, 0xb0, 0x83, 0x86, 0x43, 0x5d, 0xbc, 0xe0, 0xf3, 0xc0, 0x75, 0xbf, 0x8b, 0x8e, 0xaa, 0xf7, 0x8b, 0x64, 0x6e, 0xb0, 0x63}} , - {{0x16, 0xae, 0x8b, 0xe0, 0x9b, 0x24, 0x68, 0x5c, 0x44, 0xc2, 0xd0, 0x08, 0xb7, 0x7b, 0x62, 0xfd, 0x7f, 0xd8, 0xd4, 0xb7, 0x50, 0xfd, 0x2c, 0x1b, 0xbf, 0x41, 0x95, 0xd9, 0x8e, 0xd8, 0x17, 0x1b}}}, -{{{0x86, 0x55, 0x37, 0x8e, 0xc3, 0x38, 0x48, 0x14, 0xb5, 0x97, 0xd2, 0xa7, 0x54, 0x45, 0xf1, 0x35, 0x44, 0x38, 0x9e, 0xf1, 0x1b, 0xb6, 0x34, 0x00, 0x3c, 0x96, 0xee, 0x29, 0x00, 0xea, 0x2c, 0x0b}} , - {{0xea, 0xda, 0x99, 0x9e, 0x19, 0x83, 0x66, 0x6d, 0xe9, 0x76, 0x87, 0x50, 0xd1, 0xfd, 0x3c, 0x60, 0x87, 0xc6, 0x41, 0xd9, 0x8e, 0xdb, 0x5e, 0xde, 0xaa, 0x9a, 0xd3, 0x28, 0xda, 0x95, 0xea, 0x47}}}, -{{{0xd0, 0x80, 0xba, 0x19, 0xae, 0x1d, 0xa9, 0x79, 0xf6, 0x3f, 0xac, 0x5d, 0x6f, 0x96, 0x1f, 0x2a, 0xce, 0x29, 0xb2, 0xff, 0x37, 0xf1, 0x94, 0x8f, 0x0c, 0xb5, 0x28, 0xba, 0x9a, 0x21, 0xf6, 0x66}} , - {{0x02, 0xfb, 0x54, 0xb8, 0x05, 0xf3, 0x81, 0x52, 0x69, 0x34, 0x46, 0x9d, 0x86, 0x76, 0x8f, 0xd7, 0xf8, 0x6a, 0x66, 0xff, 0xe6, 0xa7, 0x90, 0xf7, 0x5e, 0xcd, 0x6a, 0x9b, 0x55, 0xfc, 0x9d, 0x48}}}, -{{{0xbd, 0xaa, 0x13, 0xe6, 0xcd, 0x45, 0x4a, 0xa4, 0x59, 0x0a, 0x64, 0xb1, 0x98, 0xd6, 0x34, 0x13, 0x04, 0xe6, 0x97, 0x94, 0x06, 0xcb, 0xd4, 0x4e, 0xbb, 0x96, 0xcd, 0xd1, 0x57, 0xd1, 0xe3, 0x06}} , - {{0x7a, 0x6c, 0x45, 0x27, 0xc4, 0x93, 0x7f, 0x7d, 0x7c, 0x62, 0x50, 0x38, 0x3a, 0x6b, 0xb5, 0x88, 0xc6, 0xd9, 0xf1, 0x78, 0x19, 0xb9, 0x39, 0x93, 0x3d, 0xc9, 0xe0, 0x9c, 0x3c, 0xce, 0xf5, 0x72}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x24, 0xea, 0x23, 0x7d, 0x56, 0x2c, 0xe2, 0x59, 0x0e, 0x85, 0x60, 0x04, 0x88, 0x5a, 0x74, 0x1e, 0x4b, 0xef, 0x13, 0xda, 0x4c, 0xff, 0x83, 0x45, 0x85, 0x3f, 0x08, 0x95, 0x2c, 0x20, 0x13, 0x1f}} , - {{0x48, 0x5f, 0x27, 0x90, 0x5c, 0x02, 0x42, 0xad, 0x78, 0x47, 0x5c, 0xb5, 0x7e, 0x08, 0x85, 0x00, 0xfa, 0x7f, 0xfd, 0xfd, 0xe7, 0x09, 0x11, 0xf2, 0x7e, 0x1b, 0x38, 0x6c, 0x35, 0x6d, 0x33, 0x66}}}, -{{{0x93, 0x03, 0x36, 0x81, 0xac, 0xe4, 0x20, 0x09, 0x35, 0x4c, 0x45, 0xb2, 0x1e, 0x4c, 0x14, 0x21, 0xe6, 0xe9, 0x8a, 0x7b, 0x8d, 0xfe, 0x1e, 0xc6, 0x3e, 0xc1, 0x35, 0xfa, 0xe7, 0x70, 0x4e, 0x1d}} , - {{0x61, 0x2e, 0xc2, 0xdd, 0x95, 0x57, 0xd1, 0xab, 0x80, 0xe8, 0x63, 0x17, 0xb5, 0x48, 0xe4, 0x8a, 0x11, 0x9e, 0x72, 0xbe, 0x85, 0x8d, 0x51, 0x0a, 0xf2, 0x9f, 0xe0, 0x1c, 0xa9, 0x07, 0x28, 0x7b}}}, -{{{0xbb, 0x71, 0x14, 0x5e, 0x26, 0x8c, 0x3d, 0xc8, 0xe9, 0x7c, 0xd3, 0xd6, 0xd1, 0x2f, 0x07, 0x6d, 0xe6, 0xdf, 0xfb, 0x79, 0xd6, 0x99, 0x59, 0x96, 0x48, 0x40, 0x0f, 0x3a, 0x7b, 0xb2, 0xa0, 0x72}} , - {{0x4e, 0x3b, 0x69, 0xc8, 0x43, 0x75, 0x51, 0x6c, 0x79, 0x56, 0xe4, 0xcb, 0xf7, 0xa6, 0x51, 0xc2, 0x2c, 0x42, 0x0b, 0xd4, 0x82, 0x20, 0x1c, 0x01, 0x08, 0x66, 0xd7, 0xbf, 0x04, 0x56, 0xfc, 0x02}}}, -{{{0x24, 0xe8, 0xb7, 0x60, 0xae, 0x47, 0x80, 0xfc, 0xe5, 0x23, 0xe7, 0xc2, 0xc9, 0x85, 0xe6, 0x98, 0xa0, 0x29, 0x4e, 0xe1, 0x84, 0x39, 0x2d, 0x95, 0x2c, 0xf3, 0x45, 0x3c, 0xff, 0xaf, 0x27, 0x4c}} , - {{0x6b, 0xa6, 0xf5, 0x4b, 0x11, 0xbd, 0xba, 0x5b, 0x9e, 0xc4, 0xa4, 0x51, 0x1e, 0xbe, 0xd0, 0x90, 0x3a, 0x9c, 0xc2, 0x26, 0xb6, 0x1e, 0xf1, 0x95, 0x7d, 0xc8, 0x6d, 0x52, 0xe6, 0x99, 0x2c, 0x5f}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x85, 0xe0, 0x24, 0x32, 0xb4, 0xd1, 0xef, 0xfc, 0x69, 0xa2, 0xbf, 0x8f, 0x72, 0x2c, 0x95, 0xf6, 0xe4, 0x6e, 0x7d, 0x90, 0xf7, 0x57, 0x81, 0xa0, 0xf7, 0xda, 0xef, 0x33, 0x07, 0xe3, 0x6b, 0x78}} , - {{0x36, 0x27, 0x3e, 0xc6, 0x12, 0x07, 0xab, 0x4e, 0xbe, 0x69, 0x9d, 0xb3, 0xbe, 0x08, 0x7c, 0x2a, 0x47, 0x08, 0xfd, 0xd4, 0xcd, 0x0e, 0x27, 0x34, 0x5b, 0x98, 0x34, 0x2f, 0x77, 0x5f, 0x3a, 0x65}}}, -{{{0x13, 0xaa, 0x2e, 0x4c, 0xf0, 0x22, 0xb8, 0x6c, 0xb3, 0x19, 0x4d, 0xeb, 0x6b, 0xd0, 0xa4, 0xc6, 0x9c, 0xdd, 0xc8, 0x5b, 0x81, 0x57, 0x89, 0xdf, 0x33, 0xa9, 0x68, 0x49, 0x80, 0xe4, 0xfe, 0x21}} , - {{0x00, 0x17, 0x90, 0x30, 0xe9, 0xd3, 0x60, 0x30, 0x31, 0xc2, 0x72, 0x89, 0x7a, 0x36, 0xa5, 0xbd, 0x39, 0x83, 0x85, 0x50, 0xa1, 0x5d, 0x6c, 0x41, 0x1d, 0xb5, 0x2c, 0x07, 0x40, 0x77, 0x0b, 0x50}}}, -{{{0x64, 0x34, 0xec, 0xc0, 0x9e, 0x44, 0x41, 0xaf, 0xa0, 0x36, 0x05, 0x6d, 0xea, 0x30, 0x25, 0x46, 0x35, 0x24, 0x9d, 0x86, 0xbd, 0x95, 0xf1, 0x6a, 0x46, 0xd7, 0x94, 0x54, 0xf9, 0x3b, 0xbd, 0x5d}} , - {{0x77, 0x5b, 0xe2, 0x37, 0xc7, 0xe1, 0x7c, 0x13, 0x8c, 0x9f, 0x7b, 0x7b, 0x2a, 0xce, 0x42, 0xa3, 0xb9, 0x2a, 0x99, 0xa8, 0xc0, 0xd8, 0x3c, 0x86, 0xb0, 0xfb, 0xe9, 0x76, 0x77, 0xf7, 0xf5, 0x56}}}, -{{{0xdf, 0xb3, 0x46, 0x11, 0x6e, 0x13, 0xb7, 0x28, 0x4e, 0x56, 0xdd, 0xf1, 0xac, 0xad, 0x58, 0xc3, 0xf8, 0x88, 0x94, 0x5e, 0x06, 0x98, 0xa1, 0xe4, 0x6a, 0xfb, 0x0a, 0x49, 0x5d, 0x8a, 0xfe, 0x77}} , - {{0x46, 0x02, 0xf5, 0xa5, 0xaf, 0xc5, 0x75, 0x6d, 0xba, 0x45, 0x35, 0x0a, 0xfe, 0xc9, 0xac, 0x22, 0x91, 0x8d, 0x21, 0x95, 0x33, 0x03, 0xc0, 0x8a, 0x16, 0xf3, 0x39, 0xe0, 0x01, 0x0f, 0x53, 0x3c}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x34, 0x75, 0x37, 0x1f, 0x34, 0x4e, 0xa9, 0x1d, 0x68, 0x67, 0xf8, 0x49, 0x98, 0x96, 0xfc, 0x4c, 0x65, 0x97, 0xf7, 0x02, 0x4a, 0x52, 0x6c, 0x01, 0xbd, 0x48, 0xbb, 0x1b, 0xed, 0xa4, 0xe2, 0x53}} , - {{0x59, 0xd5, 0x9b, 0x5a, 0xa2, 0x90, 0xd3, 0xb8, 0x37, 0x4c, 0x55, 0x82, 0x28, 0x08, 0x0f, 0x7f, 0xaa, 0x81, 0x65, 0xe0, 0x0c, 0x52, 0xc9, 0xa3, 0x32, 0x27, 0x64, 0xda, 0xfd, 0x34, 0x23, 0x5a}}}, -{{{0xb5, 0xb0, 0x0c, 0x4d, 0xb3, 0x7b, 0x23, 0xc8, 0x1f, 0x8a, 0x39, 0x66, 0xe6, 0xba, 0x4c, 0x10, 0x37, 0xca, 0x9c, 0x7c, 0x05, 0x9e, 0xff, 0xc0, 0xf8, 0x8e, 0xb1, 0x8f, 0x6f, 0x67, 0x18, 0x26}} , - {{0x4b, 0x41, 0x13, 0x54, 0x23, 0x1a, 0xa4, 0x4e, 0xa9, 0x8b, 0x1e, 0x4b, 0xfc, 0x15, 0x24, 0xbb, 0x7e, 0xcb, 0xb6, 0x1e, 0x1b, 0xf5, 0xf2, 0xc8, 0x56, 0xec, 0x32, 0xa2, 0x60, 0x5b, 0xa0, 0x2a}}}, -{{{0xa4, 0x29, 0x47, 0x86, 0x2e, 0x92, 0x4f, 0x11, 0x4f, 0xf3, 0xb2, 0x5c, 0xd5, 0x3e, 0xa6, 0xb9, 0xc8, 0xe2, 0x33, 0x11, 0x1f, 0x01, 0x8f, 0xb0, 0x9b, 0xc7, 0xa5, 0xff, 0x83, 0x0f, 0x1e, 0x28}} , - {{0x1d, 0x29, 0x7a, 0xa1, 0xec, 0x8e, 0xb5, 0xad, 0xea, 0x02, 0x68, 0x60, 0x74, 0x29, 0x1c, 0xa5, 0xcf, 0xc8, 0x3b, 0x7d, 0x8b, 0x2b, 0x7c, 0xad, 0xa4, 0x40, 0x17, 0x51, 0x59, 0x7c, 0x2e, 0x5d}}}, -{{{0x0a, 0x6c, 0x4f, 0xbc, 0x3e, 0x32, 0xe7, 0x4a, 0x1a, 0x13, 0xc1, 0x49, 0x38, 0xbf, 0xf7, 0xc2, 0xd3, 0x8f, 0x6b, 0xad, 0x52, 0xf7, 0xcf, 0xbc, 0x27, 0xcb, 0x40, 0x67, 0x76, 0xcd, 0x6d, 0x56}} , - {{0xe5, 0xb0, 0x27, 0xad, 0xbe, 0x9b, 0xf2, 0xb5, 0x63, 0xde, 0x3a, 0x23, 0x95, 0xb7, 0x0a, 0x7e, 0xf3, 0x9e, 0x45, 0x6f, 0x19, 0x39, 0x75, 0x8f, 0x39, 0x3d, 0x0f, 0xc0, 0x9f, 0xf1, 0xe9, 0x51}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x88, 0xaa, 0x14, 0x24, 0x86, 0x94, 0x11, 0x12, 0x3e, 0x1a, 0xb5, 0xcc, 0xbb, 0xe0, 0x9c, 0xd5, 0x9c, 0x6d, 0xba, 0x58, 0x72, 0x8d, 0xfb, 0x22, 0x7b, 0x9f, 0x7c, 0x94, 0x30, 0xb3, 0x51, 0x21}} , - {{0xf6, 0x74, 0x3d, 0xf2, 0xaf, 0xd0, 0x1e, 0x03, 0x7c, 0x23, 0x6b, 0xc9, 0xfc, 0x25, 0x70, 0x90, 0xdc, 0x9a, 0xa4, 0xfb, 0x49, 0xfc, 0x3d, 0x0a, 0x35, 0x38, 0x6f, 0xe4, 0x7e, 0x50, 0x01, 0x2a}}}, -{{{0xd6, 0xe3, 0x96, 0x61, 0x3a, 0xfd, 0xef, 0x9b, 0x1f, 0x90, 0xa4, 0x24, 0x14, 0x5b, 0xc8, 0xde, 0x50, 0xb1, 0x1d, 0xaf, 0xe8, 0x55, 0x8a, 0x87, 0x0d, 0xfe, 0xaa, 0x3b, 0x82, 0x2c, 0x8d, 0x7b}} , - {{0x85, 0x0c, 0xaf, 0xf8, 0x83, 0x44, 0x49, 0xd9, 0x45, 0xcf, 0xf7, 0x48, 0xd9, 0x53, 0xb4, 0xf1, 0x65, 0xa0, 0xe1, 0xc3, 0xb3, 0x15, 0xed, 0x89, 0x9b, 0x4f, 0x62, 0xb3, 0x57, 0xa5, 0x45, 0x1c}}}, -{{{0x8f, 0x12, 0xea, 0xaf, 0xd1, 0x1f, 0x79, 0x10, 0x0b, 0xf6, 0xa3, 0x7b, 0xea, 0xac, 0x8b, 0x57, 0x32, 0x62, 0xe7, 0x06, 0x12, 0x51, 0xa0, 0x3b, 0x43, 0x5e, 0xa4, 0x20, 0x78, 0x31, 0xce, 0x0d}} , - {{0x84, 0x7c, 0xc2, 0xa6, 0x91, 0x23, 0xce, 0xbd, 0xdc, 0xf9, 0xce, 0xd5, 0x75, 0x30, 0x22, 0xe6, 0xf9, 0x43, 0x62, 0x0d, 0xf7, 0x75, 0x9d, 0x7f, 0x8c, 0xff, 0x7d, 0xe4, 0x72, 0xac, 0x9f, 0x1c}}}, -{{{0x88, 0xc1, 0x99, 0xd0, 0x3c, 0x1c, 0x5d, 0xb4, 0xef, 0x13, 0x0f, 0x90, 0xb9, 0x36, 0x2f, 0x95, 0x95, 0xc6, 0xdc, 0xde, 0x0a, 0x51, 0xe2, 0x8d, 0xf3, 0xbc, 0x51, 0xec, 0xdf, 0xb1, 0xa2, 0x5f}} , - {{0x2e, 0x68, 0xa1, 0x23, 0x7d, 0x9b, 0x40, 0x69, 0x85, 0x7b, 0x42, 0xbf, 0x90, 0x4b, 0xd6, 0x40, 0x2f, 0xd7, 0x52, 0x52, 0xb2, 0x21, 0xde, 0x64, 0xbd, 0x88, 0xc3, 0x6d, 0xa5, 0xfa, 0x81, 0x3f}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xfb, 0xfd, 0x47, 0x7b, 0x8a, 0x66, 0x9e, 0x79, 0x2e, 0x64, 0x82, 0xef, 0xf7, 0x21, 0xec, 0xf6, 0xd8, 0x86, 0x09, 0x31, 0x7c, 0xdd, 0x03, 0x6a, 0x58, 0xa0, 0x77, 0xb7, 0x9b, 0x8c, 0x87, 0x1f}} , - {{0x55, 0x47, 0xe4, 0xa8, 0x3d, 0x55, 0x21, 0x34, 0xab, 0x1d, 0xae, 0xe0, 0xf4, 0xea, 0xdb, 0xc5, 0xb9, 0x58, 0xbf, 0xc4, 0x2a, 0x89, 0x31, 0x1a, 0xf4, 0x2d, 0xe1, 0xca, 0x37, 0x99, 0x47, 0x59}}}, -{{{0xc7, 0xca, 0x63, 0xc1, 0x49, 0xa9, 0x35, 0x45, 0x55, 0x7e, 0xda, 0x64, 0x32, 0x07, 0x50, 0xf7, 0x32, 0xac, 0xde, 0x75, 0x58, 0x9b, 0x11, 0xb2, 0x3a, 0x1f, 0xf5, 0xf7, 0x79, 0x04, 0xe6, 0x08}} , - {{0x46, 0xfa, 0x22, 0x4b, 0xfa, 0xe1, 0xfe, 0x96, 0xfc, 0x67, 0xba, 0x67, 0x97, 0xc4, 0xe7, 0x1b, 0x86, 0x90, 0x5f, 0xee, 0xf4, 0x5b, 0x11, 0xb2, 0xcd, 0xad, 0xee, 0xc2, 0x48, 0x6c, 0x2b, 0x1b}}}, -{{{0xe3, 0x39, 0x62, 0xb4, 0x4f, 0x31, 0x04, 0xc9, 0xda, 0xd5, 0x73, 0x51, 0x57, 0xc5, 0xb8, 0xf3, 0xa3, 0x43, 0x70, 0xe4, 0x61, 0x81, 0x84, 0xe2, 0xbb, 0xbf, 0x4f, 0x9e, 0xa4, 0x5e, 0x74, 0x06}} , - {{0x29, 0xac, 0xff, 0x27, 0xe0, 0x59, 0xbe, 0x39, 0x9c, 0x0d, 0x83, 0xd7, 0x10, 0x0b, 0x15, 0xb7, 0xe1, 0xc2, 0x2c, 0x30, 0x73, 0x80, 0x3a, 0x7d, 0x5d, 0xab, 0x58, 0x6b, 0xc1, 0xf0, 0xf4, 0x22}}}, -{{{0xfe, 0x7f, 0xfb, 0x35, 0x7d, 0xc6, 0x01, 0x23, 0x28, 0xc4, 0x02, 0xac, 0x1f, 0x42, 0xb4, 0x9d, 0xfc, 0x00, 0x94, 0xa5, 0xee, 0xca, 0xda, 0x97, 0x09, 0x41, 0x77, 0x87, 0x5d, 0x7b, 0x87, 0x78}} , - {{0xf5, 0xfb, 0x90, 0x2d, 0x81, 0x19, 0x9e, 0x2f, 0x6d, 0x85, 0x88, 0x8c, 0x40, 0x5c, 0x77, 0x41, 0x4d, 0x01, 0x19, 0x76, 0x60, 0xe8, 0x4c, 0x48, 0xe4, 0x33, 0x83, 0x32, 0x6c, 0xb4, 0x41, 0x03}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xff, 0x10, 0xc2, 0x09, 0x4f, 0x6e, 0xf4, 0xd2, 0xdf, 0x7e, 0xca, 0x7b, 0x1c, 0x1d, 0xba, 0xa3, 0xb6, 0xda, 0x67, 0x33, 0xd4, 0x87, 0x36, 0x4b, 0x11, 0x20, 0x05, 0xa6, 0x29, 0xc1, 0x87, 0x17}} , - {{0xf6, 0x96, 0xca, 0x2f, 0xda, 0x38, 0xa7, 0x1b, 0xfc, 0xca, 0x7d, 0xfe, 0x08, 0x89, 0xe2, 0x47, 0x2b, 0x6a, 0x5d, 0x4b, 0xfa, 0xa1, 0xb4, 0xde, 0xb6, 0xc2, 0x31, 0x51, 0xf5, 0xe0, 0xa4, 0x0b}}}, -{{{0x5c, 0xe5, 0xc6, 0x04, 0x8e, 0x2b, 0x57, 0xbe, 0x38, 0x85, 0x23, 0xcb, 0xb7, 0xbe, 0x4f, 0xa9, 0xd3, 0x6e, 0x12, 0xaa, 0xd5, 0xb2, 0x2e, 0x93, 0x29, 0x9a, 0x4a, 0x88, 0x18, 0x43, 0xf5, 0x01}} , - {{0x50, 0xfc, 0xdb, 0xa2, 0x59, 0x21, 0x8d, 0xbd, 0x7e, 0x33, 0xae, 0x2f, 0x87, 0x1a, 0xd0, 0x97, 0xc7, 0x0d, 0x4d, 0x63, 0x01, 0xef, 0x05, 0x84, 0xec, 0x40, 0xdd, 0xa8, 0x0a, 0x4f, 0x70, 0x0b}}}, -{{{0x41, 0x69, 0x01, 0x67, 0x5c, 0xd3, 0x8a, 0xc5, 0xcf, 0x3f, 0xd1, 0x57, 0xd1, 0x67, 0x3e, 0x01, 0x39, 0xb5, 0xcb, 0x81, 0x56, 0x96, 0x26, 0xb6, 0xc2, 0xe7, 0x5c, 0xfb, 0x63, 0x97, 0x58, 0x06}} , - {{0x0c, 0x0e, 0xf3, 0xba, 0xf0, 0xe5, 0xba, 0xb2, 0x57, 0x77, 0xc6, 0x20, 0x9b, 0x89, 0x24, 0xbe, 0xf2, 0x9c, 0x8a, 0xba, 0x69, 0xc1, 0xf1, 0xb0, 0x4f, 0x2a, 0x05, 0x9a, 0xee, 0x10, 0x7e, 0x36}}}, -{{{0x3f, 0x26, 0xe9, 0x40, 0xe9, 0x03, 0xad, 0x06, 0x69, 0x91, 0xe0, 0xd1, 0x89, 0x60, 0x84, 0x79, 0xde, 0x27, 0x6d, 0xe6, 0x76, 0xbd, 0xea, 0xe6, 0xae, 0x48, 0xc3, 0x67, 0xc0, 0x57, 0xcd, 0x2f}} , - {{0x7f, 0xc1, 0xdc, 0xb9, 0xc7, 0xbc, 0x86, 0x3d, 0x55, 0x4b, 0x28, 0x7a, 0xfb, 0x4d, 0xc7, 0xf8, 0xbc, 0x67, 0x2a, 0x60, 0x4d, 0x8f, 0x07, 0x0b, 0x1a, 0x17, 0xbf, 0xfa, 0xac, 0xa7, 0x3d, 0x1a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x91, 0x3f, 0xed, 0x5e, 0x18, 0x78, 0x3f, 0x23, 0x2c, 0x0d, 0x8c, 0x44, 0x00, 0xe8, 0xfb, 0xe9, 0x8e, 0xd6, 0xd1, 0x36, 0x58, 0x57, 0x9e, 0xae, 0x4b, 0x5c, 0x0b, 0x07, 0xbc, 0x6b, 0x55, 0x2b}} , - {{0x6f, 0x4d, 0x17, 0xd7, 0xe1, 0x84, 0xd9, 0x78, 0xb1, 0x90, 0xfd, 0x2e, 0xb3, 0xb5, 0x19, 0x3f, 0x1b, 0xfa, 0xc0, 0x68, 0xb3, 0xdd, 0x00, 0x2e, 0x89, 0xbd, 0x7e, 0x80, 0x32, 0x13, 0xa0, 0x7b}}}, -{{{0x1a, 0x6f, 0x40, 0xaf, 0x44, 0x44, 0xb0, 0x43, 0x8f, 0x0d, 0xd0, 0x1e, 0xc4, 0x0b, 0x19, 0x5d, 0x8e, 0xfe, 0xc1, 0xf3, 0xc5, 0x5c, 0x91, 0xf8, 0x04, 0x4e, 0xbe, 0x90, 0xb4, 0x47, 0x5c, 0x3f}} , - {{0xb0, 0x3b, 0x2c, 0xf3, 0xfe, 0x32, 0x71, 0x07, 0x3f, 0xaa, 0xba, 0x45, 0x60, 0xa8, 0x8d, 0xea, 0x54, 0xcb, 0x39, 0x10, 0xb4, 0xf2, 0x8b, 0xd2, 0x14, 0x82, 0x42, 0x07, 0x8e, 0xe9, 0x7c, 0x53}}}, -{{{0xb0, 0xae, 0xc1, 0x8d, 0xc9, 0x8f, 0xb9, 0x7a, 0x77, 0xef, 0xba, 0x79, 0xa0, 0x3c, 0xa8, 0xf5, 0x6a, 0xe2, 0x3f, 0x5d, 0x00, 0xe3, 0x4b, 0x45, 0x24, 0x7b, 0x43, 0x78, 0x55, 0x1d, 0x2b, 0x1e}} , - {{0x01, 0xb8, 0xd6, 0x16, 0x67, 0xa0, 0x15, 0xb9, 0xe1, 0x58, 0xa4, 0xa7, 0x31, 0x37, 0x77, 0x2f, 0x8b, 0x12, 0x9f, 0xf4, 0x3f, 0xc7, 0x36, 0x66, 0xd2, 0xa8, 0x56, 0xf7, 0x7f, 0x74, 0xc6, 0x41}}}, -{{{0x5d, 0xf8, 0xb4, 0xa8, 0x30, 0xdd, 0xcc, 0x38, 0xa5, 0xd3, 0xca, 0xd8, 0xd1, 0xf8, 0xb2, 0x31, 0x91, 0xd4, 0x72, 0x05, 0x57, 0x4a, 0x3b, 0x82, 0x4a, 0xc6, 0x68, 0x20, 0xe2, 0x18, 0x41, 0x61}} , - {{0x19, 0xd4, 0x8d, 0x47, 0x29, 0x12, 0x65, 0xb0, 0x11, 0x78, 0x47, 0xb5, 0xcb, 0xa3, 0xa5, 0xfa, 0x05, 0x85, 0x54, 0xa9, 0x33, 0x97, 0x8d, 0x2b, 0xc2, 0xfe, 0x99, 0x35, 0x28, 0xe5, 0xeb, 0x63}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xb1, 0x3f, 0x3f, 0xef, 0xd8, 0xf4, 0xfc, 0xb3, 0xa0, 0x60, 0x50, 0x06, 0x2b, 0x29, 0x52, 0x70, 0x15, 0x0b, 0x24, 0x24, 0xf8, 0x5f, 0x79, 0x18, 0xcc, 0xff, 0x89, 0x99, 0x84, 0xa1, 0xae, 0x13}} , - {{0x44, 0x1f, 0xb8, 0xc2, 0x01, 0xc1, 0x30, 0x19, 0x55, 0x05, 0x60, 0x10, 0xa4, 0x6c, 0x2d, 0x67, 0x70, 0xe5, 0x25, 0x1b, 0xf2, 0xbf, 0xdd, 0xfb, 0x70, 0x2b, 0xa1, 0x8c, 0x9c, 0x94, 0x84, 0x08}}}, -{{{0xe7, 0xc4, 0x43, 0x4d, 0xc9, 0x2b, 0x69, 0x5d, 0x1d, 0x3c, 0xaf, 0xbb, 0x43, 0x38, 0x4e, 0x98, 0x3d, 0xed, 0x0d, 0x21, 0x03, 0xfd, 0xf0, 0x99, 0x47, 0x04, 0xb0, 0x98, 0x69, 0x55, 0x72, 0x0f}} , - {{0x5e, 0xdf, 0x15, 0x53, 0x3b, 0x86, 0x80, 0xb0, 0xf1, 0x70, 0x68, 0x8f, 0x66, 0x7c, 0x0e, 0x49, 0x1a, 0xd8, 0x6b, 0xfe, 0x4e, 0xef, 0xca, 0x47, 0xd4, 0x03, 0xc1, 0x37, 0x50, 0x9c, 0xc1, 0x16}}}, -{{{0xcd, 0x24, 0xc6, 0x3e, 0x0c, 0x82, 0x9b, 0x91, 0x2b, 0x61, 0x4a, 0xb2, 0x0f, 0x88, 0x55, 0x5f, 0x5a, 0x57, 0xff, 0xe5, 0x74, 0x0b, 0x13, 0x43, 0x00, 0xd8, 0x6b, 0xcf, 0xd2, 0x15, 0x03, 0x2c}} , - {{0xdc, 0xff, 0x15, 0x61, 0x2f, 0x4a, 0x2f, 0x62, 0xf2, 0x04, 0x2f, 0xb5, 0x0c, 0xb7, 0x1e, 0x3f, 0x74, 0x1a, 0x0f, 0xd7, 0xea, 0xcd, 0xd9, 0x7d, 0xf6, 0x12, 0x0e, 0x2f, 0xdb, 0x5a, 0x3b, 0x16}}}, -{{{0x1b, 0x37, 0x47, 0xe3, 0xf5, 0x9e, 0xea, 0x2c, 0x2a, 0xe7, 0x82, 0x36, 0xf4, 0x1f, 0x81, 0x47, 0x92, 0x4b, 0x69, 0x0e, 0x11, 0x8c, 0x5d, 0x53, 0x5b, 0x81, 0x27, 0x08, 0xbc, 0xa0, 0xae, 0x25}} , - {{0x69, 0x32, 0xa1, 0x05, 0x11, 0x42, 0x00, 0xd2, 0x59, 0xac, 0x4d, 0x62, 0x8b, 0x13, 0xe2, 0x50, 0x5d, 0xa0, 0x9d, 0x9b, 0xfd, 0xbb, 0x12, 0x41, 0x75, 0x41, 0x9e, 0xcc, 0xdc, 0xc7, 0xdc, 0x5d}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xd9, 0xe3, 0x38, 0x06, 0x46, 0x70, 0x82, 0x5e, 0x28, 0x49, 0x79, 0xff, 0x25, 0xd2, 0x4e, 0x29, 0x8d, 0x06, 0xb0, 0x23, 0xae, 0x9b, 0x66, 0xe4, 0x7d, 0xc0, 0x70, 0x91, 0xa3, 0xfc, 0xec, 0x4e}} , - {{0x62, 0x12, 0x37, 0x6a, 0x30, 0xf6, 0x1e, 0xfb, 0x14, 0x5c, 0x0d, 0x0e, 0xb7, 0x81, 0x6a, 0xe7, 0x08, 0x05, 0xac, 0xaa, 0x38, 0x46, 0xe2, 0x73, 0xea, 0x4b, 0x07, 0x81, 0x43, 0x7c, 0x9e, 0x5e}}}, -{{{0xfc, 0xf9, 0x21, 0x4f, 0x2e, 0x76, 0x9b, 0x1f, 0x28, 0x60, 0x77, 0x43, 0x32, 0x9d, 0xbe, 0x17, 0x30, 0x2a, 0xc6, 0x18, 0x92, 0x66, 0x62, 0x30, 0x98, 0x40, 0x11, 0xa6, 0x7f, 0x18, 0x84, 0x28}} , - {{0x3f, 0xab, 0xd3, 0xf4, 0x8a, 0x76, 0xa1, 0x3c, 0xca, 0x2d, 0x49, 0xc3, 0xea, 0x08, 0x0b, 0x85, 0x17, 0x2a, 0xc3, 0x6c, 0x08, 0xfd, 0x57, 0x9f, 0x3d, 0x5f, 0xdf, 0x67, 0x68, 0x42, 0x00, 0x32}}}, -{{{0x51, 0x60, 0x1b, 0x06, 0x4f, 0x8a, 0x21, 0xba, 0x38, 0xa8, 0xba, 0xd6, 0x40, 0xf6, 0xe9, 0x9b, 0x76, 0x4d, 0x56, 0x21, 0x5b, 0x0a, 0x9b, 0x2e, 0x4f, 0x3d, 0x81, 0x32, 0x08, 0x9f, 0x97, 0x5b}} , - {{0xe5, 0x44, 0xec, 0x06, 0x9d, 0x90, 0x79, 0x9f, 0xd3, 0xe0, 0x79, 0xaf, 0x8f, 0x10, 0xfd, 0xdd, 0x04, 0xae, 0x27, 0x97, 0x46, 0x33, 0x79, 0xea, 0xb8, 0x4e, 0xca, 0x5a, 0x59, 0x57, 0xe1, 0x0e}}}, -{{{0x1a, 0xda, 0xf3, 0xa5, 0x41, 0x43, 0x28, 0xfc, 0x7e, 0xe7, 0x71, 0xea, 0xc6, 0x3b, 0x59, 0xcc, 0x2e, 0xd3, 0x40, 0xec, 0xb3, 0x13, 0x6f, 0x44, 0xcd, 0x13, 0xb2, 0x37, 0xf2, 0x6e, 0xd9, 0x1c}} , - {{0xe3, 0xdb, 0x60, 0xcd, 0x5c, 0x4a, 0x18, 0x0f, 0xef, 0x73, 0x36, 0x71, 0x8c, 0xf6, 0x11, 0xb4, 0xd8, 0xce, 0x17, 0x5e, 0x4f, 0x26, 0x77, 0x97, 0x5f, 0xcb, 0xef, 0x91, 0xeb, 0x6a, 0x62, 0x7a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x18, 0x4a, 0xa2, 0x97, 0x08, 0x81, 0x2d, 0x83, 0xc4, 0xcc, 0xf0, 0x83, 0x7e, 0xec, 0x0d, 0x95, 0x4c, 0x5b, 0xfb, 0xfa, 0x98, 0x80, 0x4a, 0x66, 0x56, 0x0c, 0x51, 0xb3, 0xf2, 0x04, 0x5d, 0x27}} , - {{0x3b, 0xb9, 0xb8, 0x06, 0x5a, 0x2e, 0xfe, 0xc3, 0x82, 0x37, 0x9c, 0xa3, 0x11, 0x1f, 0x9c, 0xa6, 0xda, 0x63, 0x48, 0x9b, 0xad, 0xde, 0x2d, 0xa6, 0xbc, 0x6e, 0x32, 0xda, 0x27, 0x65, 0xdd, 0x57}}}, -{{{0x84, 0x4f, 0x37, 0x31, 0x7d, 0x2e, 0xbc, 0xad, 0x87, 0x07, 0x2a, 0x6b, 0x37, 0xfc, 0x5f, 0xeb, 0x4e, 0x75, 0x35, 0xa6, 0xde, 0xab, 0x0a, 0x19, 0x3a, 0xb7, 0xb1, 0xef, 0x92, 0x6a, 0x3b, 0x3c}} , - {{0x3b, 0xb2, 0x94, 0x6d, 0x39, 0x60, 0xac, 0xee, 0xe7, 0x81, 0x1a, 0x3b, 0x76, 0x87, 0x5c, 0x05, 0x94, 0x2a, 0x45, 0xb9, 0x80, 0xe9, 0x22, 0xb1, 0x07, 0xcb, 0x40, 0x9e, 0x70, 0x49, 0x6d, 0x12}}}, -{{{0xfd, 0x18, 0x78, 0x84, 0xa8, 0x4c, 0x7d, 0x6e, 0x59, 0xa6, 0xe5, 0x74, 0xf1, 0x19, 0xa6, 0x84, 0x2e, 0x51, 0xc1, 0x29, 0x13, 0xf2, 0x14, 0x6b, 0x5d, 0x53, 0x51, 0xf7, 0xef, 0xbf, 0x01, 0x22}} , - {{0xa4, 0x4b, 0x62, 0x4c, 0xe6, 0xfd, 0x72, 0x07, 0xf2, 0x81, 0xfc, 0xf2, 0xbd, 0x12, 0x7c, 0x68, 0x76, 0x2a, 0xba, 0xf5, 0x65, 0xb1, 0x1f, 0x17, 0x0a, 0x38, 0xb0, 0xbf, 0xc0, 0xf8, 0xf4, 0x2a}}}, -{{{0x55, 0x60, 0x55, 0x5b, 0xe4, 0x1d, 0x71, 0x4c, 0x9d, 0x5b, 0x9f, 0x70, 0xa6, 0x85, 0x9a, 0x2c, 0xa0, 0xe2, 0x32, 0x48, 0xce, 0x9e, 0x2a, 0xa5, 0x07, 0x3b, 0xc7, 0x6c, 0x86, 0x77, 0xde, 0x3c}} , - {{0xf7, 0x18, 0x7a, 0x96, 0x7e, 0x43, 0x57, 0xa9, 0x55, 0xfc, 0x4e, 0xb6, 0x72, 0x00, 0xf2, 0xe4, 0xd7, 0x52, 0xd3, 0xd3, 0xb6, 0x85, 0xf6, 0x71, 0xc7, 0x44, 0x3f, 0x7f, 0xd7, 0xb3, 0xf2, 0x79}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x46, 0xca, 0xa7, 0x55, 0x7b, 0x79, 0xf3, 0xca, 0x5a, 0x65, 0xf6, 0xed, 0x50, 0x14, 0x7b, 0xe4, 0xc4, 0x2a, 0x65, 0x9e, 0xe2, 0xf9, 0xca, 0xa7, 0x22, 0x26, 0x53, 0xcb, 0x21, 0x5b, 0xa7, 0x31}} , - {{0x90, 0xd7, 0xc5, 0x26, 0x08, 0xbd, 0xb0, 0x53, 0x63, 0x58, 0xc3, 0x31, 0x5e, 0x75, 0x46, 0x15, 0x91, 0xa6, 0xf8, 0x2f, 0x1a, 0x08, 0x65, 0x88, 0x2f, 0x98, 0x04, 0xf1, 0x7c, 0x6e, 0x00, 0x77}}}, -{{{0x81, 0x21, 0x61, 0x09, 0xf6, 0x4e, 0xf1, 0x92, 0xee, 0x63, 0x61, 0x73, 0x87, 0xc7, 0x54, 0x0e, 0x42, 0x4b, 0xc9, 0x47, 0xd1, 0xb8, 0x7e, 0x91, 0x75, 0x37, 0x99, 0x28, 0xb8, 0xdd, 0x7f, 0x50}} , - {{0x89, 0x8f, 0xc0, 0xbe, 0x5d, 0xd6, 0x9f, 0xa0, 0xf0, 0x9d, 0x81, 0xce, 0x3a, 0x7b, 0x98, 0x58, 0xbb, 0xd7, 0x78, 0xc8, 0x3f, 0x13, 0xf1, 0x74, 0x19, 0xdf, 0xf8, 0x98, 0x89, 0x5d, 0xfa, 0x5f}}}, -{{{0x9e, 0x35, 0x85, 0x94, 0x47, 0x1f, 0x90, 0x15, 0x26, 0xd0, 0x84, 0xed, 0x8a, 0x80, 0xf7, 0x63, 0x42, 0x86, 0x27, 0xd7, 0xf4, 0x75, 0x58, 0xdc, 0x9c, 0xc0, 0x22, 0x7e, 0x20, 0x35, 0xfd, 0x1f}} , - {{0x68, 0x0e, 0x6f, 0x97, 0xba, 0x70, 0xbb, 0xa3, 0x0e, 0xe5, 0x0b, 0x12, 0xf4, 0xa2, 0xdc, 0x47, 0xf8, 0xe6, 0xd0, 0x23, 0x6c, 0x33, 0xa8, 0x99, 0x46, 0x6e, 0x0f, 0x44, 0xba, 0x76, 0x48, 0x0f}}}, -{{{0xa3, 0x2a, 0x61, 0x37, 0xe2, 0x59, 0x12, 0x0e, 0x27, 0xba, 0x64, 0x43, 0xae, 0xc0, 0x42, 0x69, 0x79, 0xa4, 0x1e, 0x29, 0x8b, 0x15, 0xeb, 0xf8, 0xaf, 0xd4, 0xa2, 0x68, 0x33, 0xb5, 0x7a, 0x24}} , - {{0x2c, 0x19, 0x33, 0xdd, 0x1b, 0xab, 0xec, 0x01, 0xb0, 0x23, 0xf8, 0x42, 0x2b, 0x06, 0x88, 0xea, 0x3d, 0x2d, 0x00, 0x2a, 0x78, 0x45, 0x4d, 0x38, 0xed, 0x2e, 0x2e, 0x44, 0x49, 0xed, 0xcb, 0x33}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xa0, 0x68, 0xe8, 0x41, 0x8f, 0x91, 0xf8, 0x11, 0x13, 0x90, 0x2e, 0xa7, 0xab, 0x30, 0xef, 0xad, 0xa0, 0x61, 0x00, 0x88, 0xef, 0xdb, 0xce, 0x5b, 0x5c, 0xbb, 0x62, 0xc8, 0x56, 0xf9, 0x00, 0x73}} , - {{0x3f, 0x60, 0xc1, 0x82, 0x2d, 0xa3, 0x28, 0x58, 0x24, 0x9e, 0x9f, 0xe3, 0x70, 0xcc, 0x09, 0x4e, 0x1a, 0x3f, 0x11, 0x11, 0x15, 0x07, 0x3c, 0xa4, 0x41, 0xe0, 0x65, 0xa3, 0x0a, 0x41, 0x6d, 0x11}}}, -{{{0x31, 0x40, 0x01, 0x52, 0x56, 0x94, 0x5b, 0x28, 0x8a, 0xaa, 0x52, 0xee, 0xd8, 0x0a, 0x05, 0x8d, 0xcd, 0xb5, 0xaa, 0x2e, 0x38, 0xaa, 0xb7, 0x87, 0xf7, 0x2b, 0xfb, 0x04, 0xcb, 0x84, 0x3d, 0x54}} , - {{0x20, 0xef, 0x59, 0xde, 0xa4, 0x2b, 0x93, 0x6e, 0x2e, 0xec, 0x42, 0x9a, 0xd4, 0x2d, 0xf4, 0x46, 0x58, 0x27, 0x2b, 0x18, 0x8f, 0x83, 0x3d, 0x69, 0x9e, 0xd4, 0x3e, 0xb6, 0xc5, 0xfd, 0x58, 0x03}}}, -{{{0x33, 0x89, 0xc9, 0x63, 0x62, 0x1c, 0x17, 0xb4, 0x60, 0xc4, 0x26, 0x68, 0x09, 0xc3, 0x2e, 0x37, 0x0f, 0x7b, 0xb4, 0x9c, 0xb6, 0xf9, 0xfb, 0xd4, 0x51, 0x78, 0xc8, 0x63, 0xea, 0x77, 0x47, 0x07}} , - {{0x32, 0xb4, 0x18, 0x47, 0x79, 0xcb, 0xd4, 0x5a, 0x07, 0x14, 0x0f, 0xa0, 0xd5, 0xac, 0xd0, 0x41, 0x40, 0xab, 0x61, 0x23, 0xe5, 0x2a, 0x2a, 0x6f, 0xf7, 0xa8, 0xd4, 0x76, 0xef, 0xe7, 0x45, 0x6c}}}, -{{{0xa1, 0x5e, 0x60, 0x4f, 0xfb, 0xe1, 0x70, 0x6a, 0x1f, 0x55, 0x4f, 0x09, 0xb4, 0x95, 0x33, 0x36, 0xc6, 0x81, 0x01, 0x18, 0x06, 0x25, 0x27, 0xa4, 0xb4, 0x24, 0xa4, 0x86, 0x03, 0x4c, 0xac, 0x02}} , - {{0x77, 0x38, 0xde, 0xd7, 0x60, 0x48, 0x07, 0xf0, 0x74, 0xa8, 0xff, 0x54, 0xe5, 0x30, 0x43, 0xff, 0x77, 0xfb, 0x21, 0x07, 0xff, 0xb2, 0x07, 0x6b, 0xe4, 0xe5, 0x30, 0xfc, 0x19, 0x6c, 0xa3, 0x01}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x13, 0xc5, 0x2c, 0xac, 0xd3, 0x83, 0x82, 0x7c, 0x29, 0xf7, 0x05, 0xa5, 0x00, 0xb6, 0x1f, 0x86, 0x55, 0xf4, 0xd6, 0x2f, 0x0c, 0x99, 0xd0, 0x65, 0x9b, 0x6b, 0x46, 0x0d, 0x43, 0xf8, 0x16, 0x28}} , - {{0x1e, 0x7f, 0xb4, 0x74, 0x7e, 0xb1, 0x89, 0x4f, 0x18, 0x5a, 0xab, 0x64, 0x06, 0xdf, 0x45, 0x87, 0xe0, 0x6a, 0xc6, 0xf0, 0x0e, 0xc9, 0x24, 0x35, 0x38, 0xea, 0x30, 0x54, 0xb4, 0xc4, 0x52, 0x54}}}, -{{{0xe9, 0x9f, 0xdc, 0x3f, 0xc1, 0x89, 0x44, 0x74, 0x27, 0xe4, 0xc1, 0x90, 0xff, 0x4a, 0xa7, 0x3c, 0xee, 0xcd, 0xf4, 0x1d, 0x25, 0x94, 0x7f, 0x63, 0x16, 0x48, 0xbc, 0x64, 0xfe, 0x95, 0xc4, 0x0c}} , - {{0x8b, 0x19, 0x75, 0x6e, 0x03, 0x06, 0x5e, 0x6a, 0x6f, 0x1a, 0x8c, 0xe3, 0xd3, 0x28, 0xf2, 0xe0, 0xb9, 0x7a, 0x43, 0x69, 0xe6, 0xd3, 0xc0, 0xfe, 0x7e, 0x97, 0xab, 0x6c, 0x7b, 0x8e, 0x13, 0x42}}}, -{{{0xd4, 0xca, 0x70, 0x3d, 0xab, 0xfb, 0x5f, 0x5e, 0x00, 0x0c, 0xcc, 0x77, 0x22, 0xf8, 0x78, 0x55, 0xae, 0x62, 0x35, 0xfb, 0x9a, 0xc6, 0x03, 0xe4, 0x0c, 0xee, 0xab, 0xc7, 0xc0, 0x89, 0x87, 0x54}} , - {{0x32, 0xad, 0xae, 0x85, 0x58, 0x43, 0xb8, 0xb1, 0xe6, 0x3e, 0x00, 0x9c, 0x78, 0x88, 0x56, 0xdb, 0x9c, 0xfc, 0x79, 0xf6, 0xf9, 0x41, 0x5f, 0xb7, 0xbc, 0x11, 0xf9, 0x20, 0x36, 0x1c, 0x53, 0x2b}}}, -{{{0x5a, 0x20, 0x5b, 0xa1, 0xa5, 0x44, 0x91, 0x24, 0x02, 0x63, 0x12, 0x64, 0xb8, 0x55, 0xf6, 0xde, 0x2c, 0xdb, 0x47, 0xb8, 0xc6, 0x0a, 0xc3, 0x00, 0x78, 0x93, 0xd8, 0xf5, 0xf5, 0x18, 0x28, 0x0a}} , - {{0xd6, 0x1b, 0x9a, 0x6c, 0xe5, 0x46, 0xea, 0x70, 0x96, 0x8d, 0x4e, 0x2a, 0x52, 0x21, 0x26, 0x4b, 0xb1, 0xbb, 0x0f, 0x7c, 0xa9, 0x9b, 0x04, 0xbb, 0x51, 0x08, 0xf1, 0x9a, 0xa4, 0x76, 0x7c, 0x18}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xfa, 0x94, 0xf7, 0x40, 0xd0, 0xd7, 0xeb, 0xa9, 0x82, 0x36, 0xd5, 0x15, 0xb9, 0x33, 0x7a, 0xbf, 0x8a, 0xf2, 0x63, 0xaa, 0x37, 0xf5, 0x59, 0xac, 0xbd, 0xbb, 0x32, 0x36, 0xbe, 0x73, 0x99, 0x38}} , - {{0x2c, 0xb3, 0xda, 0x7a, 0xd8, 0x3d, 0x99, 0xca, 0xd2, 0xf4, 0xda, 0x99, 0x8e, 0x4f, 0x98, 0xb7, 0xf4, 0xae, 0x3e, 0x9f, 0x8e, 0x35, 0x60, 0xa4, 0x33, 0x75, 0xa4, 0x04, 0x93, 0xb1, 0x6b, 0x4d}}}, -{{{0x97, 0x9d, 0xa8, 0xcd, 0x97, 0x7b, 0x9d, 0xb9, 0xe7, 0xa5, 0xef, 0xfd, 0xa8, 0x42, 0x6b, 0xc3, 0x62, 0x64, 0x7d, 0xa5, 0x1b, 0xc9, 0x9e, 0xd2, 0x45, 0xb9, 0xee, 0x03, 0xb0, 0xbf, 0xc0, 0x68}} , - {{0xed, 0xb7, 0x84, 0x2c, 0xf6, 0xd3, 0xa1, 0x6b, 0x24, 0x6d, 0x87, 0x56, 0x97, 0x59, 0x79, 0x62, 0x9f, 0xac, 0xed, 0xf3, 0xc9, 0x89, 0x21, 0x2e, 0x04, 0xb3, 0xcc, 0x2f, 0xbe, 0xd6, 0x0a, 0x4b}}}, -{{{0x39, 0x61, 0x05, 0xed, 0x25, 0x89, 0x8b, 0x5d, 0x1b, 0xcb, 0x0c, 0x55, 0xf4, 0x6a, 0x00, 0x8a, 0x46, 0xe8, 0x1e, 0xc6, 0x83, 0xc8, 0x5a, 0x76, 0xdb, 0xcc, 0x19, 0x7a, 0xcc, 0x67, 0x46, 0x0b}} , - {{0x53, 0xcf, 0xc2, 0xa1, 0xad, 0x6a, 0xf3, 0xcd, 0x8f, 0xc9, 0xde, 0x1c, 0xf8, 0x6c, 0x8f, 0xf8, 0x76, 0x42, 0xe7, 0xfe, 0xb2, 0x72, 0x21, 0x0a, 0x66, 0x74, 0x8f, 0xb7, 0xeb, 0xe4, 0x6f, 0x01}}}, -{{{0x22, 0x8c, 0x6b, 0xbe, 0xfc, 0x4d, 0x70, 0x62, 0x6e, 0x52, 0x77, 0x99, 0x88, 0x7e, 0x7b, 0x57, 0x7a, 0x0d, 0xfe, 0xdc, 0x72, 0x92, 0xf1, 0x68, 0x1d, 0x97, 0xd7, 0x7c, 0x8d, 0x53, 0x10, 0x37}} , - {{0x53, 0x88, 0x77, 0x02, 0xca, 0x27, 0xa8, 0xe5, 0x45, 0xe2, 0xa8, 0x48, 0x2a, 0xab, 0x18, 0xca, 0xea, 0x2d, 0x2a, 0x54, 0x17, 0x37, 0x32, 0x09, 0xdc, 0xe0, 0x4a, 0xb7, 0x7d, 0x82, 0x10, 0x7d}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x8a, 0x64, 0x1e, 0x14, 0x0a, 0x57, 0xd4, 0xda, 0x5c, 0x96, 0x9b, 0x01, 0x4c, 0x67, 0xbf, 0x8b, 0x30, 0xfe, 0x08, 0xdb, 0x0d, 0xd5, 0xa8, 0xd7, 0x09, 0x11, 0x85, 0xa2, 0xd3, 0x45, 0xfb, 0x7e}} , - {{0xda, 0x8c, 0xc2, 0xd0, 0xac, 0x18, 0xe8, 0x52, 0x36, 0xd4, 0x21, 0xa3, 0xdd, 0x57, 0x22, 0x79, 0xb7, 0xf8, 0x71, 0x9d, 0xc6, 0x91, 0x70, 0x86, 0x56, 0xbf, 0xa1, 0x11, 0x8b, 0x19, 0xe1, 0x0f}}}, -{{{0x18, 0x32, 0x98, 0x2c, 0x8f, 0x91, 0xae, 0x12, 0xf0, 0x8c, 0xea, 0xf3, 0x3c, 0xb9, 0x5d, 0xe4, 0x69, 0xed, 0xb2, 0x47, 0x18, 0xbd, 0xce, 0x16, 0x52, 0x5c, 0x23, 0xe2, 0xa5, 0x25, 0x52, 0x5d}} , - {{0xb9, 0xb1, 0xe7, 0x5d, 0x4e, 0xbc, 0xee, 0xbb, 0x40, 0x81, 0x77, 0x82, 0x19, 0xab, 0xb5, 0xc6, 0xee, 0xab, 0x5b, 0x6b, 0x63, 0x92, 0x8a, 0x34, 0x8d, 0xcd, 0xee, 0x4f, 0x49, 0xe5, 0xc9, 0x7e}}}, -{{{0x21, 0xac, 0x8b, 0x22, 0xcd, 0xc3, 0x9a, 0xe9, 0x5e, 0x78, 0xbd, 0xde, 0xba, 0xad, 0xab, 0xbf, 0x75, 0x41, 0x09, 0xc5, 0x58, 0xa4, 0x7d, 0x92, 0xb0, 0x7f, 0xf2, 0xa1, 0xd1, 0xc0, 0xb3, 0x6d}} , - {{0x62, 0x4f, 0xd0, 0x75, 0x77, 0xba, 0x76, 0x77, 0xd7, 0xb8, 0xd8, 0x92, 0x6f, 0x98, 0x34, 0x3d, 0xd6, 0x4e, 0x1c, 0x0f, 0xf0, 0x8f, 0x2e, 0xf1, 0xb3, 0xbd, 0xb1, 0xb9, 0xec, 0x99, 0xb4, 0x07}}}, -{{{0x60, 0x57, 0x2e, 0x9a, 0x72, 0x1d, 0x6b, 0x6e, 0x58, 0x33, 0x24, 0x8c, 0x48, 0x39, 0x46, 0x8e, 0x89, 0x6a, 0x88, 0x51, 0x23, 0x62, 0xb5, 0x32, 0x09, 0x36, 0xe3, 0x57, 0xf5, 0x98, 0xde, 0x6f}} , - {{0x8b, 0x2c, 0x00, 0x48, 0x4a, 0xf9, 0x5b, 0x87, 0x69, 0x52, 0xe5, 0x5b, 0xd1, 0xb1, 0xe5, 0x25, 0x25, 0xe0, 0x9c, 0xc2, 0x13, 0x44, 0xe8, 0xb9, 0x0a, 0x70, 0xad, 0xbd, 0x0f, 0x51, 0x94, 0x69}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xa2, 0xdc, 0xab, 0xa9, 0x25, 0x2d, 0xac, 0x5f, 0x03, 0x33, 0x08, 0xe7, 0x7e, 0xfe, 0x95, 0x36, 0x3c, 0x5b, 0x3a, 0xd3, 0x05, 0x82, 0x1c, 0x95, 0x2d, 0xd8, 0x77, 0x7e, 0x02, 0xd9, 0x5b, 0x70}} , - {{0xc2, 0xfe, 0x1b, 0x0c, 0x67, 0xcd, 0xd6, 0xe0, 0x51, 0x8e, 0x2c, 0xe0, 0x79, 0x88, 0xf0, 0xcf, 0x41, 0x4a, 0xad, 0x23, 0xd4, 0x46, 0xca, 0x94, 0xa1, 0xc3, 0xeb, 0x28, 0x06, 0xfa, 0x17, 0x14}}}, -{{{0x7b, 0xaa, 0x70, 0x0a, 0x4b, 0xfb, 0xf5, 0xbf, 0x80, 0xc5, 0xcf, 0x08, 0x7a, 0xdd, 0xa1, 0xf4, 0x9d, 0x54, 0x50, 0x53, 0x23, 0x77, 0x23, 0xf5, 0x34, 0xa5, 0x22, 0xd1, 0x0d, 0x96, 0x2e, 0x47}} , - {{0xcc, 0xb7, 0x32, 0x89, 0x57, 0xd0, 0x98, 0x75, 0xe4, 0x37, 0x99, 0xa9, 0xe8, 0xba, 0xed, 0xba, 0xeb, 0xc7, 0x4f, 0x15, 0x76, 0x07, 0x0c, 0x4c, 0xef, 0x9f, 0x52, 0xfc, 0x04, 0x5d, 0x58, 0x10}}}, -{{{0xce, 0x82, 0xf0, 0x8f, 0x79, 0x02, 0xa8, 0xd1, 0xda, 0x14, 0x09, 0x48, 0xee, 0x8a, 0x40, 0x98, 0x76, 0x60, 0x54, 0x5a, 0xde, 0x03, 0x24, 0xf5, 0xe6, 0x2f, 0xe1, 0x03, 0xbf, 0x68, 0x82, 0x7f}} , - {{0x64, 0xe9, 0x28, 0xc7, 0xa4, 0xcf, 0x2a, 0xf9, 0x90, 0x64, 0x72, 0x2c, 0x8b, 0xeb, 0xec, 0xa0, 0xf2, 0x7d, 0x35, 0xb5, 0x90, 0x4d, 0x7f, 0x5b, 0x4a, 0x49, 0xe4, 0xb8, 0x3b, 0xc8, 0xa1, 0x2f}}}, -{{{0x8b, 0xc5, 0xcc, 0x3d, 0x69, 0xa6, 0xa1, 0x18, 0x44, 0xbc, 0x4d, 0x77, 0x37, 0xc7, 0x86, 0xec, 0x0c, 0xc9, 0xd6, 0x44, 0xa9, 0x23, 0x27, 0xb9, 0x03, 0x34, 0xa7, 0x0a, 0xd5, 0xc7, 0x34, 0x37}} , - {{0xf9, 0x7e, 0x3e, 0x66, 0xee, 0xf9, 0x99, 0x28, 0xff, 0xad, 0x11, 0xd8, 0xe2, 0x66, 0xc5, 0xcd, 0x0f, 0x0d, 0x0b, 0x6a, 0xfc, 0x7c, 0x24, 0xa8, 0x4f, 0xa8, 0x5e, 0x80, 0x45, 0x8b, 0x6c, 0x41}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xef, 0x1e, 0xec, 0xf7, 0x8d, 0x77, 0xf2, 0xea, 0xdb, 0x60, 0x03, 0x21, 0xc0, 0xff, 0x5e, 0x67, 0xc3, 0x71, 0x0b, 0x21, 0xb4, 0x41, 0xa0, 0x68, 0x38, 0xc6, 0x01, 0xa3, 0xd3, 0x51, 0x3c, 0x3c}} , - {{0x92, 0xf8, 0xd6, 0x4b, 0xef, 0x42, 0x13, 0xb2, 0x4a, 0xc4, 0x2e, 0x72, 0x3f, 0xc9, 0x11, 0xbd, 0x74, 0x02, 0x0e, 0xf5, 0x13, 0x9d, 0x83, 0x1a, 0x1b, 0xd5, 0x54, 0xde, 0xc4, 0x1e, 0x16, 0x6c}}}, -{{{0x27, 0x52, 0xe4, 0x63, 0xaa, 0x94, 0xe6, 0xc3, 0x28, 0x9c, 0xc6, 0x56, 0xac, 0xfa, 0xb6, 0xbd, 0xe2, 0xcc, 0x76, 0xc6, 0x27, 0x27, 0xa2, 0x8e, 0x78, 0x2b, 0x84, 0x72, 0x10, 0xbd, 0x4e, 0x2a}} , - {{0xea, 0xa7, 0x23, 0xef, 0x04, 0x61, 0x80, 0x50, 0xc9, 0x6e, 0xa5, 0x96, 0xd1, 0xd1, 0xc8, 0xc3, 0x18, 0xd7, 0x2d, 0xfd, 0x26, 0xbd, 0xcb, 0x7b, 0x92, 0x51, 0x0e, 0x4a, 0x65, 0x57, 0xb8, 0x49}}}, -{{{0xab, 0x55, 0x36, 0xc3, 0xec, 0x63, 0x55, 0x11, 0x55, 0xf6, 0xa5, 0xc7, 0x01, 0x5f, 0xfe, 0x79, 0xd8, 0x0a, 0xf7, 0x03, 0xd8, 0x98, 0x99, 0xf5, 0xd0, 0x00, 0x54, 0x6b, 0x66, 0x28, 0xf5, 0x25}} , - {{0x7a, 0x8d, 0xa1, 0x5d, 0x70, 0x5d, 0x51, 0x27, 0xee, 0x30, 0x65, 0x56, 0x95, 0x46, 0xde, 0xbd, 0x03, 0x75, 0xb4, 0x57, 0x59, 0x89, 0xeb, 0x02, 0x9e, 0xcc, 0x89, 0x19, 0xa7, 0xcb, 0x17, 0x67}}}, -{{{0x6a, 0xeb, 0xfc, 0x9a, 0x9a, 0x10, 0xce, 0xdb, 0x3a, 0x1c, 0x3c, 0x6a, 0x9d, 0xea, 0x46, 0xbc, 0x45, 0x49, 0xac, 0xe3, 0x41, 0x12, 0x7c, 0xf0, 0xf7, 0x4f, 0xf9, 0xf7, 0xff, 0x2c, 0x89, 0x04}} , - {{0x30, 0x31, 0x54, 0x1a, 0x46, 0xca, 0xe6, 0xc6, 0xcb, 0xe2, 0xc3, 0xc1, 0x8b, 0x75, 0x81, 0xbe, 0xee, 0xf8, 0xa3, 0x11, 0x1c, 0x25, 0xa3, 0xa7, 0x35, 0x51, 0x55, 0xe2, 0x25, 0xaa, 0xe2, 0x3a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xb4, 0x48, 0x10, 0x9f, 0x8a, 0x09, 0x76, 0xfa, 0xf0, 0x7a, 0xb0, 0x70, 0xf7, 0x83, 0x80, 0x52, 0x84, 0x2b, 0x26, 0xa2, 0xc4, 0x5d, 0x4f, 0xba, 0xb1, 0xc8, 0x40, 0x0d, 0x78, 0x97, 0xc4, 0x60}} , - {{0xd4, 0xb1, 0x6c, 0x08, 0xc7, 0x40, 0x38, 0x73, 0x5f, 0x0b, 0xf3, 0x76, 0x5d, 0xb2, 0xa5, 0x2f, 0x57, 0x57, 0x07, 0xed, 0x08, 0xa2, 0x6c, 0x4f, 0x08, 0x02, 0xb5, 0x0e, 0xee, 0x44, 0xfa, 0x22}}}, -{{{0x0f, 0x00, 0x3f, 0xa6, 0x04, 0x19, 0x56, 0x65, 0x31, 0x7f, 0x8b, 0xeb, 0x0d, 0xe1, 0x47, 0x89, 0x97, 0x16, 0x53, 0xfa, 0x81, 0xa7, 0xaa, 0xb2, 0xbf, 0x67, 0xeb, 0x72, 0x60, 0x81, 0x0d, 0x48}} , - {{0x7e, 0x13, 0x33, 0xcd, 0xa8, 0x84, 0x56, 0x1e, 0x67, 0xaf, 0x6b, 0x43, 0xac, 0x17, 0xaf, 0x16, 0xc0, 0x52, 0x99, 0x49, 0x5b, 0x87, 0x73, 0x7e, 0xb5, 0x43, 0xda, 0x6b, 0x1d, 0x0f, 0x2d, 0x55}}}, -{{{0xe9, 0x58, 0x1f, 0xff, 0x84, 0x3f, 0x93, 0x1c, 0xcb, 0xe1, 0x30, 0x69, 0xa5, 0x75, 0x19, 0x7e, 0x14, 0x5f, 0xf8, 0xfc, 0x09, 0xdd, 0xa8, 0x78, 0x9d, 0xca, 0x59, 0x8b, 0xd1, 0x30, 0x01, 0x13}} , - {{0xff, 0x76, 0x03, 0xc5, 0x4b, 0x89, 0x99, 0x70, 0x00, 0x59, 0x70, 0x9c, 0xd5, 0xd9, 0x11, 0x89, 0x5a, 0x46, 0xfe, 0xef, 0xdc, 0xd9, 0x55, 0x2b, 0x45, 0xa7, 0xb0, 0x2d, 0xfb, 0x24, 0xc2, 0x29}}}, -{{{0x38, 0x06, 0xf8, 0x0b, 0xac, 0x82, 0xc4, 0x97, 0x2b, 0x90, 0xe0, 0xf7, 0xa8, 0xab, 0x6c, 0x08, 0x80, 0x66, 0x90, 0x46, 0xf7, 0x26, 0x2d, 0xf8, 0xf1, 0xc4, 0x6b, 0x4a, 0x82, 0x98, 0x8e, 0x37}} , - {{0x8e, 0xb4, 0xee, 0xb8, 0xd4, 0x3f, 0xb2, 0x1b, 0xe0, 0x0a, 0x3d, 0x75, 0x34, 0x28, 0xa2, 0x8e, 0xc4, 0x92, 0x7b, 0xfe, 0x60, 0x6e, 0x6d, 0xb8, 0x31, 0x1d, 0x62, 0x0d, 0x78, 0x14, 0x42, 0x11}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x5e, 0xa8, 0xd8, 0x04, 0x9b, 0x73, 0xc9, 0xc9, 0xdc, 0x0d, 0x73, 0xbf, 0x0a, 0x0a, 0x73, 0xff, 0x18, 0x1f, 0x9c, 0x51, 0xaa, 0xc6, 0xf1, 0x83, 0x25, 0xfd, 0xab, 0xa3, 0x11, 0xd3, 0x01, 0x24}} , - {{0x4d, 0xe3, 0x7e, 0x38, 0x62, 0x5e, 0x64, 0xbb, 0x2b, 0x53, 0xb5, 0x03, 0x68, 0xc4, 0xf2, 0x2b, 0x5a, 0x03, 0x32, 0x99, 0x4a, 0x41, 0x9a, 0xe1, 0x1a, 0xae, 0x8c, 0x48, 0xf3, 0x24, 0x32, 0x65}}}, -{{{0xe8, 0xdd, 0xad, 0x3a, 0x8c, 0xea, 0xf4, 0xb3, 0xb2, 0xe5, 0x73, 0xf2, 0xed, 0x8b, 0xbf, 0xed, 0xb1, 0x0c, 0x0c, 0xfb, 0x2b, 0xf1, 0x01, 0x48, 0xe8, 0x26, 0x03, 0x8e, 0x27, 0x4d, 0x96, 0x72}} , - {{0xc8, 0x09, 0x3b, 0x60, 0xc9, 0x26, 0x4d, 0x7c, 0xf2, 0x9c, 0xd4, 0xa1, 0x3b, 0x26, 0xc2, 0x04, 0x33, 0x44, 0x76, 0x3c, 0x02, 0xbb, 0x11, 0x42, 0x0c, 0x22, 0xb7, 0xc6, 0xe1, 0xac, 0xb4, 0x0e}}}, -{{{0x6f, 0x85, 0xe7, 0xef, 0xde, 0x67, 0x30, 0xfc, 0xbf, 0x5a, 0xe0, 0x7b, 0x7a, 0x2a, 0x54, 0x6b, 0x5d, 0x62, 0x85, 0xa1, 0xf8, 0x16, 0x88, 0xec, 0x61, 0xb9, 0x96, 0xb5, 0xef, 0x2d, 0x43, 0x4d}} , - {{0x7c, 0x31, 0x33, 0xcc, 0xe4, 0xcf, 0x6c, 0xff, 0x80, 0x47, 0x77, 0xd1, 0xd8, 0xe9, 0x69, 0x97, 0x98, 0x7f, 0x20, 0x57, 0x1d, 0x1d, 0x4f, 0x08, 0x27, 0xc8, 0x35, 0x57, 0x40, 0xc6, 0x21, 0x0c}}}, -{{{0xd2, 0x8e, 0x9b, 0xfa, 0x42, 0x8e, 0xdf, 0x8f, 0xc7, 0x86, 0xf9, 0xa4, 0xca, 0x70, 0x00, 0x9d, 0x21, 0xbf, 0xec, 0x57, 0x62, 0x30, 0x58, 0x8c, 0x0d, 0x35, 0xdb, 0x5d, 0x8b, 0x6a, 0xa0, 0x5a}} , - {{0xc1, 0x58, 0x7c, 0x0d, 0x20, 0xdd, 0x11, 0x26, 0x5f, 0x89, 0x3b, 0x97, 0x58, 0xf8, 0x8b, 0xe3, 0xdf, 0x32, 0xe2, 0xfc, 0xd8, 0x67, 0xf2, 0xa5, 0x37, 0x1e, 0x6d, 0xec, 0x7c, 0x27, 0x20, 0x79}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xd0, 0xe9, 0xc0, 0xfa, 0x95, 0x45, 0x23, 0x96, 0xf1, 0x2c, 0x79, 0x25, 0x14, 0xce, 0x40, 0x14, 0x44, 0x2c, 0x36, 0x50, 0xd9, 0x63, 0x56, 0xb7, 0x56, 0x3b, 0x9e, 0xa7, 0xef, 0x89, 0xbb, 0x0e}} , - {{0xce, 0x7f, 0xdc, 0x0a, 0xcc, 0x82, 0x1c, 0x0a, 0x78, 0x71, 0xe8, 0x74, 0x8d, 0x01, 0x30, 0x0f, 0xa7, 0x11, 0x4c, 0xdf, 0x38, 0xd7, 0xa7, 0x0d, 0xf8, 0x48, 0x52, 0x00, 0x80, 0x7b, 0x5f, 0x0e}}}, -{{{0x25, 0x83, 0xe6, 0x94, 0x7b, 0x81, 0xb2, 0x91, 0xae, 0x0e, 0x05, 0xc9, 0xa3, 0x68, 0x2d, 0xd9, 0x88, 0x25, 0x19, 0x2a, 0x61, 0x61, 0x21, 0x97, 0x15, 0xa1, 0x35, 0xa5, 0x46, 0xc8, 0xa2, 0x0e}} , - {{0x1b, 0x03, 0x0d, 0x8b, 0x5a, 0x1b, 0x97, 0x4b, 0xf2, 0x16, 0x31, 0x3d, 0x1f, 0x33, 0xa0, 0x50, 0x3a, 0x18, 0xbe, 0x13, 0xa1, 0x76, 0xc1, 0xba, 0x1b, 0xf1, 0x05, 0x7b, 0x33, 0xa8, 0x82, 0x3b}}}, -{{{0xba, 0x36, 0x7b, 0x6d, 0xa9, 0xea, 0x14, 0x12, 0xc5, 0xfa, 0x91, 0x00, 0xba, 0x9b, 0x99, 0xcc, 0x56, 0x02, 0xe9, 0xa0, 0x26, 0x40, 0x66, 0x8c, 0xc4, 0xf8, 0x85, 0x33, 0x68, 0xe7, 0x03, 0x20}} , - {{0x50, 0x5b, 0xff, 0xa9, 0xb2, 0xf1, 0xf1, 0x78, 0xcf, 0x14, 0xa4, 0xa9, 0xfc, 0x09, 0x46, 0x94, 0x54, 0x65, 0x0d, 0x9c, 0x5f, 0x72, 0x21, 0xe2, 0x97, 0xa5, 0x2d, 0x81, 0xce, 0x4a, 0x5f, 0x79}}}, -{{{0x3d, 0x5f, 0x5c, 0xd2, 0xbc, 0x7d, 0x77, 0x0e, 0x2a, 0x6d, 0x22, 0x45, 0x84, 0x06, 0xc4, 0xdd, 0xc6, 0xa6, 0xc6, 0xd7, 0x49, 0xad, 0x6d, 0x87, 0x91, 0x0e, 0x3a, 0x67, 0x1d, 0x2c, 0x1d, 0x56}} , - {{0xfe, 0x7a, 0x74, 0xcf, 0xd4, 0xd2, 0xe5, 0x19, 0xde, 0xd0, 0xdb, 0x70, 0x23, 0x69, 0xe6, 0x6d, 0xec, 0xec, 0xcc, 0x09, 0x33, 0x6a, 0x77, 0xdc, 0x6b, 0x22, 0x76, 0x5d, 0x92, 0x09, 0xac, 0x2d}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x23, 0x15, 0x17, 0xeb, 0xd3, 0xdb, 0x12, 0x5e, 0x01, 0xf0, 0x91, 0xab, 0x2c, 0x41, 0xce, 0xac, 0xed, 0x1b, 0x4b, 0x2d, 0xbc, 0xdb, 0x17, 0x66, 0x89, 0x46, 0xad, 0x4b, 0x1e, 0x6f, 0x0b, 0x14}} , - {{0x11, 0xce, 0xbf, 0xb6, 0x77, 0x2d, 0x48, 0x22, 0x18, 0x4f, 0xa3, 0x5d, 0x4a, 0xb0, 0x70, 0x12, 0x3e, 0x54, 0xd7, 0xd8, 0x0e, 0x2b, 0x27, 0xdc, 0x53, 0xff, 0xca, 0x8c, 0x59, 0xb3, 0x4e, 0x44}}}, -{{{0x07, 0x76, 0x61, 0x0f, 0x66, 0xb2, 0x21, 0x39, 0x7e, 0xc0, 0xec, 0x45, 0x28, 0x82, 0xa1, 0x29, 0x32, 0x44, 0x35, 0x13, 0x5e, 0x61, 0x5e, 0x54, 0xcb, 0x7c, 0xef, 0xf6, 0x41, 0xcf, 0x9f, 0x0a}} , - {{0xdd, 0xf9, 0xda, 0x84, 0xc3, 0xe6, 0x8a, 0x9f, 0x24, 0xd2, 0x96, 0x5d, 0x39, 0x6f, 0x58, 0x8c, 0xc1, 0x56, 0x93, 0xab, 0xb5, 0x79, 0x3b, 0xd2, 0xa8, 0x73, 0x16, 0xed, 0xfa, 0xb4, 0x2f, 0x73}}}, -{{{0x8b, 0xb1, 0x95, 0xe5, 0x92, 0x50, 0x35, 0x11, 0x76, 0xac, 0xf4, 0x4d, 0x24, 0xc3, 0x32, 0xe6, 0xeb, 0xfe, 0x2c, 0x87, 0xc4, 0xf1, 0x56, 0xc4, 0x75, 0x24, 0x7a, 0x56, 0x85, 0x5a, 0x3a, 0x13}} , - {{0x0d, 0x16, 0xac, 0x3c, 0x4a, 0x58, 0x86, 0x3a, 0x46, 0x7f, 0x6c, 0xa3, 0x52, 0x6e, 0x37, 0xe4, 0x96, 0x9c, 0xe9, 0x5c, 0x66, 0x41, 0x67, 0xe4, 0xfb, 0x79, 0x0c, 0x05, 0xf6, 0x64, 0xd5, 0x7c}}}, -{{{0x28, 0xc1, 0xe1, 0x54, 0x73, 0xf2, 0xbf, 0x76, 0x74, 0x19, 0x19, 0x1b, 0xe4, 0xb9, 0xa8, 0x46, 0x65, 0x73, 0xf3, 0x77, 0x9b, 0x29, 0x74, 0x5b, 0xc6, 0x89, 0x6c, 0x2c, 0x7c, 0xf8, 0xb3, 0x0f}} , - {{0xf7, 0xd5, 0xe9, 0x74, 0x5d, 0xb8, 0x25, 0x16, 0xb5, 0x30, 0xbc, 0x84, 0xc5, 0xf0, 0xad, 0xca, 0x12, 0x28, 0xbc, 0x9d, 0xd4, 0xfa, 0x82, 0xe6, 0xe3, 0xbf, 0xa2, 0x15, 0x2c, 0xd4, 0x34, 0x10}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x61, 0xb1, 0x46, 0xba, 0x0e, 0x31, 0xa5, 0x67, 0x6c, 0x7f, 0xd6, 0xd9, 0x27, 0x85, 0x0f, 0x79, 0x14, 0xc8, 0x6c, 0x2f, 0x5f, 0x5b, 0x9c, 0x35, 0x3d, 0x38, 0x86, 0x77, 0x65, 0x55, 0x6a, 0x7b}} , - {{0xd3, 0xb0, 0x3a, 0x66, 0x60, 0x1b, 0x43, 0xf1, 0x26, 0x58, 0x99, 0x09, 0x8f, 0x2d, 0xa3, 0x14, 0x71, 0x85, 0xdb, 0xed, 0xf6, 0x26, 0xd5, 0x61, 0x9a, 0x73, 0xac, 0x0e, 0xea, 0xac, 0xb7, 0x0c}}}, -{{{0x5e, 0xf4, 0xe5, 0x17, 0x0e, 0x10, 0x9f, 0xe7, 0x43, 0x5f, 0x67, 0x5c, 0xac, 0x4b, 0xe5, 0x14, 0x41, 0xd2, 0xbf, 0x48, 0xf5, 0x14, 0xb0, 0x71, 0xc6, 0x61, 0xc1, 0xb2, 0x70, 0x58, 0xd2, 0x5a}} , - {{0x2d, 0xba, 0x16, 0x07, 0x92, 0x94, 0xdc, 0xbd, 0x50, 0x2b, 0xc9, 0x7f, 0x42, 0x00, 0xba, 0x61, 0xed, 0xf8, 0x43, 0xed, 0xf5, 0xf9, 0x40, 0x60, 0xb2, 0xb0, 0x82, 0xcb, 0xed, 0x75, 0xc7, 0x65}}}, -{{{0x80, 0xba, 0x0d, 0x09, 0x40, 0xa7, 0x39, 0xa6, 0x67, 0x34, 0x7e, 0x66, 0xbe, 0x56, 0xfb, 0x53, 0x78, 0xc4, 0x46, 0xe8, 0xed, 0x68, 0x6c, 0x7f, 0xce, 0xe8, 0x9f, 0xce, 0xa2, 0x64, 0x58, 0x53}} , - {{0xe8, 0xc1, 0xa9, 0xc2, 0x7b, 0x59, 0x21, 0x33, 0xe2, 0x43, 0x73, 0x2b, 0xac, 0x2d, 0xc1, 0x89, 0x3b, 0x15, 0xe2, 0xd5, 0xc0, 0x97, 0x8a, 0xfd, 0x6f, 0x36, 0x33, 0xb7, 0xb9, 0xc3, 0x88, 0x09}}}, -{{{0xd0, 0xb6, 0x56, 0x30, 0x5c, 0xae, 0xb3, 0x75, 0x44, 0xa4, 0x83, 0x51, 0x6e, 0x01, 0x65, 0xef, 0x45, 0x76, 0xe6, 0xf5, 0xa2, 0x0d, 0xd4, 0x16, 0x3b, 0x58, 0x2f, 0xf2, 0x2f, 0x36, 0x18, 0x3f}} , - {{0xfd, 0x2f, 0xe0, 0x9b, 0x1e, 0x8c, 0xc5, 0x18, 0xa9, 0xca, 0xd4, 0x2b, 0x35, 0xb6, 0x95, 0x0a, 0x9f, 0x7e, 0xfb, 0xc4, 0xef, 0x88, 0x7b, 0x23, 0x43, 0xec, 0x2f, 0x0d, 0x0f, 0x7a, 0xfc, 0x5c}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x8d, 0xd2, 0xda, 0xc7, 0x44, 0xd6, 0x7a, 0xdb, 0x26, 0x7d, 0x1d, 0xb8, 0xe1, 0xde, 0x9d, 0x7a, 0x7d, 0x17, 0x7e, 0x1c, 0x37, 0x04, 0x8d, 0x2d, 0x7c, 0x5e, 0x18, 0x38, 0x1e, 0xaf, 0xc7, 0x1b}} , - {{0x33, 0x48, 0x31, 0x00, 0x59, 0xf6, 0xf2, 0xca, 0x0f, 0x27, 0x1b, 0x63, 0x12, 0x7e, 0x02, 0x1d, 0x49, 0xc0, 0x5d, 0x79, 0x87, 0xef, 0x5e, 0x7a, 0x2f, 0x1f, 0x66, 0x55, 0xd8, 0x09, 0xd9, 0x61}}}, -{{{0x54, 0x83, 0x02, 0x18, 0x82, 0x93, 0x99, 0x07, 0xd0, 0xa7, 0xda, 0xd8, 0x75, 0x89, 0xfa, 0xf2, 0xd9, 0xa3, 0xb8, 0x6b, 0x5a, 0x35, 0x28, 0xd2, 0x6b, 0x59, 0xc2, 0xf8, 0x45, 0xe2, 0xbc, 0x06}} , - {{0x65, 0xc0, 0xa3, 0x88, 0x51, 0x95, 0xfc, 0x96, 0x94, 0x78, 0xe8, 0x0d, 0x8b, 0x41, 0xc9, 0xc2, 0x58, 0x48, 0x75, 0x10, 0x2f, 0xcd, 0x2a, 0xc9, 0xa0, 0x6d, 0x0f, 0xdd, 0x9c, 0x98, 0x26, 0x3d}}}, -{{{0x2f, 0x66, 0x29, 0x1b, 0x04, 0x89, 0xbd, 0x7e, 0xee, 0x6e, 0xdd, 0xb7, 0x0e, 0xef, 0xb0, 0x0c, 0xb4, 0xfc, 0x7f, 0xc2, 0xc9, 0x3a, 0x3c, 0x64, 0xef, 0x45, 0x44, 0xaf, 0x8a, 0x90, 0x65, 0x76}} , - {{0xa1, 0x4c, 0x70, 0x4b, 0x0e, 0xa0, 0x83, 0x70, 0x13, 0xa4, 0xaf, 0xb8, 0x38, 0x19, 0x22, 0x65, 0x09, 0xb4, 0x02, 0x4f, 0x06, 0xf8, 0x17, 0xce, 0x46, 0x45, 0xda, 0x50, 0x7c, 0x8a, 0xd1, 0x4e}}}, -{{{0xf7, 0xd4, 0x16, 0x6c, 0x4e, 0x95, 0x9d, 0x5d, 0x0f, 0x91, 0x2b, 0x52, 0xfe, 0x5c, 0x34, 0xe5, 0x30, 0xe6, 0xa4, 0x3b, 0xf3, 0xf3, 0x34, 0x08, 0xa9, 0x4a, 0xa0, 0xb5, 0x6e, 0xb3, 0x09, 0x0a}} , - {{0x26, 0xd9, 0x5e, 0xa3, 0x0f, 0xeb, 0xa2, 0xf3, 0x20, 0x3b, 0x37, 0xd4, 0xe4, 0x9e, 0xce, 0x06, 0x3d, 0x53, 0xed, 0xae, 0x2b, 0xeb, 0xb6, 0x24, 0x0a, 0x11, 0xa3, 0x0f, 0xd6, 0x7f, 0xa4, 0x3a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xdb, 0x9f, 0x2c, 0xfc, 0xd6, 0xb2, 0x1e, 0x2e, 0x52, 0x7a, 0x06, 0x87, 0x2d, 0x86, 0x72, 0x2b, 0x6d, 0x90, 0x77, 0x46, 0x43, 0xb5, 0x7a, 0xf8, 0x60, 0x7d, 0x91, 0x60, 0x5b, 0x9d, 0x9e, 0x07}} , - {{0x97, 0x87, 0xc7, 0x04, 0x1c, 0x38, 0x01, 0x39, 0x58, 0xc7, 0x85, 0xa3, 0xfc, 0x64, 0x00, 0x64, 0x25, 0xa2, 0xbf, 0x50, 0x94, 0xca, 0x26, 0x31, 0x45, 0x0a, 0x24, 0xd2, 0x51, 0x29, 0x51, 0x16}}}, -{{{0x4d, 0x4a, 0xd7, 0x98, 0x71, 0x57, 0xac, 0x7d, 0x8b, 0x37, 0xbd, 0x63, 0xff, 0x87, 0xb1, 0x49, 0x95, 0x20, 0x7c, 0xcf, 0x7c, 0x59, 0xc4, 0x91, 0x9c, 0xef, 0xd0, 0xdb, 0x60, 0x09, 0x9d, 0x46}} , - {{0xcb, 0x78, 0x94, 0x90, 0xe4, 0x45, 0xb3, 0xf6, 0xd9, 0xf6, 0x57, 0x74, 0xd5, 0xf8, 0x83, 0x4f, 0x39, 0xc9, 0xbd, 0x88, 0xc2, 0x57, 0x21, 0x1f, 0x24, 0x32, 0x68, 0xf8, 0xc7, 0x21, 0x5f, 0x0b}}}, -{{{0x2a, 0x36, 0x68, 0xfc, 0x5f, 0xb6, 0x4f, 0xa5, 0xe3, 0x9d, 0x24, 0x2f, 0xc0, 0x93, 0x61, 0xcf, 0xf8, 0x0a, 0xed, 0xe1, 0xdb, 0x27, 0xec, 0x0e, 0x14, 0x32, 0x5f, 0x8e, 0xa1, 0x62, 0x41, 0x16}} , - {{0x95, 0x21, 0x01, 0xce, 0x95, 0x5b, 0x0e, 0x57, 0xc7, 0xb9, 0x62, 0xb5, 0x28, 0xca, 0x11, 0xec, 0xb4, 0x46, 0x06, 0x73, 0x26, 0xff, 0xfb, 0x66, 0x7d, 0xee, 0x5f, 0xb2, 0x56, 0xfd, 0x2a, 0x08}}}, -{{{0x92, 0x67, 0x77, 0x56, 0xa1, 0xff, 0xc4, 0xc5, 0x95, 0xf0, 0xe3, 0x3a, 0x0a, 0xca, 0x94, 0x4d, 0x9e, 0x7e, 0x3d, 0xb9, 0x6e, 0xb6, 0xb0, 0xce, 0xa4, 0x30, 0x89, 0x99, 0xe9, 0xad, 0x11, 0x59}} , - {{0xf6, 0x48, 0x95, 0xa1, 0x6f, 0x5f, 0xb7, 0xa5, 0xbb, 0x30, 0x00, 0x1c, 0xd2, 0x8a, 0xd6, 0x25, 0x26, 0x1b, 0xb2, 0x0d, 0x37, 0x6a, 0x05, 0xf4, 0x9d, 0x3e, 0x17, 0x2a, 0x43, 0xd2, 0x3a, 0x06}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x32, 0x99, 0x93, 0xd1, 0x9a, 0x72, 0xf3, 0xa9, 0x16, 0xbd, 0xb4, 0x4c, 0xdd, 0xf9, 0xd4, 0xb2, 0x64, 0x9a, 0xd3, 0x05, 0xe4, 0xa3, 0x73, 0x1c, 0xcb, 0x7e, 0x57, 0x67, 0xff, 0x04, 0xb3, 0x10}} , - {{0xb9, 0x4b, 0xa4, 0xad, 0xd0, 0x6d, 0x61, 0x23, 0xb4, 0xaf, 0x34, 0xa9, 0xaa, 0x65, 0xec, 0xd9, 0x69, 0xe3, 0x85, 0xcd, 0xcc, 0xe7, 0xb0, 0x9b, 0x41, 0xc1, 0x1c, 0xf9, 0xa0, 0xfa, 0xb7, 0x13}}}, -{{{0x04, 0xfd, 0x88, 0x3c, 0x0c, 0xd0, 0x09, 0x52, 0x51, 0x4f, 0x06, 0x19, 0xcc, 0xc3, 0xbb, 0xde, 0x80, 0xc5, 0x33, 0xbc, 0xf9, 0xf3, 0x17, 0x36, 0xdd, 0xc6, 0xde, 0xe8, 0x9b, 0x5d, 0x79, 0x1b}} , - {{0x65, 0x0a, 0xbe, 0x51, 0x57, 0xad, 0x50, 0x79, 0x08, 0x71, 0x9b, 0x07, 0x95, 0x8f, 0xfb, 0xae, 0x4b, 0x38, 0xba, 0xcf, 0x53, 0x2a, 0x86, 0x1e, 0xc0, 0x50, 0x5c, 0x67, 0x1b, 0xf6, 0x87, 0x6c}}}, -{{{0x4f, 0x00, 0xb2, 0x66, 0x55, 0xed, 0x4a, 0xed, 0x8d, 0xe1, 0x66, 0x18, 0xb2, 0x14, 0x74, 0x8d, 0xfd, 0x1a, 0x36, 0x0f, 0x26, 0x5c, 0x8b, 0x89, 0xf3, 0xab, 0xf2, 0xf3, 0x24, 0x67, 0xfd, 0x70}} , - {{0xfd, 0x4e, 0x2a, 0xc1, 0x3a, 0xca, 0x8f, 0x00, 0xd8, 0xec, 0x74, 0x67, 0xef, 0x61, 0xe0, 0x28, 0xd0, 0x96, 0xf4, 0x48, 0xde, 0x81, 0xe3, 0xef, 0xdc, 0xaa, 0x7d, 0xf3, 0xb6, 0x55, 0xa6, 0x65}}}, -{{{0xeb, 0xcb, 0xc5, 0x70, 0x91, 0x31, 0x10, 0x93, 0x0d, 0xc8, 0xd0, 0xef, 0x62, 0xe8, 0x6f, 0x82, 0xe3, 0x69, 0x3d, 0x91, 0x7f, 0x31, 0xe1, 0x26, 0x35, 0x3c, 0x4a, 0x2f, 0xab, 0xc4, 0x9a, 0x5e}} , - {{0xab, 0x1b, 0xb5, 0xe5, 0x2b, 0xc3, 0x0e, 0x29, 0xb0, 0xd0, 0x73, 0xe6, 0x4f, 0x64, 0xf2, 0xbc, 0xe4, 0xe4, 0xe1, 0x9a, 0x52, 0x33, 0x2f, 0xbd, 0xcc, 0x03, 0xee, 0x8a, 0xfa, 0x00, 0x5f, 0x50}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xf6, 0xdb, 0x0d, 0x22, 0x3d, 0xb5, 0x14, 0x75, 0x31, 0xf0, 0x81, 0xe2, 0xb9, 0x37, 0xa2, 0xa9, 0x84, 0x11, 0x9a, 0x07, 0xb5, 0x53, 0x89, 0x78, 0xa9, 0x30, 0x27, 0xa1, 0xf1, 0x4e, 0x5c, 0x2e}} , - {{0x8b, 0x00, 0x54, 0xfb, 0x4d, 0xdc, 0xcb, 0x17, 0x35, 0x40, 0xff, 0xb7, 0x8c, 0xfe, 0x4a, 0xe4, 0x4e, 0x99, 0x4e, 0xa8, 0x74, 0x54, 0x5d, 0x5c, 0x96, 0xa3, 0x12, 0x55, 0x36, 0x31, 0x17, 0x5c}}}, -{{{0xce, 0x24, 0xef, 0x7b, 0x86, 0xf2, 0x0f, 0x77, 0xe8, 0x5c, 0x7d, 0x87, 0x38, 0x2d, 0xef, 0xaf, 0xf2, 0x8c, 0x72, 0x2e, 0xeb, 0xb6, 0x55, 0x4b, 0x6e, 0xf1, 0x4e, 0x8a, 0x0e, 0x9a, 0x6c, 0x4c}} , - {{0x25, 0xea, 0x86, 0xc2, 0xd1, 0x4f, 0xb7, 0x3e, 0xa8, 0x5c, 0x8d, 0x66, 0x81, 0x25, 0xed, 0xc5, 0x4c, 0x05, 0xb9, 0xd8, 0xd6, 0x70, 0xbe, 0x73, 0x82, 0xe8, 0xa1, 0xe5, 0x1e, 0x71, 0xd5, 0x26}}}, -{{{0x4e, 0x6d, 0xc3, 0xa7, 0x4f, 0x22, 0x45, 0x26, 0xa2, 0x7e, 0x16, 0xf7, 0xf7, 0x63, 0xdc, 0x86, 0x01, 0x2a, 0x71, 0x38, 0x5c, 0x33, 0xc3, 0xce, 0x30, 0xff, 0xf9, 0x2c, 0x91, 0x71, 0x8a, 0x72}} , - {{0x8c, 0x44, 0x09, 0x28, 0xd5, 0x23, 0xc9, 0x8f, 0xf3, 0x84, 0x45, 0xc6, 0x9a, 0x5e, 0xff, 0xd2, 0xc7, 0x57, 0x93, 0xa3, 0xc1, 0x69, 0xdd, 0x62, 0x0f, 0xda, 0x5c, 0x30, 0x59, 0x5d, 0xe9, 0x4c}}}, -{{{0x92, 0x7e, 0x50, 0x27, 0x72, 0xd7, 0x0c, 0xd6, 0x69, 0x96, 0x81, 0x35, 0x84, 0x94, 0x35, 0x8b, 0x6c, 0xaa, 0x62, 0x86, 0x6e, 0x1c, 0x15, 0xf3, 0x6c, 0xb3, 0xff, 0x65, 0x1b, 0xa2, 0x9b, 0x59}} , - {{0xe2, 0xa9, 0x65, 0x88, 0xc4, 0x50, 0xfa, 0xbb, 0x3b, 0x6e, 0x5f, 0x44, 0x01, 0xca, 0x97, 0xd4, 0xdd, 0xf6, 0xcd, 0x3f, 0x3f, 0xe5, 0x97, 0x67, 0x2b, 0x8c, 0x66, 0x0f, 0x35, 0x9b, 0xf5, 0x07}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xf1, 0x59, 0x27, 0xd8, 0xdb, 0x5a, 0x11, 0x5e, 0x82, 0xf3, 0x38, 0xff, 0x1c, 0xed, 0xfe, 0x3f, 0x64, 0x54, 0x3f, 0x7f, 0xd1, 0x81, 0xed, 0xef, 0x65, 0xc5, 0xcb, 0xfd, 0xe1, 0x80, 0xcd, 0x11}} , - {{0xe0, 0xdb, 0x22, 0x28, 0xe6, 0xff, 0x61, 0x9d, 0x41, 0x14, 0x2d, 0x3b, 0x26, 0x22, 0xdf, 0xf1, 0x34, 0x81, 0xe9, 0x45, 0xee, 0x0f, 0x98, 0x8b, 0xa6, 0x3f, 0xef, 0xf7, 0x43, 0x19, 0xf1, 0x43}}}, -{{{0xee, 0xf3, 0x00, 0xa1, 0x50, 0xde, 0xc0, 0xb6, 0x01, 0xe3, 0x8c, 0x3c, 0x4d, 0x31, 0xd2, 0xb0, 0x58, 0xcd, 0xed, 0x10, 0x4a, 0x7a, 0xef, 0x80, 0xa9, 0x19, 0x32, 0xf3, 0xd8, 0x33, 0x8c, 0x06}} , - {{0xcb, 0x7d, 0x4f, 0xff, 0x30, 0xd8, 0x12, 0x3b, 0x39, 0x1c, 0x06, 0xf9, 0x4c, 0x34, 0x35, 0x71, 0xb5, 0x16, 0x94, 0x67, 0xdf, 0xee, 0x11, 0xde, 0xa4, 0x1d, 0x88, 0x93, 0x35, 0xa9, 0x32, 0x10}}}, -{{{0xe9, 0xc3, 0xbc, 0x7b, 0x5c, 0xfc, 0xb2, 0xf9, 0xc9, 0x2f, 0xe5, 0xba, 0x3a, 0x0b, 0xab, 0x64, 0x38, 0x6f, 0x5b, 0x4b, 0x93, 0xda, 0x64, 0xec, 0x4d, 0x3d, 0xa0, 0xf5, 0xbb, 0xba, 0x47, 0x48}} , - {{0x60, 0xbc, 0x45, 0x1f, 0x23, 0xa2, 0x3b, 0x70, 0x76, 0xe6, 0x97, 0x99, 0x4f, 0x77, 0x54, 0x67, 0x30, 0x9a, 0xe7, 0x66, 0xd6, 0xcd, 0x2e, 0x51, 0x24, 0x2c, 0x42, 0x4a, 0x11, 0xfe, 0x6f, 0x7e}}}, -{{{0x87, 0xc0, 0xb1, 0xf0, 0xa3, 0x6f, 0x0c, 0x93, 0xa9, 0x0a, 0x72, 0xef, 0x5c, 0xbe, 0x65, 0x35, 0xa7, 0x6a, 0x4e, 0x2c, 0xbf, 0x21, 0x23, 0xe8, 0x2f, 0x97, 0xc7, 0x3e, 0xc8, 0x17, 0xac, 0x1e}} , - {{0x7b, 0xef, 0x21, 0xe5, 0x40, 0xcc, 0x1e, 0xdc, 0xd6, 0xbd, 0x97, 0x7a, 0x7c, 0x75, 0x86, 0x7a, 0x25, 0x5a, 0x6e, 0x7c, 0xe5, 0x51, 0x3c, 0x1b, 0x5b, 0x82, 0x9a, 0x07, 0x60, 0xa1, 0x19, 0x04}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x96, 0x88, 0xa6, 0xab, 0x8f, 0xe3, 0x3a, 0x49, 0xf8, 0xfe, 0x34, 0xe7, 0x6a, 0xb2, 0xfe, 0x40, 0x26, 0x74, 0x57, 0x4c, 0xf6, 0xd4, 0x99, 0xce, 0x5d, 0x7b, 0x2f, 0x67, 0xd6, 0x5a, 0xe4, 0x4e}} , - {{0x5c, 0x82, 0xb3, 0xbd, 0x55, 0x25, 0xf6, 0x6a, 0x93, 0xa4, 0x02, 0xc6, 0x7d, 0x5c, 0xb1, 0x2b, 0x5b, 0xff, 0xfb, 0x56, 0xf8, 0x01, 0x41, 0x90, 0xc6, 0xb6, 0xac, 0x4f, 0xfe, 0xa7, 0x41, 0x70}}}, -{{{0xdb, 0xfa, 0x9b, 0x2c, 0xd4, 0x23, 0x67, 0x2c, 0x8a, 0x63, 0x6c, 0x07, 0x26, 0x48, 0x4f, 0xc2, 0x03, 0xd2, 0x53, 0x20, 0x28, 0xed, 0x65, 0x71, 0x47, 0xa9, 0x16, 0x16, 0x12, 0xbc, 0x28, 0x33}} , - {{0x39, 0xc0, 0xfa, 0xfa, 0xcd, 0x33, 0x43, 0xc7, 0x97, 0x76, 0x9b, 0x93, 0x91, 0x72, 0xeb, 0xc5, 0x18, 0x67, 0x4c, 0x11, 0xf0, 0xf4, 0xe5, 0x73, 0xb2, 0x5c, 0x1b, 0xc2, 0x26, 0x3f, 0xbf, 0x2b}}}, -{{{0x86, 0xe6, 0x8c, 0x1d, 0xdf, 0xca, 0xfc, 0xd5, 0xf8, 0x3a, 0xc3, 0x44, 0x72, 0xe6, 0x78, 0x9d, 0x2b, 0x97, 0xf8, 0x28, 0x45, 0xb4, 0x20, 0xc9, 0x2a, 0x8c, 0x67, 0xaa, 0x11, 0xc5, 0x5b, 0x2f}} , - {{0x17, 0x0f, 0x86, 0x52, 0xd7, 0x9d, 0xc3, 0x44, 0x51, 0x76, 0x32, 0x65, 0xb4, 0x37, 0x81, 0x99, 0x46, 0x37, 0x62, 0xed, 0xcf, 0x64, 0x9d, 0x72, 0x40, 0x7a, 0x4c, 0x0b, 0x76, 0x2a, 0xfb, 0x56}}}, -{{{0x33, 0xa7, 0x90, 0x7c, 0xc3, 0x6f, 0x17, 0xa5, 0xa0, 0x67, 0x72, 0x17, 0xea, 0x7e, 0x63, 0x14, 0x83, 0xde, 0xc1, 0x71, 0x2d, 0x41, 0x32, 0x7a, 0xf3, 0xd1, 0x2b, 0xd8, 0x2a, 0xa6, 0x46, 0x36}} , - {{0xac, 0xcc, 0x6b, 0x7c, 0xf9, 0xb8, 0x8b, 0x08, 0x5c, 0xd0, 0x7d, 0x8f, 0x73, 0xea, 0x20, 0xda, 0x86, 0xca, 0x00, 0xc7, 0xad, 0x73, 0x4d, 0xe9, 0xe8, 0xa9, 0xda, 0x1f, 0x03, 0x06, 0xdd, 0x24}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x9c, 0xb2, 0x61, 0x0a, 0x98, 0x2a, 0xa5, 0xd7, 0xee, 0xa9, 0xac, 0x65, 0xcb, 0x0a, 0x1e, 0xe2, 0xbe, 0xdc, 0x85, 0x59, 0x0f, 0x9c, 0xa6, 0x57, 0x34, 0xa5, 0x87, 0xeb, 0x7b, 0x1e, 0x0c, 0x3c}} , - {{0x2f, 0xbd, 0x84, 0x63, 0x0d, 0xb5, 0xa0, 0xf0, 0x4b, 0x9e, 0x93, 0xc6, 0x34, 0x9a, 0x34, 0xff, 0x73, 0x19, 0x2f, 0x6e, 0x54, 0x45, 0x2c, 0x92, 0x31, 0x76, 0x34, 0xf1, 0xb2, 0x26, 0xe8, 0x74}}}, -{{{0x0a, 0x67, 0x90, 0x6d, 0x0c, 0x4c, 0xcc, 0xc0, 0xe6, 0xbd, 0xa7, 0x5e, 0x55, 0x8c, 0xcd, 0x58, 0x9b, 0x11, 0xa2, 0xbb, 0x4b, 0xb1, 0x43, 0x04, 0x3c, 0x55, 0xed, 0x23, 0xfe, 0xcd, 0xb1, 0x53}} , - {{0x05, 0xfb, 0x75, 0xf5, 0x01, 0xaf, 0x38, 0x72, 0x58, 0xfc, 0x04, 0x29, 0x34, 0x7a, 0x67, 0xa2, 0x08, 0x50, 0x6e, 0xd0, 0x2b, 0x73, 0xd5, 0xb8, 0xe4, 0x30, 0x96, 0xad, 0x45, 0xdf, 0xa6, 0x5c}}}, -{{{0x0d, 0x88, 0x1a, 0x90, 0x7e, 0xdc, 0xd8, 0xfe, 0xc1, 0x2f, 0x5d, 0x67, 0xee, 0x67, 0x2f, 0xed, 0x6f, 0x55, 0x43, 0x5f, 0x87, 0x14, 0x35, 0x42, 0xd3, 0x75, 0xae, 0xd5, 0xd3, 0x85, 0x1a, 0x76}} , - {{0x87, 0xc8, 0xa0, 0x6e, 0xe1, 0xb0, 0xad, 0x6a, 0x4a, 0x34, 0x71, 0xed, 0x7c, 0xd6, 0x44, 0x03, 0x65, 0x4a, 0x5c, 0x5c, 0x04, 0xf5, 0x24, 0x3f, 0xb0, 0x16, 0x5e, 0x8c, 0xb2, 0xd2, 0xc5, 0x20}}}, -{{{0x98, 0x83, 0xc2, 0x37, 0xa0, 0x41, 0xa8, 0x48, 0x5c, 0x5f, 0xbf, 0xc8, 0xfa, 0x24, 0xe0, 0x59, 0x2c, 0xbd, 0xf6, 0x81, 0x7e, 0x88, 0xe6, 0xca, 0x04, 0xd8, 0x5d, 0x60, 0xbb, 0x74, 0xa7, 0x0b}} , - {{0x21, 0x13, 0x91, 0xbf, 0x77, 0x7a, 0x33, 0xbc, 0xe9, 0x07, 0x39, 0x0a, 0xdd, 0x7d, 0x06, 0x10, 0x9a, 0xee, 0x47, 0x73, 0x1b, 0x15, 0x5a, 0xfb, 0xcd, 0x4d, 0xd0, 0xd2, 0x3a, 0x01, 0xba, 0x54}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x48, 0xd5, 0x39, 0x4a, 0x0b, 0x20, 0x6a, 0x43, 0xa0, 0x07, 0x82, 0x5e, 0x49, 0x7c, 0xc9, 0x47, 0xf1, 0x7c, 0x37, 0xb9, 0x23, 0xef, 0x6b, 0x46, 0x45, 0x8c, 0x45, 0x76, 0xdf, 0x14, 0x6b, 0x6e}} , - {{0x42, 0xc9, 0xca, 0x29, 0x4c, 0x76, 0x37, 0xda, 0x8a, 0x2d, 0x7c, 0x3a, 0x58, 0xf2, 0x03, 0xb4, 0xb5, 0xb9, 0x1a, 0x13, 0x2d, 0xde, 0x5f, 0x6b, 0x9d, 0xba, 0x52, 0xc9, 0x5d, 0xb3, 0xf3, 0x30}}}, -{{{0x4c, 0x6f, 0xfe, 0x6b, 0x0c, 0x62, 0xd7, 0x48, 0x71, 0xef, 0xb1, 0x85, 0x79, 0xc0, 0xed, 0x24, 0xb1, 0x08, 0x93, 0x76, 0x8e, 0xf7, 0x38, 0x8e, 0xeb, 0xfe, 0x80, 0x40, 0xaf, 0x90, 0x64, 0x49}} , - {{0x4a, 0x88, 0xda, 0xc1, 0x98, 0x44, 0x3c, 0x53, 0x4e, 0xdb, 0x4b, 0xb9, 0x12, 0x5f, 0xcd, 0x08, 0x04, 0xef, 0x75, 0xe7, 0xb1, 0x3a, 0xe5, 0x07, 0xfa, 0xca, 0x65, 0x7b, 0x72, 0x10, 0x64, 0x7f}}}, -{{{0x3d, 0x81, 0xf0, 0xeb, 0x16, 0xfd, 0x58, 0x33, 0x8d, 0x7c, 0x1a, 0xfb, 0x20, 0x2c, 0x8a, 0xee, 0x90, 0xbb, 0x33, 0x6d, 0x45, 0xe9, 0x8e, 0x99, 0x85, 0xe1, 0x08, 0x1f, 0xc5, 0xf1, 0xb5, 0x46}} , - {{0xe4, 0xe7, 0x43, 0x4b, 0xa0, 0x3f, 0x2b, 0x06, 0xba, 0x17, 0xae, 0x3d, 0xe6, 0xce, 0xbd, 0xb8, 0xed, 0x74, 0x11, 0x35, 0xec, 0x96, 0xfe, 0x31, 0xe3, 0x0e, 0x7a, 0x4e, 0xc9, 0x1d, 0xcb, 0x20}}}, -{{{0xe0, 0x67, 0xe9, 0x7b, 0xdb, 0x96, 0x5c, 0xb0, 0x32, 0xd0, 0x59, 0x31, 0x90, 0xdc, 0x92, 0x97, 0xac, 0x09, 0x38, 0x31, 0x0f, 0x7e, 0xd6, 0x5d, 0xd0, 0x06, 0xb6, 0x1f, 0xea, 0xf0, 0x5b, 0x07}} , - {{0x81, 0x9f, 0xc7, 0xde, 0x6b, 0x41, 0x22, 0x35, 0x14, 0x67, 0x77, 0x3e, 0x90, 0x81, 0xb0, 0xd9, 0x85, 0x4c, 0xca, 0x9b, 0x3f, 0x04, 0x59, 0xd6, 0xaa, 0x17, 0xc3, 0x88, 0x34, 0x37, 0xba, 0x43}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x4c, 0xb6, 0x69, 0xc8, 0x81, 0x95, 0x94, 0x33, 0x92, 0x34, 0xe9, 0x3c, 0x84, 0x0d, 0x3d, 0x5a, 0x37, 0x9c, 0x22, 0xa0, 0xaa, 0x65, 0xce, 0xb4, 0xc2, 0x2d, 0x66, 0x67, 0x02, 0xff, 0x74, 0x10}} , - {{0x22, 0xb0, 0xd5, 0xe6, 0xc7, 0xef, 0xb1, 0xa7, 0x13, 0xda, 0x60, 0xb4, 0x80, 0xc1, 0x42, 0x7d, 0x10, 0x70, 0x97, 0x04, 0x4d, 0xda, 0x23, 0x89, 0xc2, 0x0e, 0x68, 0xcb, 0xde, 0xe0, 0x9b, 0x29}}}, -{{{0x33, 0xfe, 0x42, 0x2a, 0x36, 0x2b, 0x2e, 0x36, 0x64, 0x5c, 0x8b, 0xcc, 0x81, 0x6a, 0x15, 0x08, 0xa1, 0x27, 0xe8, 0x57, 0xe5, 0x78, 0x8e, 0xf2, 0x58, 0x19, 0x12, 0x42, 0xae, 0xc4, 0x63, 0x3e}} , - {{0x78, 0x96, 0x9c, 0xa7, 0xca, 0x80, 0xae, 0x02, 0x85, 0xb1, 0x7c, 0x04, 0x5c, 0xc1, 0x5b, 0x26, 0xc1, 0xba, 0xed, 0xa5, 0x59, 0x70, 0x85, 0x8c, 0x8c, 0xe8, 0x87, 0xac, 0x6a, 0x28, 0x99, 0x35}}}, -{{{0x9f, 0x04, 0x08, 0x28, 0xbe, 0x87, 0xda, 0x80, 0x28, 0x38, 0xde, 0x9f, 0xcd, 0xe4, 0xe3, 0x62, 0xfb, 0x2e, 0x46, 0x8d, 0x01, 0xb3, 0x06, 0x51, 0xd4, 0x19, 0x3b, 0x11, 0xfa, 0xe2, 0xad, 0x1e}} , - {{0xa0, 0x20, 0x99, 0x69, 0x0a, 0xae, 0xa3, 0x70, 0x4e, 0x64, 0x80, 0xb7, 0x85, 0x9c, 0x87, 0x54, 0x43, 0x43, 0x55, 0x80, 0x6d, 0x8d, 0x7c, 0xa9, 0x64, 0xca, 0x6c, 0x2e, 0x21, 0xd8, 0xc8, 0x6c}}}, -{{{0x91, 0x4a, 0x07, 0xad, 0x08, 0x75, 0xc1, 0x4f, 0xa4, 0xb2, 0xc3, 0x6f, 0x46, 0x3e, 0xb1, 0xce, 0x52, 0xab, 0x67, 0x09, 0x54, 0x48, 0x6b, 0x6c, 0xd7, 0x1d, 0x71, 0x76, 0xcb, 0xff, 0xdd, 0x31}} , - {{0x36, 0x88, 0xfa, 0xfd, 0xf0, 0x36, 0x6f, 0x07, 0x74, 0x88, 0x50, 0xd0, 0x95, 0x38, 0x4a, 0x48, 0x2e, 0x07, 0x64, 0x97, 0x11, 0x76, 0x01, 0x1a, 0x27, 0x4d, 0x8e, 0x25, 0x9a, 0x9b, 0x1c, 0x22}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xbe, 0x57, 0xbd, 0x0e, 0x0f, 0xac, 0x5e, 0x76, 0xa3, 0x71, 0xad, 0x2b, 0x10, 0x45, 0x02, 0xec, 0x59, 0xd5, 0x5d, 0xa9, 0x44, 0xcc, 0x25, 0x4c, 0xb3, 0x3c, 0x5b, 0x69, 0x07, 0x55, 0x26, 0x6b}} , - {{0x30, 0x6b, 0xd4, 0xa7, 0x51, 0x29, 0xe3, 0xf9, 0x7a, 0x75, 0x2a, 0x82, 0x2f, 0xd6, 0x1d, 0x99, 0x2b, 0x80, 0xd5, 0x67, 0x1e, 0x15, 0x9d, 0xca, 0xfd, 0xeb, 0xac, 0x97, 0x35, 0x09, 0x7f, 0x3f}}}, -{{{0x35, 0x0d, 0x34, 0x0a, 0xb8, 0x67, 0x56, 0x29, 0x20, 0xf3, 0x19, 0x5f, 0xe2, 0x83, 0x42, 0x73, 0x53, 0xa8, 0xc5, 0x02, 0x19, 0x33, 0xb4, 0x64, 0xbd, 0xc3, 0x87, 0x8c, 0xd7, 0x76, 0xed, 0x25}} , - {{0x47, 0x39, 0x37, 0x76, 0x0d, 0x1d, 0x0c, 0xf5, 0x5a, 0x6d, 0x43, 0x88, 0x99, 0x15, 0xb4, 0x52, 0x0f, 0x2a, 0xb3, 0xb0, 0x3f, 0xa6, 0xb3, 0x26, 0xb3, 0xc7, 0x45, 0xf5, 0x92, 0x5f, 0x9b, 0x17}}}, -{{{0x9d, 0x23, 0xbd, 0x15, 0xfe, 0x52, 0x52, 0x15, 0x26, 0x79, 0x86, 0xba, 0x06, 0x56, 0x66, 0xbb, 0x8c, 0x2e, 0x10, 0x11, 0xd5, 0x4a, 0x18, 0x52, 0xda, 0x84, 0x44, 0xf0, 0x3e, 0xe9, 0x8c, 0x35}} , - {{0xad, 0xa0, 0x41, 0xec, 0xc8, 0x4d, 0xb9, 0xd2, 0x6e, 0x96, 0x4e, 0x5b, 0xc5, 0xc2, 0xa0, 0x1b, 0xcf, 0x0c, 0xbf, 0x17, 0x66, 0x57, 0xc1, 0x17, 0x90, 0x45, 0x71, 0xc2, 0xe1, 0x24, 0xeb, 0x27}}}, -{{{0x2c, 0xb9, 0x42, 0xa4, 0xaf, 0x3b, 0x42, 0x0e, 0xc2, 0x0f, 0xf2, 0xea, 0x83, 0xaf, 0x9a, 0x13, 0x17, 0xb0, 0xbd, 0x89, 0x17, 0xe3, 0x72, 0xcb, 0x0e, 0x76, 0x7e, 0x41, 0x63, 0x04, 0x88, 0x71}} , - {{0x75, 0x78, 0x38, 0x86, 0x57, 0xdd, 0x9f, 0xee, 0x54, 0x70, 0x65, 0xbf, 0xf1, 0x2c, 0xe0, 0x39, 0x0d, 0xe3, 0x89, 0xfd, 0x8e, 0x93, 0x4f, 0x43, 0xdc, 0xd5, 0x5b, 0xde, 0xf9, 0x98, 0xe5, 0x7b}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xe7, 0x3b, 0x65, 0x11, 0xdf, 0xb2, 0xf2, 0x63, 0x94, 0x12, 0x6f, 0x5c, 0x9e, 0x77, 0xc1, 0xb6, 0xd8, 0xab, 0x58, 0x7a, 0x1d, 0x95, 0x73, 0xdd, 0xe7, 0xe3, 0x6f, 0xf2, 0x03, 0x1d, 0xdb, 0x76}} , - {{0xae, 0x06, 0x4e, 0x2c, 0x52, 0x1b, 0xbc, 0x5a, 0x5a, 0xa5, 0xbe, 0x27, 0xbd, 0xeb, 0xe1, 0x14, 0x17, 0x68, 0x26, 0x07, 0x03, 0xd1, 0x18, 0x0b, 0xdf, 0xf1, 0x06, 0x5c, 0xa6, 0x1b, 0xb9, 0x24}}}, -{{{0xc5, 0x66, 0x80, 0x13, 0x0e, 0x48, 0x8c, 0x87, 0x31, 0x84, 0xb4, 0x60, 0xed, 0xc5, 0xec, 0xb6, 0xc5, 0x05, 0x33, 0x5f, 0x2f, 0x7d, 0x40, 0xb6, 0x32, 0x1d, 0x38, 0x74, 0x1b, 0xf1, 0x09, 0x3d}} , - {{0xd4, 0x69, 0x82, 0xbc, 0x8d, 0xf8, 0x34, 0x36, 0x75, 0x55, 0x18, 0x55, 0x58, 0x3c, 0x79, 0xaf, 0x26, 0x80, 0xab, 0x9b, 0x95, 0x00, 0xf1, 0xcb, 0xda, 0xc1, 0x9f, 0xf6, 0x2f, 0xa2, 0xf4, 0x45}}}, -{{{0x17, 0xbe, 0xeb, 0x85, 0xed, 0x9e, 0xcd, 0x56, 0xf5, 0x17, 0x45, 0x42, 0xb4, 0x1f, 0x44, 0x4c, 0x05, 0x74, 0x15, 0x47, 0x00, 0xc6, 0x6a, 0x3d, 0x24, 0x09, 0x0d, 0x58, 0xb1, 0x42, 0xd7, 0x04}} , - {{0x8d, 0xbd, 0xa3, 0xc4, 0x06, 0x9b, 0x1f, 0x90, 0x58, 0x60, 0x74, 0xb2, 0x00, 0x3b, 0x3c, 0xd2, 0xda, 0x82, 0xbb, 0x10, 0x90, 0x69, 0x92, 0xa9, 0xb4, 0x30, 0x81, 0xe3, 0x7c, 0xa8, 0x89, 0x45}}}, -{{{0x3f, 0xdc, 0x05, 0xcb, 0x41, 0x3c, 0xc8, 0x23, 0x04, 0x2c, 0x38, 0x99, 0xe3, 0x68, 0x55, 0xf9, 0xd3, 0x32, 0xc7, 0xbf, 0xfa, 0xd4, 0x1b, 0x5d, 0xde, 0xdc, 0x10, 0x42, 0xc0, 0x42, 0xd9, 0x75}} , - {{0x2d, 0xab, 0x35, 0x4e, 0x87, 0xc4, 0x65, 0x97, 0x67, 0x24, 0xa4, 0x47, 0xad, 0x3f, 0x8e, 0xf3, 0xcb, 0x31, 0x17, 0x77, 0xc5, 0xe2, 0xd7, 0x8f, 0x3c, 0xc1, 0xcd, 0x56, 0x48, 0xc1, 0x6c, 0x69}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x14, 0xae, 0x5f, 0x88, 0x7b, 0xa5, 0x90, 0xdf, 0x10, 0xb2, 0x8b, 0x5e, 0x24, 0x17, 0xc3, 0xa3, 0xd4, 0x0f, 0x92, 0x61, 0x1a, 0x19, 0x5a, 0xad, 0x76, 0xbd, 0xd8, 0x1c, 0xdd, 0xe0, 0x12, 0x6d}} , - {{0x8e, 0xbd, 0x70, 0x8f, 0x02, 0xa3, 0x24, 0x4d, 0x5a, 0x67, 0xc4, 0xda, 0xf7, 0x20, 0x0f, 0x81, 0x5b, 0x7a, 0x05, 0x24, 0x67, 0x83, 0x0b, 0x2a, 0x80, 0xe7, 0xfd, 0x74, 0x4b, 0x9e, 0x5c, 0x0d}}}, -{{{0x94, 0xd5, 0x5f, 0x1f, 0xa2, 0xfb, 0xeb, 0xe1, 0x07, 0x34, 0xf8, 0x20, 0xad, 0x81, 0x30, 0x06, 0x2d, 0xa1, 0x81, 0x95, 0x36, 0xcf, 0x11, 0x0b, 0xaf, 0xc1, 0x2b, 0x9a, 0x6c, 0x55, 0xc1, 0x16}} , - {{0x36, 0x4f, 0xf1, 0x5e, 0x74, 0x35, 0x13, 0x28, 0xd7, 0x11, 0xcf, 0xb8, 0xde, 0x93, 0xb3, 0x05, 0xb8, 0xb5, 0x73, 0xe9, 0xeb, 0xad, 0x19, 0x1e, 0x89, 0x0f, 0x8b, 0x15, 0xd5, 0x8c, 0xe3, 0x23}}}, -{{{0x33, 0x79, 0xe7, 0x18, 0xe6, 0x0f, 0x57, 0x93, 0x15, 0xa0, 0xa7, 0xaa, 0xc4, 0xbf, 0x4f, 0x30, 0x74, 0x95, 0x5e, 0x69, 0x4a, 0x5b, 0x45, 0xe4, 0x00, 0xeb, 0x23, 0x74, 0x4c, 0xdf, 0x6b, 0x45}} , - {{0x97, 0x29, 0x6c, 0xc4, 0x42, 0x0b, 0xdd, 0xc0, 0x29, 0x5c, 0x9b, 0x34, 0x97, 0xd0, 0xc7, 0x79, 0x80, 0x63, 0x74, 0xe4, 0x8e, 0x37, 0xb0, 0x2b, 0x7c, 0xe8, 0x68, 0x6c, 0xc3, 0x82, 0x97, 0x57}}}, -{{{0x22, 0xbe, 0x83, 0xb6, 0x4b, 0x80, 0x6b, 0x43, 0x24, 0x5e, 0xef, 0x99, 0x9b, 0xa8, 0xfc, 0x25, 0x8d, 0x3b, 0x03, 0x94, 0x2b, 0x3e, 0xe7, 0x95, 0x76, 0x9b, 0xcc, 0x15, 0xdb, 0x32, 0xe6, 0x66}} , - {{0x84, 0xf0, 0x4a, 0x13, 0xa6, 0xd6, 0xfa, 0x93, 0x46, 0x07, 0xf6, 0x7e, 0x5c, 0x6d, 0x5e, 0xf6, 0xa6, 0xe7, 0x48, 0xf0, 0x06, 0xea, 0xff, 0x90, 0xc1, 0xcc, 0x4c, 0x19, 0x9c, 0x3c, 0x4e, 0x53}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x2a, 0x50, 0xe3, 0x07, 0x15, 0x59, 0xf2, 0x8b, 0x81, 0xf2, 0xf3, 0xd3, 0x6c, 0x99, 0x8c, 0x70, 0x67, 0xec, 0xcc, 0xee, 0x9e, 0x59, 0x45, 0x59, 0x7d, 0x47, 0x75, 0x69, 0xf5, 0x24, 0x93, 0x5d}} , - {{0x6a, 0x4f, 0x1b, 0xbe, 0x6b, 0x30, 0xcf, 0x75, 0x46, 0xe3, 0x7b, 0x9d, 0xfc, 0xcd, 0xd8, 0x5c, 0x1f, 0xb4, 0xc8, 0xe2, 0x24, 0xec, 0x1a, 0x28, 0x05, 0x32, 0x57, 0xfd, 0x3c, 0x5a, 0x98, 0x10}}}, -{{{0xa3, 0xdb, 0xf7, 0x30, 0xd8, 0xc2, 0x9a, 0xe1, 0xd3, 0xce, 0x22, 0xe5, 0x80, 0x1e, 0xd9, 0xe4, 0x1f, 0xab, 0xc0, 0x71, 0x1a, 0x86, 0x0e, 0x27, 0x99, 0x5b, 0xfa, 0x76, 0x99, 0xb0, 0x08, 0x3c}} , - {{0x2a, 0x93, 0xd2, 0x85, 0x1b, 0x6a, 0x5d, 0xa6, 0xee, 0xd1, 0xd1, 0x33, 0xbd, 0x6a, 0x36, 0x73, 0x37, 0x3a, 0x44, 0xb4, 0xec, 0xa9, 0x7a, 0xde, 0x83, 0x40, 0xd7, 0xdf, 0x28, 0xba, 0xa2, 0x30}}}, -{{{0xd3, 0xb5, 0x6d, 0x05, 0x3f, 0x9f, 0xf3, 0x15, 0x8d, 0x7c, 0xca, 0xc9, 0xfc, 0x8a, 0x7c, 0x94, 0xb0, 0x63, 0x36, 0x9b, 0x78, 0xd1, 0x91, 0x1f, 0x93, 0xd8, 0x57, 0x43, 0xde, 0x76, 0xa3, 0x43}} , - {{0x9b, 0x35, 0xe2, 0xa9, 0x3d, 0x32, 0x1e, 0xbb, 0x16, 0x28, 0x70, 0xe9, 0x45, 0x2f, 0x8f, 0x70, 0x7f, 0x08, 0x7e, 0x53, 0xc4, 0x7a, 0xbf, 0xf7, 0xe1, 0xa4, 0x6a, 0xd8, 0xac, 0x64, 0x1b, 0x11}}}, -{{{0xb2, 0xeb, 0x47, 0x46, 0x18, 0x3e, 0x1f, 0x99, 0x0c, 0xcc, 0xf1, 0x2c, 0xe0, 0xe7, 0x8f, 0xe0, 0x01, 0x7e, 0x65, 0xb8, 0x0c, 0xd0, 0xfb, 0xc8, 0xb9, 0x90, 0x98, 0x33, 0x61, 0x3b, 0xd8, 0x27}} , - {{0xa0, 0xbe, 0x72, 0x3a, 0x50, 0x4b, 0x74, 0xab, 0x01, 0xc8, 0x93, 0xc5, 0xe4, 0xc7, 0x08, 0x6c, 0xb4, 0xca, 0xee, 0xeb, 0x8e, 0xd7, 0x4e, 0x26, 0xc6, 0x1d, 0xe2, 0x71, 0xaf, 0x89, 0xa0, 0x2a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x98, 0x0b, 0xe4, 0xde, 0xdb, 0xa8, 0xfa, 0x82, 0x74, 0x06, 0x52, 0x6d, 0x08, 0x52, 0x8a, 0xff, 0x62, 0xc5, 0x6a, 0x44, 0x0f, 0x51, 0x8c, 0x1f, 0x6e, 0xb6, 0xc6, 0x2c, 0x81, 0xd3, 0x76, 0x46}} , - {{0xf4, 0x29, 0x74, 0x2e, 0x80, 0xa7, 0x1a, 0x8f, 0xf6, 0xbd, 0xd6, 0x8e, 0xbf, 0xc1, 0x95, 0x2a, 0xeb, 0xa0, 0x7f, 0x45, 0xa0, 0x50, 0x14, 0x05, 0xb1, 0x57, 0x4c, 0x74, 0xb7, 0xe2, 0x89, 0x7d}}}, -{{{0x07, 0xee, 0xa7, 0xad, 0xb7, 0x09, 0x0b, 0x49, 0x4e, 0xbf, 0xca, 0xe5, 0x21, 0xe6, 0xe6, 0xaf, 0xd5, 0x67, 0xf3, 0xce, 0x7e, 0x7c, 0x93, 0x7b, 0x5a, 0x10, 0x12, 0x0e, 0x6c, 0x06, 0x11, 0x75}} , - {{0xd5, 0xfc, 0x86, 0xa3, 0x3b, 0xa3, 0x3e, 0x0a, 0xfb, 0x0b, 0xf7, 0x36, 0xb1, 0x5b, 0xda, 0x70, 0xb7, 0x00, 0xa7, 0xda, 0x88, 0x8f, 0x84, 0xa8, 0xbc, 0x1c, 0x39, 0xb8, 0x65, 0xf3, 0x4d, 0x60}}}, -{{{0x96, 0x9d, 0x31, 0xf4, 0xa2, 0xbe, 0x81, 0xb9, 0xa5, 0x59, 0x9e, 0xba, 0x07, 0xbe, 0x74, 0x58, 0xd8, 0xeb, 0xc5, 0x9f, 0x3d, 0xd1, 0xf4, 0xae, 0xce, 0x53, 0xdf, 0x4f, 0xc7, 0x2a, 0x89, 0x4d}} , - {{0x29, 0xd8, 0xf2, 0xaa, 0xe9, 0x0e, 0xf7, 0x2e, 0x5f, 0x9d, 0x8a, 0x5b, 0x09, 0xed, 0xc9, 0x24, 0x22, 0xf4, 0x0f, 0x25, 0x8f, 0x1c, 0x84, 0x6e, 0x34, 0x14, 0x6c, 0xea, 0xb3, 0x86, 0x5d, 0x04}}}, -{{{0x07, 0x98, 0x61, 0xe8, 0x6a, 0xd2, 0x81, 0x49, 0x25, 0xd5, 0x5b, 0x18, 0xc7, 0x35, 0x52, 0x51, 0xa4, 0x46, 0xad, 0x18, 0x0d, 0xc9, 0x5f, 0x18, 0x91, 0x3b, 0xb4, 0xc0, 0x60, 0x59, 0x8d, 0x66}} , - {{0x03, 0x1b, 0x79, 0x53, 0x6e, 0x24, 0xae, 0x57, 0xd9, 0x58, 0x09, 0x85, 0x48, 0xa2, 0xd3, 0xb5, 0xe2, 0x4d, 0x11, 0x82, 0xe6, 0x86, 0x3c, 0xe9, 0xb1, 0x00, 0x19, 0xc2, 0x57, 0xf7, 0x66, 0x7a}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x0f, 0xe3, 0x89, 0x03, 0xd7, 0x22, 0x95, 0x9f, 0xca, 0xb4, 0x8d, 0x9e, 0x6d, 0x97, 0xff, 0x8d, 0x21, 0x59, 0x07, 0xef, 0x03, 0x2d, 0x5e, 0xf8, 0x44, 0x46, 0xe7, 0x85, 0x80, 0xc5, 0x89, 0x50}} , - {{0x8b, 0xd8, 0x53, 0x86, 0x24, 0x86, 0x29, 0x52, 0x01, 0xfa, 0x20, 0xc3, 0x4e, 0x95, 0xcb, 0xad, 0x7b, 0x34, 0x94, 0x30, 0xb7, 0x7a, 0xfa, 0x96, 0x41, 0x60, 0x2b, 0xcb, 0x59, 0xb9, 0xca, 0x50}}}, -{{{0xc2, 0x5b, 0x9b, 0x78, 0x23, 0x1b, 0x3a, 0x88, 0x94, 0x5f, 0x0a, 0x9b, 0x98, 0x2b, 0x6e, 0x53, 0x11, 0xf6, 0xff, 0xc6, 0x7d, 0x42, 0xcc, 0x02, 0x80, 0x40, 0x0d, 0x1e, 0xfb, 0xaf, 0x61, 0x07}} , - {{0xb0, 0xe6, 0x2f, 0x81, 0x70, 0xa1, 0x2e, 0x39, 0x04, 0x7c, 0xc4, 0x2c, 0x87, 0x45, 0x4a, 0x5b, 0x69, 0x97, 0xac, 0x6d, 0x2c, 0x10, 0x42, 0x7c, 0x3b, 0x15, 0x70, 0x60, 0x0e, 0x11, 0x6d, 0x3a}}}, -{{{0x9b, 0x18, 0x80, 0x5e, 0xdb, 0x05, 0xbd, 0xc6, 0xb7, 0x3c, 0xc2, 0x40, 0x4d, 0x5d, 0xce, 0x97, 0x8a, 0x34, 0x15, 0xab, 0x28, 0x5d, 0x10, 0xf0, 0x37, 0x0c, 0xcc, 0x16, 0xfa, 0x1f, 0x33, 0x0d}} , - {{0x19, 0xf9, 0x35, 0xaa, 0x59, 0x1a, 0x0c, 0x5c, 0x06, 0xfc, 0x6a, 0x0b, 0x97, 0x53, 0x36, 0xfc, 0x2a, 0xa5, 0x5a, 0x9b, 0x30, 0xef, 0x23, 0xaf, 0x39, 0x5d, 0x9a, 0x6b, 0x75, 0x57, 0x48, 0x0b}}}, -{{{0x26, 0xdc, 0x76, 0x3b, 0xfc, 0xf9, 0x9c, 0x3f, 0x89, 0x0b, 0x62, 0x53, 0xaf, 0x83, 0x01, 0x2e, 0xbc, 0x6a, 0xc6, 0x03, 0x0d, 0x75, 0x2a, 0x0d, 0xe6, 0x94, 0x54, 0xcf, 0xb3, 0xe5, 0x96, 0x25}} , - {{0xfe, 0x82, 0xb1, 0x74, 0x31, 0x8a, 0xa7, 0x6f, 0x56, 0xbd, 0x8d, 0xf4, 0xe0, 0x94, 0x51, 0x59, 0xde, 0x2c, 0x5a, 0xf4, 0x84, 0x6b, 0x4a, 0x88, 0x93, 0xc0, 0x0c, 0x9a, 0xac, 0xa7, 0xa0, 0x68}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x25, 0x0d, 0xd6, 0xc7, 0x23, 0x47, 0x10, 0xad, 0xc7, 0x08, 0x5c, 0x87, 0x87, 0x93, 0x98, 0x18, 0xb8, 0xd3, 0x9c, 0xac, 0x5a, 0x3d, 0xc5, 0x75, 0xf8, 0x49, 0x32, 0x14, 0xcc, 0x51, 0x96, 0x24}} , - {{0x65, 0x9c, 0x5d, 0xf0, 0x37, 0x04, 0xf0, 0x34, 0x69, 0x2a, 0xf0, 0xa5, 0x64, 0xca, 0xde, 0x2b, 0x5b, 0x15, 0x10, 0xd2, 0xab, 0x06, 0xdd, 0xc4, 0xb0, 0xb6, 0x5b, 0xc1, 0x17, 0xdf, 0x8f, 0x02}}}, -{{{0xbd, 0x59, 0x3d, 0xbf, 0x5c, 0x31, 0x44, 0x2c, 0x32, 0x94, 0x04, 0x60, 0x84, 0x0f, 0xad, 0x00, 0xb6, 0x8f, 0xc9, 0x1d, 0xcc, 0x5c, 0xa2, 0x49, 0x0e, 0x50, 0x91, 0x08, 0x9a, 0x43, 0x55, 0x05}} , - {{0x5d, 0x93, 0x55, 0xdf, 0x9b, 0x12, 0x19, 0xec, 0x93, 0x85, 0x42, 0x9e, 0x66, 0x0f, 0x9d, 0xaf, 0x99, 0xaf, 0x26, 0x89, 0xbc, 0x61, 0xfd, 0xff, 0xce, 0x4b, 0xf4, 0x33, 0x95, 0xc9, 0x35, 0x58}}}, -{{{0x12, 0x55, 0xf9, 0xda, 0xcb, 0x44, 0xa7, 0xdc, 0x57, 0xe2, 0xf9, 0x9a, 0xe6, 0x07, 0x23, 0x60, 0x54, 0xa7, 0x39, 0xa5, 0x9b, 0x84, 0x56, 0x6e, 0xaa, 0x8b, 0x8f, 0xb0, 0x2c, 0x87, 0xaf, 0x67}} , - {{0x00, 0xa9, 0x4c, 0xb2, 0x12, 0xf8, 0x32, 0xa8, 0x7a, 0x00, 0x4b, 0x49, 0x32, 0xba, 0x1f, 0x5d, 0x44, 0x8e, 0x44, 0x7a, 0xdc, 0x11, 0xfb, 0x39, 0x08, 0x57, 0x87, 0xa5, 0x12, 0x42, 0x93, 0x0e}}}, -{{{0x17, 0xb4, 0xae, 0x72, 0x59, 0xd0, 0xaa, 0xa8, 0x16, 0x8b, 0x63, 0x11, 0xb3, 0x43, 0x04, 0xda, 0x0c, 0xa8, 0xb7, 0x68, 0xdd, 0x4e, 0x54, 0xe7, 0xaf, 0x5d, 0x5d, 0x05, 0x76, 0x36, 0xec, 0x0d}} , - {{0x6d, 0x7c, 0x82, 0x32, 0x38, 0x55, 0x57, 0x74, 0x5b, 0x7d, 0xc3, 0xc4, 0xfb, 0x06, 0x29, 0xf0, 0x13, 0x55, 0x54, 0xc6, 0xa7, 0xdc, 0x4c, 0x9f, 0x98, 0x49, 0x20, 0xa8, 0xc3, 0x8d, 0xfa, 0x48}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x87, 0x47, 0x9d, 0xe9, 0x25, 0xd5, 0xe3, 0x47, 0x78, 0xdf, 0x85, 0xa7, 0x85, 0x5e, 0x7a, 0x4c, 0x5f, 0x79, 0x1a, 0xf3, 0xa2, 0xb2, 0x28, 0xa0, 0x9c, 0xdd, 0x30, 0x40, 0xd4, 0x38, 0xbd, 0x28}} , - {{0xfc, 0xbb, 0xd5, 0x78, 0x6d, 0x1d, 0xd4, 0x99, 0xb4, 0xaa, 0x44, 0x44, 0x7a, 0x1b, 0xd8, 0xfe, 0xb4, 0x99, 0xb9, 0xcc, 0xe7, 0xc4, 0xd3, 0x3a, 0x73, 0x83, 0x41, 0x5c, 0x40, 0xd7, 0x2d, 0x55}}}, -{{{0x26, 0xe1, 0x7b, 0x5f, 0xe5, 0xdc, 0x3f, 0x7d, 0xa1, 0xa7, 0x26, 0x44, 0x22, 0x23, 0xc0, 0x8f, 0x7d, 0xf1, 0xb5, 0x11, 0x47, 0x7b, 0x19, 0xd4, 0x75, 0x6f, 0x1e, 0xa5, 0x27, 0xfe, 0xc8, 0x0e}} , - {{0xd3, 0x11, 0x3d, 0xab, 0xef, 0x2c, 0xed, 0xb1, 0x3d, 0x7c, 0x32, 0x81, 0x6b, 0xfe, 0xf8, 0x1c, 0x3c, 0x7b, 0xc0, 0x61, 0xdf, 0xb8, 0x75, 0x76, 0x7f, 0xaa, 0xd8, 0x93, 0xaf, 0x3d, 0xe8, 0x3d}}}, -{{{0xfd, 0x5b, 0x4e, 0x8d, 0xb6, 0x7e, 0x82, 0x9b, 0xef, 0xce, 0x04, 0x69, 0x51, 0x52, 0xff, 0xef, 0xa0, 0x52, 0xb5, 0x79, 0x17, 0x5e, 0x2f, 0xde, 0xd6, 0x3c, 0x2d, 0xa0, 0x43, 0xb4, 0x0b, 0x19}} , - {{0xc0, 0x61, 0x48, 0x48, 0x17, 0xf4, 0x9e, 0x18, 0x51, 0x2d, 0xea, 0x2f, 0xf2, 0xf2, 0xe0, 0xa3, 0x14, 0xb7, 0x8b, 0x3a, 0x30, 0xf5, 0x81, 0xc1, 0x5d, 0x71, 0x39, 0x62, 0x55, 0x1f, 0x60, 0x5a}}}, -{{{0xe5, 0x89, 0x8a, 0x76, 0x6c, 0xdb, 0x4d, 0x0a, 0x5b, 0x72, 0x9d, 0x59, 0x6e, 0x63, 0x63, 0x18, 0x7c, 0xe3, 0xfa, 0xe2, 0xdb, 0xa1, 0x8d, 0xf4, 0xa5, 0xd7, 0x16, 0xb2, 0xd0, 0xb3, 0x3f, 0x39}} , - {{0xce, 0x60, 0x09, 0x6c, 0xf5, 0x76, 0x17, 0x24, 0x80, 0x3a, 0x96, 0xc7, 0x94, 0x2e, 0xf7, 0x6b, 0xef, 0xb5, 0x05, 0x96, 0xef, 0xd3, 0x7b, 0x51, 0xda, 0x05, 0x44, 0x67, 0xbc, 0x07, 0x21, 0x4e}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xe9, 0x73, 0x6f, 0x21, 0xb9, 0xde, 0x22, 0x7d, 0xeb, 0x97, 0x31, 0x10, 0xa3, 0xea, 0xe1, 0xc6, 0x37, 0xeb, 0x8f, 0x43, 0x58, 0xde, 0x41, 0x64, 0x0e, 0x3e, 0x07, 0x99, 0x3d, 0xf1, 0xdf, 0x1e}} , - {{0xf8, 0xad, 0x43, 0xc2, 0x17, 0x06, 0xe2, 0xe4, 0xa9, 0x86, 0xcd, 0x18, 0xd7, 0x78, 0xc8, 0x74, 0x66, 0xd2, 0x09, 0x18, 0xa5, 0xf1, 0xca, 0xa6, 0x62, 0x92, 0xc1, 0xcb, 0x00, 0xeb, 0x42, 0x2e}}}, -{{{0x7b, 0x34, 0x24, 0x4c, 0xcf, 0x38, 0xe5, 0x6c, 0x0a, 0x01, 0x2c, 0x22, 0x0b, 0x24, 0x38, 0xad, 0x24, 0x7e, 0x19, 0xf0, 0x6c, 0xf9, 0x31, 0xf4, 0x35, 0x11, 0xf6, 0x46, 0x33, 0x3a, 0x23, 0x59}} , - {{0x20, 0x0b, 0xa1, 0x08, 0x19, 0xad, 0x39, 0x54, 0xea, 0x3e, 0x23, 0x09, 0xb6, 0xe2, 0xd2, 0xbc, 0x4d, 0xfc, 0x9c, 0xf0, 0x13, 0x16, 0x22, 0x3f, 0xb9, 0xd2, 0x11, 0x86, 0x90, 0x55, 0xce, 0x3c}}}, -{{{0xc4, 0x0b, 0x4b, 0x62, 0x99, 0x37, 0x84, 0x3f, 0x74, 0xa2, 0xf9, 0xce, 0xe2, 0x0b, 0x0f, 0x2a, 0x3d, 0xa3, 0xe3, 0xdb, 0x5a, 0x9d, 0x93, 0xcc, 0xa5, 0xef, 0x82, 0x91, 0x1d, 0xe6, 0x6c, 0x68}} , - {{0xa3, 0x64, 0x17, 0x9b, 0x8b, 0xc8, 0x3a, 0x61, 0xe6, 0x9d, 0xc6, 0xed, 0x7b, 0x03, 0x52, 0x26, 0x9d, 0x3a, 0xb3, 0x13, 0xcc, 0x8a, 0xfd, 0x2c, 0x1a, 0x1d, 0xed, 0x13, 0xd0, 0x55, 0x57, 0x0e}}}, -{{{0x1a, 0xea, 0xbf, 0xfd, 0x4a, 0x3c, 0x8e, 0xec, 0x29, 0x7e, 0x77, 0x77, 0x12, 0x99, 0xd7, 0x84, 0xf9, 0x55, 0x7f, 0xf1, 0x8b, 0xb4, 0xd2, 0x95, 0xa3, 0x8d, 0xf0, 0x8a, 0xa7, 0xeb, 0x82, 0x4b}} , - {{0x2c, 0x28, 0xf4, 0x3a, 0xf6, 0xde, 0x0a, 0xe0, 0x41, 0x44, 0x23, 0xf8, 0x3f, 0x03, 0x64, 0x9f, 0xc3, 0x55, 0x4c, 0xc6, 0xc1, 0x94, 0x1c, 0x24, 0x5d, 0x5f, 0x92, 0x45, 0x96, 0x57, 0x37, 0x14}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xc1, 0xcd, 0x90, 0x66, 0xb9, 0x76, 0xa0, 0x5b, 0xa5, 0x85, 0x75, 0x23, 0xf9, 0x89, 0xa5, 0x82, 0xb2, 0x6f, 0xb1, 0xeb, 0xc4, 0x69, 0x6f, 0x18, 0x5a, 0xed, 0x94, 0x3d, 0x9d, 0xd9, 0x2c, 0x1a}} , - {{0x35, 0xb0, 0xe6, 0x73, 0x06, 0xb7, 0x37, 0xe0, 0xf8, 0xb0, 0x22, 0xe8, 0xd2, 0xed, 0x0b, 0xef, 0xe6, 0xc6, 0x5a, 0x99, 0x9e, 0x1a, 0x9f, 0x04, 0x97, 0xe4, 0x4d, 0x0b, 0xbe, 0xba, 0x44, 0x40}}}, -{{{0xc1, 0x56, 0x96, 0x91, 0x5f, 0x1f, 0xbb, 0x54, 0x6f, 0x88, 0x89, 0x0a, 0xb2, 0xd6, 0x41, 0x42, 0x6a, 0x82, 0xee, 0x14, 0xaa, 0x76, 0x30, 0x65, 0x0f, 0x67, 0x39, 0xa6, 0x51, 0x7c, 0x49, 0x24}} , - {{0x35, 0xa3, 0x78, 0xd1, 0x11, 0x0f, 0x75, 0xd3, 0x70, 0x46, 0xdb, 0x20, 0x51, 0xcb, 0x92, 0x80, 0x54, 0x10, 0x74, 0x36, 0x86, 0xa9, 0xd7, 0xa3, 0x08, 0x78, 0xf1, 0x01, 0x29, 0xf8, 0x80, 0x3b}}}, -{{{0xdb, 0xa7, 0x9d, 0x9d, 0xbf, 0xa0, 0xcc, 0xed, 0x53, 0xa2, 0xa2, 0x19, 0x39, 0x48, 0x83, 0x19, 0x37, 0x58, 0xd1, 0x04, 0x28, 0x40, 0xf7, 0x8a, 0xc2, 0x08, 0xb7, 0xa5, 0x42, 0xcf, 0x53, 0x4c}} , - {{0xa7, 0xbb, 0xf6, 0x8e, 0xad, 0xdd, 0xf7, 0x90, 0xdd, 0x5f, 0x93, 0x89, 0xae, 0x04, 0x37, 0xe6, 0x9a, 0xb7, 0xe8, 0xc0, 0xdf, 0x16, 0x2a, 0xbf, 0xc4, 0x3a, 0x3c, 0x41, 0xd5, 0x89, 0x72, 0x5a}}}, -{{{0x1f, 0x96, 0xff, 0x34, 0x2c, 0x13, 0x21, 0xcb, 0x0a, 0x89, 0x85, 0xbe, 0xb3, 0x70, 0x9e, 0x1e, 0xde, 0x97, 0xaf, 0x96, 0x30, 0xf7, 0x48, 0x89, 0x40, 0x8d, 0x07, 0xf1, 0x25, 0xf0, 0x30, 0x58}} , - {{0x1e, 0xd4, 0x93, 0x57, 0xe2, 0x17, 0xe7, 0x9d, 0xab, 0x3c, 0x55, 0x03, 0x82, 0x2f, 0x2b, 0xdb, 0x56, 0x1e, 0x30, 0x2e, 0x24, 0x47, 0x6e, 0xe6, 0xff, 0x33, 0x24, 0x2c, 0x75, 0x51, 0xd4, 0x67}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0x2b, 0x06, 0xd9, 0xa1, 0x5d, 0xe1, 0xf4, 0xd1, 0x1e, 0x3c, 0x9a, 0xc6, 0x29, 0x2b, 0x13, 0x13, 0x78, 0xc0, 0xd8, 0x16, 0x17, 0x2d, 0x9e, 0xa9, 0xc9, 0x79, 0x57, 0xab, 0x24, 0x91, 0x92, 0x19}} , - {{0x69, 0xfb, 0xa1, 0x9c, 0xa6, 0x75, 0x49, 0x7d, 0x60, 0x73, 0x40, 0x42, 0xc4, 0x13, 0x0a, 0x95, 0x79, 0x1e, 0x04, 0x83, 0x94, 0x99, 0x9b, 0x1e, 0x0c, 0xe8, 0x1f, 0x54, 0xef, 0xcb, 0xc0, 0x52}}}, -{{{0x14, 0x89, 0x73, 0xa1, 0x37, 0x87, 0x6a, 0x7a, 0xcf, 0x1d, 0xd9, 0x2e, 0x1a, 0x67, 0xed, 0x74, 0xc0, 0xf0, 0x9c, 0x33, 0xdd, 0xdf, 0x08, 0xbf, 0x7b, 0xd1, 0x66, 0xda, 0xe6, 0xc9, 0x49, 0x08}} , - {{0xe9, 0xdd, 0x5e, 0x55, 0xb0, 0x0a, 0xde, 0x21, 0x4c, 0x5a, 0x2e, 0xd4, 0x80, 0x3a, 0x57, 0x92, 0x7a, 0xf1, 0xc4, 0x2c, 0x40, 0xaf, 0x2f, 0xc9, 0x92, 0x03, 0xe5, 0x5a, 0xbc, 0xdc, 0xf4, 0x09}}}, -{{{0xf3, 0xe1, 0x2b, 0x7c, 0x05, 0x86, 0x80, 0x93, 0x4a, 0xad, 0xb4, 0x8f, 0x7e, 0x99, 0x0c, 0xfd, 0xcd, 0xef, 0xd1, 0xff, 0x2c, 0x69, 0x34, 0x13, 0x41, 0x64, 0xcf, 0x3b, 0xd0, 0x90, 0x09, 0x1e}} , - {{0x9d, 0x45, 0xd6, 0x80, 0xe6, 0x45, 0xaa, 0xf4, 0x15, 0xaa, 0x5c, 0x34, 0x87, 0x99, 0xa2, 0x8c, 0x26, 0x84, 0x62, 0x7d, 0xb6, 0x29, 0xc0, 0x52, 0xea, 0xf5, 0x81, 0x18, 0x0f, 0x35, 0xa9, 0x0e}}}, -{{{0xe7, 0x20, 0x72, 0x7c, 0x6d, 0x94, 0x5f, 0x52, 0x44, 0x54, 0xe3, 0xf1, 0xb2, 0xb0, 0x36, 0x46, 0x0f, 0xae, 0x92, 0xe8, 0x70, 0x9d, 0x6e, 0x79, 0xb1, 0xad, 0x37, 0xa9, 0x5f, 0xc0, 0xde, 0x03}} , - {{0x15, 0x55, 0x37, 0xc6, 0x1c, 0x27, 0x1c, 0x6d, 0x14, 0x4f, 0xca, 0xa4, 0xc4, 0x88, 0x25, 0x46, 0x39, 0xfc, 0x5a, 0xe5, 0xfe, 0x29, 0x11, 0x69, 0xf5, 0x72, 0x84, 0x4d, 0x78, 0x9f, 0x94, 0x15}}}, -{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, -{{{0xec, 0xd3, 0xff, 0x57, 0x0b, 0xb0, 0xb2, 0xdc, 0xf8, 0x4f, 0xe2, 0x12, 0xd5, 0x36, 0xbe, 0x6b, 0x09, 0x43, 0x6d, 0xa3, 0x4d, 0x90, 0x2d, 0xb8, 0x74, 0xe8, 0x71, 0x45, 0x19, 0x8b, 0x0c, 0x6a}} , - {{0xb8, 0x42, 0x1c, 0x03, 0xad, 0x2c, 0x03, 0x8e, 0xac, 0xd7, 0x98, 0x29, 0x13, 0xc6, 0x02, 0x29, 0xb5, 0xd4, 0xe7, 0xcf, 0xcc, 0x8b, 0x83, 0xec, 0x35, 0xc7, 0x9c, 0x74, 0xb7, 0xad, 0x85, 0x5f}}}, -{{{0x78, 0x84, 0xe1, 0x56, 0x45, 0x69, 0x68, 0x5a, 0x4f, 0xb8, 0xb1, 0x29, 0xff, 0x33, 0x03, 0x31, 0xb7, 0xcb, 0x96, 0x25, 0xe6, 0xe6, 0x41, 0x98, 0x1a, 0xbb, 0x03, 0x56, 0xf2, 0xb2, 0x91, 0x34}} , - {{0x2c, 0x6c, 0xf7, 0x66, 0xa4, 0x62, 0x6b, 0x39, 0xb3, 0xba, 0x65, 0xd3, 0x1c, 0xf8, 0x11, 0xaa, 0xbe, 0xdc, 0x80, 0x59, 0x87, 0xf5, 0x7b, 0xe5, 0xe3, 0xb3, 0x3e, 0x39, 0xda, 0xbe, 0x88, 0x09}}}, -{{{0x8b, 0xf1, 0xa0, 0xf5, 0xdc, 0x29, 0xb4, 0xe2, 0x07, 0xc6, 0x7a, 0x00, 0xd0, 0x89, 0x17, 0x51, 0xd4, 0xbb, 0xd4, 0x22, 0xea, 0x7e, 0x7d, 0x7c, 0x24, 0xea, 0xf2, 0xe8, 0x22, 0x12, 0x95, 0x06}} , - {{0xda, 0x7c, 0xa4, 0x0c, 0xf4, 0xba, 0x6e, 0xe1, 0x89, 0xb5, 0x59, 0xca, 0xf1, 0xc0, 0x29, 0x36, 0x09, 0x44, 0xe2, 0x7f, 0xd1, 0x63, 0x15, 0x99, 0xea, 0x25, 0xcf, 0x0c, 0x9d, 0xc0, 0x44, 0x6f}}}, -{{{0x1d, 0x86, 0x4e, 0xcf, 0xf7, 0x37, 0x10, 0x25, 0x8f, 0x12, 0xfb, 0x19, 0xfb, 0xe0, 0xed, 0x10, 0xc8, 0xe2, 0xf5, 0x75, 0xb1, 0x33, 0xc0, 0x96, 0x0d, 0xfb, 0x15, 0x6c, 0x0d, 0x07, 0x5f, 0x05}} , - {{0x69, 0x3e, 0x47, 0x97, 0x2c, 0xaf, 0x52, 0x7c, 0x78, 0x83, 0xad, 0x1b, 0x39, 0x82, 0x2f, 0x02, 0x6f, 0x47, 0xdb, 0x2a, 0xb0, 0xe1, 0x91, 0x99, 0x55, 0xb8, 0x99, 0x3a, 0xa0, 0x44, 0x11, 0x51}}} diff --git a/libssh/src/getpass.c b/libssh/src/getpass.c deleted file mode 100644 index c36f263b..00000000 --- a/libssh/src/getpass.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * getpass.c - platform independent getpass function. - * - * This file is part of the SSH Library - * - * Copyright (c) 2011-2013 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include - -#include - -/** - * @internal - * - * @brief Get the password from the console. - * - * @param[in] prompt The prompt to display. - * - * @param[in] buf The buffer to fill. - * - * @param[in] len The length of the buffer. - * - * @param[in] verify Should the password be verified? - * - * @return 1 on success, 0 on error. - */ -static int ssh_gets(const char *prompt, char *buf, size_t len, int verify) { - char *tmp; - char *ptr = NULL; - int ok = 0; - - tmp = malloc(len); - if (tmp == NULL) { - return 0; - } - memset(tmp,'\0',len); - - /* read the password */ - while (!ok) { - if (buf[0] != '\0') { - fprintf(stdout, "%s[%s] ", prompt, buf); - } else { - fprintf(stdout, "%s", prompt); - } - fflush(stdout); - if (fgets(tmp, len, stdin) == NULL) { - free(tmp); - return 0; - } - - if ((ptr = strchr(tmp, '\n'))) { - *ptr = '\0'; - } - fprintf(stdout, "\n"); - - if (*tmp) { - strncpy(buf, tmp, len); - } - - if (verify) { - char *key_string; - - key_string = malloc(len); - if (key_string == NULL) { - break; - } - memset(key_string, '\0', len); - - fprintf(stdout, "\nVerifying, please re-enter. %s", prompt); - fflush(stdout); - if (! fgets(key_string, len, stdin)) { - memset(key_string, '\0', len); - SAFE_FREE(key_string); - clearerr(stdin); - continue; - } - if ((ptr = strchr(key_string, '\n'))) { - *ptr = '\0'; - } - fprintf(stdout, "\n"); - if (strcmp(buf, key_string)) { - printf("\n\07\07Mismatch - try again\n"); - memset(key_string, '\0', len); - SAFE_FREE(key_string); - fflush(stdout); - continue; - } - memset(key_string, '\0', len); - SAFE_FREE(key_string); - } - ok = 1; - } - memset(tmp, '\0', len); - free(tmp); - - return ok; -} - -#ifdef _WIN32 -#include - -int ssh_getpass(const char *prompt, - char *buf, - size_t len, - int echo, - int verify) { - HANDLE h; - DWORD mode = 0; - int ok; - - /* fgets needs at least len - 1 */ - if (prompt == NULL || buf == NULL || len < 2) { - return -1; - } - - /* get stdin and mode */ - h = GetStdHandle(STD_INPUT_HANDLE); - if (!GetConsoleMode(h, &mode)) { - return -1; - } - - /* disable echo */ - if (!echo) { - if (!SetConsoleMode(h, mode & ~ENABLE_ECHO_INPUT)) { - return -1; - } - } - - ok = ssh_gets(prompt, buf, len, verify); - - /* reset echo */ - SetConsoleMode(h, mode); - - if (!ok) { - memset (buf, '\0', len); - return -1; - } - - /* force termination */ - buf[len - 1] = '\0'; - - return 0; -} - -#else - -#include -#ifdef HAVE_TERMIOS_H -#include -#endif -#ifdef HAVE_UNISTD_H -#include -#endif - -/** - * @ingroup libssh_misc - * - * @brief Get a password from the console. - * - * You should make sure that the buffer is an empty string! - * - * You can also use this function to ask for a username. Then you can fill the - * buffer with the username and it is shows to the users. If the users just - * presses enter the buffer will be untouched. - * - * @code - * char username[128]; - * - * snprintf(username, sizeof(username), "john"); - * - * ssh_getpass("Username:", username, sizeof(username), 1, 0); - * @endcode - * - * The prompt will look like this: - * - * Username: [john] - * - * If you press enter then john is used as the username, or you can type it in - * to change it. - * - * @param[in] prompt The prompt to show to ask for the password. - * - * @param[out] buf The buffer the password should be stored. It NEEDS to be - * empty or filled out. - * - * @param[in] len The length of the buffer. - * - * @param[in] echo Should we echo what you type. - * - * @param[in] verify Should we ask for the password twice. - * - * @return 0 on success, -1 on error. - */ -int ssh_getpass(const char *prompt, - char *buf, - size_t len, - int echo, - int verify) { - struct termios attr; - struct termios old_attr; - int ok = 0; - int fd = -1; - - /* fgets needs at least len - 1 */ - if (prompt == NULL || buf == NULL || len < 2) { - return -1; - } - - if (isatty(STDIN_FILENO)) { - ZERO_STRUCT(attr); - ZERO_STRUCT(old_attr); - - /* get local terminal attributes */ - if (tcgetattr(STDIN_FILENO, &attr) < 0) { - perror("tcgetattr"); - return -1; - } - - /* save terminal attributes */ - memcpy(&old_attr, &attr, sizeof(attr)); - if((fd = fcntl(0, F_GETFL, 0)) < 0) { - perror("fcntl"); - return -1; - } - - /* disable echo */ - if (!echo) { - attr.c_lflag &= ~(ECHO); - } - - /* write attributes to terminal */ - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) < 0) { - perror("tcsetattr"); - return -1; - } - } - - /* disable nonblocking I/O */ - if (fd & O_NDELAY) { - fcntl(0, F_SETFL, fd & ~O_NDELAY); - } - - ok = ssh_gets(prompt, buf, len, verify); - - if (isatty(STDIN_FILENO)) { - /* reset terminal */ - tcsetattr(STDIN_FILENO, TCSANOW, &old_attr); - } - - /* close fd */ - if (fd & O_NDELAY) { - fcntl(0, F_SETFL, fd); - } - - if (!ok) { - memset (buf, '\0', len); - return -1; - } - - /* force termination */ - buf[len - 1] = '\0'; - - return 0; -} - -#endif - -/* vim: set ts=4 sw=4 et cindent syntax=c.doxygen: */ diff --git a/libssh/src/gssapi.c b/libssh/src/gssapi.c deleted file mode 100644 index 099294ee..00000000 --- a/libssh/src/gssapi.c +++ /dev/null @@ -1,876 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/** current state of an GSSAPI authentication */ -enum ssh_gssapi_state_e { - SSH_GSSAPI_STATE_NONE, /* no status */ - SSH_GSSAPI_STATE_RCV_TOKEN, /* Expecting a token */ - SSH_GSSAPI_STATE_RCV_MIC, /* Expecting a MIC */ -}; - -struct ssh_gssapi_struct{ - enum ssh_gssapi_state_e state; /* current state */ - struct gss_OID_desc_struct mech; /* mechanism being elected for auth */ - gss_cred_id_t server_creds; /* credentials of server */ - gss_cred_id_t client_creds; /* creds delegated by the client */ - gss_ctx_id_t ctx; /* the authentication context */ - gss_name_t client_name; /* Identity of the client */ - char *user; /* username of client */ - char *canonic_user; /* canonic form of the client's username */ - char *service; /* name of the service */ - struct { - gss_name_t server_name; /* identity of server */ - OM_uint32 flags; /* flags used for init context */ - gss_OID oid; /* mech being used for authentication */ - gss_cred_id_t creds; /* creds used to initialize context */ - gss_cred_id_t client_deleg_creds; /* delegated creds (const, not freeable) */ - } client; -}; - - -/** @internal - * @initializes a gssapi context for authentication - */ -static int ssh_gssapi_init(ssh_session session){ - if (session->gssapi != NULL) - return SSH_OK; - session->gssapi = malloc(sizeof(struct ssh_gssapi_struct)); - if(!session->gssapi){ - ssh_set_error_oom(session); - return SSH_ERROR; - } - ZERO_STRUCTP(session->gssapi); - session->gssapi->server_creds = GSS_C_NO_CREDENTIAL; - session->gssapi->client_creds = GSS_C_NO_CREDENTIAL; - session->gssapi->ctx = GSS_C_NO_CONTEXT; - session->gssapi->state = SSH_GSSAPI_STATE_NONE; - return SSH_OK; -} - -/** @internal - * @frees a gssapi context - */ -static void ssh_gssapi_free(ssh_session session){ - OM_uint32 min; - if (session->gssapi == NULL) - return; - SAFE_FREE(session->gssapi->user); - SAFE_FREE(session->gssapi->mech.elements); - gss_release_cred(&min,&session->gssapi->server_creds); - if (session->gssapi->client.creds != - session->gssapi->client.client_deleg_creds) { - gss_release_cred(&min, &session->gssapi->client.creds); - } - SAFE_FREE(session->gssapi); -} - -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token){ -#ifdef WITH_SERVER - if(session->server) - return ssh_packet_userauth_gssapi_token_server(session, type, packet, user); -#endif - return ssh_packet_userauth_gssapi_token_client(session, type, packet, user); -} -#ifdef WITH_SERVER - -/** @internal - * @brief sends a SSH_MSG_USERAUTH_GSSAPI_RESPONSE packet - * @param[in] oid the OID that was selected for authentication - */ -static int ssh_gssapi_send_response(ssh_session session, ssh_string oid){ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) < 0 || - buffer_add_ssh_string(session->out_buffer,oid) < 0) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - - packet_send(session); - SSH_LOG(SSH_LOG_PACKET, - "Sent SSH_MSG_USERAUTH_GSSAPI_RESPONSE"); - return SSH_OK; -} - -#endif /* WITH_SERVER */ - -static void ssh_gssapi_log_error(int verb, const char *msg, int maj_stat){ - gss_buffer_desc buffer; - OM_uint32 dummy, message_context; - gss_display_status(&dummy,maj_stat,GSS_C_GSS_CODE, GSS_C_NO_OID, &message_context, &buffer); - SSH_LOG(verb, "GSSAPI(%s): %s", msg, (const char *)buffer.value); -} - -#ifdef WITH_SERVER - -/** @internal - * @brief handles an user authentication using GSSAPI - */ -int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids){ - char service_name[]="host"; - gss_buffer_desc name_buf; - gss_name_t server_name; /* local server fqdn */ - OM_uint32 maj_stat, min_stat; - unsigned int i; - char *ptr; - gss_OID_set supported; /* oids supported by server */ - gss_OID_set both_supported; /* oids supported by both client and server */ - gss_OID_set selected; /* oid selected for authentication */ - int present=0; - int oid_count=0; - struct gss_OID_desc_struct oid; - int rc; - - if (ssh_callbacks_exists(session->server_callbacks, gssapi_select_oid_function)){ - ssh_string oid_s = session->server_callbacks->gssapi_select_oid_function(session, - user, n_oid, oids, - session->server_callbacks->userdata); - if (oid_s != NULL){ - if (ssh_gssapi_init(session) == SSH_ERROR) - return SSH_ERROR; - session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN; - rc = ssh_gssapi_send_response(session, oid_s); - ssh_string_free(oid_s); - return rc; - } else { - return ssh_auth_reply_default(session,0); - } - } - gss_create_empty_oid_set(&min_stat, &both_supported); - - maj_stat = gss_indicate_mechs(&min_stat, &supported); - for (i=0; i < supported->count; ++i){ - ptr = ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length); - SSH_LOG(SSH_LOG_DEBUG, "Supported mech %d: %s\n", i, ptr); - free(ptr); - } - - for (i=0 ; i< n_oid ; ++i){ - unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]); - size_t len = ssh_string_len(oids[i]); - if(len < 2 || oid_s[0] != SSH_OID_TAG || ((size_t)oid_s[1]) != len - 2){ - SSH_LOG(SSH_LOG_WARNING,"GSSAPI: received invalid OID"); - continue; - } - oid.elements = &oid_s[2]; - oid.length = len - 2; - gss_test_oid_set_member(&min_stat,&oid,supported,&present); - if(present){ - gss_add_oid_set_member(&min_stat,&oid,&both_supported); - oid_count++; - } - } - gss_release_oid_set(&min_stat, &supported); - if (oid_count == 0){ - SSH_LOG(SSH_LOG_PROTOCOL,"GSSAPI: no OID match"); - ssh_auth_reply_default(session, 0); - gss_release_oid_set(&min_stat, &both_supported); - return SSH_OK; - } - /* from now we have room for context */ - if (ssh_gssapi_init(session) == SSH_ERROR) - return SSH_ERROR; - - name_buf.value = service_name; - name_buf.length = strlen(name_buf.value) + 1; - maj_stat = gss_import_name(&min_stat, &name_buf, - (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name); - if (maj_stat != GSS_S_COMPLETE) { - SSH_LOG(SSH_LOG_WARNING, "importing name %d, %d", maj_stat, min_stat); - ssh_gssapi_log_error(SSH_LOG_WARNING, "importing name", maj_stat); - return -1; - } - - maj_stat = gss_acquire_cred(&min_stat, server_name, 0, - both_supported, GSS_C_ACCEPT, - &session->gssapi->server_creds, &selected, NULL); - gss_release_name(&min_stat, &server_name); - gss_release_oid_set(&min_stat, &both_supported); - - if (maj_stat != GSS_S_COMPLETE) { - SSH_LOG(SSH_LOG_WARNING, "error acquiring credentials %d, %d", maj_stat, min_stat); - ssh_gssapi_log_error(SSH_LOG_WARNING, "acquiring creds", maj_stat); - ssh_auth_reply_default(session,0); - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_PROTOCOL, "acquiring credentials %d, %d", maj_stat, min_stat); - - /* finding which OID from client we selected */ - for (i=0 ; i< n_oid ; ++i){ - unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]); - size_t len = ssh_string_len(oids[i]); - if(len < 2 || oid_s[0] != SSH_OID_TAG || ((size_t)oid_s[1]) != len - 2){ - SSH_LOG(SSH_LOG_WARNING,"GSSAPI: received invalid OID"); - continue; - } - oid.elements = &oid_s[2]; - oid.length = len - 2; - gss_test_oid_set_member(&min_stat,&oid,selected,&present); - if(present){ - SSH_LOG(SSH_LOG_PACKET, "Selected oid %d", i); - break; - } - } - session->gssapi->mech.length = oid.length; - session->gssapi->mech.elements = malloc(oid.length); - if (session->gssapi->mech.elements == NULL){ - ssh_set_error_oom(session); - return SSH_ERROR; - } - memcpy(session->gssapi->mech.elements, oid.elements, oid.length); - gss_release_oid_set(&min_stat, &selected); - session->gssapi->user = strdup(user); - session->gssapi->service = service_name; - session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN; - return ssh_gssapi_send_response(session, oids[i]); -} - -static char *ssh_gssapi_name_to_char(gss_name_t name){ - gss_buffer_desc buffer; - OM_uint32 maj_stat, min_stat; - char *ptr; - maj_stat = gss_display_name(&min_stat, name, &buffer, NULL); - ssh_gssapi_log_error(SSH_LOG_WARNING, "converting name", maj_stat); - ptr=malloc(buffer.length + 1); - memcpy(ptr, buffer.value, buffer.length); - ptr[buffer.length] = '\0'; - gss_release_buffer(&min_stat, &buffer); - return ptr; - -} - -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ - ssh_string token; - char *hexa; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER; - gss_name_t client_name = GSS_C_NO_NAME; - OM_uint32 ret_flags=0; - gss_channel_bindings_t input_bindings=GSS_C_NO_CHANNEL_BINDINGS; - int rc; - - (void)user; - (void)type; - - SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_USERAUTH_GSSAPI_TOKEN"); - if (!session->gssapi || session->gssapi->state != SSH_GSSAPI_STATE_RCV_TOKEN){ - ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_USERAUTH_GSSAPI_TOKEN in invalid state"); - return SSH_PACKET_USED; - } - token = buffer_get_ssh_string(packet); - - if (token == NULL){ - ssh_set_error(session, SSH_REQUEST_DENIED, "ssh_packet_userauth_gssapi_token: invalid packet"); - return SSH_PACKET_USED; - } - - if (ssh_callbacks_exists(session->server_callbacks, gssapi_accept_sec_ctx_function)){ - ssh_string out_token=NULL; - rc = session->server_callbacks->gssapi_accept_sec_ctx_function(session, - token, &out_token, session->server_callbacks->userdata); - if (rc == SSH_ERROR){ - ssh_auth_reply_default(session, 0); - ssh_gssapi_free(session); - session->gssapi=NULL; - return SSH_PACKET_USED; - } - if (ssh_string_len(out_token) != 0){ - rc = ssh_buffer_pack(session->out_buffer, - "bS", - SSH2_MSG_USERAUTH_GSSAPI_TOKEN, - out_token); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - return SSH_PACKET_USED; - } - packet_send(session); - ssh_string_free(out_token); - } else { - session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC; - } - return SSH_PACKET_USED; - } - hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token)); - SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa); - SAFE_FREE(hexa); - input_token.length = ssh_string_len(token); - input_token.value = ssh_string_data(token); - - maj_stat = gss_accept_sec_context(&min_stat, &session->gssapi->ctx, session->gssapi->server_creds, - &input_token, input_bindings, &client_name, NULL /*mech_oid*/, &output_token, &ret_flags, - NULL /*time*/, &session->gssapi->client_creds); - ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "accepting token", maj_stat); - ssh_string_free(token); - if (client_name != GSS_C_NO_NAME){ - session->gssapi->client_name = client_name; - session->gssapi->canonic_user = ssh_gssapi_name_to_char(client_name); - } - if (GSS_ERROR(maj_stat)){ - ssh_gssapi_log_error(SSH_LOG_WARNING, "Gssapi error", maj_stat); - ssh_auth_reply_default(session,0); - ssh_gssapi_free(session); - session->gssapi=NULL; - return SSH_PACKET_USED; - } - - if (output_token.length != 0){ - hexa = ssh_get_hexa(output_token.value, output_token.length); - SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); - SAFE_FREE(hexa); - ssh_buffer_pack(session->out_buffer, - "bdP", - SSH2_MSG_USERAUTH_GSSAPI_TOKEN, - output_token.length, - (size_t)output_token.length, output_token.value); - packet_send(session); - } - if(maj_stat == GSS_S_COMPLETE){ - session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC; - } - return SSH_PACKET_USED; -} - -#endif /* WITH_SERVER */ - -static ssh_buffer ssh_gssapi_build_mic(ssh_session session){ - ssh_buffer mic_buffer; - int rc; - - mic_buffer = ssh_buffer_new(); - if (mic_buffer == NULL) { - ssh_set_error_oom(session); - return NULL; - } - - rc = ssh_buffer_pack(mic_buffer, - "dPbsss", - session->current_crypto->digest_len, - (size_t)session->current_crypto->digest_len, session->current_crypto->session_id, - SSH2_MSG_USERAUTH_REQUEST, - session->gssapi->user, - "ssh-connection", - "gssapi-with-mic"); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - ssh_buffer_free(mic_buffer); - return NULL; - } - - return mic_buffer; -} - -#ifdef WITH_SERVER - -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic) -{ - ssh_string mic_token; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER; - gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER; - ssh_buffer mic_buffer = NULL; - - (void)user; - (void)type; - - SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_USERAUTH_GSSAPI_MIC"); - mic_token = buffer_get_ssh_string(packet); - if (mic_token == NULL) { - ssh_set_error(session, SSH_FATAL, "Missing MIC in packet"); - goto error; - } - if (session->gssapi == NULL - || session->gssapi->state != SSH_GSSAPI_STATE_RCV_MIC) { - ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_USERAUTH_GSSAPI_MIC in invalid state"); - goto error; - } - - mic_buffer = ssh_gssapi_build_mic(session); - if (mic_buffer == NULL) { - ssh_set_error_oom(session); - goto error; - } - if (ssh_callbacks_exists(session->server_callbacks, gssapi_verify_mic_function)){ - int rc = session->server_callbacks->gssapi_verify_mic_function(session, mic_token, - ssh_buffer_get_begin(mic_buffer), ssh_buffer_get_len(mic_buffer), - session->server_callbacks->userdata); - if (rc != SSH_OK) { - goto error; - } - } else { - mic_buf.length = ssh_buffer_get_len(mic_buffer); - mic_buf.value = ssh_buffer_get_begin(mic_buffer); - mic_token_buf.length = ssh_string_len(mic_token); - mic_token_buf.value = ssh_string_data(mic_token); - - maj_stat = gss_verify_mic(&min_stat, session->gssapi->ctx, &mic_buf, &mic_token_buf, NULL); - ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "verifying MIC", maj_stat); - ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "verifying MIC (min stat)", min_stat); - if (maj_stat == GSS_S_DEFECTIVE_TOKEN || GSS_ERROR(maj_stat)) { - goto error; - } - } - - if (ssh_callbacks_exists(session->server_callbacks, auth_gssapi_mic_function)){ - switch(session->server_callbacks->auth_gssapi_mic_function(session, - session->gssapi->user, session->gssapi->canonic_user, - session->server_callbacks->userdata)){ - case SSH_AUTH_SUCCESS: - ssh_auth_reply_success(session, 0); - break; - case SSH_AUTH_PARTIAL: - ssh_auth_reply_success(session, 1); - break; - default: - ssh_auth_reply_default(session, 0); - break; - } - } - - goto end; - -error: - ssh_auth_reply_default(session,0); - -end: - ssh_gssapi_free(session); - if (mic_buffer != NULL) { - ssh_buffer_free(mic_buffer); - } - if (mic_token != NULL) { - ssh_string_free(mic_token); - } - - return SSH_PACKET_USED; -} - -/** @brief returns the client credentials of the connected client. - * If the client has given a forwardable token, the SSH server will - * retrieve it. - * @returns gssapi credentials handle. - * @returns NULL if no forwardable token is available. - */ -ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session){ - if (!session || !session->gssapi || session->gssapi->client_creds == GSS_C_NO_CREDENTIAL) - return NULL; - return (ssh_gssapi_creds)session->gssapi->client_creds; -} - -#endif /* SERVER */ - -/** - * @brief Set the forwadable ticket to be given to the server for authentication. - * Unlike ssh_gssapi_get_creds() this is called on the client side of an ssh - * connection. - * - * @param[in] creds gssapi credentials handle. - */ -void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds) -{ - if (session == NULL) { - return; - } - if (session->gssapi == NULL) { - ssh_gssapi_init(session); - if (session->gssapi == NULL) { - return; - } - } - - session->gssapi->client.client_deleg_creds = (gss_cred_id_t)creds; -} - -static int ssh_gssapi_send_auth_mic(ssh_session session, ssh_string *oid_set, int n_oid){ - int rc; - int i; - - rc = ssh_buffer_pack(session->out_buffer, - "bsssd", - SSH2_MSG_USERAUTH_REQUEST, - session->opts.username, - "ssh-connection", - "gssapi-with-mic", - n_oid); - - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto fail; - } - - for (i=0; iout_buffer, oid_set[i]); - if (rc < 0) { - goto fail; - } - } - - session->auth_state = SSH_AUTH_STATE_GSSAPI_REQUEST_SENT; - return packet_send(session); -fail: - ssh_buffer_reinit(session->out_buffer); - return SSH_ERROR; -} - -/** @brief returns the OIDs of the mechs that have usable credentials - */ -static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids) -{ - OM_uint32 maj_stat, min_stat, lifetime; - gss_OID_set actual_mechs; - gss_buffer_desc namebuf; - gss_name_t client_id = GSS_C_NO_NAME; - gss_OID oid; - unsigned int i; - char *ptr; - int ret; - - if (session->gssapi->client.client_deleg_creds == NULL) { - if (session->opts.gss_client_identity != NULL) { - namebuf.value = (void *)session->opts.gss_client_identity; - namebuf.length = strlen(session->opts.gss_client_identity); - - maj_stat = gss_import_name(&min_stat, &namebuf, - GSS_C_NT_USER_NAME, &client_id); - if (GSS_ERROR(maj_stat)) { - ret = SSH_ERROR; - goto end; - } - } - - maj_stat = gss_acquire_cred(&min_stat, client_id, GSS_C_INDEFINITE, - GSS_C_NO_OID_SET, GSS_C_INITIATE, - &session->gssapi->client.creds, - &actual_mechs, NULL); - if (GSS_ERROR(maj_stat)) { - ret = SSH_ERROR; - goto end; - } - } else { - session->gssapi->client.creds = - session->gssapi->client.client_deleg_creds; - - maj_stat = gss_inquire_cred(&min_stat, session->gssapi->client.creds, - &client_id, NULL, NULL, &actual_mechs); - if (GSS_ERROR(maj_stat)) { - ret = SSH_ERROR; - goto end; - } - } - - gss_create_empty_oid_set(&min_stat, valid_oids); - - /* double check each single cred */ - for (i = 0; i < actual_mechs->count; i++) { - /* check lifetime is not 0 or skip */ - lifetime = 0; - oid = &actual_mechs->elements[i]; - maj_stat = gss_inquire_cred_by_mech(&min_stat, - session->gssapi->client.creds, - oid, NULL, &lifetime, NULL, NULL); - if (maj_stat == GSS_S_COMPLETE && lifetime > 0) { - gss_add_oid_set_member(&min_stat, oid, valid_oids); - ptr = ssh_get_hexa(oid->elements, oid->length); - SSH_LOG(SSH_LOG_DEBUG, "GSSAPI valid oid %d : %s\n", i, ptr); - SAFE_FREE(ptr); - } - } - - ret = SSH_OK; - -end: - gss_release_name(&min_stat, &client_id); - return ret; -} - -/** - * @brief launches a gssapi-with-mic auth request - * @returns SSH_AUTH_ERROR: A serious error happened\n - * SSH_AUTH_DENIED: Authentication failed : use another method\n - * SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again - * later. - */ -int ssh_gssapi_auth_mic(ssh_session session){ - int i; - gss_OID_set selected; /* oid selected for authentication */ - ssh_string *oids; - int rc; - int n_oids = 0; - OM_uint32 maj_stat, min_stat; - char name_buf[256]; - gss_buffer_desc hostname; - const char *gss_host = session->opts.host; - - rc = ssh_gssapi_init(session); - if (rc == SSH_ERROR) { - return SSH_AUTH_ERROR; - } - - if (session->opts.gss_server_identity != NULL) { - gss_host = session->opts.gss_server_identity; - } - /* import target host name */ - snprintf(name_buf, sizeof(name_buf), "host@%s", gss_host); - - hostname.value = name_buf; - hostname.length = strlen(name_buf) + 1; - maj_stat = gss_import_name(&min_stat, &hostname, - (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, - &session->gssapi->client.server_name); - if (maj_stat != GSS_S_COMPLETE) { - SSH_LOG(SSH_LOG_WARNING, "importing name %d, %d", maj_stat, min_stat); - ssh_gssapi_log_error(SSH_LOG_WARNING, "importing name", maj_stat); - return SSH_PACKET_USED; - } - - /* copy username */ - session->gssapi->user = strdup(session->opts.username); - if (session->gssapi->user == NULL) { - ssh_set_error_oom(session); - return SSH_AUTH_ERROR; - } - - SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi to host %s with user %s", - session->opts.host, session->gssapi->user); - rc = ssh_gssapi_match(session, &selected); - if (rc == SSH_ERROR) { - return SSH_AUTH_DENIED; - } - - n_oids = selected->count; - SSH_LOG(SSH_LOG_PROTOCOL, "Sending %d oids", n_oids); - - oids = calloc(n_oids, sizeof(ssh_string)); - if (oids == NULL) { - ssh_set_error_oom(session); - return SSH_AUTH_ERROR; - } - - for (i=0; ielements[i].length + 2); - ((unsigned char *)oids[i]->data)[0] = SSH_OID_TAG; - ((unsigned char *)oids[i]->data)[1] = selected->elements[i].length; - memcpy((unsigned char *)oids[i]->data + 2, selected->elements[i].elements, - selected->elements[i].length); - } - - rc = ssh_gssapi_send_auth_mic(session, oids, n_oids); - for (i = 0; i < n_oids; i++) { - ssh_string_free(oids[i]); - } - free(oids); - if (rc != SSH_ERROR) { - return SSH_AUTH_AGAIN; - } - - return SSH_AUTH_ERROR; -} - -static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s){ - gss_OID ret = malloc(sizeof (gss_OID_desc)); - unsigned char *data = ssh_string_data(oid_s); - size_t len = ssh_string_len(oid_s); - if(len > 256 || len <= 2){ - SAFE_FREE(ret); - return NULL; - } - if(data[0] != SSH_OID_TAG || data[1] != len - 2){ - SAFE_FREE(ret); - return NULL; - } - ret->elements = malloc(len - 2); - memcpy(ret->elements, &data[2], len-2); - ret->length = len-2; - return ret; -} - -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){ - ssh_string oid_s; - gss_uint32 maj_stat, min_stat; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - char *hexa; - (void)type; - (void)user; - - SSH_LOG(SSH_LOG_PACKET, "Received SSH_USERAUTH_GSSAPI_RESPONSE"); - if (session->auth_state != SSH_AUTH_STATE_GSSAPI_REQUEST_SENT){ - ssh_set_error(session, SSH_FATAL, "Invalid state in ssh_packet_userauth_gssapi_response"); - return SSH_PACKET_USED; - } - oid_s = buffer_get_ssh_string(packet); - if (!oid_s){ - ssh_set_error(session, SSH_FATAL, "Missing OID"); - return SSH_PACKET_USED; - } - session->gssapi->client.oid = ssh_gssapi_oid_from_string(oid_s); - ssh_string_free(oid_s); - if (!session->gssapi->client.oid) { - ssh_set_error(session, SSH_FATAL, "Invalid OID"); - return SSH_PACKET_USED; - } - - session->gssapi->client.flags = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; - if (session->opts.gss_delegate_creds) { - session->gssapi->client.flags |= GSS_C_DELEG_FLAG; - } - - /* prepare the first TOKEN response */ - maj_stat = gss_init_sec_context(&min_stat, - session->gssapi->client.creds, - &session->gssapi->ctx, - session->gssapi->client.server_name, - session->gssapi->client.oid, - session->gssapi->client.flags, - 0, NULL, &input_token, NULL, - &output_token, NULL, NULL); - if(GSS_ERROR(maj_stat)){ - ssh_gssapi_log_error(SSH_LOG_WARNING, "Initializing gssapi context", maj_stat); - return SSH_PACKET_USED; - } - if (output_token.length != 0){ - hexa = ssh_get_hexa(output_token.value, output_token.length); - SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); - SAFE_FREE(hexa); - ssh_buffer_pack(session->out_buffer, - "bdP", - SSH2_MSG_USERAUTH_GSSAPI_TOKEN, - output_token.length, - (size_t)output_token.length, output_token.value); - packet_send(session); - session->auth_state = SSH_AUTH_STATE_GSSAPI_TOKEN; - } - return SSH_PACKET_USED; -} - -static int ssh_gssapi_send_mic(ssh_session session){ - OM_uint32 maj_stat, min_stat; - gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER; - gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER; - ssh_buffer mic_buffer; - int rc; - - SSH_LOG(SSH_LOG_PACKET,"Sending SSH_MSG_USERAUTH_GSSAPI_MIC"); - - mic_buffer = ssh_gssapi_build_mic(session); - if (mic_buffer == NULL) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - mic_buf.length = ssh_buffer_get_len(mic_buffer); - mic_buf.value = ssh_buffer_get_begin(mic_buffer); - - maj_stat = gss_get_mic(&min_stat,session->gssapi->ctx, GSS_C_QOP_DEFAULT, &mic_buf, &mic_token_buf); - if (GSS_ERROR(maj_stat)){ - ssh_buffer_free(mic_buffer); - ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "generating MIC", maj_stat); - return SSH_ERROR; - } - - rc = ssh_buffer_pack(session->out_buffer, - "bdP", - SSH2_MSG_USERAUTH_GSSAPI_MIC, - mic_token_buf.length, - (size_t)mic_token_buf.length, mic_token_buf.value); - if (rc != SSH_OK) { - ssh_buffer_free(mic_buffer); - ssh_set_error_oom(session); - return SSH_ERROR; - } - - return packet_send(session); -} - -SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){ - ssh_string token; - char *hexa; - OM_uint32 maj_stat, min_stat; - gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER; - (void)user; - (void)type; - - SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_USERAUTH_GSSAPI_TOKEN"); - if (!session->gssapi || session->auth_state != SSH_AUTH_STATE_GSSAPI_TOKEN){ - ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_USERAUTH_GSSAPI_TOKEN in invalid state"); - return SSH_PACKET_USED; - } - token = buffer_get_ssh_string(packet); - - if (token == NULL){ - ssh_set_error(session, SSH_REQUEST_DENIED, "ssh_packet_userauth_gssapi_token: invalid packet"); - return SSH_PACKET_USED; - } - hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token)); - SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa); - SAFE_FREE(hexa); - input_token.length = ssh_string_len(token); - input_token.value = ssh_string_data(token); - maj_stat = gss_init_sec_context(&min_stat, - session->gssapi->client.creds, - &session->gssapi->ctx, - session->gssapi->client.server_name, - session->gssapi->client.oid, - session->gssapi->client.flags, - 0, NULL, &input_token, NULL, - &output_token, NULL, NULL); - - ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "accepting token", maj_stat); - ssh_string_free(token); - if (GSS_ERROR(maj_stat)){ - ssh_gssapi_log_error(SSH_LOG_PROTOCOL, "Gssapi error", maj_stat); - ssh_gssapi_free(session); - session->gssapi=NULL; - return SSH_PACKET_USED; - } - - if (output_token.length != 0){ - hexa = ssh_get_hexa(output_token.value, output_token.length); - SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa); - SAFE_FREE(hexa); - ssh_buffer_pack(session->out_buffer, - "bdP", - SSH2_MSG_USERAUTH_GSSAPI_TOKEN, - output_token.length, - (size_t)output_token.length, output_token.value); - packet_send(session); - } - if(maj_stat == GSS_S_COMPLETE){ - session->auth_state = SSH_AUTH_STATE_NONE; - ssh_gssapi_send_mic(session); - } - return SSH_PACKET_USED; -} diff --git a/libssh/src/gzip.c b/libssh/src/gzip.c deleted file mode 100644 index ca190bc2..00000000 --- a/libssh/src/gzip.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * gzip.c - hooks for compression of packets - * - * This file is part of the SSH Library - * - * Copyright (c) 2003 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/buffer.h" -#include "libssh/crypto.h" -#include "libssh/session.h" - -#define BLOCKSIZE 4092 - -static z_stream *initcompress(ssh_session session, int level) { - z_stream *stream = NULL; - int status; - - stream = malloc(sizeof(z_stream)); - if (stream == NULL) { - return NULL; - } - memset(stream, 0, sizeof(z_stream)); - - status = deflateInit(stream, level); - if (status != Z_OK) { - SAFE_FREE(stream); - ssh_set_error(session, SSH_FATAL, - "status %d inititalising zlib deflate", status); - return NULL; - } - - return stream; -} - -static ssh_buffer gzip_compress(ssh_session session,ssh_buffer source,int level){ - z_stream *zout = session->current_crypto->compress_out_ctx; - void *in_ptr = buffer_get_rest(source); - unsigned long in_size = buffer_get_rest_len(source); - ssh_buffer dest = NULL; - unsigned char out_buf[BLOCKSIZE] = {0}; - unsigned long len; - int status; - - if(zout == NULL) { - zout = session->current_crypto->compress_out_ctx = initcompress(session, level); - if (zout == NULL) { - return NULL; - } - } - - dest = ssh_buffer_new(); - if (dest == NULL) { - return NULL; - } - - zout->next_out = out_buf; - zout->next_in = in_ptr; - zout->avail_in = in_size; - do { - zout->avail_out = BLOCKSIZE; - status = deflate(zout, Z_PARTIAL_FLUSH); - if (status != Z_OK) { - ssh_buffer_free(dest); - ssh_set_error(session, SSH_FATAL, - "status %d deflating zlib packet", status); - return NULL; - } - len = BLOCKSIZE - zout->avail_out; - if (ssh_buffer_add_data(dest, out_buf, len) < 0) { - ssh_buffer_free(dest); - return NULL; - } - zout->next_out = out_buf; - } while (zout->avail_out == 0); - - return dest; -} - -int compress_buffer(ssh_session session, ssh_buffer buf) { - ssh_buffer dest = NULL; - - dest = gzip_compress(session, buf, session->opts.compressionlevel); - if (dest == NULL) { - return -1; - } - - if (ssh_buffer_reinit(buf) < 0) { - ssh_buffer_free(dest); - return -1; - } - - if (ssh_buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) { - ssh_buffer_free(dest); - return -1; - } - - ssh_buffer_free(dest); - return 0; -} - -/* decompression */ - -static z_stream *initdecompress(ssh_session session) { - z_stream *stream = NULL; - int status; - - stream = malloc(sizeof(z_stream)); - if (stream == NULL) { - return NULL; - } - memset(stream,0,sizeof(z_stream)); - - status = inflateInit(stream); - if (status != Z_OK) { - SAFE_FREE(stream); - ssh_set_error(session, SSH_FATAL, - "Status = %d initiating inflate context!", status); - return NULL; - } - - return stream; -} - -static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen) { - z_stream *zin = session->current_crypto->compress_in_ctx; - void *in_ptr = buffer_get_rest(source); - unsigned long in_size = buffer_get_rest_len(source); - unsigned char out_buf[BLOCKSIZE] = {0}; - ssh_buffer dest = NULL; - unsigned long len; - int status; - - if (zin == NULL) { - zin = session->current_crypto->compress_in_ctx = initdecompress(session); - if (zin == NULL) { - return NULL; - } - } - - dest = ssh_buffer_new(); - if (dest == NULL) { - return NULL; - } - - zin->next_out = out_buf; - zin->next_in = in_ptr; - zin->avail_in = in_size; - - do { - zin->avail_out = BLOCKSIZE; - status = inflate(zin, Z_PARTIAL_FLUSH); - if (status != Z_OK && status != Z_BUF_ERROR) { - ssh_set_error(session, SSH_FATAL, - "status %d inflating zlib packet", status); - ssh_buffer_free(dest); - return NULL; - } - - len = BLOCKSIZE - zin->avail_out; - if (ssh_buffer_add_data(dest,out_buf,len) < 0) { - ssh_buffer_free(dest); - return NULL; - } - if (buffer_get_rest_len(dest) > maxlen){ - /* Size of packet exceeded, avoid a denial of service attack */ - ssh_buffer_free(dest); - return NULL; - } - zin->next_out = out_buf; - } while (zin->avail_out == 0); - - return dest; -} - -int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen){ - ssh_buffer dest = NULL; - - dest = gzip_decompress(session,buf, maxlen); - if (dest == NULL) { - return -1; - } - - if (ssh_buffer_reinit(buf) < 0) { - ssh_buffer_free(dest); - return -1; - } - - if (ssh_buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) { - ssh_buffer_free(dest); - return -1; - } - - ssh_buffer_free(dest); - return 0; -} - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/init.c b/libssh/src/init.c deleted file mode 100644 index 241b8618..00000000 --- a/libssh/src/init.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * init.c - initialization and finalization of the library - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" -#include "libssh/priv.h" -#include "libssh/socket.h" -#include "libssh/dh.h" -#include "libssh/poll.h" -#include "libssh/threads.h" - -#ifdef _WIN32 -#include -#endif - -/** - * @defgroup libssh The libssh API - * - * The libssh library is implementing the SSH protocols and some of its - * extensions. This group of functions is mostly used to implment a SSH client. - * Some function are needed to implement a SSH server too. - * - * @{ - */ - -/** - * @brief Initialize global cryptographic data structures. - * - * This function should only be called once, at the beginning of the program, in - * the main thread. It may be omitted if your program is not multithreaded. - * - * @returns 0 on success, -1 if an error occured. - */ -int ssh_init(void) { - if(ssh_threads_init()) - return -1; - if(ssh_crypto_init()) - return -1; - if(ssh_socket_init()) - return -1; - return 0; -} - - -/** - * @brief Finalize and cleanup all libssh and cryptographic data structures. - * - * This function should only be called once, at the end of the program! - * - * @returns 0 on succes, -1 if an error occured. - * - @returns 0 otherwise - */ -int ssh_finalize(void) { - ssh_crypto_finalize(); - ssh_socket_cleanup(); - /* It is important to finalize threading after CRYPTO because - * it still depends on it */ - ssh_threads_finalize(); - - return 0; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/kex.c b/libssh/src/kex.c deleted file mode 100644 index f1a1b566..00000000 --- a/libssh/src/kex.c +++ /dev/null @@ -1,650 +0,0 @@ -/* - * kex.c - key exchange - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/buffer.h" -#include "libssh/dh.h" -#include "libssh/kex.h" -#include "libssh/session.h" -#include "libssh/ssh2.h" -#include "libssh/string.h" -#include "libssh/curve25519.h" -#include "libssh/knownhosts.h" - -#ifdef HAVE_LIBGCRYPT -# define BLOWFISH "blowfish-cbc," -# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc," -# define DES "3des-cbc,des-cbc-ssh1" -#elif defined(HAVE_LIBCRYPTO) -# ifdef HAVE_OPENSSL_BLOWFISH_H -# define BLOWFISH "blowfish-cbc," -# else -# define BLOWFISH "" -# endif -# ifdef HAVE_OPENSSL_AES_H -# ifdef BROKEN_AES_CTR -# define AES "aes256-cbc,aes192-cbc,aes128-cbc," -# else -# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc," -# endif /* BROKEN_AES_CTR */ -# else -# define AES "" -# endif -# define DES "3des-cbc,des-cbc-ssh1" -#endif - -#ifdef WITH_ZLIB -#define ZLIB "none,zlib,zlib@openssh.com" -#else -#define ZLIB "none" -#endif - -#ifdef HAVE_CURVE25519 -#define CURVE25519 "curve25519-sha256@libssh.org," -#else -#define CURVE25519 "" -#endif - -#ifdef HAVE_ECDH -#define ECDH "ecdh-sha2-nistp256," -#define HOSTKEYS "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss" -#else -#define HOSTKEYS "ssh-rsa,ssh-dss" -#define ECDH "" -#endif - -#define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" -#define KEX_METHODS_SIZE 10 - -/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ -static const char *default_methods[] = { - KEY_EXCHANGE, - HOSTKEYS, - AES BLOWFISH DES, - AES BLOWFISH DES, - "hmac-sha1,hmac-sha2-256,hmac-sha2-512", - "hmac-sha1,hmac-sha2-256,hmac-sha2-512", - "none", - "none", - "", - "", - NULL -}; - -/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ -static const char *supported_methods[] = { - KEY_EXCHANGE, - HOSTKEYS, - AES BLOWFISH DES, - AES BLOWFISH DES, - "hmac-sha1,hmac-sha2-256,hmac-sha2-512", - "hmac-sha1,hmac-sha2-256,hmac-sha2-512", - ZLIB, - ZLIB, - "", - "", - NULL -}; - -/* descriptions of the key exchange packet */ -static const char *ssh_kex_descriptions[] = { - "kex algos", - "server host key algo", - "encryption client->server", - "encryption server->client", - "mac algo client->server", - "mac algo server->client", - "compression algo client->server", - "compression algo server->client", - "languages client->server", - "languages server->client", - NULL -}; - -/* tokenize will return a token of strings delimited by ",". the first element has to be freed */ -static char **tokenize(const char *chain){ - char **tokens; - int n=1; - int i=0; - char *tmp; - char *ptr; - - tmp = strdup(chain); - if (tmp == NULL) { - return NULL; - } - ptr = tmp; - while(*ptr){ - if(*ptr==','){ - n++; - *ptr=0; - } - ptr++; - } - /* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */ - tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */ - if (tokens == NULL) { - SAFE_FREE(tmp); - return NULL; - } - ptr=tmp; - for(i=0;i= KEX_METHODS_SIZE) { - return NULL; - } - - return supported_methods[algo]; -} - -const char *ssh_kex_get_description(uint32_t algo) { - if (algo >= KEX_METHODS_SIZE) { - return NULL; - } - - return ssh_kex_descriptions[algo]; -} - -/* find_matching gets 2 parameters : a list of available objects (available_d), separated by colons,*/ -/* and a list of preferred objects (preferred_d) */ -/* it will return a strduped pointer on the first preferred object found in the available objects list */ - -char *ssh_find_matching(const char *available_d, const char *preferred_d){ - char ** tok_available, **tok_preferred; - int i_avail, i_pref; - char *ret; - - if ((available_d == NULL) || (preferred_d == NULL)) { - return NULL; /* don't deal with null args */ - } - - tok_available = tokenize(available_d); - if (tok_available == NULL) { - return NULL; - } - - tok_preferred = tokenize(preferred_d); - if (tok_preferred == NULL) { - SAFE_FREE(tok_available[0]); - SAFE_FREE(tok_available); - return NULL; - } - - for(i_pref=0; tok_preferred[i_pref] ; ++i_pref){ - for(i_avail=0; tok_available[i_avail]; ++i_avail){ - if(strcmp(tok_available[i_avail],tok_preferred[i_pref]) == 0){ - /* match */ - ret=strdup(tok_available[i_avail]); - /* free the tokens */ - SAFE_FREE(tok_available[0]); - SAFE_FREE(tok_preferred[0]); - SAFE_FREE(tok_available); - SAFE_FREE(tok_preferred); - return ret; - } - } - } - SAFE_FREE(tok_available[0]); - SAFE_FREE(tok_preferred[0]); - SAFE_FREE(tok_available); - SAFE_FREE(tok_preferred); - return NULL; -} - -/** - * @internal - * @brief returns whether the first client key exchange algorithm matches - * the first server key exchange algorithm - * @returns whether the first client key exchange algorithm matches - * the first server key exchange algorithm - */ -static int is_first_kex_packet_follows_guess_wrong(const char *client_kex, - const char *server_kex) { - int is_wrong = 1; - char **server_kex_tokens = NULL; - char **client_kex_tokens = NULL; - - if ((client_kex == NULL) || (server_kex == NULL)) { - goto out; - } - - client_kex_tokens = tokenize(client_kex); - - if (client_kex_tokens == NULL) { - goto out; - } - - if (client_kex_tokens[0] == NULL) { - goto freeout; - } - - server_kex_tokens = tokenize(server_kex); - if (server_kex_tokens == NULL) { - goto freeout; - } - - is_wrong = (strcmp(client_kex_tokens[0], server_kex_tokens[0]) != 0); - - SAFE_FREE(server_kex_tokens[0]); - SAFE_FREE(server_kex_tokens); -freeout: - SAFE_FREE(client_kex_tokens[0]); - SAFE_FREE(client_kex_tokens); -out: - return is_wrong; -} - -SSH_PACKET_CALLBACK(ssh_packet_kexinit){ - int i; - int server_kex=session->server; - ssh_string str = NULL; - char *strings[KEX_METHODS_SIZE]; - int rc = SSH_ERROR; - - uint8_t first_kex_packet_follows = 0; - uint32_t kexinit_reserved = 0; - - (void)type; - (void)user; - - memset(strings, 0, sizeof(strings)); - if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED){ - SSH_LOG(SSH_LOG_WARNING, "Other side initiating key re-exchange"); - } else if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){ - ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); - goto error; - } - - if (server_kex) { - rc = buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16); - if (rc != 16) { - ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); - goto error; - } - - rc = hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); - goto error; - } - } else { - rc = buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16); - if (rc != 16) { - ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet"); - goto error; - } - - rc = hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed"); - goto error; - } - } - - for (i = 0; i < KEX_METHODS_SIZE; i++) { - str = buffer_get_ssh_string(packet); - if (str == NULL) { - break; - } - - rc = buffer_add_ssh_string(session->in_hashbuf, str); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer"); - goto error; - } - - strings[i] = ssh_string_to_char(str); - if (strings[i] == NULL) { - ssh_set_error_oom(session); - goto error; - } - ssh_string_free(str); - str = NULL; - } - - /* copy the server kex info into an array of strings */ - if (server_kex) { - for (i = 0; i < SSH_KEX_METHODS; i++) { - session->next_crypto->client_kex.methods[i] = strings[i]; - } - } else { /* client */ - for (i = 0; i < SSH_KEX_METHODS; i++) { - session->next_crypto->server_kex.methods[i] = strings[i]; - } - } - - /* - * Handle the two final fields for the KEXINIT message (RFC 4253 7.1): - * - * boolean first_kex_packet_follows - * uint32 0 (reserved for future extension) - * - * Notably if clients set 'first_kex_packet_follows', it is expected - * that its value is included when computing the session ID (see - * 'make_sessionid'). - */ - if (server_kex) { - rc = buffer_get_u8(packet, &first_kex_packet_follows); - if (rc != 1) { - goto error; - } - - rc = buffer_add_u8(session->in_hashbuf, first_kex_packet_follows); - if (rc < 0) { - goto error; - } - - rc = buffer_add_u32(session->in_hashbuf, kexinit_reserved); - if (rc < 0) { - goto error; - } - - /* - * Remember whether 'first_kex_packet_follows' was set and the client - * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message - * must be ignored. - */ - if (first_kex_packet_follows) { - session->first_kex_follows_guess_wrong = - is_first_kex_packet_follows_guess_wrong(session->next_crypto->client_kex.methods[SSH_KEX], - session->next_crypto->server_kex.methods[SSH_KEX]); - } - } - - session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED; - session->dh_handshake_state = DH_STATE_INIT; - session->ssh_connection_callback(session); - return SSH_PACKET_USED; - -error: - ssh_string_free(str); - for (i = 0; i < SSH_KEX_METHODS; i++) { - SAFE_FREE(strings[i]); - } - - session->session_state = SSH_SESSION_STATE_ERROR; - - return SSH_PACKET_USED; -} - -void ssh_list_kex(struct ssh_kex_struct *kex) { - int i = 0; - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("session cookie", kex->cookie, 16); -#endif - - for(i = 0; i < SSH_KEX_METHODS; i++) { - if (kex->methods[i] == NULL) { - continue; - } - SSH_LOG(SSH_LOG_FUNCTIONS, "%s: %s", - ssh_kex_descriptions[i], kex->methods[i]); - } -} - -/** - * @internal - * @brief selects the hostkey mechanisms to be chosen for the key exchange, - * as some hostkey mechanisms may be present in known_hosts file and preferred - * @returns a cstring containing a comma-separated list of hostkey methods. - * NULL if no method matches - */ -static char *ssh_client_select_hostkeys(ssh_session session){ - char methods_buffer[128]={0}; - static const char *preferred_hostkeys[]={"ecdsa-sha2-nistp521","ecdsa-sha2-nistp384", - "ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss", "ssh-rsa1", NULL}; - char **methods; - int i,j; - int needcoma=0; - - methods = ssh_knownhosts_algorithms(session); - if (methods == NULL || methods[0] == NULL){ - SAFE_FREE(methods); - return NULL; - } - - for (i=0;preferred_hostkeys[i] != NULL; ++i){ - for (j=0; methods[j] != NULL; ++j){ - if(strcmp(preferred_hostkeys[i], methods[j]) == 0){ - if (verify_existing_algo(SSH_HOSTKEYS, methods[j])){ - if(needcoma) - strncat(methods_buffer,",",sizeof(methods_buffer)-strlen(methods_buffer)-1); - strncat(methods_buffer, methods[j], sizeof(methods_buffer)-strlen(methods_buffer)-1); - needcoma = 1; - } - } - } - } - for(i=0;methods[i]!= NULL; ++i){ - SAFE_FREE(methods[i]); - } - SAFE_FREE(methods); - - if(strlen(methods_buffer) > 0){ - SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", methods_buffer); - return strdup(methods_buffer); - } else { - SSH_LOG(SSH_LOG_DEBUG, "No supported kex method for existing key in known_hosts file"); - return NULL; - } - -} -/** - * @brief sets the key exchange parameters to be sent to the server, - * in function of the options and available methods. - */ -int set_client_kex(ssh_session session){ - struct ssh_kex_struct *client= &session->next_crypto->client_kex; - const char *wanted; - int i; - - ssh_get_random(client->cookie, 16, 0); - - memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **)); - /* first check if we have specific host key methods */ - if(session->opts.wanted_methods[SSH_HOSTKEYS] == NULL){ - /* Only if no override */ - session->opts.wanted_methods[SSH_HOSTKEYS] = - ssh_client_select_hostkeys(session); - } - - for (i = 0; i < KEX_METHODS_SIZE; i++) { - wanted = session->opts.wanted_methods[i]; - if (wanted == NULL) - wanted = default_methods[i]; - client->methods[i] = strdup(wanted); - } - - return SSH_OK; -} - -/** @brief Select the different methods on basis of client's and - * server's kex messages, and watches out if a match is possible. - */ -int ssh_kex_select_methods (ssh_session session){ - struct ssh_kex_struct *server = &session->next_crypto->server_kex; - struct ssh_kex_struct *client = &session->next_crypto->client_kex; - int i; - - for (i = 0; i < KEX_METHODS_SIZE; i++) { - session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]); - if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){ - ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]", - ssh_kex_descriptions[i],server->methods[i],client->methods[i]); - return SSH_ERROR; - } else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) { - /* we can safely do that for languages */ - session->next_crypto->kex_methods[i] = strdup(""); - } - } - if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){ - session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1; - } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha1") == 0){ - session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1; - } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){ - session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256; - } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){ - session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG; - } - - return SSH_OK; -} - - -/* this function only sends the predefined set of kex methods */ -int ssh_send_kex(ssh_session session, int server_kex) { - struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex : - &session->next_crypto->client_kex); - ssh_string str = NULL; - int i; - int rc; - - rc = ssh_buffer_pack(session->out_buffer, - "bP", - SSH2_MSG_KEXINIT, - 16, - kex->cookie); /* cookie */ - if (rc != SSH_OK) - goto error; - if (hashbufout_add_cookie(session) < 0) { - goto error; - } - - ssh_list_kex(kex); - - for (i = 0; i < KEX_METHODS_SIZE; i++) { - str = ssh_string_from_char(kex->methods[i]); - if (str == NULL) { - goto error; - } - - if (buffer_add_ssh_string(session->out_hashbuf, str) < 0) { - goto error; - } - if (buffer_add_ssh_string(session->out_buffer, str) < 0) { - goto error; - } - ssh_string_free(str); - str = NULL; - } - - rc = ssh_buffer_pack(session->out_buffer, - "bd", - 0, - 0); - if (rc != SSH_OK) { - goto error; - } - - if (packet_send(session) == SSH_ERROR) { - return -1; - } - - return 0; -error: - ssh_buffer_reinit(session->out_buffer); - ssh_buffer_reinit(session->out_hashbuf); - ssh_string_free(str); - - return -1; -} - -/* returns 1 if at least one of the name algos is in the default algorithms table */ -int verify_existing_algo(int algo, const char *name){ - char *ptr; - if(algo>9 || algo <0) - return -1; - ptr=ssh_find_matching(supported_methods[algo],name); - if(ptr){ - free(ptr); - return 1; - } - return 0; -} - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/kex1.c b/libssh/src/kex1.c deleted file mode 100644 index 758054f8..00000000 --- a/libssh/src/kex1.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * kex.c - key exchange - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#ifndef _WIN32 -#include -#endif - -#include "libssh/priv.h" -#include "libssh/buffer.h" -#include "libssh/crypto.h" -#include "libssh/kex.h" -#include "libssh/keys.h" -#include "libssh/session.h" -#include "libssh/ssh1.h" -#include "libssh/wrapper.h" - -/* SSHv1 functions */ - -/* makes a STRING contating 3 strings : ssh-rsa1,e and n */ -/* this is a public key in openssh's format */ -static ssh_string make_rsa1_string(ssh_string e, ssh_string n){ - ssh_buffer buffer = NULL; - ssh_string rsa = NULL; - ssh_string ret = NULL; - - buffer = ssh_buffer_new(); - rsa = ssh_string_from_char("ssh-rsa1"); - if (rsa == NULL) { - goto error; - } - - if (buffer_add_ssh_string(buffer, rsa) < 0) { - goto error; - } - if (buffer_add_ssh_string(buffer, e) < 0) { - goto error; - } - if (buffer_add_ssh_string(buffer, n) < 0) { - goto error; - } - - ret = ssh_string_new(ssh_buffer_get_len(buffer)); - if (ret == NULL) { - goto error; - } - - ssh_string_fill(ret, ssh_buffer_get_begin(buffer), ssh_buffer_get_len(buffer)); -error: - ssh_buffer_free(buffer); - ssh_string_free(rsa); - - return ret; -} - -static int build_session_id1(ssh_session session, ssh_string servern, - ssh_string hostn) { - MD5CTX md5 = NULL; - - md5 = md5_init(); - if (md5 == NULL) { - return -1; - } - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("host modulus",ssh_string_data(hostn),ssh_string_len(hostn)); - ssh_print_hexa("server modulus",ssh_string_data(servern),ssh_string_len(servern)); -#endif - md5_update(md5,ssh_string_data(hostn),ssh_string_len(hostn)); - md5_update(md5,ssh_string_data(servern),ssh_string_len(servern)); - md5_update(md5,session->next_crypto->server_kex.cookie,8); - if(session->next_crypto->session_id != NULL) - SAFE_FREE(session->next_crypto->session_id); - session->next_crypto->session_id = malloc(MD5_DIGEST_LEN); - if(session->next_crypto->session_id == NULL){ - ssh_set_error_oom(session); - return SSH_ERROR; - } - md5_final(session->next_crypto->session_id,md5); -#ifdef DEBUG_CRYPTO - ssh_print_hexa("session_id",session->next_crypto->session_id,MD5_DIGEST_LEN); -#endif - - return 0; -} - -/* returns 1 if the modulus of k1 is < than the one of k2 */ -static int modulus_smaller(ssh_public_key k1, ssh_public_key k2){ - bignum n1; - bignum n2; - int res; -#ifdef HAVE_LIBGCRYPT - gcry_sexp_t sexp; - sexp=gcry_sexp_find_token(k1->rsa_pub,"n",0); - n1=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG); - gcry_sexp_release(sexp); - sexp=gcry_sexp_find_token(k2->rsa_pub,"n",0); - n2=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG); - gcry_sexp_release(sexp); -#elif defined HAVE_LIBCRYPTO - n1=k1->rsa_pub->n; - n2=k2->rsa_pub->n; -#endif - if(bignum_cmp(n1,n2)<0) - res=1; - else - res=0; -#ifdef HAVE_LIBGCRYPT - bignum_free(n1); - bignum_free(n2); -#endif - return res; - -} - -static ssh_string ssh_encrypt_rsa1(ssh_session session, - ssh_string data, - ssh_public_key key) { - ssh_string str = NULL; - size_t len = ssh_string_len(data); - size_t size = 0; -#ifdef HAVE_LIBGCRYPT - const char *tmp = NULL; - gcry_sexp_t ret_sexp; - gcry_sexp_t data_sexp; - - if (gcry_sexp_build(&data_sexp, NULL, "(data(flags pkcs1)(value %b))", - len, ssh_string_data(data))) { - ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error"); - return NULL; - } - if (gcry_pk_encrypt(&ret_sexp, data_sexp, key->rsa_pub)) { - gcry_sexp_release(data_sexp); - ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error"); - return NULL; - } - - gcry_sexp_release(data_sexp); - - data_sexp = gcry_sexp_find_token(ret_sexp, "a", 0); - if (data_sexp == NULL) { - ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error"); - gcry_sexp_release(ret_sexp); - return NULL; - } - tmp = gcry_sexp_nth_data(data_sexp, 1, &size); - if (*tmp == 0) { - size--; - tmp++; - } - - str = ssh_string_new(size); - if (str == NULL) { - ssh_set_error(session, SSH_FATAL, "Not enough space"); - gcry_sexp_release(data_sexp); - gcry_sexp_release(ret_sexp); - return NULL; - } - ssh_string_fill(str, tmp, size); - - gcry_sexp_release(data_sexp); - gcry_sexp_release(ret_sexp); -#elif defined HAVE_LIBCRYPTO - size = RSA_size(key->rsa_pub); - - str = ssh_string_new(size); - if (str == NULL) { - ssh_set_error(session, SSH_FATAL, "Not enough space"); - return NULL; - } - - if (RSA_public_encrypt(len, ssh_string_data(data), ssh_string_data(str), key->rsa_pub, - RSA_PKCS1_PADDING) < 0) { - ssh_string_free(str); - return NULL; - } -#endif - - return str; -} - -#define ABS(A) ( (A)<0 ? -(A):(A) ) -static ssh_string encrypt_session_key(ssh_session session, ssh_public_key srvkey, - ssh_public_key hostkey, int slen, int hlen) { - unsigned char buffer[32] = {0}; - int i; - ssh_string data1 = NULL; - ssh_string data2 = NULL; - if(session->next_crypto->encryptkey != NULL) - SAFE_FREE(session->next_crypto->encryptkey); - if(session->next_crypto->decryptkey != NULL) - SAFE_FREE(session->next_crypto->decryptkey); - if(session->next_crypto->encryptIV != NULL) - SAFE_FREE(session->next_crypto->encryptIV); - if(session->next_crypto->decryptIV != NULL) - SAFE_FREE(session->next_crypto->decryptIV); - session->next_crypto->encryptkey = malloc(32); - session->next_crypto->decryptkey = malloc(32); - session->next_crypto->encryptIV = malloc(32); - session->next_crypto->decryptIV = malloc(32); - if(session->next_crypto->encryptkey == NULL || - session->next_crypto->decryptkey == NULL || - session->next_crypto->encryptIV == NULL || - session->next_crypto->decryptIV == NULL){ - ssh_set_error_oom(session); - return NULL; - } - /* first, generate a session key */ - ssh_get_random(session->next_crypto->encryptkey, 32, 1); - memcpy(buffer, session->next_crypto->encryptkey, 32); - memcpy(session->next_crypto->decryptkey, session->next_crypto->encryptkey, 32); - memset(session->next_crypto->encryptIV, 0, 32); - memset(session->next_crypto->decryptIV, 0, 32); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("session key",buffer,32); -#endif - - /* xor session key with session_id */ - for (i = 0; i < 16; i++) { - buffer[i] ^= session->next_crypto->session_id[i]; - } - data1 = ssh_string_new(32); - if (data1 == NULL) { - return NULL; - } - ssh_string_fill(data1, buffer, 32); - if (ABS(hlen - slen) < 128){ - SSH_LOG(SSH_LOG_FUNCTIONS, - "Difference between server modulus and host modulus is only %d. " - "It's illegal and may not work", - ABS(hlen - slen)); - } - - if (modulus_smaller(srvkey, hostkey)) { - data2 = ssh_encrypt_rsa1(session, data1, srvkey); - ssh_string_free(data1); - data1 = NULL; - if (data2 == NULL) { - return NULL; - } - data1 = ssh_encrypt_rsa1(session, data2, hostkey); - ssh_string_free(data2); - if (data1 == NULL) { - return NULL; - } - } else { - data2 = ssh_encrypt_rsa1(session, data1, hostkey); - ssh_string_free(data1); - data1 = NULL; - if (data2 == NULL) { - return NULL; - } - data1 = ssh_encrypt_rsa1(session, data2, srvkey); - ssh_string_free(data2); - if (data1 == NULL) { - return NULL; - } - } - - return data1; -} - -/* 2 SSH_SMSG_PUBLIC_KEY - * - * 8 bytes anti_spoofing_cookie - * 32-bit int server_key_bits - * mp-int server_key_public_exponent - * mp-int server_key_public_modulus - * 32-bit int host_key_bits - * mp-int host_key_public_exponent - * mp-int host_key_public_modulus - * 32-bit int protocol_flags - * 32-bit int supported_ciphers_mask - * 32-bit int supported_authentications_mask - */ -/** - * @brief Wait for a SSH_SMSG_PUBLIC_KEY and does the key exchange - */ -SSH_PACKET_CALLBACK(ssh_packet_publickey1){ - ssh_string server_exp = NULL; - ssh_string server_mod = NULL; - ssh_string host_exp = NULL; - ssh_string host_mod = NULL; - ssh_string serverkey = NULL; - ssh_string hostkey = NULL; - ssh_public_key srv = NULL; - ssh_public_key host = NULL; - uint32_t server_bits; - uint32_t host_bits; - uint32_t protocol_flags; - uint32_t supported_ciphers_mask; - uint32_t supported_authentications_mask; - ssh_string enc_session = NULL; - uint16_t bits; - int ko; - uint32_t support_3DES = 0; - uint32_t support_DES = 0; - - (void)type; - (void)user; - SSH_LOG(SSH_LOG_PROTOCOL, "Got a SSH_SMSG_PUBLIC_KEY"); - if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){ - ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state"); - goto error; - } - if (buffer_get_data(packet, session->next_crypto->server_kex.cookie, 8) != 8) { - ssh_set_error(session, SSH_FATAL, "Can't get cookie in buffer"); - goto error; - } - - buffer_get_u32(packet, &server_bits); - server_exp = buffer_get_mpint(packet); - if (server_exp == NULL) { - goto error; - } - server_mod = buffer_get_mpint(packet); - if (server_mod == NULL) { - goto error; - } - buffer_get_u32(packet, &host_bits); - host_exp = buffer_get_mpint(packet); - if (host_exp == NULL) { - goto error; - } - host_mod = buffer_get_mpint(packet); - if (host_mod == NULL) { - goto error; - } - buffer_get_u32(packet, &protocol_flags); - buffer_get_u32(packet, &supported_ciphers_mask); - ko = buffer_get_u32(packet, &supported_authentications_mask); - - if ((ko != sizeof(uint32_t)) || !host_mod || !host_exp - || !server_mod || !server_exp) { - SSH_LOG(SSH_LOG_RARE, "Invalid SSH_SMSG_PUBLIC_KEY packet"); - ssh_set_error(session, SSH_FATAL, "Invalid SSH_SMSG_PUBLIC_KEY packet"); - goto error; - } - - server_bits = ntohl(server_bits); - host_bits = ntohl(host_bits); - protocol_flags = ntohl(protocol_flags); - supported_ciphers_mask = ntohl(supported_ciphers_mask); - supported_authentications_mask = ntohl(supported_authentications_mask); - SSH_LOG(SSH_LOG_PROTOCOL, - "Server bits: %d; Host bits: %d; Protocol flags: %.8lx; " - "Cipher mask: %.8lx; Auth mask: %.8lx", - server_bits, - host_bits, - (unsigned long int) protocol_flags, - (unsigned long int) supported_ciphers_mask, - (unsigned long int) supported_authentications_mask); - - serverkey = make_rsa1_string(server_exp, server_mod); - if (serverkey == NULL) { - goto error; - } - hostkey = make_rsa1_string(host_exp,host_mod); - if (hostkey == NULL) { - goto error; - } - if (build_session_id1(session, server_mod, host_mod) < 0) { - goto error; - } - - srv = publickey_from_string(session, serverkey); - if (srv == NULL) { - goto error; - } - host = publickey_from_string(session, hostkey); - if (host == NULL) { - goto error; - } - - session->next_crypto->server_pubkey = ssh_string_copy(hostkey); - if (session->next_crypto->server_pubkey == NULL) { - goto error; - } - session->next_crypto->server_pubkey_type = "ssh-rsa1"; - - /* now, we must choose an encryption algo */ - /* hardcode 3des */ - // - support_3DES = (supported_ciphers_mask & (1<out_buffer, SSH_CMSG_SESSION_KEY) < 0) { - goto error; - } - if (buffer_add_u8(session->out_buffer, support_3DES ? SSH_CIPHER_3DES : SSH_CIPHER_DES) < 0) { - goto error; - } - if (ssh_buffer_add_data(session->out_buffer, session->next_crypto->server_kex.cookie, 8) < 0) { - goto error; - } - - enc_session = encrypt_session_key(session, srv, host, server_bits, host_bits); - if (enc_session == NULL) { - goto error; - } - - bits = ssh_string_len(enc_session) * 8 - 7; - SSH_LOG(SSH_LOG_PROTOCOL, "%d bits, %" PRIdS " bytes encrypted session", - bits, ssh_string_len(enc_session)); - bits = htons(bits); - /* the encrypted mpint */ - if (ssh_buffer_add_data(session->out_buffer, &bits, sizeof(uint16_t)) < 0) { - goto error; - } - if (ssh_buffer_add_data(session->out_buffer, ssh_string_data(enc_session), - ssh_string_len(enc_session)) < 0) { - goto error; - } - /* the protocol flags */ - if (buffer_add_u32(session->out_buffer, 0) < 0) { - goto error; - } - session->session_state=SSH_SESSION_STATE_KEXINIT_RECEIVED; - if (packet_send(session) == SSH_ERROR) { - goto error; - } - - /* we can set encryption */ - if(crypt_set_algorithms(session, support_3DES ? SSH_3DES : SSH_DES)){ - goto error; - } - - session->current_crypto = session->next_crypto; - session->next_crypto = NULL; - goto end; -error: - session->session_state=SSH_SESSION_STATE_ERROR; -end: - - ssh_string_free(host_mod); - ssh_string_free(host_exp); - ssh_string_free(server_mod); - ssh_string_free(server_exp); - ssh_string_free(serverkey); - ssh_string_free(hostkey); - ssh_string_free(enc_session); - - publickey_free(srv); - publickey_free(host); - - return SSH_PACKET_USED; -} - -int ssh_get_kex1(ssh_session session) { - SSH_LOG(SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_PUBLIC_KEY"); - - /* Here the callback is called */ - while(session->session_state==SSH_SESSION_STATE_INITIAL_KEX){ - ssh_handle_packets(session, SSH_TIMEOUT_USER); - } - if (session->session_state==SSH_SESSION_STATE_ERROR) { - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_SUCCESS"); - /* Waiting for SSH_SMSG_SUCCESS */ - while(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){ - ssh_handle_packets(session, SSH_TIMEOUT_USER); - } - if(session->session_state==SSH_SESSION_STATE_ERROR) { - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PROTOCOL, "received SSH_SMSG_SUCCESS\n"); - - return SSH_OK; -} diff --git a/libssh/src/known_hosts.c b/libssh/src/known_hosts.c deleted file mode 100644 index 8c05c8de..00000000 --- a/libssh/src/known_hosts.c +++ /dev/null @@ -1,748 +0,0 @@ -/* - * keyfiles.c - private and public key handling for authentication. - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * Copyright (c) 2009 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/session.h" -#include "libssh/buffer.h" -#include "libssh/misc.h" -#include "libssh/pki.h" -#include "libssh/options.h" -#include "libssh/knownhosts.h" -/*todo: remove this include */ -#include "libssh/string.h" - -#ifdef HAVE_LIBGCRYPT -#include -#elif defined HAVE_LIBCRYPTO -#include -#include -#include -#include -#endif /* HAVE_LIBCRYPTO */ - -#ifndef _WIN32 -# include -# include -#endif - -/** - * @addtogroup libssh_session - * - * @{ - */ - -static int alldigits(const char *s) { - while (*s) { - if (isdigit(*s)) { - s++; - } else { - return 0; - } - } - - return 1; -} - -/** - * @internal - * - * @brief Free a token array. - */ -static void tokens_free(char **tokens) { - if (tokens == NULL) { - return; - } - - SAFE_FREE(tokens[0]); - /* It's not needed to free other pointers because tokens generated by - * space_tokenize fit all in one malloc - */ - SAFE_FREE(tokens); -} - -/** - * @internal - * - * @brief Return one line of known host file. - * - * This will return a token array containing (host|ip), keytype and key. - * - * @param[out] file A pointer to the known host file. Could be pointing to - * NULL at start. - * - * @param[in] filename The file name of the known host file. - * - * @param[out] found_type A pointer to a string to be set with the found key - * type. - * - * @returns The found_type type of key (ie "dsa","ssh-rsa1"). Don't - * free that value. NULL if no match was found or the file - * was not found. - */ -static char **ssh_get_knownhost_line(FILE **file, const char *filename, - const char **found_type) { - char buffer[4096] = {0}; - char *ptr; - char **tokens; - - if(*file == NULL){ - *file = fopen(filename,"r"); - if (*file == NULL) { - return NULL; - } - } - - while (fgets(buffer, sizeof(buffer), *file)) { - ptr = strchr(buffer, '\n'); - if (ptr) { - *ptr = '\0'; - } - - ptr = strchr(buffer,'\r'); - if (ptr) { - *ptr = '\0'; - } - - if (buffer[0] == '\0' || buffer[0] == '#') { - continue; /* skip empty lines */ - } - - tokens = space_tokenize(buffer); - if (tokens == NULL) { - fclose(*file); - *file = NULL; - - return NULL; - } - - if(!tokens[0] || !tokens[1] || !tokens[2]) { - /* it should have at least 3 tokens */ - tokens_free(tokens); - continue; - } - - *found_type = tokens[1]; - if (tokens[3]) { - /* openssh rsa1 format has 4 tokens on the line. Recognize it - by the fact that everything is all digits */ - if (tokens[4]) { - /* that's never valid */ - tokens_free(tokens); - continue; - } - if (alldigits(tokens[1]) && alldigits(tokens[2]) && alldigits(tokens[3])) { - *found_type = "ssh-rsa1"; - } else { - /* 3 tokens only, not four */ - tokens_free(tokens); - continue; - } - } - - return tokens; - } - - fclose(*file); - *file = NULL; - - /* we did not find anything, end of file*/ - return NULL; -} - -/** - * @brief Check the public key in the known host line matches the public key of - * the currently connected server. - * - * @param[in] session The SSH session to use. - * - * @param[in] tokens A list of tokens in the known_hosts line. - * - * @returns 1 if the key matches, 0 if the key doesn't match and -1 - * on error. - */ -static int check_public_key(ssh_session session, char **tokens) { - ssh_string pubkey = session->current_crypto->server_pubkey; - ssh_buffer pubkey_buffer; - char *pubkey_64; - - /* ok we found some public key in known hosts file. now un-base64it */ - if (alldigits(tokens[1])) { - /* openssh rsa1 format */ - bignum tmpbn; - ssh_string tmpstring; - unsigned int len; - int i; - - pubkey_buffer = ssh_buffer_new(); - if (pubkey_buffer == NULL) { - return -1; - } - - tmpstring = ssh_string_from_char("ssh-rsa1"); - if (tmpstring == NULL) { - ssh_buffer_free(pubkey_buffer); - return -1; - } - - if (buffer_add_ssh_string(pubkey_buffer, tmpstring) < 0) { - ssh_buffer_free(pubkey_buffer); - ssh_string_free(tmpstring); - return -1; - } - ssh_string_free(tmpstring); - - for (i = 2; i < 4; i++) { /* e, then n */ - tmpbn = NULL; - bignum_dec2bn(tokens[i], &tmpbn); - if (tmpbn == NULL) { - ssh_buffer_free(pubkey_buffer); - return -1; - } - /* for some reason, make_bignum_string does not work - because of the padding which it does --kv */ - /* tmpstring = make_bignum_string(tmpbn); */ - /* do it manually instead */ - len = bignum_num_bytes(tmpbn); - tmpstring = malloc(4 + len); - if (tmpstring == NULL) { - ssh_buffer_free(pubkey_buffer); - bignum_free(tmpbn); - return -1; - } - /* TODO: fix the hardcoding */ - tmpstring->size = htonl(len); -#ifdef HAVE_LIBGCRYPT - bignum_bn2bin(tmpbn, len, ssh_string_data(tmpstring)); -#elif defined HAVE_LIBCRYPTO - bignum_bn2bin(tmpbn, ssh_string_data(tmpstring)); -#endif - bignum_free(tmpbn); - if (buffer_add_ssh_string(pubkey_buffer, tmpstring) < 0) { - ssh_buffer_free(pubkey_buffer); - ssh_string_free(tmpstring); - bignum_free(tmpbn); - return -1; - } - ssh_string_free(tmpstring); - } - } else { - /* ssh-dss or ssh-rsa */ - pubkey_64 = tokens[2]; - pubkey_buffer = base64_to_bin(pubkey_64); - } - - if (pubkey_buffer == NULL) { - ssh_set_error(session, SSH_FATAL, - "Verifying that server is a known host: base64 error"); - return -1; - } - - if (buffer_get_rest_len(pubkey_buffer) != ssh_string_len(pubkey)) { - ssh_buffer_free(pubkey_buffer); - return 0; - } - - /* now test that they are identical */ - if (memcmp(buffer_get_rest(pubkey_buffer), ssh_string_data(pubkey), - buffer_get_rest_len(pubkey_buffer)) != 0) { - ssh_buffer_free(pubkey_buffer); - return 0; - } - - ssh_buffer_free(pubkey_buffer); - return 1; -} - -/** - * @brief Check if a hostname matches a openssh-style hashed known host. - * - * @param[in] host The host to check. - * - * @param[in] hashed The hashed value. - * - * @returns 1 if it matches, 0 otherwise. - */ -static int match_hashed_host(const char *host, const char *sourcehash) -{ - /* Openssh hash structure : - * |1|base64 encoded salt|base64 encoded hash - * hash is produced that way : - * hash := HMAC_SHA1(key=salt,data=host) - */ - unsigned char buffer[256] = {0}; - ssh_buffer salt; - ssh_buffer hash; - HMACCTX mac; - char *source; - char *b64hash; - int match; - unsigned int size; - - if (strncmp(sourcehash, "|1|", 3) != 0) { - return 0; - } - - source = strdup(sourcehash + 3); - if (source == NULL) { - return 0; - } - - b64hash = strchr(source, '|'); - if (b64hash == NULL) { - /* Invalid hash */ - SAFE_FREE(source); - - return 0; - } - - *b64hash = '\0'; - b64hash++; - - salt = base64_to_bin(source); - if (salt == NULL) { - SAFE_FREE(source); - - return 0; - } - - hash = base64_to_bin(b64hash); - SAFE_FREE(source); - if (hash == NULL) { - ssh_buffer_free(salt); - - return 0; - } - - mac = hmac_init(buffer_get_rest(salt), buffer_get_rest_len(salt), SSH_HMAC_SHA1); - if (mac == NULL) { - ssh_buffer_free(salt); - ssh_buffer_free(hash); - - return 0; - } - size = sizeof(buffer); - hmac_update(mac, host, strlen(host)); - hmac_final(mac, buffer, &size); - - if (size == buffer_get_rest_len(hash) && - memcmp(buffer, buffer_get_rest(hash), size) == 0) { - match = 1; - } else { - match = 0; - } - - ssh_buffer_free(salt); - ssh_buffer_free(hash); - - SSH_LOG(SSH_LOG_PACKET, - "Matching a hashed host: %s match=%d", host, match); - - return match; -} - -/* How it's working : - * 1- we open the known host file and bitch if it doesn't exist - * 2- we need to examine each line of the file, until going on state SSH_SERVER_KNOWN_OK: - * - there's a match. if the key is good, state is SSH_SERVER_KNOWN_OK, - * else it's SSH_SERVER_KNOWN_CHANGED (or SSH_SERVER_FOUND_OTHER) - * - there's no match : no change - */ - -/** - * @brief Check if the server is known. - * - * Checks the user's known host file for a previous connection to the - * current server. - * - * @param[in] session The SSH session to use. - * - * @returns SSH_SERVER_KNOWN_OK: The server is known and has not changed.\n - * SSH_SERVER_KNOWN_CHANGED: The server key has changed. Either you - * are under attack or the administrator - * changed the key. You HAVE to warn the - * user about a possible attack.\n - * SSH_SERVER_FOUND_OTHER: The server gave use a key of a type while - * we had an other type recorded. It is a - * possible attack.\n - * SSH_SERVER_NOT_KNOWN: The server is unknown. User should - * confirm the MD5 is correct.\n - * SSH_SERVER_FILE_NOT_FOUND: The known host file does not exist. The - * host is thus unknown. File will be - * created if host key is accepted.\n - * SSH_SERVER_ERROR: Some error happened. - * - * @see ssh_get_pubkey_hash() - * - * @bug There is no current way to remove or modify an entry into the known - * host table. - */ -int ssh_is_server_known(ssh_session session) { - FILE *file = NULL; - char **tokens; - char *host; - char *hostport; - const char *type; - int match; - int ret = SSH_SERVER_NOT_KNOWN; - - if (session->opts.knownhosts == NULL) { - if (ssh_options_apply(session) < 0) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "Can't find a known_hosts file"); - - return SSH_SERVER_FILE_NOT_FOUND; - } - } - - if (session->opts.host == NULL) { - ssh_set_error(session, SSH_FATAL, - "Can't verify host in known hosts if the hostname isn't known"); - - return SSH_SERVER_ERROR; - } - - if (session->current_crypto == NULL){ - ssh_set_error(session, SSH_FATAL, - "ssh_is_host_known called without cryptographic context"); - - return SSH_SERVER_ERROR; - } - host = ssh_lowercase(session->opts.host); - hostport = ssh_hostport(host, session->opts.port); - if (host == NULL || hostport == NULL) { - ssh_set_error_oom(session); - SAFE_FREE(host); - SAFE_FREE(hostport); - - return SSH_SERVER_ERROR; - } - - do { - tokens = ssh_get_knownhost_line(&file, - session->opts.knownhosts, - &type); - - /* End of file, return the current state */ - if (tokens == NULL) { - break; - } - match = match_hashed_host(host, tokens[0]); - if (match == 0){ - match = match_hostname(hostport, tokens[0], strlen(tokens[0])); - } - if (match == 0) { - match = match_hostname(host, tokens[0], strlen(tokens[0])); - } - if (match == 0) { - match = match_hashed_host(hostport, tokens[0]); - } - if (match) { - /* We got a match. Now check the key type */ - if (strcmp(session->current_crypto->server_pubkey_type, type) != 0) { - SSH_LOG(SSH_LOG_PACKET, - "ssh_is_server_known: server type [%s] doesn't match the " - "type [%s] in known_hosts file", - session->current_crypto->server_pubkey_type, - type); - /* Different type. We don't override the known_changed error which is - * more important */ - if (ret != SSH_SERVER_KNOWN_CHANGED) - ret = SSH_SERVER_FOUND_OTHER; - tokens_free(tokens); - continue; - } - /* so we know the key type is good. We may get a good key or a bad key. */ - match = check_public_key(session, tokens); - tokens_free(tokens); - - if (match < 0) { - ret = SSH_SERVER_ERROR; - break; - } else if (match == 1) { - ret = SSH_SERVER_KNOWN_OK; - break; - } else if(match == 0) { - /* We override the status with the wrong key state */ - ret = SSH_SERVER_KNOWN_CHANGED; - } - } else { - tokens_free(tokens); - } - } while (1); - - if ((ret == SSH_SERVER_NOT_KNOWN) && - (session->opts.StrictHostKeyChecking == 0)) { - ssh_write_knownhost(session); - ret = SSH_SERVER_KNOWN_OK; - } - - SAFE_FREE(host); - SAFE_FREE(hostport); - if (file != NULL) { - fclose(file); - } - - /* Return the current state at end of file */ - return ret; -} - -/** - * @brief Write the current server as known in the known hosts file. - * - * This will create the known hosts file if it does not exist. You generaly use - * it when ssh_is_server_known() answered SSH_SERVER_NOT_KNOWN. - * - * @param[in] session The ssh session to use. - * - * @return SSH_OK on success, SSH_ERROR on error. - */ -int ssh_write_knownhost(ssh_session session) { - ssh_key key; - ssh_string pubkey_s; - char *b64_key; - char buffer[4096] = {0}; - FILE *file; - char *dir; - char *host; - char *hostport; - int rc; - - if (session->opts.host == NULL) { - ssh_set_error(session, SSH_FATAL, - "Can't write host in known hosts if the hostname isn't known"); - return SSH_ERROR; - } - - host = ssh_lowercase(session->opts.host); - /* If using a nonstandard port, save the host in the [host]:port format */ - if(session->opts.port != 22) { - hostport = ssh_hostport(host, session->opts.port); - SAFE_FREE(host); - if (hostport == NULL) { - return SSH_ERROR; - } - host = hostport; - hostport = NULL; - } - - if (session->opts.knownhosts == NULL) { - if (ssh_options_apply(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Can't find a known_hosts file"); - SAFE_FREE(host); - return SSH_ERROR; - } - } - - if (session->current_crypto==NULL) { - ssh_set_error(session, SSH_FATAL, "No current crypto context"); - SAFE_FREE(host); - return SSH_ERROR; - } - - pubkey_s = session->current_crypto->server_pubkey; - if (pubkey_s == NULL){ - ssh_set_error(session, SSH_FATAL, "No public key present"); - SAFE_FREE(host); - return SSH_ERROR; - } - - /* Check if ~/.ssh exists and create it if not */ - dir = ssh_dirname(session->opts.knownhosts); - if (dir == NULL) { - ssh_set_error(session, SSH_FATAL, "%s", strerror(errno)); - SAFE_FREE(host); - return SSH_ERROR; - } - - if (!ssh_file_readaccess_ok(dir)) { - if (ssh_mkdir(dir, 0700) < 0) { - ssh_set_error(session, SSH_FATAL, - "Cannot create %s directory.", dir); - SAFE_FREE(dir); - SAFE_FREE(host); - return SSH_ERROR; - } - } - SAFE_FREE(dir); - - file = fopen(session->opts.knownhosts, "a"); - if (file == NULL) { - ssh_set_error(session, SSH_FATAL, - "Couldn't open known_hosts file %s for appending: %s", - session->opts.knownhosts, strerror(errno)); - SAFE_FREE(host); - return SSH_ERROR; - } - - rc = ssh_pki_import_pubkey_blob(pubkey_s, &key); - if (rc < 0) { - fclose(file); - SAFE_FREE(host); - return -1; - } - - if (strcmp(session->current_crypto->server_pubkey_type, "ssh-rsa1") == 0) { - /* openssh uses a different format for ssh-rsa1 keys. - Be compatible --kv */ - rc = ssh_pki_export_pubkey_rsa1(key, host, buffer, sizeof(buffer)); - ssh_key_free(key); - SAFE_FREE(host); - if (rc < 0) { - fclose(file); - return -1; - } - } else { - rc = ssh_pki_export_pubkey_base64(key, &b64_key); - if (rc < 0) { - ssh_key_free(key); - fclose(file); - SAFE_FREE(host); - return -1; - } - - snprintf(buffer, sizeof(buffer), - "%s %s %s\n", - host, - key->type_c, - b64_key); - - ssh_key_free(key); - SAFE_FREE(host); - SAFE_FREE(b64_key); - } - - if (fwrite(buffer, strlen(buffer), 1, file) != 1 || ferror(file)) { - fclose(file); - return -1; - } - - fclose(file); - return 0; -} - -#define KNOWNHOSTS_MAXTYPES 10 - -/** - * @internal - * @brief Check which kind of host keys should be preferred for connection - * by reading the known_hosts file. - * - * @param[in] session The SSH session to use. - * - * @returns array of supported key types - * NULL on error - */ -char **ssh_knownhosts_algorithms(ssh_session session) { - FILE *file = NULL; - char **tokens; - char *host; - char *hostport; - const char *type; - int match; - char **array; - int i=0, j; - - if (session->opts.knownhosts == NULL) { - if (ssh_options_apply(session) < 0) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "Can't find a known_hosts file"); - return NULL; - } - } - - if (session->opts.host == NULL) { - return NULL; - } - - host = ssh_lowercase(session->opts.host); - hostport = ssh_hostport(host, session->opts.port); - array = malloc(sizeof(char *) * KNOWNHOSTS_MAXTYPES); - - if (host == NULL || hostport == NULL || array == NULL) { - ssh_set_error_oom(session); - SAFE_FREE(host); - SAFE_FREE(hostport); - SAFE_FREE(array); - return NULL; - } - - do { - tokens = ssh_get_knownhost_line(&file, - session->opts.knownhosts, &type); - - /* End of file, return the current state */ - if (tokens == NULL) { - break; - } - match = match_hashed_host(host, tokens[0]); - if (match == 0){ - match = match_hostname(hostport, tokens[0], strlen(tokens[0])); - } - if (match == 0) { - match = match_hostname(host, tokens[0], strlen(tokens[0])); - } - if (match == 0) { - match = match_hashed_host(hostport, tokens[0]); - } - if (match) { - /* We got a match. Now check the key type */ - SSH_LOG(SSH_LOG_DEBUG, "server %s:%d has %s in known_hosts", - host, session->opts.port, type); - /* don't copy more than once */ - for(j=0;j= KNOWNHOSTS_MAXTYPES-1){ - tokens_free(tokens); - break; - } - } - } - tokens_free(tokens); - } while (1); - - array[i]=NULL; - SAFE_FREE(host); - SAFE_FREE(hostport); - if (file != NULL) { - fclose(file); - } - - /* Return the current state at end of file */ - return array; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/legacy.c b/libssh/src/legacy.c deleted file mode 100644 index 15f287a6..00000000 --- a/libssh/src/legacy.c +++ /dev/null @@ -1,728 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/** functions in that file are wrappers to the newly named functions. All - * of them are depreciated, but these wrapper will avoid breaking backward - * compatibility - */ - -#include "config.h" - -#include -#include - -#include -#include -#include -#include -#include -#include "libssh/pki_priv.h" -#include -#include -#include "libssh/options.h" - -/* AUTH FUNCTIONS */ -int ssh_auth_list(ssh_session session) { - return ssh_userauth_list(session, NULL); -} - -int ssh_userauth_offer_pubkey(ssh_session session, const char *username, - int type, ssh_string publickey) -{ - ssh_key key; - int rc; - - (void) type; /* unused */ - - rc = ssh_pki_import_pubkey_blob(publickey, &key); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "Failed to convert public key"); - return SSH_AUTH_ERROR; - } - - rc = ssh_userauth_try_publickey(session, username, key); - ssh_key_free(key); - - return rc; -} - -int ssh_userauth_pubkey(ssh_session session, - const char *username, - ssh_string publickey, - ssh_private_key privatekey) -{ - ssh_key key; - int rc; - - (void) publickey; /* unused */ - - key = ssh_key_new(); - if (key == NULL) { - return SSH_AUTH_ERROR; - } - - key->type = privatekey->type; - key->type_c = ssh_key_type_to_char(key->type); - key->flags = SSH_KEY_FLAG_PRIVATE|SSH_KEY_FLAG_PUBLIC; - key->dsa = privatekey->dsa_priv; - key->rsa = privatekey->rsa_priv; - - rc = ssh_userauth_publickey(session, username, key); - key->dsa = NULL; - key->rsa = NULL; - ssh_key_free(key); - - return rc; -} - -int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) { - return ssh_userauth_publickey_auto(session, NULL, passphrase); -} - -int ssh_userauth_privatekey_file(ssh_session session, - const char *username, - const char *filename, - const char *passphrase) { - char *pubkeyfile = NULL; - ssh_string pubkey = NULL; - ssh_private_key privkey = NULL; - int type = 0; - int rc = SSH_AUTH_ERROR; - size_t klen = strlen(filename) + 4 + 1; - - pubkeyfile = malloc(klen); - if (pubkeyfile == NULL) { - ssh_set_error_oom(session); - - return SSH_AUTH_ERROR; - } - snprintf(pubkeyfile, klen, "%s.pub", filename); - - pubkey = publickey_from_file(session, pubkeyfile, &type); - if (pubkey == NULL) { - SSH_LOG(SSH_LOG_RARE, "Public key file %s not found. Trying to generate it.", pubkeyfile); - /* auto-detect the key type with type=0 */ - privkey = privatekey_from_file(session, filename, 0, passphrase); - } else { - SSH_LOG(SSH_LOG_RARE, "Public key file %s loaded.", pubkeyfile); - privkey = privatekey_from_file(session, filename, type, passphrase); - } - if (privkey == NULL) { - goto error; - } - /* ssh_userauth_pubkey is responsible for taking care of null-pubkey */ - rc = ssh_userauth_pubkey(session, username, pubkey, privkey); - privatekey_free(privkey); - -error: - SAFE_FREE(pubkeyfile); - ssh_string_free(pubkey); - - return rc; -} - -/* BUFFER FUNCTIONS */ - -void buffer_free(ssh_buffer buffer){ - ssh_buffer_free(buffer); -} -void *buffer_get(ssh_buffer buffer){ - return ssh_buffer_get_begin(buffer); -} -uint32_t buffer_get_len(ssh_buffer buffer){ - return ssh_buffer_get_len(buffer); -} -ssh_buffer buffer_new(void){ - return ssh_buffer_new(); -} - -ssh_channel channel_accept_x11(ssh_channel channel, int timeout_ms){ - return ssh_channel_accept_x11(channel, timeout_ms); -} - -int channel_change_pty_size(ssh_channel channel,int cols,int rows){ - return ssh_channel_change_pty_size(channel,cols,rows); -} - -ssh_channel channel_forward_accept(ssh_session session, int timeout_ms){ - return ssh_channel_accept_forward(session, timeout_ms, NULL); -} - -int channel_close(ssh_channel channel){ - return ssh_channel_close(channel); -} - -int channel_forward_cancel(ssh_session session, const char *address, int port){ - return ssh_channel_cancel_forward(session, address, port); -} - -int channel_forward_listen(ssh_session session, const char *address, - int port, int *bound_port){ - return ssh_channel_listen_forward(session, address, port, bound_port); -} - -void channel_free(ssh_channel channel){ - ssh_channel_free(channel); -} - -int channel_get_exit_status(ssh_channel channel){ - return ssh_channel_get_exit_status(channel); -} - -ssh_session channel_get_session(ssh_channel channel){ - return ssh_channel_get_session(channel); -} - -int channel_is_closed(ssh_channel channel){ - return ssh_channel_is_closed(channel); -} - -int channel_is_eof(ssh_channel channel){ - return ssh_channel_is_eof(channel); -} - -int channel_is_open(ssh_channel channel){ - return ssh_channel_is_open(channel); -} - -ssh_channel channel_new(ssh_session session){ - return ssh_channel_new(session); -} - -int channel_open_forward(ssh_channel channel, const char *remotehost, - int remoteport, const char *sourcehost, int localport){ - return ssh_channel_open_forward(channel, remotehost, remoteport, - sourcehost,localport); -} - -int channel_open_session(ssh_channel channel){ - return ssh_channel_open_session(channel); -} - -int channel_poll(ssh_channel channel, int is_stderr){ - return ssh_channel_poll(channel, is_stderr); -} - -int channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr){ - return ssh_channel_read(channel, dest, count, is_stderr); -} - -/* - * This function will completely be depreciated. The old implementation was not - * renamed. - * int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count, - * int is_stderr); - */ - -int channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, - int is_stderr){ - return ssh_channel_read_nonblocking(channel, dest, count, is_stderr); -} - -int channel_request_env(ssh_channel channel, const char *name, const char *value){ - return ssh_channel_request_env(channel, name, value); -} - -int channel_request_exec(ssh_channel channel, const char *cmd){ - return ssh_channel_request_exec(channel, cmd); -} - -int channel_request_pty(ssh_channel channel){ - return ssh_channel_request_pty(channel); -} - -int channel_request_pty_size(ssh_channel channel, const char *term, - int cols, int rows){ - return ssh_channel_request_pty_size(channel, term, cols, rows); -} - -int channel_request_shell(ssh_channel channel){ - return ssh_channel_request_shell(channel); -} - -int channel_request_send_signal(ssh_channel channel, const char *signum){ - return ssh_channel_request_send_signal(channel, signum); -} - -int channel_request_sftp(ssh_channel channel){ - return ssh_channel_request_sftp(channel); -} - -int channel_request_subsystem(ssh_channel channel, const char *subsystem){ - return ssh_channel_request_subsystem(channel, subsystem); -} - -int channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, - const char *cookie, int screen_number){ - return ssh_channel_request_x11(channel, single_connection, protocol, cookie, - screen_number); -} - -int channel_send_eof(ssh_channel channel){ - return ssh_channel_send_eof(channel); -} - -int channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct - timeval * timeout){ - return ssh_channel_select(readchans, writechans, exceptchans, timeout); -} - -void channel_set_blocking(ssh_channel channel, int blocking){ - ssh_channel_set_blocking(channel, blocking); -} - -int channel_write(ssh_channel channel, const void *data, uint32_t len){ - return ssh_channel_write(channel, data, len); -} - -/* - * These functions have to be wrapped around the pki.c functions. - -void privatekey_free(ssh_private_key prv); -ssh_private_key privatekey_from_file(ssh_session session, const char *filename, - int type, const char *passphrase); -int ssh_publickey_to_file(ssh_session session, const char *file, - ssh_string pubkey, int type); -ssh_string publickey_to_string(ssh_public_key key); - * - */ - -void string_burn(ssh_string str){ - ssh_string_burn(str); -} - -ssh_string string_copy(ssh_string str){ - return ssh_string_copy(str); -} - -void *string_data(ssh_string str){ - return ssh_string_data(str); -} - -int string_fill(ssh_string str, const void *data, size_t len){ - return ssh_string_fill(str,data,len); -} - -void string_free(ssh_string str){ - ssh_string_free(str); -} - -ssh_string string_from_char(const char *what){ - return ssh_string_from_char(what); -} - -size_t string_len(ssh_string str){ - return ssh_string_len(str); -} - -ssh_string string_new(size_t size){ - return ssh_string_new(size); -} - -char *string_to_char(ssh_string str){ - return ssh_string_to_char(str); -} - -/* OLD PKI FUNCTIONS */ - -void publickey_free(ssh_public_key key) { - if (key == NULL) { - return; - } - - switch(key->type) { - case SSH_KEYTYPE_DSS: -#ifdef HAVE_LIBGCRYPT - gcry_sexp_release(key->dsa_pub); -#elif HAVE_LIBCRYPTO - DSA_free(key->dsa_pub); -#endif - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: -#ifdef HAVE_LIBGCRYPT - gcry_sexp_release(key->rsa_pub); -#elif defined HAVE_LIBCRYPTO - RSA_free(key->rsa_pub); -#endif - break; - default: - break; - } - SAFE_FREE(key); -} - -ssh_public_key publickey_from_privatekey(ssh_private_key prv) { - struct ssh_public_key_struct *p; - ssh_key privkey; - ssh_key pubkey; - int rc; - - privkey = ssh_key_new(); - if (privkey == NULL) { - return NULL; - } - - privkey->type = prv->type; - privkey->type_c = ssh_key_type_to_char(privkey->type); - privkey->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; - privkey->dsa = prv->dsa_priv; - privkey->rsa = prv->rsa_priv; - - rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); - privkey->dsa = NULL; - privkey->rsa = NULL; - ssh_key_free(privkey); - if (rc < 0) { - return NULL; - } - - p = ssh_pki_convert_key_to_publickey(pubkey); - ssh_key_free(pubkey); - - return p; -} - -ssh_private_key privatekey_from_file(ssh_session session, - const char *filename, - int type, - const char *passphrase) { - ssh_auth_callback auth_fn = NULL; - void *auth_data = NULL; - ssh_private_key privkey; - ssh_key key; - int rc; - - (void) type; /* unused */ - - if (session->common.callbacks) { - auth_fn = session->common.callbacks->auth_function; - auth_data = session->common.callbacks->userdata; - } - - - rc = ssh_pki_import_privkey_file(filename, - passphrase, - auth_fn, - auth_data, - &key); - if (rc == SSH_ERROR) { - return NULL; - } - - privkey = malloc(sizeof(struct ssh_private_key_struct)); - if (privkey == NULL) { - ssh_key_free(key); - return NULL; - } - - privkey->type = key->type; - privkey->dsa_priv = key->dsa; - privkey->rsa_priv = key->rsa; - - key->dsa = NULL; - key->rsa = NULL; - - ssh_key_free(key); - - return privkey; -} - -enum ssh_keytypes_e ssh_privatekey_type(ssh_private_key privatekey){ - if (privatekey==NULL) - return SSH_KEYTYPE_UNKNOWN; - return privatekey->type; -} - -void privatekey_free(ssh_private_key prv) { - if (prv == NULL) { - return; - } - -#ifdef HAVE_LIBGCRYPT - gcry_sexp_release(prv->dsa_priv); - gcry_sexp_release(prv->rsa_priv); -#elif defined HAVE_LIBCRYPTO - DSA_free(prv->dsa_priv); - RSA_free(prv->rsa_priv); -#endif - memset(prv, 0, sizeof(struct ssh_private_key_struct)); - SAFE_FREE(prv); -} - -ssh_string publickey_from_file(ssh_session session, const char *filename, - int *type) { - ssh_key key; - ssh_string key_str = NULL; - int rc; - - (void) session; /* unused */ - - rc = ssh_pki_import_pubkey_file(filename, &key); - if (rc < 0) { - return NULL; - } - - rc = ssh_pki_export_pubkey_blob(key, &key_str); - if (rc < 0) { - ssh_key_free(key); - return NULL; - } - - if (type) { - *type = key->type; - } - ssh_key_free(key); - - return key_str; -} - -const char *ssh_type_to_char(int type) { - return ssh_key_type_to_char(type); -} - -int ssh_type_from_name(const char *name) { - return ssh_key_type_from_name(name); -} - -ssh_public_key publickey_from_string(ssh_session session, ssh_string pubkey_s) { - struct ssh_public_key_struct *pubkey; - ssh_key key; - int rc; - - (void) session; /* unused */ - - rc = ssh_pki_import_pubkey_blob(pubkey_s, &key); - if (rc < 0) { - return NULL; - } - - pubkey = malloc(sizeof(struct ssh_public_key_struct)); - if (pubkey == NULL) { - ssh_key_free(key); - return NULL; - } - - pubkey->type = key->type; - pubkey->type_c = key->type_c; - - pubkey->dsa_pub = key->dsa; - key->dsa = NULL; - pubkey->rsa_pub = key->rsa; - key->rsa = NULL; - - ssh_key_free(key); - - return pubkey; -} - -ssh_string publickey_to_string(ssh_public_key pubkey) { - ssh_key key; - ssh_string key_blob; - int rc; - - key = ssh_key_new(); - if (key == NULL) { - return NULL; - } - - key->type = pubkey->type; - key->type_c = pubkey->type_c; - - key->dsa = pubkey->dsa_pub; - key->rsa = pubkey->rsa_pub; - - rc = ssh_pki_export_pubkey_blob(key, &key_blob); - if (rc < 0) { - key_blob = NULL; - } - - key->dsa = NULL; - key->rsa = NULL; - ssh_key_free(key); - - return key_blob; -} - -int ssh_publickey_to_file(ssh_session session, - const char *file, - ssh_string pubkey, - int type) -{ - FILE *fp; - char *user; - char buffer[1024]; - char host[256]; - unsigned char *pubkey_64; - size_t len; - int rc; - if(session==NULL) - return SSH_ERROR; - if(file==NULL || pubkey==NULL){ - ssh_set_error(session, SSH_FATAL, "Invalid parameters"); - return SSH_ERROR; - } - pubkey_64 = bin_to_base64(ssh_string_data(pubkey), ssh_string_len(pubkey)); - if (pubkey_64 == NULL) { - return SSH_ERROR; - } - - user = ssh_get_local_username(); - if (user == NULL) { - SAFE_FREE(pubkey_64); - return SSH_ERROR; - } - - rc = gethostname(host, sizeof(host)); - if (rc < 0) { - SAFE_FREE(user); - SAFE_FREE(pubkey_64); - return SSH_ERROR; - } - - snprintf(buffer, sizeof(buffer), "%s %s %s@%s\n", - ssh_type_to_char(type), - pubkey_64, - user, - host); - - SAFE_FREE(pubkey_64); - SAFE_FREE(user); - - SSH_LOG(SSH_LOG_RARE, "Trying to write public key file: %s", file); - SSH_LOG(SSH_LOG_PACKET, "public key file content: %s", buffer); - - fp = fopen(file, "w+"); - if (fp == NULL) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "Error opening %s: %s", file, strerror(errno)); - return SSH_ERROR; - } - - len = strlen(buffer); - if (fwrite(buffer, len, 1, fp) != 1 || ferror(fp)) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "Unable to write to %s", file); - fclose(fp); - unlink(file); - return SSH_ERROR; - } - - fclose(fp); - return SSH_OK; -} - -int ssh_try_publickey_from_file(ssh_session session, - const char *keyfile, - ssh_string *publickey, - int *type) { - char *pubkey_file; - size_t len; - ssh_string pubkey_string; - int pubkey_type; - - if (session == NULL || keyfile == NULL || publickey == NULL || type == NULL) { - return -1; - } - - if (session->opts.sshdir == NULL) { - if (ssh_options_apply(session) < 0) { - return -1; - } - } - - SSH_LOG(SSH_LOG_PACKET, "Trying to open privatekey %s", keyfile); - if (!ssh_file_readaccess_ok(keyfile)) { - SSH_LOG(SSH_LOG_PACKET, "Failed to open privatekey %s", keyfile); - return -1; - } - - len = strlen(keyfile) + 5; - pubkey_file = malloc(len); - if (pubkey_file == NULL) { - return -1; - } - snprintf(pubkey_file, len, "%s.pub", keyfile); - - SSH_LOG(SSH_LOG_PACKET, "Trying to open publickey %s", - pubkey_file); - if (!ssh_file_readaccess_ok(pubkey_file)) { - SSH_LOG(SSH_LOG_PACKET, "Failed to open publickey %s", - pubkey_file); - SAFE_FREE(pubkey_file); - return 1; - } - - SSH_LOG(SSH_LOG_PACKET, "Success opening public and private key"); - - /* - * We are sure both the private and public key file is readable. We return - * the public as a string, and the private filename as an argument - */ - pubkey_string = publickey_from_file(session, pubkey_file, &pubkey_type); - if (pubkey_string == NULL) { - SSH_LOG(SSH_LOG_PACKET, - "Wasn't able to open public key file %s: %s", - pubkey_file, - ssh_get_error(session)); - SAFE_FREE(pubkey_file); - return -1; - } - - SAFE_FREE(pubkey_file); - - *publickey = pubkey_string; - *type = pubkey_type; - - return 0; -} - -ssh_string ssh_get_pubkey(ssh_session session){ - if(session==NULL || session->current_crypto ==NULL || - session->current_crypto->server_pubkey==NULL) - return NULL; - else - return ssh_string_copy(session->current_crypto->server_pubkey); -} - -/**************************************************************************** - * SERVER SUPPORT - ****************************************************************************/ - -#ifdef WITH_SERVER -int ssh_accept(ssh_session session) { - return ssh_handle_key_exchange(session); -} - -int channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) { - return ssh_channel_write(channel, data, len); -} - -/** @deprecated - * @brief Interface previously exported by error. - */ -ssh_message ssh_message_retrieve(ssh_session session, uint32_t packettype){ - (void) packettype; - ssh_set_error(session, SSH_FATAL, "ssh_message_retrieve: obsolete libssh call"); - return NULL; -} - -#endif /* WITH_SERVER */ diff --git a/libssh/src/libcrypto.c b/libssh/src/libcrypto.c deleted file mode 100644 index 479c8c18..00000000 --- a/libssh/src/libcrypto.c +++ /dev/null @@ -1,706 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - - -#include -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/session.h" -#include "libssh/crypto.h" -#include "libssh/wrapper.h" -#include "libssh/libcrypto.h" - -#ifdef HAVE_LIBCRYPTO - -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_OPENSSL_AES_H -#define HAS_AES -#include -#endif -#ifdef HAVE_OPENSSL_BLOWFISH_H -#define HAS_BLOWFISH -#include -#endif -#ifdef HAVE_OPENSSL_DES_H -#define HAS_DES -#include -#endif - -#if (OPENSSL_VERSION_NUMBER<0x00907000L) -#define OLD_CRYPTO -#endif - -#include "libssh/crypto.h" - -struct ssh_mac_ctx_struct { - enum ssh_mac_e mac_type; - union { - SHACTX sha1_ctx; - SHA256CTX sha256_ctx; - SHA384CTX sha384_ctx; - SHA512CTX sha512_ctx; - } ctx; -}; - -static int alloc_key(struct ssh_cipher_struct *cipher) { - cipher->key = malloc(cipher->keylen); - if (cipher->key == NULL) { - return -1; - } - - return 0; -} - -void ssh_reseed(void){ - struct timeval tv; - gettimeofday(&tv, NULL); - RAND_add(&tv, sizeof(tv), 0.0); -} - -SHACTX sha1_init(void) { - SHACTX c = malloc(sizeof(*c)); - if (c == NULL) { - return NULL; - } - SHA1_Init(c); - - return c; -} - -void sha1_update(SHACTX c, const void *data, unsigned long len) { - SHA1_Update(c,data,len); -} - -void sha1_final(unsigned char *md, SHACTX c) { - SHA1_Final(md, c); - SAFE_FREE(c); -} - -void sha1(unsigned char *digest, int len, unsigned char *hash) { - SHA1(digest, len, hash); -} - -#ifdef HAVE_OPENSSL_ECC -static const EVP_MD *nid_to_evpmd(int nid) -{ - switch (nid) { - case NID_X9_62_prime256v1: - return EVP_sha256(); - case NID_secp384r1: - return EVP_sha384(); - case NID_secp521r1: - return EVP_sha512(); - default: - return NULL; - } - - return NULL; -} - -void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen) -{ - const EVP_MD *evp_md = nid_to_evpmd(nid); - EVP_MD_CTX md; - - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, digest, len); - EVP_DigestFinal(&md, hash, hlen); -} - -EVPCTX evp_init(int nid) -{ - const EVP_MD *evp_md = nid_to_evpmd(nid); - - EVPCTX ctx = malloc(sizeof(EVP_MD_CTX)); - if (ctx == NULL) { - return NULL; - } - - EVP_DigestInit(ctx, evp_md); - - return ctx; -} - -void evp_update(EVPCTX ctx, const void *data, unsigned long len) -{ - EVP_DigestUpdate(ctx, data, len); -} - -void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen) -{ - EVP_DigestFinal(ctx, md, mdlen); -} -#endif - -SHA256CTX sha256_init(void){ - SHA256CTX c = malloc(sizeof(*c)); - if (c == NULL) { - return NULL; - } - SHA256_Init(c); - - return c; -} - -void sha256_update(SHA256CTX c, const void *data, unsigned long len){ - SHA256_Update(c,data,len); -} - -void sha256_final(unsigned char *md, SHA256CTX c) { - SHA256_Final(md, c); - SAFE_FREE(c); -} - -void sha256(unsigned char *digest, int len, unsigned char *hash) { - SHA256(digest, len, hash); -} - -SHA384CTX sha384_init(void){ - SHA384CTX c = malloc(sizeof(*c)); - if (c == NULL) { - return NULL; - } - SHA384_Init(c); - - return c; -} - -void sha384_update(SHA384CTX c, const void *data, unsigned long len){ - SHA384_Update(c,data,len); -} - -void sha384_final(unsigned char *md, SHA384CTX c) { - SHA384_Final(md, c); - SAFE_FREE(c); -} - -void sha384(unsigned char *digest, int len, unsigned char *hash) { - SHA384(digest, len, hash); -} - -SHA512CTX sha512_init(void){ - SHA512CTX c = malloc(sizeof(*c)); - if (c == NULL) { - return NULL; - } - SHA512_Init(c); - - return c; -} - -void sha512_update(SHA512CTX c, const void *data, unsigned long len){ - SHA512_Update(c,data,len); -} - -void sha512_final(unsigned char *md, SHA512CTX c) { - SHA512_Final(md, c); - SAFE_FREE(c); -} - -void sha512(unsigned char *digest, int len, unsigned char *hash) { - SHA512(digest, len, hash); -} - -MD5CTX md5_init(void) { - MD5CTX c = malloc(sizeof(*c)); - if (c == NULL) { - return NULL; - } - - MD5_Init(c); - - return c; -} - -void md5_update(MD5CTX c, const void *data, unsigned long len) { - MD5_Update(c, data, len); -} - -void md5_final(unsigned char *md, MD5CTX c) { - MD5_Final(md,c); - SAFE_FREE(c); -} - -ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ - ssh_mac_ctx ctx = malloc(sizeof(struct ssh_mac_ctx_struct)); - if (ctx == NULL) { - return NULL; - } - - ctx->mac_type=type; - switch(type){ - case SSH_MAC_SHA1: - ctx->ctx.sha1_ctx = sha1_init(); - return ctx; - case SSH_MAC_SHA256: - ctx->ctx.sha256_ctx = sha256_init(); - return ctx; - case SSH_MAC_SHA384: - ctx->ctx.sha384_ctx = sha384_init(); - return ctx; - case SSH_MAC_SHA512: - ctx->ctx.sha512_ctx = sha512_init(); - return ctx; - default: - SAFE_FREE(ctx); - return NULL; - } -} - -void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { - switch(ctx->mac_type){ - case SSH_MAC_SHA1: - sha1_update(ctx->ctx.sha1_ctx, data, len); - break; - case SSH_MAC_SHA256: - sha256_update(ctx->ctx.sha256_ctx, data, len); - break; - case SSH_MAC_SHA384: - sha384_update(ctx->ctx.sha384_ctx, data, len); - break; - case SSH_MAC_SHA512: - sha512_update(ctx->ctx.sha512_ctx, data, len); - break; - default: - break; - } -} - -void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { - switch(ctx->mac_type){ - case SSH_MAC_SHA1: - sha1_final(md,ctx->ctx.sha1_ctx); - break; - case SSH_MAC_SHA256: - sha256_final(md,ctx->ctx.sha256_ctx); - break; - case SSH_MAC_SHA384: - sha384_final(md,ctx->ctx.sha384_ctx); - break; - case SSH_MAC_SHA512: - sha512_final(md,ctx->ctx.sha512_ctx); - break; - default: - break; - } - SAFE_FREE(ctx); -} - -HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { - HMACCTX ctx = NULL; - - ctx = malloc(sizeof(*ctx)); - if (ctx == NULL) { - return NULL; - } - -#ifndef OLD_CRYPTO - HMAC_CTX_init(ctx); // openssl 0.9.7 requires it. -#endif - - switch(type) { - case SSH_HMAC_SHA1: - HMAC_Init(ctx, key, len, EVP_sha1()); - break; - case SSH_HMAC_SHA256: - HMAC_Init(ctx, key, len, EVP_sha256()); - break; - case SSH_HMAC_SHA384: - HMAC_Init(ctx, key, len, EVP_sha384()); - break; - case SSH_HMAC_SHA512: - HMAC_Init(ctx, key, len, EVP_sha512()); - break; - case SSH_HMAC_MD5: - HMAC_Init(ctx, key, len, EVP_md5()); - break; - default: - SAFE_FREE(ctx); - ctx = NULL; - } - - return ctx; -} - -void hmac_update(HMACCTX ctx, const void *data, unsigned long len) { - HMAC_Update(ctx, data, len); -} - -void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len) { - HMAC_Final(ctx,hashmacbuf,len); - -#ifndef OLD_CRYPTO - HMAC_CTX_cleanup(ctx); -#else - HMAC_cleanup(ctx); -#endif - - SAFE_FREE(ctx); -} - -#ifdef HAS_BLOWFISH -/* the wrapper functions for blowfish */ -static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){ - if (cipher->key == NULL) { - if (alloc_key(cipher) < 0) { - return -1; - } - BF_set_key(cipher->key, 16, key); - } - cipher->IV = IV; - return 0; -} - -static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_ENCRYPT); -} - -static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_DECRYPT); -} -#endif /* HAS_BLOWFISH */ - -#ifdef HAS_AES -static int aes_set_encrypt_key(struct ssh_cipher_struct *cipher, void *key, - void *IV) { - if (cipher->key == NULL) { - if (alloc_key(cipher) < 0) { - return -1; - } - if (AES_set_encrypt_key(key,cipher->keysize,cipher->key) < 0) { - SAFE_FREE(cipher->key); - return -1; - } - } - cipher->IV=IV; - return 0; -} -static int aes_set_decrypt_key(struct ssh_cipher_struct *cipher, void *key, - void *IV) { - if (cipher->key == NULL) { - if (alloc_key(cipher) < 0) { - return -1; - } - if (AES_set_decrypt_key(key,cipher->keysize,cipher->key) < 0) { - SAFE_FREE(cipher->key); - return -1; - } - } - cipher->IV=IV; - return 0; -} - -static void aes_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len) { - AES_cbc_encrypt(in, out, len, cipher->key, cipher->IV, AES_ENCRYPT); -} - -static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len) { - AES_cbc_encrypt(in, out, len, cipher->key, cipher->IV, AES_DECRYPT); -} - -#ifndef BROKEN_AES_CTR -/* OpenSSL until 0.9.7c has a broken AES_ctr128_encrypt implementation which - * increments the counter from 2^64 instead of 1. It's better not to use it - */ - -/** @internal - * @brief encrypts/decrypts data with stream cipher AES_ctr128. 128 bits is actually - * the size of the CTR counter and incidentally the blocksize, but not the keysize. - * @param len[in] must be a multiple of AES128 block size. - */ -static void aes_ctr128_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len) { - unsigned char tmp_buffer[128/8]; - unsigned int num=0; - /* Some things are special with ctr128 : - * In this case, tmp_buffer is not being used, because it is used to store temporary data - * when an encryption is made on lengths that are not multiple of blocksize. - * Same for num, which is being used to store the current offset in blocksize in CTR - * function. - */ - AES_ctr128_encrypt(in, out, len, cipher->key, cipher->IV, tmp_buffer, &num); -} -#endif /* BROKEN_AES_CTR */ -#endif /* HAS_AES */ - -#ifdef HAS_DES -static int des3_set_key(struct ssh_cipher_struct *cipher, void *key,void *IV) { - if (cipher->key == NULL) { - if (alloc_key(cipher) < 0) { - return -1; - } - - DES_set_odd_parity(key); - DES_set_odd_parity((void*)((uint8_t*)key + 8)); - DES_set_odd_parity((void*)((uint8_t*)key + 16)); - DES_set_key_unchecked(key, cipher->key); - DES_set_key_unchecked((void*)((uint8_t*)key + 8), (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule))); - DES_set_key_unchecked((void*)((uint8_t*)key + 16), (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule))); - } - cipher->IV=IV; - return 0; -} - -static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - DES_ede3_cbc_encrypt(in, out, len, cipher->key, - (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), - (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), - cipher->IV, 1); -} - -static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - DES_ede3_cbc_encrypt(in, out, len, cipher->key, - (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), - (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), - cipher->IV, 0); -} - -static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Encrypt IV before", cipher->IV, 24); -#endif - DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1); - DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), - (void*)((uint8_t*)cipher->IV + 8), 0); - DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), - (void*)((uint8_t*)cipher->IV + 16), 1); -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Encrypt IV after", cipher->IV, 24); -#endif -} - -static void des3_1_decrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Decrypt IV before", cipher->IV, 24); -#endif - - DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), - cipher->IV, 0); - DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), - (void*)((uint8_t*)cipher->IV + 8), 1); - DES_ncbc_encrypt(in, out, len, cipher->key, (void*)((uint8_t*)cipher->IV + 16), 0); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Decrypt IV after", cipher->IV, 24); -#endif -} - -static int des1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){ - if(!cipher->key){ - if (alloc_key(cipher) < 0) { - return -1; - } - DES_set_odd_parity(key); - DES_set_key_unchecked(key,cipher->key); - } - cipher->IV=IV; - return 0; -} - -static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len){ - - DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1); -} - -static void des1_1_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len){ - - DES_ncbc_encrypt(in,out,len, cipher->key, cipher->IV, 0); -} - -#endif /* HAS_DES */ - -/* - * The table of supported ciphers - * - * WARNING: If you modify ssh_cipher_struct, you must make sure the order is - * correct! - */ -static struct ssh_cipher_struct ssh_ciphertab[] = { -#ifdef HAS_BLOWFISH - { - "blowfish-cbc", - 8, - sizeof (BF_KEY), - NULL, - NULL, - 128, - blowfish_set_key, - blowfish_set_key, - blowfish_encrypt, - blowfish_decrypt - }, -#endif /* HAS_BLOWFISH */ -#ifdef HAS_AES -#ifndef BROKEN_AES_CTR - { - "aes128-ctr", - 16, - sizeof(AES_KEY), - NULL, - NULL, - 128, - aes_set_encrypt_key, - aes_set_encrypt_key, - aes_ctr128_encrypt, - aes_ctr128_encrypt - }, - { - "aes192-ctr", - 16, - sizeof(AES_KEY), - NULL, - NULL, - 192, - aes_set_encrypt_key, - aes_set_encrypt_key, - aes_ctr128_encrypt, - aes_ctr128_encrypt - }, - { - "aes256-ctr", - 16, - sizeof(AES_KEY), - NULL, - NULL, - 256, - aes_set_encrypt_key, - aes_set_encrypt_key, - aes_ctr128_encrypt, - aes_ctr128_encrypt - }, -#endif /* BROKEN_AES_CTR */ - { - "aes128-cbc", - 16, - sizeof(AES_KEY), - NULL, - NULL, - 128, - aes_set_encrypt_key, - aes_set_decrypt_key, - aes_encrypt, - aes_decrypt - }, - { - "aes192-cbc", - 16, - sizeof(AES_KEY), - NULL, - NULL, - 192, - aes_set_encrypt_key, - aes_set_decrypt_key, - aes_encrypt, - aes_decrypt - }, - { - "aes256-cbc", - 16, - sizeof(AES_KEY), - NULL, - NULL, - 256, - aes_set_encrypt_key, - aes_set_decrypt_key, - aes_encrypt, - aes_decrypt - }, -#endif /* HAS_AES */ -#ifdef HAS_DES - { - "3des-cbc", - 8, - sizeof(DES_key_schedule) * 3, - NULL, - NULL, - 192, - des3_set_key, - des3_set_key, - des3_encrypt, - des3_decrypt - }, - { - "3des-cbc-ssh1", - 8, - sizeof(DES_key_schedule) * 3, - NULL, - NULL, - 192, - des3_set_key, - des3_set_key, - des3_1_encrypt, - des3_1_decrypt - }, - { - "des-cbc-ssh1", - 8, - sizeof(DES_key_schedule), - NULL, - NULL, - 64, - des1_set_key, - des1_set_key, - des1_1_encrypt, - des1_1_decrypt - }, -#endif /* HAS_DES */ - { - NULL, - 0, - 0, - NULL, - NULL, - 0, - NULL, - NULL, - NULL, - NULL - } -}; - - -struct ssh_cipher_struct *ssh_get_ciphertab(void) -{ - return ssh_ciphertab; -} - -#endif /* LIBCRYPTO */ - diff --git a/libssh/src/libgcrypt.c b/libssh/src/libgcrypt.c deleted file mode 100644 index 24d4a3c5..00000000 --- a/libssh/src/libgcrypt.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/session.h" -#include "libssh/crypto.h" -#include "libssh/wrapper.h" - -#ifdef HAVE_LIBGCRYPT -#include - -struct ssh_mac_ctx_struct { - enum ssh_mac_e mac_type; - gcry_md_hd_t ctx; -}; - -static int alloc_key(struct ssh_cipher_struct *cipher) { - cipher->key = malloc(cipher->keylen); - if (cipher->key == NULL) { - return -1; - } - - return 0; -} - -void ssh_reseed(void){ - } - -SHACTX sha1_init(void) { - SHACTX ctx = NULL; - gcry_md_open(&ctx, GCRY_MD_SHA1, 0); - - return ctx; -} - -void sha1_update(SHACTX c, const void *data, unsigned long len) { - gcry_md_write(c, data, len); -} - -void sha1_final(unsigned char *md, SHACTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), SHA_DIGEST_LEN); - gcry_md_close(c); -} - -void sha1(unsigned char *digest, int len, unsigned char *hash) { - gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len); -} - -SHA256CTX sha256_init(void) { - SHA256CTX ctx = NULL; - gcry_md_open(&ctx, GCRY_MD_SHA256, 0); - - return ctx; -} - -void sha256_update(SHACTX c, const void *data, unsigned long len) { - gcry_md_write(c, data, len); -} - -void sha256_final(unsigned char *md, SHACTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), SHA256_DIGEST_LEN); - gcry_md_close(c); -} - -void sha256(unsigned char *digest, int len, unsigned char *hash){ - gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len); -} - -SHA384CTX sha384_init(void) { - SHA384CTX ctx = NULL; - gcry_md_open(&ctx, GCRY_MD_SHA384, 0); - - return ctx; -} - -void sha384_update(SHACTX c, const void *data, unsigned long len) { - gcry_md_write(c, data, len); -} - -void sha384_final(unsigned char *md, SHACTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), SHA384_DIGEST_LEN); - gcry_md_close(c); -} - -void sha384(unsigned char *digest, int len, unsigned char *hash) { - gcry_md_hash_buffer(GCRY_MD_SHA384, hash, digest, len); -} - -SHA512CTX sha512_init(void) { - SHA512CTX ctx = NULL; - gcry_md_open(&ctx, GCRY_MD_SHA512, 0); - - return ctx; -} - -void sha512_update(SHACTX c, const void *data, unsigned long len) { - gcry_md_write(c, data, len); -} - -void sha512_final(unsigned char *md, SHACTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), SHA512_DIGEST_LEN); - gcry_md_close(c); -} - -void sha512(unsigned char *digest, int len, unsigned char *hash) { - gcry_md_hash_buffer(GCRY_MD_SHA512, hash, digest, len); -} - -MD5CTX md5_init(void) { - MD5CTX c = NULL; - gcry_md_open(&c, GCRY_MD_MD5, 0); - - return c; -} - -void md5_update(MD5CTX c, const void *data, unsigned long len) { - gcry_md_write(c,data,len); -} - -void md5_final(unsigned char *md, MD5CTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), MD5_DIGEST_LEN); - gcry_md_close(c); -} - -ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ - ssh_mac_ctx ctx = malloc(sizeof(struct ssh_mac_ctx_struct)); - if (ctx == NULL) { - return NULL; - } - - ctx->mac_type=type; - switch(type){ - case SSH_MAC_SHA1: - gcry_md_open(&ctx->ctx, GCRY_MD_SHA1, 0); - break; - case SSH_MAC_SHA256: - gcry_md_open(&ctx->ctx, GCRY_MD_SHA256, 0); - break; - case SSH_MAC_SHA384: - gcry_md_open(&ctx->ctx, GCRY_MD_SHA384, 0); - break; - case SSH_MAC_SHA512: - gcry_md_open(&ctx->ctx, GCRY_MD_SHA512, 0); - break; - default: - SAFE_FREE(ctx); - return NULL; - } - return ctx; -} - -void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { - gcry_md_write(ctx->ctx,data,len); -} - -void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { - size_t len; - switch(ctx->mac_type){ - case SSH_MAC_SHA1: - len=SHA_DIGEST_LEN; - break; - case SSH_MAC_SHA256: - len=SHA256_DIGEST_LEN; - break; - case SSH_MAC_SHA384: - len=SHA384_DIGEST_LEN; - break; - case SSH_MAC_SHA512: - len=SHA512_DIGEST_LEN; - break; - } - gcry_md_final(ctx->ctx); - memcpy(md, gcry_md_read(ctx->ctx, 0), len); - gcry_md_close(ctx->ctx); - SAFE_FREE(ctx); -} - -HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { - HMACCTX c = NULL; - - switch(type) { - case SSH_HMAC_SHA1: - gcry_md_open(&c, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); - break; - case SSH_HMAC_SHA256: - gcry_md_open(&c, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); - break; - case SSH_HMAC_SHA384: - gcry_md_open(&c, GCRY_MD_SHA384, GCRY_MD_FLAG_HMAC); - break; - case SSH_HMAC_SHA512: - gcry_md_open(&c, GCRY_MD_SHA512, GCRY_MD_FLAG_HMAC); - break; - case SSH_HMAC_MD5: - gcry_md_open(&c, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC); - break; - default: - c = NULL; - } - - gcry_md_setkey(c, key, len); - - return c; -} - -void hmac_update(HMACCTX c, const void *data, unsigned long len) { - gcry_md_write(c, data, len); -} - -void hmac_final(HMACCTX c, unsigned char *hashmacbuf, unsigned int *len) { - *len = gcry_md_get_algo_dlen(gcry_md_get_algo(c)); - memcpy(hashmacbuf, gcry_md_read(c, 0), *len); - gcry_md_close(c); -} - -/* the wrapper functions for blowfish */ -static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){ - if (cipher->key == NULL) { - if (alloc_key(cipher) < 0) { - return -1; - } - - if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_BLOWFISH, - GCRY_CIPHER_MODE_CBC, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setkey(cipher->key[0], key, 16)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - } - - return 0; -} - -static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - gcry_cipher_encrypt(cipher->key[0], out, len, in, len); -} - -static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - gcry_cipher_decrypt(cipher->key[0], out, len, in, len); -} - -static int aes_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) { - int mode=GCRY_CIPHER_MODE_CBC; - if (cipher->key == NULL) { - if (alloc_key(cipher) < 0) { - return -1; - } - if(strstr(cipher->name,"-ctr")) - mode=GCRY_CIPHER_MODE_CTR; - switch (cipher->keysize) { - case 128: - if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES128, - mode, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - break; - case 192: - if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES192, - mode, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - break; - case 256: - if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES256, - mode, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - break; - } - if (gcry_cipher_setkey(cipher->key[0], key, cipher->keysize / 8)) { - SAFE_FREE(cipher->key); - return -1; - } - if(mode == GCRY_CIPHER_MODE_CBC){ - if (gcry_cipher_setiv(cipher->key[0], IV, 16)) { - - SAFE_FREE(cipher->key); - return -1; - } - } else { - if(gcry_cipher_setctr(cipher->key[0],IV,16)){ - SAFE_FREE(cipher->key); - return -1; - } - } - } - - return 0; -} - -static void aes_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len) { - gcry_cipher_encrypt(cipher->key[0], out, len, in, len); -} - -static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out, - unsigned long len) { - gcry_cipher_decrypt(cipher->key[0], out, len, in, len); -} - -static int des1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){ - if(!cipher->key){ - if (alloc_key(cipher) < 0) { - return -1; - } - if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_CBC, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setkey(cipher->key[0], key, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - } - return 0; -} - -static int des3_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) { - if (cipher->key == NULL) { - if (alloc_key(cipher) < 0) { - return -1; - } - if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_3DES, - GCRY_CIPHER_MODE_CBC, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setkey(cipher->key[0], key, 24)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - } - - return 0; -} - - -static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - gcry_cipher_encrypt(cipher->key[0], out, len, in, len); -} - -static void des1_1_decrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - gcry_cipher_decrypt(cipher->key[0], out, len, in, len); -} - -static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - gcry_cipher_encrypt(cipher->key[0], out, len, in, len); -} - -static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - gcry_cipher_decrypt(cipher->key[0], out, len, in, len); -} - -static int des3_1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) { - if (cipher->key == NULL) { - if (alloc_key(cipher) < 0) { - return -1; - } - if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_CBC, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setkey(cipher->key[0], key, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - - if (gcry_cipher_open(&cipher->key[1], GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_CBC, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setkey(cipher->key[1], (unsigned char *)key + 8, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setiv(cipher->key[1], (unsigned char *)IV + 8, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - - if (gcry_cipher_open(&cipher->key[2], GCRY_CIPHER_DES, - GCRY_CIPHER_MODE_CBC, 0)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setkey(cipher->key[2], (unsigned char *)key + 16, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - if (gcry_cipher_setiv(cipher->key[2], (unsigned char *)IV + 16, 8)) { - SAFE_FREE(cipher->key); - return -1; - } - } - - return 0; -} - -static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - gcry_cipher_encrypt(cipher->key[0], out, len, in, len); - gcry_cipher_decrypt(cipher->key[1], in, len, out, len); - gcry_cipher_encrypt(cipher->key[2], out, len, in, len); -} - -static void des3_1_decrypt(struct ssh_cipher_struct *cipher, void *in, - void *out, unsigned long len) { - gcry_cipher_decrypt(cipher->key[2], out, len, in, len); - gcry_cipher_encrypt(cipher->key[1], in, len, out, len); - gcry_cipher_decrypt(cipher->key[0], out, len, in, len); -} - -/* the table of supported ciphers */ -static struct ssh_cipher_struct ssh_ciphertab[] = { - { - .name = "blowfish-cbc", - .blocksize = 8, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 128, - .set_encrypt_key = blowfish_set_key, - .set_decrypt_key = blowfish_set_key, - .encrypt = blowfish_encrypt, - .decrypt = blowfish_decrypt - }, - { - .name = "aes128-ctr", - .blocksize = 16, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 128, - .set_encrypt_key = aes_set_key, - .set_decrypt_key = aes_set_key, - .encrypt = aes_encrypt, - .decrypt = aes_encrypt - }, - { - .name = "aes192-ctr", - .blocksize = 16, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 192, - .set_encrypt_key = aes_set_key, - .set_decrypt_key = aes_set_key, - .encrypt = aes_encrypt, - .decrypt = aes_encrypt - }, - { - .name = "aes256-ctr", - .blocksize = 16, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 256, - .set_encrypt_key = aes_set_key, - .set_decrypt_key = aes_set_key, - .encrypt = aes_encrypt, - .decrypt = aes_encrypt - }, - { - .name = "aes128-cbc", - .blocksize = 16, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 128, - .set_encrypt_key = aes_set_key, - .set_decrypt_key = aes_set_key, - .encrypt = aes_encrypt, - .decrypt = aes_decrypt - }, - { - .name = "aes192-cbc", - .blocksize = 16, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 192, - .set_encrypt_key = aes_set_key, - .set_decrypt_key = aes_set_key, - .encrypt = aes_encrypt, - .decrypt = aes_decrypt - }, - { - .name = "aes256-cbc", - .blocksize = 16, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 256, - .set_encrypt_key = aes_set_key, - .set_decrypt_key = aes_set_key, - .encrypt = aes_encrypt, - .decrypt = aes_decrypt - }, - { - .name = "3des-cbc", - .blocksize = 8, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 192, - .set_encrypt_key = des3_set_key, - .set_decrypt_key = des3_set_key, - .encrypt = des3_encrypt, - .decrypt = des3_decrypt - }, - { - .name = "3des-cbc-ssh1", - .blocksize = 8, - .keylen = sizeof(gcry_cipher_hd_t) * 3, - .key = NULL, - .keysize = 192, - .set_encrypt_key = des3_1_set_key, - .set_decrypt_key = des3_1_set_key, - .encrypt = des3_1_encrypt, - .decrypt = des3_1_decrypt - }, - { - .name = "des-cbc-ssh1", - .blocksize = 8, - .keylen = sizeof(gcry_cipher_hd_t), - .key = NULL, - .keysize = 64, - .set_encrypt_key = des1_set_key, - .set_decrypt_key = des1_set_key, - .encrypt = des1_1_encrypt, - .decrypt = des1_1_decrypt - }, - { - .name = NULL, - .blocksize = 0, - .keylen = 0, - .key = NULL, - .keysize = 0, - .set_encrypt_key = NULL, - .set_decrypt_key = NULL, - .encrypt = NULL, - .decrypt = NULL - } -}; - -struct ssh_cipher_struct *ssh_get_ciphertab(void) -{ - return ssh_ciphertab; -} - -#endif diff --git a/libssh/src/log.c b/libssh/src/log.c deleted file mode 100644 index a72dd412..00000000 --- a/libssh/src/log.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * log.c - logging and debugging functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2008-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include -#ifndef _WIN32 -#include -#else -#include -#endif -#include - -#include "libssh/priv.h" -#include "libssh/misc.h" -#include "libssh/session.h" - -LIBSSH_THREAD int ssh_log_level; -LIBSSH_THREAD ssh_logging_callback ssh_log_cb; -LIBSSH_THREAD void *ssh_log_userdata; - -/** - * @defgroup libssh_log The SSH logging functions. - * @ingroup libssh - * - * Logging functions for debugging and problem resolving. - * - * @{ - */ - -static int current_timestring(int hires, char *buf, size_t len) -{ - char tbuf[64]; - struct timeval tv; - struct tm *tm; - time_t t; - - gettimeofday(&tv, NULL); - t = (time_t) tv.tv_sec; - - tm = localtime(&t); - if (tm == NULL) { - return -1; - } - - if (hires) { - strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm); - snprintf(buf, len, "%s.%06ld", tbuf, (long)tv.tv_usec); - } else { - strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm); - snprintf(buf, len, "%s", tbuf); - } - - return 0; -} - -static void ssh_log_stderr(int verbosity, - const char *function, - const char *buffer) -{ - char date[64] = {0}; - int rc; - - rc = current_timestring(1, date, sizeof(date)); - if (rc == 0) { - fprintf(stderr, "[%s, %d] %s:", date, verbosity, function); - } else { - fprintf(stderr, "[%d] %s", verbosity, function); - } - - fprintf(stderr, " %s\n", buffer); -} - -void ssh_log_function(int verbosity, - const char *function, - const char *buffer) -{ - ssh_logging_callback log_fn = ssh_get_log_callback(); - if (log_fn) { - char buf[1024]; - - snprintf(buf, sizeof(buf), "%s: %s", function, buffer); - - log_fn(verbosity, - function, - buf, - ssh_get_log_userdata()); - return; - } - - ssh_log_stderr(verbosity, function, buffer); -} - -void _ssh_log(int verbosity, - const char *function, - const char *format, ...) -{ - char buffer[1024]; - va_list va; - - if (verbosity <= ssh_get_log_level()) { - va_start(va, format); - vsnprintf(buffer, sizeof(buffer), format, va); - va_end(va); - ssh_log_function(verbosity, function, buffer); - } -} - -/* LEGACY */ - -void ssh_log(ssh_session session, - int verbosity, - const char *format, ...) -{ - char buffer[1024]; - va_list va; - - if (verbosity <= session->common.log_verbosity) { - va_start(va, format); - vsnprintf(buffer, sizeof(buffer), format, va); - va_end(va); - ssh_log_function(verbosity, "", buffer); - } -} - -/** @internal - * @brief log a SSH event with a common pointer - * @param common The SSH/bind session. - * @param verbosity The verbosity of the event. - * @param format The format string of the log entry. - */ -void ssh_log_common(struct ssh_common_struct *common, - int verbosity, - const char *function, - const char *format, ...) -{ - char buffer[1024]; - va_list va; - - if (verbosity <= common->log_verbosity) { - va_start(va, format); - vsnprintf(buffer, sizeof(buffer), format, va); - va_end(va); - ssh_log_function(verbosity, function, buffer); - } -} - - -/* PUBLIC */ - -/** - * @brief Set the log level of the library. - * - * @param[in] level The level to set. - * - * @return SSH_OK on success, SSH_ERROR on error. - */ -int ssh_set_log_level(int level) { - if (level < 0) { - return SSH_ERROR; - } - - ssh_log_level = level; - - return SSH_OK; -} - -/** - * @brief Get the log level of the library. - * - * @return The value of the log level. - */ -int ssh_get_log_level(void) { - return ssh_log_level; -} - -int ssh_set_log_callback(ssh_logging_callback cb) { - if (cb == NULL) { - return SSH_ERROR; - } - - ssh_log_cb = cb; - - return SSH_OK; -} - -ssh_logging_callback ssh_get_log_callback(void) { - return ssh_log_cb; -} - -/** - * @brief Get the userdata of the logging function. - * - * @return The userdata if set or NULL. - */ -void *ssh_get_log_userdata(void) -{ - if (ssh_log_userdata == NULL) { - return NULL; - } - - return ssh_log_userdata; -} - -/** - * @brief Set the userdata for the logging function. - * - * @param[in] data The userdata to set. - * - * @return SSH_OK on success. - */ -int ssh_set_log_userdata(void *data) -{ - ssh_log_userdata = data; - - return 0; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/match.c b/libssh/src/match.c deleted file mode 100644 index 53620bdd..00000000 --- a/libssh/src/match.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Author: Tatu Ylonen - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved - * Simple pattern matching, with '*' and '?' as wildcards. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -/* - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#include "libssh/priv.h" - -/* - * Returns true if the given string matches the pattern (which may contain ? - * and * as wildcards), and zero if it does not match. - */ -static int match_pattern(const char *s, const char *pattern) { - if (s == NULL || pattern == NULL) { - return 0; - } - - for (;;) { - /* If at end of pattern, accept if also at end of string. */ - if (*pattern == '\0') { - return (*s == '\0'); - } - - if (*pattern == '*') { - /* Skip the asterisk. */ - pattern++; - - /* If at end of pattern, accept immediately. */ - if (!*pattern) - return 1; - - /* If next character in pattern is known, optimize. */ - if (*pattern != '?' && *pattern != '*') { - /* - * Look instances of the next character in - * pattern, and try to match starting from - * those. - */ - for (; *s; s++) - if (*s == *pattern && match_pattern(s + 1, pattern + 1)) { - return 1; - } - /* Failed. */ - return 0; - } - /* - * Move ahead one character at a time and try to - * match at each position. - */ - for (; *s; s++) { - if (match_pattern(s, pattern)) { - return 1; - } - } - /* Failed. */ - return 0; - } - /* - * There must be at least one more character in the string. - * If we are at the end, fail. - */ - if (!*s) { - return 0; - } - - /* Check if the next character of the string is acceptable. */ - if (*pattern != '?' && *pattern != *s) { - return 0; - } - - /* Move to the next character, both in string and in pattern. */ - s++; - pattern++; - } - - /* NOTREACHED */ -} - -/* - * Tries to match the string against the comma-separated sequence of subpatterns - * (each possibly preceded by ! to indicate negation). - * Returns -1 if negation matches, 1 if there is a positive match, 0 if there is - * no match at all. - */ -static int match_pattern_list(const char *string, const char *pattern, - unsigned int len, int dolower) { - char sub[1024]; - int negated; - int got_positive; - unsigned int i, subi; - - got_positive = 0; - for (i = 0; i < len;) { - /* Check if the subpattern is negated. */ - if (pattern[i] == '!') { - negated = 1; - i++; - } else { - negated = 0; - } - - /* - * Extract the subpattern up to a comma or end. Convert the - * subpattern to lowercase. - */ - for (subi = 0; - i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; - subi++, i++) { - sub[subi] = dolower && isupper(pattern[i]) ? - (char)tolower(pattern[i]) : pattern[i]; - } - - /* If subpattern too long, return failure (no match). */ - if (subi >= sizeof(sub) - 1) { - return 0; - } - - /* If the subpattern was terminated by a comma, skip the comma. */ - if (i < len && pattern[i] == ',') { - i++; - } - - /* Null-terminate the subpattern. */ - sub[subi] = '\0'; - - /* Try to match the subpattern against the string. */ - if (match_pattern(string, sub)) { - if (negated) { - return -1; /* Negative */ - } else { - got_positive = 1; /* Positive */ - } - } - } - - /* - * Return success if got a positive match. If there was a negative - * match, we have already returned -1 and never get here. - */ - return got_positive; -} - -/* - * Tries to match the host name (which must be in all lowercase) against the - * comma-separated sequence of subpatterns (each possibly preceded by ! to - * indicate negation). - * Returns -1 if negation matches, 1 if there is a positive match, 0 if there - * is no match at all. - */ -int match_hostname(const char *host, const char *pattern, unsigned int len) { - return match_pattern_list(host, pattern, len, 1); -} - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/messages.c b/libssh/src/messages.c deleted file mode 100644 index 5a6963e8..00000000 --- a/libssh/src/messages.c +++ /dev/null @@ -1,1379 +0,0 @@ -/* - * messages.c - message parsing for client and server - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/libssh.h" -#include "libssh/priv.h" -#include "libssh/ssh2.h" -#include "libssh/buffer.h" -#include "libssh/packet.h" -#include "libssh/channels.h" -#include "libssh/session.h" -#include "libssh/misc.h" -#include "libssh/pki.h" -#include "libssh/dh.h" -#include "libssh/messages.h" -#ifdef WITH_SERVER -#include "libssh/server.h" -#include "libssh/gssapi.h" -#endif - -/** - * @defgroup libssh_messages The SSH message functions - * @ingroup libssh - * - * This file contains the message parsing utilities for client and server - * programs using libssh. - * - * On the server the the main loop of the program will call - * ssh_message_get(session) to get messages as they come. They are not 1-1 with - * the protocol messages. Then, the user will know what kind of a message it is - * and use the appropriate functions to handle it (or use the default handlers - * if you don't know what to do). - * - * @{ - */ - -static ssh_message ssh_message_new(ssh_session session){ - ssh_message msg = malloc(sizeof(struct ssh_message_struct)); - if (msg == NULL) { - return NULL; - } - ZERO_STRUCTP(msg); - msg->session = session; - return msg; -} - -#ifndef WITH_SERVER - -/* Reduced version of the reply default that only reply with - * SSH_MSG_UNIMPLEMENTED - */ -static int ssh_message_reply_default(ssh_message msg) { - SSH_LOG(SSH_LOG_FUNCTIONS, "Reporting unknown packet"); - - if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_UNIMPLEMENTED) < 0) - goto error; - if (buffer_add_u32(msg->session->out_buffer, - htonl(msg->session->recv_seq-1)) < 0) - goto error; - return packet_send(msg->session); - error: - return SSH_ERROR; -} - -#endif - -#ifdef WITH_SERVER - -static int ssh_execute_server_request(ssh_session session, ssh_message msg) -{ - ssh_channel channel = NULL; - int rc; - - switch(msg->type) { - case SSH_REQUEST_AUTH: - if (msg->auth_request.method == SSH_AUTH_METHOD_PASSWORD && - ssh_callbacks_exists(session->server_callbacks, auth_password_function)) { - rc = session->server_callbacks->auth_password_function(session, - msg->auth_request.username, msg->auth_request.password, - session->server_callbacks->userdata); - if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) { - ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL); - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } else if(msg->auth_request.method == SSH_AUTH_METHOD_PUBLICKEY && - ssh_callbacks_exists(session->server_callbacks, auth_pubkey_function)) { - rc = session->server_callbacks->auth_pubkey_function(session, - msg->auth_request.username, msg->auth_request.pubkey, - msg->auth_request.signature_state, - session->server_callbacks->userdata); - if (msg->auth_request.signature_state != SSH_PUBLICKEY_STATE_NONE) { - if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) { - ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL); - } else { - ssh_message_reply_default(msg); - } - } else { - if (rc == SSH_AUTH_SUCCESS) { - ssh_message_auth_reply_pk_ok_simple(msg); - } else { - ssh_message_reply_default(msg); - } - } - - return SSH_OK; - } else if (msg->auth_request.method == SSH_AUTH_METHOD_NONE && - ssh_callbacks_exists(session->server_callbacks, auth_none_function)) { - rc = session->server_callbacks->auth_none_function(session, - msg->auth_request.username, session->server_callbacks->userdata); - if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL){ - ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL); - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } - break; - case SSH_REQUEST_CHANNEL_OPEN: - if (msg->channel_request_open.type == SSH_CHANNEL_SESSION && - ssh_callbacks_exists(session->server_callbacks, channel_open_request_session_function)) { - channel = session->server_callbacks->channel_open_request_session_function(session, - session->server_callbacks->userdata); - if (channel != NULL) { - rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel); - return SSH_OK; - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } - break; - case SSH_REQUEST_CHANNEL: - channel = msg->channel_request.channel; - - if (msg->channel_request.type == SSH_CHANNEL_REQUEST_PTY && - ssh_callbacks_exists(channel->callbacks, channel_pty_request_function)) { - rc = channel->callbacks->channel_pty_request_function(session, channel, - msg->channel_request.TERM, - msg->channel_request.width, msg->channel_request.height, - msg->channel_request.pxwidth, msg->channel_request.pxheight, - channel->callbacks->userdata); - if (rc == 0) { - ssh_message_channel_request_reply_success(msg); - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SHELL && - ssh_callbacks_exists(channel->callbacks, channel_shell_request_function)) { - rc = channel->callbacks->channel_shell_request_function(session, - channel, - channel->callbacks->userdata); - if (rc == 0) { - ssh_message_channel_request_reply_success(msg); - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_X11 && - ssh_callbacks_exists(channel->callbacks, channel_x11_req_function)) { - channel->callbacks->channel_x11_req_function(session, - channel, - msg->channel_request.x11_single_connection, - msg->channel_request.x11_auth_protocol, - msg->channel_request.x11_auth_cookie, - msg->channel_request.x11_screen_number, - channel->callbacks->userdata); - ssh_message_channel_request_reply_success(msg); - - return SSH_OK; - } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_WINDOW_CHANGE && - ssh_callbacks_exists(channel->callbacks, channel_pty_window_change_function)) { - rc = channel->callbacks->channel_pty_window_change_function(session, - channel, - msg->channel_request.width, msg->channel_request.height, - msg->channel_request.pxwidth, msg->channel_request.pxheight, - channel->callbacks->userdata); - } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_EXEC && - ssh_callbacks_exists(channel->callbacks, channel_exec_request_function)) { - rc = channel->callbacks->channel_exec_request_function(session, - channel, - msg->channel_request.command, - channel->callbacks->userdata); - if (rc == 0) { - ssh_message_channel_request_reply_success(msg); - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_ENV && - ssh_callbacks_exists(channel->callbacks, channel_env_request_function)) { - rc = channel->callbacks->channel_env_request_function(session, - channel, - msg->channel_request.var_name, msg->channel_request.var_value, - channel->callbacks->userdata); - if (rc == 0) { - ssh_message_channel_request_reply_success(msg); - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SUBSYSTEM && - ssh_callbacks_exists(channel->callbacks, channel_subsystem_request_function)) { - rc = channel->callbacks->channel_subsystem_request_function(session, - channel, - msg->channel_request.subsystem, - channel->callbacks->userdata); - if (rc == 0) { - ssh_message_channel_request_reply_success(msg); - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } - break; - case SSH_REQUEST_SERVICE: - if (ssh_callbacks_exists(session->server_callbacks, service_request_function)) { - rc = session->server_callbacks->service_request_function(session, - msg->service_request.service, session->server_callbacks->userdata); - if (rc == 0) { - ssh_message_reply_default(msg); - } else { - ssh_disconnect(session); - } - - return SSH_OK; - } - - return SSH_AGAIN; - case SSH_REQUEST_GLOBAL: - break; - } - - return SSH_AGAIN; -} - -static int ssh_execute_client_request(ssh_session session, ssh_message msg) -{ - ssh_channel channel = NULL; - int rc = SSH_AGAIN; - - if (msg->type == SSH_REQUEST_CHANNEL_OPEN - && msg->channel_request_open.type == SSH_CHANNEL_X11 - && ssh_callbacks_exists(session->common.callbacks, channel_open_request_x11_function)) { - channel = session->common.callbacks->channel_open_request_x11_function (session, - msg->channel_request_open.originator, - msg->channel_request_open.originator_port, - session->common.callbacks->userdata); - if (channel != NULL) { - rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel); - - return rc; - } else { - ssh_message_reply_default(msg); - } - - return SSH_OK; - } - - return rc; -} - -/** @internal - * Executes the callbacks defined in session->server_callbacks, out of an ssh_message - * I don't like ssh_message interface but it works. - * @returns SSH_OK if the message has been handled, or SSH_AGAIN otherwise. - */ -static int ssh_execute_server_callbacks(ssh_session session, ssh_message msg){ - int rc = SSH_AGAIN; - - if (session->server_callbacks != NULL){ - rc = ssh_execute_server_request(session, msg); - } else if (session->common.callbacks != NULL) { - /* This one is in fact a client callback... */ - rc = ssh_execute_client_request(session, msg); - } - - return rc; -} - -#endif /* WITH_SERVER */ - -static int ssh_execute_message_callback(ssh_session session, ssh_message msg) { - int ret; - if(session->ssh_message_callback != NULL) { - ret = session->ssh_message_callback(session, msg, - session->ssh_message_callback_data); - if(ret == 1) { - ret = ssh_message_reply_default(msg); - ssh_message_free(msg); - if(ret != SSH_OK) { - return ret; - } - } else { - ssh_message_free(msg); - } - } else { - ret = ssh_message_reply_default(msg); - ssh_message_free(msg); - if(ret != SSH_OK) { - return ret; - } - } - return SSH_OK; -} - -/** - * @internal - * - * @brief Add a message to the current queue of messages to be parsed and/or call - * the various callback functions. - * - * @param[in] session The SSH session to add the message. - * - * @param[in] message The message to add to the queue. - */ -void ssh_message_queue(ssh_session session, ssh_message message){ - if (message != NULL) { -#ifdef WITH_SERVER - int ret; - /* probably not the best place to execute server callbacks, but still better - * than nothing. - */ - ret = ssh_execute_server_callbacks(session, message); - if (ret == SSH_OK){ - ssh_message_free(message); - return; - } -#endif /* WITH_SERVER */ - if(session->ssh_message_callback != NULL) { - ssh_execute_message_callback(session, message); - return; - } - if (session->server_callbacks != NULL){ - /* if we have server callbacks, but nothing was executed, it means we are - * in non-synchronous mode, and we just don't care about the message we - * received. Just send a default response. Do not queue it. - */ - ssh_message_reply_default(message); - ssh_message_free(message); - return; - } - if(session->ssh_message_list == NULL) { - session->ssh_message_list = ssh_list_new(); - } - if (session->ssh_message_list != NULL) { - ssh_list_append(session->ssh_message_list, message); - } - } -} - -/** - * @internal - * - * @brief Pop a message from the message list and dequeue it. - * - * @param[in] session The SSH session to pop the message. - * - * @returns The head message or NULL if it doesn't exist. - */ -ssh_message ssh_message_pop_head(ssh_session session){ - ssh_message msg=NULL; - struct ssh_iterator *i; - if(session->ssh_message_list == NULL) - return NULL; - i=ssh_list_get_iterator(session->ssh_message_list); - if(i != NULL){ - msg=ssh_iterator_value(ssh_message,i); - ssh_list_remove(session->ssh_message_list,i); - } - return msg; -} - -/* Returns 1 if there is a message available */ -static int ssh_message_termination(void *s){ - ssh_session session = s; - struct ssh_iterator *it; - if(session->session_state == SSH_SESSION_STATE_ERROR) - return 1; - it = ssh_list_get_iterator(session->ssh_message_list); - if(!it) - return 0; - else - return 1; -} -/** - * @brief Retrieve a SSH message from a SSH session. - * - * @param[in] session The SSH session to get the message. - * - * @returns The SSH message received, NULL in case of error, or timeout - * elapsed. - * - * @warning This function blocks until a message has been received. Betterset up - * a callback if this behavior is unwanted. - */ -ssh_message ssh_message_get(ssh_session session) { - ssh_message msg = NULL; - int rc; - - msg=ssh_message_pop_head(session); - if(msg) { - return msg; - } - if(session->ssh_message_list == NULL) { - session->ssh_message_list = ssh_list_new(); - } - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, - ssh_message_termination, session); - if(rc || session->session_state == SSH_SESSION_STATE_ERROR) - return NULL; - msg=ssh_list_pop_head(ssh_message, session->ssh_message_list); - - return msg; -} - -/** - * @brief Get the type of the message. - * - * @param[in] msg The message to get the type from. - * - * @return The message type or -1 on error. - */ -int ssh_message_type(ssh_message msg) { - if (msg == NULL) { - return -1; - } - - return msg->type; -} - -/** - * @brief Get the subtype of the message. - * - * @param[in] msg The message to get the subtype from. - * - * @return The message type or -1 on error. - */ -int ssh_message_subtype(ssh_message msg) { - if (msg == NULL) { - return -1; - } - - switch(msg->type) { - case SSH_REQUEST_AUTH: - return msg->auth_request.method; - case SSH_REQUEST_CHANNEL_OPEN: - return msg->channel_request_open.type; - case SSH_REQUEST_CHANNEL: - return msg->channel_request.type; - case SSH_REQUEST_GLOBAL: - return msg->global_request.type; - } - - return -1; -} - -/** - * @brief Free a SSH message. - * - * @param[in] msg The message to release the memory. - */ -void ssh_message_free(ssh_message msg){ - if (msg == NULL) { - return; - } - - switch(msg->type) { - case SSH_REQUEST_AUTH: - SAFE_FREE(msg->auth_request.username); - if (msg->auth_request.password) { - BURN_STRING(msg->auth_request.password); - SAFE_FREE(msg->auth_request.password); - } - ssh_key_free(msg->auth_request.pubkey); - break; - case SSH_REQUEST_CHANNEL_OPEN: - SAFE_FREE(msg->channel_request_open.originator); - SAFE_FREE(msg->channel_request_open.destination); - break; - case SSH_REQUEST_CHANNEL: - SAFE_FREE(msg->channel_request.TERM); - SAFE_FREE(msg->channel_request.modes); - SAFE_FREE(msg->channel_request.var_name); - SAFE_FREE(msg->channel_request.var_value); - SAFE_FREE(msg->channel_request.command); - SAFE_FREE(msg->channel_request.subsystem); - break; - case SSH_REQUEST_SERVICE: - SAFE_FREE(msg->service_request.service); - break; - case SSH_REQUEST_GLOBAL: - SAFE_FREE(msg->global_request.bind_address); - break; - } - ZERO_STRUCTP(msg); - SAFE_FREE(msg); -} - -#ifdef WITH_SERVER - -SSH_PACKET_CALLBACK(ssh_packet_service_request){ - ssh_string service = NULL; - char *service_c = NULL; - ssh_message msg=NULL; - - (void)type; - (void)user; - service = buffer_get_ssh_string(packet); - if (service == NULL) { - ssh_set_error(session, SSH_FATAL, "Invalid SSH_MSG_SERVICE_REQUEST packet"); - goto error; - } - - service_c = ssh_string_to_char(service); - if (service_c == NULL) { - goto error; - } - SSH_LOG(SSH_LOG_PACKET, - "Received a SERVICE_REQUEST for service %s", service_c); - msg=ssh_message_new(session); - if(!msg){ - SAFE_FREE(service_c); - goto error; - } - msg->type=SSH_REQUEST_SERVICE; - msg->service_request.service=service_c; -error: - ssh_string_free(service); - if(msg != NULL) - ssh_message_queue(session,msg); - - return SSH_PACKET_USED; -} - - -/* - * This function concats in a buffer the values needed to do a signature - * verification. - */ -static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session, - ssh_message msg, - const char *service) -{ - struct ssh_crypto_struct *crypto = - session->current_crypto ? session->current_crypto : - session->next_crypto; - ssh_buffer buffer; - ssh_string str=NULL; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - return NULL; - } - rc = ssh_pki_export_pubkey_blob(msg->auth_request.pubkey, &str); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - rc = ssh_buffer_pack(buffer, - "dPbsssbsS", - crypto->digest_len, /* session ID string */ - (size_t)crypto->digest_len, crypto->session_id, - SSH2_MSG_USERAUTH_REQUEST, /* type */ - msg->auth_request.username, - service, - "publickey", /* method */ - 1, /* has to be signed (true) */ - msg->auth_request.pubkey->type_c, /* pubkey algorithm */ - str); /* public key as a blob */ - - ssh_string_free(str); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - ssh_buffer_free(buffer); - return NULL; - } - - return buffer; -} - -/** - * @internal - * - * @brief Handle a SSH_MSG_MSG_USERAUTH_REQUEST packet and queue a - * SSH Message - */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_request){ - ssh_message msg = NULL; - char *service = NULL; - char *method = NULL; - int rc; - - (void)user; - (void)type; - - msg = ssh_message_new(session); - if (msg == NULL) { - ssh_set_error_oom(session); - goto error; - } - msg->type = SSH_REQUEST_AUTH; - rc = ssh_buffer_unpack(packet, - "sss", - &msg->auth_request.username, - &service, - &method); - - if (rc != SSH_OK) { - goto error; - } - - SSH_LOG(SSH_LOG_PACKET, - "Auth request for service %s, method %s for user '%s'", - service, method, - msg->auth_request.username); - - - if (strcmp(method, "none") == 0) { - msg->auth_request.method = SSH_AUTH_METHOD_NONE; - goto end; - } - - if (strcmp(method, "password") == 0) { - uint8_t tmp; - - msg->auth_request.method = SSH_AUTH_METHOD_PASSWORD; - rc = ssh_buffer_unpack(packet, "bs", &tmp, &msg->auth_request.password); - if (rc != SSH_OK) { - goto error; - } - goto end; - } - - if (strcmp(method, "keyboard-interactive") == 0) { - ssh_string lang = NULL; - ssh_string submethods = NULL; - - msg->auth_request.method = SSH_AUTH_METHOD_INTERACTIVE; - lang = buffer_get_ssh_string(packet); - if (lang == NULL) { - goto error; - } - /* from the RFC 4256 - * 3.1. Initial Exchange - * "The language tag is deprecated and SHOULD be the empty string." - */ - ssh_string_free(lang); - - submethods = buffer_get_ssh_string(packet); - if (submethods == NULL) { - goto error; - } - /* from the RFC 4256 - * 3.1. Initial Exchange - * "One possible implementation strategy of the submethods field on the - * server is that, unless the user may use multiple different - * submethods, the server ignores this field." - */ - ssh_string_free(submethods); - - goto end; - } - - if (strcmp(method, "publickey") == 0) { - ssh_string algo = NULL; - ssh_string pubkey_blob = NULL; - uint8_t has_sign; - - msg->auth_request.method = SSH_AUTH_METHOD_PUBLICKEY; - SAFE_FREE(method); - rc = ssh_buffer_unpack(packet, "bSS", - &has_sign, - &algo, - &pubkey_blob - ); - - if (rc != SSH_OK) { - goto error; - } - ssh_string_free(algo); - algo = NULL; - - rc = ssh_pki_import_pubkey_blob(pubkey_blob, &msg->auth_request.pubkey); - ssh_string_free(pubkey_blob); - pubkey_blob = NULL; - if (rc < 0) { - goto error; - } - msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_NONE; - // has a valid signature ? - if(has_sign) { - ssh_string sig_blob = NULL; - ssh_buffer digest = NULL; - - sig_blob = buffer_get_ssh_string(packet); - if(sig_blob == NULL) { - SSH_LOG(SSH_LOG_PACKET, "Invalid signature packet from peer"); - msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_ERROR; - goto error; - } - - digest = ssh_msg_userauth_build_digest(session, msg, service); - if (digest == NULL) { - ssh_string_free(sig_blob); - SSH_LOG(SSH_LOG_PACKET, "Failed to get digest"); - msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG; - goto error; - } - - rc = ssh_pki_signature_verify_blob(session, - sig_blob, - msg->auth_request.pubkey, - buffer_get_rest(digest), - buffer_get_rest_len(digest)); - ssh_string_free(sig_blob); - ssh_buffer_free(digest); - if (rc < 0) { - SSH_LOG( - SSH_LOG_PACKET, - "Received an invalid signature from peer"); - msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_WRONG; - goto error; - } - - SSH_LOG(SSH_LOG_PACKET, "Valid signature received"); - - msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_VALID; - } - goto end; - } -#ifdef WITH_GSSAPI - if (strcmp(method, "gssapi-with-mic") == 0) { - uint32_t n_oid; - ssh_string *oids; - ssh_string oid; - char *hexa; - int i; - buffer_get_u32(packet, &n_oid); - n_oid=ntohl(n_oid); - if(n_oid > 100){ - ssh_set_error(session, SSH_FATAL, "USERAUTH_REQUEST: gssapi-with-mic OID count too big (%d)",n_oid); - goto error; - } - SSH_LOG(SSH_LOG_PACKET, "gssapi: %d OIDs", n_oid); - oids = calloc(n_oid, sizeof(ssh_string)); - if (oids == NULL){ - ssh_set_error_oom(session); - goto error; - } - for (i=0;i<(int) n_oid;++i){ - oid=buffer_get_ssh_string(packet); - if(oid == NULL){ - for(i=i-1;i>=0;--i){ - SAFE_FREE(oids[i]); - } - SAFE_FREE(oids); - ssh_set_error(session, SSH_LOG_PACKET, "USERAUTH_REQUEST: gssapi-with-mic missing OID"); - goto error; - } - oids[i] = oid; - if(session->common.log_verbosity >= SSH_LOG_PACKET){ - hexa = ssh_get_hexa(ssh_string_data(oid), ssh_string_len(oid)); - SSH_LOG(SSH_LOG_PACKET,"gssapi: OID %d: %s",i, hexa); - SAFE_FREE(hexa); - } - } - ssh_gssapi_handle_userauth(session, msg->auth_request.username, n_oid, oids); - - for(i=0;i<(int)n_oid;++i){ - SAFE_FREE(oids[i]); - } - SAFE_FREE(oids); - /* bypass the message queue thing */ - SAFE_FREE(service); - SAFE_FREE(method); - ssh_message_free(msg); - - return SSH_PACKET_USED; - } -#endif - - msg->auth_request.method = SSH_AUTH_METHOD_UNKNOWN; - SAFE_FREE(method); - goto end; -error: - SAFE_FREE(service); - SAFE_FREE(method); - - ssh_message_free(msg); - - return SSH_PACKET_USED; -end: - SAFE_FREE(service); - SAFE_FREE(method); - - ssh_message_queue(session,msg); - - return SSH_PACKET_USED; -} - -#endif /* WITH_SERVER */ -/** - * @internal - * - * @brief Handle a SSH_MSG_MSG_USERAUTH_INFO_RESPONSE packet and queue a - * SSH Message - */ -#ifndef WITH_SERVER -SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ - (void)session; - (void)type; - (void)packet; - (void)user; - return SSH_PACKET_USED; -} -#else /* WITH_SERVER */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){ - uint32_t nanswers; - uint32_t i; - ssh_string tmp; - int rc; - - ssh_message msg = NULL; - - /* GSSAPI_TOKEN has same packed number. XXX fix this */ -#ifdef WITH_GSSAPI - if (session->gssapi != NULL) { - return ssh_packet_userauth_gssapi_token(session, type, packet, user); - } -#endif - (void)user; - (void)type; - - msg = ssh_message_new(session); - if (msg == NULL) { - ssh_set_error_oom(session); - goto error; - } - - /* HACK: we forge a message to be able to handle it in the - * same switch() as other auth methods */ - msg->type = SSH_REQUEST_AUTH; - msg->auth_request.method = SSH_AUTH_METHOD_INTERACTIVE; - msg->auth_request.kbdint_response = 1; -#if 0 // should we wipe the username ? - msg->auth_request.username = NULL; -#endif - - rc = ssh_buffer_unpack(packet, "d", &nanswers); - if (rc != SSH_OK) { - ssh_set_error_invalid(session); - goto error; - } - - if (session->kbdint == NULL) { - SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a keyboard-interactive " - "response but it seems we didn't send the request."); - - session->kbdint = ssh_kbdint_new(); - if (session->kbdint == NULL) { - ssh_set_error_oom(session); - - goto error; - } - } - - SSH_LOG(SSH_LOG_PACKET,"kbdint: %d answers",nanswers); - if (nanswers > KBDINT_MAX_PROMPT) { - ssh_set_error(session, SSH_FATAL, - "Too much answers received from client: %u (0x%.4x)", - nanswers, nanswers); - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - goto error; - } - - if(nanswers != session->kbdint->nprompts) { - /* warn but let the application handle this case */ - SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Number of prompts and answers" - " mismatch: p=%u a=%u", session->kbdint->nprompts, nanswers); - } - session->kbdint->nanswers = nanswers; - session->kbdint->answers = malloc(nanswers * sizeof(char *)); - if (session->kbdint->answers == NULL) { - session->kbdint->nanswers = 0; - ssh_set_error_oom(session); - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - goto error; - } - memset(session->kbdint->answers, 0, nanswers * sizeof(char *)); - - for (i = 0; i < nanswers; i++) { - tmp = buffer_get_ssh_string(packet); - if (tmp == NULL) { - ssh_set_error(session, SSH_FATAL, "Short INFO_RESPONSE packet"); - session->kbdint->nanswers = i; - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - goto error; - } - session->kbdint->answers[i] = ssh_string_to_char(tmp); - ssh_string_free(tmp); - if (session->kbdint->answers[i] == NULL) { - ssh_set_error_oom(session); - session->kbdint->nanswers = i; - ssh_kbdint_free(session->kbdint); - session->kbdint = NULL; - - goto error; - } - } - - ssh_message_queue(session,msg); - - return SSH_PACKET_USED; - -error: - ssh_message_free(msg); - - return SSH_PACKET_USED; -} -#endif /* WITH_SERVER */ - -SSH_PACKET_CALLBACK(ssh_packet_channel_open){ - ssh_message msg = NULL; - char *type_c = NULL; - uint32_t originator_port, destination_port; - int rc; - - (void)type; - (void)user; - msg = ssh_message_new(session); - if (msg == NULL) { - ssh_set_error_oom(session); - goto error; - } - - msg->type = SSH_REQUEST_CHANNEL_OPEN; - rc = ssh_buffer_unpack(packet, "s", &type_c); - if (rc != SSH_OK){ - goto error; - } - - SSH_LOG(SSH_LOG_PACKET, - "Clients wants to open a %s channel", type_c); - - ssh_buffer_unpack(packet,"ddd", - &msg->channel_request_open.sender, - &msg->channel_request_open.window, - &msg->channel_request_open.packet_size); - - if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED){ - ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)"); - goto error; - } - - if (strcmp(type_c,"session") == 0) { - msg->channel_request_open.type = SSH_CHANNEL_SESSION; - SAFE_FREE(type_c); - goto end; - } - - if (strcmp(type_c,"direct-tcpip") == 0) { - rc = ssh_buffer_unpack(packet, - "sdsd", - &msg->channel_request_open.destination, - &destination_port, - &msg->channel_request_open.originator, - &originator_port); - if (rc != SSH_OK) { - goto error; - } - - msg->channel_request_open.destination_port = (uint16_t) destination_port; - msg->channel_request_open.originator_port = (uint16_t) originator_port; - msg->channel_request_open.type = SSH_CHANNEL_DIRECT_TCPIP; - goto end; - } - - if (strcmp(type_c,"forwarded-tcpip") == 0) { - rc = ssh_buffer_unpack(packet, "sdsd", - &msg->channel_request_open.destination, - &destination_port, - &msg->channel_request_open.originator, - &originator_port - ); - if (rc != SSH_OK){ - goto error; - } - msg->channel_request_open.destination_port = (uint16_t) destination_port; - msg->channel_request_open.originator_port = (uint16_t) originator_port; - msg->channel_request_open.type = SSH_CHANNEL_FORWARDED_TCPIP; - goto end; - } - - if (strcmp(type_c,"x11") == 0) { - rc = ssh_buffer_unpack(packet, "sd", - &msg->channel_request_open.originator, - &originator_port); - if (rc != SSH_OK){ - goto error; - } - msg->channel_request_open.originator_port = (uint16_t) originator_port; - msg->channel_request_open.type = SSH_CHANNEL_X11; - goto end; - } - - msg->channel_request_open.type = SSH_CHANNEL_UNKNOWN; - goto end; - -error: - ssh_message_free(msg); - msg=NULL; -end: - SAFE_FREE(type_c); - if(msg != NULL) - ssh_message_queue(session,msg); - - return SSH_PACKET_USED; -} - -int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_channel chan) { - ssh_session session; - int rc; - - if (msg == NULL) { - return SSH_ERROR; - } - - session = msg->session; - - chan->local_channel = ssh_channel_new_id(session); - chan->local_maxpacket = 35000; - chan->local_window = 32000; - chan->remote_channel = msg->channel_request_open.sender; - chan->remote_maxpacket = msg->channel_request_open.packet_size; - chan->remote_window = msg->channel_request_open.window; - chan->state = SSH_CHANNEL_STATE_OPEN; - - rc = ssh_buffer_pack(session->out_buffer, - "bdddd", - SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, - chan->remote_channel, - chan->local_channel, - chan->local_window, - chan->local_maxpacket); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_PACKET, - "Accepting a channel request_open for chan %d", - chan->remote_channel); - - rc = packet_send(session); - - return rc; -} - - -ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg) { - ssh_channel chan; - int rc; - - if (msg == NULL) { - return NULL; - } - - chan = ssh_channel_new(msg->session); - if (chan == NULL) { - return NULL; - } - rc = ssh_message_channel_request_open_reply_accept_channel(msg, chan); - if (rc < 0) { - ssh_channel_free(chan); - chan = NULL; - } - return chan; - -} - -/** - * @internal - * - * @brief This function parses the last end of a channel request packet. - * - * This is normally converted to a SSH message and placed in the queue. - * - * @param[in] session The SSH session. - * - * @param[in] channel The channel the request is made on. - * - * @param[in] packet The rest of the packet to be parsed. - * - * @param[in] request The type of request. - * - * @param[in] want_reply The want_reply field from the request. - * - * @returns SSH_OK on success, SSH_ERROR if an error occured. - */ -int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet, - const char *request, uint8_t want_reply) { - ssh_message msg = NULL; - int rc; - - msg = ssh_message_new(session); - if (msg == NULL) { - ssh_set_error_oom(session); - goto error; - } - - SSH_LOG(SSH_LOG_PACKET, - "Received a %s channel_request for channel (%d:%d) (want_reply=%hhd)", - request, channel->local_channel, channel->remote_channel, want_reply); - - msg->type = SSH_REQUEST_CHANNEL; - msg->channel_request.channel = channel; - msg->channel_request.want_reply = want_reply; - - if (strcmp(request, "pty-req") == 0) { - rc = ssh_buffer_unpack(packet, "sddddS", - &msg->channel_request.TERM, - &msg->channel_request.width, - &msg->channel_request.height, - &msg->channel_request.pxwidth, - &msg->channel_request.pxheight, - &msg->channel_request.modes - ); - - msg->channel_request.type = SSH_CHANNEL_REQUEST_PTY; - - if (rc != SSH_OK) { - goto error; - } - goto end; - } - - if (strcmp(request, "window-change") == 0) { - msg->channel_request.type = SSH_CHANNEL_REQUEST_WINDOW_CHANGE; - rc = ssh_buffer_unpack(packet, "dddd", - &msg->channel_request.width, - &msg->channel_request.height, - &msg->channel_request.pxwidth, - &msg->channel_request.pxheight); - if (rc != SSH_OK){ - goto error; - } - goto end; - } - - if (strcmp(request, "subsystem") == 0) { - rc = ssh_buffer_unpack(packet, "s", - &msg->channel_request.subsystem); - msg->channel_request.type = SSH_CHANNEL_REQUEST_SUBSYSTEM; - if (rc != SSH_OK){ - goto error; - } - goto end; - } - - if (strcmp(request, "shell") == 0) { - msg->channel_request.type = SSH_CHANNEL_REQUEST_SHELL; - goto end; - } - - if (strcmp(request, "exec") == 0) { - rc = ssh_buffer_unpack(packet, "s", - &msg->channel_request.command); - msg->channel_request.type = SSH_CHANNEL_REQUEST_EXEC; - if (rc != SSH_OK) { - goto error; - } - goto end; - } - - if (strcmp(request, "env") == 0) { - rc = ssh_buffer_unpack(packet, "ss", - &msg->channel_request.var_name, - &msg->channel_request.var_value); - msg->channel_request.type = SSH_CHANNEL_REQUEST_ENV; - if (rc != SSH_OK) { - goto error; - } - goto end; - } - - if (strcmp(request, "x11-req") == 0) { - rc = ssh_buffer_unpack(packet, "bssd", - &msg->channel_request.x11_single_connection, - &msg->channel_request.x11_auth_protocol, - &msg->channel_request.x11_auth_cookie, - &msg->channel_request.x11_screen_number); - - msg->channel_request.type = SSH_CHANNEL_REQUEST_X11; - if (rc != SSH_OK) { - goto error; - } - - goto end; - } - - msg->channel_request.type = SSH_CHANNEL_REQUEST_UNKNOWN; -end: - ssh_message_queue(session,msg); - - return SSH_OK; -error: - ssh_message_free(msg); - - return SSH_ERROR; -} - -int ssh_message_channel_request_reply_success(ssh_message msg) { - uint32_t channel; - int rc; - - if (msg == NULL) { - return SSH_ERROR; - } - - if (msg->channel_request.want_reply) { - channel = msg->channel_request.channel->remote_channel; - - SSH_LOG(SSH_LOG_PACKET, - "Sending a channel_request success to channel %d", channel); - - rc = ssh_buffer_pack(msg->session->out_buffer, - "bd", - SSH2_MSG_CHANNEL_SUCCESS, - channel); - if (rc != SSH_OK){ - ssh_set_error_oom(msg->session); - return SSH_ERROR; - } - - return packet_send(msg->session); - } - - SSH_LOG(SSH_LOG_PACKET, - "The client doesn't want to know the request succeeded"); - - return SSH_OK; -} - -#ifdef WITH_SERVER -SSH_PACKET_CALLBACK(ssh_packet_global_request){ - ssh_message msg = NULL; - char *request=NULL; - uint8_t want_reply; - int rc = SSH_PACKET_USED; - int r; - (void)user; - (void)type; - (void)packet; - - SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH_MSG_GLOBAL_REQUEST packet"); - r = ssh_buffer_unpack(packet, "sb", - &request, - &want_reply); - if (r != SSH_OK){ - goto error; - } - - msg = ssh_message_new(session); - if (msg == NULL) { - ssh_set_error_oom(session); - goto error; - } - msg->type = SSH_REQUEST_GLOBAL; - - if (strcmp(request, "tcpip-forward") == 0) { - r = ssh_buffer_unpack(packet, "sd", - &msg->global_request.bind_address, - &msg->global_request.bind_port - ); - if (r != SSH_OK){ - goto error; - } - msg->global_request.type = SSH_GLOBAL_REQUEST_TCPIP_FORWARD; - msg->global_request.want_reply = want_reply; - - SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, - msg->global_request.bind_address, - msg->global_request.bind_port); - - if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) { - SSH_LOG(SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, - want_reply, msg->global_request.bind_address, - msg->global_request.bind_port); - session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata); - } else { - ssh_message_reply_default(msg); - } - } else if (strcmp(request, "cancel-tcpip-forward") == 0) { - r = ssh_buffer_unpack(packet, "sd", - &msg->global_request.bind_address, - &msg->global_request.bind_port); - if (r != SSH_OK){ - goto error; - } - msg->global_request.type = SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD; - msg->global_request.want_reply = want_reply; - - SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, - msg->global_request.bind_address, - msg->global_request.bind_port); - - if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) { - session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata); - } else { - ssh_message_reply_default(msg); - } - } else { - SSH_LOG(SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s %d", request, want_reply); - rc = SSH_PACKET_NOT_USED; - } - - SAFE_FREE(msg); - SAFE_FREE(request); - return rc; -error: - SAFE_FREE(msg); - SAFE_FREE(request); - SSH_LOG(SSH_LOG_WARNING, "Invalid SSH_MSG_GLOBAL_REQUEST packet"); - return SSH_PACKET_NOT_USED; -} - -#endif /* WITH_SERVER */ - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/misc.c b/libssh/src/misc.c deleted file mode 100644 index 6daf60ab..00000000 --- a/libssh/src/misc.c +++ /dev/null @@ -1,1035 +0,0 @@ -/* - * misc.c - useful client functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * Copyright (c) 2008-2009 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#ifndef _WIN32 -/* This is needed for a standard getpwuid_r on opensolaris */ -#define _POSIX_PTHREAD_SEMANTICS -#include -#include -#include -#include -#include - -#ifndef HAVE_CLOCK_GETTIME -#include -#endif /* HAVE_CLOCK_GETTIME */ -#endif /* _WIN32 */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 - -#ifndef _WIN32_IE -# define _WIN32_IE 0x0501 // SHGetSpecialFolderPath -#endif - -#include // Must be the first to include -#include -#include -#include - -#if _MSC_VER >= 1400 -# include -#endif /* _MSC_VER */ - -#endif /* _WIN32 */ - -#include "libssh/priv.h" -#include "libssh/misc.h" -#include "libssh/session.h" - -#ifdef HAVE_LIBGCRYPT -#define GCRYPT_STRING "/gnutls" -#else -#define GCRYPT_STRING "" -#endif - -#ifdef HAVE_LIBCRYPTO -#define CRYPTO_STRING "/openssl" -#else -#define CRYPTO_STRING "" -#endif - -#ifdef WITH_ZLIB -#define ZLIB_STRING "/zlib" -#else -#define ZLIB_STRING "" -#endif - -/** - * @defgroup libssh_misc The SSH helper functions. - * @ingroup libssh - * - * Different helper functions used in the SSH Library. - * - * @{ - */ - -#ifdef _WIN32 -char *ssh_get_user_home_dir(void) { - char tmp[MAX_PATH] = {0}; - char *szPath = NULL; - - if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) { - szPath = malloc(strlen(tmp) + 1); - if (szPath == NULL) { - return NULL; - } - - strcpy(szPath, tmp); - return szPath; - } - - return NULL; -} - -/* we have read access on file */ -int ssh_file_readaccess_ok(const char *file) { - if (_access(file, 4) < 0) { - return 0; - } - - return 1; -} - -#define SSH_USEC_IN_SEC 1000000LL -#define SSH_SECONDS_SINCE_1601 11644473600LL - -int gettimeofday(struct timeval *__p, void *__t) { - union { - unsigned long long ns100; /* time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } now; - - GetSystemTimeAsFileTime (&now.ft); - __p->tv_usec = (long) ((now.ns100 / 10LL) % SSH_USEC_IN_SEC); - __p->tv_sec = (long)(((now.ns100 / 10LL ) / SSH_USEC_IN_SEC) - SSH_SECONDS_SINCE_1601); - - return (0); -} - -char *ssh_get_local_username(void) { - DWORD size = 0; - char *user; - - /* get the size */ - GetUserName(NULL, &size); - - user = (char *) malloc(size); - if (user == NULL) { - return NULL; - } - - if (GetUserName(user, &size)) { - return user; - } - - return NULL; -} - -int ssh_is_ipaddr_v4(const char *str) { - struct sockaddr_storage ss; - int sslen = sizeof(ss); - int rc = SOCKET_ERROR; - - /* WSAStringToAddressA thinks that 0.0.0 is a valid IP */ - if (strlen(str) < 7) { - return 0; - } - - rc = WSAStringToAddressA((LPSTR) str, - AF_INET, - NULL, - (struct sockaddr*)&ss, - &sslen); - if (rc == 0) { - return 1; - } - - return 0; -} - -int ssh_is_ipaddr(const char *str) { - int rc = SOCKET_ERROR; - - if (strchr(str, ':')) { - struct sockaddr_storage ss; - int sslen = sizeof(ss); - - /* TODO link-local (IP:v6:addr%ifname). */ - rc = WSAStringToAddressA((LPSTR) str, - AF_INET6, - NULL, - (struct sockaddr*)&ss, - &sslen); - if (rc == 0) { - return 1; - } - } - - return ssh_is_ipaddr_v4(str); -} -#else /* _WIN32 */ - -#ifndef NSS_BUFLEN_PASSWD -#define NSS_BUFLEN_PASSWD 4096 -#endif /* NSS_BUFLEN_PASSWD */ - -char *ssh_get_user_home_dir(void) { - char *szPath = NULL; - struct passwd pwd; - struct passwd *pwdbuf; - char buf[NSS_BUFLEN_PASSWD]; - int rc; - - rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); - if (rc != 0) { - szPath = getenv("HOME"); - if (szPath == NULL) { - return NULL; - } - memset(buf, 0, sizeof(buf)); - snprintf(buf, sizeof(buf), "%s", szPath); - - return strdup(buf); - } - - szPath = strdup(pwd.pw_dir); - - return szPath; -} - -/* we have read access on file */ -int ssh_file_readaccess_ok(const char *file) { - if (access(file, R_OK) < 0) { - return 0; - } - - return 1; -} - -char *ssh_get_local_username(void) { - struct passwd pwd; - struct passwd *pwdbuf; - char buf[NSS_BUFLEN_PASSWD]; - char *name; - int rc; - - rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); - if (rc != 0) { - return NULL; - } - - name = strdup(pwd.pw_name); - - if (name == NULL) { - return NULL; - } - - return name; -} - -int ssh_is_ipaddr_v4(const char *str) { - int rc = -1; - struct in_addr dest; - - rc = inet_pton(AF_INET, str, &dest); - if (rc > 0) { - return 1; - } - - return 0; -} - -int ssh_is_ipaddr(const char *str) { - int rc = -1; - - if (strchr(str, ':')) { - struct in6_addr dest6; - - /* TODO link-local (IP:v6:addr%ifname). */ - rc = inet_pton(AF_INET6, str, &dest6); - if (rc > 0) { - return 1; - } - } - - return ssh_is_ipaddr_v4(str); -} - -#endif /* _WIN32 */ - -#ifndef HAVE_NTOHLL -uint64_t ntohll(uint64_t a) { -#ifdef WORDS_BIGENDIAN - return a; -#else /* WORDS_BIGENDIAN */ - return (((uint64_t)(a) << 56) | \ - (((uint64_t)(a) << 40) & 0xff000000000000ULL) | \ - (((uint64_t)(a) << 24) & 0xff0000000000ULL) | \ - (((uint64_t)(a) << 8) & 0xff00000000ULL) | \ - (((uint64_t)(a) >> 8) & 0xff000000ULL) | \ - (((uint64_t)(a) >> 24) & 0xff0000ULL) | \ - (((uint64_t)(a) >> 40) & 0xff00ULL) | \ - ((uint64_t)(a) >> 56)); -#endif /* WORDS_BIGENDIAN */ -} -#endif /* HAVE_NTOHLL */ - -char *ssh_lowercase(const char* str) { - char *new, *p; - - if (str == NULL) { - return NULL; - } - - new = strdup(str); - if (new == NULL) { - return NULL; - } - - for (p = new; *p; p++) { - *p = tolower(*p); - } - - return new; -} - -char *ssh_hostport(const char *host, int port){ - char *dest; - size_t len; - if(host==NULL) - return NULL; - /* 3 for []:, 5 for 65536 and 1 for nul */ - len=strlen(host) + 3 + 5 + 1; - dest=malloc(len); - if(dest==NULL) - return NULL; - snprintf(dest,len,"[%s]:%d",host,port); - return dest; -} - -/** - * @brief Check if libssh is the required version or get the version - * string. - * - * @param[in] req_version The version required. - * - * @return If the version of libssh is newer than the version - * required it will return a version string. - * NULL if the version is older. - * - * Example: - * - * @code - * if (ssh_version(SSH_VERSION_INT(0,2,1)) == NULL) { - * fprintf(stderr, "libssh version is too old!\n"); - * exit(1); - * } - * - * if (debug) { - * printf("libssh %s\n", ssh_version(0)); - * } - * @endcode - */ -const char *ssh_version(int req_version) { - if (req_version <= LIBSSH_VERSION_INT) { - return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING - ZLIB_STRING; - } - - return NULL; -} - -struct ssh_list *ssh_list_new(void) { - struct ssh_list *ret=malloc(sizeof(struct ssh_list)); - if(!ret) - return NULL; - ret->root=ret->end=NULL; - return ret; -} - -void ssh_list_free(struct ssh_list *list){ - struct ssh_iterator *ptr,*next; - if(!list) - return; - ptr=list->root; - while(ptr){ - next=ptr->next; - SAFE_FREE(ptr); - ptr=next; - } - SAFE_FREE(list); -} - -struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list){ - if(!list) - return NULL; - return list->root; -} - -struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value){ - struct ssh_iterator *it; - for(it = ssh_list_get_iterator(list); it != NULL ;it=it->next) - if(it->data==value) - return it; - return NULL; -} - -static struct ssh_iterator *ssh_iterator_new(const void *data){ - struct ssh_iterator *iterator=malloc(sizeof(struct ssh_iterator)); - if(!iterator) - return NULL; - iterator->next=NULL; - iterator->data=data; - return iterator; -} - -int ssh_list_append(struct ssh_list *list,const void *data){ - struct ssh_iterator *iterator=ssh_iterator_new(data); - if(!iterator) - return SSH_ERROR; - if(!list->end){ - /* list is empty */ - list->root=list->end=iterator; - } else { - /* put it on end of list */ - list->end->next=iterator; - list->end=iterator; - } - return SSH_OK; -} - -int ssh_list_prepend(struct ssh_list *list, const void *data){ - struct ssh_iterator *it = ssh_iterator_new(data); - - if (it == NULL) { - return SSH_ERROR; - } - - if (list->end == NULL) { - /* list is empty */ - list->root = list->end = it; - } else { - /* set as new root */ - it->next = list->root; - list->root = it; - } - - return SSH_OK; -} - -void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator){ - struct ssh_iterator *ptr,*prev; - prev=NULL; - ptr=list->root; - while(ptr && ptr != iterator){ - prev=ptr; - ptr=ptr->next; - } - if(!ptr){ - /* we did not find the element */ - return; - } - /* unlink it */ - if(prev) - prev->next=ptr->next; - /* if iterator was the head */ - if(list->root == iterator) - list->root=iterator->next; - /* if iterator was the tail */ - if(list->end == iterator) - list->end = prev; - SAFE_FREE(iterator); -} - -/** - * @internal - * - * @brief Removes the top element of the list and returns the data value - * attached to it. - * - * @param[in[ list The ssh_list to remove the element. - * - * @returns A pointer to the element being stored in head, or NULL - * if the list is empty. - */ -const void *_ssh_list_pop_head(struct ssh_list *list){ - struct ssh_iterator *iterator=list->root; - const void *data; - if(!list->root) - return NULL; - data=iterator->data; - list->root=iterator->next; - if(list->end==iterator) - list->end=NULL; - SAFE_FREE(iterator); - return data; -} - -/** - * @brief Parse directory component. - * - * dirname breaks a null-terminated pathname string into a directory component. - * In the usual case, ssh_dirname() returns the string up to, but not including, - * the final '/'. Trailing '/' characters are not counted as part of the - * pathname. The caller must free the memory. - * - * @param[in] path The path to parse. - * - * @return The dirname of path or NULL if we can't allocate memory. - * If path does not contain a slash, c_dirname() returns - * the string ".". If path is the string "/", it returns - * the string "/". If path is NULL or an empty string, - * "." is returned. - */ -char *ssh_dirname (const char *path) { - char *new = NULL; - size_t len; - - if (path == NULL || *path == '\0') { - return strdup("."); - } - - len = strlen(path); - - /* Remove trailing slashes */ - while(len > 0 && path[len - 1] == '/') --len; - - /* We have only slashes */ - if (len == 0) { - return strdup("/"); - } - - /* goto next slash */ - while(len > 0 && path[len - 1] != '/') --len; - - if (len == 0) { - return strdup("."); - } else if (len == 1) { - return strdup("/"); - } - - /* Remove slashes again */ - while(len > 0 && path[len - 1] == '/') --len; - - new = malloc(len + 1); - if (new == NULL) { - return NULL; - } - - strncpy(new, path, len); - new[len] = '\0'; - - return new; -} - -/** - * @brief basename - parse filename component. - * - * basename breaks a null-terminated pathname string into a filename component. - * ssh_basename() returns the component following the final '/'. Trailing '/' - * characters are not counted as part of the pathname. - * - * @param[in] path The path to parse. - * - * @return The filename of path or NULL if we can't allocate - * memory. If path is a the string "/", basename returns - * the string "/". If path is NULL or an empty string, - * "." is returned. - */ -char *ssh_basename (const char *path) { - char *new = NULL; - const char *s; - size_t len; - - if (path == NULL || *path == '\0') { - return strdup("."); - } - - len = strlen(path); - /* Remove trailing slashes */ - while(len > 0 && path[len - 1] == '/') --len; - - /* We have only slashes */ - if (len == 0) { - return strdup("/"); - } - - while(len > 0 && path[len - 1] != '/') --len; - - if (len > 0) { - s = path + len; - len = strlen(s); - - while(len > 0 && s[len - 1] == '/') --len; - } else { - return strdup(path); - } - - new = malloc(len + 1); - if (new == NULL) { - return NULL; - } - - strncpy(new, s, len); - new[len] = '\0'; - - return new; -} - -/** - * @brief Attempts to create a directory with the given pathname. - * - * This is the portable version of mkdir, mode is ignored on Windows systems. - * - * @param[in] pathname The path name to create the directory. - * - * @param[in] mode The permissions to use. - * - * @return 0 on success, < 0 on error with errno set. - */ -int ssh_mkdir(const char *pathname, mode_t mode) { - int r; - -#ifdef _WIN32 - r = _mkdir(pathname); -#else - r = mkdir(pathname, mode); -#endif - - return r; -} - -/** - * @brief Expand a directory starting with a tilde '~' - * - * @param[in] d The directory to expand. - * - * @return The expanded directory, NULL on error. - */ -char *ssh_path_expand_tilde(const char *d) { - char *h = NULL, *r; - const char *p; - size_t ld; - size_t lh = 0; - - if (d[0] != '~') { - return strdup(d); - } - d++; - - /* handle ~user/path */ - p = strchr(d, '/'); - if (p != NULL && p > d) { -#ifdef _WIN32 - return strdup(d); -#else - struct passwd *pw; - size_t s = p - d; - char u[128]; - - if (s >= sizeof(u)) { - return NULL; - } - memcpy(u, d, s); - u[s] = '\0'; - pw = getpwnam(u); - if (pw == NULL) { - return NULL; - } - ld = strlen(p); - h = strdup(pw->pw_dir); -#endif - } else { - ld = strlen(d); - p = (char *) d; - h = ssh_get_user_home_dir(); - } - if (h == NULL) { - return NULL; - } - lh = strlen(h); - - r = malloc(ld + lh + 1); - if (r == NULL) { - SAFE_FREE(h); - return NULL; - } - - if (lh > 0) { - memcpy(r, h, lh); - } - SAFE_FREE(h); - memcpy(r + lh, p, ld + 1); - - return r; -} - -char *ssh_path_expand_escape(ssh_session session, const char *s) { - char host[NI_MAXHOST]; - char buf[MAX_BUF_SIZE]; - char *r, *x = NULL; - const char *p; - size_t i, l; - - r = ssh_path_expand_tilde(s); - if (r == NULL) { - ssh_set_error_oom(session); - return NULL; - } - - if (strlen(r) > MAX_BUF_SIZE) { - ssh_set_error(session, SSH_FATAL, "string to expand too long"); - free(r); - return NULL; - } - - p = r; - buf[0] = '\0'; - - for (i = 0; *p != '\0'; p++) { - if (*p != '%') { - buf[i] = *p; - i++; - if (i >= MAX_BUF_SIZE) { - free(r); - return NULL; - } - buf[i] = '\0'; - continue; - } - - p++; - if (*p == '\0') { - break; - } - - switch (*p) { - case 'd': - x = strdup(session->opts.sshdir); - break; - case 'u': - x = ssh_get_local_username(); - break; - case 'l': - if (gethostname(host, sizeof(host) == 0)) { - x = strdup(host); - } - break; - case 'h': - x = strdup(session->opts.host); - break; - case 'r': - x = strdup(session->opts.username); - break; - case 'p': - if (session->opts.port < 65536) { - char tmp[6]; - - snprintf(tmp, sizeof(tmp), "%u", session->opts.port); - x = strdup(tmp); - } - break; - default: - ssh_set_error(session, SSH_FATAL, - "Wrong escape sequence detected"); - free(r); - return NULL; - } - - if (x == NULL) { - ssh_set_error_oom(session); - free(r); - return NULL; - } - - i += strlen(x); - if (i >= MAX_BUF_SIZE) { - ssh_set_error(session, SSH_FATAL, - "String too long"); - free(x); - free(r); - return NULL; - } - l = strlen(buf); - strncpy(buf + l, x, sizeof(buf) - l - 1); - buf[i] = '\0'; - SAFE_FREE(x); - } - - free(r); - return strdup(buf); -#undef MAX_BUF_SIZE -} - -/** - * @internal - * - * @brief Analyze the SSH banner to find out if we have a SSHv1 or SSHv2 - * server. - * - * @param session The session to analyze the banner from. - * @param server 0 means we are a client, 1 a server. - * @param ssh1 The variable which is set if it is a SSHv1 server. - * @param ssh2 The variable which is set if it is a SSHv2 server. - * - * @return 0 on success, < 0 on error. - * - * @see ssh_get_banner() - */ -int ssh_analyze_banner(ssh_session session, int server, int *ssh1, int *ssh2) { - const char *banner; - const char *openssh; - - if (server) { - banner = session->clientbanner; - } else { - banner = session->serverbanner; - } - - if (banner == NULL) { - ssh_set_error(session, SSH_FATAL, "Invalid banner"); - return -1; - } - - /* - * Typical banners e.g. are: - * - * SSH-1.5-openSSH_5.4 - * SSH-1.99-openSSH_3.0 - * - * SSH-2.0-something - * 012345678901234567890 - */ - if (strlen(banner) < 6 || - strncmp(banner, "SSH-", 4) != 0) { - ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner); - return -1; - } - - SSH_LOG(SSH_LOG_RARE, "Analyzing banner: %s", banner); - - switch(banner[4]) { - case '1': - *ssh1 = 1; - if (strlen(banner) > 6) { - if (banner[6] == '9') { - *ssh2 = 1; - } else { - *ssh2 = 0; - } - } - break; - case '2': - *ssh1 = 0; - *ssh2 = 1; - break; - default: - ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner); - return -1; - } - - openssh = strstr(banner, "OpenSSH"); - if (openssh != NULL) { - int major, minor; - - /* - * The banner is typical: - * OpenSSH_5.4 - * 012345678901234567890 - */ - if (strlen(openssh) > 9) { - major = strtol(openssh + 8, (char **) NULL, 10); - minor = strtol(openssh + 10, (char **) NULL, 10); - session->openssh = SSH_VERSION_INT(major, minor, 0); - SSH_LOG(SSH_LOG_RARE, - "We are talking to an OpenSSH client version: %d.%d (%x)", - major, minor, session->openssh); - } - } - - - return 0; -} - -/* try the Monotonic clock if possible for perfs reasons */ -#ifdef _POSIX_MONOTONIC_CLOCK -#define CLOCK CLOCK_MONOTONIC -#else -#define CLOCK CLOCK_REALTIME -#endif - -/** - * @internal - * @brief initializes a timestamp to the current time - * @param[out] ts pointer to an allocated ssh_timestamp structure - */ -void ssh_timestamp_init(struct ssh_timestamp *ts){ -#ifdef HAVE_CLOCK_GETTIME - struct timespec tp; - clock_gettime(CLOCK, &tp); - ts->useconds = tp.tv_nsec / 1000; -#else - struct timeval tp; - gettimeofday(&tp, NULL); - ts->useconds = tp.tv_usec; -#endif - ts->seconds = tp.tv_sec; -} - -#undef CLOCK - -/** - * @internal - * @brief gets the time difference between two timestamps in ms - * @param[in] old older value - * @param[in] new newer value - * @returns difference in milliseconds - */ - -static int ssh_timestamp_difference(struct ssh_timestamp *old, - struct ssh_timestamp *new){ - long seconds, usecs, msecs; - seconds = new->seconds - old->seconds; - usecs = new->useconds - old->useconds; - if (usecs < 0){ - seconds--; - usecs += 1000000; - } - msecs = seconds * 1000 + usecs/1000; - return msecs; -} - -/** - * @internal - * @brief turn seconds and microseconds pair (as provided by user-set options) - * into millisecond value - * @param[in] sec number of seconds - * @param[in] usec number of microseconds - * @returns milliseconds, or 10000 if user supplied values are equal to zero - */ -int ssh_make_milliseconds(long sec, long usec) { - int res = usec ? (usec / 1000) : 0; - res += (sec * 1000); - if (res == 0) { - res = 10 * 1000; /* use a reasonable default value in case - * SSH_OPTIONS_TIMEOUT is not set in options. */ - } - return res; -} - -/** - * @internal - * @brief Checks if a timeout is elapsed, in function of a previous - * timestamp and an assigned timeout - * @param[in] ts pointer to an existing timestamp - * @param[in] timeout timeout in milliseconds. Negative values mean infinite - * timeout - * @returns 1 if timeout is elapsed - * 0 otherwise - */ -int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) { - struct ssh_timestamp now; - - switch(timeout) { - case -2: /* - * -2 means user-defined timeout as available in - * session->timeout, session->timeout_usec. - */ - fprintf(stderr, "ssh_timeout_elapsed called with -2. this needs to " - "be fixed. please set a breakpoint on %s:%d and " - "fix the caller\n", __FILE__, __LINE__); - case -1: /* -1 means infinite timeout */ - return 0; - case 0: /* 0 means no timeout */ - return 1; - default: - break; - } - - ssh_timestamp_init(&now); - - return (ssh_timestamp_difference(ts,&now) >= timeout); -} - -/** - * @brief updates a timeout value so it reflects the remaining time - * @param[in] ts pointer to an existing timestamp - * @param[in] timeout timeout in milliseconds. Negative values mean infinite - * timeout - * @returns remaining time in milliseconds, 0 if elapsed, -1 if never. - */ -int ssh_timeout_update(struct ssh_timestamp *ts, int timeout){ - struct ssh_timestamp now; - int ms, ret; - if (timeout <= 0) { - return timeout; - } - ssh_timestamp_init(&now); - ms = ssh_timestamp_difference(ts,&now); - if(ms < 0) - ms = 0; - ret = timeout - ms; - return ret >= 0 ? ret: 0; -} - - -int ssh_match_group(const char *group, const char *object) -{ - const char *a; - const char *z; - - z = group; - do { - a = strchr(z, ','); - if (a == NULL) { - if (strcmp(z, object) == 0) { - return 1; - } - return 0; - } else { - if (strncmp(z, object, a - z) == 0) { - return 1; - } - } - z = a + 1; - } while(1); - - /* not reached */ - return 0; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/options.c b/libssh/src/options.c deleted file mode 100644 index f7f24553..00000000 --- a/libssh/src/options.c +++ /dev/null @@ -1,1568 +0,0 @@ -/* - * options.c - handle pre-connection options - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * Copyright (c) 2009-2013 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" -#include -#include -#include -#ifndef _WIN32 -#include -#else -#include -#endif -#include -#include "libssh/priv.h" -#include "libssh/session.h" -#include "libssh/misc.h" -#include "libssh/options.h" -#ifdef WITH_SERVER -#include "libssh/server.h" -#include "libssh/bind.h" -#endif - -/** - * @addtogroup libssh_session - * @{ - */ - -/** - * @brief Duplicate the options of a session structure. - * - * If you make several sessions with the same options this is useful. You - * cannot use twice the same option structure in ssh_session_connect. - * - * @param src The session to use to copy the options. - * - * @param dest A pointer to store the allocated session with duplicated - * options. You have to free the memory. - * - * @returns 0 on sucess, -1 on error with errno set. - * - * @see ssh_session_connect() - */ -int ssh_options_copy(ssh_session src, ssh_session *dest) { - ssh_session new; - int i; - - if (src == NULL || dest == NULL) { - return -1; - } - - new = ssh_new(); - if (new == NULL) { - return -1; - } - - if (src->opts.username) { - new->opts.username = strdup(src->opts.username); - if (new->opts.username == NULL) { - ssh_free(new); - return -1; - } - } - - if (src->opts.host) { - new->opts.host = strdup(src->opts.host); - if (new->opts.host == NULL) { - ssh_free(new); - return -1; - } - } - - if (src->opts.identity) { - struct ssh_iterator *it; - - new->opts.identity = ssh_list_new(); - if (new->opts.identity == NULL) { - ssh_free(new); - return -1; - } - - it = ssh_list_get_iterator(src->opts.identity); - while (it) { - char *id; - int rc; - - id = strdup((char *) it->data); - if (id == NULL) { - ssh_free(new); - return -1; - } - - rc = ssh_list_append(new->opts.identity, id); - if (rc < 0) { - free(id); - ssh_free(new); - return -1; - } - it = it->next; - } - } - - if (src->opts.sshdir) { - new->opts.sshdir = strdup(src->opts.sshdir); - if (new->opts.sshdir == NULL) { - ssh_free(new); - return -1; - } - } - - if (src->opts.knownhosts) { - new->opts.knownhosts = strdup(src->opts.knownhosts); - if (new->opts.knownhosts == NULL) { - ssh_free(new); - return -1; - } - } - - for (i = 0; i < 10; i++) { - if (src->opts.wanted_methods[i]) { - new->opts.wanted_methods[i] = strdup(src->opts.wanted_methods[i]); - if (new->opts.wanted_methods[i] == NULL) { - ssh_free(new); - return -1; - } - } - } - - if (src->opts.ProxyCommand) { - new->opts.ProxyCommand = strdup(src->opts.ProxyCommand); - if (new->opts.ProxyCommand == NULL) { - ssh_free(new); - return -1; - } - } - new->opts.fd = src->opts.fd; - new->opts.port = src->opts.port; - new->opts.timeout = src->opts.timeout; - new->opts.timeout_usec = src->opts.timeout_usec; - new->opts.ssh2 = src->opts.ssh2; - new->opts.ssh1 = src->opts.ssh1; - new->opts.compressionlevel = src->opts.compressionlevel; - new->common.log_verbosity = src->common.log_verbosity; - new->common.callbacks = src->common.callbacks; - - *dest = new; - - return 0; -} - -int ssh_options_set_algo(ssh_session session, int algo, - const char *list) { - if (!verify_existing_algo(algo, list)) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "Setting method: no algorithm for method \"%s\" (%s)\n", - ssh_kex_get_description(algo), list); - return -1; - } - - SAFE_FREE(session->opts.wanted_methods[algo]); - session->opts.wanted_methods[algo] = strdup(list); - if (session->opts.wanted_methods[algo] == NULL) { - ssh_set_error_oom(session); - return -1; - } - - return 0; -} - -/** - * @brief This function can set all possible ssh options. - * - * @param session An allocated SSH session structure. - * - * @param type The option type to set. This could be one of the - * following: - * - * - SSH_OPTIONS_HOST: - * The hostname or ip address to connect to (const char *). - * - * - SSH_OPTIONS_PORT: - * The port to connect to (unsigned int). - * - * - SSH_OPTIONS_PORT_STR: - * The port to connect to (const char *). - * - * - SSH_OPTIONS_FD: - * The file descriptor to use (socket_t).\n - * \n - * If you wish to open the socket yourself for a reason - * or another, set the file descriptor. Don't forget to - * set the hostname as the hostname is used as a key in - * the known_host mechanism. - * - * - SSH_OPTIONS_BINDADDR: - * The address to bind the client to (const char *). - * - * - SSH_OPTIONS_USER: - * The username for authentication (const char *).\n - * \n - * If the value is NULL, the username is set to the - * default username. - * - * - SSH_OPTIONS_SSH_DIR: - * Set the ssh directory (const char *,format string).\n - * \n - * If the value is NULL, the directory is set to the - * default ssh directory.\n - * \n - * The ssh directory is used for files like known_hosts - * and identity (private and public key). It may include - * "%s" which will be replaced by the user home - * directory. - * - * - SSH_OPTIONS_KNOWNHOSTS: - * Set the known hosts file name (const char *,format string).\n - * \n - * If the value is NULL, the directory is set to the - * default known hosts file, normally - * ~/.ssh/known_hosts.\n - * \n - * The known hosts file is used to certify remote hosts - * are genuine. It may include "%s" which will be - * replaced by the user home directory. - * - * - SSH_OPTIONS_IDENTITY: - * Set the identity file name (const char *,format string).\n - * \n - * By default identity, id_dsa and id_rsa are checked.\n - * \n - * The identity file used authenticate with public key. - * It may include "%s" which will be replaced by the - * user home directory. - * - * - SSH_OPTIONS_TIMEOUT: - * Set a timeout for the connection in seconds (long). - * - * - SSH_OPTIONS_TIMEOUT_USEC: - * Set a timeout for the connection in micro seconds - * (long). - * - * - SSH_OPTIONS_SSH1: - * Allow or deny the connection to SSH1 servers - * (int, 0 is false). - * - * - SSH_OPTIONS_SSH2: - * Allow or deny the connection to SSH2 servers - * (int, 0 is false). - * - * - SSH_OPTIONS_LOG_VERBOSITY: - * Set the session logging verbosity (int).\n - * \n - * The verbosity of the messages. Every log smaller or - * equal to verbosity will be shown. - * - SSH_LOG_NOLOG: No logging - * - SSH_LOG_RARE: Rare conditions or warnings - * - SSH_LOG_ENTRY: API-accessible entrypoints - * - SSH_LOG_PACKET: Packet id and size - * - SSH_LOG_FUNCTIONS: Function entering and leaving - * - * - SSH_OPTIONS_LOG_VERBOSITY_STR: - * Set the session logging verbosity (const char *).\n - * \n - * The verbosity of the messages. Every log smaller or - * equal to verbosity will be shown. - * - SSH_LOG_NOLOG: No logging - * - SSH_LOG_RARE: Rare conditions or warnings - * - SSH_LOG_ENTRY: API-accessible entrypoints - * - SSH_LOG_PACKET: Packet id and size - * - SSH_LOG_FUNCTIONS: Function entering and leaving - * \n - * See the corresponding numbers in libssh.h. - * - * - SSH_OPTIONS_AUTH_CALLBACK: - * Set a callback to use your own authentication function - * (function pointer). - * - * - SSH_OPTIONS_AUTH_USERDATA: - * Set the user data passed to the authentication - * function (generic pointer). - * - * - SSH_OPTIONS_LOG_CALLBACK: - * Set a callback to use your own logging function - * (function pointer). - * - * - SSH_OPTIONS_LOG_USERDATA: - * Set the user data passed to the logging function - * (generic pointer). - * - * - SSH_OPTIONS_STATUS_CALLBACK: - * Set a callback to show connection status in realtime - * (function pointer).\n - * \n - * @code - * fn(void *arg, float status) - * @endcode - * \n - * During ssh_connect(), libssh will call the callback - * with status from 0.0 to 1.0. - * - * - SSH_OPTIONS_STATUS_ARG: - * Set the status argument which should be passed to the - * status callback (generic pointer). - * - * - SSH_OPTIONS_CIPHERS_C_S: - * Set the symmetric cipher client to server (const char *, - * comma-separated list). - * - * - SSH_OPTIONS_CIPHERS_S_C: - * Set the symmetric cipher server to client (const char *, - * comma-separated list). - * - * - SSH_OPTIONS_KEY_EXCHANGE: - * Set the key exchange method to be used (const char *, - * comma-separated list). ex: - * "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" - * - * - SSH_OPTIONS_HOSTKEYS: - * Set the preferred server host key types (const char *, - * comma-separated list). ex: - * "ssh-rsa,ssh-dsa,ecdh-sha2-nistp256" - * - * - SSH_OPTIONS_COMPRESSION_C_S: - * Set the compression to use for client to server - * communication (const char *, "yes", "no" or a specific - * algorithm name if needed ("zlib","zlib@openssh.com","none"). - * - * - SSH_OPTIONS_COMPRESSION_S_C: - * Set the compression to use for server to client - * communication (const char *, "yes", "no" or a specific - * algorithm name if needed ("zlib","zlib@openssh.com","none"). - * - * - SSH_OPTIONS_COMPRESSION: - * Set the compression to use for both directions - * communication (const char *, "yes", "no" or a specific - * algorithm name if needed ("zlib","zlib@openssh.com","none"). - * - * - SSH_OPTIONS_COMPRESSION_LEVEL: - * Set the compression level to use for zlib functions. (int, - * value from 1 to 9, 9 being the most efficient but slower). - * - * - SSH_OPTIONS_STRICTHOSTKEYCHECK: - * Set the parameter StrictHostKeyChecking to avoid - * asking about a fingerprint (int, 0 = false). - * - * - SSH_OPTIONS_PROXYCOMMAND: - * Set the command to be executed in order to connect to - * server (const char *). - * - * - SSH_OPTIONS_GSSAPI_SERVER_IDENTITY - * Set it to specify the GSSAPI server identity that libssh - * should expect when connecting to the server (const char *). - * - * - SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY - * Set it to specify the GSSAPI client identity that libssh - * should expect when connecting to the server (const char *). - * - * - SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS - * Set it to specify that GSSAPI should delegate credentials - * to the server (int, 0 = false). - * - * @param value The value to set. This is a generic pointer and the - * datatype which is used should be set according to the - * type set. - * - * @return 0 on success, < 0 on error. - */ -int ssh_options_set(ssh_session session, enum ssh_options_e type, - const void *value) { - const char *v; - char *p, *q; - long int i; - int rc; - - if (session == NULL) { - return -1; - } - - switch (type) { - case SSH_OPTIONS_HOST: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - q = strdup(value); - if (q == NULL) { - ssh_set_error_oom(session); - return -1; - } - p = strchr(q, '@'); - - SAFE_FREE(session->opts.host); - - if (p) { - *p = '\0'; - session->opts.host = strdup(p + 1); - if (session->opts.host == NULL) { - SAFE_FREE(q); - ssh_set_error_oom(session); - return -1; - } - - SAFE_FREE(session->opts.username); - session->opts.username = strdup(q); - SAFE_FREE(q); - if (session->opts.username == NULL) { - ssh_set_error_oom(session); - return -1; - } - } else { - session->opts.host = q; - } - } - break; - case SSH_OPTIONS_PORT: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - int *x = (int *) value; - if (*x <= 0) { - ssh_set_error_invalid(session); - return -1; - } - - session->opts.port = *x & 0xffff; - } - break; - case SSH_OPTIONS_PORT_STR: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - q = strdup(v); - if (q == NULL) { - ssh_set_error_oom(session); - return -1; - } - i = strtol(q, &p, 10); - if (q == p) { - SAFE_FREE(q); - } - SAFE_FREE(q); - if (i <= 0) { - ssh_set_error_invalid(session); - return -1; - } - - session->opts.port = i & 0xffff; - } - break; - case SSH_OPTIONS_FD: - if (value == NULL) { - session->opts.fd = SSH_INVALID_SOCKET; - ssh_set_error_invalid(session); - return -1; - } else { - socket_t *x = (socket_t *) value; - if (*x < 0) { - session->opts.fd = SSH_INVALID_SOCKET; - ssh_set_error_invalid(session); - return -1; - } - - session->opts.fd = *x & 0xffff; - } - break; - case SSH_OPTIONS_BINDADDR: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } - - q = strdup(v); - if (q == NULL) { - return -1; - } - SAFE_FREE(session->opts.bindaddr); - session->opts.bindaddr = q; - break; - case SSH_OPTIONS_USER: - v = value; - SAFE_FREE(session->opts.username); - if (v == NULL) { - q = ssh_get_local_username(); - if (q == NULL) { - ssh_set_error_oom(session); - return -1; - } - session->opts.username = q; - } else if (v[0] == '\0') { - ssh_set_error_oom(session); - return -1; - } else { /* username provided */ - session->opts.username = strdup(value); - if (session->opts.username == NULL) { - ssh_set_error_oom(session); - return -1; - } - } - break; - case SSH_OPTIONS_SSH_DIR: - v = value; - SAFE_FREE(session->opts.sshdir); - if (v == NULL) { - session->opts.sshdir = ssh_path_expand_tilde("~/.ssh"); - if (session->opts.sshdir == NULL) { - return -1; - } - } else if (v[0] == '\0') { - ssh_set_error_oom(session); - return -1; - } else { - session->opts.sshdir = ssh_path_expand_tilde(v); - if (session->opts.sshdir == NULL) { - ssh_set_error_oom(session); - return -1; - } - } - break; - case SSH_OPTIONS_IDENTITY: - case SSH_OPTIONS_ADD_IDENTITY: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } - q = strdup(v); - if (q == NULL) { - return -1; - } - rc = ssh_list_prepend(session->opts.identity, q); - if (rc < 0) { - free(q); - return -1; - } - break; - case SSH_OPTIONS_KNOWNHOSTS: - v = value; - SAFE_FREE(session->opts.knownhosts); - if (v == NULL) { - session->opts.knownhosts = ssh_path_expand_escape(session, - "%d/known_hosts"); - if (session->opts.knownhosts == NULL) { - ssh_set_error_oom(session); - return -1; - } - } else if (v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - session->opts.knownhosts = strdup(v); - if (session->opts.knownhosts == NULL) { - ssh_set_error_oom(session); - return -1; - } - } - break; - case SSH_OPTIONS_TIMEOUT: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - long *x = (long *) value; - if (*x < 0) { - ssh_set_error_invalid(session); - return -1; - } - - session->opts.timeout = *x & 0xffffffff; - } - break; - case SSH_OPTIONS_TIMEOUT_USEC: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - long *x = (long *) value; - if (*x < 0) { - ssh_set_error_invalid(session); - return -1; - } - - session->opts.timeout_usec = *x & 0xffffffff; - } - break; - case SSH_OPTIONS_SSH1: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - int *x = (int *) value; - if (*x < 0) { - ssh_set_error_invalid(session); - return -1; - } - - session->opts.ssh1 = *x; - } - break; - case SSH_OPTIONS_SSH2: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - int *x = (int *) value; - if (*x < 0) { - ssh_set_error_invalid(session); - return -1; - } - - session->opts.ssh2 = *x & 0xffff; - } - break; - case SSH_OPTIONS_LOG_VERBOSITY: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - int *x = (int *) value; - if (*x < 0) { - ssh_set_error_invalid(session); - return -1; - } - - session->common.log_verbosity = *x & 0xffff; - ssh_set_log_level(*x & 0xffff); - } - break; - case SSH_OPTIONS_LOG_VERBOSITY_STR: - v = value; - if (v == NULL || v[0] == '\0') { - session->common.log_verbosity = 0; - ssh_set_error_invalid(session); - return -1; - } else { - q = strdup(v); - if (q == NULL) { - ssh_set_error_oom(session); - return -1; - } - i = strtol(q, &p, 10); - if (q == p) { - SAFE_FREE(q); - } - SAFE_FREE(q); - if (i < 0) { - ssh_set_error_invalid(session); - return -1; - } - - session->common.log_verbosity = i & 0xffff; - ssh_set_log_level(i & 0xffff); - } - break; - case SSH_OPTIONS_CIPHERS_C_S: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - if (ssh_options_set_algo(session, SSH_CRYPT_C_S, v) < 0) - return -1; - } - break; - case SSH_OPTIONS_CIPHERS_S_C: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - if (ssh_options_set_algo(session, SSH_CRYPT_S_C, v) < 0) - return -1; - } - break; - case SSH_OPTIONS_KEY_EXCHANGE: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - if (ssh_options_set_algo(session, SSH_KEX, v) < 0) - return -1; - } - break; - case SSH_OPTIONS_HOSTKEYS: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - if (ssh_options_set_algo(session, SSH_HOSTKEYS, v) < 0) - return -1; - } - break; - case SSH_OPTIONS_HMAC_C_S: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - if (ssh_options_set_algo(session, SSH_MAC_C_S, v) < 0) - return -1; - } - break; - case SSH_OPTIONS_HMAC_S_C: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - if (ssh_options_set_algo(session, SSH_MAC_S_C, v) < 0) - return -1; - } - break; - case SSH_OPTIONS_COMPRESSION_C_S: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - if (strcasecmp(value,"yes")==0){ - if(ssh_options_set_algo(session,SSH_COMP_C_S,"zlib@openssh.com,zlib") < 0) - return -1; - } else if (strcasecmp(value,"no")==0){ - if(ssh_options_set_algo(session,SSH_COMP_C_S,"none") < 0) - return -1; - } else { - if (ssh_options_set_algo(session, SSH_COMP_C_S, v) < 0) - return -1; - } - } - break; - case SSH_OPTIONS_COMPRESSION_S_C: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - if (strcasecmp(value,"yes")==0){ - if(ssh_options_set_algo(session,SSH_COMP_S_C,"zlib@openssh.com,zlib") < 0) - return -1; - } else if (strcasecmp(value,"no")==0){ - if(ssh_options_set_algo(session,SSH_COMP_S_C,"none") < 0) - return -1; - } else { - if (ssh_options_set_algo(session, SSH_COMP_S_C, v) < 0) - return -1; - } - } - break; - case SSH_OPTIONS_COMPRESSION: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } - if(ssh_options_set(session,SSH_OPTIONS_COMPRESSION_C_S, v) < 0) - return -1; - if(ssh_options_set(session,SSH_OPTIONS_COMPRESSION_S_C, v) < 0) - return -1; - break; - case SSH_OPTIONS_COMPRESSION_LEVEL: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - int *x = (int *)value; - if (*x < 1 || *x > 9) { - ssh_set_error_invalid(session); - return -1; - } - session->opts.compressionlevel = *x & 0xff; - } - break; - case SSH_OPTIONS_STRICTHOSTKEYCHECK: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - int *x = (int *) value; - - session->opts.StrictHostKeyChecking = (*x & 0xff) > 0 ? 1 : 0; - } - session->opts.StrictHostKeyChecking = *(int*)value; - break; - case SSH_OPTIONS_PROXYCOMMAND: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - SAFE_FREE(session->opts.ProxyCommand); - /* Setting the command to 'none' disables this option. */ - rc = strcasecmp(v, "none"); - if (rc != 0) { - q = strdup(v); - if (q == NULL) { - return -1; - } - session->opts.ProxyCommand = q; - } - } - break; - case SSH_OPTIONS_GSSAPI_SERVER_IDENTITY: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - SAFE_FREE(session->opts.gss_server_identity); - session->opts.gss_server_identity = strdup(v); - if (session->opts.gss_server_identity == NULL) { - ssh_set_error_oom(session); - return -1; - } - } - break; - case SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY: - v = value; - if (v == NULL || v[0] == '\0') { - ssh_set_error_invalid(session); - return -1; - } else { - SAFE_FREE(session->opts.gss_client_identity); - session->opts.gss_client_identity = strdup(v); - if (session->opts.gss_client_identity == NULL) { - ssh_set_error_oom(session); - return -1; - } - } - break; - case SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS: - if (value == NULL) { - ssh_set_error_invalid(session); - return -1; - } else { - int x = *(int *)value; - - session->opts.gss_delegate_creds = (x & 0xff); - } - break; - - default: - ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); - return -1; - break; - } - - return 0; -} - -/** - * @brief This function can get ssh the ssh port. It must only be used on - * a valid ssh session. This function is useful when the session - * options have been automatically inferred from the environment - * or configuration files and one - * - * @param session An allocated SSH session structure. - * - * @param port_target An unsigned integer into which the - * port will be set from the ssh session. - * - * @return 0 on success, < 0 on error. - * - */ -int ssh_options_get_port(ssh_session session, unsigned int* port_target) { - if (session == NULL) { - return -1; - } - if (!session->opts.port) { - ssh_set_error_invalid(session); - return -1; - } - *port_target = session->opts.port; - return 0; -} - -/** - * @brief This function can get ssh options, it does not support all options provided for - * ssh options set, but mostly those which a user-space program may care about having - * trusted the ssh driver to infer these values from underlaying configuration files. - * It operates only on those SSH_OPTIONS_* which return char*. If you wish to receive - * the port then please use ssh_options_get_port() which returns an unsigned int. - * - * @param session An allocated SSH session structure. - * - * @param type The option type to get. This could be one of the - * following: - * - * - SSH_OPTIONS_HOST: - * The hostname or ip address to connect to (const char *). - * - * - SSH_OPTIONS_USER: - * The username for authentication (const char *).\n - * \n when not explicitly set this will be inferred from the - * ~/.ssh/config file. - * - * - SSH_OPTIONS_IDENTITY: - * Set the identity file name (const char *,format string).\n - * \n - * By default identity, id_dsa and id_rsa are checked.\n - * \n - * The identity file used authenticate with public key. - * It may include "%s" which will be replaced by the - * user home directory. - * - * - SSH_OPTIONS_PROXYCOMMAND: - * Get the proxycommand necessary to log into the - * remote host. When not explicitly set, it will be read - * from the ~/.ssh/config file. - * - * @param value The value to get into. As a char**, space will be - * allocated by the function for the value, it is - * your responsibility to free the memory using - * ssh_string_free_char(). - * - * @return SSH_OK on success, SSH_ERROR on error. - */ -int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value) -{ - char* src = NULL; - - if (session == NULL) { - return SSH_ERROR; - } - - if (value == NULL) { - ssh_set_error_invalid(session); - return SSH_ERROR; - } - - switch(type) - { - case SSH_OPTIONS_HOST: { - src = session->opts.host; - break; - } - case SSH_OPTIONS_USER: { - src = session->opts.username; - break; - } - case SSH_OPTIONS_IDENTITY: { - struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity); - if (it == NULL) { - return SSH_ERROR; - } - src = ssh_iterator_value(char *, it); - break; - } - case SSH_OPTIONS_PROXYCOMMAND: { - src = session->opts.ProxyCommand; - break; - } - default: - ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); - return SSH_ERROR; - break; - } - if (src == NULL) { - return SSH_ERROR; - } - *value = strdup(src); - if (*value == NULL) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - return SSH_OK; -} - -/** - * @brief Parse command line arguments. - * - * This is a helper for your application to generate the appropriate - * options from the command line arguments.\n - * The argv array and argc value are changed so that the parsed - * arguments wont appear anymore in them.\n - * The single arguments (without switches) are not parsed. thus, - * myssh -l user localhost\n - * The command wont set the hostname value of options to localhost. - * - * @param session The session to configure. - * - * @param argcptr The pointer to the argument count. - * - * @param argv The arguments list pointer. - * - * @returns 0 on success, < 0 on error. - * - * @see ssh_session_new() - */ -int ssh_options_getopt(ssh_session session, int *argcptr, char **argv) { - char *user = NULL; - char *cipher = NULL; - char *identity = NULL; - char *port = NULL; - char **save = NULL; - char **tmp = NULL; - int i = 0; - int argc = *argcptr; - int debuglevel = 0; - int usersa = 0; - int usedss = 0; - int compress = 0; - int cont = 1; - int current = 0; -#ifdef WITH_SSH1 - int ssh1 = 1; -#else - int ssh1 = 0; -#endif - int ssh2 = 1; -#ifdef _MSC_VER - /* Not supported with a Microsoft compiler */ - return -1; -#else - int saveoptind = optind; /* need to save 'em */ - int saveopterr = opterr; - - opterr = 0; /* shut up getopt */ - while(cont && ((i = getopt(argc, argv, "c:i:Cl:p:vb:rd12")) != -1)) { - switch(i) { - case 'l': - user = optarg; - break; - case 'p': - port = optarg; - break; - case 'v': - debuglevel++; - break; - case 'r': - usersa++; - break; - case 'd': - usedss++; - break; - case 'c': - cipher = optarg; - break; - case 'i': - identity = optarg; - break; - case 'C': - compress++; - break; - case '2': - ssh2 = 1; - ssh1 = 0; - break; - case '1': - ssh2 = 0; - ssh1 = 1; - break; - default: - { - char opt[3]="- "; - opt[1] = optopt; - tmp = realloc(save, (current + 1) * sizeof(char*)); - if (tmp == NULL) { - SAFE_FREE(save); - ssh_set_error_oom(session); - return -1; - } - save = tmp; - save[current] = strdup(opt); - if (save[current] == NULL) { - SAFE_FREE(save); - ssh_set_error_oom(session); - return -1; - } - current++; - if (optarg) { - save[current++] = argv[optind + 1]; - } - } - } /* switch */ - } /* while */ - opterr = saveopterr; - tmp = realloc(save, (current + (argc - optind)) * sizeof(char*)); - if (tmp == NULL) { - SAFE_FREE(save); - ssh_set_error_oom(session); - return -1; - } - save = tmp; - while (optind < argc) { - tmp = realloc(save, (current + 1) * sizeof(char*)); - if (tmp == NULL) { - SAFE_FREE(save); - ssh_set_error_oom(session); - return -1; - } - save = tmp; - save[current] = argv[optind]; - current++; - optind++; - } - - if (usersa && usedss) { - ssh_set_error(session, SSH_FATAL, "Either RSA or DSS must be chosen"); - cont = 0; - } - - ssh_set_log_level(debuglevel); - - optind = saveoptind; - - if(!cont) { - SAFE_FREE(save); - return -1; - } - - /* first recopy the save vector into the original's */ - for (i = 0; i < current; i++) { - /* don't erase argv[0] */ - argv[ i + 1] = save[i]; - } - argv[current + 1] = NULL; - *argcptr = current + 1; - SAFE_FREE(save); - - /* set a new option struct */ - if (compress) { - if (ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes") < 0) { - cont = 0; - } - } - - if (cont && cipher) { - if (ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher) < 0) { - cont = 0; - } - if (cont && ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher) < 0) { - cont = 0; - } - } - - if (cont && user) { - if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) { - cont = 0; - } - } - - if (cont && identity) { - if (ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity) < 0) { - cont = 0; - } - } - - ssh_options_set(session, SSH_OPTIONS_PORT_STR, port); - - ssh_options_set(session, SSH_OPTIONS_SSH1, &ssh1); - ssh_options_set(session, SSH_OPTIONS_SSH2, &ssh2); - - if (!cont) { - return SSH_ERROR; - } - - return SSH_OK; -#endif -} - -/** - * @brief Parse the ssh config file. - * - * This should be the last call of all options, it may overwrite options which - * are already set. It requires that the host name is already set with - * ssh_options_set_host(). - * - * @param session SSH session handle - * - * @param filename The options file to use, if NULL the default - * ~/.ssh/config will be used. - * - * @return 0 on success, < 0 on error. - * - * @see ssh_options_set_host() - */ -int ssh_options_parse_config(ssh_session session, const char *filename) { - char *expanded_filename; - int r; - - if (session == NULL) { - return -1; - } - if (session->opts.host == NULL) { - ssh_set_error_invalid(session); - return -1; - } - - if (session->opts.sshdir == NULL) { - r = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL); - if (r < 0) { - ssh_set_error_oom(session); - return -1; - } - } - - /* set default filename */ - if (filename == NULL) { - expanded_filename = ssh_path_expand_escape(session, "%d/config"); - } else { - expanded_filename = ssh_path_expand_escape(session, filename); - } - if (expanded_filename == NULL) { - return -1; - } - - r = ssh_config_parse_file(session, expanded_filename); - if (r < 0) { - goto out; - } - if (filename == NULL) { - r = ssh_config_parse_file(session, "/etc/ssh/ssh_config"); - } - -out: - free(expanded_filename); - return r; -} - -int ssh_options_apply(ssh_session session) { - struct ssh_iterator *it; - char *tmp; - int rc; - - if (session->opts.sshdir == NULL) { - rc = ssh_options_set(session, SSH_OPTIONS_SSH_DIR, NULL); - if (rc < 0) { - return -1; - } - } - - if (session->opts.username == NULL) { - rc = ssh_options_set(session, SSH_OPTIONS_USER, NULL); - if (rc < 0) { - return -1; - } - } - - if (session->opts.knownhosts == NULL) { - tmp = ssh_path_expand_escape(session, "%d/known_hosts"); - } else { - tmp = ssh_path_expand_escape(session, session->opts.knownhosts); - } - if (tmp == NULL) { - return -1; - } - free(session->opts.knownhosts); - session->opts.knownhosts = tmp; - - if (session->opts.ProxyCommand != NULL) { - tmp = ssh_path_expand_escape(session, session->opts.ProxyCommand); - if (tmp == NULL) { - return -1; - } - free(session->opts.ProxyCommand); - session->opts.ProxyCommand = tmp; - } - - for (it = ssh_list_get_iterator(session->opts.identity); - it != NULL; - it = it->next) { - char *id = (char *) it->data; - tmp = ssh_path_expand_escape(session, id); - if (tmp == NULL) { - return -1; - } - free(id); - it->data = tmp; - } - - return 0; -} - -/** @} */ - -#ifdef WITH_SERVER -/** - * @addtogroup libssh_server - * @{ - */ -static int ssh_bind_set_key(ssh_bind sshbind, char **key_loc, - const void *value) { - if (value == NULL) { - ssh_set_error_invalid(sshbind); - return -1; - } else { - SAFE_FREE(*key_loc); - *key_loc = strdup(value); - if (*key_loc == NULL) { - ssh_set_error_oom(sshbind); - return -1; - } - } - return 0; -} - -/** - * @brief Set options for an SSH server bind. - * - * @param sshbind The ssh server bind to configure. - * - * @param type The option type to set. This should be one of the - * following: - * - * - SSH_BIND_OPTIONS_HOSTKEY: - * Set the path to an ssh host key, regardless - * of type. Only one key from per key type - * (RSA, DSA, ECDSA) is allowed in an ssh_bind - * at a time, and later calls to this function - * with this option for the same key type will - * override prior calls (const char *). - * - * - SSH_BIND_OPTIONS_BINDADDR: - * Set the IP address to bind (const char *). - * - * - SSH_BIND_OPTIONS_BINDPORT: - * Set the port to bind (unsigned int *). - * - * - SSH_BIND_OPTIONS_BINDPORT_STR: - * Set the port to bind (const char *). - * - * - SSH_BIND_OPTIONS_LOG_VERBOSITY: - * Set the session logging verbosity (int *). - * The logging verbosity should have one of the - * following values, which are listed in order - * of increasing verbosity. Every log message - * with verbosity less than or equal to the - * logging verbosity will be shown. - * - SSH_LOG_NOLOG: No logging - * - SSH_LOG_RARE: Rare conditions or warnings - * - SSH_LOG_ENTRY: API-accessible entrypoints - * - SSH_LOG_PACKET: Packet id and size - * - SSH_LOG_FUNCTIONS: Function entering and leaving - * - * - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: - * Set the session logging verbosity via a - * string that will be converted to a numerical - * value (e.g. "3") and interpreted according - * to the values of - * SSH_BIND_OPTIONS_LOG_VERBOSITY above (const - * char *). - * - * - SSH_BIND_OPTIONS_DSAKEY: - * Set the path to the ssh host dsa key, SSHv2 - * only (const char *). - * - * - SSH_BIND_OPTIONS_RSAKEY: - * Set the path to the ssh host rsa key, SSHv2 - * only (const char *). - * - * - SSH_BIND_OPTIONS_ECDSAKEY: - * Set the path to the ssh host ecdsa key, - * SSHv2 only (const char *). - * - * - SSH_BIND_OPTIONS_BANNER: - * Set the server banner sent to clients (const char *). - * - * @param value The value to set. This is a generic pointer and the - * datatype which should be used is described at the - * corresponding value of type above. - * - * @return 0 on success, < 0 on error, invalid option, or parameter. - */ -int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type, - const void *value) { - char *p, *q; - int i, rc; - - if (sshbind == NULL) { - return -1; - } - - switch (type) { - case SSH_BIND_OPTIONS_HOSTKEY: - if (value == NULL) { - ssh_set_error_invalid(sshbind); - return -1; - } else { - int key_type; - ssh_key key; - ssh_key *bind_key_loc = NULL; - char **bind_key_path_loc; - - rc = ssh_pki_import_privkey_file(value, NULL, NULL, NULL, &key); - if (rc != SSH_OK) { - return -1; - } - key_type = ssh_key_type(key); - switch (key_type) { - case SSH_KEYTYPE_DSS: - bind_key_loc = &sshbind->dsa; - bind_key_path_loc = &sshbind->dsakey; - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_ECC - bind_key_loc = &sshbind->ecdsa; - bind_key_path_loc = &sshbind->ecdsakey; -#else - ssh_set_error(sshbind, - SSH_FATAL, - "ECDSA key used and libssh compiled " - "without ECDSA support"); -#endif - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - bind_key_loc = &sshbind->rsa; - bind_key_path_loc = &sshbind->rsakey; - break; - default: - ssh_set_error(sshbind, - SSH_FATAL, - "Unsupported key type %d", key_type); - } - - if (bind_key_loc == NULL) { - ssh_key_free(key); - return -1; - } - - /* Set the location of the key on disk even though we don't - need it in case some other function wants it */ - rc = ssh_bind_set_key(sshbind, bind_key_path_loc, value); - if (rc < 0) { - ssh_key_free(key); - return -1; - } - ssh_key_free(*bind_key_loc); - *bind_key_loc = key; - } - break; - case SSH_BIND_OPTIONS_BINDADDR: - if (value == NULL) { - ssh_set_error_invalid(sshbind); - return -1; - } else { - SAFE_FREE(sshbind->bindaddr); - sshbind->bindaddr = strdup(value); - if (sshbind->bindaddr == NULL) { - ssh_set_error_oom(sshbind); - return -1; - } - } - break; - case SSH_BIND_OPTIONS_BINDPORT: - if (value == NULL) { - ssh_set_error_invalid(sshbind); - return -1; - } else { - int *x = (int *) value; - sshbind->bindport = *x & 0xffff; - } - break; - case SSH_BIND_OPTIONS_BINDPORT_STR: - if (value == NULL) { - sshbind->bindport = 22 & 0xffff; - } else { - q = strdup(value); - if (q == NULL) { - ssh_set_error_oom(sshbind); - return -1; - } - i = strtol(q, &p, 10); - if (q == p) { - SAFE_FREE(q); - } - SAFE_FREE(q); - - sshbind->bindport = i & 0xffff; - } - break; - case SSH_BIND_OPTIONS_LOG_VERBOSITY: - if (value == NULL) { - ssh_set_error_invalid(sshbind); - return -1; - } else { - int *x = (int *) value; - ssh_set_log_level(*x & 0xffff); - } - break; - case SSH_BIND_OPTIONS_LOG_VERBOSITY_STR: - if (value == NULL) { - ssh_set_log_level(0); - } else { - q = strdup(value); - if (q == NULL) { - ssh_set_error_oom(sshbind); - return -1; - } - i = strtol(q, &p, 10); - if (q == p) { - SAFE_FREE(q); - } - SAFE_FREE(q); - - ssh_set_log_level(i & 0xffff); - } - break; - case SSH_BIND_OPTIONS_DSAKEY: - rc = ssh_bind_set_key(sshbind, &sshbind->dsakey, value); - if (rc < 0) { - return -1; - } - break; - case SSH_BIND_OPTIONS_RSAKEY: - rc = ssh_bind_set_key(sshbind, &sshbind->rsakey, value); - if (rc < 0) { - return -1; - } - break; - case SSH_BIND_OPTIONS_ECDSAKEY: - rc = ssh_bind_set_key(sshbind, &sshbind->ecdsakey, value); - if (rc < 0) { - return -1; - } - break; - case SSH_BIND_OPTIONS_BANNER: - if (value == NULL) { - ssh_set_error_invalid(sshbind); - return -1; - } else { - SAFE_FREE(sshbind->banner); - sshbind->banner = strdup(value); - if (sshbind->banner == NULL) { - ssh_set_error_oom(sshbind); - return -1; - } - } - break; - default: - ssh_set_error(sshbind, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); - return -1; - break; - } - - return 0; -} -#endif - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/packet.c b/libssh/src/packet.c deleted file mode 100644 index d16cd165..00000000 --- a/libssh/src/packet.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - * packet.c - packet building functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/priv.h" -#include "libssh/ssh2.h" -#include "libssh/crypto.h" -#include "libssh/buffer.h" -#include "libssh/packet.h" -#include "libssh/socket.h" -#include "libssh/channels.h" -#include "libssh/misc.h" -#include "libssh/session.h" -#include "libssh/messages.h" -#include "libssh/pcap.h" -#include "libssh/kex.h" -#include "libssh/auth.h" -#include "libssh/gssapi.h" - -static ssh_packet_callback default_packet_handlers[]= { - ssh_packet_disconnect_callback, // SSH2_MSG_DISCONNECT 1 - ssh_packet_ignore_callback, // SSH2_MSG_IGNORE 2 - ssh_packet_unimplemented, // SSH2_MSG_UNIMPLEMENTED 3 - ssh_packet_ignore_callback, // SSH2_MSG_DEBUG 4 -#if WITH_SERVER - ssh_packet_service_request, // SSH2_MSG_SERVICE_REQUEST 5 -#else - NULL, -#endif - ssh_packet_service_accept, // SSH2_MSG_SERVICE_ACCEPT 6 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, // 7-19 - ssh_packet_kexinit, // SSH2_MSG_KEXINIT 20 - ssh_packet_newkeys, // SSH2_MSG_NEWKEYS 21 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, // 22-29 -#if WITH_SERVER - ssh_packet_kexdh_init, // SSH2_MSG_KEXDH_INIT 30 - // SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 -#else - NULL, -#endif - ssh_packet_dh_reply, // SSH2_MSG_KEXDH_REPLY 31 - // SSH2_MSG_KEX_DH_GEX_GROUP 31 - NULL, // SSH2_MSG_KEX_DH_GEX_INIT 32 - NULL, // SSH2_MSG_KEX_DH_GEX_REPLY 33 - NULL, // SSH2_MSG_KEX_DH_GEX_REQUEST 34 - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, // 35-49 -#if WITH_SERVER - ssh_packet_userauth_request, // SSH2_MSG_USERAUTH_REQUEST 50 -#else - NULL, -#endif - ssh_packet_userauth_failure, // SSH2_MSG_USERAUTH_FAILURE 51 - ssh_packet_userauth_success, // SSH2_MSG_USERAUTH_SUCCESS 52 - ssh_packet_userauth_banner, // SSH2_MSG_USERAUTH_BANNER 53 - NULL,NULL,NULL,NULL,NULL,NULL, // 54-59 - ssh_packet_userauth_pk_ok, // SSH2_MSG_USERAUTH_PK_OK 60 - // SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 - // SSH2_MSG_USERAUTH_INFO_REQUEST 60 - // SSH2_MSG_USERAUTH_GSSAPI_RESPONSE 60 - ssh_packet_userauth_info_response, // SSH2_MSG_USERAUTH_INFO_RESPONSE 61 - // SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 - NULL, // 62 - NULL, // SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 - NULL, // SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 - NULL, // SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 -#if defined(WITH_GSSAPI) && defined(WITH_SERVER) - ssh_packet_userauth_gssapi_mic, // SSH2_MSG_USERAUTH_GSSAPI_MIC 66 -#else /* WITH_GSSAPI && WITH_SERVER */ - NULL, -#endif /* WITH_GSSAPI && WITH_SERVER */ - NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, // 67-79 -#ifdef WITH_SERVER - ssh_packet_global_request, // SSH2_MSG_GLOBAL_REQUEST 80 -#else /* WITH_SERVER */ - NULL, -#endif /* WITH_SERVER */ - ssh_request_success, // SSH2_MSG_REQUEST_SUCCESS 81 - ssh_request_denied, // SSH2_MSG_REQUEST_FAILURE 82 - NULL, NULL, NULL, NULL, NULL, NULL, NULL,// 83-89 - ssh_packet_channel_open, // SSH2_MSG_CHANNEL_OPEN 90 - ssh_packet_channel_open_conf, // SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 - ssh_packet_channel_open_fail, // SSH2_MSG_CHANNEL_OPEN_FAILURE 92 - channel_rcv_change_window, // SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 - channel_rcv_data, // SSH2_MSG_CHANNEL_DATA 94 - channel_rcv_data, // SSH2_MSG_CHANNEL_EXTENDED_DATA 95 - channel_rcv_eof, // SSH2_MSG_CHANNEL_EOF 96 - channel_rcv_close, // SSH2_MSG_CHANNEL_CLOSE 97 - channel_rcv_request, // SSH2_MSG_CHANNEL_REQUEST 98 - ssh_packet_channel_success, // SSH2_MSG_CHANNEL_SUCCESS 99 - ssh_packet_channel_failure, // SSH2_MSG_CHANNEL_FAILURE 100 -}; - -/* in nonblocking mode, socket_read will read as much as it can, and return */ -/* SSH_OK if it has read at least len bytes, otherwise, SSH_AGAIN. */ -/* in blocking mode, it will read at least len bytes and will block until it's ok. */ - -/** @internal - * @handles a data received event. It then calls the handlers for the different packet types - * or and exception handler callback. - * @param user pointer to current ssh_session - * @param data pointer to the data received - * @len length of data received. It might not be enough for a complete packet - * @returns number of bytes read and processed. - */ -int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) -{ - ssh_session session= (ssh_session) user; - unsigned int blocksize = (session->current_crypto ? - session->current_crypto->in_cipher->blocksize : 8); - unsigned char mac[DIGEST_MAX_LEN] = {0}; - char buffer[16] = {0}; - size_t current_macsize = 0; - const uint8_t *packet; - int to_be_read; - int rc; - uint32_t len, compsize, payloadsize; - uint8_t padding; - size_t processed = 0; /* number of byte processed from the callback */ - - if(session->current_crypto != NULL) { - current_macsize = hmac_digest_len(session->current_crypto->in_hmac); - } - - if (data == NULL) { - goto error; - } - - if (session->session_state == SSH_SESSION_STATE_ERROR) { - goto error; - } - - switch(session->packet_state) { - case PACKET_STATE_INIT: - if (receivedlen < blocksize) { - /* - * We didn't receive enough data to read at least one - * block size, give up - */ - return 0; - } - - memset(&session->in_packet, 0, sizeof(PACKET)); - - if (session->in_buffer) { - rc = ssh_buffer_reinit(session->in_buffer); - if (rc < 0) { - goto error; - } - } else { - session->in_buffer = ssh_buffer_new(); - if (session->in_buffer == NULL) { - goto error; - } - } - - memcpy(buffer, data, blocksize); - processed += blocksize; - len = packet_decrypt_len(session, buffer); - - rc = ssh_buffer_add_data(session->in_buffer, buffer, blocksize); - if (rc < 0) { - goto error; - } - - if (len > MAX_PACKET_LEN) { - ssh_set_error(session, - SSH_FATAL, - "read_packet(): Packet len too high(%u %.4x)", - len, len); - goto error; - } - - to_be_read = len - blocksize + sizeof(uint32_t); - if (to_be_read < 0) { - /* remote sshd sends invalid sizes? */ - ssh_set_error(session, - SSH_FATAL, - "Given numbers of bytes left to be read < 0 (%d)!", - to_be_read); - goto error; - } - - /* Saves the status of the current operations */ - session->in_packet.len = len; - session->packet_state = PACKET_STATE_SIZEREAD; - /* FALL TROUGH */ - case PACKET_STATE_SIZEREAD: - len = session->in_packet.len; - to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize; - /* if to_be_read is zero, the whole packet was blocksize bytes. */ - if (to_be_read != 0) { - if (receivedlen - processed < (unsigned int)to_be_read) { - /* give up, not enough data in buffer */ - SSH_LOG(SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len); - return processed; - } - - packet = ((uint8_t*)data) + processed; -#if 0 - ssh_socket_read(session->socket, - packet, - to_be_read - current_macsize); -#endif - - rc = ssh_buffer_add_data(session->in_buffer, - packet, - to_be_read - current_macsize); - if (rc < 0) { - goto error; - } - processed += to_be_read - current_macsize; - } - - if (session->current_crypto) { - /* - * Decrypt the rest of the packet (blocksize bytes already - * have been decrypted) - */ - uint32_t buffer_len = buffer_get_rest_len(session->in_buffer); - - /* The following check avoids decrypting zero bytes */ - if (buffer_len > blocksize) { - uint8_t *payload = ((uint8_t*)buffer_get_rest(session->in_buffer) + blocksize); - uint32_t plen = buffer_len - blocksize; - - rc = packet_decrypt(session, payload, plen); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "Decrypt error"); - goto error; - } - } - - /* copy the last part from the incoming buffer */ - packet = ((uint8_t *)data) + processed; - memcpy(mac, packet, current_macsize); - - rc = packet_hmac_verify(session, session->in_buffer, mac, session->current_crypto->in_hmac); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "HMAC error"); - goto error; - } - processed += current_macsize; - } - - /* skip the size field which has been processed before */ - buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); - - rc = buffer_get_u8(session->in_buffer, &padding); - if (rc == 0) { - ssh_set_error(session, - SSH_FATAL, - "Packet too short to read padding"); - goto error; - } - - if (padding > buffer_get_rest_len(session->in_buffer)) { - ssh_set_error(session, - SSH_FATAL, - "Invalid padding: %d (%d left)", - padding, - buffer_get_rest_len(session->in_buffer)); - goto error; - } - buffer_pass_bytes_end(session->in_buffer, padding); - compsize = buffer_get_rest_len(session->in_buffer); - -#ifdef WITH_ZLIB - if (session->current_crypto - && session->current_crypto->do_compress_in - && buffer_get_rest_len(session->in_buffer) > 0) { - rc = decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN); - if (rc < 0) { - goto error; - } - } -#endif /* WITH_ZLIB */ - payloadsize = buffer_get_rest_len(session->in_buffer); - session->recv_seq++; - if (session->raw_counter != NULL) { - session->raw_counter->in_bytes += payloadsize; - session->raw_counter->in_packets++; - } - - /* - * We don't want to rewrite a new packet while still executing the - * packet callbacks - */ - session->packet_state = PACKET_STATE_PROCESSING; - ssh_packet_parse_type(session); - SSH_LOG(SSH_LOG_PACKET, - "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", - session->in_packet.type, len, padding, compsize, payloadsize); - - /* Execute callbacks */ - ssh_packet_process(session, session->in_packet.type); - session->packet_state = PACKET_STATE_INIT; - if (processed < receivedlen) { - /* Handle a potential packet left in socket buffer */ - SSH_LOG(SSH_LOG_PACKET, - "Processing %" PRIdS " bytes left in socket buffer", - receivedlen-processed); - - packet = ((uint8_t*)data) + processed; - - rc = ssh_packet_socket_callback(packet, receivedlen - processed,user); - processed += rc; - } - - return processed; - case PACKET_STATE_PROCESSING: - SSH_LOG(SSH_LOG_PACKET, "Nested packet processing. Delaying."); - return 0; - } - - ssh_set_error(session, - SSH_FATAL, - "Invalid state into packet_read2(): %d", - session->packet_state); - -error: - session->session_state= SSH_SESSION_STATE_ERROR; - - return processed; -} - -void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){ - session->socket_callbacks.data=ssh_packet_socket_callback; - session->socket_callbacks.connected=NULL; - session->socket_callbacks.controlflow=NULL; - session->socket_callbacks.exception=NULL; - session->socket_callbacks.userdata=session; - ssh_socket_set_callbacks(s,&session->socket_callbacks); -} - -/** @internal - * @brief sets the callbacks for the packet layer - */ -void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks){ - if(session->packet_callbacks == NULL){ - session->packet_callbacks = ssh_list_new(); - } - if (session->packet_callbacks != NULL) { - ssh_list_append(session->packet_callbacks, callbacks); - } -} - -/** @internal - * @brief sets the default packet handlers - */ -void ssh_packet_set_default_callbacks(ssh_session session){ -#ifdef WITH_SSH1 - if(session->version==1){ - ssh_packet_set_default_callbacks1(session); - return; - } -#endif - session->default_packet_callbacks.start=1; - session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers)/sizeof(ssh_packet_callback); - session->default_packet_callbacks.user=session; - session->default_packet_callbacks.callbacks=default_packet_handlers; - ssh_packet_set_callbacks(session, &session->default_packet_callbacks); -} - -/** @internal - * @brief dispatch the call of packet handlers callbacks for a received packet - * @param type type of packet - */ -void ssh_packet_process(ssh_session session, uint8_t type){ - struct ssh_iterator *i; - int r=SSH_PACKET_NOT_USED; - ssh_packet_callbacks cb; - - SSH_LOG(SSH_LOG_PACKET, "Dispatching handler for packet type %d",type); - if(session->packet_callbacks == NULL){ - SSH_LOG(SSH_LOG_RARE,"Packet callback is not initialized !"); - - return; - } - i=ssh_list_get_iterator(session->packet_callbacks); - while(i != NULL){ - cb=ssh_iterator_value(ssh_packet_callbacks,i); - i=i->next; - if(!cb) - continue; - if(cb->start > type) - continue; - if(cb->start + cb->n_callbacks <= type) - continue; - if(cb->callbacks[type - cb->start]==NULL) - continue; - r=cb->callbacks[type - cb->start](session,type,session->in_buffer,cb->user); - if(r==SSH_PACKET_USED) - break; - } - if(r==SSH_PACKET_NOT_USED){ - SSH_LOG(SSH_LOG_RARE,"Couldn't do anything with packet type %d",type); - ssh_packet_send_unimplemented(session, session->recv_seq-1); - } -} - -/** @internal - * @brief sends a SSH_MSG_UNIMPLEMENTED answer to an unhandled packet - * @param session the SSH session - * @param seqnum the sequence number of the unknown packet - * @return SSH_ERROR on error, else SSH_OK - */ -int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum){ - int rc; - - rc = ssh_buffer_pack(session->out_buffer, - "bd", - SSH2_MSG_UNIMPLEMENTED, - seqnum); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - rc = packet_send(session); - - return rc; -} - -/** @internal - * @brief handles a SSH_MSG_UNIMPLEMENTED packet - */ -SSH_PACKET_CALLBACK(ssh_packet_unimplemented){ - uint32_t seq; - int rc; - - (void)session; /* unused */ - (void)type; - (void)user; - - rc = ssh_buffer_unpack(packet, "d", &seq); - if (rc != SSH_OK) { - SSH_LOG(SSH_LOG_WARNING, - "Could not unpack SSH_MSG_UNIMPLEMENTED packet"); - } - - SSH_LOG(SSH_LOG_RARE, - "Received SSH_MSG_UNIMPLEMENTED (sequence number %d)",seq); - - return SSH_PACKET_USED; -} - -/** @internal - * @parse the "Type" header field of a packet and updates the session - */ -int ssh_packet_parse_type(ssh_session session) { - memset(&session->in_packet, 0, sizeof(PACKET)); - if(session->in_buffer == NULL) { - return SSH_ERROR; - } - - if(buffer_get_u8(session->in_buffer, &session->in_packet.type) == 0) { - ssh_set_error(session, SSH_FATAL, "Packet too short to read type"); - return SSH_ERROR; - } - - session->in_packet.valid = 1; - - return SSH_OK; -} - -/* - * This function places the outgoing packet buffer into an outgoing - * socket buffer - */ -static int ssh_packet_write(ssh_session session) { - int rc = SSH_ERROR; - - rc=ssh_socket_write(session->socket, - buffer_get_rest(session->out_buffer), - buffer_get_rest_len(session->out_buffer)); - - return rc; -} - -static int packet_send2(ssh_session session) { - unsigned int blocksize = (session->current_crypto ? - session->current_crypto->out_cipher->blocksize : 8); - enum ssh_hmac_e hmac_type = (session->current_crypto ? - session->current_crypto->out_hmac : session->next_crypto->out_hmac); - uint32_t currentlen = buffer_get_rest_len(session->out_buffer); - unsigned char *hmac = NULL; - char padstring[32] = { 0 }; - int rc = SSH_ERROR; - uint32_t finallen,payloadsize,compsize; - uint8_t padding; - - uint8_t header[sizeof(padding) + sizeof(finallen)] = { 0 }; - - payloadsize = currentlen; -#ifdef WITH_ZLIB - if (session->current_crypto - && session->current_crypto->do_compress_out - && buffer_get_rest_len(session->out_buffer)) { - if (compress_buffer(session,session->out_buffer) < 0) { - goto error; - } - currentlen = buffer_get_rest_len(session->out_buffer); - } -#endif /* WITH_ZLIB */ - compsize = currentlen; - padding = (blocksize - ((currentlen +5) % blocksize)); - if(padding < 4) { - padding += blocksize; - } - - if (session->current_crypto) { - ssh_get_random(padstring, padding, 0); - } - - finallen = htonl(currentlen + padding + 1); - - memcpy(&header[0], &finallen, sizeof(finallen)); - header[sizeof(finallen)] = padding; - rc = buffer_prepend_data(session->out_buffer, &header, sizeof(header)); - if (rc < 0) { - goto error; - } - rc = ssh_buffer_add_data(session->out_buffer, padstring, padding); - if (rc < 0) { - goto error; - } -#ifdef WITH_PCAP - if(session->pcap_ctx){ - ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT, - buffer_get_rest(session->out_buffer),buffer_get_rest_len(session->out_buffer) - ,buffer_get_rest_len(session->out_buffer)); - } -#endif - hmac = packet_encrypt(session, buffer_get_rest(session->out_buffer), - buffer_get_rest_len(session->out_buffer)); - if (hmac) { - rc = ssh_buffer_add_data(session->out_buffer, hmac, hmac_digest_len(hmac_type)); - if (rc < 0) { - goto error; - } - } - - rc = ssh_packet_write(session); - session->send_seq++; - if (session->raw_counter != NULL) { - session->raw_counter->out_bytes += payloadsize; - session->raw_counter->out_packets++; - } - - SSH_LOG(SSH_LOG_PACKET, - "packet: wrote [len=%d,padding=%hhd,comp=%d,payload=%d]", - ntohl(finallen), padding, compsize, payloadsize); - if (ssh_buffer_reinit(session->out_buffer) < 0) { - rc = SSH_ERROR; - } -error: - - return rc; /* SSH_OK, AGAIN or ERROR */ -} - - -int packet_send(ssh_session session) { -#ifdef WITH_SSH1 - if (session->version == 1) { - return packet_send1(session); - } -#endif - return packet_send2(session); -} diff --git a/libssh/src/packet1.c b/libssh/src/packet1.c deleted file mode 100644 index eac70084..00000000 --- a/libssh/src/packet1.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include - -#ifndef _WIN32 -#include -#endif /* _WIN32 */ - -#include "libssh/priv.h" -#include "libssh/ssh1.h" -#include "libssh/crc32.h" -#include "libssh/packet.h" -#include "libssh/session.h" -#include "libssh/buffer.h" -#include "libssh/socket.h" -#include "libssh/kex.h" -#include "libssh/crypto.h" - -#ifdef WITH_SSH1 - -static ssh_packet_callback default_packet_handlers1[]= { - NULL, //SSH_MSG_NONE 0 - ssh_packet_disconnect1, //SSH_MSG_DISCONNECT 1 - ssh_packet_publickey1, //SSH_SMSG_PUBLIC_KEY 2 - NULL, //SSH_CMSG_SESSION_KEY 3 - NULL, //SSH_CMSG_USER 4 - NULL, //SSH_CMSG_AUTH_RHOSTS 5 - NULL, //SSH_CMSG_AUTH_RSA 6 - NULL, //SSH_SMSG_AUTH_RSA_CHALLENGE 7 - NULL, //SSH_CMSG_AUTH_RSA_RESPONSE 8 - NULL, //SSH_CMSG_AUTH_PASSWORD 9 - NULL, //SSH_CMSG_REQUEST_PTY 10 - NULL, //SSH_CMSG_WINDOW_SIZE 11 - NULL, //SSH_CMSG_EXEC_SHELL 12 - NULL, //SSH_CMSG_EXEC_CMD 13 - ssh_packet_smsg_success1, //SSH_SMSG_SUCCESS 14 - ssh_packet_smsg_failure1, //SSH_SMSG_FAILURE 15 - NULL, //SSH_CMSG_STDIN_DATA 16 - ssh_packet_data1, //SSH_SMSG_STDOUT_DATA 17 - ssh_packet_data1, //SSH_SMSG_STDERR_DATA 18 - NULL, //SSH_CMSG_EOF 19 - ssh_packet_exist_status1, //SSH_SMSG_EXITSTATUS 20 - NULL, //SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21 - NULL, //SSH_MSG_CHANNEL_OPEN_FAILURE 22 - NULL, //SSH_MSG_CHANNEL_DATA 23 - ssh_packet_close1, //SSH_MSG_CHANNEL_CLOSE 24 - NULL, //SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25 - NULL, //SSH_CMSG_X11_REQUEST_FORWARDING 26 - NULL, //SSH_SMSG_X11_OPEN 27 - NULL, //SSH_CMSG_PORT_FORWARD_REQUEST 28 - NULL, //SSH_MSG_PORT_OPEN 29 - NULL, //SSH_CMSG_AGENT_REQUEST_FORWARDING 30 - NULL, //SSH_SMSG_AGENT_OPEN 31 - ssh_packet_ignore_callback, //SSH_MSG_IGNORE 32 - NULL, //SSH_CMSG_EXIT_CONFIRMATION 33 - NULL, //SSH_CMSG_X11_REQUEST_FORWARDING 34 - NULL, //SSH_CMSG_AUTH_RHOSTS_RSA 35 - ssh_packet_ignore_callback, //SSH_MSG_DEBUG 36 -}; - -/** @internal - * @brief sets the default packet handlers - */ -void ssh_packet_set_default_callbacks1(ssh_session session){ - session->default_packet_callbacks.start=0; - session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers1)/sizeof(ssh_packet_callback); - session->default_packet_callbacks.user=session; - session->default_packet_callbacks.callbacks=default_packet_handlers1; - ssh_packet_set_callbacks(session, &session->default_packet_callbacks); -} - - -/** @internal - * @handles a data received event. It then calls the handlers for the different packet types - * or and exception handler callback. Adapted for SSH-1 packets. - * @param user pointer to current ssh_session - * @param data pointer to the data received - * @len length of data received. It might not be enough for a complete packet - * @returns number of bytes read and processed. - */ - -int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user) { - void *packet = NULL; - int to_be_read; - size_t processed=0; - uint32_t padding; - uint32_t crc; - uint32_t len, buffer_len; - ssh_session session=(ssh_session)user; - - switch (session->packet_state){ - case PACKET_STATE_INIT: - memset(&session->in_packet, 0, sizeof(PACKET)); - - if (session->in_buffer) { - if (ssh_buffer_reinit(session->in_buffer) < 0) { - goto error; - } - } else { - session->in_buffer = ssh_buffer_new(); - if (session->in_buffer == NULL) { - goto error; - } - } - /* must have at least enough bytes for size */ - if(receivedlen < sizeof(uint32_t)){ - return 0; - } - memcpy(&len,data,sizeof(uint32_t)); - processed += sizeof(uint32_t); - - /* len is not encrypted */ - len = ntohl(len); - if (len > MAX_PACKET_LEN) { - ssh_set_error(session, SSH_FATAL, - "read_packet(): Packet len too high (%u %.8x)", len, len); - goto error; - } - - SSH_LOG(SSH_LOG_PACKET, "Reading a %d bytes packet", len); - - session->in_packet.len = len; - session->packet_state = PACKET_STATE_SIZEREAD; - /* FALL THROUGH */ - case PACKET_STATE_SIZEREAD: - len = session->in_packet.len; - /* SSH-1 has a fixed padding lenght */ - padding = 8 - (len % 8); - to_be_read = len + padding; - if(to_be_read + processed > receivedlen){ - /* wait for rest of packet */ - return processed; - } - /* it is _not_ possible that to_be_read be < 8. */ - packet = (char *)data + processed; - - if (ssh_buffer_add_data(session->in_buffer,packet,to_be_read) < 0) { - goto error; - } - processed += to_be_read; -#ifdef DEBUG_CRYPTO - ssh_print_hexa("read packet:", ssh_buffer_get_begin(session->in_buffer), - ssh_buffer_get_len(session->in_buffer)); -#endif - if (session->current_crypto) { - /* - * We decrypt everything, missing the lenght part (which was - * previously read, unencrypted, and is not part of the buffer - */ - buffer_len = ssh_buffer_get_len(session->in_buffer); - if (buffer_len > 0) { - int rc; - rc = packet_decrypt(session, - ssh_buffer_get_begin(session->in_buffer), - buffer_len); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, "Packet decrypt error"); - goto error; - } - } - } -#ifdef DEBUG_CRYPTO - ssh_print_hexa("read packet decrypted:", ssh_buffer_get_begin(session->in_buffer), - ssh_buffer_get_len(session->in_buffer)); -#endif - SSH_LOG(SSH_LOG_PACKET, "%d bytes padding", padding); - if(((len + padding) != buffer_get_rest_len(session->in_buffer)) || - ((len + padding) < sizeof(uint32_t))) { - SSH_LOG(SSH_LOG_RARE, "no crc32 in packet"); - ssh_set_error(session, SSH_FATAL, "no crc32 in packet"); - goto error; - } - - memcpy(&crc, - (unsigned char *)buffer_get_rest(session->in_buffer) + (len+padding) - sizeof(uint32_t), - sizeof(uint32_t)); - buffer_pass_bytes_end(session->in_buffer, sizeof(uint32_t)); - crc = ntohl(crc); - if (ssh_crc32(buffer_get_rest(session->in_buffer), - (len + padding) - sizeof(uint32_t)) != crc) { -#ifdef DEBUG_CRYPTO - ssh_print_hexa("crc32 on",buffer_get_rest(session->in_buffer), - len + padding - sizeof(uint32_t)); -#endif - SSH_LOG(SSH_LOG_RARE, "Invalid crc32"); - ssh_set_error(session, SSH_FATAL, - "Invalid crc32: expected %.8x, got %.8x", - crc, - ssh_crc32(buffer_get_rest(session->in_buffer), - len + padding - sizeof(uint32_t))); - goto error; - } - /* pass the padding */ - buffer_pass_bytes(session->in_buffer, padding); - SSH_LOG(SSH_LOG_PACKET, "The packet is valid"); - -/* TODO FIXME -#ifdef WITH_ZLIB - if(session->current_crypto && session->current_crypto->do_compress_in){ - decompress_buffer(session,session->in_buffer); - } -#endif -*/ - session->recv_seq++; - /* We don't want to rewrite a new packet while still executing the packet callbacks */ - session->packet_state = PACKET_STATE_PROCESSING; - ssh_packet_parse_type(session); - /* execute callbacks */ - ssh_packet_process(session, session->in_packet.type); - session->packet_state = PACKET_STATE_INIT; - if(processed < receivedlen){ - int rc; - /* Handle a potential packet left in socket buffer */ - SSH_LOG(SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", - receivedlen-processed); - rc = ssh_packet_socket_callback1((char *)data + processed, - receivedlen - processed,user); - processed += rc; - } - - return processed; - case PACKET_STATE_PROCESSING: - SSH_LOG(SSH_LOG_PACKET, "Nested packet processing. Delaying."); - return 0; - } - -error: - session->session_state=SSH_SESSION_STATE_ERROR; - - return processed; -} - - -int packet_send1(ssh_session session) { - unsigned int blocksize = (session->current_crypto ? - session->current_crypto->out_cipher->blocksize : 8); - uint32_t currentlen = ssh_buffer_get_len(session->out_buffer) + sizeof(uint32_t); - char padstring[32] = {0}; - int rc = SSH_ERROR; - uint32_t finallen; - uint32_t crc; - uint8_t padding; - - SSH_LOG(SSH_LOG_PACKET,"Sending a %d bytes long packet",currentlen); - -/* TODO FIXME -#ifdef WITH_ZLIB - if (session->current_crypto && session->current_crypto->do_compress_out) { - if (compress_buffer(session, session->out_buffer) < 0) { - goto error; - } - currentlen = buffer_get_len(session->out_buffer); - } -#endif -*/ - padding = blocksize - (currentlen % blocksize); - if (session->current_crypto) { - ssh_get_random(padstring, padding, 0); - } else { - memset(padstring, 0, padding); - } - - finallen = htonl(currentlen); - SSH_LOG(SSH_LOG_PACKET, - "%d bytes after comp + %d padding bytes = %d bytes packet", - currentlen, padding, ntohl(finallen)); - - if (buffer_prepend_data(session->out_buffer, &padstring, padding) < 0) { - goto error; - } - if (buffer_prepend_data(session->out_buffer, &finallen, sizeof(uint32_t)) < 0) { - goto error; - } - - crc = ssh_crc32((char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t), - ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t)); - - if (buffer_add_u32(session->out_buffer, ntohl(crc)) < 0) { - goto error; - } - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Clear packet", ssh_buffer_get_begin(session->out_buffer), - ssh_buffer_get_len(session->out_buffer)); -#endif - - /* session->out_buffer should have more than sizeof(uint32_t) bytes - in it as required for packet_encrypt */ - packet_encrypt(session, (unsigned char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t), - ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t)); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("encrypted packet",ssh_buffer_get_begin(session->out_buffer), - ssh_buffer_get_len(session->out_buffer)); -#endif - rc=ssh_socket_write(session->socket, ssh_buffer_get_begin(session->out_buffer), - ssh_buffer_get_len(session->out_buffer)); - if(rc== SSH_ERROR) { - goto error; - } - - session->send_seq++; - - if (ssh_buffer_reinit(session->out_buffer) < 0) { - rc = SSH_ERROR; - } -error: - - return rc; /* SSH_OK, AGAIN or ERROR */ -} - -SSH_PACKET_CALLBACK(ssh_packet_disconnect1){ - (void)packet; - (void)user; - (void)type; - SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT"); - ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_DISCONNECT"); - ssh_socket_close(session->socket); - session->alive = 0; - session->session_state=SSH_SESSION_STATE_DISCONNECTED; - return SSH_PACKET_USED; -} - -SSH_PACKET_CALLBACK(ssh_packet_smsg_success1){ - if(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){ - session->session_state=SSH_SESSION_STATE_AUTHENTICATING; - return SSH_PACKET_USED; - } else if(session->session_state==SSH_SESSION_STATE_AUTHENTICATING){ - ssh_auth1_handler(session,type); - return SSH_PACKET_USED; - } else { - return ssh_packet_channel_success(session,type,packet,user); - } -} - -SSH_PACKET_CALLBACK(ssh_packet_smsg_failure1){ - if(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){ - session->session_state=SSH_SESSION_STATE_ERROR; - ssh_set_error(session,SSH_FATAL,"Key exchange failed: received SSH_SMSG_FAILURE"); - return SSH_PACKET_USED; - } else if(session->session_state==SSH_SESSION_STATE_AUTHENTICATING){ - ssh_auth1_handler(session,type); - return SSH_PACKET_USED; - } else { - return ssh_packet_channel_failure(session,type,packet,user); - } -} - - -#endif /* WITH_SSH1 */ - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/packet_cb.c b/libssh/src/packet_cb.c deleted file mode 100644 index a10dd1ab..00000000 --- a/libssh/src/packet_cb.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * packet.c - packet building functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2011 Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include - -#include "libssh/priv.h" -#include "libssh/buffer.h" -#include "libssh/crypto.h" -#include "libssh/dh.h" -#include "libssh/misc.h" -#include "libssh/packet.h" -#include "libssh/pki.h" -#include "libssh/session.h" -#include "libssh/socket.h" -#include "libssh/ssh2.h" -#include "libssh/curve25519.h" - -/** - * @internal - * - * @brief Handle a SSH_DISCONNECT packet. - */ -SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){ - int rc; - uint32_t code = 0; - char *error = NULL; - ssh_string error_s; - (void)user; - (void)type; - - rc = buffer_get_u32(packet, &code); - if (rc != 0) { - code = ntohl(code); - } - - error_s = buffer_get_ssh_string(packet); - if (error_s != NULL) { - error = ssh_string_to_char(error_s); - ssh_string_free(error_s); - } - SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s", - code, error != NULL ? error : "no error"); - ssh_set_error(session, SSH_FATAL, - "Received SSH_MSG_DISCONNECT: %d:%s", - code, error != NULL ? error : "no error"); - SAFE_FREE(error); - - ssh_socket_close(session->socket); - session->alive = 0; - session->session_state = SSH_SESSION_STATE_ERROR; - /* TODO: handle a graceful disconnect */ - return SSH_PACKET_USED; -} - -/** - * @internal - * - * @brief Handle a SSH_IGNORE and SSH_DEBUG packet. - */ -SSH_PACKET_CALLBACK(ssh_packet_ignore_callback){ - (void)session; /* unused */ - (void)user; - (void)type; - (void)packet; - SSH_LOG(SSH_LOG_PROTOCOL,"Received %s packet",type==SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG"); - /* TODO: handle a graceful disconnect */ - return SSH_PACKET_USED; -} - -SSH_PACKET_CALLBACK(ssh_packet_dh_reply){ - int rc; - (void)type; - (void)user; - SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY"); - if(session->session_state!= SSH_SESSION_STATE_DH && - session->dh_handshake_state != DH_STATE_INIT_SENT){ - ssh_set_error(session,SSH_FATAL,"ssh_packet_dh_reply called in wrong state : %d:%d", - session->session_state,session->dh_handshake_state); - goto error; - } - switch(session->next_crypto->kex_type){ - case SSH_KEX_DH_GROUP1_SHA1: - case SSH_KEX_DH_GROUP14_SHA1: - rc=ssh_client_dh_reply(session, packet); - break; -#ifdef HAVE_ECDH - case SSH_KEX_ECDH_SHA2_NISTP256: - rc = ssh_client_ecdh_reply(session, packet); - break; -#endif -#ifdef HAVE_CURVE25519 - case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: - rc = ssh_client_curve25519_reply(session, packet); - break; -#endif - default: - ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_dh_reply"); - goto error; - } - if(rc==SSH_OK) { - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - return SSH_PACKET_USED; - } -error: - session->session_state=SSH_SESSION_STATE_ERROR; - return SSH_PACKET_USED; -} - -SSH_PACKET_CALLBACK(ssh_packet_newkeys){ - ssh_string sig_blob = NULL; - int rc; - (void)packet; - (void)user; - (void)type; - SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_NEWKEYS"); - if(session->session_state!= SSH_SESSION_STATE_DH && - session->dh_handshake_state != DH_STATE_NEWKEYS_SENT){ - ssh_set_error(session,SSH_FATAL,"ssh_packet_newkeys called in wrong state : %d:%d", - session->session_state,session->dh_handshake_state); - goto error; - } - if(session->server){ - /* server things are done in server.c */ - session->dh_handshake_state=DH_STATE_FINISHED; - } else { - ssh_key key; - /* client */ - rc = make_sessionid(session); - if (rc != SSH_OK) { - goto error; - } - - /* - * Set the cryptographic functions for the next crypto - * (it is needed for generate_session_keys for key lengths) - */ - if (crypt_set_algorithms(session, SSH_3DES) /* knows nothing about DES*/ ) { - goto error; - } - - if (generate_session_keys(session) < 0) { - goto error; - } - - /* Verify the host's signature. FIXME do it sooner */ - sig_blob = session->next_crypto->dh_server_signature; - session->next_crypto->dh_server_signature = NULL; - - /* get the server public key */ - rc = ssh_pki_import_pubkey_blob(session->next_crypto->server_pubkey, &key); - if (rc < 0) { - return SSH_ERROR; - } - - /* check if public key from server matches user preferences */ - if (session->opts.wanted_methods[SSH_HOSTKEYS]) { - if(!ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS], - key->type_c)) { - ssh_set_error(session, - SSH_FATAL, - "Public key from server (%s) doesn't match user " - "preference (%s)", - key->type_c, - session->opts.wanted_methods[SSH_HOSTKEYS]); - ssh_key_free(key); - return -1; - } - } - - rc = ssh_pki_signature_verify_blob(session, - sig_blob, - key, - session->next_crypto->secret_hash, - session->next_crypto->digest_len); - /* Set the server public key type for known host checking */ - session->next_crypto->server_pubkey_type = key->type_c; - - ssh_key_free(key); - ssh_string_burn(sig_blob); - ssh_string_free(sig_blob); - sig_blob = NULL; - if (rc == SSH_ERROR) { - goto error; - } - SSH_LOG(SSH_LOG_PROTOCOL,"Signature verified and valid"); - - /* - * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and - * current_crypto - */ - if (session->current_crypto) { - crypto_free(session->current_crypto); - session->current_crypto=NULL; - } - - /* FIXME later, include a function to change keys */ - session->current_crypto = session->next_crypto; - - session->next_crypto = crypto_new(); - if (session->next_crypto == NULL) { - ssh_set_error_oom(session); - goto error; - } - session->next_crypto->session_id = malloc(session->current_crypto->digest_len); - if (session->next_crypto->session_id == NULL) { - ssh_set_error_oom(session); - goto error; - } - memcpy(session->next_crypto->session_id, session->current_crypto->session_id, - session->current_crypto->digest_len); - } - session->dh_handshake_state = DH_STATE_FINISHED; - session->ssh_connection_callback(session); - return SSH_PACKET_USED; -error: - session->session_state=SSH_SESSION_STATE_ERROR; - return SSH_PACKET_USED; -} - -/** - * @internal - * @brief handles a SSH_SERVICE_ACCEPT packet - * - */ -SSH_PACKET_CALLBACK(ssh_packet_service_accept){ - (void)packet; - (void)type; - (void)user; - - session->auth_service_state=SSH_AUTH_SERVICE_ACCEPTED; - SSH_LOG(SSH_LOG_PACKET, - "Received SSH_MSG_SERVICE_ACCEPT"); - - return SSH_PACKET_USED; -} diff --git a/libssh/src/packet_crypt.c b/libssh/src/packet_crypt.c deleted file mode 100644 index 914727e0..00000000 --- a/libssh/src/packet_crypt.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * crypt.c - blowfish-cbc code - * - * This file is part of the SSH Library - * - * Copyright (c) 2003 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#ifdef OPENSSL_CRYPTO -#include -#include -#include -#endif - -#include "libssh/priv.h" -#include "libssh/session.h" -#include "libssh/wrapper.h" -#include "libssh/crypto.h" -#include "libssh/buffer.h" - -uint32_t packet_decrypt_len(ssh_session session, char *crypted){ - uint32_t decrypted; - - if (session->current_crypto) { - if (packet_decrypt(session, crypted, - session->current_crypto->in_cipher->blocksize) < 0) { - return 0; - } - } - memcpy(&decrypted,crypted,sizeof(decrypted)); - return ntohl(decrypted); -} - -int packet_decrypt(ssh_session session, void *data,uint32_t len) { - struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher; - char *out = NULL; - - assert(len); - - if(len % session->current_crypto->in_cipher->blocksize != 0){ - ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); - return SSH_ERROR; - } - out = malloc(len); - if (out == NULL) { - return -1; - } - - if (crypto->set_decrypt_key(crypto, session->current_crypto->decryptkey, - session->current_crypto->decryptIV) < 0) { - SAFE_FREE(out); - return -1; - } - crypto->decrypt(crypto,data,out,len); - - memcpy(data,out,len); - BURN_BUFFER(out, len); - SAFE_FREE(out); - return 0; -} - -unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { - struct ssh_cipher_struct *crypto = NULL; - HMACCTX ctx = NULL; - char *out = NULL; - unsigned int finallen; - uint32_t seq; - enum ssh_hmac_e type; - - assert(len); - - if (!session->current_crypto) { - return NULL; /* nothing to do here */ - } - if(len % session->current_crypto->in_cipher->blocksize != 0){ - ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); - return NULL; - } - out = malloc(len); - if (out == NULL) { - return NULL; - } - - type = session->current_crypto->out_hmac; - seq = ntohl(session->send_seq); - crypto = session->current_crypto->out_cipher; - - if (crypto->set_encrypt_key(crypto, session->current_crypto->encryptkey, - session->current_crypto->encryptIV) < 0) { - SAFE_FREE(out); - return NULL; - } - - if (session->version == 2) { - ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type); - if (ctx == NULL) { - SAFE_FREE(out); - return NULL; - } - hmac_update(ctx,(unsigned char *)&seq,sizeof(uint32_t)); - hmac_update(ctx,data,len); - hmac_final(ctx,session->current_crypto->hmacbuf,&finallen); -#ifdef DEBUG_CRYPTO - ssh_print_hexa("mac: ",data,hmac_digest_len(type)); - if (finallen != hmac_digest_len(type)) { - printf("Final len is %d\n",finallen); - } - ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, hmac_digest_len(type)); -#endif - } - - crypto->encrypt(crypto, data, out, len); - - memcpy(data, out, len); - BURN_BUFFER(out, len); - SAFE_FREE(out); - - if (session->version == 2) { - return session->current_crypto->hmacbuf; - } - - return NULL; -} - -/** - * @internal - * - * @brief Verify the hmac of a packet - * - * @param session The session to use. - * @param buffer The buffer to verify the hmac from. - * @param mac The mac to compare with the hmac. - * - * @return 0 if hmac and mac are equal, < 0 if not or an error - * occurred. - */ -int packet_hmac_verify(ssh_session session, ssh_buffer buffer, - unsigned char *mac, enum ssh_hmac_e type) { - unsigned char hmacbuf[DIGEST_MAX_LEN] = {0}; - HMACCTX ctx; - unsigned int len; - uint32_t seq; - - ctx = hmac_init(session->current_crypto->decryptMAC, hmac_digest_len(type), type); - if (ctx == NULL) { - return -1; - } - - seq = htonl(session->recv_seq); - - hmac_update(ctx, (unsigned char *) &seq, sizeof(uint32_t)); - hmac_update(ctx, buffer_get_rest(buffer), buffer_get_rest_len(buffer)); - hmac_final(ctx, hmacbuf, &len); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("received mac",mac,len); - ssh_print_hexa("Computed mac",hmacbuf,len); - ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(uint32_t)); -#endif - if (memcmp(mac, hmacbuf, len) == 0) { - return 0; - } - - return -1; -} - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/pcap.c b/libssh/src/pcap.c deleted file mode 100644 index 134bdf10..00000000 --- a/libssh/src/pcap.c +++ /dev/null @@ -1,511 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/* pcap.c */ -#include "config.h" -#ifdef WITH_PCAP - -#include -#ifdef _WIN32 -#include -#include -#else -#include -#include -#include -#endif -#include -#include - -#include "libssh/libssh.h" -#include "libssh/pcap.h" -#include "libssh/session.h" -#include "libssh/buffer.h" -#include "libssh/socket.h" - -/** - * @internal - * - * @defgroup libssh_pcap The libssh pcap functions - * @ingroup libssh - * - * The pcap file generation - * - * - * @{ - */ - -/* The header of a pcap file is the following. We are not going to make it - * very complicated. - * Just for information. - */ -struct pcap_hdr_s { - uint32_t magic_number; /* magic number */ - uint16_t version_major; /* major version number */ - uint16_t version_minor; /* minor version number */ - int32_t thiszone; /* GMT to local correction */ - uint32_t sigfigs; /* accuracy of timestamps */ - uint32_t snaplen; /* max length of captured packets, in octets */ - uint32_t network; /* data link type */ -}; - -#define PCAP_MAGIC 0xa1b2c3d4 -#define PCAP_VERSION_MAJOR 2 -#define PCAP_VERSION_MINOR 4 - -#define DLT_RAW 12 /* raw IP */ - -/* TCP flags */ -#define TH_FIN 0x01 -#define TH_SYN 0x02 -#define TH_RST 0x04 -#define TH_PUSH 0x08 -#define TH_ACK 0x10 -#define TH_URG 0x20 - -/* The header of a pcap packet. - * Just for information. - */ -struct pcaprec_hdr_s { - uint32_t ts_sec; /* timestamp seconds */ - uint32_t ts_usec; /* timestamp microseconds */ - uint32_t incl_len; /* number of octets of packet saved in file */ - uint32_t orig_len; /* actual length of packet */ -}; - -/** @private - * @brief a pcap context expresses the state of a pcap dump - * in a SSH session only. Multiple pcap contexts may be used into - * a single pcap file. - */ - -struct ssh_pcap_context_struct { - ssh_session session; - ssh_pcap_file file; - int connected; - /* All of these information are useful to generate - * the dummy IP and TCP packets - */ - uint32_t ipsource; - uint32_t ipdest; - uint16_t portsource; - uint16_t portdest; - uint32_t outsequence; - uint32_t insequence; -}; - -/** @private - * @brief a pcap file expresses the state of a pcap file which may - * contain several streams. - */ -struct ssh_pcap_file_struct { - FILE *output; - uint16_t ipsequence; -}; - -/** - * @brief create a new ssh_pcap_file object - */ -ssh_pcap_file ssh_pcap_file_new(void) { - struct ssh_pcap_file_struct *pcap; - - pcap = (struct ssh_pcap_file_struct *) malloc(sizeof(struct ssh_pcap_file_struct)); - if (pcap == NULL) { - return NULL; - } - ZERO_STRUCTP(pcap); - - return pcap; -} - -/** @internal - * @brief writes a packet on file - */ -static int ssh_pcap_file_write(ssh_pcap_file pcap, ssh_buffer packet){ - int err; - uint32_t len; - if(pcap == NULL || pcap->output==NULL) - return SSH_ERROR; - len=buffer_get_rest_len(packet); - err=fwrite(buffer_get_rest(packet),len,1,pcap->output); - if(err<0) - return SSH_ERROR; - else - return SSH_OK; -} - -/** @internal - * @brief prepends a packet with the pcap header and writes packet - * on file - */ -int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len){ - ssh_buffer header=ssh_buffer_new(); - struct timeval now; - int err; - if(header == NULL) - return SSH_ERROR; - gettimeofday(&now,NULL); - err = buffer_add_u32(header,htonl(now.tv_sec)); - if (err < 0) { - goto error; - } - err = buffer_add_u32(header,htonl(now.tv_usec)); - if (err < 0) { - goto error; - } - err = buffer_add_u32(header,htonl(buffer_get_rest_len(packet))); - if (err < 0) { - goto error; - } - err = buffer_add_u32(header,htonl(original_len)); - if (err < 0) { - goto error; - } - err = buffer_add_buffer(header,packet); - if (err < 0) { - goto error; - } - err=ssh_pcap_file_write(pcap,header); -error: - ssh_buffer_free(header); - return err; -} - -/** - * @brief opens a new pcap file and create header - */ -int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){ - ssh_buffer header; - int err; - if(pcap == NULL) - return SSH_ERROR; - if(pcap->output){ - fclose(pcap->output); - pcap->output=NULL; - } - pcap->output=fopen(filename,"wb"); - if(pcap->output==NULL) - return SSH_ERROR; - header=ssh_buffer_new(); - if(header==NULL) - return SSH_ERROR; - err = buffer_add_u32(header,htonl(PCAP_MAGIC)); - if (err < 0) { - goto error; - } - err = buffer_add_u16(header,htons(PCAP_VERSION_MAJOR)); - if (err < 0) { - goto error; - } - err = buffer_add_u16(header,htons(PCAP_VERSION_MINOR)); - if (err < 0) { - goto error; - } - /* currently hardcode GMT to 0 */ - err = buffer_add_u32(header,htonl(0)); - if (err < 0) { - goto error; - } - /* accuracy */ - err = buffer_add_u32(header,htonl(0)); - if (err < 0) { - goto error; - } - /* size of the biggest packet */ - err = buffer_add_u32(header,htonl(MAX_PACKET_LEN)); - if (err < 0) { - goto error; - } - /* we will write sort-of IP */ - err = buffer_add_u32(header,htonl(DLT_RAW)); - if (err < 0) { - goto error; - } - err=ssh_pcap_file_write(pcap,header); -error: - ssh_buffer_free(header); - return err; -} - -int ssh_pcap_file_close(ssh_pcap_file pcap){ - int err; - if(pcap ==NULL || pcap->output==NULL) - return SSH_ERROR; - err=fclose(pcap->output); - pcap->output=NULL; - if(err != 0) - return SSH_ERROR; - else - return SSH_OK; -} - -void ssh_pcap_file_free(ssh_pcap_file pcap){ - ssh_pcap_file_close(pcap); - SAFE_FREE(pcap); -} - - -/** @internal - * @brief allocates a new ssh_pcap_context object - */ - -ssh_pcap_context ssh_pcap_context_new(ssh_session session){ - ssh_pcap_context ctx = (struct ssh_pcap_context_struct *) malloc(sizeof(struct ssh_pcap_context_struct)); - if(ctx==NULL){ - ssh_set_error_oom(session); - return NULL; - } - ZERO_STRUCTP(ctx); - ctx->session=session; - return ctx; -} - -void ssh_pcap_context_free(ssh_pcap_context ctx){ - SAFE_FREE(ctx); -} - -void ssh_pcap_context_set_file(ssh_pcap_context ctx, ssh_pcap_file pcap){ - ctx->file=pcap; -} - -/** @internal - * @brief sets the IP and port parameters in the connection - */ -static int ssh_pcap_context_connect(ssh_pcap_context ctx){ - ssh_session session=ctx->session; - struct sockaddr_in local, remote; - socket_t fd; - socklen_t len; - if(session==NULL) - return SSH_ERROR; - if(session->socket==NULL) - return SSH_ERROR; - fd=ssh_socket_get_fd_in(session->socket); - /* TODO: adapt for windows */ - if(fd<0) - return SSH_ERROR; - len=sizeof(local); - if(getsockname(fd,(struct sockaddr *)&local,&len)<0){ - ssh_set_error(session,SSH_REQUEST_DENIED,"Getting local IP address: %s",strerror(errno)); - return SSH_ERROR; - } - len=sizeof(remote); - if(getpeername(fd,(struct sockaddr *)&remote,&len)<0){ - ssh_set_error(session,SSH_REQUEST_DENIED,"Getting remote IP address: %s",strerror(errno)); - return SSH_ERROR; - } - if(local.sin_family != AF_INET){ - ssh_set_error(session,SSH_REQUEST_DENIED,"Only IPv4 supported for pcap logging"); - return SSH_ERROR; - } - memcpy(&ctx->ipsource,&local.sin_addr,sizeof(ctx->ipsource)); - memcpy(&ctx->ipdest,&remote.sin_addr,sizeof(ctx->ipdest)); - memcpy(&ctx->portsource,&local.sin_port,sizeof(ctx->portsource)); - memcpy(&ctx->portdest,&remote.sin_port,sizeof(ctx->portdest)); - - ctx->connected=1; - return SSH_OK; -} - -#define IPHDR_LEN 20 -#define TCPHDR_LEN 20 -#define TCPIPHDR_LEN (IPHDR_LEN + TCPHDR_LEN) -/** @internal - * @brief write a SSH packet as a TCP over IP in a pcap file - * @param ctx open pcap context - * @param direction SSH_PCAP_DIRECTION_IN if the packet has been received - * @param direction SSH_PCAP_DIRECTION_OUT if the packet has been emitted - * @param data pointer to the data to write - * @param len data to write in the pcap file. May be smaller than origlen. - * @param origlen number of bytes of complete data. - * @returns SSH_OK write is successful - * @returns SSH_ERROR an error happened. - */ -int ssh_pcap_context_write(ssh_pcap_context ctx,enum ssh_pcap_direction direction - , void *data, uint32_t len, uint32_t origlen){ - ssh_buffer ip; - int rc; - if(ctx==NULL || ctx->file ==NULL) - return SSH_ERROR; - if(ctx->connected==0) - if(ssh_pcap_context_connect(ctx)==SSH_ERROR) - return SSH_ERROR; - ip=ssh_buffer_new(); - if(ip==NULL){ - ssh_set_error_oom(ctx->session); - return SSH_ERROR; - } - - /* build an IP packet */ - rc = ssh_buffer_pack(ip, - "bbwwwbbw", - 4 << 4 | 5, /* V4, 20 bytes */ - 0, /* tos */ - origlen + TCPIPHDR_LEN, /* total len */ - ctx->file->ipsequence, /* IP id number */ - 0, /* fragment offset */ - 64, /* TTL */ - 6, /* protocol TCP=6 */ - 0); /* checksum */ - - ctx->file->ipsequence++; - if (rc != SSH_OK){ - goto error; - } - if(direction==SSH_PCAP_DIR_OUT){ - rc = buffer_add_u32(ip,ctx->ipsource); - if (rc < 0) { - goto error; - } - rc = buffer_add_u32(ip,ctx->ipdest); - if (rc < 0) { - goto error; - } - } else { - rc = buffer_add_u32(ip,ctx->ipdest); - if (rc < 0) { - goto error; - } - rc = buffer_add_u32(ip,ctx->ipsource); - if (rc < 0) { - goto error; - } - } - /* TCP */ - if(direction==SSH_PCAP_DIR_OUT){ - rc = buffer_add_u16(ip,ctx->portsource); - if (rc < 0) { - goto error; - } - rc = buffer_add_u16(ip,ctx->portdest); - if (rc < 0) { - goto error; - } - } else { - rc = buffer_add_u16(ip,ctx->portdest); - if (rc < 0) { - goto error; - } - rc = buffer_add_u16(ip,ctx->portsource); - if (rc < 0) { - goto error; - } - } - /* sequence number */ - if(direction==SSH_PCAP_DIR_OUT){ - rc = ssh_buffer_pack(ip, "d", ctx->outsequence); - if (rc != SSH_OK) { - goto error; - } - ctx->outsequence+=origlen; - } else { - rc = ssh_buffer_pack(ip, "d", ctx->insequence); - if (rc != SSH_OK) { - goto error; - } - ctx->insequence+=origlen; - } - /* ack number */ - if(direction==SSH_PCAP_DIR_OUT){ - rc = ssh_buffer_pack(ip, "d", ctx->insequence); - if (rc != SSH_OK) { - goto error; - } - } else { - rc = ssh_buffer_pack(ip, "d", ctx->outsequence); - if (rc != SSH_OK) { - goto error; - } - } - - rc = ssh_buffer_pack(ip, - "bbwwwP", - 5 << 4, /* header len = 20 = 5 * 32 bits, at offset 4*/ - TH_PUSH | TH_ACK, /* flags */ - 65535, /* window */ - 0, /* checksum */ - 0, /* urgent data ptr */ - (size_t)len, data); /* actual data */ - if (rc != SSH_OK) { - goto error; - } - rc=ssh_pcap_file_write_packet(ctx->file,ip,origlen + TCPIPHDR_LEN); -error: - ssh_buffer_free(ip); - return rc; -} - -/** @brief sets the pcap file used to trace the session - * @param current session - * @param pcap an handler to a pcap file. A pcap file may be used in several - * sessions. - * @returns SSH_ERROR in case of error, SSH_OK otherwise. - */ -int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap){ - ssh_pcap_context ctx=ssh_pcap_context_new(session); - if(ctx==NULL){ - ssh_set_error_oom(session); - return SSH_ERROR; - } - ctx->file=pcap; - if(session->pcap_ctx) - ssh_pcap_context_free(session->pcap_ctx); - session->pcap_ctx=ctx; - return SSH_OK; -} - - -#else /* WITH_PCAP */ - -/* Simple stub returning errors when no pcap compiled in */ - -#include "libssh/libssh.h" -#include "libssh/priv.h" - -int ssh_pcap_file_close(ssh_pcap_file pcap){ - (void) pcap; - return SSH_ERROR; -} - -void ssh_pcap_file_free(ssh_pcap_file pcap){ - (void) pcap; -} - -ssh_pcap_file ssh_pcap_file_new(void){ - return NULL; -} -int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){ - (void) pcap; - (void) filename; - return SSH_ERROR; -} - -int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile){ - (void) pcapfile; - ssh_set_error(session,SSH_REQUEST_DENIED,"Pcap support not compiled in"); - return SSH_ERROR; -} - -#endif - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/pki.c b/libssh/src/pki.c deleted file mode 100644 index af472ebb..00000000 --- a/libssh/src/pki.c +++ /dev/null @@ -1,1649 +0,0 @@ -/* - * known_hosts.c - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * Copyright (c) 2011-2013 Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/** - * @defgroup libssh_pki The SSH Public Key Infrastructure - * @ingroup libssh - * - * Functions for the creation, importation and manipulation of public and - * private keys in the context of the SSH protocol - * - * @{ - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -# if _MSC_VER >= 1400 -# include -# undef open -# define open _open -# undef close -# define close _close -# undef read -# define read _read -# undef unlink -# define unlink _unlink -# endif /* _MSC_VER */ -#endif - -#include "libssh/libssh.h" -#include "libssh/session.h" -#include "libssh/priv.h" -#include "libssh/pki.h" -#include "libssh/pki_priv.h" -#include "libssh/keys.h" -#include "libssh/buffer.h" -#include "libssh/misc.h" -#include "libssh/agent.h" - -void _ssh_pki_log(const char *function, const char *format, ...) -{ -#ifdef DEBUG_CRYPTO - char buffer[1024]; - va_list va; - - va_start(va, format); - vsnprintf(buffer, sizeof(buffer), format, va); - va_end(va); - - ssh_log_function(SSH_LOG_DEBUG, function, buffer); -#else - (void) function; - (void) format; -#endif - return; -} - -enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) { - if (strncmp(privkey, DSA_HEADER_BEGIN, strlen(DSA_HEADER_BEGIN)) == 0) { - return SSH_KEYTYPE_DSS; - } - - if (strncmp(privkey, RSA_HEADER_BEGIN, strlen(RSA_HEADER_BEGIN)) == 0) { - return SSH_KEYTYPE_RSA; - } - - if (strncmp(privkey, ECDSA_HEADER_BEGIN, strlen(ECDSA_HEADER_BEGIN)) == 0) { - return SSH_KEYTYPE_ECDSA; - } - - return SSH_KEYTYPE_UNKNOWN; -} - -/** - * @brief returns the ECDSA key name ("ecdsa-sha2-nistp256" for example) - * - * @param[in] key the ssh_key whose ECDSA name to get - * - * @returns the ECDSA key name ("ecdsa-sha2-nistp256" for example) - * - * @returns "unknown" if the ECDSA key name is not known - */ -const char *ssh_pki_key_ecdsa_name(const ssh_key key) -{ -#ifdef HAVE_OPENSSL_ECC /* FIXME Better ECC check needed */ - return pki_key_ecdsa_nid_to_name(key->ecdsa_nid); -#else - (void) key; /* unused */ - return NULL; -#endif -} - -/** - * @brief creates a new empty SSH key - * @returns an empty ssh_key handle, or NULL on error. - */ -ssh_key ssh_key_new (void) { - ssh_key ptr = malloc (sizeof (struct ssh_key_struct)); - if (ptr == NULL) { - return NULL; - } - ZERO_STRUCTP(ptr); - return ptr; -} - -ssh_key ssh_key_dup(const ssh_key key) -{ - if (key == NULL) { - return NULL; - } - - return pki_key_dup(key, 0); -} - -/** - * @brief clean up the key and deallocate all existing keys - * @param[in] key ssh_key to clean - */ -void ssh_key_clean (ssh_key key){ - if(key == NULL) - return; -#ifdef HAVE_LIBGCRYPT - if(key->dsa) gcry_sexp_release(key->dsa); - if(key->rsa) gcry_sexp_release(key->rsa); - if(key->ecdsa) gcry_sexp_release(key->ecdsa); -#elif defined HAVE_LIBCRYPTO - if(key->dsa) DSA_free(key->dsa); - if(key->rsa) RSA_free(key->rsa); -#ifdef HAVE_OPENSSL_ECC - if(key->ecdsa) EC_KEY_free(key->ecdsa); -#endif /* HAVE_OPENSSL_ECC */ -#endif - if (key->ed25519_privkey != NULL){ - BURN_BUFFER(key->ed25519_privkey, sizeof(ed25519_privkey)); - SAFE_FREE(key->ed25519_privkey); - } - SAFE_FREE(key->ed25519_pubkey); - key->flags=SSH_KEY_FLAG_EMPTY; - key->type=SSH_KEYTYPE_UNKNOWN; - key->ecdsa_nid = 0; - key->type_c=NULL; - key->dsa = NULL; - key->rsa = NULL; - key->ecdsa = NULL; -} - -/** - * @brief deallocate a SSH key - * @param[in] key ssh_key handle to free - */ -void ssh_key_free (ssh_key key){ - if(key){ - ssh_key_clean(key); - SAFE_FREE(key); - } -} - -/** - * @brief returns the type of a ssh key - * @param[in] key the ssh_key handle - * @returns one of SSH_KEYTYPE_RSA,SSH_KEYTYPE_DSS,SSH_KEYTYPE_RSA1 - * @returns SSH_KEYTYPE_UNKNOWN if the type is unknown - */ -enum ssh_keytypes_e ssh_key_type(const ssh_key key){ - if (key == NULL) { - return SSH_KEYTYPE_UNKNOWN; - } - return key->type; -} - -/** - * @brief Convert a key type to a string. - * - * @param[in] type The type to convert. - * - * @return A string for the keytype or NULL if unknown. - */ -const char *ssh_key_type_to_char(enum ssh_keytypes_e type) { - switch (type) { - case SSH_KEYTYPE_DSS: - return "ssh-dss"; - case SSH_KEYTYPE_RSA: - return "ssh-rsa"; - case SSH_KEYTYPE_RSA1: - return "ssh-rsa1"; - case SSH_KEYTYPE_ECDSA: - return "ssh-ecdsa"; - case SSH_KEYTYPE_ED25519: - return "ssh-ed25519"; - case SSH_KEYTYPE_UNKNOWN: - return NULL; - } - - /* We should never reach this */ - return NULL; -} - -/** - * @brief Convert a ssh key name to a ssh key type. - * - * @param[in] name The name to convert. - * - * @return The enum ssh key type. - */ -enum ssh_keytypes_e ssh_key_type_from_name(const char *name) { - if (name == NULL) { - return SSH_KEYTYPE_UNKNOWN; - } - - if (strcmp(name, "rsa1") == 0) { - return SSH_KEYTYPE_RSA1; - } else if (strcmp(name, "rsa") == 0) { - return SSH_KEYTYPE_RSA; - } else if (strcmp(name, "dsa") == 0) { - return SSH_KEYTYPE_DSS; - } else if (strcmp(name, "ssh-rsa1") == 0) { - return SSH_KEYTYPE_RSA1; - } else if (strcmp(name, "ssh-rsa") == 0) { - return SSH_KEYTYPE_RSA; - } else if (strcmp(name, "ssh-dss") == 0) { - return SSH_KEYTYPE_DSS; - } else if (strcmp(name, "ssh-ecdsa") == 0 - || strcmp(name, "ecdsa") == 0 - || strcmp(name, "ecdsa-sha2-nistp256") == 0 - || strcmp(name, "ecdsa-sha2-nistp384") == 0 - || strcmp(name, "ecdsa-sha2-nistp521") == 0) { - return SSH_KEYTYPE_ECDSA; - } else if (strcmp(name, "ssh-ed25519") == 0){ - return SSH_KEYTYPE_ED25519; - } - - return SSH_KEYTYPE_UNKNOWN; -} - -/** - * @brief Check if the key has/is a public key. - * - * @param[in] k The key to check. - * - * @return 1 if it is a public key, 0 if not. - */ -int ssh_key_is_public(const ssh_key k) { - if (k == NULL) { - return 0; - } - - return (k->flags & SSH_KEY_FLAG_PUBLIC); -} - -/** - * @brief Check if the key is a private key. - * - * @param[in] k The key to check. - * - * @return 1 if it is a private key, 0 if not. - */ -int ssh_key_is_private(const ssh_key k) { - if (k == NULL) { - return 0; - } - - return (k->flags & SSH_KEY_FLAG_PRIVATE); -} - -/** - * @brief Compare keys if they are equal. - * - * @param[in] k1 The first key to compare. - * - * @param[in] k2 The second key to compare. - * - * @param[in] what What part or type of the key do you want to compare. - * - * @return 0 if equal, 1 if not. - */ -int ssh_key_cmp(const ssh_key k1, - const ssh_key k2, - enum ssh_keycmp_e what) -{ - if (k1 == NULL || k2 == NULL) { - return 1; - } - - if (k1->type != k2->type) { - ssh_pki_log("key types don't match!"); - return 1; - } - - if (what == SSH_KEY_CMP_PRIVATE) { - if (!ssh_key_is_private(k1) || - !ssh_key_is_private(k2)) { - return 1; - } - } - - if (k1->type == SSH_KEYTYPE_ED25519) { - return pki_ed25519_key_cmp(k1, k2, what); - } - - return pki_key_compare(k1, k2, what); -} - -ssh_signature ssh_signature_new(void) -{ - struct ssh_signature_struct *sig; - - sig = malloc(sizeof(struct ssh_signature_struct)); - if (sig == NULL) { - return NULL; - } - ZERO_STRUCTP(sig); - - return sig; -} - -void ssh_signature_free(ssh_signature sig) -{ - if (sig == NULL) { - return; - } - - switch(sig->type) { - case SSH_KEYTYPE_DSS: -#ifdef HAVE_LIBGCRYPT - gcry_sexp_release(sig->dsa_sig); -#elif defined HAVE_LIBCRYPTO - DSA_SIG_free(sig->dsa_sig); -#endif - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: -#ifdef HAVE_LIBGCRYPT - gcry_sexp_release(sig->rsa_sig); -#elif defined HAVE_LIBCRYPTO - SAFE_FREE(sig->rsa_sig); -#endif - break; - case SSH_KEYTYPE_ECDSA: -#if defined(HAVE_LIBCRYPTO) && defined(HAVE_OPENSSL_ECC) - ECDSA_SIG_free(sig->ecdsa_sig); -#endif - break; - case SSH_KEYTYPE_ED25519: - SAFE_FREE(sig->ed25519_sig); - break; - case SSH_KEYTYPE_UNKNOWN: - break; - } - - SAFE_FREE(sig); -} - -/** - * @brief import a base64 formated key from a memory c-string - * - * @param[in] b64_key The c-string holding the base64 encoded key - * - * @param[in] passphrase The passphrase to decrypt the key, or NULL - * - * @param[in] auth_fn An auth function you may want to use or NULL. - * - * @param[in] auth_data Private data passed to the auth function. - * - * @param[out] pkey A pointer where the allocated key can be stored. You - * need to free the memory. - * - * @return SSH_ERROR in case of error, SSH_OK otherwise. - * - * @see ssh_key_free() - */ -int ssh_pki_import_privkey_base64(const char *b64_key, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data, - ssh_key *pkey) -{ - ssh_key key; - - if (b64_key == NULL || pkey == NULL) { - return SSH_ERROR; - } - - if (b64_key == NULL || !*b64_key) { - return SSH_ERROR; - } - - ssh_pki_log("Trying to decode privkey passphrase=%s", - passphrase ? "true" : "false"); - - key = pki_private_key_from_base64(b64_key, passphrase, auth_fn, auth_data); - if (key == NULL) { - return SSH_ERROR; - } - - *pkey = key; - - return SSH_OK; -} - -/** - * @brief Import a key from a file. - * - * @param[in] filename The filename of the the private key. - * - * @param[in] passphrase The passphrase to decrypt the private key. Set to NULL - * if none is needed or it is unknown. - * - * @param[in] auth_fn An auth function you may want to use or NULL. - * - * @param[in] auth_data Private data passed to the auth function. - * - * @param[out] pkey A pointer to store the allocated ssh_key. You need to - * free the key. - * - * @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission - * denied, SSH_ERROR otherwise. - * - * @see ssh_key_free() - **/ -int ssh_pki_import_privkey_file(const char *filename, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data, - ssh_key *pkey) { - struct stat sb; - char *key_buf; - ssh_key key; - FILE *file; - off_t size; - int rc; - - if (pkey == NULL || filename == NULL || *filename == '\0') { - return SSH_ERROR; - } - - file = fopen(filename, "rb"); - if (file == NULL) { - ssh_pki_log("Error opening %s: %s", - filename, strerror(errno)); - return SSH_EOF; - } - - rc = fstat(fileno(file), &sb); - if (rc < 0) { - fclose(file); - ssh_pki_log("Error getting stat of %s: %s", - filename, strerror(errno)); - switch (errno) { - case ENOENT: - case EACCES: - return SSH_EOF; - } - - return SSH_ERROR; - } - - if (sb.st_size > MAX_PRIVKEY_SIZE) { - ssh_pki_log("Private key is bigger than 4M."); - fclose(file); - return SSH_ERROR; - } - - key_buf = malloc(sb.st_size + 1); - if (key_buf == NULL) { - fclose(file); - ssh_pki_log("Out of memory!"); - return SSH_ERROR; - } - - size = fread(key_buf, 1, sb.st_size, file); - fclose(file); - - if (size != sb.st_size) { - SAFE_FREE(key_buf); - ssh_pki_log("Error reading %s: %s", - filename, strerror(errno)); - return SSH_ERROR; - } - key_buf[size] = 0; - - key = pki_private_key_from_base64(key_buf, passphrase, auth_fn, auth_data); - SAFE_FREE(key_buf); - if (key == NULL) { - return SSH_ERROR; - } - - *pkey = key; - return SSH_OK; -} - -/** - * @brief Export a private key to a pam file on disk. - * - * @param[in] privkey The private key to export. - * - * @param[in] passphrase The passphrase to use to encrypt the key with or - * NULL. An empty string means no passphrase. - * - * @param[in] auth_fn An auth function you may want to use or NULL. - * - * @param[in] auth_data Private data passed to the auth function. - * - * @param[in] filename The path where to store the pem file. - * - * @return SSH_OK on success, SSH_ERROR on error. - */ -int ssh_pki_export_privkey_file(const ssh_key privkey, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data, - const char *filename) -{ - ssh_string blob; - FILE *fp; - int rc; - - if (privkey == NULL || !ssh_key_is_private(privkey)) { - return SSH_ERROR; - } - - fp = fopen(filename, "wb"); - if (fp == NULL) { - SSH_LOG(SSH_LOG_FUNCTIONS, "Error opening %s: %s", - filename, strerror(errno)); - return SSH_EOF; - } - - - blob = pki_private_key_to_pem(privkey, - passphrase, - auth_fn, - auth_data); - if (blob == NULL) { - fclose(fp); - return -1; - } - - rc = fwrite(ssh_string_data(blob), ssh_string_len(blob), 1, fp); - ssh_string_free(blob); - if (rc != 1 || ferror(fp)) { - fclose(fp); - unlink(filename); - return SSH_ERROR; - } - fclose(fp); - - return SSH_OK; -} - -/* temporary function to migrate seemlessly to ssh_key */ -ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) { - ssh_public_key pub; - ssh_key tmp; - - if(key == NULL) { - return NULL; - } - - tmp = ssh_key_dup(key); - if (tmp == NULL) { - return NULL; - } - - pub = malloc(sizeof(struct ssh_public_key_struct)); - if (pub == NULL) { - ssh_key_free(tmp); - return NULL; - } - ZERO_STRUCTP(pub); - - pub->type = tmp->type; - pub->type_c = tmp->type_c; - - pub->dsa_pub = tmp->dsa; - tmp->dsa = NULL; - pub->rsa_pub = tmp->rsa; - tmp->rsa = NULL; - - ssh_key_free(tmp); - - return pub; -} - -ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key) { - ssh_private_key privkey; - - privkey = malloc(sizeof(struct ssh_private_key_struct)); - if (privkey == NULL) { - ssh_key_free(key); - return NULL; - } - - privkey->type = key->type; - privkey->dsa_priv = key->dsa; - privkey->rsa_priv = key->rsa; - - return privkey; -} - -static int pki_import_pubkey_buffer(ssh_buffer buffer, - enum ssh_keytypes_e type, - ssh_key *pkey) { - ssh_key key; - int rc; - - key = ssh_key_new(); - if (key == NULL) { - return SSH_ERROR; - } - - key->type = type; - key->type_c = ssh_key_type_to_char(type); - key->flags = SSH_KEY_FLAG_PUBLIC; - - switch (type) { - case SSH_KEYTYPE_DSS: - { - ssh_string p; - ssh_string q; - ssh_string g; - ssh_string pubkey; - - p = buffer_get_ssh_string(buffer); - if (p == NULL) { - goto fail; - } - q = buffer_get_ssh_string(buffer); - if (q == NULL) { - ssh_string_burn(p); - ssh_string_free(p); - - goto fail; - } - g = buffer_get_ssh_string(buffer); - if (g == NULL) { - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(q); - ssh_string_free(q); - - goto fail; - } - pubkey = buffer_get_ssh_string(buffer); - if (pubkey == NULL) { - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(q); - ssh_string_free(q); - ssh_string_burn(g); - ssh_string_free(g); - - goto fail; - } - - rc = pki_pubkey_build_dss(key, p, q, g, pubkey); -#ifdef DEBUG_CRYPTO - ssh_print_hexa("p", ssh_string_data(p), ssh_string_len(p)); - ssh_print_hexa("q", ssh_string_data(q), ssh_string_len(q)); - ssh_print_hexa("g", ssh_string_data(g), ssh_string_len(g)); -#endif - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(q); - ssh_string_free(q); - ssh_string_burn(g); - ssh_string_free(g); - ssh_string_burn(pubkey); - ssh_string_free(pubkey); - if (rc == SSH_ERROR) { - goto fail; - } - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - { - ssh_string e; - ssh_string n; - - e = buffer_get_ssh_string(buffer); - if (e == NULL) { - goto fail; - } - n = buffer_get_ssh_string(buffer); - if (n == NULL) { - ssh_string_burn(e); - ssh_string_free(e); - - goto fail; - } - - rc = pki_pubkey_build_rsa(key, e, n); -#ifdef DEBUG_CRYPTO - ssh_print_hexa("e", ssh_string_data(e), ssh_string_len(e)); - ssh_print_hexa("n", ssh_string_data(n), ssh_string_len(n)); -#endif - ssh_string_burn(e); - ssh_string_free(e); - ssh_string_burn(n); - ssh_string_free(n); - if (rc == SSH_ERROR) { - goto fail; - } - } - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_ECC - { - ssh_string e; - ssh_string i; - int nid; - - i = buffer_get_ssh_string(buffer); - if (i == NULL) { - goto fail; - } - nid = pki_key_ecdsa_nid_from_name(ssh_string_get_char(i)); - ssh_string_free(i); - if (nid == -1) { - goto fail; - } - - - e = buffer_get_ssh_string(buffer); - if (e == NULL) { - goto fail; - } - - rc = pki_pubkey_build_ecdsa(key, nid, e); - ssh_string_burn(e); - ssh_string_free(e); - if (rc < 0) { - goto fail; - } - - /* Update key type */ - key->type_c = ssh_pki_key_ecdsa_name(key); - } - break; -#endif - case SSH_KEYTYPE_ED25519: - { - ssh_string pubkey = buffer_get_ssh_string(buffer); - if (ssh_string_len(pubkey) != ED25519_PK_LEN) { - ssh_pki_log("Invalid public key length"); - ssh_string_burn(pubkey); - ssh_string_free(pubkey); - goto fail; - } - - key->ed25519_pubkey = malloc(ED25519_PK_LEN); - if (key->ed25519_pubkey == NULL) { - ssh_string_burn(pubkey); - ssh_string_free(pubkey); - goto fail; - } - - memcpy(key->ed25519_pubkey, ssh_string_data(pubkey), ED25519_PK_LEN); - ssh_string_burn(pubkey); - ssh_string_free(pubkey); - } - break; - case SSH_KEYTYPE_UNKNOWN: - default: - ssh_pki_log("Unknown public key protocol %d", type); - goto fail; - } - - *pkey = key; - return SSH_OK; -fail: - ssh_key_free(key); - - return SSH_ERROR; -} - -/** - * @brief Import a base64 formated public key from a memory c-string. - * - * @param[in] b64_key The base64 key to format. - * - * @param[in] type The type of the key to format. - * - * @param[out] pkey A pointer where the allocated key can be stored. You - * need to free the memory. - * - * @return SSH_OK on success, SSH_ERROR on error. - * - * @see ssh_key_free() - */ -int ssh_pki_import_pubkey_base64(const char *b64_key, - enum ssh_keytypes_e type, - ssh_key *pkey) { - ssh_buffer buffer; - ssh_string type_s; - int rc; - - if (b64_key == NULL || pkey == NULL) { - return SSH_ERROR; - } - - buffer = base64_to_bin(b64_key); - if (buffer == NULL) { - return SSH_ERROR; - } - - type_s = buffer_get_ssh_string(buffer); - if (type_s == NULL) { - ssh_buffer_free(buffer); - return SSH_ERROR; - } - ssh_string_free(type_s); - - rc = pki_import_pubkey_buffer(buffer, type, pkey); - ssh_buffer_free(buffer); - - return rc; -} - -/** - * @internal - * - * @brief Import a public key from a ssh string. - * - * @param[in] key_blob The key blob to import as specified in RFC 4253 section - * 6.6 "Public Key Algorithms". - * - * @param[out] pkey A pointer where the allocated key can be stored. You - * need to free the memory. - * - * @return SSH_OK on success, SSH_ERROR on error. - * - * @see ssh_key_free() - */ -int ssh_pki_import_pubkey_blob(const ssh_string key_blob, - ssh_key *pkey) { - ssh_buffer buffer; - ssh_string type_s = NULL; - enum ssh_keytypes_e type; - int rc; - - if (key_blob == NULL || pkey == NULL) { - return SSH_ERROR; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_pki_log("Out of memory!"); - return SSH_ERROR; - } - - rc = ssh_buffer_add_data(buffer, ssh_string_data(key_blob), - ssh_string_len(key_blob)); - if (rc < 0) { - ssh_pki_log("Out of memory!"); - goto fail; - } - - type_s = buffer_get_ssh_string(buffer); - if (type_s == NULL) { - ssh_pki_log("Out of memory!"); - goto fail; - } - - type = ssh_key_type_from_name(ssh_string_get_char(type_s)); - if (type == SSH_KEYTYPE_UNKNOWN) { - ssh_pki_log("Unknown key type found!"); - goto fail; - } - ssh_string_free(type_s); - - rc = pki_import_pubkey_buffer(buffer, type, pkey); - - ssh_buffer_free(buffer); - - return rc; -fail: - ssh_buffer_free(buffer); - ssh_string_free(type_s); - - return SSH_ERROR; -} - -/** - * @brief Import a public key from the given filename. - * - * @param[in] filename The path to the public key. - * - * @param[out] pkey A pointer to store the allocated public key. You need to - * free the memory. - * - * @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission - * denied, SSH_ERROR otherwise. - * - * @see ssh_key_free() - */ -int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey) -{ - enum ssh_keytypes_e type; - struct stat sb; - char *key_buf, *p; - const char *q; - FILE *file; - off_t size; - int rc; - - if (pkey == NULL || filename == NULL || *filename == '\0') { - return SSH_ERROR; - } - - file = fopen(filename, "r"); - if (file == NULL) { - ssh_pki_log("Error opening %s: %s", - filename, strerror(errno)); - return SSH_EOF; - } - - rc = fstat(fileno(file), &sb); - if (rc < 0) { - fclose(file); - ssh_pki_log("Error gettint stat of %s: %s", - filename, strerror(errno)); - switch (errno) { - case ENOENT: - case EACCES: - return SSH_EOF; - } - return SSH_ERROR; - } - - if (sb.st_size > MAX_PUBKEY_SIZE) { - fclose(file); - return SSH_ERROR; - } - - key_buf = malloc(sb.st_size + 1); - if (key_buf == NULL) { - fclose(file); - ssh_pki_log("Out of memory!"); - return SSH_ERROR; - } - - size = fread(key_buf, 1, sb.st_size, file); - fclose(file); - - if (size != sb.st_size) { - SAFE_FREE(key_buf); - ssh_pki_log("Error reading %s: %s", - filename, strerror(errno)); - return SSH_ERROR; - } - key_buf[size] = '\0'; - - q = p = key_buf; - while (!isspace((int)*p)) p++; - *p = '\0'; - - type = ssh_key_type_from_name(q); - if (type == SSH_KEYTYPE_UNKNOWN) { - SAFE_FREE(key_buf); - return SSH_ERROR; - } - q = ++p; - while (!isspace((int)*p)) p++; - *p = '\0'; - - rc = ssh_pki_import_pubkey_base64(q, type, pkey); - SAFE_FREE(key_buf); - - return rc; -} - -/** - * @brief Generates a keypair. - * - * @param[in] type Type of key to create - * - * @param[in] parameter Parameter to the creation of key: - * rsa : length of the key in bits (e.g. 1024, 2048, 4096) - * dsa : length of the key in bits (e.g. 1024, 2048, 3072) - * ecdsa : bits of the key (e.g. 256, 384, 512) - * @param[out] pkey A pointer to store the allocated private key. You need - * to free the memory. - * - * @return SSH_OK on success, SSH_ERROR on error. - * - * @warning Generating a key pair may take some time. - */ -int ssh_pki_generate(enum ssh_keytypes_e type, int parameter, - ssh_key *pkey){ - int rc; - ssh_key key = ssh_key_new(); - - if (key == NULL) { - return SSH_ERROR; - } - - key->type = type; - key->type_c = ssh_key_type_to_char(type); - key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; - - switch(type){ - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - rc = pki_key_generate_rsa(key, parameter); - if(rc == SSH_ERROR) - goto error; - break; - case SSH_KEYTYPE_DSS: - rc = pki_key_generate_dss(key, parameter); - if(rc == SSH_ERROR) - goto error; - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_ECC - rc = pki_key_generate_ecdsa(key, parameter); - if (rc == SSH_ERROR) { - goto error; - } - - /* Update key type */ - key->type_c = ssh_pki_key_ecdsa_name(key); - break; -#endif - case SSH_KEYTYPE_ED25519: - rc = pki_key_generate_ed25519(key); - if (rc == SSH_ERROR) { - goto error; - } - break; - case SSH_KEYTYPE_UNKNOWN: - goto error; - } - - *pkey = key; - return SSH_OK; -error: - ssh_key_free(key); - return SSH_ERROR; -} - -/** - * @brief Create a public key from a private key. - * - * @param[in] privkey The private key to get the public key from. - * - * @param[out] pkey A pointer to store the newly allocated public key. You - * NEED to free the key. - * - * @return A public key, NULL on error. - * - * @see ssh_key_free() - */ -int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey, - ssh_key *pkey) -{ - ssh_key pubkey; - - if (privkey == NULL || !ssh_key_is_private(privkey)) { - return SSH_ERROR; - } - - pubkey = pki_key_dup(privkey, 1); - if (pubkey == NULL) { - return SSH_ERROR; - } - - *pkey = pubkey; - return SSH_OK; -} - -/** - * @internal - * - * @brief Create a key_blob from a public key. - * - * The "key_blob" is encoded as per RFC 4253 section 6.6 "Public Key - * Algorithms" for any of the supported protocol 2 key types. - * - * @param[in] key A public or private key to create the public ssh_string - * from. - * - * @param[out] pblob A pointer to store the newly allocated key blob. You - * NEED to free it. - * - * @return SSH_OK on success, SSH_ERROR otherwise. - * - * @see ssh_string_free() - */ -int ssh_pki_export_pubkey_blob(const ssh_key key, - ssh_string *pblob) -{ - ssh_string blob; - - if (key == NULL) { - return SSH_OK; - } - - blob = pki_publickey_to_blob(key); - if (blob == NULL) { - return SSH_ERROR; - } - - *pblob = blob; - return SSH_OK; -} - -/** - * @brief Convert a public key to a base64 encoded key. - * - * @param[in] key The key to hash - * - * @param[out] b64_key A pointer to store the allocated base64 encoded key. You - * need to free the buffer. - * - * @return SSH_OK on success, SSH_ERROR on error. - * - * @see ssh_string_free_char() - */ -int ssh_pki_export_pubkey_base64(const ssh_key key, - char **b64_key) -{ - ssh_string key_blob; - unsigned char *b64; - - if (key == NULL || b64_key == NULL) { - return SSH_ERROR; - } - - key_blob = pki_publickey_to_blob(key); - if (key_blob == NULL) { - return SSH_ERROR; - } - - b64 = bin_to_base64(ssh_string_data(key_blob), ssh_string_len(key_blob)); - ssh_string_free(key_blob); - if (b64 == NULL) { - return SSH_ERROR; - } - - *b64_key = (char *)b64; - - return SSH_OK; -} - -int ssh_pki_export_pubkey_file(const ssh_key key, - const char *filename) -{ - char key_buf[4096]; - char host[256]; - char *b64_key; - char *user; - FILE *fp; - int rc; - - if (key == NULL || filename == NULL || *filename == '\0') { - return SSH_ERROR; - } - - user = ssh_get_local_username(); - if (user == NULL) { - return SSH_ERROR; - } - - rc = gethostname(host, sizeof(host)); - if (rc < 0) { - free(user); - return SSH_ERROR; - } - - rc = ssh_pki_export_pubkey_base64(key, &b64_key); - if (rc < 0) { - free(user); - return SSH_ERROR; - } - - rc = snprintf(key_buf, sizeof(key_buf), - "%s %s %s@%s\n", - key->type_c, - b64_key, - user, - host); - free(user); - free(b64_key); - if (rc < 0) { - return SSH_ERROR; - } - - fp = fopen(filename, "w+"); - if (fp == NULL) { - return SSH_ERROR; - } - rc = fwrite(key_buf, strlen(key_buf), 1, fp); - if (rc != 1 || ferror(fp)) { - fclose(fp); - unlink(filename); - return SSH_ERROR; - } - fclose(fp); - - return SSH_OK; -} - -int ssh_pki_export_pubkey_rsa1(const ssh_key key, - const char *host, - char *rsa1, - size_t rsa1_len) -{ - return pki_export_pubkey_rsa1(key, host, rsa1, rsa1_len); -} - -int ssh_pki_export_signature_blob(const ssh_signature sig, - ssh_string *sig_blob) -{ - ssh_buffer buf = NULL; - ssh_string str; - int rc; - - if (sig == NULL || sig_blob == NULL) { - return SSH_ERROR; - } - - buf = ssh_buffer_new(); - if (buf == NULL) { - return SSH_ERROR; - } - - str = ssh_string_from_char(sig->type_c); - if (str == NULL) { - ssh_buffer_free(buf); - return SSH_ERROR; - } - - rc = buffer_add_ssh_string(buf, str); - ssh_string_free(str); - if (rc < 0) { - ssh_buffer_free(buf); - return SSH_ERROR; - } - - str = pki_signature_to_blob(sig); - if (str == NULL) { - ssh_buffer_free(buf); - return SSH_ERROR; - } - - rc = buffer_add_ssh_string(buf, str); - ssh_string_free(str); - if (rc < 0) { - ssh_buffer_free(buf); - return SSH_ERROR; - } - - str = ssh_string_new(buffer_get_rest_len(buf)); - if (str == NULL) { - ssh_buffer_free(buf); - return SSH_ERROR; - } - - ssh_string_fill(str, buffer_get_rest(buf), buffer_get_rest_len(buf)); - ssh_buffer_free(buf); - - *sig_blob = str; - - return SSH_OK; -} - -int ssh_pki_import_signature_blob(const ssh_string sig_blob, - const ssh_key pubkey, - ssh_signature *psig) -{ - ssh_signature sig; - enum ssh_keytypes_e type; - ssh_string str; - ssh_buffer buf; - int rc; - - if (sig_blob == NULL || psig == NULL) { - return SSH_ERROR; - } - - buf = ssh_buffer_new(); - if (buf == NULL) { - return SSH_ERROR; - } - - rc = ssh_buffer_add_data(buf, - ssh_string_data(sig_blob), - ssh_string_len(sig_blob)); - if (rc < 0) { - ssh_buffer_free(buf); - return SSH_ERROR; - } - - str = buffer_get_ssh_string(buf); - if (str == NULL) { - ssh_buffer_free(buf); - return SSH_ERROR; - } - - type = ssh_key_type_from_name(ssh_string_get_char(str)); - ssh_string_free(str); - - str = buffer_get_ssh_string(buf); - ssh_buffer_free(buf); - if (str == NULL) { - return SSH_ERROR; - } - - sig = pki_signature_from_blob(pubkey, str, type); - ssh_string_free(str); - if (sig == NULL) { - return SSH_ERROR; - } - - *psig = sig; - return SSH_OK; -} - -int ssh_pki_signature_verify_blob(ssh_session session, - ssh_string sig_blob, - const ssh_key key, - unsigned char *digest, - size_t dlen) -{ - ssh_signature sig; - int rc; - - rc = ssh_pki_import_signature_blob(sig_blob, key, &sig); - if (rc < 0) { - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_FUNCTIONS, - "Going to verify a %s type signature", - key->type_c); - - - if (key->type == SSH_KEYTYPE_ECDSA) { -#if HAVE_ECC - unsigned char ehash[EVP_DIGEST_LEN] = {0}; - uint32_t elen; - - evp(key->ecdsa_nid, digest, dlen, ehash, &elen); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Hash to be verified with ecdsa", - ehash, elen); -#endif - - rc = pki_signature_verify(session, - sig, - key, - ehash, - elen); -#endif - } else if (key->type == SSH_KEYTYPE_ED25519) { - rc = pki_signature_verify(session, sig, key, digest, dlen); - } else { - unsigned char hash[SHA_DIGEST_LEN] = {0}; - - sha1(digest, dlen, hash); -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Hash to be verified with dsa", hash, SHA_DIGEST_LEN); -#endif - - rc = pki_signature_verify(session, - sig, - key, - hash, - SHA_DIGEST_LEN); - } - - ssh_signature_free(sig); - - return rc; -} - -/* - * This function signs the session id as a string then - * the content of sigbuf */ -ssh_string ssh_pki_do_sign(ssh_session session, - ssh_buffer sigbuf, - const ssh_key privkey) { - struct ssh_crypto_struct *crypto = - session->current_crypto ? session->current_crypto : - session->next_crypto; - ssh_signature sig; - ssh_string sig_blob; - ssh_string session_id; - int rc; - - if (privkey == NULL || !ssh_key_is_private(privkey)) { - return NULL; - } - - session_id = ssh_string_new(crypto->digest_len); - if (session_id == NULL) { - return NULL; - } - ssh_string_fill(session_id, crypto->session_id, crypto->digest_len); - - if (privkey->type == SSH_KEYTYPE_ECDSA) { -#ifdef HAVE_ECC - unsigned char ehash[EVP_DIGEST_LEN] = {0}; - uint32_t elen; - EVPCTX ctx; - - ctx = evp_init(privkey->ecdsa_nid); - if (ctx == NULL) { - ssh_string_free(session_id); - return NULL; - } - - evp_update(ctx, session_id, ssh_string_len(session_id) + 4); - evp_update(ctx, buffer_get_rest(sigbuf), buffer_get_rest_len(sigbuf)); - evp_final(ctx, ehash, &elen); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Hash being signed", ehash, elen); -#endif - - sig = pki_do_sign(privkey, ehash, elen); -#endif - } else if (privkey->type == SSH_KEYTYPE_ED25519){ - ssh_buffer buf; - - buf = ssh_buffer_new(); - if (buf == NULL) { - ssh_string_free(session_id); - return NULL; - } - - ssh_buffer_set_secure(buf); - rc = ssh_buffer_pack(buf, - "SP", - session_id, - buffer_get_rest_len(sigbuf), buffer_get_rest(sigbuf)); - if (rc != SSH_OK) { - ssh_string_free(session_id); - ssh_buffer_free(buf); - return NULL; - } - - sig = pki_do_sign(privkey, - ssh_buffer_get_begin(buf), - ssh_buffer_get_len(buf)); - ssh_buffer_free(buf); - } else { - unsigned char hash[SHA_DIGEST_LEN] = {0}; - SHACTX ctx; - - ctx = sha1_init(); - if (ctx == NULL) { - ssh_string_free(session_id); - return NULL; - } - - sha1_update(ctx, session_id, ssh_string_len(session_id) + 4); - sha1_update(ctx, buffer_get_rest(sigbuf), buffer_get_rest_len(sigbuf)); - sha1_final(hash, ctx); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN); -#endif - - sig = pki_do_sign(privkey, hash, SHA_DIGEST_LEN); - } - ssh_string_free(session_id); - if (sig == NULL) { - return NULL; - } - - rc = ssh_pki_export_signature_blob(sig, &sig_blob); - ssh_signature_free(sig); - if (rc < 0) { - return NULL; - } - - return sig_blob; -} - -#ifndef _WIN32 -ssh_string ssh_pki_do_sign_agent(ssh_session session, - struct ssh_buffer_struct *buf, - const ssh_key pubkey) { - struct ssh_crypto_struct *crypto; - ssh_string session_id; - ssh_string sig_blob; - ssh_buffer sig_buf; - int rc; - - if (session->current_crypto) { - crypto = session->current_crypto; - } else { - crypto = session->next_crypto; - } - - /* prepend session identifier */ - session_id = ssh_string_new(crypto->digest_len); - if (session_id == NULL) { - return NULL; - } - ssh_string_fill(session_id, crypto->session_id, crypto->digest_len); - - sig_buf = ssh_buffer_new(); - if (sig_buf == NULL) { - ssh_string_free(session_id); - return NULL; - } - - rc = buffer_add_ssh_string(sig_buf, session_id); - if (rc < 0) { - ssh_string_free(session_id); - ssh_buffer_free(sig_buf); - return NULL; - } - ssh_string_free(session_id); - - /* append out buffer */ - if (buffer_add_buffer(sig_buf, buf) < 0) { - ssh_buffer_free(sig_buf); - return NULL; - } - - /* create signature */ - sig_blob = ssh_agent_sign_data(session, pubkey, sig_buf); - - ssh_buffer_free(sig_buf); - - return sig_blob; -} -#endif /* _WIN32 */ - -#ifdef WITH_SERVER -ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, - const ssh_key privkey) -{ - struct ssh_crypto_struct *crypto; - ssh_signature sig; - ssh_string sig_blob; - int rc; - - if (session == NULL || privkey == NULL || !ssh_key_is_private(privkey)) { - return NULL; - } - crypto = session->next_crypto ? session->next_crypto : - session->current_crypto; - - if (crypto->secret_hash == NULL){ - ssh_set_error(session,SSH_FATAL,"Missing secret_hash"); - return NULL; - } - - if (privkey->type == SSH_KEYTYPE_ECDSA) { -#ifdef HAVE_ECC - unsigned char ehash[EVP_DIGEST_LEN] = {0}; - uint32_t elen; - - evp(privkey->ecdsa_nid, crypto->secret_hash, crypto->digest_len, - ehash, &elen); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Hash being signed", ehash, elen); -#endif - - sig = pki_do_sign_sessionid(privkey, ehash, elen); - if (sig == NULL) { - return NULL; - } -#endif - } else if (privkey->type == SSH_KEYTYPE_ED25519) { - sig = ssh_signature_new(); - if (sig == NULL){ - return NULL; - } - - sig->type = privkey->type; - sig->type_c = privkey->type_c; - - rc = pki_ed25519_sign(privkey, - sig, - crypto->secret_hash, - crypto->digest_len); - if (rc != SSH_OK){ - ssh_signature_free(sig); - sig = NULL; - } - } else { - unsigned char hash[SHA_DIGEST_LEN] = {0}; - SHACTX ctx; - - ctx = sha1_init(); - if (ctx == NULL) { - return NULL; - } - sha1_update(ctx, crypto->secret_hash, crypto->digest_len); - sha1_final(hash, ctx); - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN); -#endif - - sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN); - if (sig == NULL) { - return NULL; - } - } - - rc = ssh_pki_export_signature_blob(sig, &sig_blob); - ssh_signature_free(sig); - if (rc < 0) { - return NULL; - } - - return sig_blob; -} -#endif /* WITH_SERVER */ - -/** - * @} - */ diff --git a/libssh/src/pki_crypto.c b/libssh/src/pki_crypto.c deleted file mode 100644 index 5706fdf0..00000000 --- a/libssh/src/pki_crypto.c +++ /dev/null @@ -1,1671 +0,0 @@ -/* - * pki_crypto.c - PKI infrastructure using OpenSSL - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 by Aris Adamantiadis - * Copyright (c) 2009-2013 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#ifndef _PKI_CRYPTO_H -#define _PKI_CRYPTO_H - -#include "libssh/priv.h" - -#include -#include -#include -#include - -#ifdef HAVE_OPENSSL_EC_H -#include -#endif -#ifdef HAVE_OPENSSL_ECDSA_H -#include -#endif - -#include "libssh/libssh.h" -#include "libssh/buffer.h" -#include "libssh/session.h" -#include "libssh/pki.h" -#include "libssh/pki_priv.h" -#include "libssh/bignum.h" - -struct pem_get_password_struct { - ssh_auth_callback fn; - void *data; -}; - -static int pem_get_password(char *buf, int size, int rwflag, void *userdata) { - struct pem_get_password_struct *pgp = userdata; - - (void) rwflag; /* unused */ - - if (buf == NULL) { - return 0; - } - - memset(buf, '\0', size); - if (pgp) { - int rc; - - rc = pgp->fn("Passphrase for private key:", - buf, size, 0, 0, - pgp->data); - if (rc == 0) { - return strlen(buf); - } - } - - return 0; -} - -#ifdef HAVE_OPENSSL_ECC -static int pki_key_ecdsa_to_nid(EC_KEY *k) -{ - const EC_GROUP *g = EC_KEY_get0_group(k); - int nid; - - nid = EC_GROUP_get_curve_name(g); - if (nid) { - return nid; - } - - return -1; -} - -const char *pki_key_ecdsa_nid_to_name(int nid) -{ - switch (nid) { - case NID_X9_62_prime256v1: - return "ecdsa-sha2-nistp256"; - case NID_secp384r1: - return "ecdsa-sha2-nistp384"; - case NID_secp521r1: - return "ecdsa-sha2-nistp521"; - default: - break; - } - - return "unknown"; -} - -static const char *pki_key_ecdsa_nid_to_char(int nid) -{ - switch (nid) { - case NID_X9_62_prime256v1: - return "nistp256"; - case NID_secp384r1: - return "nistp384"; - case NID_secp521r1: - return "nistp521"; - default: - break; - } - - return "unknown"; -} - -int pki_key_ecdsa_nid_from_name(const char *name) -{ - if (strcmp(name, "nistp256") == 0) { - return NID_X9_62_prime256v1; - } else if (strcmp(name, "nistp384") == 0) { - return NID_secp384r1; - } else if (strcmp(name, "nistp521") == 0) { - return NID_secp521r1; - } - - return -1; -} - -static ssh_string make_ecpoint_string(const EC_GROUP *g, - const EC_POINT *p) -{ - ssh_string s; - size_t len; - - len = EC_POINT_point2oct(g, - p, - POINT_CONVERSION_UNCOMPRESSED, - NULL, - 0, - NULL); - if (len == 0) { - return NULL; - } - - s = ssh_string_new(len); - if (s == NULL) { - return NULL; - } - - len = EC_POINT_point2oct(g, - p, - POINT_CONVERSION_UNCOMPRESSED, - ssh_string_data(s), - ssh_string_len(s), - NULL); - if (len != ssh_string_len(s)) { - ssh_string_free(s); - return NULL; - } - - return s; -} - -int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e) -{ - EC_POINT *p; - const EC_GROUP *g; - int ok; - - key->ecdsa_nid = nid; - key->type_c = pki_key_ecdsa_nid_to_name(nid); - - key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid); - if (key->ecdsa == NULL) { - return -1; - } - - g = EC_KEY_get0_group(key->ecdsa); - - p = EC_POINT_new(g); - if (p == NULL) { - return -1; - } - - ok = EC_POINT_oct2point(g, - p, - ssh_string_data(e), - ssh_string_len(e), - NULL); - if (!ok) { - EC_POINT_free(p); - return -1; - } - - /* EC_KEY_set_public_key duplicates p */ - ok = EC_KEY_set_public_key(key->ecdsa, p); - EC_POINT_free(p); - if (!ok) { - return -1; - } - - return 0; -} -#endif - -ssh_key pki_key_dup(const ssh_key key, int demote) -{ - ssh_key new; - int rc; - - new = ssh_key_new(); - if (new == NULL) { - return NULL; - } - - new->type = key->type; - new->type_c = key->type_c; - if (demote) { - new->flags = SSH_KEY_FLAG_PUBLIC; - } else { - new->flags = key->flags; - } - - switch (key->type) { - case SSH_KEYTYPE_DSS: - new->dsa = DSA_new(); - if (new->dsa == NULL) { - goto fail; - } - - /* - * p = public prime number - * q = public 160-bit subprime, q | p-1 - * g = public generator of subgroup - * pub_key = public key y = g^x - * priv_key = private key x - */ - new->dsa->p = BN_dup(key->dsa->p); - if (new->dsa->p == NULL) { - goto fail; - } - - new->dsa->q = BN_dup(key->dsa->q); - if (new->dsa->q == NULL) { - goto fail; - } - - new->dsa->g = BN_dup(key->dsa->g); - if (new->dsa->g == NULL) { - goto fail; - } - - new->dsa->pub_key = BN_dup(key->dsa->pub_key); - if (new->dsa->pub_key == NULL) { - goto fail; - } - - if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { - new->dsa->priv_key = BN_dup(key->dsa->priv_key); - if (new->dsa->priv_key == NULL) { - goto fail; - } - } - - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - new->rsa = RSA_new(); - if (new->rsa == NULL) { - goto fail; - } - - /* - * n = public modulus - * e = public exponent - * d = private exponent - * p = secret prime factor - * q = secret prime factor - * dmp1 = d mod (p-1) - * dmq1 = d mod (q-1) - * iqmp = q^-1 mod p - */ - new->rsa->n = BN_dup(key->rsa->n); - if (new->rsa->n == NULL) { - goto fail; - } - - new->rsa->e = BN_dup(key->rsa->e); - if (new->rsa->e == NULL) { - goto fail; - } - - if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { - new->rsa->d = BN_dup(key->rsa->d); - if (new->rsa->d == NULL) { - goto fail; - } - - /* p, q, dmp1, dmq1 and iqmp may be NULL in private keys, but the - * RSA operations are much faster when these values are available. - */ - if (key->rsa->p != NULL) { - new->rsa->p = BN_dup(key->rsa->p); - if (new->rsa->p == NULL) { - goto fail; - } - } - - if (key->rsa->q != NULL) { - new->rsa->q = BN_dup(key->rsa->q); - if (new->rsa->q == NULL) { - goto fail; - } - } - - if (key->rsa->dmp1 != NULL) { - new->rsa->dmp1 = BN_dup(key->rsa->dmp1); - if (new->rsa->dmp1 == NULL) { - goto fail; - } - } - - if (key->rsa->dmq1 != NULL) { - new->rsa->dmq1 = BN_dup(key->rsa->dmq1); - if (new->rsa->dmq1 == NULL) { - goto fail; - } - } - - if (key->rsa->iqmp != NULL) { - new->rsa->iqmp = BN_dup(key->rsa->iqmp); - if (new->rsa->iqmp == NULL) { - goto fail; - } - } - } - - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - new->ecdsa_nid = key->ecdsa_nid; - - /* privkey -> pubkey */ - if (demote && ssh_key_is_private(key)) { - const EC_POINT *p; - int ok; - - new->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid); - if (new->ecdsa == NULL) { - goto fail; - } - - p = EC_KEY_get0_public_key(key->ecdsa); - if (p == NULL) { - goto fail; - } - - ok = EC_KEY_set_public_key(new->ecdsa, p); - if (!ok) { - goto fail; - } - } else { - new->ecdsa = EC_KEY_dup(key->ecdsa); - } - break; -#endif - case SSH_KEYTYPE_ED25519: - rc = pki_ed25519_key_dup(new, key); - if (rc != SSH_OK) { - goto fail; - } - break; - case SSH_KEYTYPE_UNKNOWN: - default: - ssh_key_free(new); - return NULL; - } - - return new; -fail: - ssh_key_free(new); - return NULL; -} - -int pki_key_generate_rsa(ssh_key key, int parameter){ - BIGNUM *e; - int rc; - - e = BN_new(); - key->rsa = RSA_new(); - - BN_set_word(e, 65537); - rc = RSA_generate_key_ex(key->rsa, parameter, e, NULL); - - BN_free(e); - - if (rc == -1 || key->rsa == NULL) - return SSH_ERROR; - return SSH_OK; -} - -int pki_key_generate_dss(ssh_key key, int parameter){ - int rc; - key->dsa = DSA_generate_parameters(parameter, NULL, 0, NULL, NULL, - NULL, NULL); - if(key->dsa == NULL){ - return SSH_ERROR; - } - rc = DSA_generate_key(key->dsa); - if (rc != 1){ - DSA_free(key->dsa); - key->dsa=NULL; - return SSH_ERROR; - } - return SSH_OK; -} - -#ifdef HAVE_OPENSSL_ECC -int pki_key_generate_ecdsa(ssh_key key, int parameter) { - int nid; - int ok; - - switch (parameter) { - case 384: - nid = NID_secp384r1; - break; - case 512: - nid = NID_secp521r1; - break; - case 256: - default: - nid = NID_X9_62_prime256v1; - } - - key->ecdsa_nid = nid; - key->type = SSH_KEYTYPE_ECDSA; - key->type_c = pki_key_ecdsa_nid_to_name(nid); - - key->ecdsa = EC_KEY_new_by_curve_name(nid); - if (key->ecdsa == NULL) { - return SSH_ERROR; - } - - ok = EC_KEY_generate_key(key->ecdsa); - if (!ok) { - EC_KEY_free(key->ecdsa); - return SSH_ERROR; - } - - EC_KEY_set_asn1_flag(key->ecdsa, OPENSSL_EC_NAMED_CURVE); - - return SSH_OK; -} -#endif - -int pki_key_compare(const ssh_key k1, - const ssh_key k2, - enum ssh_keycmp_e what) -{ - switch (k1->type) { - case SSH_KEYTYPE_DSS: - if (DSA_size(k1->dsa) != DSA_size(k2->dsa)) { - return 1; - } - if (bignum_cmp(k1->dsa->p, k2->dsa->p) != 0) { - return 1; - } - if (bignum_cmp(k1->dsa->q, k2->dsa->q) != 0) { - return 1; - } - if (bignum_cmp(k1->dsa->g, k2->dsa->g) != 0) { - return 1; - } - if (bignum_cmp(k1->dsa->pub_key, k2->dsa->pub_key) != 0) { - return 1; - } - - if (what == SSH_KEY_CMP_PRIVATE) { - if (bignum_cmp(k1->dsa->priv_key, k2->dsa->priv_key) != 0) { - return 1; - } - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - if (RSA_size(k1->rsa) != RSA_size(k2->rsa)) { - return 1; - } - if (bignum_cmp(k1->rsa->e, k2->rsa->e) != 0) { - return 1; - } - if (bignum_cmp(k1->rsa->n, k2->rsa->n) != 0) { - return 1; - } - - if (what == SSH_KEY_CMP_PRIVATE) { - if (bignum_cmp(k1->rsa->p, k2->rsa->p) != 0) { - return 1; - } - - if (bignum_cmp(k1->rsa->q, k2->rsa->q) != 0) { - return 1; - } - } - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - { - const EC_POINT *p1 = EC_KEY_get0_public_key(k1->ecdsa); - const EC_POINT *p2 = EC_KEY_get0_public_key(k2->ecdsa); - const EC_GROUP *g1 = EC_KEY_get0_group(k1->ecdsa); - const EC_GROUP *g2 = EC_KEY_get0_group(k2->ecdsa); - - if (p1 == NULL || p2 == NULL) { - return 1; - } - - if (EC_GROUP_cmp(g1, g2, NULL) != 0) { - return 1; - } - - if (EC_POINT_cmp(g1, p1, p2, NULL) != 0) { - return 1; - } - - if (what == SSH_KEY_CMP_PRIVATE) { - if (bignum_cmp(EC_KEY_get0_private_key(k1->ecdsa), - EC_KEY_get0_private_key(k2->ecdsa))) { - return 1; - } - } - - break; - } -#endif - case SSH_KEYTYPE_ED25519: - /* ed25519 keys handled globaly */ - case SSH_KEYTYPE_UNKNOWN: - default: - return 1; - } - - return 0; -} - -ssh_string pki_private_key_to_pem(const ssh_key key, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data) -{ - ssh_string blob; - BUF_MEM *buf; - BIO *mem; - int rc; - - /* needed for openssl initialization */ - if (ssh_init() < 0) { - return NULL; - } - - mem = BIO_new(BIO_s_mem()); - if (mem == NULL) { - return NULL; - } - - switch (key->type) { - case SSH_KEYTYPE_DSS: - if (passphrase == NULL) { - struct pem_get_password_struct pgp = { auth_fn, auth_data }; - - rc = PEM_write_bio_DSAPrivateKey(mem, - key->dsa, - NULL, /* cipher */ - NULL, /* kstr */ - 0, /* klen */ - pem_get_password, - &pgp); - } else { - rc = PEM_write_bio_DSAPrivateKey(mem, - key->dsa, - NULL, /* cipher */ - NULL, /* kstr */ - 0, /* klen */ - NULL, /* auth_fn */ - (void*) passphrase); - } - if (rc != 1) { - goto err; - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - if (passphrase == NULL) { - struct pem_get_password_struct pgp = { auth_fn, auth_data }; - - rc = PEM_write_bio_RSAPrivateKey(mem, - key->rsa, - NULL, /* cipher */ - NULL, /* kstr */ - 0, /* klen */ - pem_get_password, - &pgp); - } else { - rc = PEM_write_bio_RSAPrivateKey(mem, - key->rsa, - NULL, /* cipher */ - NULL, /* kstr */ - 0, /* klen */ - NULL, /* auth_fn */ - (void*) passphrase); - } - if (rc != 1) { - goto err; - } - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_ECC - if (passphrase == NULL) { - struct pem_get_password_struct pgp = { auth_fn, auth_data }; - - rc = PEM_write_bio_ECPrivateKey(mem, - key->ecdsa, - NULL, /* cipher */ - NULL, /* kstr */ - 0, /* klen */ - pem_get_password, - &pgp); - } else { - rc = PEM_write_bio_ECPrivateKey(mem, - key->ecdsa, - NULL, /* cipher */ - NULL, /* kstr */ - 0, /* klen */ - NULL, /* auth_fn */ - (void*) passphrase); - } - if (rc != 1) { - goto err; - } - break; -#endif - case SSH_KEYTYPE_ED25519: - case SSH_KEYTYPE_UNKNOWN: - BIO_free(mem); - ssh_pki_log("Unkown or invalid private key type %d", key->type); - return NULL; - } - - BIO_get_mem_ptr(mem, &buf); - - blob = ssh_string_new(buf->length); - if (blob == NULL) { - goto err; - } - - ssh_string_fill(blob, buf->data, buf->length); - BIO_free(mem); - - return blob; -err: - BIO_free(mem); - return NULL; -} - -ssh_key pki_private_key_from_base64(const char *b64_key, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data) { - BIO *mem = NULL; - DSA *dsa = NULL; - RSA *rsa = NULL; - ssh_key key; - enum ssh_keytypes_e type; -#ifdef HAVE_OPENSSL_ECC - EC_KEY *ecdsa = NULL; -#else - void *ecdsa = NULL; -#endif - - /* needed for openssl initialization */ - if (ssh_init() < 0) { - return NULL; - } - - type = pki_privatekey_type_from_string(b64_key); - if (type == SSH_KEYTYPE_UNKNOWN) { - ssh_pki_log("Unknown or invalid private key."); - return NULL; - } - - mem = BIO_new_mem_buf((void*)b64_key, -1); - - switch (type) { - case SSH_KEYTYPE_DSS: - if (passphrase == NULL) { - if (auth_fn) { - struct pem_get_password_struct pgp = { auth_fn, auth_data }; - - dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, pem_get_password, &pgp); - } else { - /* openssl uses its own callback to get the passphrase here */ - dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, NULL, NULL); - } - } else { - dsa = PEM_read_bio_DSAPrivateKey(mem, NULL, NULL, (void *) passphrase); - } - - BIO_free(mem); - - if (dsa == NULL) { - ssh_pki_log("Parsing private key: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NULL; - } - - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - if (passphrase == NULL) { - if (auth_fn) { - struct pem_get_password_struct pgp = { auth_fn, auth_data }; - - rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, pem_get_password, &pgp); - } else { - /* openssl uses its own callback to get the passphrase here */ - rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL); - } - } else { - rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, (void *) passphrase); - } - - BIO_free(mem); - - if (rsa == NULL) { - ssh_pki_log("Parsing private key: %s", - ERR_error_string(ERR_get_error(),NULL)); - return NULL; - } - - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - if (passphrase == NULL) { - if (auth_fn) { - struct pem_get_password_struct pgp = { auth_fn, auth_data }; - - ecdsa = PEM_read_bio_ECPrivateKey(mem, NULL, pem_get_password, &pgp); - } else { - /* openssl uses its own callback to get the passphrase here */ - ecdsa = PEM_read_bio_ECPrivateKey(mem, NULL, NULL, NULL); - } - } else { - ecdsa = PEM_read_bio_ECPrivateKey(mem, NULL, NULL, (void *) passphrase); - } - - BIO_free(mem); - - if (ecdsa == NULL) { - ssh_pki_log("Parsing private key: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NULL; - } - - break; -#endif - case SSH_KEYTYPE_ED25519: - case SSH_KEYTYPE_UNKNOWN: - BIO_free(mem); - ssh_pki_log("Unkown or invalid private key type %d", type); - return NULL; - } - - key = ssh_key_new(); - if (key == NULL) { - goto fail; - } - - key->type = type; - key->type_c = ssh_key_type_to_char(type); - key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; - key->dsa = dsa; - key->rsa = rsa; - key->ecdsa = ecdsa; -#ifdef HAVE_OPENSSL_ECC - if (key->type == SSH_KEYTYPE_ECDSA) { - key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa); - key->type_c = pki_key_ecdsa_nid_to_name(key->ecdsa_nid); - } -#endif - - return key; -fail: - ssh_key_free(key); - DSA_free(dsa); - RSA_free(rsa); -#ifdef HAVE_OPENSSL_ECC - EC_KEY_free(ecdsa); -#endif - - return NULL; -} - -int pki_pubkey_build_dss(ssh_key key, - ssh_string p, - ssh_string q, - ssh_string g, - ssh_string pubkey) { - key->dsa = DSA_new(); - if (key->dsa == NULL) { - return SSH_ERROR; - } - - key->dsa->p = make_string_bn(p); - key->dsa->q = make_string_bn(q); - key->dsa->g = make_string_bn(g); - key->dsa->pub_key = make_string_bn(pubkey); - if (key->dsa->p == NULL || - key->dsa->q == NULL || - key->dsa->g == NULL || - key->dsa->pub_key == NULL) { - DSA_free(key->dsa); - return SSH_ERROR; - } - - return SSH_OK; -} - -int pki_pubkey_build_rsa(ssh_key key, - ssh_string e, - ssh_string n) { - key->rsa = RSA_new(); - if (key->rsa == NULL) { - return SSH_ERROR; - } - - key->rsa->e = make_string_bn(e); - key->rsa->n = make_string_bn(n); - if (key->rsa->e == NULL || - key->rsa->n == NULL) { - RSA_free(key->rsa); - return SSH_ERROR; - } - - return SSH_OK; -} - -ssh_string pki_publickey_to_blob(const ssh_key key) -{ - ssh_buffer buffer; - ssh_string type_s; - ssh_string str = NULL; - ssh_string e = NULL; - ssh_string n = NULL; - ssh_string p = NULL; - ssh_string g = NULL; - ssh_string q = NULL; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - return NULL; - } - - type_s = ssh_string_from_char(key->type_c); - if (type_s == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - - rc = buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - switch (key->type) { - case SSH_KEYTYPE_DSS: - p = make_bignum_string(key->dsa->p); - if (p == NULL) { - goto fail; - } - - q = make_bignum_string(key->dsa->q); - if (q == NULL) { - goto fail; - } - - g = make_bignum_string(key->dsa->g); - if (g == NULL) { - goto fail; - } - - n = make_bignum_string(key->dsa->pub_key); - if (n == NULL) { - goto fail; - } - - if (buffer_add_ssh_string(buffer, p) < 0) { - goto fail; - } - if (buffer_add_ssh_string(buffer, q) < 0) { - goto fail; - } - if (buffer_add_ssh_string(buffer, g) < 0) { - goto fail; - } - if (buffer_add_ssh_string(buffer, n) < 0) { - goto fail; - } - - ssh_string_burn(p); - ssh_string_free(p); - p = NULL; - ssh_string_burn(g); - ssh_string_free(g); - g = NULL; - ssh_string_burn(q); - ssh_string_free(q); - q = NULL; - ssh_string_burn(n); - ssh_string_free(n); - n = NULL; - - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - e = make_bignum_string(key->rsa->e); - if (e == NULL) { - goto fail; - } - - n = make_bignum_string(key->rsa->n); - if (n == NULL) { - goto fail; - } - - if (buffer_add_ssh_string(buffer, e) < 0) { - goto fail; - } - if (buffer_add_ssh_string(buffer, n) < 0) { - goto fail; - } - - ssh_string_burn(e); - ssh_string_free(e); - e = NULL; - ssh_string_burn(n); - ssh_string_free(n); - n = NULL; - - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - rc = ssh_buffer_reinit(buffer); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_name(key->ecdsa_nid)); - if (type_s == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - - rc = buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_char(key->ecdsa_nid)); - if (type_s == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - - rc = buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - e = make_ecpoint_string(EC_KEY_get0_group(key->ecdsa), - EC_KEY_get0_public_key(key->ecdsa)); - if (e == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - - rc = buffer_add_ssh_string(buffer, e); - if (rc < 0) { - goto fail; - } - - ssh_string_burn(e); - ssh_string_free(e); - e = NULL; - - break; -#endif - case SSH_KEYTYPE_ED25519: - rc = pki_ed25519_public_key_to_blob(buffer, key); - if (rc == SSH_ERROR){ - goto fail; - } - break; - case SSH_KEYTYPE_UNKNOWN: - default: - goto fail; - } - - str = ssh_string_new(buffer_get_rest_len(buffer)); - if (str == NULL) { - goto fail; - } - - rc = ssh_string_fill(str, buffer_get_rest(buffer), buffer_get_rest_len(buffer)); - if (rc < 0) { - goto fail; - } - ssh_buffer_free(buffer); - - return str; -fail: - ssh_buffer_free(buffer); - ssh_string_burn(str); - ssh_string_free(str); - ssh_string_burn(e); - ssh_string_free(e); - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(g); - ssh_string_free(g); - ssh_string_burn(q); - ssh_string_free(q); - ssh_string_burn(n); - ssh_string_free(n); - - return NULL; -} - -int pki_export_pubkey_rsa1(const ssh_key key, - const char *host, - char *rsa1, - size_t rsa1_len) -{ - char *e; - char *n; - int rsa_size = RSA_size(key->rsa); - - e = bignum_bn2dec(key->rsa->e); - if (e == NULL) { - return SSH_ERROR; - } - - n = bignum_bn2dec(key->rsa->n); - if (n == NULL) { - OPENSSL_free(e); - return SSH_ERROR; - } - - snprintf(rsa1, rsa1_len, - "%s %d %s %s\n", - host, rsa_size << 3, e, n); - OPENSSL_free(e); - OPENSSL_free(n); - - return SSH_OK; -} - -/** - * @internal - * - * @brief Compute a digital signature. - * - * @param[in] digest The message digest. - * - * @param[in] dlen The length of the digest. - * - * @param[in] privkey The private rsa key to use for signing. - * - * @return A newly allocated rsa sig blob or NULL on error. - */ -static ssh_string _RSA_do_sign(const unsigned char *digest, - int dlen, - RSA *privkey) -{ - ssh_string sig_blob; - unsigned char *sig; - unsigned int slen; - int ok; - - sig = malloc(RSA_size(privkey)); - if (sig == NULL) { - return NULL; - } - - ok = RSA_sign(NID_sha1, digest, dlen, sig, &slen, privkey); - if (!ok) { - SAFE_FREE(sig); - return NULL; - } - - sig_blob = ssh_string_new(slen); - if (sig_blob == NULL) { - SAFE_FREE(sig); - return NULL; - } - - ssh_string_fill(sig_blob, sig, slen); - memset(sig, 'd', slen); - SAFE_FREE(sig); - - return sig_blob; -} - -static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig) -{ - char buffer[40] = { 0 }; - ssh_string sig_blob = NULL; - - ssh_string r; - int r_len, r_offset_in, r_offset_out; - - ssh_string s; - int s_len, s_offset_in, s_offset_out; - - r = make_bignum_string(sig->dsa_sig->r); - if (r == NULL) { - return NULL; - } - - s = make_bignum_string(sig->dsa_sig->s); - if (s == NULL) { - ssh_string_free(r); - return NULL; - } - - r_len = ssh_string_len(r); - r_offset_in = (r_len > 20) ? (r_len - 20) : 0; - r_offset_out = (r_len < 20) ? (20 - r_len) : 0; - - s_len = ssh_string_len(s); - s_offset_in = (s_len > 20) ? (s_len - 20) : 0; - s_offset_out = (s_len < 20) ? (20 - s_len) : 0; - - memcpy(buffer + r_offset_out, - ((char *)ssh_string_data(r)) + r_offset_in, - r_len - r_offset_in); - memcpy(buffer + 20 + s_offset_out, - ((char *)ssh_string_data(s)) + s_offset_in, - s_len - s_offset_in); - - ssh_string_free(r); - ssh_string_free(s); - - sig_blob = ssh_string_new(40); - if (sig_blob == NULL) { - return NULL; - } - - ssh_string_fill(sig_blob, buffer, 40); - - return sig_blob; -} - -ssh_string pki_signature_to_blob(const ssh_signature sig) -{ - ssh_string sig_blob = NULL; - - switch(sig->type) { - case SSH_KEYTYPE_DSS: - sig_blob = pki_dsa_signature_to_blob(sig); - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - sig_blob = ssh_string_copy(sig->rsa_sig); - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - { - ssh_string r; - ssh_string s; - ssh_buffer b; - int rc; - - b = ssh_buffer_new(); - if (b == NULL) { - return NULL; - } - - r = make_bignum_string(sig->ecdsa_sig->r); - if (r == NULL) { - ssh_buffer_free(b); - return NULL; - } - rc = buffer_add_ssh_string(b, r); - ssh_string_free(r); - if (rc < 0) { - ssh_buffer_free(b); - return NULL; - } - - s = make_bignum_string(sig->ecdsa_sig->s); - if (s == NULL) { - ssh_buffer_free(b); - return NULL; - } - rc = buffer_add_ssh_string(b, s); - ssh_string_free(s); - if (rc < 0) { - ssh_buffer_free(b); - return NULL; - } - - sig_blob = ssh_string_new(buffer_get_rest_len(b)); - if (sig_blob == NULL) { - ssh_buffer_free(b); - return NULL; - } - - ssh_string_fill(sig_blob, buffer_get_rest(b), buffer_get_rest_len(b)); - ssh_buffer_free(b); - break; - } -#endif - case SSH_KEYTYPE_ED25519: - sig_blob = pki_ed25519_sig_to_blob(sig); - break; - default: - case SSH_KEYTYPE_UNKNOWN: - ssh_pki_log("Unknown signature key type: %s", sig->type_c); - return NULL; - } - - return sig_blob; -} - -static ssh_signature pki_signature_from_rsa_blob(const ssh_key pubkey, - const ssh_string sig_blob, - ssh_signature sig) -{ - uint32_t pad_len = 0; - char *blob_orig; - char *blob_padded_data; - ssh_string sig_blob_padded; - - size_t rsalen = 0; - size_t len = ssh_string_len(sig_blob); - - if (pubkey->rsa == NULL) { - ssh_pki_log("Pubkey RSA field NULL"); - goto errout; - } - - rsalen = RSA_size(pubkey->rsa); - if (len > rsalen) { - ssh_pki_log("Signature is too big: %lu > %lu", - (unsigned long)len, (unsigned long)rsalen); - goto errout; - } - -#ifdef DEBUG_CRYPTO - ssh_pki_log("RSA signature len: %lu", (unsigned long)len); - ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); -#endif - - if (len == rsalen) { - sig->rsa_sig = ssh_string_copy(sig_blob); - } else { - /* pad the blob to the expected rsalen size */ - ssh_pki_log("RSA signature len %lu < %lu", - (unsigned long)len, (unsigned long)rsalen); - - pad_len = rsalen - len; - - sig_blob_padded = ssh_string_new(rsalen); - if (sig_blob_padded == NULL) { - goto errout; - } - - blob_padded_data = (char *) ssh_string_data(sig_blob_padded); - blob_orig = (char *) ssh_string_data(sig_blob); - - /* front-pad the buffer with zeroes */ - BURN_BUFFER(blob_padded_data, pad_len); - /* fill the rest with the actual signature blob */ - memcpy(blob_padded_data + pad_len, blob_orig, len); - - sig->rsa_sig = sig_blob_padded; - } - - return sig; - -errout: - ssh_signature_free(sig); - return NULL; -} - -ssh_signature pki_signature_from_blob(const ssh_key pubkey, - const ssh_string sig_blob, - enum ssh_keytypes_e type) -{ - ssh_signature sig; - ssh_string r; - ssh_string s; - size_t len; - int rc; - - sig = ssh_signature_new(); - if (sig == NULL) { - return NULL; - } - - sig->type = type; - sig->type_c = ssh_key_type_to_char(type); - - len = ssh_string_len(sig_blob); - - switch(type) { - case SSH_KEYTYPE_DSS: - /* 40 is the dual signature blob len. */ - if (len != 40) { - ssh_pki_log("Signature has wrong size: %lu", - (unsigned long)len); - ssh_signature_free(sig); - return NULL; - } - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("r", ssh_string_data(sig_blob), 20); - ssh_print_hexa("s", (unsigned char *)ssh_string_data(sig_blob) + 20, 20); -#endif - - sig->dsa_sig = DSA_SIG_new(); - if (sig->dsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } - - r = ssh_string_new(20); - if (r == NULL) { - ssh_signature_free(sig); - return NULL; - } - ssh_string_fill(r, ssh_string_data(sig_blob), 20); - - sig->dsa_sig->r = make_string_bn(r); - ssh_string_free(r); - if (sig->dsa_sig->r == NULL) { - ssh_signature_free(sig); - return NULL; - } - - s = ssh_string_new(20); - if (s == NULL) { - ssh_signature_free(sig); - return NULL; - } - ssh_string_fill(s, (char *)ssh_string_data(sig_blob) + 20, 20); - - sig->dsa_sig->s = make_string_bn(s); - ssh_string_free(s); - if (sig->dsa_sig->s == NULL) { - ssh_signature_free(sig); - return NULL; - } - - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - sig = pki_signature_from_rsa_blob(pubkey, sig_blob, sig); - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - sig->ecdsa_sig = ECDSA_SIG_new(); - if (sig->ecdsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } - - { /* build ecdsa siganature */ - ssh_buffer b; - uint32_t rlen; - - b = ssh_buffer_new(); - if (b == NULL) { - ssh_signature_free(sig); - return NULL; - } - - rc = ssh_buffer_add_data(b, - ssh_string_data(sig_blob), - ssh_string_len(sig_blob)); - if (rc < 0) { - ssh_buffer_free(b); - ssh_signature_free(sig); - return NULL; - } - - r = buffer_get_ssh_string(b); - if (r == NULL) { - ssh_buffer_free(b); - ssh_signature_free(sig); - return NULL; - } - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("r", ssh_string_data(r), ssh_string_len(r)); -#endif - - sig->ecdsa_sig->r = make_string_bn(r); - ssh_string_burn(r); - ssh_string_free(r); - if (sig->ecdsa_sig->r == NULL) { - ssh_buffer_free(b); - ssh_signature_free(sig); - return NULL; - } - - s = buffer_get_ssh_string(b); - rlen = buffer_get_rest_len(b); - ssh_buffer_free(b); - if (s == NULL) { - ssh_signature_free(sig); - return NULL; - } - -#ifdef DEBUG_CRYPTO - ssh_print_hexa("s", ssh_string_data(s), ssh_string_len(s)); -#endif - - sig->ecdsa_sig->s = make_string_bn(s); - ssh_string_burn(s); - ssh_string_free(s); - if (sig->ecdsa_sig->s == NULL) { - ssh_signature_free(sig); - return NULL; - } - - if (rlen != 0) { - ssh_pki_log("Signature has remaining bytes in inner " - "sigblob: %lu", - (unsigned long)rlen); - ssh_signature_free(sig); - return NULL; - } - } - - break; -#endif - case SSH_KEYTYPE_ED25519: - rc = pki_ed25519_sig_from_blob(sig, sig_blob); - if (rc == SSH_ERROR){ - ssh_signature_free(sig); - return NULL; - } - break; - default: - case SSH_KEYTYPE_UNKNOWN: - ssh_pki_log("Unknown signature type"); - ssh_signature_free(sig); - return NULL; - } - - return sig; -} - -int pki_signature_verify(ssh_session session, - const ssh_signature sig, - const ssh_key key, - const unsigned char *hash, - size_t hlen) -{ - int rc; - - switch(key->type) { - case SSH_KEYTYPE_DSS: - rc = DSA_do_verify(hash, - hlen, - sig->dsa_sig, - key->dsa); - if (rc <= 0) { - ssh_set_error(session, - SSH_FATAL, - "DSA error: %s", - ERR_error_string(ERR_get_error(), NULL)); - return SSH_ERROR; - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - rc = RSA_verify(NID_sha1, - hash, - hlen, - ssh_string_data(sig->rsa_sig), - ssh_string_len(sig->rsa_sig), - key->rsa); - if (rc <= 0) { - ssh_set_error(session, - SSH_FATAL, - "RSA error: %s", - ERR_error_string(ERR_get_error(), NULL)); - return SSH_ERROR; - } - break; - case SSH_KEYTYPE_ED25519: - rc = pki_ed25519_verify(key, sig, hash, hlen); - if (rc != SSH_OK){ - ssh_set_error(session, - SSH_FATAL, - "ed25519 signature verification error"); - return SSH_ERROR; - } - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - rc = ECDSA_do_verify(hash, - hlen, - sig->ecdsa_sig, - key->ecdsa); - if (rc <= 0) { - ssh_set_error(session, - SSH_FATAL, - "ECDSA error: %s", - ERR_error_string(ERR_get_error(), NULL)); - return SSH_ERROR; - } - break; -#endif - case SSH_KEYTYPE_UNKNOWN: - default: - ssh_set_error(session, SSH_FATAL, "Unknown public key type"); - return SSH_ERROR; - } - - return SSH_OK; -} - -ssh_signature pki_do_sign(const ssh_key privkey, - const unsigned char *hash, - size_t hlen) { - ssh_signature sig; - int rc; - - sig = ssh_signature_new(); - if (sig == NULL) { - return NULL; - } - - sig->type = privkey->type; - sig->type_c = privkey->type_c; - - switch(privkey->type) { - case SSH_KEYTYPE_DSS: - sig->dsa_sig = DSA_do_sign(hash, hlen, privkey->dsa); - if (sig->dsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } - -#ifdef DEBUG_CRYPTO - ssh_print_bignum("r", sig->dsa_sig->r); - ssh_print_bignum("s", sig->dsa_sig->s); -#endif - - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - sig->rsa_sig = _RSA_do_sign(hash, hlen, privkey->rsa); - if (sig->rsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } - sig->dsa_sig = NULL; - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - sig->ecdsa_sig = ECDSA_do_sign(hash, hlen, privkey->ecdsa); - if (sig->ecdsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } - -# ifdef DEBUG_CRYPTO - ssh_print_bignum("r", sig->ecdsa_sig->r); - ssh_print_bignum("s", sig->ecdsa_sig->s); -# endif /* DEBUG_CRYPTO */ - - break; -#endif /* HAVE_OPENSSL_ECC */ - case SSH_KEYTYPE_ED25519: - rc = pki_ed25519_sign(privkey, sig, hash, hlen); - if (rc != SSH_OK){ - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_UNKNOWN: - default: - ssh_signature_free(sig); - return NULL; - } - - return sig; -} - -#ifdef WITH_SERVER -ssh_signature pki_do_sign_sessionid(const ssh_key key, - const unsigned char *hash, - size_t hlen) -{ - ssh_signature sig; - - sig = ssh_signature_new(); - if (sig == NULL) { - return NULL; - } - sig->type = key->type; - sig->type_c = key->type_c; - - switch(key->type) { - case SSH_KEYTYPE_DSS: - sig->dsa_sig = DSA_do_sign(hash, hlen, key->dsa); - if (sig->dsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - sig->rsa_sig = _RSA_do_sign(hash, hlen, key->rsa); - if (sig->rsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_ECDSA: -#ifdef HAVE_OPENSSL_ECC - sig->ecdsa_sig = ECDSA_do_sign(hash, hlen, key->ecdsa); - if (sig->ecdsa_sig == NULL) { - ssh_signature_free(sig); - return NULL; - } - break; -#endif - case SSH_KEYTYPE_ED25519: - /* ED25519 handled in caller */ - case SSH_KEYTYPE_UNKNOWN: - default: - ssh_signature_free(sig); - return NULL; - } - - return sig; -} -#endif /* WITH_SERVER */ - -#endif /* _PKI_CRYPTO_H */ diff --git a/libssh/src/pki_ed25519.c b/libssh/src/pki_ed25519.c deleted file mode 100644 index 7fb9827c..00000000 --- a/libssh/src/pki_ed25519.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * pki_ed25519 .c - PKI infrastructure using ed25519 - * - * This file is part of the SSH Library - * - * Copyright (c) 2014 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "libssh/pki.h" -#include "libssh/pki_priv.h" -#include "libssh/ed25519.h" -#include "libssh/buffer.h" - -int pki_key_generate_ed25519(ssh_key key) -{ - int rc; - - key->ed25519_privkey = malloc(sizeof (ed25519_privkey)); - if (key->ed25519_privkey == NULL) { - goto error; - } - - key->ed25519_pubkey = malloc(sizeof (ed25519_privkey)); - if (key->ed25519_privkey == NULL) { - goto error; - } - - rc = crypto_sign_ed25519_keypair(*key->ed25519_pubkey, - *key->ed25519_privkey); - if (rc != 0) { - goto error; - } - - return SSH_OK; -error: - SAFE_FREE(key->ed25519_privkey); - SAFE_FREE(key->ed25519_pubkey); - - return SSH_ERROR; -} - -int pki_ed25519_sign(const ssh_key privkey, - ssh_signature sig, - const unsigned char *hash, - size_t hlen) -{ - int rc; - uint8_t *buffer; - unsigned long long dlen = 0; - - buffer = malloc(hlen + ED25519_SIG_LEN); - if (buffer == NULL) { - return SSH_ERROR; - } - - rc = crypto_sign_ed25519(buffer, - &dlen, - hash, - hlen, - *privkey->ed25519_privkey); - if (rc != 0) { - goto error; - } - sig->ed25519_sig = malloc(ED25519_SIG_LEN); - if (sig->ed25519_sig == NULL) { - goto error; - } - - /* This shouldn't happen */ - if (dlen - hlen != ED25519_SIG_LEN) { - goto error; - } - memcpy(sig->ed25519_sig, buffer, dlen - hlen); - SAFE_FREE(buffer); - - return SSH_OK; -error: - SAFE_FREE(buffer); - return SSH_ERROR; -} - -int pki_ed25519_verify(const ssh_key pubkey, - ssh_signature sig, - const unsigned char *hash, - size_t hlen) -{ - unsigned long long mlen = 0; - uint8_t *buffer; - uint8_t *buffer2; - int rc; - - if (pubkey == NULL || sig == NULL || - hash == NULL || sig->ed25519_sig == NULL) { - return SSH_ERROR; - } - - buffer = malloc(hlen + ED25519_SIG_LEN); - if (buffer == NULL) { - return SSH_ERROR; - } - - buffer2 = malloc(hlen + ED25519_SIG_LEN); - if (buffer2 == NULL) { - goto error; - } - - memcpy(buffer, sig->ed25519_sig, ED25519_SIG_LEN); - memcpy(buffer + ED25519_SIG_LEN, hash, hlen); - - rc = crypto_sign_ed25519_open(buffer2, - &mlen, - buffer, - hlen + ED25519_SIG_LEN, - *pubkey->ed25519_pubkey); - - BURN_BUFFER(buffer, hlen + ED25519_SIG_LEN); - BURN_BUFFER(buffer2, hlen); - SAFE_FREE(buffer); - SAFE_FREE(buffer2); - if (rc == 0) { - return SSH_OK; - } else { - return SSH_ERROR; - } -error: - SAFE_FREE(buffer); - SAFE_FREE(buffer2); - - return SSH_ERROR; -} - -/** - * @internal - * - * @brief Compare ed25519 keys if they are equal. - * - * @param[in] k1 The first key to compare. - * - * @param[in] k2 The second key to compare. - * - * @param[in] what What part or type of the key do you want to compare. - * - * @return 0 if equal, 1 if not. - */ -int pki_ed25519_key_cmp(const ssh_key k1, - const ssh_key k2, - enum ssh_keycmp_e what) -{ - int cmp; - - switch(what) { - case SSH_KEY_CMP_PRIVATE: - if (k1->ed25519_privkey == NULL || k2->ed25519_privkey == NULL) { - return 1; - } - cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, ED25519_SK_LEN); - if (cmp != 0) { - return 1; - } - /* FALL THROUGH */ - case SSH_KEY_CMP_PUBLIC: - if (k1->ed25519_pubkey == NULL || k2->ed25519_pubkey == NULL) { - return 1; - } - cmp = memcmp(k1->ed25519_pubkey, k2->ed25519_pubkey, ED25519_PK_LEN); - if (cmp != 0) { - return 1; - } - } - - return 0; -} - -/** - * @internal - * - * @brief duplicate an ed25519 key - * - * @param[out\ new preinitialized output ssh_ke - * - * @param[in] key key to copy - * - * @return SSH_ERROR on error, SSH_OK on success - */ -int pki_ed25519_key_dup(ssh_key new, const ssh_key key) -{ - if (key->ed25519_privkey == NULL || key->ed25519_pubkey == NULL) { - return SSH_ERROR; - } - - new->ed25519_privkey = malloc(ED25519_SK_LEN); - if (new->ed25519_privkey == NULL) { - return SSH_ERROR; - } - - new->ed25519_pubkey = malloc(ED25519_PK_LEN); - if (new->ed25519_privkey == NULL || new->ed25519_pubkey == NULL){ - SAFE_FREE(new->ed25519_privkey); - return SSH_ERROR; - } - - memcpy(new->ed25519_privkey, key->ed25519_privkey, ED25519_SK_LEN); - memcpy(new->ed25519_pubkey, key->ed25519_pubkey, ED25519_PK_LEN); - - return SSH_OK; -} - -/** - * @internal - * - * @brief outputs an ed25519 public key in a blob buffer. - * - * @param[out] buffer output buffer - * - * @param[in] key key to output - * - * @return SSH_ERROR on error, SSH_OK on success - */ -int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key) -{ - int rc; - - if (key->ed25519_pubkey == NULL){ - return SSH_ERROR; - } - - rc = ssh_buffer_pack(buffer, - "dP", - (uint32_t)ED25519_PK_LEN, - (size_t)ED25519_PK_LEN, key->ed25519_pubkey); - - return rc; -} - -/** - * @internal - * - * @brief output a signature blob from an ed25519 signature - * - * @param[in] sig signature to convert - * - * @return Signature blob in SSH string, or NULL on error - */ -ssh_string pki_ed25519_sig_to_blob(ssh_signature sig) -{ - ssh_string sig_blob; - - if (sig->ed25519_sig == NULL) { - return NULL; - } - - sig_blob = ssh_string_new(ED25519_SIG_LEN); - if (sig_blob == NULL) { - return NULL; - } - ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN); - - return sig_blob; -} - -/** - * @internal - * - * @brief Convert a signature blob in an ed25519 signature. - * - * @param[out] sig a preinitialized signature - * - * @param[in] sig_blob a signature blob - * - * @return SSH_ERROR on error, SSH_OK on success - */ -int pki_ed25519_sig_from_blob(ssh_signature sig, ssh_string sig_blob) -{ - size_t len; - - len = ssh_string_len(sig_blob); - if (len != ED25519_SIG_LEN){ - ssh_pki_log("Invalid ssh-ed25519 signature len: %zu", len); - return SSH_ERROR; - } - - sig->ed25519_sig = malloc(ED25519_SIG_LEN); - if (sig->ed25519_sig == NULL){ - return SSH_ERROR; - } - - memcpy(sig->ed25519_sig, ssh_string_data(sig_blob), ED25519_SIG_LEN); - - return SSH_OK; -} diff --git a/libssh/src/pki_gcrypt.c b/libssh/src/pki_gcrypt.c deleted file mode 100644 index 2811acce..00000000 --- a/libssh/src/pki_gcrypt.c +++ /dev/null @@ -1,1713 +0,0 @@ -/* - * pki_gcrypt.c private and public key handling using gcrypt. - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2009 Aris Adamantiadis - * Copyright (c) 2009-2011 Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#ifdef HAVE_LIBGCRYPT - -#include -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/buffer.h" -#include "libssh/session.h" -#include "libssh/wrapper.h" -#include "libssh/misc.h" -#include "libssh/pki.h" -#include "libssh/pki_priv.h" - -#define MAXLINESIZE 80 -#define RSA_HEADER_BEGIN "-----BEGIN RSA PRIVATE KEY-----" -#define RSA_HEADER_END "-----END RSA PRIVATE KEY-----" -#define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----" -#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----" - -#define MAX_KEY_SIZE 32 -#define MAX_PASSPHRASE_SIZE 1024 -#define ASN1_INTEGER 2 -#define ASN1_SEQUENCE 48 -#define PKCS5_SALT_LEN 8 - -static int load_iv(const char *header, unsigned char *iv, int iv_len) { - int i; - int j; - int k; - - memset(iv, 0, iv_len); - for (i = 0; i < iv_len; i++) { - if ((header[2*i] >= '0') && (header[2*i] <= '9')) - j = header[2*i] - '0'; - else if ((header[2*i] >= 'A') && (header[2*i] <= 'F')) - j = header[2*i] - 'A' + 10; - else if ((header[2*i] >= 'a') && (header[2*i] <= 'f')) - j = header[2*i] - 'a' + 10; - else - return -1; - if ((header[2*i+1] >= '0') && (header[2*i+1] <= '9')) - k = header[2*i+1] - '0'; - else if ((header[2*i+1] >= 'A') && (header[2*i+1] <= 'F')) - k = header[2*i+1] - 'A' + 10; - else if ((header[2*i+1] >= 'a') && (header[2*i+1] <= 'f')) - k = header[2*i+1] - 'a' + 10; - else - return -1; - iv[i] = (j << 4) + k; - } - return 0; -} - -static uint32_t char_to_u32(unsigned char *data, uint32_t size) { - uint32_t ret; - uint32_t i; - - for (i = 0, ret = 0; i < size; ret = ret << 8, ret += data[i++]) - ; - return ret; -} - -static uint32_t asn1_get_len(ssh_buffer buffer) { - uint32_t len; - unsigned char tmp[4]; - - if (buffer_get_data(buffer,tmp,1) == 0) { - return 0; - } - - if (tmp[0] > 127) { - len = tmp[0] & 127; - if (len > 4) { - return 0; /* Length doesn't fit in u32. Can this really happen? */ - } - if (buffer_get_data(buffer,tmp,len) == 0) { - return 0; - } - len = char_to_u32(tmp, len); - } else { - len = char_to_u32(tmp, 1); - } - - return len; -} - -static ssh_string asn1_get_int(ssh_buffer buffer) { - ssh_string str; - unsigned char type; - uint32_t size; - - if (buffer_get_data(buffer, &type, 1) == 0 || type != ASN1_INTEGER) { - return NULL; - } - size = asn1_get_len(buffer); - if (size == 0) { - return NULL; - } - - str = ssh_string_new(size); - if (str == NULL) { - return NULL; - } - - if (buffer_get_data(buffer, ssh_string_data(str), size) == 0) { - ssh_string_free(str); - return NULL; - } - - return str; -} - -static int asn1_check_sequence(ssh_buffer buffer) { - unsigned char *j = NULL; - unsigned char tmp; - int i; - uint32_t size; - uint32_t padding; - - if (buffer_get_data(buffer, &tmp, 1) == 0 || tmp != ASN1_SEQUENCE) { - return 0; - } - - size = asn1_get_len(buffer); - if ((padding = ssh_buffer_get_len(buffer) - buffer->pos - size) > 0) { - for (i = ssh_buffer_get_len(buffer) - buffer->pos - size, - j = (unsigned char*)ssh_buffer_get_begin(buffer) + size + buffer->pos; - i; - i--, j++) - { - if (*j != padding) { /* padding is allowed */ - return 0; /* but nothing else */ - } - } - } - - return 1; -} - -static int passphrase_to_key(char *data, unsigned int datalen, - unsigned char *salt, unsigned char *key, unsigned int keylen) { - MD5CTX md; - unsigned char digest[MD5_DIGEST_LEN] = {0}; - unsigned int i; - unsigned int j; - unsigned int md_not_empty; - - for (j = 0, md_not_empty = 0; j < keylen; ) { - md = md5_init(); - if (md == NULL) { - return -1; - } - - if (md_not_empty) { - md5_update(md, digest, MD5_DIGEST_LEN); - } else { - md_not_empty = 1; - } - - md5_update(md, data, datalen); - if (salt) { - md5_update(md, salt, PKCS5_SALT_LEN); - } - md5_final(digest, md); - - for (i = 0; j < keylen && i < MD5_DIGEST_LEN; j++, i++) { - if (key) { - key[j] = digest[i]; - } - } - } - - return 0; -} - -static int privatekey_decrypt(int algo, int mode, unsigned int key_len, - unsigned char *iv, unsigned int iv_len, - ssh_buffer data, ssh_auth_callback cb, - void *userdata, - const char *desc) -{ - char passphrase[MAX_PASSPHRASE_SIZE] = {0}; - unsigned char key[MAX_KEY_SIZE] = {0}; - unsigned char *tmp = NULL; - gcry_cipher_hd_t cipher; - int rc = -1; - - if (!algo) { - return -1; - } - - if (cb) { - rc = (*cb)(desc, passphrase, MAX_PASSPHRASE_SIZE, 0, 0, userdata); - if (rc < 0) { - return -1; - } - } else if (cb == NULL && userdata != NULL) { - snprintf(passphrase, MAX_PASSPHRASE_SIZE, "%s", (char *) userdata); - } - - if (passphrase_to_key(passphrase, strlen(passphrase), iv, key, key_len) < 0) { - return -1; - } - - if (gcry_cipher_open(&cipher, algo, mode, 0) - || gcry_cipher_setkey(cipher, key, key_len) - || gcry_cipher_setiv(cipher, iv, iv_len) - || (tmp = malloc(ssh_buffer_get_len(data) * sizeof (char))) == NULL - || gcry_cipher_decrypt(cipher, tmp, ssh_buffer_get_len(data), - ssh_buffer_get_begin(data), ssh_buffer_get_len(data))) { - gcry_cipher_close(cipher); - return -1; - } - - memcpy(ssh_buffer_get_begin(data), tmp, ssh_buffer_get_len(data)); - - SAFE_FREE(tmp); - gcry_cipher_close(cipher); - - return 0; -} - -static int privatekey_dek_header(const char *header, unsigned int header_len, - int *algo, int *mode, unsigned int *key_len, unsigned char **iv, - unsigned int *iv_len) { - unsigned int iv_pos; - - if (header_len > 13 && !strncmp("DES-EDE3-CBC", header, 12)) - { - *algo = GCRY_CIPHER_3DES; - iv_pos = 13; - *mode = GCRY_CIPHER_MODE_CBC; - *key_len = 24; - *iv_len = 8; - } - else if (header_len > 8 && !strncmp("DES-CBC", header, 7)) - { - *algo = GCRY_CIPHER_DES; - iv_pos = 8; - *mode = GCRY_CIPHER_MODE_CBC; - *key_len = 8; - *iv_len = 8; - } - else if (header_len > 12 && !strncmp("AES-128-CBC", header, 11)) - { - *algo = GCRY_CIPHER_AES128; - iv_pos = 12; - *mode = GCRY_CIPHER_MODE_CBC; - *key_len = 16; - *iv_len = 16; - } - else if (header_len > 12 && !strncmp("AES-192-CBC", header, 11)) - { - *algo = GCRY_CIPHER_AES192; - iv_pos = 12; - *mode = GCRY_CIPHER_MODE_CBC; - *key_len = 24; - *iv_len = 16; - } - else if (header_len > 12 && !strncmp("AES-256-CBC", header, 11)) - { - *algo = GCRY_CIPHER_AES256; - iv_pos = 12; - *mode = GCRY_CIPHER_MODE_CBC; - *key_len = 32; - *iv_len = 16; - } else { - return -1; - } - - *iv = malloc(*iv_len); - if (*iv == NULL) { - return -1; - } - - return load_iv(header + iv_pos, *iv, *iv_len); -} - -#define get_next_line(p, len) { \ - while(p[len] == '\n' || p[len] == '\r') /* skip empty lines */ \ - len++; \ - if(p[len] == '\0') /* EOL */ \ - len = -1; \ - else /* calculate length */ \ - for(p += len, len = 0; p[len] && p[len] != '\n' \ - && p[len] != '\r'; len++); \ - } - -static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type, - ssh_auth_callback cb, void *userdata, const char *desc) { - ssh_buffer buffer = NULL; - ssh_buffer out = NULL; - const char *p; - unsigned char *iv = NULL; - const char *header_begin; - const char *header_end; - unsigned int header_begin_size; - unsigned int header_end_size; - unsigned int key_len = 0; - unsigned int iv_len = 0; - int algo = 0; - int mode = 0; - int len; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - return NULL; - } - - switch(type) { - case SSH_KEYTYPE_DSS: - header_begin = DSA_HEADER_BEGIN; - header_end = DSA_HEADER_END; - break; - case SSH_KEYTYPE_RSA: - header_begin = RSA_HEADER_BEGIN; - header_end = RSA_HEADER_END; - break; - default: - ssh_buffer_free(buffer); - return NULL; - } - - header_begin_size = strlen(header_begin); - header_end_size = strlen(header_end); - - p = pkey; - len = 0; - get_next_line(p, len); - - while(len > 0 && strncmp(p, header_begin, header_begin_size)) { - /* skip line */ - get_next_line(p, len); - } - if(len < 0) { - /* no header found */ - return NULL; - } - /* skip header line */ - get_next_line(p, len); - - if (len > 11 && strncmp("Proc-Type: 4,ENCRYPTED", p, 11) == 0) { - /* skip line */ - get_next_line(p, len); - - if (len > 10 && strncmp("DEK-Info: ", p, 10) == 0) { - p += 10; - len = 0; - get_next_line(p, len); - if (privatekey_dek_header(p, len, &algo, &mode, &key_len, - &iv, &iv_len) < 0) { - ssh_buffer_free(buffer); - SAFE_FREE(iv); - return NULL; - } - } else { - ssh_buffer_free(buffer); - SAFE_FREE(iv); - return NULL; - } - } else { - if(len > 0) { - if (ssh_buffer_add_data(buffer, p, len) < 0) { - ssh_buffer_free(buffer); - SAFE_FREE(iv); - return NULL; - } - } - } - - get_next_line(p, len); - while(len > 0 && strncmp(p, header_end, header_end_size) != 0) { - if (ssh_buffer_add_data(buffer, p, len) < 0) { - ssh_buffer_free(buffer); - SAFE_FREE(iv); - return NULL; - } - get_next_line(p, len); - } - - if (len == -1 || strncmp(p, header_end, header_end_size) != 0) { - ssh_buffer_free(buffer); - SAFE_FREE(iv); - return NULL; - } - - if (ssh_buffer_add_data(buffer, "\0", 1) < 0) { - ssh_buffer_free(buffer); - SAFE_FREE(iv); - return NULL; - } - - out = base64_to_bin(ssh_buffer_get_begin(buffer)); - ssh_buffer_free(buffer); - if (out == NULL) { - SAFE_FREE(iv); - return NULL; - } - - if (algo) { - if (privatekey_decrypt(algo, mode, key_len, iv, iv_len, out, - cb, userdata, desc) < 0) { - ssh_buffer_free(out); - SAFE_FREE(iv); - return NULL; - } - } - SAFE_FREE(iv); - - return out; -} - -static int b64decode_rsa_privatekey(const char *pkey, gcry_sexp_t *r, - ssh_auth_callback cb, void *userdata, const char *desc) { - const unsigned char *data; - ssh_string n = NULL; - ssh_string e = NULL; - ssh_string d = NULL; - ssh_string p = NULL; - ssh_string q = NULL; - ssh_string unused1 = NULL; - ssh_string unused2 = NULL; - ssh_string u = NULL; - ssh_string v = NULL; - ssh_buffer buffer = NULL; - int rc = 1; - - buffer = privatekey_string_to_buffer(pkey, SSH_KEYTYPE_RSA, cb, userdata, desc); - if (buffer == NULL) { - return 0; - } - - if (!asn1_check_sequence(buffer)) { - ssh_buffer_free(buffer); - return 0; - } - - v = asn1_get_int(buffer); - if (v == NULL) { - ssh_buffer_free(buffer); - return 0; - } - - data = ssh_string_data(v); - if (ssh_string_len(v) != 1 || data[0] != 0) { - ssh_buffer_free(buffer); - return 0; - } - - n = asn1_get_int(buffer); - e = asn1_get_int(buffer); - d = asn1_get_int(buffer); - q = asn1_get_int(buffer); - p = asn1_get_int(buffer); - unused1 = asn1_get_int(buffer); - unused2 = asn1_get_int(buffer); - u = asn1_get_int(buffer); - - ssh_buffer_free(buffer); - - if (n == NULL || e == NULL || d == NULL || p == NULL || q == NULL || - unused1 == NULL || unused2 == NULL|| u == NULL) { - rc = 0; - goto error; - } - - if (gcry_sexp_build(r, NULL, - "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %b)))", - ssh_string_len(n), ssh_string_data(n), - ssh_string_len(e), ssh_string_data(e), - ssh_string_len(d), ssh_string_data(d), - ssh_string_len(p), ssh_string_data(p), - ssh_string_len(q), ssh_string_data(q), - ssh_string_len(u), ssh_string_data(u))) { - rc = 0; - } - -error: - ssh_string_free(n); - ssh_string_free(e); - ssh_string_free(d); - ssh_string_free(p); - ssh_string_free(q); - ssh_string_free(unused1); - ssh_string_free(unused2); - ssh_string_free(u); - ssh_string_free(v); - - return rc; -} - -static int b64decode_dsa_privatekey(const char *pkey, gcry_sexp_t *r, ssh_auth_callback cb, - void *userdata, const char *desc) { - const unsigned char *data; - ssh_buffer buffer = NULL; - ssh_string p = NULL; - ssh_string q = NULL; - ssh_string g = NULL; - ssh_string y = NULL; - ssh_string x = NULL; - ssh_string v = NULL; - int rc = 1; - - buffer = privatekey_string_to_buffer(pkey, SSH_KEYTYPE_DSS, cb, userdata, desc); - if (buffer == NULL) { - return 0; - } - - if (!asn1_check_sequence(buffer)) { - ssh_buffer_free(buffer); - return 0; - } - - v = asn1_get_int(buffer); - if (v == NULL) { - ssh_buffer_free(buffer); - return 0; - } - - data = ssh_string_data(v); - if (ssh_string_len(v) != 1 || data[0] != 0) { - ssh_buffer_free(buffer); - return 0; - } - - p = asn1_get_int(buffer); - q = asn1_get_int(buffer); - g = asn1_get_int(buffer); - y = asn1_get_int(buffer); - x = asn1_get_int(buffer); - ssh_buffer_free(buffer); - - if (p == NULL || q == NULL || g == NULL || y == NULL || x == NULL) { - rc = 0; - goto error; - } - - if (gcry_sexp_build(r, NULL, - "(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))", - ssh_string_len(p), ssh_string_data(p), - ssh_string_len(q), ssh_string_data(q), - ssh_string_len(g), ssh_string_data(g), - ssh_string_len(y), ssh_string_data(y), - ssh_string_len(x), ssh_string_data(x))) { - rc = 0; - } - -error: - ssh_string_free(p); - ssh_string_free(q); - ssh_string_free(g); - ssh_string_free(y); - ssh_string_free(x); - ssh_string_free(v); - - return rc; -} - -#ifdef HAVE_GCRYPT_ECC -int pki_key_ecdsa_nid_from_name(const char *name) -{ - return -1; -} -#endif - -ssh_string pki_private_key_to_pem(const ssh_key key, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data) -{ - (void) key; - (void) passphrase; - (void) auth_fn; - (void) auth_data; - - return NULL; -} - -ssh_key pki_private_key_from_base64(const char *b64_key, - const char *passphrase, - ssh_auth_callback auth_fn, - void *auth_data) -{ - gcry_sexp_t dsa = NULL; - gcry_sexp_t rsa = NULL; - ssh_key key = NULL; - enum ssh_keytypes_e type; - int valid; - - /* needed for gcrypt initialization */ - if (ssh_init() < 0) { - return NULL; - } - - type = pki_privatekey_type_from_string(b64_key); - if (type == SSH_KEYTYPE_UNKNOWN) { - ssh_pki_log("Unknown or invalid private key."); - return NULL; - } - - switch (type) { - case SSH_KEYTYPE_DSS: - if (passphrase == NULL) { - if (auth_fn) { - valid = b64decode_dsa_privatekey(b64_key, &dsa, auth_fn, - auth_data, "Passphrase for private key:"); - } else { - valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, NULL, - NULL); - } - } else { - valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, (void *) - passphrase, NULL); - } - - if (!valid) { - ssh_pki_log("Parsing private key"); - goto fail; - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - if (passphrase == NULL) { - if (auth_fn) { - valid = b64decode_rsa_privatekey(b64_key, &rsa, auth_fn, - auth_data, "Passphrase for private key:"); - } else { - valid = b64decode_rsa_privatekey(b64_key, &rsa, NULL, NULL, - NULL); - } - } else { - valid = b64decode_rsa_privatekey(b64_key, &rsa, NULL, - (void *)passphrase, NULL); - } - - if (!valid) { - ssh_pki_log("Parsing private key"); - goto fail; - } - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - ssh_pki_log("Unkown or invalid private key type %d", type); - return NULL; - } - - key = ssh_key_new(); - if (key == NULL) { - goto fail; - } - - key->type = type; - key->type_c = ssh_key_type_to_char(type); - key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC; - key->dsa = dsa; - key->rsa = rsa; - - return key; -fail: - ssh_key_free(key); - gcry_sexp_release(dsa); - gcry_sexp_release(rsa); - - return NULL; -} - -int pki_pubkey_build_dss(ssh_key key, - ssh_string p, - ssh_string q, - ssh_string g, - ssh_string pubkey) { - gcry_sexp_build(&key->dsa, NULL, - "(public-key(dsa(p %b)(q %b)(g %b)(y %b)))", - ssh_string_len(p), ssh_string_data(p), - ssh_string_len(q), ssh_string_data(q), - ssh_string_len(g), ssh_string_data(g), - ssh_string_len(pubkey), ssh_string_data(pubkey)); - if (key->dsa == NULL) { - return SSH_ERROR; - } - - return SSH_OK; -} - -int pki_pubkey_build_rsa(ssh_key key, - ssh_string e, - ssh_string n) { - gcry_sexp_build(&key->rsa, NULL, - "(public-key(rsa(n %b)(e %b)))", - ssh_string_len(n), ssh_string_data(n), - ssh_string_len(e),ssh_string_data(e)); - if (key->rsa == NULL) { - return SSH_ERROR; - } - - return SSH_OK; -} - -#ifdef HAVE_GCRYPT_ECC -int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e) -{ - return -1; -} -#endif - -ssh_key pki_key_dup(const ssh_key key, int demote) -{ - ssh_key new; - gcry_sexp_t sexp; - gcry_error_t err; - const char *tmp = NULL; - size_t size; - - ssh_string p = NULL; - ssh_string q = NULL; - ssh_string g = NULL; - ssh_string y = NULL; - ssh_string x = NULL; - - ssh_string e = NULL; - ssh_string n = NULL; - ssh_string d = NULL; - ssh_string u = NULL; - - new = ssh_key_new(); - if (new == NULL) { - return NULL; - } - new->type = key->type; - new->type_c = key->type_c; - if (demote) { - new->flags = SSH_KEY_FLAG_PUBLIC; - } else { - new->flags = key->flags; - } - - switch(key->type) { - case SSH_KEYTYPE_DSS: - sexp = gcry_sexp_find_token(key->dsa, "p", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - p = ssh_string_new(size); - if (p == NULL) { - goto fail; - } - ssh_string_fill(p, (char *)tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->dsa, "q", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - q = ssh_string_new(size); - if (q == NULL) { - goto fail; - } - ssh_string_fill(q, (char *)tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->dsa, "g", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - g = ssh_string_new(size); - if (g == NULL) { - goto fail; - } - ssh_string_fill(g, (char *)tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->dsa, "y", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - y = ssh_string_new(size); - if (y == NULL) { - goto fail; - } - ssh_string_fill(y, (char *)tmp, size); - gcry_sexp_release(sexp); - - if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { - sexp = gcry_sexp_find_token(key->dsa, "x", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - x = ssh_string_new(size); - if (x == NULL) { - goto fail; - } - ssh_string_fill(x, (char *)tmp, size); - gcry_sexp_release(sexp); - - err = gcry_sexp_build(&new->dsa, NULL, - "(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))", - ssh_string_len(p), ssh_string_data(p), - ssh_string_len(q), ssh_string_data(q), - ssh_string_len(g), ssh_string_data(g), - ssh_string_len(y), ssh_string_data(y), - ssh_string_len(x), ssh_string_data(x)); - } else { - err = gcry_sexp_build(&new->dsa, NULL, - "(public-key(dsa(p %b)(q %b)(g %b)(y %b)))", - ssh_string_len(p), ssh_string_data(p), - ssh_string_len(q), ssh_string_data(q), - ssh_string_len(g), ssh_string_data(g), - ssh_string_len(y), ssh_string_data(y)); - } - if (err) { - goto fail; - } - - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(q); - ssh_string_free(q); - ssh_string_burn(g); - ssh_string_free(g); - ssh_string_burn(y); - ssh_string_free(y); - ssh_string_burn(x); - ssh_string_free(x); - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - sexp = gcry_sexp_find_token(key->rsa, "e", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - e = ssh_string_new(size); - if (e == NULL) { - goto fail; - } - ssh_string_fill(e, (char *)tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->rsa, "n", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - n = ssh_string_new(size); - if (n == NULL) { - goto fail; - } - ssh_string_fill(n, (char *)tmp, size); - gcry_sexp_release(sexp); - - if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) { - sexp = gcry_sexp_find_token(key->rsa, "d", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - d = ssh_string_new(size); - if (e == NULL) { - goto fail; - } - ssh_string_fill(d, (char *)tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->rsa, "p", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - p = ssh_string_new(size); - if (p == NULL) { - goto fail; - } - ssh_string_fill(p, (char *)tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->rsa, "q", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - q = ssh_string_new(size); - if (q == NULL) { - goto fail; - } - ssh_string_fill(q, (char *)tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->rsa, "u", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - u = ssh_string_new(size); - if (u == NULL) { - goto fail; - } - ssh_string_fill(u, (char *)tmp, size); - gcry_sexp_release(sexp); - - err = gcry_sexp_build(&new->rsa, NULL, - "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %b)))", - ssh_string_len(n), ssh_string_data(n), - ssh_string_len(e), ssh_string_data(e), - ssh_string_len(d), ssh_string_data(d), - ssh_string_len(p), ssh_string_data(p), - ssh_string_len(q), ssh_string_data(q), - ssh_string_len(u), ssh_string_data(u)); - } else { - err = gcry_sexp_build(&new->rsa, NULL, - "(public-key(rsa(n %b)(e %b)))", - ssh_string_len(n), ssh_string_data(n), - ssh_string_len(e), ssh_string_data(e)); - } - - if (err) { - goto fail; - } - - ssh_string_burn(e); - ssh_string_free(e); - ssh_string_burn(n); - ssh_string_free(n); - ssh_string_burn(d); - ssh_string_free(d); - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(q); - ssh_string_free(q); - ssh_string_burn(u); - ssh_string_free(u); - - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - ssh_key_free(new); - return NULL; - } - - return new; -fail: - gcry_sexp_release(sexp); - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(q); - ssh_string_free(q); - ssh_string_burn(g); - ssh_string_free(g); - ssh_string_burn(y); - ssh_string_free(y); - ssh_string_burn(x); - ssh_string_free(x); - - ssh_string_burn(e); - ssh_string_free(e); - ssh_string_burn(n); - ssh_string_free(n); - ssh_string_burn(u); - ssh_string_free(u); - - ssh_key_free(new); - - return NULL; -} - -static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int type){ - gcry_sexp_t parms; - int rc; - rc = gcry_sexp_build(&parms, - NULL, - "(genkey(%s(nbits %d)(transient-key)))", - type_s, - parameter); - if (rc != 0) - return SSH_ERROR; - if(type == SSH_KEYTYPE_RSA) - rc = gcry_pk_genkey(&key->rsa, parms); - else - rc = gcry_pk_genkey(&key->dsa, parms); - gcry_sexp_release(parms); - if (rc != 0) - return SSH_ERROR; - return SSH_OK; -} - -int pki_key_generate_rsa(ssh_key key, int parameter){ - return pki_key_generate(key, parameter, "rsa", SSH_KEYTYPE_RSA); -} -int pki_key_generate_dss(ssh_key key, int parameter){ - return pki_key_generate(key, parameter, "dsa", SSH_KEYTYPE_DSS); -} - -#ifdef HAVE_GCRYPT_ECC -int pki_key_generate_ecdsa(ssh_key key, int parameter) { - return -1; -} -#endif - -static int _bignum_cmp(const gcry_sexp_t s1, - const gcry_sexp_t s2, - const char *what) -{ - gcry_sexp_t sexp; - bignum b1; - bignum b2; - - sexp = gcry_sexp_find_token(s1, what, 0); - if (sexp == NULL) { - return 1; - } - b1 = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); - gcry_sexp_release(sexp); - if (b1 == NULL) { - return 1; - } - - sexp = gcry_sexp_find_token(s2, what, 0); - if (sexp == NULL) { - return 1; - } - b2 = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); - gcry_sexp_release(sexp); - if (b2 == NULL) { - return 1; - } - - if (bignum_cmp(b1, b2) != 0) { - return 1; - } - - return 0; -} - -int pki_key_compare(const ssh_key k1, - const ssh_key k2, - enum ssh_keycmp_e what) -{ - switch (k1->type) { - case SSH_KEYTYPE_DSS: - if (_bignum_cmp(k1->dsa, k2->dsa, "p") != 0) { - return 1; - } - - if (_bignum_cmp(k1->dsa, k2->dsa, "q") != 0) { - return 1; - } - - if (_bignum_cmp(k1->dsa, k2->dsa, "g") != 0) { - return 1; - } - - if (_bignum_cmp(k1->dsa, k2->dsa, "y") != 0) { - return 1; - } - - if (what == SSH_KEY_CMP_PRIVATE) { - if (_bignum_cmp(k1->dsa, k2->dsa, "x") != 0) { - return 1; - } - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - if (_bignum_cmp(k1->rsa, k2->rsa, "e") != 0) { - return 1; - } - - if (_bignum_cmp(k1->rsa, k2->rsa, "n") != 0) { - return 1; - } - - if (what == SSH_KEY_CMP_PRIVATE) { - if (_bignum_cmp(k1->rsa, k2->rsa, "d") != 0) { - return 1; - } - - if (_bignum_cmp(k1->rsa, k2->rsa, "p") != 0) { - return 1; - } - - if (_bignum_cmp(k1->rsa, k2->rsa, "q") != 0) { - return 1; - } - - if (_bignum_cmp(k1->rsa, k2->rsa, "u") != 0) { - return 1; - } - } - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - return 1; - } - - return 0; -} - -ssh_string pki_publickey_to_blob(const ssh_key key) -{ - ssh_buffer buffer; - ssh_string type_s; - ssh_string str = NULL; - ssh_string e = NULL; - ssh_string n = NULL; - ssh_string p = NULL; - ssh_string g = NULL; - ssh_string q = NULL; - const char *tmp = NULL; - size_t size; - gcry_sexp_t sexp; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - return NULL; - } - - type_s = ssh_string_from_char(key->type_c); - if (type_s == NULL) { - ssh_buffer_free(buffer); - return NULL; - } - - rc = buffer_add_ssh_string(buffer, type_s); - ssh_string_free(type_s); - if (rc < 0) { - ssh_buffer_free(buffer); - return NULL; - } - - switch (key->type) { - case SSH_KEYTYPE_DSS: - sexp = gcry_sexp_find_token(key->dsa, "p", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - p = ssh_string_new(size); - if (p == NULL) { - goto fail; - } - ssh_string_fill(p, (char *) tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->dsa, "q", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - q = ssh_string_new(size); - if (q == NULL) { - goto fail; - } - ssh_string_fill(q, (char *) tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->dsa, "g", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - g = ssh_string_new(size); - if (g == NULL) { - goto fail; - } - ssh_string_fill(g, (char *) tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->dsa, "y", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - n = ssh_string_new(size); - if (n == NULL) { - goto fail; - } - ssh_string_fill(n, (char *) tmp, size); - - if (buffer_add_ssh_string(buffer, p) < 0) { - goto fail; - } - if (buffer_add_ssh_string(buffer, q) < 0) { - goto fail; - } - if (buffer_add_ssh_string(buffer, g) < 0) { - goto fail; - } - if (buffer_add_ssh_string(buffer, n) < 0) { - goto fail; - } - - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(g); - ssh_string_free(g); - ssh_string_burn(q); - ssh_string_free(q); - ssh_string_burn(n); - ssh_string_free(n); - - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - sexp = gcry_sexp_find_token(key->rsa, "e", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - e = ssh_string_new(size); - if (e == NULL) { - goto fail; - } - ssh_string_fill(e, (char *) tmp, size); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(key->rsa, "n", 0); - if (sexp == NULL) { - goto fail; - } - tmp = gcry_sexp_nth_data(sexp, 1, &size); - n = ssh_string_new(size); - if (n == NULL) { - goto fail; - } - ssh_string_fill(n, (char *) tmp, size); - gcry_sexp_release(sexp); - - if (buffer_add_ssh_string(buffer, e) < 0) { - goto fail; - } - if (buffer_add_ssh_string(buffer, n) < 0) { - goto fail; - } - - ssh_string_burn(e); - ssh_string_free(e); - ssh_string_burn(n); - ssh_string_free(n); - - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - goto fail; - } - - str = ssh_string_new(buffer_get_rest_len(buffer)); - if (str == NULL) { - goto fail; - } - - rc = ssh_string_fill(str, buffer_get_rest(buffer), buffer_get_rest_len(buffer)); - if (rc < 0) { - goto fail; - } - ssh_buffer_free(buffer); - - return str; -fail: - ssh_buffer_free(buffer); - ssh_string_burn(str); - ssh_string_free(str); - ssh_string_burn(e); - ssh_string_free(e); - ssh_string_burn(p); - ssh_string_free(p); - ssh_string_burn(g); - ssh_string_free(g); - ssh_string_burn(q); - ssh_string_free(q); - ssh_string_burn(n); - ssh_string_free(n); - - return NULL; -} - -int pki_export_pubkey_rsa1(const ssh_key key, - const char *host, - char *rsa1, - size_t rsa1_len) -{ - gcry_sexp_t sexp; - int rsa_size; - bignum b; - char *e, *n; - - sexp = gcry_sexp_find_token(key->rsa, "e", 0); - if (sexp == NULL) { - return SSH_ERROR; - } - b = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); - gcry_sexp_release(sexp); - if (b == NULL) { - return SSH_ERROR; - } - e = bignum_bn2dec(b); - - sexp = gcry_sexp_find_token(key->rsa, "n", 0); - if (sexp == NULL) { - SAFE_FREE(e); - return SSH_ERROR; - } - b = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG); - gcry_sexp_release(sexp); - if (b == NULL) { - SAFE_FREE(e); - return SSH_ERROR; - } - n = bignum_bn2dec(b); - - rsa_size = (gcry_pk_get_nbits(key->rsa) + 7) / 8; - - snprintf(rsa1, rsa1_len, - "%s %d %s %s\n", - host, rsa_size << 3, e, n); - SAFE_FREE(e); - SAFE_FREE(n); - - return SSH_OK; -} - -ssh_string pki_signature_to_blob(const ssh_signature sig) -{ - char buffer[40] = {0}; - const char *r = NULL; - const char *s = NULL; - gcry_sexp_t sexp; - size_t size = 0; - ssh_string sig_blob = NULL; - - switch(sig->type) { - case SSH_KEYTYPE_DSS: - sexp = gcry_sexp_find_token(sig->dsa_sig, "r", 0); - if (sexp == NULL) { - return NULL; - } - r = gcry_sexp_nth_data(sexp, 1, &size); - /* libgcrypt put 0 when first bit is set */ - if (*r == 0) { - size--; - r++; - } - memcpy(buffer, r + size - 20, 20); - gcry_sexp_release(sexp); - - sexp = gcry_sexp_find_token(sig->dsa_sig, "s", 0); - if (sexp == NULL) { - return NULL; - } - s = gcry_sexp_nth_data(sexp,1,&size); - if (*s == 0) { - size--; - s++; - } - memcpy(buffer+ 20, s + size - 20, 20); - gcry_sexp_release(sexp); - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - sexp = gcry_sexp_find_token(sig->rsa_sig, "s", 0); - if (sexp == NULL) { - return NULL; - } - s = gcry_sexp_nth_data(sexp, 1, &size); - if (*s == 0) { - size--; - s++; - } - - sig_blob = ssh_string_new(size); - if (sig_blob == NULL) { - return NULL; - } - ssh_string_fill(sig_blob, discard_const_p(char, s), size); - - gcry_sexp_release(sexp); - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - ssh_pki_log("Unknown signature key type: %d", sig->type); - return NULL; - break; - } - - return sig_blob; -} - -ssh_signature pki_signature_from_blob(const ssh_key pubkey, - const ssh_string sig_blob, - enum ssh_keytypes_e type) -{ - ssh_signature sig; - gcry_error_t err; - size_t len; - size_t rsalen; - - sig = ssh_signature_new(); - if (sig == NULL) { - return NULL; - } - - sig->type = type; - - len = ssh_string_len(sig_blob); - - switch(type) { - case SSH_KEYTYPE_DSS: - /* 40 is the dual signature blob len. */ - if (len != 40) { - ssh_pki_log("Signature has wrong size: %lu", - (unsigned long)len); - ssh_signature_free(sig); - return NULL; - } - -#ifdef DEBUG_CRYPTO - ssh_pki_log("DSA signature len: %lu", (unsigned long)len); - ssh_print_hexa("DSA signature", ssh_string_data(sig_blob), len); -#endif - - err = gcry_sexp_build(&sig->dsa_sig, - NULL, - "(sig-val(dsa(r %b)(s %b)))", - 20, - ssh_string_data(sig_blob), - 20, - (unsigned char *)ssh_string_data(sig_blob) + 20); - if (err) { - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - rsalen = (gcry_pk_get_nbits(pubkey->rsa) + 7) / 8; - - if (len > rsalen) { - ssh_pki_log("Signature is to big size: %lu", - (unsigned long)len); - ssh_signature_free(sig); - return NULL; - } - - if (len < rsalen) { - ssh_pki_log("RSA signature len %lu < %lu", - (unsigned long)len, (unsigned long)rsalen); - } - -#ifdef DEBUG_CRYPTO - ssh_pki_log("RSA signature len: %lu", (unsigned long)len); - ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len); -#endif - - err = gcry_sexp_build(&sig->rsa_sig, - NULL, - "(sig-val(rsa(s %b)))", - ssh_string_len(sig_blob), - ssh_string_data(sig_blob)); - if (err) { - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - ssh_pki_log("Unknown signature type"); - return NULL; - } - - return sig; -} - -int pki_signature_verify(ssh_session session, - const ssh_signature sig, - const ssh_key key, - const unsigned char *hash, - size_t hlen) -{ - unsigned char ghash[hlen + 1]; - gcry_sexp_t sexp; - gcry_error_t err; - - switch(key->type) { - case SSH_KEYTYPE_DSS: - /* That is to mark the number as positive */ - if(hash[0] >= 0x80) { - memcpy(ghash + 1, hash, hlen); - ghash[0] = 0; - hash = ghash; - hlen += 1; - } - - err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash); - if (err) { - ssh_set_error(session, - SSH_FATAL, - "DSA hash error: %s", gcry_strerror(err)); - return SSH_ERROR; - } - err = gcry_pk_verify(sig->dsa_sig, sexp, key->dsa); - gcry_sexp_release(sexp); - if (err) { - ssh_set_error(session, SSH_FATAL, "Invalid DSA signature"); - if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) { - ssh_set_error(session, - SSH_FATAL, - "DSA verify error: %s", - gcry_strerror(err)); - } - return SSH_ERROR; - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - err = gcry_sexp_build(&sexp, - NULL, - "(data(flags pkcs1)(hash sha1 %b))", - hlen, hash); - if (err) { - ssh_set_error(session, - SSH_FATAL, - "RSA hash error: %s", - gcry_strerror(err)); - return SSH_ERROR; - } - err = gcry_pk_verify(sig->rsa_sig, sexp, key->rsa); - gcry_sexp_release(sexp); - if (err) { - ssh_set_error(session, SSH_FATAL, "Invalid RSA signature"); - if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) { - ssh_set_error(session, - SSH_FATAL, - "RSA verify error: %s", - gcry_strerror(err)); - } - return SSH_ERROR; - } - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - ssh_set_error(session, SSH_FATAL, "Unknown public key type"); - return SSH_ERROR; - } - - return SSH_OK; -} - -ssh_signature pki_do_sign(const ssh_key privkey, - const unsigned char *hash, - size_t hlen) { - unsigned char ghash[hlen + 1]; - ssh_signature sig; - gcry_sexp_t sexp; - gcry_error_t err; - - sig = ssh_signature_new(); - if (sig == NULL) { - return NULL; - } - sig->type = privkey->type; - sig->type_c = privkey->type_c; - switch (privkey->type) { - case SSH_KEYTYPE_DSS: - /* That is to mark the number as positive */ - if(hash[0] >= 0x80) { - memcpy(ghash + 1, hash, hlen); - ghash[0] = 0; - hash = ghash; - hlen += 1; - } - - err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash); - if (err) { - ssh_signature_free(sig); - return NULL; - } - - err = gcry_pk_sign(&sig->dsa_sig, sexp, privkey->dsa); - gcry_sexp_release(sexp); - if (err) { - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - err = gcry_sexp_build(&sexp, - NULL, - "(data(flags pkcs1)(hash sha1 %b))", - hlen, - hash); - if (err) { - ssh_signature_free(sig); - return NULL; - } - - err = gcry_pk_sign(&sig->rsa_sig, sexp, privkey->rsa); - gcry_sexp_release(sexp); - if (err) { - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - ssh_signature_free(sig); - return NULL; - } - - return sig; -} - -#ifdef WITH_SERVER -ssh_signature pki_do_sign_sessionid(const ssh_key key, - const unsigned char *hash, - size_t hlen) -{ - unsigned char ghash[hlen + 1]; - ssh_signature sig; - gcry_sexp_t sexp; - gcry_error_t err; - - sig = ssh_signature_new(); - if (sig == NULL) { - return NULL; - } - sig->type = key->type; - sig->type_c = key->type_c; - - switch(key->type) { - case SSH_KEYTYPE_DSS: - /* That is to mark the number as positive */ - if(hash[0] >= 0x80) { - memcpy(ghash + 1, hash, hlen); - ghash[0] = 0; - hash = ghash; - hlen += 1; - } - - err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash); - if (err) { - ssh_signature_free(sig); - return NULL; - } - err = gcry_pk_sign(&sig->dsa_sig, sexp, key->dsa); - gcry_sexp_release(sexp); - if (err) { - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - err = gcry_sexp_build(&sexp, - NULL, - "(data(flags pkcs1)(hash sha1 %b))", - hlen, - hash); - if (err) { - ssh_signature_free(sig); - return NULL; - } - err = gcry_pk_sign(&sig->rsa_sig, sexp, key->rsa); - gcry_sexp_release(sexp); - if (err) { - ssh_signature_free(sig); - return NULL; - } - break; - case SSH_KEYTYPE_ECDSA: - case SSH_KEYTYPE_UNKNOWN: - return NULL; - } - - return sig; -} -#endif /* WITH_SERVER */ - -#endif /* HAVE_LIBGCRYPT */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/poll.c b/libssh/src/poll.c deleted file mode 100644 index 4e9f19f0..00000000 --- a/libssh/src/poll.c +++ /dev/null @@ -1,970 +0,0 @@ -/* - * poll.c - poll wrapper - * - * This file is part of the SSH Library - * - * Copyright (c) 2009-2013 by Andreas Schneider - * Copyright (c) 2003-2013 by Aris Adamantiadis - * Copyright (c) 2009 Aleksandar Kanchev - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - * - * vim: ts=2 sw=2 et cindent - */ - -#include "config.h" - -#include -#include - -#include "libssh/priv.h" -#include "libssh/libssh.h" -#include "libssh/poll.h" -#include "libssh/socket.h" -#include "libssh/session.h" -#ifdef WITH_SERVER -#include "libssh/server.h" -#include "libssh/misc.h" -#endif - - -#ifndef SSH_POLL_CTX_CHUNK -#define SSH_POLL_CTX_CHUNK 5 -#endif - -/** - * @defgroup libssh_poll The SSH poll functions. - * @ingroup libssh - * - * Add a generic way to handle sockets asynchronously. - * - * It's based on poll objects, each of which store a socket, its events and a - * callback, which gets called whenever an event is set. The poll objects are - * attached to a poll context, which should be allocated on per thread basis. - * - * Polling the poll context will poll all the attached poll objects and call - * their callbacks (handlers) if any of the socket events are set. This should - * be done within the main loop of an application. - * - * @{ - */ - -struct ssh_poll_handle_struct { - ssh_poll_ctx ctx; - ssh_session session; - union { - socket_t fd; - size_t idx; - } x; - short events; - int lock; - ssh_poll_callback cb; - void *cb_data; -}; - -struct ssh_poll_ctx_struct { - ssh_poll_handle *pollptrs; - ssh_pollfd_t *pollfds; - size_t polls_allocated; - size_t polls_used; - size_t chunk_size; -}; - -#ifdef HAVE_POLL -#include - -void ssh_poll_init(void) { - return; -} - -void ssh_poll_cleanup(void) { - return; -} - -int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { - return poll((struct pollfd *) fds, nfds, timeout); -} - -#else /* HAVE_POLL */ - -typedef int (*poll_fn)(ssh_pollfd_t *, nfds_t, int); -static poll_fn ssh_poll_emu; - -#include - -#ifdef _WIN32 -#ifndef STRICT -#define STRICT -#endif /* STRICT */ - -#include -#include -#include -#else /* _WIN32 */ -#include -#include -#include -#endif /* _WIN32 */ - -#ifdef HAVE_UNISTD_H -#include -#endif - - -/* - * This is a poll(2)-emulation using select for systems not providing a native - * poll implementation. - * - * Keep in mind that select is terribly inefficient. The interface is simply not - * meant to be used with maximum descriptor value greater, say, 32 or so. With - * a value as high as 1024 on Linux you'll pay dearly in every single call. - * poll() will be orders of magnitude faster. - */ -static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { - fd_set readfds, writefds, exceptfds; - struct timeval tv, *ptv; - socket_t max_fd; - int rc; - nfds_t i; - - if (fds == NULL) { - errno = EFAULT; - return -1; - } - - FD_ZERO (&readfds); - FD_ZERO (&writefds); - FD_ZERO (&exceptfds); - - /* compute fd_sets and find largest descriptor */ - for (rc = -1, max_fd = 0, i = 0; i < nfds; i++) { - if (fds[i].fd == SSH_INVALID_SOCKET) { - continue; - } -#ifndef _WIN32 - if (fds[i].fd >= FD_SETSIZE) { - rc = -1; - break; - } -#endif - - if (fds[i].events & (POLLIN | POLLRDNORM)) { - FD_SET (fds[i].fd, &readfds); - } - if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { - FD_SET (fds[i].fd, &writefds); - } - if (fds[i].events & (POLLPRI | POLLRDBAND)) { - FD_SET (fds[i].fd, &exceptfds); - } - if (fds[i].fd > max_fd && - (fds[i].events & (POLLIN | POLLOUT | POLLPRI | - POLLRDNORM | POLLRDBAND | - POLLWRNORM | POLLWRBAND))) { - max_fd = fds[i].fd; - rc = 0; - } - } - - if (max_fd == SSH_INVALID_SOCKET || rc == -1) { - errno = EINVAL; - return -1; - } - - if (timeout < 0) { - ptv = NULL; - } else { - ptv = &tv; - if (timeout == 0) { - tv.tv_sec = 0; - tv.tv_usec = 0; - } else { - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - } - } - - rc = select (max_fd + 1, &readfds, &writefds, &exceptfds, ptv); - if (rc < 0) { - return -1; - } - - for (rc = 0, i = 0; i < nfds; i++) - if (fds[i].fd >= 0) { - fds[i].revents = 0; - - if (FD_ISSET(fds[i].fd, &readfds)) { - int save_errno = errno; - char data[64] = {0}; - int ret; - - /* support for POLLHUP */ - ret = recv(fds[i].fd, data, 64, MSG_PEEK); -#ifdef _WIN32 - if ((ret == -1) && - (errno == WSAESHUTDOWN || errno == WSAECONNRESET || - errno == WSAECONNABORTED || errno == WSAENETRESET)) { -#else - if ((ret == -1) && - (errno == ESHUTDOWN || errno == ECONNRESET || - errno == ECONNABORTED || errno == ENETRESET)) { -#endif - fds[i].revents |= POLLHUP; - } else { - fds[i].revents |= fds[i].events & (POLLIN | POLLRDNORM); - } - - errno = save_errno; - } - if (FD_ISSET(fds[i].fd, &writefds)) { - fds[i].revents |= fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND); - } - - if (FD_ISSET(fds[i].fd, &exceptfds)) { - fds[i].revents |= fds[i].events & (POLLPRI | POLLRDBAND); - } - - if (fds[i].revents & ~POLLHUP) { - rc++; - } - } else { - fds[i].revents = POLLNVAL; - } - - return rc; -} - -void ssh_poll_init(void) { - ssh_poll_emu = bsd_poll; -} - -void ssh_poll_cleanup(void) { - ssh_poll_emu = bsd_poll; -} - -int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { - return (ssh_poll_emu)(fds, nfds, timeout); -} - -#endif /* HAVE_POLL */ - -/** - * @brief Allocate a new poll object, which could be used within a poll context. - * - * @param fd Socket that will be polled. - * @param events Poll events that will be monitored for the socket. i.e. - * POLLIN, POLLPRI, POLLOUT - * @param cb Function to be called if any of the events are set. - * The prototype of cb is: - * int (*ssh_poll_callback)(ssh_poll_handle p, socket_t fd, - * int revents, void *userdata); - * @param userdata Userdata to be passed to the callback function. NULL if - * not needed. - * - * @return A new poll object, NULL on error - */ - -ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb, - void *userdata) { - ssh_poll_handle p; - - p = malloc(sizeof(struct ssh_poll_handle_struct)); - if (p == NULL) { - return NULL; - } - ZERO_STRUCTP(p); - - p->x.fd = fd; - p->events = events; - p->cb = cb; - p->cb_data = userdata; - - return p; -} - - -/** - * @brief Free a poll object. - * - * @param p Pointer to an already allocated poll object. - */ - -void ssh_poll_free(ssh_poll_handle p) { - if(p->ctx != NULL){ - ssh_poll_ctx_remove(p->ctx,p); - p->ctx=NULL; - } - SAFE_FREE(p); -} - -/** - * @brief Get the poll context of a poll object. - * - * @param p Pointer to an already allocated poll object. - * - * @return Poll context or NULL if the poll object isn't attached. - */ -ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p) { - return p->ctx; -} - -/** - * @brief Get the events of a poll object. - * - * @param p Pointer to an already allocated poll object. - * - * @return Poll events. - */ -short ssh_poll_get_events(ssh_poll_handle p) { - return p->events; -} - -/** - * @brief Set the events of a poll object. The events will also be propagated - * to an associated poll context. - * - * @param p Pointer to an already allocated poll object. - * @param events Poll events. - */ -void ssh_poll_set_events(ssh_poll_handle p, short events) { - p->events = events; - if (p->ctx != NULL && !p->lock) { - p->ctx->pollfds[p->x.idx].events = events; - } -} - -/** - * @brief Set the file descriptor of a poll object. The FD will also be propagated - * to an associated poll context. - * - * @param p Pointer to an already allocated poll object. - * @param fd New file descriptor. - */ -void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd) { - if (p->ctx != NULL) { - p->ctx->pollfds[p->x.idx].fd = fd; - } else { - p->x.fd = fd; - } -} - -/** - * @brief Add extra events to a poll object. Duplicates are ignored. - * The events will also be propagated to an associated poll context. - * - * @param p Pointer to an already allocated poll object. - * @param events Poll events. - */ -void ssh_poll_add_events(ssh_poll_handle p, short events) { - ssh_poll_set_events(p, ssh_poll_get_events(p) | events); -} - -/** - * @brief Remove events from a poll object. Non-existent are ignored. - * The events will also be propagated to an associated poll context. - * - * @param p Pointer to an already allocated poll object. - * @param events Poll events. - */ -void ssh_poll_remove_events(ssh_poll_handle p, short events) { - ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events); -} - -/** - * @brief Get the raw socket of a poll object. - * - * @param p Pointer to an already allocated poll object. - * - * @return Raw socket. - */ - -socket_t ssh_poll_get_fd(ssh_poll_handle p) { - if (p->ctx != NULL) { - return p->ctx->pollfds[p->x.idx].fd; - } - - return p->x.fd; -} -/** - * @brief Set the callback of a poll object. - * - * @param p Pointer to an already allocated poll object. - * @param cb Function to be called if any of the events are set. - * @param userdata Userdata to be passed to the callback function. NULL if - * not needed. - */ -void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata) { - if (cb != NULL) { - p->cb = cb; - p->cb_data = userdata; - } -} - -/** - * @brief Create a new poll context. It could be associated with many poll object - * which are going to be polled at the same time as the poll context. You - * would need a single poll context per thread. - * - * @param chunk_size The size of the memory chunk that will be allocated, when - * more memory is needed. This is for efficiency reasons, - * i.e. don't allocate memory for each new poll object, but - * for the next 5. Set it to 0 if you want to use the - * library's default value. - */ -ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size) { - ssh_poll_ctx ctx; - - ctx = malloc(sizeof(struct ssh_poll_ctx_struct)); - if (ctx == NULL) { - return NULL; - } - ZERO_STRUCTP(ctx); - - if (chunk_size == 0) { - chunk_size = SSH_POLL_CTX_CHUNK; - } - - ctx->chunk_size = chunk_size; - - return ctx; -} - -/** - * @brief Free a poll context. - * - * @param ctx Pointer to an already allocated poll context. - */ -void ssh_poll_ctx_free(ssh_poll_ctx ctx) { - if (ctx->polls_allocated > 0) { - while (ctx->polls_used > 0){ - ssh_poll_handle p = ctx->pollptrs[0]; - /* - * The free function calls ssh_poll_ctx_remove() and decrements - * ctx->polls_used - */ - ssh_poll_free(p); - } - - SAFE_FREE(ctx->pollptrs); - SAFE_FREE(ctx->pollfds); - } - - SAFE_FREE(ctx); -} - -static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) { - ssh_poll_handle *pollptrs; - ssh_pollfd_t *pollfds; - - pollptrs = realloc(ctx->pollptrs, sizeof(ssh_poll_handle) * new_size); - if (pollptrs == NULL) { - return -1; - } - ctx->pollptrs = pollptrs; - - pollfds = realloc(ctx->pollfds, sizeof(ssh_pollfd_t) * new_size); - if (pollfds == NULL) { - pollptrs = realloc(ctx->pollptrs, sizeof(ssh_poll_handle) * ctx->polls_allocated); - if (pollptrs == NULL) { - return -1; - } - ctx->pollptrs = pollptrs; - return -1; - } - - ctx->pollfds = pollfds; - ctx->polls_allocated = new_size; - - return 0; -} - -/** - * @brief Add a poll object to a poll context. - * - * @param ctx Pointer to an already allocated poll context. - * @param p Pointer to an already allocated poll object. - * - * @return 0 on success, < 0 on error - */ -int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) { - socket_t fd; - - if (p->ctx != NULL) { - /* already attached to a context */ - return -1; - } - - if (ctx->polls_used == ctx->polls_allocated && - ssh_poll_ctx_resize(ctx, ctx->polls_allocated + ctx->chunk_size) < 0) { - return -1; - } - - fd = p->x.fd; - p->x.idx = ctx->polls_used++; - ctx->pollptrs[p->x.idx] = p; - ctx->pollfds[p->x.idx].fd = fd; - ctx->pollfds[p->x.idx].events = p->events; - ctx->pollfds[p->x.idx].revents = 0; - p->ctx = ctx; - - return 0; -} - -/** - * @brief Add a socket object to a poll context. - * - * @param ctx Pointer to an already allocated poll context. - * @param s A SSH socket handle - * - * @return 0 on success, < 0 on error - */ -int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, ssh_socket s) { - ssh_poll_handle p_in, p_out; - int ret; - p_in=ssh_socket_get_poll_handle_in(s); - if(p_in==NULL) - return -1; - ret = ssh_poll_ctx_add(ctx,p_in); - if(ret != 0) - return ret; - p_out=ssh_socket_get_poll_handle_out(s); - if(p_in != p_out) - ret = ssh_poll_ctx_add(ctx,p_out); - return ret; -} - - -/** - * @brief Remove a poll object from a poll context. - * - * @param ctx Pointer to an already allocated poll context. - * @param p Pointer to an already allocated poll object. - */ -void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) { - size_t i; - - i = p->x.idx; - p->x.fd = ctx->pollfds[i].fd; - p->ctx = NULL; - - ctx->polls_used--; - - /* fill the empty poll slot with the last one */ - if (ctx->polls_used > 0 && ctx->polls_used != i) { - ctx->pollfds[i] = ctx->pollfds[ctx->polls_used]; - ctx->pollptrs[i] = ctx->pollptrs[ctx->polls_used]; - ctx->pollptrs[i]->x.idx = i; - } - - /* this will always leave at least chunk_size polls allocated */ - if (ctx->polls_allocated - ctx->polls_used > ctx->chunk_size) { - ssh_poll_ctx_resize(ctx, ctx->polls_allocated - ctx->chunk_size); - } -} - -/** - * @brief Poll all the sockets associated through a poll object with a - * poll context. If any of the events are set after the poll, the - * call back function of the socket will be called. - * This function should be called once within the programs main loop. - * - * @param ctx Pointer to an already allocated poll context. - * @param timeout An upper limit on the time for which ssh_poll_ctx() will - * block, in milliseconds. Specifying a negative value - * means an infinite timeout. This parameter is passed to - * the poll() function. - * @returns SSH_OK No error. - * SSH_ERROR Error happened during the poll. - * SSH_AGAIN Timeout occured - */ - -int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout) { - int rc; - int i, used; - ssh_poll_handle p; - socket_t fd; - int revents; - - if (!ctx->polls_used) - return SSH_ERROR; - - rc = ssh_poll(ctx->pollfds, ctx->polls_used, timeout); - if(rc < 0) - return SSH_ERROR; - if (rc == 0) - return SSH_AGAIN; - used = ctx->polls_used; - for (i = 0; i < used && rc > 0; ) { - if (!ctx->pollfds[i].revents || ctx->pollptrs[i]->lock) { - i++; - } else { - int ret; - - p = ctx->pollptrs[i]; - fd = ctx->pollfds[i].fd; - revents = ctx->pollfds[i].revents; - /* avoid having any event caught during callback */ - ctx->pollfds[i].events = 0; - p->lock = 1; - if (p->cb && (ret = p->cb(p, fd, revents, p->cb_data)) < 0) { - if (ret == -2) { - return -1; - } - /* the poll was removed, reload the used counter and start again */ - used = ctx->polls_used; - i=0; - } else { - ctx->pollfds[i].revents = 0; - ctx->pollfds[i].events = p->events; - p->lock = 0; - i++; - } - - rc--; - } - } - - return rc; -} - -/** - * @internal - * @brief gets the default poll structure for the current session, - * when used in blocking mode. - * @param session SSH session - * @returns the default ssh_poll_ctx - */ -ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session){ - if(session->default_poll_ctx != NULL) - return session->default_poll_ctx; - /* 2 is enough for the default one */ - session->default_poll_ctx = ssh_poll_ctx_new(2); - return session->default_poll_ctx; -} - -/* public event API */ - -struct ssh_event_fd_wrapper { - ssh_event_callback cb; - void * userdata; -}; - -struct ssh_event_struct { - ssh_poll_ctx ctx; -#ifdef WITH_SERVER - struct ssh_list *sessions; -#endif -}; - -/** - * @brief Create a new event context. It could be associated with many - * ssh_session objects and socket fd which are going to be polled at the - * same time as the event context. You would need a single event context - * per thread. - * - * @return The ssh_event object on success, NULL on failure. - */ -ssh_event ssh_event_new(void) { - ssh_event event; - - event = malloc(sizeof(struct ssh_event_struct)); - if (event == NULL) { - return NULL; - } - ZERO_STRUCTP(event); - - event->ctx = ssh_poll_ctx_new(2); - if(event->ctx == NULL) { - free(event); - return NULL; - } - -#ifdef WITH_SERVER - event->sessions = ssh_list_new(); - if(event->sessions == NULL) { - ssh_poll_ctx_free(event->ctx); - free(event); - return NULL; - } -#endif - - return event; -} - -static int ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int revents, - void *userdata) { - struct ssh_event_fd_wrapper *pw = (struct ssh_event_fd_wrapper *)userdata; - - (void)p; - if(pw->cb != NULL) { - return pw->cb(fd, revents, pw->userdata); - } - return 0; -} - -/** - * @brief Add a fd to the event and assign it a callback, - * when used in blocking mode. - * @param event The ssh_event - * @param fd Socket that will be polled. - * @param events Poll events that will be monitored for the socket. i.e. - * POLLIN, POLLPRI, POLLOUT - * @param cb Function to be called if any of the events are set. - * The prototype of cb is: - * int (*ssh_event_callback)(socket_t fd, int revents, - * void *userdata); - * @param userdata Userdata to be passed to the callback function. NULL if - * not needed. - * - * @returns SSH_OK on success - * SSH_ERROR on failure - */ -int ssh_event_add_fd(ssh_event event, socket_t fd, short events, - ssh_event_callback cb, void *userdata) { - ssh_poll_handle p; - struct ssh_event_fd_wrapper *pw; - - if(event == NULL || event->ctx == NULL || cb == NULL - || fd == SSH_INVALID_SOCKET) { - return SSH_ERROR; - } - pw = malloc(sizeof(struct ssh_event_fd_wrapper)); - if(pw == NULL) { - return SSH_ERROR; - } - - pw->cb = cb; - pw->userdata = userdata; - - /* pw is freed by ssh_event_remove_fd */ - p = ssh_poll_new(fd, events, ssh_event_fd_wrapper_callback, pw); - if(p == NULL) { - free(pw); - return SSH_ERROR; - } - - if(ssh_poll_ctx_add(event->ctx, p) < 0) { - free(pw); - ssh_poll_free(p); - return SSH_ERROR; - } - return SSH_OK; -} - -/** - * @brief remove the poll handle from session and assign them to a event, - * when used in blocking mode. - * - * @param event The ssh_event object - * @param session The session to add to the event. - * - * @returns SSH_OK on success - * SSH_ERROR on failure - */ -int ssh_event_add_session(ssh_event event, ssh_session session) { - unsigned int i; - ssh_poll_handle p; -#ifdef WITH_SERVER - struct ssh_iterator *iterator; -#endif - - if(event == NULL || event->ctx == NULL || session == NULL) { - return SSH_ERROR; - } - if(session->default_poll_ctx == NULL) { - return SSH_ERROR; - } - for(i = 0; i < session->default_poll_ctx->polls_used; i++) { - p = session->default_poll_ctx->pollptrs[i]; - ssh_poll_ctx_remove(session->default_poll_ctx, p); - ssh_poll_ctx_add(event->ctx, p); - /* associate the pollhandler with a session so we can put it back - * at ssh_event_free() - */ - p->session = session; - } -#ifdef WITH_SERVER - iterator = ssh_list_get_iterator(event->sessions); - while(iterator != NULL) { - if((ssh_session)iterator->data == session) { - /* allow only one instance of this session */ - return SSH_OK; - } - iterator = iterator->next; - } - if(ssh_list_append(event->sessions, session) == SSH_ERROR) { - return SSH_ERROR; - } -#endif - return SSH_OK; -} - -/** - * @brief Poll all the sockets and sessions associated through an event object. - * If any of the events are set after the poll, the - * call back functions of the sessions or sockets will be called. - * This function should be called once within the programs main loop. - * - * @param event The ssh_event object to poll. - * @param timeout An upper limit on the time for which the poll will - * block, in milliseconds. Specifying a negative value - * means an infinite timeout. This parameter is passed to - * the poll() function. - * @returns SSH_OK No error. - * SSH_ERROR Error happened during the poll. - */ -int ssh_event_dopoll(ssh_event event, int timeout) { - int rc; - - if(event == NULL || event->ctx == NULL) { - return SSH_ERROR; - } - rc = ssh_poll_ctx_dopoll(event->ctx, timeout); - return rc; -} - -/** - * @brief Remove a socket fd from an event context. - * - * @param event The ssh_event object. - * @param fd The fd to remove. - * - * @returns SSH_OK on success - * SSH_ERROR on failure - */ -int ssh_event_remove_fd(ssh_event event, socket_t fd) { - register size_t i, used; - int rc = SSH_ERROR; - - if(event == NULL || event->ctx == NULL) { - return SSH_ERROR; - } - - used = event->ctx->polls_used; - for (i = 0; i < used; i++) { - if(fd == event->ctx->pollfds[i].fd) { - ssh_poll_handle p = event->ctx->pollptrs[i]; - if (p->session != NULL){ - /* we cannot free that handle, it's owned by its session */ - continue; - } - if (p->cb == ssh_event_fd_wrapper_callback) { - struct ssh_event_fd_wrapper *pw = p->cb_data; - SAFE_FREE(pw); - } - - /* - * The free function calls ssh_poll_ctx_remove() and decrements - * event->ctx->polls_used. - */ - ssh_poll_free(p); - rc = SSH_OK; - - /* restart the loop */ - used = event->ctx->polls_used; - i = 0; - } - } - - return rc; -} - -/** - * @brief Remove a session object from an event context. - * - * @param event The ssh_event object. - * @param session The session to remove. - * - * @returns SSH_OK on success - * SSH_ERROR on failure - */ -int ssh_event_remove_session(ssh_event event, ssh_session session) { - ssh_poll_handle p; - register size_t i, used; - int rc = SSH_ERROR; -#ifdef WITH_SERVER - struct ssh_iterator *iterator; -#endif - - if(event == NULL || event->ctx == NULL || session == NULL) { - return SSH_ERROR; - } - - used = event->ctx->polls_used; - for(i = 0; i < used; i++) { - p = event->ctx->pollptrs[i]; - if(p->session == session){ - ssh_poll_ctx_remove(event->ctx, p); - p->session = NULL; - ssh_poll_ctx_add(session->default_poll_ctx, p); - rc = SSH_OK; - used = 0; - } - } -#ifdef WITH_SERVER - iterator = ssh_list_get_iterator(event->sessions); - while(iterator != NULL) { - if((ssh_session)iterator->data == session) { - ssh_list_remove(event->sessions, iterator); - /* there should be only one instance of this session */ - break; - } - iterator = iterator->next; - } -#endif - - return rc; -} - -/** - * @brief Free an event context. - * - * @param event The ssh_event object to free. - * Note: you have to manually remove sessions and socket - * fds before freeing the event object. - * - */ -void ssh_event_free(ssh_event event) { - int used, i; - ssh_poll_handle p; - if(event == NULL) { - return; - } - if(event->ctx != NULL) { - used = event->ctx->polls_used; - for(i = 0; i < used; i++) { - p = event->ctx->pollptrs[i]; - if(p->session != NULL){ - ssh_poll_ctx_remove(event->ctx, p); - ssh_poll_ctx_add(p->session->default_poll_ctx, p); - p->session = NULL; - used = 0; - } - } - - ssh_poll_ctx_free(event->ctx); - } -#ifdef WITH_SERVER - if(event->sessions != NULL) { - ssh_list_free(event->sessions); - } -#endif - free(event); -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/sc25519.c b/libssh/src/sc25519.c deleted file mode 100644 index 9576d416..00000000 --- a/libssh/src/sc25519.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Public Domain, Authors: Daniel J. Bernstein, Niels Duif, Tanja Lange, - * Peter Schwabe, Bo-Yin Yang. - * Copied from supercop-20130419/crypto_sign/ed25519/ref/sc25519.c - */ - -#include "libssh/priv.h" -#include "libssh/sc25519.h" - -/*Arithmetic modulo the group order m = 2^252 + 27742317777372353535851937790883648493 = 7237005577332262213973186563042994240857116359379907606001950938285454250989 */ - -static const uint32_t m[32] = { - 0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, - 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 -}; - -static const uint32_t mu[33] = { - 0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, - 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21, - 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F -}; - -static uint32_t lt(uint32_t a,uint32_t b) /* 16-bit inputs */ -{ - unsigned int x = a; - - x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */ - x >>= 31; /* 0: no; 1: yes */ - - return x; -} - -/* Reduce coefficients of r before calling reduce_add_sub */ -static void reduce_add_sub(sc25519 *r) -{ - uint32_t pb = 0; - uint32_t b; - uint32_t mask; - int i; - unsigned char t[32]; - - for (i = 0; i < 32; i++) { - pb += m[i]; - b = lt(r->v[i],pb); - t[i] = r->v[i]-pb+(b<<8); - pb = b; - } - mask = b - 1; - for (i = 0; i < 32; i++) { - r->v[i] ^= mask & (r->v[i] ^ t[i]); - } -} - -/* Reduce coefficients of x before calling barrett_reduce */ -static void barrett_reduce(sc25519 *r, const uint32_t x[64]) -{ - /* See HAC, Alg. 14.42 */ - int i,j; - uint32_t q2[66]; - uint32_t *q3 = q2 + 33; - uint32_t r1[33]; - uint32_t r2[33]; - uint32_t carry; - uint32_t pb = 0; - uint32_t b; - - for (i = 0; i < 66; i++) { - q2[i] = 0; - } - for (i = 0; i < 33; i++) { - r2[i] = 0; - } - - for (i = 0; i < 33; i++) { - for (j = 0; j < 33; j++) { - if (i + j >= 31) { - q2[i+j] += mu[i]*x[j+31]; - } - } - } - - carry = q2[31] >> 8; - q2[32] += carry; - carry = q2[32] >> 8; - q2[33] += carry; - - for (i = 0; i < 33; i++) { - r1[i] = x[i]; - } - - for (i = 0; i < 32; i++) { - for (j = 0; j < 33; j++) { - if (i + j < 33) { - r2[i+j] += m[i]*q3[j]; - } - } - } - - for (i = 0; i < 32; i++) { - carry = r2[i] >> 8; - r2[i+1] += carry; - r2[i] &= 0xff; - } - - for (i = 0; i < 32; i++) { - pb += r2[i]; - b = lt(r1[i],pb); - r->v[i] = r1[i]-pb+(b<<8); - pb = b; - } - - /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3 - * If so: Handle it here! - */ - - reduce_add_sub(r); - reduce_add_sub(r); -} - -void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]) -{ - int i; - uint32_t t[64]; - - for (i = 0; i < 32; i++) { - t[i] = x[i]; - } - for (i = 32; i < 64; i++) { - t[i] = 0; - } - - barrett_reduce(r, t); -} - -void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16]) -{ - int i; - - for (i = 0; i < 16; i++) { - r->v[i] = x[i]; - } -} - -void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]) -{ - int i; - uint32_t t[64]; - - for (i = 0; i < 64; i++) { - t[i] = x[i]; - } - - barrett_reduce(r, t); -} - -void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x) -{ - int i; - - for (i = 0; i < 16; i++) { - r->v[i] = x->v[i]; - } - for (i = 0; i < 16; i++) { - r->v[16+i] = 0; - } -} - -void sc25519_to32bytes(unsigned char r[32], const sc25519 *x) -{ - int i; - - for (i = 0; i < 32; i++) { - r[i] = x->v[i]; - } -} - -int sc25519_iszero_vartime(const sc25519 *x) -{ - int i; - - for (i = 0; i < 32; i++) { - if(x->v[i] != 0) { - return 0; - } - } - - return 1; -} - -int sc25519_isshort_vartime(const sc25519 *x) -{ - int i; - - for (i = 31; i > 15; i--) { - if (x->v[i] != 0) { - return 0; - } - } - - return 1; -} - -int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y) -{ - int i; - - for (i = 31; i >= 0; i--) { - if (x->v[i] < y->v[i]) { - return 1; - } - if (x->v[i] > y->v[i]) { - return 0; - } - } - - return 0; -} - -void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y) -{ - int i, carry; - - for (i = 0; i < 32; i++) { - r->v[i] = x->v[i] + y->v[i]; - } - - for (i = 0;i < 31; i++) { - carry = r->v[i] >> 8; - r->v[i+1] += carry; - r->v[i] &= 0xff; - } - - reduce_add_sub(r); -} - -void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y) -{ - uint32_t b = 0; - uint32_t t; - int i; - - for (i = 0; i < 32; i++) { - t = x->v[i] - y->v[i] - b; - r->v[i] = t & 255; - b = (t >> 8) & 1; - } -} - -void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y) -{ - int i,j,carry; - uint32_t t[64]; - - for (i = 0; i < 64; i++) { - t[i] = 0; - } - - for (i = 0; i < 32; i++) { - for (j = 0; j < 32; j++) { - t[i+j] += x->v[i] * y->v[j]; - } - } - - /* Reduce coefficients */ - for (i = 0; i < 63; i++) { - carry = t[i] >> 8; - t[i+1] += carry; - t[i] &= 0xff; - } - - barrett_reduce(r, t); -} - -void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y) -{ - sc25519 t; - sc25519_from_shortsc(&t, y); - sc25519_mul(r, x, &t); -} - -void sc25519_window3(signed char r[85], const sc25519 *s) -{ - char carry; - int i; - - for (i = 0; i < 10; i++) { - r[8*i+0] = s->v[3*i+0] & 7; - r[8*i+1] = (s->v[3*i+0] >> 3) & 7; - r[8*i+2] = (s->v[3*i+0] >> 6) & 7; - r[8*i+2] ^= (s->v[3*i+1] << 2) & 7; - r[8*i+3] = (s->v[3*i+1] >> 1) & 7; - r[8*i+4] = (s->v[3*i+1] >> 4) & 7; - r[8*i+5] = (s->v[3*i+1] >> 7) & 7; - r[8*i+5] ^= (s->v[3*i+2] << 1) & 7; - r[8*i+6] = (s->v[3*i+2] >> 2) & 7; - r[8*i+7] = (s->v[3*i+2] >> 5) & 7; - } - r[8*i+0] = s->v[3*i+0] & 7; - r[8*i+1] = (s->v[3*i+0] >> 3) & 7; - r[8*i+2] = (s->v[3*i+0] >> 6) & 7; - r[8*i+2] ^= (s->v[3*i+1] << 2) & 7; - r[8*i+3] = (s->v[3*i+1] >> 1) & 7; - r[8*i+4] = (s->v[3*i+1] >> 4) & 7; - - /* Making it signed */ - carry = 0; - for (i = 0; i < 84; i++) { - r[i] += carry; - r[i+1] += r[i] >> 3; - r[i] &= 7; - carry = r[i] >> 2; - r[i] -= carry<<3; - } - - r[84] += carry; -} - -void sc25519_window5(signed char r[51], const sc25519 *s) -{ - char carry; - int i; - - for (i = 0; i < 6; i++) { - r[8*i+0] = s->v[5*i+0] & 31; - r[8*i+1] = (s->v[5*i+0] >> 5) & 31; - r[8*i+1] ^= (s->v[5*i+1] << 3) & 31; - r[8*i+2] = (s->v[5*i+1] >> 2) & 31; - r[8*i+3] = (s->v[5*i+1] >> 7) & 31; - r[8*i+3] ^= (s->v[5*i+2] << 1) & 31; - r[8*i+4] = (s->v[5*i+2] >> 4) & 31; - r[8*i+4] ^= (s->v[5*i+3] << 4) & 31; - r[8*i+5] = (s->v[5*i+3] >> 1) & 31; - r[8*i+6] = (s->v[5*i+3] >> 6) & 31; - r[8*i+6] ^= (s->v[5*i+4] << 2) & 31; - r[8*i+7] = (s->v[5*i+4] >> 3) & 31; - } - r[8*i+0] = s->v[5*i+0] & 31; - r[8*i+1] = (s->v[5*i+0] >> 5) & 31; - r[8*i+1] ^= (s->v[5*i+1] << 3) & 31; - r[8*i+2] = (s->v[5*i+1] >> 2) & 31; - - /* Making it signed */ - carry = 0; - for (i = 0; i < 50; i++) { - r[i] += carry; - r[i+1] += r[i] >> 5; - r[i] &= 31; - carry = r[i] >> 4; - r[i] -= carry<<5; - } - - r[50] += carry; -} - -void sc25519_2interleave2(unsigned char r[127], - const sc25519 *s1, - const sc25519 *s2) -{ - int i; - - for (i = 0; i < 31; i++) { - r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2); - r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2); - r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2); - r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2); - } - r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2); - r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2); - r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2); -} diff --git a/libssh/src/scp.c b/libssh/src/scp.c deleted file mode 100644 index 0208ddc5..00000000 --- a/libssh/src/scp.c +++ /dev/null @@ -1,837 +0,0 @@ -/* - * scp - SSH scp wrapper functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/scp.h" - -/** - * @defgroup libssh_scp The SSH scp functions - * @ingroup libssh - * - * SCP protocol over SSH functions - * - * @{ - */ - -/** - * @brief Create a new scp session. - * - * @param[in] session The SSH session to use. - * - * @param[in] mode One of SSH_SCP_WRITE or SSH_SCP_READ, depending if you - * need to drop files remotely or read them. - * It is not possible to combine read and write. - * SSH_SCP_RECURSIVE Flag can be or'ed to this to indicate - * that you're going to use recursion. Browsing through - * directories is not possible without this. - * - * @param[in] location The directory in which write or read will be done. Any - * push or pull will be relative to this place. - * This can also be a pattern of files to download (read). - * - * @returns A ssh_scp handle, NULL if the creation was impossible. - */ -ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location){ - ssh_scp scp=malloc(sizeof(struct ssh_scp_struct)); - if(scp == NULL){ - ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp"); - return NULL; - } - ZERO_STRUCTP(scp); - if((mode&~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE && (mode &~SSH_SCP_RECURSIVE) != SSH_SCP_READ){ - ssh_set_error(session,SSH_FATAL,"Invalid mode %d for ssh_scp_new()",mode); - ssh_scp_free(scp); - return NULL; - } - scp->location=strdup(location); - if (scp->location == NULL) { - ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp"); - ssh_scp_free(scp); - return NULL; - } - scp->session=session; - scp->mode=mode & ~SSH_SCP_RECURSIVE; - scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0; - scp->channel=NULL; - scp->state=SSH_SCP_NEW; - return scp; -} - -/** - * @brief Initialize the scp channel. - * - * @param[in] scp The scp context to initialize. - * - * @return SSH_OK on success or an SSH error code. - * - * @see ssh_scp_new() - */ -int ssh_scp_init(ssh_scp scp) -{ - int r; - char execbuffer[1024]; - uint8_t code; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_NEW){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_init called under invalid state"); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PROTOCOL,"Initializing scp session %s %son location '%s'", - scp->mode==SSH_SCP_WRITE?"write":"read", - scp->recursive?"recursive ":"", - scp->location); - scp->channel=ssh_channel_new(scp->session); - if(scp->channel == NULL){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r= ssh_channel_open_session(scp->channel); - if(r==SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(scp->mode == SSH_SCP_WRITE) - snprintf(execbuffer,sizeof(execbuffer),"scp -t %s %s", - scp->recursive ? "-r":"", scp->location); - else - snprintf(execbuffer,sizeof(execbuffer),"scp -f %s %s", - scp->recursive ? "-r":"", scp->location); - if(ssh_channel_request_exec(scp->channel,execbuffer) == SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(scp->mode == SSH_SCP_WRITE){ - r=ssh_channel_read(scp->channel,&code,1,0); - if(r<=0){ - ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(code != 0){ - ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - } else { - ssh_channel_write(scp->channel,"",1); - } - if(scp->mode == SSH_SCP_WRITE) - scp->state=SSH_SCP_WRITE_INITED; - else - scp->state=SSH_SCP_READ_INITED; - return SSH_OK; -} - -/** - * @brief Close the scp channel. - * - * @param[in] scp The scp context to close. - * - * @return SSH_OK on success or an SSH error code. - * - * @see ssh_scp_init() - */ -int ssh_scp_close(ssh_scp scp) -{ - char buffer[128]; - int err; - if(scp==NULL) - return SSH_ERROR; - if(scp->channel != NULL){ - if(ssh_channel_send_eof(scp->channel) == SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - /* avoid situations where data are buffered and - * not yet stored on disk. This can happen if the close is sent - * before we got the EOF back - */ - while(!ssh_channel_is_eof(scp->channel)){ - err=ssh_channel_read(scp->channel,buffer,sizeof(buffer),0); - if(err==SSH_ERROR || err==0) - break; - } - if(ssh_channel_close(scp->channel) == SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - ssh_channel_free(scp->channel); - scp->channel=NULL; - } - scp->state=SSH_SCP_NEW; - return SSH_OK; -} - -/** - * @brief Free a scp context. - * - * @param[in] scp The context to free. - * - * @see ssh_scp_new() - */ -void ssh_scp_free(ssh_scp scp) -{ - if(scp==NULL) - return; - if(scp->state != SSH_SCP_NEW) - ssh_scp_close(scp); - if(scp->channel) - ssh_channel_free(scp->channel); - SAFE_FREE(scp->location); - SAFE_FREE(scp->request_name); - SAFE_FREE(scp->warning); - SAFE_FREE(scp); -} - -/** - * @brief Create a directory in a scp in sink mode. - * - * @param[in] scp The scp handle. - * - * @param[in] dirname The name of the directory being created. - * - * @param[in] mode The UNIX permissions for the new directory, e.g. 0755. - * - * @returns SSH_OK if the directory has been created, SSH_ERROR if - * an error occured. - * - * @see ssh_scp_leave_directory() - */ -int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode){ - char buffer[1024]; - int r; - uint8_t code; - char *dir; - char *perms; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_WRITE_INITED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_directory called under invalid state"); - return SSH_ERROR; - } - dir=ssh_basename(dirname); - perms=ssh_scp_string_mode(mode); - snprintf(buffer, sizeof(buffer), "D%s 0 %s\n", perms, dir); - SAFE_FREE(dir); - SAFE_FREE(perms); - r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); - if(r==SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r=ssh_channel_read(scp->channel,&code,1,0); - if(r<=0){ - ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(code != 0){ - ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - return SSH_OK; -} - -/** - * @brief Leave a directory. - * - * @returns SSH_OK if the directory has been left,SSH_ERROR if an - * error occured. - * - * @see ssh_scp_push_directory() - */ - int ssh_scp_leave_directory(ssh_scp scp){ - char buffer[]="E\n"; - int r; - uint8_t code; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_WRITE_INITED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_leave_directory called under invalid state"); - return SSH_ERROR; - } - r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); - if(r==SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r=ssh_channel_read(scp->channel,&code,1,0); - if(r<=0){ - ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(code != 0){ - ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - return SSH_OK; -} - -/** - * @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit size. - * - * @param[in] scp The scp handle. - * - * @param[in] filename The name of the file being sent. It should not contain - * any path indicator - * - * @param[in] size Exact size in bytes of the file being sent. - * - * @param[in] mode The UNIX permissions for the new file, e.g. 0644. - * - * @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an - * error occured. - * - * @see ssh_scp_push_file() - */ -int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int mode){ - char buffer[1024]; - int r; - uint8_t code; - char *file; - char *perms; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_WRITE_INITED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_file called under invalid state"); - return SSH_ERROR; - } - file=ssh_basename(filename); - perms=ssh_scp_string_mode(mode); - SSH_LOG(SSH_LOG_PROTOCOL,"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",file,size,perms); - snprintf(buffer, sizeof(buffer), "C%s %" PRIu64 " %s\n", perms, size, file); - SAFE_FREE(file); - SAFE_FREE(perms); - r=ssh_channel_write(scp->channel,buffer,strlen(buffer)); - if(r==SSH_ERROR){ - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r=ssh_channel_read(scp->channel,&code,1,0); - if(r<=0){ - ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session)); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - if(code != 0){ - ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - scp->filelen = size; - scp->processed = 0; - scp->state=SSH_SCP_WRITE_WRITING; - return SSH_OK; -} - -/** - * @brief Initialize the sending of a file to a scp in sink mode. - * - * @param[in] scp The scp handle. - * - * @param[in] filename The name of the file being sent. It should not contain - * any path indicator - * - * @param[in] size Exact size in bytes of the file being sent. - * - * @param[in] mode The UNIX permissions for the new file, e.g. 0644. - * - * @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an - * error occured. - */ -int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode){ - return ssh_scp_push_file64(scp, filename, (uint64_t) size, mode); -} - -/** - * @internal - * - * @brief Wait for a response of the scp server. - * - * @param[in] scp The scp handle. - * - * @param[out] response A pointer where the response message must be copied if - * any. This pointer must then be free'd. - * - * @returns The return code, SSH_ERROR a error occured. - */ -int ssh_scp_response(ssh_scp scp, char **response){ - unsigned char code; - int r; - char msg[128]; - if(scp==NULL) - return SSH_ERROR; - r=ssh_channel_read(scp->channel,&code,1,0); - if(r == SSH_ERROR) - return SSH_ERROR; - if(code == 0) - return 0; - if(code > 2){ - ssh_set_error(scp->session,SSH_FATAL, "SCP: invalid status code %ud received", code); - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - r=ssh_scp_read_string(scp,msg,sizeof(msg)); - if(r==SSH_ERROR) - return r; - /* Warning */ - if(code == 1){ - ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Warning: status code 1 received: %s", msg); - SSH_LOG(SSH_LOG_RARE,"SCP: Warning: status code 1 received: %s", msg); - if(response) - *response=strdup(msg); - return 1; - } - if(code == 2){ - ssh_set_error(scp->session,SSH_FATAL, "SCP: Error: status code 2 received: %s", msg); - if(response) - *response=strdup(msg); - return 2; - } - /* Not reached */ - return SSH_ERROR; -} - -/** - * @brief Write into a remote scp file. - * - * @param[in] scp The scp handle. - * - * @param[in] buffer The buffer to write. - * - * @param[in] len The number of bytes to write. - * - * @returns SSH_OK if the write was successful, SSH_ERROR an error - * occured while writing. - */ -int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len){ - int w; - int r; - uint8_t code; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_WRITE_WRITING){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_write called under invalid state"); - return SSH_ERROR; - } - if(scp->processed + len > scp->filelen) - len = (size_t) (scp->filelen - scp->processed); - /* hack to avoid waiting for window change */ - r = ssh_channel_poll(scp->channel, 0); - if (r == SSH_ERROR) { - scp->state = SSH_SCP_ERROR; - return SSH_ERROR; - } - w=ssh_channel_write(scp->channel,buffer,len); - if(w != SSH_ERROR) - scp->processed += w; - else { - scp->state=SSH_SCP_ERROR; - //return=channel_get_exit_status(scp->channel); - return SSH_ERROR; - } - /* Far end sometimes send a status message, which we need to read - * and handle */ - r = ssh_channel_poll(scp->channel,0); - if(r > 0){ - r = ssh_channel_read(scp->channel, &code, 1, 0); - if(r == SSH_ERROR){ - return SSH_ERROR; - } - if(code == 1 || code == 2){ - ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Error: status code %i received", code); - return SSH_ERROR; - } - } - /* Check if we arrived at end of file */ - if(scp->processed == scp->filelen) { - code = 0; - w = ssh_channel_write(scp->channel, &code, 1); - if(w == SSH_ERROR){ - scp->state = SSH_SCP_ERROR; - return SSH_ERROR; - } - scp->processed=scp->filelen=0; - scp->state=SSH_SCP_WRITE_INITED; - } - return SSH_OK; -} - -/** - * @brief Read a string on a channel, terminated by '\n' - * - * @param[in] scp The scp handle. - * - * @param[out] buffer A pointer to a buffer to place the string. - * - * @param[in] len The size of the buffer in bytes. If the string is bigger - * than len-1, only len-1 bytes are read and the string is - * null-terminated. - * - * @returns SSH_OK if the string was read, SSH_ERROR if an error - * occured while reading. - */ -int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len){ - size_t r=0; - int err=SSH_OK; - if(scp==NULL) - return SSH_ERROR; - while(rchannel,&buffer[r],1,0); - if(err==SSH_ERROR){ - break; - } - if(err==0){ - ssh_set_error(scp->session,SSH_FATAL,"End of file while reading string"); - err=SSH_ERROR; - break; - } - r++; - if(buffer[r-1] == '\n') - break; - } - buffer[r]=0; - return err; -} - -/** - * @brief Wait for a scp request (file, directory). - * - * @returns SSH_SCP_REQUEST_NEWFILE: The other side is sending - * a file - * SSH_SCP_REQUEST_NEWDIR: The other side is sending - * a directory - * SSH_SCP_REQUEST_ENDDIR: The other side has - * finished with the current - * directory - * SSH_SCP_REQUEST_WARNING: The other side sent us a warning - * SSH_SCP_REQUEST_EOF: The other side finished sending us - * files and data. - * SSH_ERROR: Some error happened - * - * @see ssh_scp_read() - * @see ssh_scp_deny_request() - * @see ssh_scp_accept_request() - * @see ssh_scp_request_get_warning() - */ -int ssh_scp_pull_request(ssh_scp scp){ - char buffer[MAX_BUF_SIZE] = {0}; - char *mode=NULL; - char *p,*tmp; - uint64_t size; - char *name=NULL; - int err; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_READ_INITED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_pull_request called under invalid state"); - return SSH_ERROR; - } - err=ssh_scp_read_string(scp,buffer,sizeof(buffer)); - if(err==SSH_ERROR){ - if(ssh_channel_is_eof(scp->channel)){ - scp->state=SSH_SCP_TERMINATED; - return SSH_SCP_REQUEST_EOF; - } - return err; - } - p=strchr(buffer,'\n'); - if(p!=NULL) - *p='\0'; - SSH_LOG(SSH_LOG_PROTOCOL,"Received SCP request: '%s'",buffer); - switch(buffer[0]){ - case 'C': - /* File */ - case 'D': - /* Directory */ - p=strchr(buffer,' '); - if(p==NULL) - goto error; - *p='\0'; - p++; - //mode=strdup(&buffer[1]); - scp->request_mode=ssh_scp_integer_mode(&buffer[1]); - tmp=p; - p=strchr(p,' '); - if(p==NULL) - goto error; - *p=0; - size = strtoull(tmp,NULL,10); - p++; - name=strdup(p); - SAFE_FREE(scp->request_name); - scp->request_name=name; - if(buffer[0]=='C'){ - scp->filelen=size; - scp->request_type=SSH_SCP_REQUEST_NEWFILE; - } else { - scp->filelen='0'; - scp->request_type=SSH_SCP_REQUEST_NEWDIR; - } - scp->state=SSH_SCP_READ_REQUESTED; - scp->processed = 0; - return scp->request_type; - break; - case 'E': - scp->request_type=SSH_SCP_REQUEST_ENDDIR; - ssh_channel_write(scp->channel,"",1); - return scp->request_type; - case 0x1: - ssh_set_error(scp->session,SSH_REQUEST_DENIED,"SCP: Warning: %s",&buffer[1]); - scp->request_type=SSH_SCP_REQUEST_WARNING; - SAFE_FREE(scp->warning); - scp->warning=strdup(&buffer[1]); - return scp->request_type; - case 0x2: - ssh_set_error(scp->session,SSH_FATAL,"SCP: Error: %s",&buffer[1]); - return SSH_ERROR; - case 'T': - /* Timestamp */ - default: - ssh_set_error(scp->session,SSH_FATAL,"Unhandled message: (%d)%s",buffer[0],buffer); - return SSH_ERROR; - } - - /* a parsing error occured */ - error: - SAFE_FREE(name); - SAFE_FREE(mode); - ssh_set_error(scp->session,SSH_FATAL,"Parsing error while parsing message: %s",buffer); - return SSH_ERROR; -} - -/** - * @brief Deny the transfer of a file or creation of a directory coming from the - * remote party. - * - * @param[in] scp The scp handle. - * @param[in] reason A nul-terminated string with a human-readable - * explanation of the deny. - * - * @returns SSH_OK if the message was sent, SSH_ERROR if the sending - * the message failed, or sending it in a bad state. - */ -int ssh_scp_deny_request(ssh_scp scp, const char *reason){ - char buffer[MAX_BUF_SIZE]; - int err; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_READ_REQUESTED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state"); - return SSH_ERROR; - } - snprintf(buffer,sizeof(buffer),"%c%s\n",2,reason); - err=ssh_channel_write(scp->channel,buffer,strlen(buffer)); - if(err==SSH_ERROR) { - return SSH_ERROR; - } - else { - scp->state=SSH_SCP_READ_INITED; - return SSH_OK; - } -} - -/** - * @brief Accepts transfer of a file or creation of a directory coming from the - * remote party. - * - * @param[in] scp The scp handle. - * - * @returns SSH_OK if the message was sent, SSH_ERROR if sending the - * message failed, or sending it in a bad state. - */ -int ssh_scp_accept_request(ssh_scp scp){ - char buffer[]={0x00}; - int err; - if(scp==NULL) - return SSH_ERROR; - if(scp->state != SSH_SCP_READ_REQUESTED){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state"); - return SSH_ERROR; - } - err=ssh_channel_write(scp->channel,buffer,1); - if(err==SSH_ERROR) { - return SSH_ERROR; - } - if(scp->request_type==SSH_SCP_REQUEST_NEWFILE) - scp->state=SSH_SCP_READ_READING; - else - scp->state=SSH_SCP_READ_INITED; - return SSH_OK; -} - -/** @brief Read from a remote scp file - * @param[in] scp The scp handle. - * - * @param[in] buffer The destination buffer. - * - * @param[in] size The size of the buffer. - * - * @returns The nNumber of bytes read, SSH_ERROR if an error occured - * while reading. - */ -int ssh_scp_read(ssh_scp scp, void *buffer, size_t size){ - int r; - int code; - if(scp==NULL) - return SSH_ERROR; - if(scp->state == SSH_SCP_READ_REQUESTED && scp->request_type == SSH_SCP_REQUEST_NEWFILE){ - r=ssh_scp_accept_request(scp); - if(r==SSH_ERROR) - return r; - } - if(scp->state != SSH_SCP_READ_READING){ - ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_read called under invalid state"); - return SSH_ERROR; - } - if(scp->processed + size > scp->filelen) - size = (size_t) (scp->filelen - scp->processed); - if(size > 65536) - size=65536; /* avoid too large reads */ - r=ssh_channel_read(scp->channel,buffer,size,0); - if(r != SSH_ERROR) - scp->processed += r; - else { - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - /* Check if we arrived at end of file */ - if(scp->processed == scp->filelen) { - scp->processed=scp->filelen=0; - ssh_channel_write(scp->channel,"",1); - code=ssh_scp_response(scp,NULL); - if(code == 0){ - scp->state=SSH_SCP_READ_INITED; - return r; - } - if(code==1){ - scp->state=SSH_SCP_READ_INITED; - return SSH_ERROR; - } - scp->state=SSH_SCP_ERROR; - return SSH_ERROR; - } - return r; -} - -/** - * @brief Get the name of the directory or file being pushed from the other - * party. - * - * @returns The file name, NULL on error. The string should not be - * freed. - */ -const char *ssh_scp_request_get_filename(ssh_scp scp){ - if(scp==NULL) - return NULL; - return scp->request_name; -} - -/** - * @brief Get the permissions of the directory or file being pushed from the - * other party. - * - * @returns The UNIX permission, e.g 0644, -1 on error. - */ -int ssh_scp_request_get_permissions(ssh_scp scp){ - if(scp==NULL) - return -1; - return scp->request_mode; -} - -/** @brief Get the size of the file being pushed from the other party. - * - * @returns The numeric size of the file being read. - * @warning The real size may not fit in a 32 bits field and may - * be truncated. - * @see ssh_scp_request_get_size64() - */ -size_t ssh_scp_request_get_size(ssh_scp scp){ - if(scp==NULL) - return 0; - return (size_t)scp->filelen; -} - -/** @brief Get the size of the file being pushed from the other party. - * - * @returns The numeric size of the file being read. - */ -uint64_t ssh_scp_request_get_size64(ssh_scp scp){ - if(scp==NULL) - return 0; - return scp->filelen; -} - -/** - * @brief Convert a scp text mode to an integer. - * - * @param[in] mode The mode to convert, e.g. "0644". - * - * @returns An integer value, e.g. 420 for "0644". - */ -int ssh_scp_integer_mode(const char *mode){ - int value=strtoul(mode,NULL,8) & 0xffff; - return value; -} - -/** - * @brief Convert a unix mode into a scp string. - * - * @param[in] mode The mode to convert, e.g. 420 or 0644. - * - * @returns A pointer to a malloc'ed string containing the scp mode, - * e.g. "0644". - */ -char *ssh_scp_string_mode(int mode){ - char buffer[16]; - snprintf(buffer,sizeof(buffer),"%.4o",mode); - return strdup(buffer); -} - -/** - * @brief Get the warning string from a scp handle. - * - * @param[in] scp The scp handle. - * - * @returns A warning string, or NULL on error. The string should - * not be freed. - */ -const char *ssh_scp_request_get_warning(ssh_scp scp){ - if(scp==NULL) - return NULL; - return scp->warning; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/server.c b/libssh/src/server.c deleted file mode 100644 index 3a38fc7b..00000000 --- a/libssh/src/server.c +++ /dev/null @@ -1,1207 +0,0 @@ -/* - * server.c - functions for creating a SSH server - * - * This file is part of the SSH Library - * - * Copyright (c) 2004-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -# include -# include - - /* - * is necessary for getaddrinfo before Windows XP, but it isn't - * available on some platforms like MinGW. - */ -# ifdef HAVE_WSPIAPI_H -# include -# endif -#else -# include -#endif - -#include "libssh/priv.h" -#include "libssh/libssh.h" -#include "libssh/server.h" -#include "libssh/ssh2.h" -#include "libssh/buffer.h" -#include "libssh/packet.h" -#include "libssh/socket.h" -#include "libssh/session.h" -#include "libssh/kex.h" -#include "libssh/misc.h" -#include "libssh/pki.h" -#include "libssh/dh.h" -#include "libssh/messages.h" -#include "libssh/options.h" -#include "libssh/curve25519.h" - -#define set_status(session, status) do {\ - if (session->common.callbacks && session->common.callbacks->connect_status_function) \ - session->common.callbacks->connect_status_function(session->common.callbacks->userdata, status); \ - } while (0) - -static int dh_handshake_server(ssh_session session); - - -/** - * @addtogroup libssh_server - * - * @{ - */ - -/** @internal - * This functions sets the Key Exchange protocols to be accepted - * by the server. They depend on - * -What the user asked (via options) - * -What is available (keys) - * It should then accept the intersection of what the user asked - * and what is available, and return an error if nothing matches - */ - -static int server_set_kex(ssh_session session) { - struct ssh_kex_struct *server = &session->next_crypto->server_kex; - int i, j, rc; - const char *wanted; - char hostkeys[64] = {0}; - enum ssh_keytypes_e keytype; - size_t len; - - ZERO_STRUCTP(server); - ssh_get_random(server->cookie, 16, 0); - -#ifdef HAVE_ECC - if (session->srv.ecdsa_key != NULL) { - snprintf(hostkeys, sizeof(hostkeys), - "%s", session->srv.ecdsa_key->type_c); - } -#endif - if (session->srv.dsa_key != NULL) { - len = strlen(hostkeys); - keytype = ssh_key_type(session->srv.dsa_key); - - snprintf(hostkeys + len, sizeof(hostkeys) - len, - ",%s", ssh_key_type_to_char(keytype)); - } - if (session->srv.rsa_key != NULL) { - len = strlen(hostkeys); - keytype = ssh_key_type(session->srv.rsa_key); - - snprintf(hostkeys + len, sizeof(hostkeys) - len, - ",%s", ssh_key_type_to_char(keytype)); - } - - if (strlen(hostkeys) == 0) { - return -1; - } - - rc = ssh_options_set_algo(session, - SSH_HOSTKEYS, - hostkeys[0] == ',' ? hostkeys + 1 : hostkeys); - if (rc < 0) { - return -1; - } - - for (i = 0; i < 10; i++) { - if ((wanted = session->opts.wanted_methods[i]) == NULL) { - wanted = ssh_kex_get_supported_method(i); - } - server->methods[i] = strdup(wanted); - if (server->methods[i] == NULL) { - for (j = 0; j < i; j++) { - SAFE_FREE(server->methods[j]); - } - return -1; - } - } - - return 0; -} - -/** @internal - * @brief parse an incoming SSH_MSG_KEXDH_INIT packet and complete - * key exchange - **/ -static int ssh_server_kexdh_init(ssh_session session, ssh_buffer packet){ - ssh_string e; - e = buffer_get_ssh_string(packet); - if (e == NULL) { - ssh_set_error(session, SSH_FATAL, "No e number in client request"); - return -1; - } - if (dh_import_e(session, e) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot import e number"); - session->session_state=SSH_SESSION_STATE_ERROR; - } else { - session->dh_handshake_state=DH_STATE_INIT_SENT; - dh_handshake_server(session); - } - ssh_string_free(e); - return SSH_OK; -} - -SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){ - int rc; - (void)type; - (void)user; - - SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_KEXDH_INIT"); - if(session->dh_handshake_state != DH_STATE_INIT){ - SSH_LOG(SSH_LOG_RARE,"Invalid state for SSH_MSG_KEXDH_INIT"); - goto error; - } - - /* If first_kex_packet_follows guess was wrong, ignore this message. */ - if (session->first_kex_follows_guess_wrong != 0) { - SSH_LOG(SSH_LOG_RARE, "first_kex_packet_follows guess was wrong, " - "ignoring first SSH_MSG_KEXDH_INIT message"); - session->first_kex_follows_guess_wrong = 0; - goto error; - } - - switch(session->next_crypto->kex_type){ - case SSH_KEX_DH_GROUP1_SHA1: - case SSH_KEX_DH_GROUP14_SHA1: - rc=ssh_server_kexdh_init(session, packet); - break; - #ifdef HAVE_ECDH - case SSH_KEX_ECDH_SHA2_NISTP256: - rc = ssh_server_ecdh_init(session, packet); - break; - #endif - #ifdef HAVE_CURVE25519 - case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: - rc = ssh_server_curve25519_init(session, packet); - break; - #endif - default: - ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_kexdh_init"); - rc = SSH_ERROR; - } - if (rc == SSH_ERROR) - session->session_state = SSH_SESSION_STATE_ERROR; - error: - - return SSH_PACKET_USED; -} - -int ssh_get_key_params(ssh_session session, ssh_key *privkey){ - ssh_key pubkey; - ssh_string pubkey_blob; - int rc; - - switch(session->srv.hostkey) { - case SSH_KEYTYPE_DSS: - *privkey = session->srv.dsa_key; - break; - case SSH_KEYTYPE_RSA: - case SSH_KEYTYPE_RSA1: - *privkey = session->srv.rsa_key; - break; - case SSH_KEYTYPE_ECDSA: - *privkey = session->srv.ecdsa_key; - break; - case SSH_KEYTYPE_UNKNOWN: - default: - *privkey = NULL; - } - - rc = ssh_pki_export_privkey_to_pubkey(*privkey, &pubkey); - if (rc < 0) { - ssh_set_error(session, SSH_FATAL, - "Could not get the public key from the private key"); - - return -1; - } - - rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob); - ssh_key_free(pubkey); - if (rc < 0) { - ssh_set_error_oom(session); - return -1; - } - - dh_import_pubkey(session, pubkey_blob); - return SSH_OK; -} - -static int dh_handshake_server(ssh_session session) { - ssh_key privkey; - ssh_string sig_blob; - ssh_string f; - int rc; - - if (dh_generate_y(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Could not create y number"); - return -1; - } - if (dh_generate_f(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Could not create f number"); - return -1; - } - - f = dh_get_f(session); - if (f == NULL) { - ssh_set_error(session, SSH_FATAL, "Could not get the f number"); - return -1; - } - - if (ssh_get_key_params(session,&privkey) != SSH_OK){ - ssh_string_free(f); - return -1; - } - - if (dh_build_k(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Could not import the public key"); - ssh_string_free(f); - return -1; - } - - if (make_sessionid(session) != SSH_OK) { - ssh_set_error(session, SSH_FATAL, "Could not create a session id"); - ssh_string_free(f); - return -1; - } - - sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); - if (sig_blob == NULL) { - ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); - ssh_string_free(f); - return -1; - } - - rc = ssh_buffer_pack(session->out_buffer, - "bSSS", - SSH2_MSG_KEXDH_REPLY, - session->next_crypto->server_pubkey, - f, - sig_blob); - ssh_string_free(f); - ssh_string_free(sig_blob); - if(rc != SSH_OK){ - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - return -1; - } - - if (packet_send(session) == SSH_ERROR) { - return -1; - } - - if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - ssh_buffer_reinit(session->out_buffer); - return -1; - } - - if (packet_send(session) == SSH_ERROR) { - return -1; - } - SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent"); - session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; - - return 0; -} - -/** - * @internal - * - * @brief A function to be called each time a step has been done in the - * connection. - */ -static void ssh_server_connection_callback(ssh_session session){ - int ssh1,ssh2; - - switch(session->session_state){ - case SSH_SESSION_STATE_NONE: - case SSH_SESSION_STATE_CONNECTING: - case SSH_SESSION_STATE_SOCKET_CONNECTED: - break; - case SSH_SESSION_STATE_BANNER_RECEIVED: - if (session->clientbanner == NULL) { - goto error; - } - set_status(session, 0.4f); - SSH_LOG(SSH_LOG_RARE, - "SSH client banner: %s", session->clientbanner); - - /* Here we analyze the different protocols the server allows. */ - if (ssh_analyze_banner(session, 1, &ssh1, &ssh2) < 0) { - goto error; - } - /* Here we decide which version of the protocol to use. */ - if (ssh2 && session->opts.ssh2) { - session->version = 2; - } else if (ssh1 && session->opts.ssh1) { - session->version = 1; - } else if (ssh1 && !session->opts.ssh1) { -#ifdef WITH_SSH1 - ssh_set_error(session, SSH_FATAL, - "SSH-1 protocol not available (configure session to allow SSH-1)"); - goto error; -#else - ssh_set_error(session, SSH_FATAL, - "SSH-1 protocol not available (libssh compiled without SSH-1 support)"); - goto error; -#endif - } else { - ssh_set_error(session, SSH_FATAL, - "No version of SSH protocol usable (banner: %s)", - session->clientbanner); - goto error; - } - /* from now, the packet layer is handling incoming packets */ - if(session->version==2) - session->socket_callbacks.data=ssh_packet_socket_callback; -#ifdef WITH_SSH1 - else - session->socket_callbacks.data=ssh_packet_socket_callback1; -#endif - ssh_packet_set_default_callbacks(session); - set_status(session, 0.5f); - session->session_state=SSH_SESSION_STATE_INITIAL_KEX; - if (ssh_send_kex(session, 1) < 0) { - goto error; - } - break; - case SSH_SESSION_STATE_INITIAL_KEX: - /* TODO: This state should disappear in favor of get_key handle */ -#ifdef WITH_SSH1 - if(session->version==1){ - if (ssh_get_kex1(session) < 0) - goto error; - set_status(session,0.6f); - session->connected = 1; - break; - } -#endif - break; - case SSH_SESSION_STATE_KEXINIT_RECEIVED: - set_status(session,0.6f); - if(session->next_crypto->server_kex.methods[0]==NULL){ - if(server_set_kex(session) == SSH_ERROR) - goto error; - /* We are in a rekeying, so we need to send the server kex */ - if(ssh_send_kex(session, 1) < 0) - goto error; - } - ssh_list_kex(&session->next_crypto->client_kex); // log client kex - if (ssh_kex_select_methods(session) < 0) { - goto error; - } - if (crypt_set_algorithms_server(session) == SSH_ERROR) - goto error; - set_status(session,0.8f); - session->session_state=SSH_SESSION_STATE_DH; - break; - case SSH_SESSION_STATE_DH: - if(session->dh_handshake_state==DH_STATE_FINISHED){ - if (generate_session_keys(session) < 0) { - goto error; - } - - /* - * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and - * current_crypto - */ - if (session->current_crypto) { - crypto_free(session->current_crypto); - } - - /* FIXME TODO later, include a function to change keys */ - session->current_crypto = session->next_crypto; - session->next_crypto = crypto_new(); - if (session->next_crypto == NULL) { - goto error; - } - session->next_crypto->session_id = malloc(session->current_crypto->digest_len); - if (session->next_crypto->session_id == NULL) { - ssh_set_error_oom(session); - goto error; - } - memcpy(session->next_crypto->session_id, session->current_crypto->session_id, - session->current_crypto->digest_len); - - set_status(session,1.0f); - session->connected = 1; - session->session_state=SSH_SESSION_STATE_AUTHENTICATING; - if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) - session->session_state = SSH_SESSION_STATE_AUTHENTICATED; - } - break; - case SSH_SESSION_STATE_AUTHENTICATING: - break; - case SSH_SESSION_STATE_ERROR: - goto error; - default: - ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state); - } - - return; -error: - ssh_socket_close(session->socket); - session->alive = 0; - session->session_state=SSH_SESSION_STATE_ERROR; -} - -/** - * @internal - * - * @brief Gets the banner from socket and saves it in session. - * Updates the session state - * - * @param data pointer to the beginning of header - * @param len size of the banner - * @param user is a pointer to session - * @returns Number of bytes processed, or zero if the banner is not complete. - */ -static int callback_receive_banner(const void *data, size_t len, void *user) { - char *buffer = (char *) data; - ssh_session session = (ssh_session) user; - char *str = NULL; - size_t i; - int ret=0; - - for (i = 0; i < len; i++) { -#ifdef WITH_PCAP - if(session->pcap_ctx && buffer[i] == '\n') { - ssh_pcap_context_write(session->pcap_ctx, - SSH_PCAP_DIR_IN, - buffer, - i + 1, - i + 1); - } -#endif - if (buffer[i] == '\r') { - buffer[i]='\0'; - } - - if (buffer[i] == '\n') { - buffer[i]='\0'; - - str = strdup(buffer); - /* number of bytes read */ - ret = i + 1; - session->clientbanner = str; - session->session_state = SSH_SESSION_STATE_BANNER_RECEIVED; - SSH_LOG(SSH_LOG_PACKET, "Received banner: %s", str); - session->ssh_connection_callback(session); - - return ret; - } - - if(i > 127) { - /* Too big banner */ - session->session_state = SSH_SESSION_STATE_ERROR; - ssh_set_error(session, SSH_FATAL, "Receiving banner: too large banner"); - - return 0; - } - } - - return ret; -} - -/* returns 0 until the key exchange is not finished */ -static int ssh_server_kex_termination(void *s){ - ssh_session session = s; - if (session->session_state != SSH_SESSION_STATE_ERROR && - session->session_state != SSH_SESSION_STATE_AUTHENTICATING && - session->session_state != SSH_SESSION_STATE_DISCONNECTED) - return 0; - else - return 1; -} - -/** Set the acceptable authentication methods to be sent to - * client. - * @param[in] session the SSH server session - * @param[in] auth_methods Bitfield of authentication methods - * to be accepted, e.g. SSH_AUTH_METHOD_PUBLICKEY - */ -void ssh_set_auth_methods(ssh_session session, int auth_methods){ - /* accept only methods in range */ - session->auth_methods = auth_methods & 0x3f; -} - -/* Do the banner and key exchange */ -int ssh_handle_key_exchange(ssh_session session) { - int rc; - if (session->session_state != SSH_SESSION_STATE_NONE) - goto pending; - rc = ssh_send_banner(session, 1); - if (rc < 0) { - return SSH_ERROR; - } - - session->alive = 1; - - session->ssh_connection_callback = ssh_server_connection_callback; - session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED; - ssh_socket_set_callbacks(session->socket,&session->socket_callbacks); - session->socket_callbacks.data=callback_receive_banner; - session->socket_callbacks.exception=ssh_socket_exception_callback; - session->socket_callbacks.userdata=session; - - rc = server_set_kex(session); - if (rc < 0) { - return SSH_ERROR; - } - pending: - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, - ssh_server_kex_termination,session); - SSH_LOG(SSH_LOG_PACKET, "ssh_handle_key_exchange: current state : %d", - session->session_state); - if (rc != SSH_OK) - return rc; - if (session->session_state == SSH_SESSION_STATE_ERROR || - session->session_state == SSH_SESSION_STATE_DISCONNECTED) { - return SSH_ERROR; - } - - return SSH_OK; -} - -/* messages */ - -/** @internal - * replies to an SSH_AUTH packet with a default (denied) response. - */ -int ssh_auth_reply_default(ssh_session session,int partial) { - char methods_c[128] = {0}; - int rc = SSH_ERROR; - - - if (session->auth_methods == 0) { - session->auth_methods = SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD; - } - if (session->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { - strncat(methods_c, "publickey,", - sizeof(methods_c) - strlen(methods_c) - 1); - } - if (session->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC){ - strncat(methods_c,"gssapi-with-mic,", - sizeof(methods_c) - strlen(methods_c) - 1); - } - if (session->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { - strncat(methods_c, "keyboard-interactive,", - sizeof(methods_c) - strlen(methods_c) - 1); - } - if (session->auth_methods & SSH_AUTH_METHOD_PASSWORD) { - strncat(methods_c, "password,", - sizeof(methods_c) - strlen(methods_c) - 1); - } - if (session->auth_methods & SSH_AUTH_METHOD_HOSTBASED) { - strncat(methods_c, "hostbased,", - sizeof(methods_c) - strlen(methods_c) - 1); - } - - if (methods_c[0] == '\0' || methods_c[strlen(methods_c)-1] != ',') { - return SSH_ERROR; - } - - /* Strip the comma. */ - methods_c[strlen(methods_c) - 1] = '\0'; // strip the comma. We are sure there is at - - SSH_LOG(SSH_LOG_PACKET, - "Sending a auth failure. methods that can continue: %s", methods_c); - - rc = ssh_buffer_pack(session->out_buffer, - "bsb", - SSH2_MSG_USERAUTH_FAILURE, - methods_c, - partial ? 1 : 0); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - return SSH_ERROR; - } - rc = packet_send(session); - return rc; -} - -static int ssh_message_channel_request_open_reply_default(ssh_message msg) { - int rc; - - SSH_LOG(SSH_LOG_FUNCTIONS, "Refusing a channel"); - - rc = ssh_buffer_pack(msg->session->out_buffer, - "bdddd", - SSH2_MSG_CHANNEL_OPEN_FAILURE, - msg->channel_request_open.sender, - SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, - 0, /* reason is empty string */ - 0); /* language string */ - if (rc != SSH_OK){ - ssh_set_error_oom(msg->session); - return SSH_ERROR; - } - - rc = packet_send(msg->session); - return rc; -} - -static int ssh_message_channel_request_reply_default(ssh_message msg) { - uint32_t channel; - int rc; - - if (msg->channel_request.want_reply) { - channel = msg->channel_request.channel->remote_channel; - - SSH_LOG(SSH_LOG_PACKET, - "Sending a default channel_request denied to channel %d", channel); - - rc = ssh_buffer_pack(msg->session->out_buffer, - "bd", - SSH2_MSG_CHANNEL_FAILURE, - channel); - if (rc != SSH_OK){ - ssh_set_error_oom(msg->session); - return SSH_ERROR; - } - return packet_send(msg->session); - } - - SSH_LOG(SSH_LOG_PACKET, - "The client doesn't want to know the request failed!"); - - return SSH_OK; -} - -static int ssh_message_service_request_reply_default(ssh_message msg) { - /* The only return code accepted by specifications are success or disconnect */ - return ssh_message_service_reply_success(msg); -} - -int ssh_message_service_reply_success(ssh_message msg) { - ssh_session session; - int rc; - - if (msg == NULL) { - return SSH_ERROR; - } - session = msg->session; - - SSH_LOG(SSH_LOG_PACKET, - "Sending a SERVICE_ACCEPT for service %s", msg->service_request.service); - - rc = ssh_buffer_pack(session->out_buffer, - "bs", - SSH2_MSG_SERVICE_ACCEPT, - msg->service_request.service); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - return SSH_ERROR; - } - rc = packet_send(msg->session); - return rc; -} - -int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) { - int rc; - - SSH_LOG(SSH_LOG_FUNCTIONS, "Accepting a global request"); - - if (msg->global_request.want_reply) { - if (buffer_add_u8(msg->session->out_buffer - , SSH2_MSG_REQUEST_SUCCESS) < 0) { - goto error; - } - - if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD - && msg->global_request.bind_port == 0) { - rc = ssh_buffer_pack(msg->session->out_buffer, "d", bound_port); - if (rc != SSH_ERROR) { - ssh_set_error_oom(msg->session); - goto error; - } - } - - return packet_send(msg->session); - } - - if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD - && msg->global_request.bind_port == 0) { - SSH_LOG(SSH_LOG_PACKET, - "The client doesn't want to know the remote port!"); - } - - return SSH_OK; -error: - return SSH_ERROR; -} - -static int ssh_message_global_request_reply_default(ssh_message msg) { - SSH_LOG(SSH_LOG_FUNCTIONS, "Refusing a global request"); - - if (msg->global_request.want_reply) { - if (buffer_add_u8(msg->session->out_buffer - , SSH2_MSG_REQUEST_FAILURE) < 0) { - goto error; - } - return packet_send(msg->session); - } - SSH_LOG(SSH_LOG_PACKET, - "The client doesn't want to know the request failed!"); - - return SSH_OK; -error: - return SSH_ERROR; -} - -int ssh_message_reply_default(ssh_message msg) { - if (msg == NULL) { - return -1; - } - - switch(msg->type) { - case SSH_REQUEST_AUTH: - return ssh_auth_reply_default(msg->session, 0); - case SSH_REQUEST_CHANNEL_OPEN: - return ssh_message_channel_request_open_reply_default(msg); - case SSH_REQUEST_CHANNEL: - return ssh_message_channel_request_reply_default(msg); - case SSH_REQUEST_SERVICE: - return ssh_message_service_request_reply_default(msg); - case SSH_REQUEST_GLOBAL: - return ssh_message_global_request_reply_default(msg); - default: - SSH_LOG(SSH_LOG_PACKET, - "Don't know what to default reply to %d type", - msg->type); - break; - } - - return -1; -} - -const char *ssh_message_service_service(ssh_message msg){ - if (msg == NULL) { - return NULL; - } - return msg->service_request.service; -} - -const char *ssh_message_auth_user(ssh_message msg) { - if (msg == NULL) { - return NULL; - } - - return msg->auth_request.username; -} - -const char *ssh_message_auth_password(ssh_message msg){ - if (msg == NULL) { - return NULL; - } - - return msg->auth_request.password; -} - -ssh_key ssh_message_auth_pubkey(ssh_message msg) { - if (msg == NULL) { - return NULL; - } - - return msg->auth_request.pubkey; -} - -/* Get the publickey of an auth request */ -ssh_public_key ssh_message_auth_publickey(ssh_message msg){ - if (msg == NULL) { - return NULL; - } - - return ssh_pki_convert_key_to_publickey(msg->auth_request.pubkey); -} - -enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg){ - if (msg == NULL) { - return -1; - } - return msg->auth_request.signature_state; -} - -int ssh_message_auth_kbdint_is_response(ssh_message msg) { - if (msg == NULL) { - return -1; - } - - return msg->auth_request.kbdint_response != 0; -} - -int ssh_message_auth_set_methods(ssh_message msg, int methods) { - if (msg == NULL || msg->session == NULL) { - return -1; - } - - msg->session->auth_methods = methods; - - return 0; -} - -int ssh_message_auth_interactive_request(ssh_message msg, const char *name, - const char *instruction, unsigned int num_prompts, - const char **prompts, char *echo) { - int rc; - unsigned int i = 0; - - if(name == NULL || instruction == NULL) { - return SSH_ERROR; - } - if(num_prompts > 0 && (prompts == NULL || echo == NULL)) { - return SSH_ERROR; - } - - rc = ssh_buffer_pack(msg->session->out_buffer, - "bsssd", - SSH2_MSG_USERAUTH_INFO_REQUEST, - name, - instruction, - "", /* language tag */ - num_prompts); - if (rc != SSH_OK){ - ssh_set_error_oom(msg->session); - return SSH_ERROR; - } - - for(i = 0; i < num_prompts; i++) { - rc = ssh_buffer_pack(msg->session->out_buffer, - "sb", - prompts[i], - echo[1] ? 1 : 0); - if (rc != SSH_OK){ - ssh_set_error_oom(msg->session); - return SSH_ERROR; - } - } - - rc = packet_send(msg->session); - - /* fill in the kbdint structure */ - if (msg->session->kbdint == NULL) { - SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a " - "keyboard-interactive response but it " - "seems we didn't send the request."); - - msg->session->kbdint = ssh_kbdint_new(); - if (msg->session->kbdint == NULL) { - ssh_set_error_oom(msg->session); - - return SSH_ERROR; - } - } else { - ssh_kbdint_clean(msg->session->kbdint); - } - - msg->session->kbdint->name = strdup(name); - if(msg->session->kbdint->name == NULL) { - ssh_set_error_oom(msg->session); - ssh_kbdint_free(msg->session->kbdint); - msg->session->kbdint = NULL; - return SSH_PACKET_USED; - } - msg->session->kbdint->instruction = strdup(instruction); - if(msg->session->kbdint->instruction == NULL) { - ssh_set_error_oom(msg->session); - ssh_kbdint_free(msg->session->kbdint); - msg->session->kbdint = NULL; - return SSH_PACKET_USED; - } - - msg->session->kbdint->nprompts = num_prompts; - if(num_prompts > 0) { - msg->session->kbdint->prompts = malloc(num_prompts * sizeof(char *)); - if (msg->session->kbdint->prompts == NULL) { - msg->session->kbdint->nprompts = 0; - ssh_set_error_oom(msg->session); - ssh_kbdint_free(msg->session->kbdint); - msg->session->kbdint = NULL; - return SSH_ERROR; - } - msg->session->kbdint->echo = malloc(num_prompts * sizeof(unsigned char)); - if (msg->session->kbdint->echo == NULL) { - ssh_set_error_oom(msg->session); - ssh_kbdint_free(msg->session->kbdint); - msg->session->kbdint = NULL; - return SSH_ERROR; - } - for (i = 0; i < num_prompts; i++) { - msg->session->kbdint->echo[i] = echo[i]; - msg->session->kbdint->prompts[i] = strdup(prompts[i]); - if (msg->session->kbdint->prompts[i] == NULL) { - ssh_set_error_oom(msg->session); - msg->session->kbdint->nprompts = i; - ssh_kbdint_free(msg->session->kbdint); - msg->session->kbdint = NULL; - return SSH_PACKET_USED; - } - } - } else { - msg->session->kbdint->prompts = NULL; - msg->session->kbdint->echo = NULL; - } - - return rc; -} - -int ssh_auth_reply_success(ssh_session session, int partial) { - int r; - - if (session == NULL) { - return SSH_ERROR; - } - - if (partial) { - return ssh_auth_reply_default(session, partial); - } - - session->session_state = SSH_SESSION_STATE_AUTHENTICATED; - session->flags |= SSH_SESSION_FLAG_AUTHENTICATED; - - if (buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_SUCCESS) < 0) { - return SSH_ERROR; - } - - r = packet_send(session); - if(session->current_crypto && session->current_crypto->delayed_compress_out){ - SSH_LOG(SSH_LOG_PROTOCOL,"Enabling delayed compression OUT"); - session->current_crypto->do_compress_out=1; - } - if(session->current_crypto && session->current_crypto->delayed_compress_in){ - SSH_LOG(SSH_LOG_PROTOCOL,"Enabling delayed compression IN"); - session->current_crypto->do_compress_in=1; - } - return r; -} - -int ssh_message_auth_reply_success(ssh_message msg, int partial) { - if(msg == NULL) - return SSH_ERROR; - return ssh_auth_reply_success(msg->session, partial); -} - -/* Answer OK to a pubkey auth request */ -int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) { - int rc; - if (msg == NULL) { - return SSH_ERROR; - } - - rc = ssh_buffer_pack(msg->session->out_buffer, - "bSS", - SSH2_MSG_USERAUTH_PK_OK, - algo, - pubkey); - if(rc != SSH_OK){ - ssh_set_error_oom(msg->session); - return SSH_ERROR; - } - - rc = packet_send(msg->session); - return rc; -} - -int ssh_message_auth_reply_pk_ok_simple(ssh_message msg) { - ssh_string algo; - ssh_string pubkey_blob = NULL; - int ret; - - algo = ssh_string_from_char(msg->auth_request.pubkey->type_c); - if (algo == NULL) { - return SSH_ERROR; - } - - ret = ssh_pki_export_pubkey_blob(msg->auth_request.pubkey, &pubkey_blob); - if (ret < 0) { - ssh_string_free(algo); - return SSH_ERROR; - } - - ret = ssh_message_auth_reply_pk_ok(msg, algo, pubkey_blob); - - ssh_string_free(algo); - ssh_string_free(pubkey_blob); - - return ret; -} - - -const char *ssh_message_channel_request_open_originator(ssh_message msg){ - return msg->channel_request_open.originator; -} - -int ssh_message_channel_request_open_originator_port(ssh_message msg){ - return msg->channel_request_open.originator_port; -} - -const char *ssh_message_channel_request_open_destination(ssh_message msg){ - return msg->channel_request_open.destination; -} - -int ssh_message_channel_request_open_destination_port(ssh_message msg){ - return msg->channel_request_open.destination_port; -} - -ssh_channel ssh_message_channel_request_channel(ssh_message msg){ - return msg->channel_request.channel; -} - -const char *ssh_message_channel_request_pty_term(ssh_message msg){ - return msg->channel_request.TERM; -} - -int ssh_message_channel_request_pty_width(ssh_message msg){ - return msg->channel_request.width; -} - -int ssh_message_channel_request_pty_height(ssh_message msg){ - return msg->channel_request.height; -} - -int ssh_message_channel_request_pty_pxwidth(ssh_message msg){ - return msg->channel_request.pxwidth; -} - -int ssh_message_channel_request_pty_pxheight(ssh_message msg){ - return msg->channel_request.pxheight; -} - -const char *ssh_message_channel_request_env_name(ssh_message msg){ - return msg->channel_request.var_name; -} - -const char *ssh_message_channel_request_env_value(ssh_message msg){ - return msg->channel_request.var_value; -} - -const char *ssh_message_channel_request_command(ssh_message msg){ - return msg->channel_request.command; -} - -const char *ssh_message_channel_request_subsystem(ssh_message msg){ - return msg->channel_request.subsystem; -} - -int ssh_message_channel_request_x11_single_connection(ssh_message msg){ - return msg->channel_request.x11_single_connection ? 1 : 0; -} - -const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg){ - return msg->channel_request.x11_auth_protocol; -} - -const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg){ - return msg->channel_request.x11_auth_cookie; -} - -int ssh_message_channel_request_x11_screen_number(ssh_message msg){ - return msg->channel_request.x11_screen_number; -} - -const char *ssh_message_global_request_address(ssh_message msg){ - return msg->global_request.bind_address; -} - -int ssh_message_global_request_port(ssh_message msg){ - return msg->global_request.bind_port; -} - -/** @brief defines the ssh_message callback - * @param session the current ssh session - * @param[in] ssh_bind_message_callback a function pointer to a callback taking the - * current ssh session and received message as parameters. the function returns - * 0 if the message has been parsed and treated successfully, 1 otherwise (libssh - * must take care of the response). - * @param[in] data void pointer to be passed to callback functions - */ -void ssh_set_message_callback(ssh_session session, - int(*ssh_bind_message_callback)(ssh_session session, ssh_message msg, void *data), - void *data) { - session->ssh_message_callback = ssh_bind_message_callback; - session->ssh_message_callback_data = data; -} - -int ssh_execute_message_callbacks(ssh_session session){ - ssh_message msg=NULL; - int ret; - ssh_handle_packets(session, SSH_TIMEOUT_NONBLOCKING); - if(!session->ssh_message_list) - return SSH_OK; - if(session->ssh_message_callback){ - while((msg=ssh_message_pop_head(session)) != NULL) { - ret=session->ssh_message_callback(session,msg, - session->ssh_message_callback_data); - if(ret==1){ - ret = ssh_message_reply_default(msg); - ssh_message_free(msg); - if(ret != SSH_OK) - return ret; - } else { - ssh_message_free(msg); - } - } - } else { - while((msg=ssh_message_pop_head(session)) != NULL) { - ret = ssh_message_reply_default(msg); - ssh_message_free(msg); - if(ret != SSH_OK) - return ret; - } - } - return SSH_OK; -} - -int ssh_send_keepalive(ssh_session session) -{ - int rc; - - rc = ssh_buffer_pack(session->out_buffer, - "bsb", - SSH2_MSG_GLOBAL_REQUEST, - "keepalive@openssh.com", - 1); - if (rc != SSH_OK) { - goto err; - } - - if (packet_send(session) == SSH_ERROR) { - goto err; - } - - ssh_handle_packets(session, SSH_TIMEOUT_NONBLOCKING); - - SSH_LOG(SSH_LOG_PACKET, "Sent a keepalive"); - return SSH_OK; - -err: - ssh_set_error_oom(session); - ssh_buffer_reinit(session->out_buffer); - return SSH_ERROR; -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/session.c b/libssh/src/session.c deleted file mode 100644 index a3b19ede..00000000 --- a/libssh/src/session.c +++ /dev/null @@ -1,888 +0,0 @@ -/* - * session.c - non-networking functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2005-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include - -#include "libssh/priv.h" -#include "libssh/libssh.h" -#include "libssh/crypto.h" -#include "libssh/server.h" -#include "libssh/socket.h" -#include "libssh/ssh2.h" -#include "libssh/agent.h" -#include "libssh/packet.h" -#include "libssh/session.h" -#include "libssh/misc.h" -#include "libssh/buffer.h" -#include "libssh/poll.h" - -#define FIRST_CHANNEL 42 // why not ? it helps to find bugs. - -/** - * @defgroup libssh_session The SSH session functions. - * @ingroup libssh - * - * Functions that manage a session. - * - * @{ - */ - -/** - * @brief Create a new ssh session. - * - * @returns A new ssh_session pointer, NULL on error. - */ -ssh_session ssh_new(void) { - ssh_session session; - char *id = NULL; - int rc; - - session = malloc(sizeof (struct ssh_session_struct)); - if (session == NULL) { - return NULL; - } - ZERO_STRUCTP(session); - - session->next_crypto = crypto_new(); - if (session->next_crypto == NULL) { - goto err; - } - - session->socket = ssh_socket_new(session); - if (session->socket == NULL) { - goto err; - } - - session->out_buffer = ssh_buffer_new(); - if (session->out_buffer == NULL) { - goto err; - } - - session->in_buffer=ssh_buffer_new(); - if (session->in_buffer == NULL) { - goto err; - } - - session->alive = 0; - session->auth_methods = 0; - ssh_set_blocking(session, 1); - session->maxchannel = FIRST_CHANNEL; - -#ifndef _WIN32 - session->agent = agent_new(session); - if (session->agent == NULL) { - goto err; - } -#endif /* _WIN32 */ - - /* OPTIONS */ - session->opts.StrictHostKeyChecking = 1; - session->opts.port = 22; - session->opts.fd = -1; - session->opts.ssh2 = 1; - session->opts.compressionlevel=7; -#ifdef WITH_SSH1 - session->opts.ssh1 = 1; -#else - session->opts.ssh1 = 0; -#endif - - session->opts.identity = ssh_list_new(); - if (session->opts.identity == NULL) { - goto err; - } - -#ifdef HAVE_ECC - id = strdup("%d/id_ecdsa"); - if (id == NULL) { - goto err; - } - rc = ssh_list_append(session->opts.identity, id); - if (rc == SSH_ERROR) { - goto err; - } -#endif - - id = strdup("%d/id_rsa"); - if (id == NULL) { - goto err; - } - rc = ssh_list_append(session->opts.identity, id); - if (rc == SSH_ERROR) { - goto err; - } - - id = strdup("%d/id_dsa"); - if (id == NULL) { - goto err; - } - rc = ssh_list_append(session->opts.identity, id); - if (rc == SSH_ERROR) { - goto err; - } - - id = strdup("%d/identity"); - if (id == NULL) { - goto err; - } - rc = ssh_list_append(session->opts.identity, id); - if (rc == SSH_ERROR) { - goto err; - } - - return session; - -err: - free(id); - ssh_free(session); - return NULL; -} - -/** - * @brief Deallocate a SSH session handle. - * - * @param[in] session The SSH session to free. - * - * @see ssh_disconnect() - * @see ssh_new() - */ -void ssh_free(ssh_session session) { - int i; - struct ssh_iterator *it; - - if (session == NULL) { - return; - } - - /* - * Delete all channels - * - * This needs the first thing we clean up cause if there is still an open - * channel we call ssh_channel_close() first. So we need a working socket - * and poll context for it. - */ - for (it = ssh_list_get_iterator(session->channels); - it != NULL; - it = ssh_list_get_iterator(session->channels)) { - ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); - ssh_list_remove(session->channels, it); - } - ssh_list_free(session->channels); - session->channels = NULL; - -#ifdef WITH_PCAP - if (session->pcap_ctx) { - ssh_pcap_context_free(session->pcap_ctx); - session->pcap_ctx = NULL; - } -#endif - - ssh_socket_free(session->socket); - session->socket = NULL; - - if (session->default_poll_ctx) { - ssh_poll_ctx_free(session->default_poll_ctx); - } - - ssh_buffer_free(session->in_buffer); - ssh_buffer_free(session->out_buffer); - session->in_buffer = session->out_buffer = NULL; - - if (session->in_hashbuf != NULL) { - ssh_buffer_free(session->in_hashbuf); - } - if (session->out_hashbuf != NULL) { - ssh_buffer_free(session->out_hashbuf); - } - - crypto_free(session->current_crypto); - crypto_free(session->next_crypto); - -#ifndef _WIN32 - agent_free(session->agent); -#endif /* _WIN32 */ - - ssh_key_free(session->srv.dsa_key); - session->srv.dsa_key = NULL; - ssh_key_free(session->srv.rsa_key); - session->srv.rsa_key = NULL; - ssh_key_free(session->srv.ecdsa_key); - session->srv.ecdsa_key = NULL; - - if (session->ssh_message_list) { - ssh_message msg; - - for (msg = ssh_list_pop_head(ssh_message, session->ssh_message_list); - msg != NULL; - msg = ssh_list_pop_head(ssh_message, session->ssh_message_list)) { - ssh_message_free(msg); - } - ssh_list_free(session->ssh_message_list); - } - - if (session->packet_callbacks) { - ssh_list_free(session->packet_callbacks); - } - - /* options */ - if (session->opts.identity) { - char *id; - - for (id = ssh_list_pop_head(char *, session->opts.identity); - id != NULL; - id = ssh_list_pop_head(char *, session->opts.identity)) { - SAFE_FREE(id); - } - ssh_list_free(session->opts.identity); - } - - SAFE_FREE(session->auth_auto_state); - SAFE_FREE(session->serverbanner); - SAFE_FREE(session->clientbanner); - SAFE_FREE(session->banner); - - SAFE_FREE(session->opts.bindaddr); - SAFE_FREE(session->opts.custombanner); - SAFE_FREE(session->opts.username); - SAFE_FREE(session->opts.host); - SAFE_FREE(session->opts.sshdir); - SAFE_FREE(session->opts.knownhosts); - SAFE_FREE(session->opts.ProxyCommand); - SAFE_FREE(session->opts.gss_server_identity); - SAFE_FREE(session->opts.gss_client_identity); - - for (i = 0; i < 10; i++) { - if (session->opts.wanted_methods[i]) { - SAFE_FREE(session->opts.wanted_methods[i]); - } - } - - /* burn connection, it could contain sensitive data */ - BURN_BUFFER(session, sizeof(struct ssh_session_struct)); - SAFE_FREE(session); -} - -/** - * @brief get the client banner - * - * @param[in] session The SSH session - * - * @return Returns the client banner string or NULL. - */ -const char* ssh_get_clientbanner(ssh_session session) { - if (session == NULL) { - return NULL; - } - - return session->clientbanner; -} - -/** - * @brief get the server banner - * - * @param[in] session The SSH session - * - * @return Returns the server banner string or NULL. - */ -const char* ssh_get_serverbanner(ssh_session session) { - if(!session) { - return NULL; - } - return session->serverbanner; -} - -/** - * @brief get the name of the input cipher for the given session. - * - * @param[in] session The SSH session. - * - * @return Returns cipher name or NULL. - */ -const char* ssh_get_cipher_in(ssh_session session) { - if ((session != NULL) && - (session->current_crypto != NULL) && - (session->current_crypto->in_cipher != NULL)) { - return session->current_crypto->in_cipher->name; - } - return NULL; -} - -/** - * @brief get the name of the output cipher for the given session. - * - * @param[in] session The SSH session. - * - * @return Returns cipher name or NULL. - */ -const char* ssh_get_cipher_out(ssh_session session) { - if ((session != NULL) && - (session->current_crypto != NULL) && - (session->current_crypto->out_cipher != NULL)) { - return session->current_crypto->out_cipher->name; - } - return NULL; -} - -/** - * @brief get the name of the input HMAC algorithm for the given session. - * - * @param[in] session The SSH session. - * - * @return Returns HMAC algorithm name or NULL if unknown. - */ -const char* ssh_get_hmac_in(ssh_session session) { - if ((session != NULL) && - (session->current_crypto != NULL)) { - return ssh_hmac_type_to_string(session->current_crypto->in_hmac); - } - return NULL; -} - -/** - * @brief get the name of the output HMAC algorithm for the given session. - * - * @param[in] session The SSH session. - * - * @return Returns HMAC algorithm name or NULL if unknown. - */ -const char* ssh_get_hmac_out(ssh_session session) { - if ((session != NULL) && - (session->current_crypto != NULL)) { - return ssh_hmac_type_to_string(session->current_crypto->out_hmac); - } - return NULL; -} - -/** - * @brief Disconnect impolitely from a remote host by closing the socket. - * - * Suitable if you forked and want to destroy this session. - * - * @param[in] session The SSH session to disconnect. - */ -void ssh_silent_disconnect(ssh_session session) { - if (session == NULL) { - return; - } - - ssh_socket_close(session->socket); - session->alive = 0; - ssh_disconnect(session); -} - -/** - * @brief Set the session in blocking/nonblocking mode. - * - * @param[in] session The ssh session to change. - * - * @param[in] blocking Zero for nonblocking mode. - */ -void ssh_set_blocking(ssh_session session, int blocking) { - if (session == NULL) { - return; - } - session->flags &= ~SSH_SESSION_FLAG_BLOCKING; - session->flags |= blocking ? SSH_SESSION_FLAG_BLOCKING : 0; -} - -/** - * @brief Return the blocking mode of libssh - * @param[in] session The SSH session - * @returns 0 if the session is nonblocking, - * @returns 1 if the functions may block. - */ -int ssh_is_blocking(ssh_session session){ - return (session->flags&SSH_SESSION_FLAG_BLOCKING) ? 1 : 0; -} - -/* Waits until the output socket is empty */ -static int ssh_flush_termination(void *c){ - ssh_session session = c; - if (ssh_socket_buffered_write_bytes(session->socket) == 0 || - session->session_state == SSH_SESSION_STATE_ERROR) - return 1; - else - return 0; -} - -/** - * @brief Blocking flush of the outgoing buffer - * @param[in] session The SSH session - * @param[in] timeout Set an upper limit on the time for which this function - * will block, in milliseconds. Specifying -1 - * means an infinite timeout. This parameter is passed to - * the poll() function. - * @returns SSH_OK on success, SSH_AGAIN if timeout occurred, - * SSH_ERROR otherwise. - */ - -int ssh_blocking_flush(ssh_session session, int timeout){ - int rc; - if (session == NULL) { - return SSH_ERROR; - } - - rc = ssh_handle_packets_termination(session, timeout, - ssh_flush_termination, session); - if (rc == SSH_ERROR) { - return rc; - } - if (!ssh_flush_termination(session)) { - rc = SSH_AGAIN; - } - - return rc; -} - -/** - * @brief Check if we are connected. - * - * @param[in] session The session to check if it is connected. - * - * @return 1 if we are connected, 0 if not. - */ -int ssh_is_connected(ssh_session session) { - if (session == NULL) { - return 0; - } - - return session->alive; -} - -/** - * @brief Get the fd of a connection. - * - * In case you'd need the file descriptor of the connection to the server/client. - * - * @param[in] session The ssh session to use. - * - * @return The file descriptor of the connection, or -1 if it is - * not connected - */ -socket_t ssh_get_fd(ssh_session session) { - if (session == NULL) { - return -1; - } - - return ssh_socket_get_fd_in(session->socket); -} - -/** - * @brief Tell the session it has data to read on the file descriptor without - * blocking. - * - * @param[in] session The ssh session to use. - */ -void ssh_set_fd_toread(ssh_session session) { - if (session == NULL) { - return; - } - - ssh_socket_set_read_wontblock(session->socket); -} - -/** - * @brief Tell the session it may write to the file descriptor without blocking. - * - * @param[in] session The ssh session to use. - */ -void ssh_set_fd_towrite(ssh_session session) { - if (session == NULL) { - return; - } - - ssh_socket_set_write_wontblock(session->socket); -} - -/** - * @brief Tell the session it has an exception to catch on the file descriptor. - * - * \param[in] session The ssh session to use. - */ -void ssh_set_fd_except(ssh_session session) { - if (session == NULL) { - return; - } - - ssh_socket_set_except(session->socket); -} - -/** - * @internal - * - * @brief Poll the current session for an event and call the appropriate - * callbacks. This function will not loop until the timeout is expired. - * - * This will block until one event happens. - * - * @param[in] session The session handle to use. - * - * @param[in] timeout Set an upper limit on the time for which this function - * will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE - * (-1) means an infinite timeout. - * Specifying SSH_TIMEOUT_USER means to use the timeout - * specified in options. 0 means poll will return immediately. - * This parameter is passed to the poll() function. - * - * @return SSH_OK on success, SSH_ERROR otherwise. - */ -int ssh_handle_packets(ssh_session session, int timeout) { - ssh_poll_handle spoll_in,spoll_out; - ssh_poll_ctx ctx; - int tm = timeout; - int rc; - - if (session == NULL || session->socket == NULL) { - return SSH_ERROR; - } - - spoll_in = ssh_socket_get_poll_handle_in(session->socket); - spoll_out = ssh_socket_get_poll_handle_out(session->socket); - ssh_poll_add_events(spoll_in, POLLIN); - ctx = ssh_poll_get_ctx(spoll_in); - - if (!ctx) { - ctx = ssh_poll_get_default_ctx(session); - ssh_poll_ctx_add(ctx, spoll_in); - if (spoll_in != spoll_out) { - ssh_poll_ctx_add(ctx, spoll_out); - } - } - - if (timeout == SSH_TIMEOUT_USER) { - if (ssh_is_blocking(session)) - tm = ssh_make_milliseconds(session->opts.timeout, - session->opts.timeout_usec); - else - tm = 0; - } - rc = ssh_poll_ctx_dopoll(ctx, tm); - if (rc == SSH_ERROR) { - session->session_state = SSH_SESSION_STATE_ERROR; - } - - return rc; -} - -/** - * @internal - * - * @brief Poll the current session for an event and call the appropriate - * callbacks. - * - * This will block until termination function returns true, or timeout expired. - * - * @param[in] session The session handle to use. - * - * @param[in] timeout Set an upper limit on the time for which this function - * will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE - * (-1) means an infinite timeout. - * Specifying SSH_TIMEOUT_USER means to use the timeout - * specified in options. 0 means poll will return immediately. - * SSH_TIMEOUT_DEFAULT uses blocking parameters of the session. - * This parameter is passed to the poll() function. - * - * @param[in] fct Termination function to be used to determine if it is - * possible to stop polling. - * @param[in] user User parameter to be passed to fct termination function. - * @return SSH_OK on success, SSH_ERROR otherwise. - */ -int ssh_handle_packets_termination(ssh_session session, - int timeout, - ssh_termination_function fct, - void *user) -{ - struct ssh_timestamp ts; - int ret = SSH_OK; - int tm; - - if (timeout == SSH_TIMEOUT_USER) { - if (ssh_is_blocking(session)) { - timeout = ssh_make_milliseconds(session->opts.timeout, - session->opts.timeout_usec); - } else { - timeout = SSH_TIMEOUT_NONBLOCKING; - } - } else if (timeout == SSH_TIMEOUT_DEFAULT) { - if (ssh_is_blocking(session)) { - timeout = SSH_TIMEOUT_INFINITE; - } else { - timeout = SSH_TIMEOUT_NONBLOCKING; - } - } - - /* avoid unnecessary syscall for the SSH_TIMEOUT_NONBLOCKING case */ - if (timeout != SSH_TIMEOUT_NONBLOCKING) { - ssh_timestamp_init(&ts); - } - - tm = timeout; - while(!fct(user)) { - ret = ssh_handle_packets(session, tm); - if (ret == SSH_ERROR) { - break; - } - if (ssh_timeout_elapsed(&ts,timeout)) { - ret = fct(user) ? SSH_OK : SSH_AGAIN; - break; - } - - tm = ssh_timeout_update(&ts, timeout); - } - - return ret; -} - -/** - * @brief Get session status - * - * @param session The ssh session to use. - * - * @returns A bitmask including SSH_CLOSED, SSH_READ_PENDING, SSH_WRITE_PENDING - * or SSH_CLOSED_ERROR which respectively means the session is closed, - * has data to read on the connection socket and session was closed - * due to an error. - */ -int ssh_get_status(ssh_session session) { - int socketstate; - int r = 0; - - if (session == NULL) { - return 0; - } - - socketstate = ssh_socket_get_status(session->socket); - - if (session->session_state == SSH_SESSION_STATE_DISCONNECTED) { - r |= SSH_CLOSED; - } - if (socketstate & SSH_READ_PENDING) { - r |= SSH_READ_PENDING; - } - if (socketstate & SSH_WRITE_PENDING) { - r |= SSH_WRITE_PENDING; - } - if ((session->session_state == SSH_SESSION_STATE_DISCONNECTED && - (socketstate & SSH_CLOSED_ERROR)) || - session->session_state == SSH_SESSION_STATE_ERROR) { - r |= SSH_CLOSED_ERROR; - } - - return r; -} - -/** - * @brief Get poll flags for an external mainloop - * - * @param session The ssh session to use. - * - * @returns A bitmask including SSH_READ_PENDING or SSH_WRITE_PENDING. - * For SSH_READ_PENDING, your invocation of poll() should include - * POLLIN. For SSH_WRITE_PENDING, your invocation of poll() should - * include POLLOUT. - */ -int ssh_get_poll_flags(ssh_session session) -{ - if (session == NULL) { - return 0; - } - - return ssh_socket_get_poll_flags (session->socket); -} - -/** - * @brief Get the disconnect message from the server. - * - * @param[in] session The ssh session to use. - * - * @return The message sent by the server along with the - * disconnect, or NULL in which case the reason of the - * disconnect may be found with ssh_get_error. - * - * @see ssh_get_error() - */ -const char *ssh_get_disconnect_message(ssh_session session) { - if (session == NULL) { - return NULL; - } - - if (session->session_state != SSH_SESSION_STATE_DISCONNECTED) { - ssh_set_error(session, SSH_REQUEST_DENIED, - "Connection not closed yet"); - } else if(!session->discon_msg) { - ssh_set_error(session, SSH_FATAL, - "Connection correctly closed but no disconnect message"); - } else { - return session->discon_msg; - } - - return NULL; -} - -/** - * @brief Get the protocol version of the session. - * - * @param session The ssh session to use. - * - * @return 1 or 2, for ssh1 or ssh2, < 0 on error. - */ -int ssh_get_version(ssh_session session) { - if (session == NULL) { - return -1; - } - - return session->version; -} - -/** - * @internal - * @brief Callback to be called when the socket received an exception code. - * @param user is a pointer to session - */ -void ssh_socket_exception_callback(int code, int errno_code, void *user){ - ssh_session session=(ssh_session)user; - - SSH_LOG(SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code); - session->session_state = SSH_SESSION_STATE_ERROR; - if (errno_code == 0 && code == SSH_SOCKET_EXCEPTION_EOF) { - ssh_set_error(session, SSH_FATAL, "Socket error: disconnected"); - } else { - ssh_set_error(session, SSH_FATAL, "Socket error: %s", strerror(errno_code)); - } - - session->ssh_connection_callback(session); -} - -/** - * @brief Send a message that should be ignored - * - * @param[in] session The SSH session - * @param[in] data Data to be sent - * - * @return SSH_OK on success, SSH_ERROR otherwise. - */ -int ssh_send_ignore (ssh_session session, const char *data) { - int rc; - - if (ssh_socket_is_open(session->socket)) { - - rc = ssh_buffer_pack(session->out_buffer, - "bs", - SSH2_MSG_IGNORE, - data); - if (rc != SSH_OK){ - ssh_set_error_oom(session); - goto error; - } - packet_send(session); - ssh_handle_packets(session, 0); - } - - return SSH_OK; - -error: - ssh_buffer_reinit(session->out_buffer); - return SSH_ERROR; -} - -/** - * @brief Send a debug message - * - * @param[in] session The SSH session - * @param[in] message Data to be sent - * @param[in] always_display Message SHOULD be displayed by the server. It - * SHOULD NOT be displayed unless debugging - * information has been explicitly requested. - * - * @return SSH_OK on success, SSH_ERROR otherwise. - */ -int ssh_send_debug (ssh_session session, const char *message, int always_display) { - int rc; - - if (ssh_socket_is_open(session->socket)) { - rc = ssh_buffer_pack(session->out_buffer, - "bbsd", - SSH2_MSG_DEBUG, - always_display != 0 ? 1 : 0, - message, - 0); /* empty language tag */ - if (rc != SSH_OK) { - ssh_set_error_oom(session); - goto error; - } - packet_send(session); - ssh_handle_packets(session, 0); - } - - return SSH_OK; - -error: - ssh_buffer_reinit(session->out_buffer); - return SSH_ERROR; -} - - /** - * @brief Set the session data counters. - * - * This functions sets the counter structures to be used to calculate data - * which comes in and goes out through the session at different levels. - * - * @code - * struct ssh_counter_struct scounter = { - * .in_bytes = 0, - * .out_bytes = 0, - * .in_packets = 0, - * .out_packets = 0 - * }; - * - * struct ssh_counter_struct rcounter = { - * .in_bytes = 0, - * .out_bytes = 0, - * .in_packets = 0, - * .out_packets = 0 - * }; - * - * ssh_set_counters(session, &scounter, &rcounter); - * @endcode - * - * @param[in] session The SSH session. - * - * @param[in] scounter Counter for byte data handled by the session sockets. - * - * @param[in] rcounter Counter for byte and packet data handled by the session, - * prior compression and SSH overhead. - */ -void ssh_set_counters(ssh_session session, ssh_counter scounter, - ssh_counter rcounter) { - if (session != NULL) { - session->socket_counter = scounter; - session->raw_counter = rcounter; - } -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/sftp.c b/libssh/src/sftp.c deleted file mode 100644 index b57da645..00000000 --- a/libssh/src/sftp.c +++ /dev/null @@ -1,3081 +0,0 @@ -/* - * sftp.c - Secure FTP functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2005-2008 by Aris Adamantiadis - * Copyright (c) 2008-2009 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/* This file contains code written by Nick Zitzmann */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#else -#define S_IFSOCK 0140000 -#define S_IFLNK 0120000 - -#ifdef _MSC_VER -#define S_IFBLK 0060000 -#define S_IFIFO 0010000 -#endif -#endif - -#include "libssh/priv.h" -#include "libssh/ssh2.h" -#include "libssh/sftp.h" -#include "libssh/buffer.h" -#include "libssh/channels.h" -#include "libssh/session.h" -#include "libssh/misc.h" - -#ifdef WITH_SFTP - -struct sftp_ext_struct { - unsigned int count; - char **name; - char **data; -}; - -/* functions */ -static int sftp_enqueue(sftp_session session, sftp_message msg); -static void sftp_message_free(sftp_message msg); -static void sftp_set_error(sftp_session sftp, int errnum); -static void status_msg_free(sftp_status_message status); - -static sftp_ext sftp_ext_new(void) { - sftp_ext ext; - - ext = malloc(sizeof(struct sftp_ext_struct)); - if (ext == NULL) { - return NULL; - } - ZERO_STRUCTP(ext); - - return ext; -} - -static void sftp_ext_free(sftp_ext ext) { - unsigned int i; - - if (ext == NULL) { - return; - } - - if (ext->count) { - for (i = 0; i < ext->count; i++) { - SAFE_FREE(ext->name[i]); - SAFE_FREE(ext->data[i]); - } - SAFE_FREE(ext->name); - SAFE_FREE(ext->data); - } - - SAFE_FREE(ext); -} - -sftp_session sftp_new(ssh_session session){ - sftp_session sftp; - - if (session == NULL) { - return NULL; - } - - sftp = malloc(sizeof(struct sftp_session_struct)); - if (sftp == NULL) { - ssh_set_error_oom(session); - - return NULL; - } - ZERO_STRUCTP(sftp); - - sftp->ext = sftp_ext_new(); - if (sftp->ext == NULL) { - ssh_set_error_oom(session); - SAFE_FREE(sftp); - - return NULL; - } - - sftp->session = session; - sftp->channel = ssh_channel_new(session); - if (sftp->channel == NULL) { - SAFE_FREE(sftp); - - return NULL; - } - - if (ssh_channel_open_session(sftp->channel)) { - ssh_channel_free(sftp->channel); - SAFE_FREE(sftp); - - return NULL; - } - - if (ssh_channel_request_sftp(sftp->channel)) { - sftp_free(sftp); - - return NULL; - } - - return sftp; -} - -sftp_session sftp_new_channel(ssh_session session, ssh_channel channel){ - sftp_session sftp; - - if (session == NULL) { - return NULL; - } - - sftp = malloc(sizeof(struct sftp_session_struct)); - if (sftp == NULL) { - ssh_set_error_oom(session); - - return NULL; - } - ZERO_STRUCTP(sftp); - - sftp->ext = sftp_ext_new(); - if (sftp->ext == NULL) { - ssh_set_error_oom(session); - SAFE_FREE(sftp); - - return NULL; - } - - sftp->session = session; - sftp->channel = channel; - - return sftp; -} - -#ifdef WITH_SERVER -sftp_session sftp_server_new(ssh_session session, ssh_channel chan){ - sftp_session sftp = NULL; - - sftp = malloc(sizeof(struct sftp_session_struct)); - if (sftp == NULL) { - ssh_set_error_oom(session); - return NULL; - } - ZERO_STRUCTP(sftp); - - sftp->session = session; - sftp->channel = chan; - - return sftp; -} - -int sftp_server_init(sftp_session sftp){ - ssh_session session = sftp->session; - sftp_packet packet = NULL; - ssh_buffer reply = NULL; - uint32_t version; - - packet = sftp_packet_read(sftp); - if (packet == NULL) { - return -1; - } - - if (packet->type != SSH_FXP_INIT) { - ssh_set_error(session, SSH_FATAL, - "Packet read of type %d instead of SSH_FXP_INIT", - packet->type); - - sftp_packet_free(packet); - return -1; - } - - SSH_LOG(SSH_LOG_PACKET, "Received SSH_FXP_INIT"); - - buffer_get_u32(packet->payload, &version); - version = ntohl(version); - SSH_LOG(SSH_LOG_PACKET, "Client version: %d", version); - sftp->client_version = version; - - sftp_packet_free(packet); - - reply = ssh_buffer_new(); - if (reply == NULL) { - ssh_set_error_oom(session); - return -1; - } - - if (buffer_add_u32(reply, ntohl(LIBSFTP_VERSION)) < 0) { - ssh_set_error_oom(session); - ssh_buffer_free(reply); - return -1; - } - - if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) { - ssh_buffer_free(reply); - return -1; - } - ssh_buffer_free(reply); - - SSH_LOG(SSH_LOG_RARE, "Server version sent"); - - if (version > LIBSFTP_VERSION) { - sftp->version = LIBSFTP_VERSION; - } else { - sftp->version=version; - } - - return 0; -} -#endif /* WITH_SERVER */ - -void sftp_free(sftp_session sftp){ - sftp_request_queue ptr; - - if (sftp == NULL) { - return; - } - - ssh_channel_send_eof(sftp->channel); - ptr = sftp->queue; - while(ptr) { - sftp_request_queue old; - sftp_message_free(ptr->message); - old = ptr->next; - SAFE_FREE(ptr); - ptr = old; - } - - ssh_channel_free(sftp->channel); - - SAFE_FREE(sftp->handles); - - sftp_ext_free(sftp->ext); - ZERO_STRUCTP(sftp); - - SAFE_FREE(sftp); -} - -int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload){ - int size; - - if (buffer_prepend_data(payload, &type, sizeof(uint8_t)) < 0) { - ssh_set_error_oom(sftp->session); - return -1; - } - - size = htonl(buffer_get_rest_len(payload)); - if (buffer_prepend_data(payload, &size, sizeof(uint32_t)) < 0) { - ssh_set_error_oom(sftp->session); - return -1; - } - - size = ssh_channel_write(sftp->channel, buffer_get_rest(payload), - buffer_get_rest_len(payload)); - if (size < 0) { - return -1; - } else if((uint32_t) size != buffer_get_rest_len(payload)) { - SSH_LOG(SSH_LOG_PACKET, - "Had to write %d bytes, wrote only %d", - buffer_get_rest_len(payload), - size); - } - - return size; -} - -sftp_packet sftp_packet_read(sftp_session sftp) { - unsigned char buffer[MAX_BUF_SIZE]; - sftp_packet packet = NULL; - uint32_t size; - int r; - - packet = malloc(sizeof(struct sftp_packet_struct)); - if (packet == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - packet->sftp = sftp; - packet->payload = ssh_buffer_new(); - if (packet->payload == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(packet); - return NULL; - } - - r=ssh_channel_read(sftp->channel, buffer, 4, 0); - if (r < 0) { - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - return NULL; - } - ssh_buffer_add_data(packet->payload, buffer, r); - if (buffer_get_u32(packet->payload, &size) != sizeof(uint32_t)) { - ssh_set_error(sftp->session, SSH_FATAL, "Short sftp packet!"); - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - return NULL; - } - - size = ntohl(size); - r=ssh_channel_read(sftp->channel, buffer, 1, 0); - if (r <= 0) { - /* TODO: check if there are cases where an error needs to be set here */ - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - return NULL; - } - ssh_buffer_add_data(packet->payload, buffer, r); - buffer_get_u8(packet->payload, &packet->type); - size=size-1; - while (size>0){ - r=ssh_channel_read(sftp->channel,buffer, - sizeof(buffer)>size ? size:sizeof(buffer),0); - - if(r <= 0) { - /* TODO: check if there are cases where an error needs to be set here */ - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - return NULL; - } - if (ssh_buffer_add_data(packet->payload, buffer, r) == SSH_ERROR) { - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - ssh_set_error_oom(sftp->session); - return NULL; - } - size -= r; - } - - return packet; -} - -static void sftp_set_error(sftp_session sftp, int errnum) { - if (sftp != NULL) { - sftp->errnum = errnum; - } -} - -/* Get the last sftp error */ -int sftp_get_error(sftp_session sftp) { - if (sftp == NULL) { - return -1; - } - - return sftp->errnum; -} - -static sftp_message sftp_message_new(sftp_session sftp){ - sftp_message msg = NULL; - - msg = malloc(sizeof(struct sftp_message_struct)); - if (msg == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(msg); - - msg->payload = ssh_buffer_new(); - if (msg->payload == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(msg); - return NULL; - } - msg->sftp = sftp; - - return msg; -} - -static void sftp_message_free(sftp_message msg) { - if (msg == NULL) { - return; - } - - ssh_buffer_free(msg->payload); - SAFE_FREE(msg); -} - -static sftp_message sftp_get_message(sftp_packet packet) { - sftp_session sftp = packet->sftp; - sftp_message msg = NULL; - int rc; - - msg = sftp_message_new(sftp); - if (msg == NULL) { - return NULL; - } - - msg->sftp = packet->sftp; - msg->packet_type = packet->type; - - if ((packet->type != SSH_FXP_STATUS) && (packet->type!=SSH_FXP_HANDLE) && - (packet->type != SSH_FXP_DATA) && (packet->type != SSH_FXP_ATTRS) && - (packet->type != SSH_FXP_NAME) && (packet->type != SSH_FXP_EXTENDED_REPLY)) { - ssh_set_error(packet->sftp->session, SSH_FATAL, - "Unknown packet type %d", packet->type); - sftp_message_free(msg); - return NULL; - } - - rc = ssh_buffer_unpack(packet->payload, "d", &msg->id); - if (rc != SSH_OK) { - ssh_set_error(packet->sftp->session, SSH_FATAL, - "Invalid packet %d: no ID", packet->type); - sftp_message_free(msg); - return NULL; - } - - SSH_LOG(SSH_LOG_PACKET, - "Packet with id %d type %d", - msg->id, - msg->packet_type); - - if (ssh_buffer_add_data(msg->payload, buffer_get_rest(packet->payload), - buffer_get_rest_len(packet->payload)) < 0) { - ssh_set_error_oom(sftp->session); - sftp_message_free(msg); - return NULL; - } - - return msg; -} - -static int sftp_read_and_dispatch(sftp_session sftp) { - sftp_packet packet = NULL; - sftp_message msg = NULL; - - packet = sftp_packet_read(sftp); - if (packet == NULL) { - return -1; /* something nasty happened reading the packet */ - } - - msg = sftp_get_message(packet); - sftp_packet_free(packet); - if (msg == NULL) { - return -1; - } - - if (sftp_enqueue(sftp, msg) < 0) { - sftp_message_free(msg); - return -1; - } - - return 0; -} - -void sftp_packet_free(sftp_packet packet) { - if (packet == NULL) { - return; - } - - ssh_buffer_free(packet->payload); - free(packet); -} - -/* Initialize the sftp session with the server. */ -int sftp_init(sftp_session sftp) { - sftp_packet packet = NULL; - ssh_buffer buffer = NULL; - char *ext_name = NULL; - char *ext_data = NULL; - uint32_t version; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - rc = ssh_buffer_pack(buffer, "d", LIBSFTP_VERSION); - if (rc != SSH_OK) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_INIT, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - packet = sftp_packet_read(sftp); - if (packet == NULL) { - return -1; - } - - if (packet->type != SSH_FXP_VERSION) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received a %d messages instead of SSH_FXP_VERSION", packet->type); - sftp_packet_free(packet); - return -1; - } - - /* TODO: are we sure there are 4 bytes ready? */ - rc = ssh_buffer_unpack(packet->payload, "d", &version); - if (rc != SSH_OK){ - return -1; - } - SSH_LOG(SSH_LOG_RARE, - "SFTP server version %d", - version); - rc = ssh_buffer_unpack(packet->payload, "s", &ext_name); - while (rc == SSH_OK) { - int count = sftp->ext->count; - - rc = ssh_buffer_unpack(packet->payload, "s", &ext_data); - if (rc == SSH_ERROR) { - break; - } - - SSH_LOG(SSH_LOG_RARE, - "SFTP server extension: %s, version: %s", - ext_name, ext_data); - - count++; - sftp->ext->name = realloc(sftp->ext->name, count * sizeof(char *)); - if (sftp->ext->name == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(ext_name); - SAFE_FREE(ext_data); - return -1; - } - sftp->ext->name[count - 1] = ext_name; - - sftp->ext->data = realloc(sftp->ext->data, count * sizeof(char *)); - if (sftp->ext->data == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(ext_name); - SAFE_FREE(ext_data); - return -1; - } - sftp->ext->data[count - 1] = ext_data; - - sftp->ext->count = count; - - rc = ssh_buffer_unpack(packet->payload, "s", &ext_name); - } - - sftp_packet_free(packet); - - sftp->version = sftp->server_version = version; - - - return 0; -} - -unsigned int sftp_extensions_get_count(sftp_session sftp) { - if (sftp == NULL || sftp->ext == NULL) { - return 0; - } - - return sftp->ext->count; -} - -const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx) { - if (sftp == NULL) - return NULL; - if (sftp->ext == NULL || sftp->ext->name == NULL) { - ssh_set_error_invalid(sftp->session); - return NULL; - } - - if (idx > sftp->ext->count) { - ssh_set_error_invalid(sftp->session); - return NULL; - } - - return sftp->ext->name[idx]; -} - -const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx) { - if (sftp == NULL) - return NULL; - if (sftp->ext == NULL || sftp->ext->name == NULL) { - ssh_set_error_invalid(sftp->session); - return NULL; - } - - if (idx > sftp->ext->count) { - ssh_set_error_invalid(sftp->session); - return NULL; - } - - return sftp->ext->data[idx]; -} - -int sftp_extension_supported(sftp_session sftp, const char *name, - const char *data) { - int i, n; - - if (sftp == NULL || name == NULL || data == NULL) { - return 0; - } - - n = sftp_extensions_get_count(sftp); - for (i = 0; i < n; i++) { - const char *ext_name = sftp_extensions_get_name(sftp, i); - const char *ext_data = sftp_extensions_get_data(sftp, i); - - if (ext_name != NULL && ext_data != NULL && - strcmp(ext_name, name) == 0 && - strcmp(ext_data, data) == 0) { - return 1; - } - } - - return 0; -} - -static sftp_request_queue request_queue_new(sftp_message msg) { - sftp_request_queue queue = NULL; - - queue = malloc(sizeof(struct sftp_request_queue_struct)); - if (queue == NULL) { - ssh_set_error_oom(msg->sftp->session); - return NULL; - } - ZERO_STRUCTP(queue); - - queue->message = msg; - - return queue; -} - -static void request_queue_free(sftp_request_queue queue) { - if (queue == NULL) { - return; - } - - ZERO_STRUCTP(queue); - SAFE_FREE(queue); -} - -static int sftp_enqueue(sftp_session sftp, sftp_message msg) { - sftp_request_queue queue = NULL; - sftp_request_queue ptr; - - queue = request_queue_new(msg); - if (queue == NULL) { - return -1; - } - - SSH_LOG(SSH_LOG_PACKET, - "Queued msg type %d id %d", - msg->id, msg->packet_type); - - if(sftp->queue == NULL) { - sftp->queue = queue; - } else { - ptr = sftp->queue; - while(ptr->next) { - ptr=ptr->next; /* find end of linked list */ - } - ptr->next = queue; /* add it on bottom */ - } - - return 0; -} - -/* - * Pulls of a message from the queue based on the ID. - * Returns NULL if no message has been found. - */ -static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){ - sftp_request_queue prev = NULL; - sftp_request_queue queue; - sftp_message msg; - - if(sftp->queue == NULL) { - return NULL; - } - - queue = sftp->queue; - while (queue) { - if(queue->message->id == id) { - /* remove from queue */ - if (prev == NULL) { - sftp->queue = queue->next; - } else { - prev->next = queue->next; - } - msg = queue->message; - request_queue_free(queue); - SSH_LOG(SSH_LOG_PACKET, - "Dequeued msg id %d type %d", - msg->id, - msg->packet_type); - return msg; - } - prev = queue; - queue = queue->next; - } - - return NULL; -} - -/* - * Assigns a new SFTP ID for new requests and assures there is no collision - * between them. - * Returns a new ID ready to use in a request - */ -static inline uint32_t sftp_get_new_id(sftp_session session) { - return ++session->id_counter; -} - -static sftp_status_message parse_status_msg(sftp_message msg){ - sftp_status_message status; - int rc; - - if (msg->packet_type != SSH_FXP_STATUS) { - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Not a ssh_fxp_status message passed in!"); - return NULL; - } - - status = malloc(sizeof(struct sftp_status_message_struct)); - if (status == NULL) { - ssh_set_error_oom(msg->sftp->session); - return NULL; - } - ZERO_STRUCTP(status); - - status->id = msg->id; - rc = ssh_buffer_unpack(msg->payload, "d", - &status->status); - if (rc != SSH_OK){ - SAFE_FREE(status); - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Invalid SSH_FXP_STATUS message"); - return NULL; - } - rc = ssh_buffer_unpack(msg->payload, "ss", - &status->errormsg, - &status->langmsg); - - if(rc != SSH_OK && msg->sftp->version >=3){ - /* These are mandatory from version 3 */ - SAFE_FREE(status); - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Invalid SSH_FXP_STATUS message"); - return NULL; - } - if (status->errormsg == NULL) - status->errormsg = strdup("No error message in packet"); - if (status->langmsg == NULL) - status->langmsg = strdup(""); - if (status->errormsg == NULL || status->langmsg == NULL) { - ssh_set_error_oom(msg->sftp->session); - status_msg_free(status); - return NULL; - } - - return status; -} - -static void status_msg_free(sftp_status_message status){ - if (status == NULL) { - return; - } - - SAFE_FREE(status->errormsg); - SAFE_FREE(status->langmsg); - SAFE_FREE(status); -} - -static sftp_file parse_handle_msg(sftp_message msg){ - sftp_file file; - - if(msg->packet_type != SSH_FXP_HANDLE) { - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Not a ssh_fxp_handle message passed in!"); - return NULL; - } - - file = malloc(sizeof(struct sftp_file_struct)); - if (file == NULL) { - ssh_set_error_oom(msg->sftp->session); - return NULL; - } - ZERO_STRUCTP(file); - - file->handle = buffer_get_ssh_string(msg->payload); - if (file->handle == NULL) { - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Invalid SSH_FXP_HANDLE message"); - SAFE_FREE(file); - return NULL; - } - - file->sftp = msg->sftp; - file->offset = 0; - file->eof = 0; - - return file; -} - -/* Open a directory */ -sftp_dir sftp_opendir(sftp_session sftp, const char *path){ - sftp_message msg = NULL; - sftp_file file = NULL; - sftp_dir dir = NULL; - sftp_status_message status; - ssh_string path_s; - ssh_buffer payload; - uint32_t id; - - payload = ssh_buffer_new(); - if (payload == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - path_s = ssh_string_from_char(path); - if (path_s == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(payload); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(payload, htonl(id)) < 0 || - buffer_add_ssh_string(payload, path_s) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(payload); - ssh_string_free(path_s); - return NULL; - } - ssh_string_free(path_s); - - if (sftp_packet_write(sftp, SSH_FXP_OPENDIR, payload) < 0) { - ssh_buffer_free(payload); - return NULL; - } - ssh_buffer_free(payload); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - sftp_set_error(sftp, status->status); - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return NULL; - case SSH_FXP_HANDLE: - file = parse_handle_msg(msg); - sftp_message_free(msg); - if (file != NULL) { - dir = malloc(sizeof(struct sftp_dir_struct)); - if (dir == NULL) { - ssh_set_error_oom(sftp->session); - free(file); - return NULL; - } - ZERO_STRUCTP(dir); - - dir->sftp = sftp; - dir->name = strdup(path); - if (dir->name == NULL) { - SAFE_FREE(dir); - SAFE_FREE(file); - return NULL; - } - dir->handle = file->handle; - SAFE_FREE(file); - } - return dir; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during opendir!", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -/* - * Parse the attributes from a payload from some messages. It is coded on - * baselines from the protocol version 4. - * This code is more or less dead but maybe we need it in future. - */ -static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf, - int expectnames) { - sftp_attributes attr; - ssh_string owner = NULL; - ssh_string group = NULL; - uint32_t flags = 0; - int ok = 0; - - /* unused member variable */ - (void) expectnames; - - attr = malloc(sizeof(struct sftp_attributes_struct)); - if (attr == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(attr); - - /* This isn't really a loop, but it is like a try..catch.. */ - do { - if (buffer_get_u32(buf, &flags) != 4) { - break; - } - - flags = ntohl(flags); - attr->flags = flags; - - if (flags & SSH_FILEXFER_ATTR_SIZE) { - if (buffer_get_u64(buf, &attr->size) != 8) { - break; - } - attr->size = ntohll(attr->size); - } - - if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) { - owner = buffer_get_ssh_string(buf); - if (owner == NULL) { - break; - } - attr->owner = ssh_string_to_char(owner); - ssh_string_free(owner); - if (attr->owner == NULL) { - break; - } - - group = buffer_get_ssh_string(buf); - if (group == NULL) { - break; - } - attr->group = ssh_string_to_char(group); - ssh_string_free(group); - if (attr->group == NULL) { - break; - } - } - - if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { - if (buffer_get_u32(buf, &attr->permissions) != 4) { - break; - } - attr->permissions = ntohl(attr->permissions); - - /* FIXME on windows! */ - switch (attr->permissions & S_IFMT) { - case S_IFSOCK: - case S_IFBLK: - case S_IFCHR: - case S_IFIFO: - attr->type = SSH_FILEXFER_TYPE_SPECIAL; - break; - case S_IFLNK: - attr->type = SSH_FILEXFER_TYPE_SYMLINK; - break; - case S_IFREG: - attr->type = SSH_FILEXFER_TYPE_REGULAR; - break; - case S_IFDIR: - attr->type = SSH_FILEXFER_TYPE_DIRECTORY; - break; - default: - attr->type = SSH_FILEXFER_TYPE_UNKNOWN; - break; - } - } - - if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { - if (buffer_get_u64(buf, &attr->atime64) != 8) { - break; - } - attr->atime64 = ntohll(attr->atime64); - } - - if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { - if (buffer_get_u32(buf, &attr->atime_nseconds) != 4) { - break; - } - attr->atime_nseconds = ntohl(attr->atime_nseconds); - } - - if (flags & SSH_FILEXFER_ATTR_CREATETIME) { - if (buffer_get_u64(buf, &attr->createtime) != 8) { - break; - } - attr->createtime = ntohll(attr->createtime); - } - - if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { - if (buffer_get_u32(buf, &attr->createtime_nseconds) != 4) { - break; - } - attr->createtime_nseconds = ntohl(attr->createtime_nseconds); - } - - if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) { - if (buffer_get_u64(buf, &attr->mtime64) != 8) { - break; - } - attr->mtime64 = ntohll(attr->mtime64); - } - - if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { - if (buffer_get_u32(buf, &attr->mtime_nseconds) != 4) { - break; - } - attr->mtime_nseconds = ntohl(attr->mtime_nseconds); - } - - if (flags & SSH_FILEXFER_ATTR_ACL) { - if ((attr->acl = buffer_get_ssh_string(buf)) == NULL) { - break; - } - } - - if (flags & SSH_FILEXFER_ATTR_EXTENDED) { - if (buffer_get_u32(buf,&attr->extended_count) != 4) { - break; - } - attr->extended_count = ntohl(attr->extended_count); - - while(attr->extended_count && - (attr->extended_type = buffer_get_ssh_string(buf)) && - (attr->extended_data = buffer_get_ssh_string(buf))){ - attr->extended_count--; - } - - if (attr->extended_count) { - break; - } - } - ok = 1; - } while (0); - - if (ok == 0) { - /* break issued somewhere */ - ssh_string_free(attr->acl); - ssh_string_free(attr->extended_type); - ssh_string_free(attr->extended_data); - SAFE_FREE(attr->owner); - SAFE_FREE(attr->group); - SAFE_FREE(attr); - - ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure"); - - return NULL; - } - - return attr; -} - -enum sftp_longname_field_e { - SFTP_LONGNAME_PERM = 0, - SFTP_LONGNAME_FIXME, - SFTP_LONGNAME_OWNER, - SFTP_LONGNAME_GROUP, - SFTP_LONGNAME_SIZE, - SFTP_LONGNAME_DATE, - SFTP_LONGNAME_TIME, - SFTP_LONGNAME_NAME, -}; - -static char *sftp_parse_longname(const char *longname, - enum sftp_longname_field_e longname_field) { - const char *p, *q; - size_t len, field = 0; - char *x; - - p = longname; - /* Find the beginning of the field which is specified by sftp_longanme_field_e. */ - while(field != longname_field) { - if(isspace(*p)) { - field++; - p++; - while(*p && isspace(*p)) { - p++; - } - } else { - p++; - } - } - - q = p; - while (! isspace(*q)) { - q++; - } - - /* There is no strndup on windows */ - len = q - p + 1; - x = malloc(len); - if (x == NULL) { - return NULL; - } - - snprintf(x, len, "%s", p); - - return x; -} - -/* sftp version 0-3 code. It is different from the v4 */ -/* maybe a paste of the draft is better than the code */ -/* - uint32 flags - uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE - uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID - uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID - uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS - uint32 atime present only if flag SSH_FILEXFER_ACMODTIME - uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME - uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED - string extended_type - string extended_data - ... more extended data (extended_type - extended_data pairs), - so that number of pairs equals extended_count */ -static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, - int expectname) { - sftp_attributes attr; - int rc; - - attr = malloc(sizeof(struct sftp_attributes_struct)); - if (attr == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(attr); - - if (expectname) { - rc = ssh_buffer_unpack(buf, "ss", - &attr->name, - &attr->longname); - if (rc != SSH_OK){ - goto error; - } - SSH_LOG(SSH_LOG_RARE, "Name: %s", attr->name); - - /* Set owner and group if we talk to openssh and have the longname */ - if (ssh_get_openssh_version(sftp->session)) { - attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER); - if (attr->owner == NULL) { - goto error; - } - - attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP); - if (attr->group == NULL) { - goto error; - } - } - } - - rc = ssh_buffer_unpack(buf, "d", &attr->flags); - if (rc != SSH_OK){ - goto error; - } - SSH_LOG(SSH_LOG_RARE, - "Flags: %.8lx\n", (long unsigned int) attr->flags); - - if (attr->flags & SSH_FILEXFER_ATTR_SIZE) { - rc = ssh_buffer_unpack(buf, "q", &attr->size); - if(rc != SSH_OK) { - goto error; - } - SSH_LOG(SSH_LOG_RARE, - "Size: %llu\n", - (long long unsigned int) attr->size); - } - - if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) { - rc = ssh_buffer_unpack(buf, "dd", - &attr->uid, - &attr->gid); - if (rc != SSH_OK){ - goto error; - } - } - - if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) { - rc = ssh_buffer_unpack(buf, "d", &attr->permissions); - if (rc != SSH_OK){ - goto error; - } - - switch (attr->permissions & S_IFMT) { - case S_IFSOCK: - case S_IFBLK: - case S_IFCHR: - case S_IFIFO: - attr->type = SSH_FILEXFER_TYPE_SPECIAL; - break; - case S_IFLNK: - attr->type = SSH_FILEXFER_TYPE_SYMLINK; - break; - case S_IFREG: - attr->type = SSH_FILEXFER_TYPE_REGULAR; - break; - case S_IFDIR: - attr->type = SSH_FILEXFER_TYPE_DIRECTORY; - break; - default: - attr->type = SSH_FILEXFER_TYPE_UNKNOWN; - break; - } - } - - if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) { - rc = ssh_buffer_unpack(buf, "dd", - &attr->atime, - &attr->mtime); - if (rc != SSH_OK){ - goto error; - } - } - - if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) { - rc = ssh_buffer_unpack(buf, "d", &attr->extended_count); - if (rc != SSH_OK){ - goto error; - } - - if (attr->extended_count > 0){ - rc = ssh_buffer_unpack(buf, "ss", - &attr->extended_type, - &attr->extended_data); - if (rc != SSH_OK){ - goto error; - } - attr->extended_count--; - } - /* just ignore the remaining extensions */ - - while (attr->extended_count > 0){ - ssh_string tmp1,tmp2; - rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2); - if (rc != SSH_OK){ - goto error; - } - SAFE_FREE(tmp1); - SAFE_FREE(tmp2); - attr->extended_count--; - } - } - - return attr; - - error: - ssh_string_free(attr->extended_type); - ssh_string_free(attr->extended_data); - SAFE_FREE(attr->name); - SAFE_FREE(attr->longname); - SAFE_FREE(attr->owner); - SAFE_FREE(attr->group); - SAFE_FREE(attr); - ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure"); - - return NULL; -} - -/* FIXME is this really needed as a public function? */ -int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr) { - uint32_t flags = (attr ? attr->flags : 0); - int rc; - - flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID | - SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); - - rc = ssh_buffer_pack(buffer, "d", flags); - if (rc != SSH_OK) { - return -1; - } - - if (attr != NULL) { - if (flags & SSH_FILEXFER_ATTR_SIZE) { - rc = ssh_buffer_pack(buffer, "q", attr->size); - if (rc != SSH_OK) { - return -1; - } - } - - if (flags & SSH_FILEXFER_ATTR_UIDGID) { - rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid); - if (rc != SSH_OK) { - return -1; - } - } - - if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { - rc = ssh_buffer_pack(buffer, "d", attr->permissions); - if (rc != SSH_OK) { - return -1; - } - } - - if (flags & SSH_FILEXFER_ATTR_ACMODTIME) { - rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime); - if (rc != SSH_OK) { - return -1; - } - } - } - return 0; -} - - -sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf, - int expectname) { - switch(session->version) { - case 4: - return sftp_parse_attr_4(session, buf, expectname); - case 3: - case 2: - case 1: - case 0: - return sftp_parse_attr_3(session, buf, expectname); - default: - ssh_set_error(session->session, SSH_FATAL, - "Version %d unsupported by client", session->server_version); - return NULL; - } - - return NULL; -} - -/* Get the version of the SFTP protocol supported by the server */ -int sftp_server_version(sftp_session sftp) { - return sftp->server_version; -} - -/* Get a single file attributes structure of a directory. */ -sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) { - sftp_message msg = NULL; - sftp_status_message status; - sftp_attributes attr; - ssh_buffer payload; - uint32_t id; - - if (dir->buffer == NULL) { - payload = ssh_buffer_new(); - if (payload == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(payload, htonl(id)) < 0 || - buffer_add_ssh_string(payload, dir->handle) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(payload); - return NULL; - } - - if (sftp_packet_write(sftp, SSH_FXP_READDIR, payload) < 0) { - ssh_buffer_free(payload); - return NULL; - } - ssh_buffer_free(payload); - - SSH_LOG(SSH_LOG_PACKET, - "Sent a ssh_fxp_readdir with id %d", id); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - switch (msg->packet_type){ - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_EOF: - dir->eof = 1; - status_msg_free(status); - return NULL; - default: - break; - } - - ssh_set_error(sftp->session, SSH_FATAL, - "Unknown error status: %d", status->status); - status_msg_free(status); - - return NULL; - case SSH_FXP_NAME: - buffer_get_u32(msg->payload, &dir->count); - dir->count = ntohl(dir->count); - dir->buffer = msg->payload; - msg->payload = NULL; - sftp_message_free(msg); - break; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Unsupported message back %d", msg->packet_type); - sftp_message_free(msg); - - return NULL; - } - } - - /* now dir->buffer contains a buffer and dir->count != 0 */ - if (dir->count == 0) { - ssh_set_error(sftp->session, SSH_FATAL, - "Count of files sent by the server is zero, which is invalid, or " - "libsftp bug"); - return NULL; - } - - SSH_LOG(SSH_LOG_RARE, "Count is %d", dir->count); - - attr = sftp_parse_attr(sftp, dir->buffer, 1); - if (attr == NULL) { - ssh_set_error(sftp->session, SSH_FATAL, - "Couldn't parse the SFTP attributes"); - return NULL; - } - - dir->count--; - if (dir->count == 0) { - ssh_buffer_free(dir->buffer); - dir->buffer = NULL; - } - - return attr; -} - -/* Tell if the directory has reached EOF (End Of File). */ -int sftp_dir_eof(sftp_dir dir) { - return dir->eof; -} - -/* Free a SFTP_ATTRIBUTE handle */ -void sftp_attributes_free(sftp_attributes file){ - if (file == NULL) { - return; - } - - ssh_string_free(file->acl); - ssh_string_free(file->extended_data); - ssh_string_free(file->extended_type); - - SAFE_FREE(file->name); - SAFE_FREE(file->longname); - SAFE_FREE(file->group); - SAFE_FREE(file->owner); - - SAFE_FREE(file); -} - -static int sftp_handle_close(sftp_session sftp, ssh_string handle) { - sftp_status_message status; - sftp_message msg = NULL; - ssh_buffer buffer = NULL; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, handle) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_CLOSE ,buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return -1; - } - msg = sftp_dequeue(sftp,id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if(status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - break; - default: - break; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during sftp_handle_close!", msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* Close an open file handle. */ -int sftp_close(sftp_file file){ - int err = SSH_NO_ERROR; - - SAFE_FREE(file->name); - if (file->handle){ - err = sftp_handle_close(file->sftp,file->handle); - ssh_string_free(file->handle); - } - /* FIXME: check server response and implement errno */ - SAFE_FREE(file); - - return err; -} - -/* Close an open directory. */ -int sftp_closedir(sftp_dir dir){ - int err = SSH_NO_ERROR; - - SAFE_FREE(dir->name); - if (dir->handle) { - err = sftp_handle_close(dir->sftp, dir->handle); - ssh_string_free(dir->handle); - } - /* FIXME: check server response and implement errno */ - ssh_buffer_free(dir->buffer); - SAFE_FREE(dir); - - return err; -} - -/* Open a file on the server. */ -sftp_file sftp_open(sftp_session sftp, const char *file, int flags, - mode_t mode) { - sftp_message msg = NULL; - sftp_status_message status; - struct sftp_attributes_struct attr; - sftp_file handle; - ssh_string filename; - ssh_buffer buffer; - uint32_t sftp_flags = 0; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - filename = ssh_string_from_char(file); - if (filename == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - ZERO_STRUCT(attr); - attr.permissions = mode; - attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; - - if (flags == O_RDONLY) - sftp_flags |= SSH_FXF_READ; /* if any of the other flag is set, - READ should not be set initialy */ - if (flags & O_WRONLY) - sftp_flags |= SSH_FXF_WRITE; - if (flags & O_RDWR) - sftp_flags |= (SSH_FXF_WRITE | SSH_FXF_READ); - if (flags & O_CREAT) - sftp_flags |= SSH_FXF_CREAT; - if (flags & O_TRUNC) - sftp_flags |= SSH_FXF_TRUNC; - if (flags & O_EXCL) - sftp_flags |= SSH_FXF_EXCL; - SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags); - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, filename) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(filename); - return NULL; - } - ssh_string_free(filename); - - if (buffer_add_u32(buffer, htonl(sftp_flags)) < 0 || - buffer_add_attributes(buffer, &attr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_OPEN, buffer) < 0) { - ssh_buffer_free(buffer); - return NULL; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - sftp_set_error(sftp, status->status); - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - - return NULL; - case SSH_FXP_HANDLE: - handle = parse_handle_msg(msg); - sftp_message_free(msg); - return handle; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during open!", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -void sftp_file_set_nonblocking(sftp_file handle){ - handle->nonblocking=1; -} -void sftp_file_set_blocking(sftp_file handle){ - handle->nonblocking=0; -} - -/* Read from a file using an opened sftp file handle. */ -ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { - sftp_session sftp = handle->sftp; - sftp_message msg = NULL; - sftp_status_message status; - ssh_string datastring; - ssh_buffer buffer; - int id; - int rc; - - if (handle->eof) { - return 0; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(handle->sftp); - - rc = ssh_buffer_pack(buffer, - "dSqd", - id, - handle->handle, - handle->offset, - count); - if (rc != SSH_OK){ - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - if (sftp_packet_write(handle->sftp, SSH_FXP_READ, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (handle->nonblocking) { - if (ssh_channel_poll(handle->sftp->channel, 0) == 0) { - /* we cannot block */ - return 0; - } - } - if (sftp_read_and_dispatch(handle->sftp) < 0) { - /* something nasty has happened */ - return -1; - } - msg = sftp_dequeue(handle->sftp, id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_EOF: - handle->eof = 1; - status_msg_free(status); - return 0; - default: - break; - } - ssh_set_error(sftp->session,SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - case SSH_FXP_DATA: - datastring = buffer_get_ssh_string(msg->payload); - sftp_message_free(msg); - if (datastring == NULL) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received invalid DATA packet from sftp server"); - return -1; - } - - if (ssh_string_len(datastring) > count) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received a too big DATA packet from sftp server: " - "%" PRIdS " and asked for %" PRIdS, - ssh_string_len(datastring), count); - ssh_string_free(datastring); - return -1; - } - count = ssh_string_len(datastring); - handle->offset += count; - memcpy(buf, ssh_string_data(datastring), count); - ssh_string_free(datastring); - return count; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during read!", msg->packet_type); - sftp_message_free(msg); - return -1; - } - - return -1; /* not reached */ -} - -/* Start an asynchronous read from a file using an opened sftp file handle. */ -int sftp_async_read_begin(sftp_file file, uint32_t len){ - sftp_session sftp = file->sftp; - ssh_buffer buffer; - uint32_t id; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(sftp); - - rc = ssh_buffer_pack(buffer, - "dSqd", - id, - file->handle, - file->offset, - len); - if (rc != SSH_OK) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_READ, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - file->offset += len; /* assume we'll read len bytes */ - - return id; -} - -/* Wait for an asynchronous read to complete and save the data. */ -int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){ - sftp_session sftp; - sftp_message msg = NULL; - sftp_status_message status; - ssh_string datastring; - int err = SSH_OK; - uint32_t len; - - if (file == NULL) { - return SSH_ERROR; - } - sftp = file->sftp; - - if (file->eof) { - return 0; - } - - /* handle an existing request */ - while (msg == NULL) { - if (file->nonblocking){ - if (ssh_channel_poll(sftp->channel, 0) == 0) { - /* we cannot block */ - return SSH_AGAIN; - } - } - - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return SSH_ERROR; - } - - msg = sftp_dequeue(sftp,id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - if (status->status != SSH_FX_EOF) { - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server : %s", status->errormsg); - err = SSH_ERROR; - } else { - file->eof = 1; - } - status_msg_free(status); - return err; - case SSH_FXP_DATA: - datastring = buffer_get_ssh_string(msg->payload); - sftp_message_free(msg); - if (datastring == NULL) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received invalid DATA packet from sftp server"); - return SSH_ERROR; - } - if (ssh_string_len(datastring) > size) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received a too big DATA packet from sftp server: " - "%" PRIdS " and asked for %u", - ssh_string_len(datastring), size); - ssh_string_free(datastring); - return SSH_ERROR; - } - len = ssh_string_len(datastring); - /* Update the offset with the correct value */ - file->offset = file->offset - (size - len); - memcpy(data, ssh_string_data(datastring), len); - ssh_string_free(datastring); - return len; - default: - ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during read!",msg->packet_type); - sftp_message_free(msg); - return SSH_ERROR; - } - - return SSH_ERROR; -} - -ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { - sftp_session sftp = file->sftp; - sftp_message msg = NULL; - sftp_status_message status; - ssh_buffer buffer; - uint32_t id; - int len; - int packetlen; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(file->sftp); - - rc = ssh_buffer_pack(buffer, - "dSqdP", - id, - file->handle, - file->offset, - count, /* len of datastring */ - (size_t)count, buf); - if (rc != SSH_OK){ - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - packetlen=buffer_get_rest_len(buffer); - len = sftp_packet_write(file->sftp, SSH_FXP_WRITE, buffer); - ssh_buffer_free(buffer); - if (len < 0) { - return -1; - } else if (len != packetlen) { - SSH_LOG(SSH_LOG_PACKET, - "Could not write as much data as expected"); - } - - while (msg == NULL) { - if (sftp_read_and_dispatch(file->sftp) < 0) { - /* something nasty has happened */ - return -1; - } - msg = sftp_dequeue(file->sftp, id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - file->offset += count; - status_msg_free(status); - return count; - default: - break; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - file->offset += count; - status_msg_free(status); - return -1; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during write!", msg->packet_type); - sftp_message_free(msg); - return -1; - } - - return -1; /* not reached */ -} - -/* Seek to a specific location in a file. */ -int sftp_seek(sftp_file file, uint32_t new_offset) { - if (file == NULL) { - return -1; - } - - file->offset = new_offset; - file->eof = 0; - - return 0; -} - -int sftp_seek64(sftp_file file, uint64_t new_offset) { - if (file == NULL) { - return -1; - } - - file->offset = new_offset; - file->eof = 0; - - return 0; -} - -/* Report current byte position in file. */ -unsigned long sftp_tell(sftp_file file) { - return (unsigned long)file->offset; -} -/* Report current byte position in file. */ -uint64_t sftp_tell64(sftp_file file) { - return (uint64_t) file->offset; -} - -/* Rewinds the position of the file pointer to the beginning of the file.*/ -void sftp_rewind(sftp_file file) { - file->offset = 0; - file->eof = 0; -} - -/* code written by Nick */ -int sftp_unlink(sftp_session sftp, const char *file) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_buffer buffer; - uint32_t id; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(sftp); - - rc = ssh_buffer_pack(buffer, - "ds", - id, - file); - if (rc != SSH_OK) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - if (sftp_packet_write(sftp, SSH_FXP_REMOVE, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp)) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_STATUS) { - /* by specification, this command's only supposed to return SSH_FXP_STATUS */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - default: - break; - } - - /* - * The status should be SSH_FX_OK if the command was successful, if it - * didn't, then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session,SSH_FATAL, - "Received message %d when attempting to remove file", msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* code written by Nick */ -int sftp_rmdir(sftp_session sftp, const char *directory) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_buffer buffer; - uint32_t id; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(sftp); - - rc = ssh_buffer_pack(buffer, - "ds", - id, - directory); - if (rc != SSH_OK) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - break; - default: - break; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to remove directory", - msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* Code written by Nick */ -int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - sftp_attributes errno_attr = NULL; - struct sftp_attributes_struct attr; - ssh_buffer buffer; - ssh_string path; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - path = ssh_string_from_char(directory); - if (path == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - ZERO_STRUCT(attr); - attr.permissions = mode; - attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, path) < 0 || - buffer_add_attributes(buffer, &attr) < 0 || - sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(path); - return -1; - } - ssh_buffer_free(buffer); - ssh_string_free(path); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command only returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_FAILURE: - /* - * mkdir always returns a failure, even if the path already exists. - * To be POSIX conform and to be able to map it to EEXIST a stat - * call is needed here. - */ - errno_attr = sftp_lstat(sftp, directory); - if (errno_attr != NULL) { - SAFE_FREE(errno_attr); - sftp_set_error(sftp, SSH_FX_FILE_ALREADY_EXISTS); - } - break; - case SSH_FX_OK: - status_msg_free(status); - return 0; - break; - default: - break; - } - /* - * The status should be SSH_FX_OK if the command was successful, if it - * didn't, then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to make directory", - msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* code written by nick */ -int sftp_rename(sftp_session sftp, const char *original, const char *newname) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_buffer buffer; - uint32_t id; - int rc; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(sftp); - - rc = ssh_buffer_pack(buffer, - "dss", - id, - original, - newname); - if (rc != SSH_OK) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - if (sftp->version >= 4){ - /* POSIX rename atomically replaces newpath, we should do the same - * only available on >=v4 */ - buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE); - } - - if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command only returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - default: - break; - } - /* - * Status should be SSH_FX_OK if the command was successful, if it didn't, - * then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to rename", - msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* Code written by Nick */ -/* Set file attributes on a file, directory or symbolic link. */ -int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) { - uint32_t id; - ssh_buffer buffer; - ssh_string path; - sftp_message msg = NULL; - sftp_status_message status = NULL; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - path = ssh_string_from_char(file); - if (path == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, path) < 0 || - buffer_add_attributes(buffer, attr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(path); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_SETSTAT, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(path); - return -1; - } - ssh_buffer_free(buffer); - ssh_string_free(path); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command only returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - default: - break; - } - /* - * The status should be SSH_FX_OK if the command was successful, if it - * didn't, then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* Change the file owner and group */ -int sftp_chown(sftp_session sftp, const char *file, uid_t owner, gid_t group) { - struct sftp_attributes_struct attr; - ZERO_STRUCT(attr); - - attr.uid = owner; - attr.gid = group; - - attr.flags = SSH_FILEXFER_ATTR_UIDGID; - - return sftp_setstat(sftp, file, &attr); -} - -/* Change permissions of a file */ -int sftp_chmod(sftp_session sftp, const char *file, mode_t mode) { - struct sftp_attributes_struct attr; - ZERO_STRUCT(attr); - attr.permissions = mode; - attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; - - return sftp_setstat(sftp, file, &attr); -} - -/* Change the last modification and access time of a file. */ -int sftp_utimes(sftp_session sftp, const char *file, - const struct timeval *times) { - struct sftp_attributes_struct attr; - ZERO_STRUCT(attr); - - attr.atime = times[0].tv_sec; - attr.atime_nseconds = times[0].tv_usec; - - attr.mtime = times[1].tv_sec; - attr.mtime_nseconds = times[1].tv_usec; - - attr.flags |= SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_MODIFYTIME | - SSH_FILEXFER_ATTR_SUBSECOND_TIMES; - - return sftp_setstat(sftp, file, &attr); -} - -int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_buffer buffer; - uint32_t id; - int rc; - - if (sftp == NULL) - return -1; - if (target == NULL || dest == NULL) { - ssh_set_error_invalid(sftp->session); - return -1; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(sftp); - - /* TODO check for version number if they ever fix it. */ - if (ssh_get_openssh_version(sftp->session)) { - rc = ssh_buffer_pack(buffer, - "dss", - id, - target, - dest); - } else { - rc = ssh_buffer_pack(buffer, - "dss", - id, - dest, - target); - } - if (rc != SSH_OK){ - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - if (sftp_packet_write(sftp, SSH_FXP_SYMLINK, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command only returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - default: - break; - } - /* - * The status should be SSH_FX_OK if the command was successful, if it - * didn't, then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -char *sftp_readlink(sftp_session sftp, const char *path) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string path_s = NULL; - ssh_string link_s = NULL; - ssh_buffer buffer; - char *lnk; - uint32_t ignored; - uint32_t id; - - if (sftp == NULL) - return NULL; - if (path == NULL) { - ssh_set_error_invalid(sftp); - return NULL; - } - if (sftp->version < 3){ - ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_readlink",sftp->version); - return NULL; - } - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - path_s = ssh_string_from_char(path); - if (path_s == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, path_s) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(path_s); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_READLINK, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(path_s); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(path_s); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_NAME) { - /* we don't care about "count" */ - buffer_get_u32(msg->payload, &ignored); - /* we only care about the file name string */ - link_s = buffer_get_ssh_string(msg->payload); - sftp_message_free(msg); - if (link_s == NULL) { - /* TODO: what error to set here? */ - return NULL; - } - lnk = ssh_string_to_char(link_s); - ssh_string_free(link_s); - - return lnk; - } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - } else { /* this shouldn't happen */ - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) { - sftp_statvfs_t statvfs; - int rc; - - statvfs = malloc(sizeof(struct sftp_statvfs_struct)); - if (statvfs == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(statvfs); - - rc = ssh_buffer_unpack(buf, "qqqqqqqqqqq", - &statvfs->f_bsize, /* file system block size */ - &statvfs->f_frsize, /* fundamental fs block size */ - &statvfs->f_blocks, /* number of blocks (unit f_frsize) */ - &statvfs->f_bfree, /* free blocks in file system */ - &statvfs->f_bavail, /* free blocks for non-root */ - &statvfs->f_files, /* total file inodes */ - &statvfs->f_ffree, /* free file inodes */ - &statvfs->f_favail, /* free file inodes for to non-root */ - &statvfs->f_fsid, /* file system id */ - &statvfs->f_flag, /* bit mask of f_flag values */ - &statvfs->f_namemax/* maximum filename length */ - ); - if (rc != SSH_OK) { - SAFE_FREE(statvfs); - ssh_set_error(sftp->session, SSH_FATAL, "Invalid statvfs structure"); - return NULL; - } - - return statvfs; -} - -sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string pathstr; - ssh_string ext; - ssh_buffer buffer; - uint32_t id; - - if (sftp == NULL) - return NULL; - if (path == NULL) { - ssh_set_error_invalid(sftp->session); - return NULL; - } - if (sftp->version < 3){ - ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_statvfs",sftp->version); - return NULL; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - ext = ssh_string_from_char("statvfs@openssh.com"); - if (ext == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - pathstr = ssh_string_from_char(path); - if (pathstr == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(ext); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, ext) < 0 || - buffer_add_ssh_string(buffer, pathstr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(ext); - ssh_string_free(pathstr); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(ext); - ssh_string_free(pathstr); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(ext); - ssh_string_free(pathstr); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) { - sftp_statvfs_t buf = sftp_parse_statvfs(sftp, msg->payload); - sftp_message_free(msg); - if (buf == NULL) { - return NULL; - } - - return buf; - } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - } else { /* this shouldn't happen */ - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to get statvfs", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -sftp_statvfs_t sftp_fstatvfs(sftp_file file) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - sftp_session sftp; - ssh_string ext; - ssh_buffer buffer; - uint32_t id; - - if (file == NULL) { - return NULL; - } - sftp = file->sftp; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - ext = ssh_string_from_char("fstatvfs@openssh.com"); - if (ext == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, ext) < 0 || - buffer_add_ssh_string(buffer, file->handle) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(ext); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(ext); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(ext); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) { - sftp_statvfs_t buf = sftp_parse_statvfs(sftp, msg->payload); - sftp_message_free(msg); - if (buf == NULL) { - return NULL; - } - - return buf; - } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - } else { /* this shouldn't happen */ - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -void sftp_statvfs_free(sftp_statvfs_t statvfs) { - if (statvfs == NULL) { - return; - } - - SAFE_FREE(statvfs); -} - -/* another code written by Nick */ -char *sftp_canonicalize_path(sftp_session sftp, const char *path) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string name = NULL; - ssh_string pathstr; - ssh_buffer buffer; - char *cname; - uint32_t ignored; - uint32_t id; - - if (sftp == NULL) - return NULL; - if (path == NULL) { - ssh_set_error_invalid(sftp->session); - return NULL; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - pathstr = ssh_string_from_char(path); - if (pathstr == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, pathstr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_REALPATH, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_NAME) { - /* we don't care about "count" */ - buffer_get_u32(msg->payload, &ignored); - /* we only care about the file name string */ - name = buffer_get_ssh_string(msg->payload); - sftp_message_free(msg); - if (name == NULL) { - /* TODO: error message? */ - return NULL; - } - cname = ssh_string_to_char(name); - ssh_string_free(name); - if (cname == NULL) { - ssh_set_error_oom(sftp->session); - } - return cname; - } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - } else { /* this shouldn't happen */ - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -static sftp_attributes sftp_xstat(sftp_session sftp, const char *path, - int param) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string pathstr; - ssh_buffer buffer; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - pathstr = ssh_string_from_char(path); - if (pathstr == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, pathstr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - return NULL; - } - if (sftp_packet_write(sftp, param, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_ATTRS) { - sftp_attributes attr = sftp_parse_attr(sftp, msg->payload, 0); - sftp_message_free(msg); - - return attr; - } else if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - sftp_set_error(sftp, status->status); - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return NULL; - } - ssh_set_error(sftp->session, SSH_FATAL, - "Received mesg %d during stat()", msg->packet_type); - sftp_message_free(msg); - - return NULL; -} - -sftp_attributes sftp_stat(sftp_session session, const char *path) { - return sftp_xstat(session, path, SSH_FXP_STAT); -} - -sftp_attributes sftp_lstat(sftp_session session, const char *path) { - return sftp_xstat(session, path, SSH_FXP_LSTAT); -} - -sftp_attributes sftp_fstat(sftp_file file) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_buffer buffer; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(file->sftp->session); - return NULL; - } - - id = sftp_get_new_id(file->sftp); - if (buffer_add_u32(buffer, htonl(id)) < 0 || - buffer_add_ssh_string(buffer, file->handle) < 0) { - ssh_set_error_oom(file->sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - if (sftp_packet_write(file->sftp, SSH_FXP_FSTAT, buffer) < 0) { - ssh_buffer_free(buffer); - return NULL; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(file->sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(file->sftp, id); - } - - if (msg->packet_type == SSH_FXP_ATTRS){ - return sftp_parse_attr(file->sftp, msg->payload, 0); - } else if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(file->sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - - return NULL; - } - ssh_set_error(file->sftp->session, SSH_FATAL, - "Received msg %d during fstat()", msg->packet_type); - sftp_message_free(msg); - - return NULL; -} - -#endif /* WITH_SFTP */ -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/sftpserver.c b/libssh/src/sftpserver.c deleted file mode 100644 index 60498794..00000000 --- a/libssh/src/sftpserver.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * sftpserver.c - server based function for the sftp protocol - * - * This file is part of the SSH Library - * - * Copyright (c) 2005 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/libssh.h" -#include "libssh/sftp.h" -#include "libssh/ssh2.h" -#include "libssh/priv.h" -#include "libssh/buffer.h" -#include "libssh/misc.h" - -sftp_client_message sftp_get_client_message(sftp_session sftp) { - ssh_session session = sftp->session; - sftp_packet packet; - sftp_client_message msg; - ssh_buffer payload; - int rc; - - msg = malloc(sizeof (struct sftp_client_message_struct)); - if (msg == NULL) { - ssh_set_error_oom(session); - return NULL; - } - ZERO_STRUCTP(msg); - - packet = sftp_packet_read(sftp); - if (packet == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - - payload = packet->payload; - msg->type = packet->type; - msg->sftp = sftp; - - /* take a copy of the whole packet */ - msg->complete_message = ssh_buffer_new(); - ssh_buffer_add_data(msg->complete_message, - buffer_get_rest(payload), - buffer_get_rest_len(payload)); - - buffer_get_u32(payload, &msg->id); - - switch(msg->type) { - case SSH_FXP_CLOSE: - case SSH_FXP_READDIR: - msg->handle = buffer_get_ssh_string(payload); - if (msg->handle == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - case SSH_FXP_READ: - rc = ssh_buffer_unpack(payload, - "Sqd", - &msg->handle, - &msg->offset, - &msg->len); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - case SSH_FXP_WRITE: - rc = ssh_buffer_unpack(payload, - "SqS", - &msg->handle, - &msg->offset, - &msg->data); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - case SSH_FXP_REMOVE: - case SSH_FXP_RMDIR: - case SSH_FXP_OPENDIR: - case SSH_FXP_READLINK: - case SSH_FXP_REALPATH: - rc = ssh_buffer_unpack(payload, - "s", - &msg->filename); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - case SSH_FXP_RENAME: - case SSH_FXP_SYMLINK: - rc = ssh_buffer_unpack(payload, - "sS", - &msg->filename, - &msg->data); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - case SSH_FXP_MKDIR: - case SSH_FXP_SETSTAT: - rc = ssh_buffer_unpack(payload, - "s", - &msg->filename); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - msg->attr = sftp_parse_attr(sftp, payload, 0); - if (msg->attr == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - case SSH_FXP_FSETSTAT: - msg->handle = buffer_get_ssh_string(payload); - if (msg->handle == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - msg->attr = sftp_parse_attr(sftp, payload, 0); - if (msg->attr == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - case SSH_FXP_LSTAT: - case SSH_FXP_STAT: - rc = ssh_buffer_unpack(payload, - "s", - &msg->filename); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - if(sftp->version > 3) { - ssh_buffer_unpack(payload, "d", &msg->flags); - } - break; - case SSH_FXP_OPEN: - rc = ssh_buffer_unpack(payload, - "sd", - &msg->filename, - &msg->flags); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - msg->attr = sftp_parse_attr(sftp, payload, 0); - if (msg->attr == NULL) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - case SSH_FXP_FSTAT: - rc = ssh_buffer_unpack(payload, - "Sd", - &msg->handle, - &msg->flags); - if (rc != SSH_OK) { - ssh_set_error_oom(session); - sftp_client_message_free(msg); - return NULL; - } - break; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received unhandled sftp message %d\n", msg->type); - sftp_client_message_free(msg); - return NULL; - } - - sftp_packet_free(packet); - - return msg; -} - -/* Send an sftp client message. Can be used in cas of proxying */ -int sftp_send_client_message(sftp_session sftp, sftp_client_message msg){ - return sftp_packet_write(sftp, msg->type, msg->complete_message); -} - -uint8_t sftp_client_message_get_type(sftp_client_message msg){ - return msg->type; -} - -const char *sftp_client_message_get_filename(sftp_client_message msg){ - return msg->filename; -} - -void sftp_client_message_set_filename(sftp_client_message msg, const char *newname){ - free(msg->filename); - msg->filename = strdup(newname); -} - -const char *sftp_client_message_get_data(sftp_client_message msg){ - if (msg->str_data == NULL) - msg->str_data = ssh_string_to_char(msg->data); - return msg->str_data; -} - -uint32_t sftp_client_message_get_flags(sftp_client_message msg){ - return msg->flags; -} - -void sftp_client_message_free(sftp_client_message msg) { - if (msg == NULL) { - return; - } - - SAFE_FREE(msg->filename); - ssh_string_free(msg->data); - ssh_string_free(msg->handle); - sftp_attributes_free(msg->attr); - ssh_buffer_free(msg->complete_message); - SAFE_FREE(msg->str_data); - ZERO_STRUCTP(msg); - SAFE_FREE(msg); -} - -int sftp_reply_name(sftp_client_message msg, const char *name, - sftp_attributes attr) { - ssh_buffer out; - ssh_string file; - - out = ssh_buffer_new(); - if (out == NULL) { - return -1; - } - - file = ssh_string_from_char(name); - if (file == NULL) { - ssh_buffer_free(out); - return -1; - } - - if (buffer_add_u32(out, msg->id) < 0 || - buffer_add_u32(out, htonl(1)) < 0 || - buffer_add_ssh_string(out, file) < 0 || - buffer_add_ssh_string(out, file) < 0 || /* The protocol is broken here between 3 & 4 */ - buffer_add_attributes(out, attr) < 0 || - sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) { - ssh_buffer_free(out); - ssh_string_free(file); - return -1; - } - ssh_buffer_free(out); - ssh_string_free(file); - - return 0; -} - -int sftp_reply_handle(sftp_client_message msg, ssh_string handle){ - ssh_buffer out; - - out = ssh_buffer_new(); - if (out == NULL) { - return -1; - } - - if (buffer_add_u32(out, msg->id) < 0 || - buffer_add_ssh_string(out, handle) < 0 || - sftp_packet_write(msg->sftp, SSH_FXP_HANDLE, out) < 0) { - ssh_buffer_free(out); - return -1; - } - ssh_buffer_free(out); - - return 0; -} - -int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr) { - ssh_buffer out; - - out = ssh_buffer_new(); - if (out == NULL) { - return -1; - } - - if (buffer_add_u32(out, msg->id) < 0 || - buffer_add_attributes(out, attr) < 0 || - sftp_packet_write(msg->sftp, SSH_FXP_ATTRS, out) < 0) { - ssh_buffer_free(out); - return -1; - } - ssh_buffer_free(out); - - return 0; -} - -int sftp_reply_names_add(sftp_client_message msg, const char *file, - const char *longname, sftp_attributes attr) { - ssh_string name; - - name = ssh_string_from_char(file); - if (name == NULL) { - return -1; - } - - if (msg->attrbuf == NULL) { - msg->attrbuf = ssh_buffer_new(); - if (msg->attrbuf == NULL) { - ssh_string_free(name); - return -1; - } - } - - if (buffer_add_ssh_string(msg->attrbuf, name) < 0) { - ssh_string_free(name); - return -1; - } - - ssh_string_free(name); - name = ssh_string_from_char(longname); - if (name == NULL) { - return -1; - } - if (buffer_add_ssh_string(msg->attrbuf,name) < 0 || - buffer_add_attributes(msg->attrbuf,attr) < 0) { - ssh_string_free(name); - return -1; - } - ssh_string_free(name); - msg->attr_num++; - - return 0; -} - -int sftp_reply_names(sftp_client_message msg) { - ssh_buffer out; - - out = ssh_buffer_new(); - if (out == NULL) { - ssh_buffer_free(msg->attrbuf); - return -1; - } - - if (buffer_add_u32(out, msg->id) < 0 || - buffer_add_u32(out, htonl(msg->attr_num)) < 0 || - ssh_buffer_add_data(out, buffer_get_rest(msg->attrbuf), - buffer_get_rest_len(msg->attrbuf)) < 0 || - sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) { - ssh_buffer_free(out); - ssh_buffer_free(msg->attrbuf); - return -1; - } - - ssh_buffer_free(out); - ssh_buffer_free(msg->attrbuf); - - msg->attr_num = 0; - msg->attrbuf = NULL; - - return 0; -} - -int sftp_reply_status(sftp_client_message msg, uint32_t status, - const char *message) { - ssh_buffer out; - ssh_string s; - - out = ssh_buffer_new(); - if (out == NULL) { - return -1; - } - - s = ssh_string_from_char(message ? message : ""); - if (s == NULL) { - ssh_buffer_free(out); - return -1; - } - - if (buffer_add_u32(out, msg->id) < 0 || - buffer_add_u32(out, htonl(status)) < 0 || - buffer_add_ssh_string(out, s) < 0 || - buffer_add_u32(out, 0) < 0 || /* language string */ - sftp_packet_write(msg->sftp, SSH_FXP_STATUS, out) < 0) { - ssh_buffer_free(out); - ssh_string_free(s); - return -1; - } - - ssh_buffer_free(out); - ssh_string_free(s); - - return 0; -} - -int sftp_reply_data(sftp_client_message msg, const void *data, int len) { - ssh_buffer out; - - out = ssh_buffer_new(); - if (out == NULL) { - return -1; - } - - if (buffer_add_u32(out, msg->id) < 0 || - buffer_add_u32(out, ntohl(len)) < 0 || - ssh_buffer_add_data(out, data, len) < 0 || - sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) { - ssh_buffer_free(out); - return -1; - } - ssh_buffer_free(out); - - return 0; -} - -/* - * This function will return you a new handle to give the client. - * the function accepts an info that can be retrieved later with - * the handle. Care is given that a corrupted handle won't give a - * valid info (or worse). - */ -ssh_string sftp_handle_alloc(sftp_session sftp, void *info) { - ssh_string ret; - uint32_t val; - int i; - - if (sftp->handles == NULL) { - sftp->handles = malloc(sizeof(void *) * SFTP_HANDLES); - if (sftp->handles == NULL) { - return NULL; - } - memset(sftp->handles, 0, sizeof(void *) * SFTP_HANDLES); - } - - for (i = 0; i < SFTP_HANDLES; i++) { - if (sftp->handles[i] == NULL) { - break; - } - } - - if (i == SFTP_HANDLES) { - return NULL; /* no handle available */ - } - - val = i; - ret = ssh_string_new(4); - if (ret == NULL) { - return NULL; - } - - memcpy(ssh_string_data(ret), &val, sizeof(uint32_t)); - sftp->handles[i] = info; - - return ret; -} - -void *sftp_handle(sftp_session sftp, ssh_string handle){ - uint32_t val; - - if (sftp->handles == NULL) { - return NULL; - } - - if (ssh_string_len(handle) != sizeof(uint32_t)) { - return NULL; - } - - memcpy(&val, ssh_string_data(handle), sizeof(uint32_t)); - - if (val > SFTP_HANDLES) { - return NULL; - } - - return sftp->handles[val]; -} - -void sftp_handle_remove(sftp_session sftp, void *handle) { - int i; - - for (i = 0; i < SFTP_HANDLES; i++) { - if (sftp->handles[i] == handle) { - sftp->handles[i] = NULL; - break; - } - } -} - -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/src/socket.c b/libssh/src/socket.c deleted file mode 100644 index 498da77e..00000000 --- a/libssh/src/socket.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * socket.c - socket functions for the library - * - * This file is part of the SSH Library - * - * Copyright (c) 2008-2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#ifdef _WIN32 -#include -#include -#if _MSC_VER >= 1400 -#include -#undef open -#define open _open -#undef close -#define close _close -#undef read -#define read _read -#undef write -#define write _write -#endif /* _MSC_VER */ -#else /* _WIN32 */ -#include -#include -#include -#include -#endif /* _WIN32 */ - -#include "libssh/priv.h" -#include "libssh/callbacks.h" -#include "libssh/socket.h" -#include "libssh/buffer.h" -#include "libssh/poll.h" -#include "libssh/session.h" - -/** - * @internal - * - * @defgroup libssh_socket The SSH socket functions. - * @ingroup libssh - * - * Functions for handling sockets. - * - * @{ - */ - -enum ssh_socket_states_e { - SSH_SOCKET_NONE, - SSH_SOCKET_CONNECTING, - SSH_SOCKET_CONNECTED, - SSH_SOCKET_EOF, - SSH_SOCKET_ERROR, - SSH_SOCKET_CLOSED -}; - -struct ssh_socket_struct { - socket_t fd_in; - socket_t fd_out; - int fd_is_socket; - int last_errno; - int read_wontblock; /* reading now on socket will - not block */ - int write_wontblock; - int data_except; - enum ssh_socket_states_e state; - ssh_buffer out_buffer; - ssh_buffer in_buffer; - ssh_session session; - ssh_socket_callbacks callbacks; - ssh_poll_handle poll_in; - ssh_poll_handle poll_out; -}; - -static int sockets_initialized = 0; - -static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len); -static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, - uint32_t len); - -/** - * \internal - * \brief inits the socket system (windows specific) - */ -int ssh_socket_init(void) { - if (sockets_initialized == 0) { -#ifdef _WIN32 - struct WSAData wsaData; - - /* Initiates use of the Winsock DLL by a process. */ - if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { - return -1; - } - -#endif - ssh_poll_init(); - - sockets_initialized = 1; - } - - return 0; -} - -/** - * @brief Cleanup the socket system. - */ -void ssh_socket_cleanup(void) { - if (sockets_initialized == 1) { - ssh_poll_cleanup(); -#ifdef _WIN32 - WSACleanup(); -#endif - sockets_initialized = 0; - } -} - - -/** - * \internal - * \brief creates a new Socket object - */ -ssh_socket ssh_socket_new(ssh_session session) { - ssh_socket s; - - s = malloc(sizeof(struct ssh_socket_struct)); - if (s == NULL) { - ssh_set_error_oom(session); - return NULL; - } - s->fd_in = SSH_INVALID_SOCKET; - s->fd_out= SSH_INVALID_SOCKET; - s->last_errno = -1; - s->fd_is_socket = 1; - s->session = session; - s->in_buffer = ssh_buffer_new(); - if (s->in_buffer == NULL) { - ssh_set_error_oom(session); - SAFE_FREE(s); - return NULL; - } - s->out_buffer=ssh_buffer_new(); - if (s->out_buffer == NULL) { - ssh_set_error_oom(session); - ssh_buffer_free(s->in_buffer); - SAFE_FREE(s); - return NULL; - } - s->read_wontblock = 0; - s->write_wontblock = 0; - s->data_except = 0; - s->poll_in=s->poll_out=NULL; - s->state=SSH_SOCKET_NONE; - return s; -} - -/** - * @internal - * @brief Reset the state of a socket so it looks brand-new - * @param[in] s socket to rest - */ -void ssh_socket_reset(ssh_socket s){ - s->fd_in = SSH_INVALID_SOCKET; - s->fd_out= SSH_INVALID_SOCKET; - s->last_errno = -1; - s->fd_is_socket = 1; - ssh_buffer_reinit(s->in_buffer); - ssh_buffer_reinit(s->out_buffer); - s->read_wontblock = 0; - s->write_wontblock = 0; - s->data_except = 0; - s->poll_in=s->poll_out=NULL; - s->state=SSH_SOCKET_NONE; -} - -/** - * @internal - * @brief the socket callbacks, i.e. callbacks to be called - * upon a socket event. - * @param s socket to set callbacks on. - * @param callbacks a ssh_socket_callback object reference. - */ - -void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){ - s->callbacks=callbacks; -} - -/** - * @brief SSH poll callback. This callback will be used when an event - * caught on the socket. - * - * @param p Poll object this callback belongs to. - * @param fd The raw socket. - * @param revents The current poll events on the socket. - * @param userdata Userdata to be passed to the callback function, - * in this case the socket object. - * - * @return 0 on success, < 0 when the poll object has been removed - * from its poll context. - */ -int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, - int revents, void *v_s) { - ssh_socket s = (ssh_socket)v_s; - char buffer[MAX_BUF_SIZE]; - int r; - int err = 0; - socklen_t errlen = sizeof(err); - /* Do not do anything if this socket was already closed */ - if (!ssh_socket_is_open(s)) { - return -1; - } - if (revents & POLLERR || revents & POLLHUP) { - /* Check if we are in a connecting state */ - if (s->state == SSH_SOCKET_CONNECTING) { - s->state = SSH_SOCKET_ERROR; - r = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); - if (r < 0) { - err = errno; - } - s->last_errno = err; - ssh_socket_close(s); - if (s->callbacks && s->callbacks->connected) { - s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR, err, - s->callbacks->userdata); - } - return -1; - } - /* Then we are in a more standard kind of error */ - /* force a read to get an explanation */ - revents |= POLLIN; - } - if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) { - s->read_wontblock = 1; - r = ssh_socket_unbuffered_read(s, buffer, sizeof(buffer)); - if (r < 0) { - if (p != NULL) { - ssh_poll_remove_events(p, POLLIN); - } - if (s->callbacks && s->callbacks->exception) { - s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR, - s->last_errno, s->callbacks->userdata); - /* p may have been freed, so don't use it - * anymore in this function */ - p = NULL; - return -2; - } - } - if (r == 0) { - if (p != NULL) { - ssh_poll_remove_events(p, POLLIN); - } - if (p != NULL) { - ssh_poll_remove_events(p, POLLIN); - } - if (s->callbacks && s->callbacks->exception) { - s->callbacks->exception(SSH_SOCKET_EXCEPTION_EOF, - 0, s->callbacks->userdata); - /* p may have been freed, so don't use it - * anymore in this function */ - p = NULL; - return -2; - } - } - if (r > 0) { - if (s->session->socket_counter != NULL) { - s->session->socket_counter->in_bytes += r; - } - /* Bufferize the data and then call the callback */ - r = ssh_buffer_add_data(s->in_buffer, buffer, r); - if (r < 0) { - return -1; - } - if (s->callbacks && s->callbacks->data) { - do { - r = s->callbacks->data(buffer_get_rest(s->in_buffer), - buffer_get_rest_len(s->in_buffer), - s->callbacks->userdata); - buffer_pass_bytes(s->in_buffer, r); - } while ((r > 0) && (s->state == SSH_SOCKET_CONNECTED)); - /* p may have been freed, so don't use it - * anymore in this function */ - p = NULL; - } - } - } -#ifdef _WIN32 - if (revents & POLLOUT || revents & POLLWRNORM) { -#else - if (revents & POLLOUT) { -#endif - /* First, POLLOUT is a sign we may be connected */ - if (s->state == SSH_SOCKET_CONNECTING) { - SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state"); - s->state = SSH_SOCKET_CONNECTED; - if (p != NULL) { - ssh_poll_set_events(p, POLLOUT | POLLIN); - } - r = ssh_socket_set_blocking(ssh_socket_get_fd_in(s)); - if (r < 0) { - return -1; - } - if (s->callbacks && s->callbacks->connected) { - s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, - s->callbacks->userdata); - } - return 0; - } - /* So, we can write data */ - s->write_wontblock=1; - if (p != NULL) { - ssh_poll_remove_events(p, POLLOUT); - } - - /* If buffered data is pending, write it */ - if (buffer_get_rest_len(s->out_buffer) > 0) { - ssh_socket_nonblocking_flush(s); - } else if (s->callbacks && s->callbacks->controlflow) { - /* Otherwise advertise the upper level that write can be done */ - s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK, - s->callbacks->userdata); - } - /* TODO: Find a way to put back POLLOUT when buffering occurs */ - } - /* Return -1 if one of the poll handlers disappeared */ - return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0; -} - -/** @internal - * @brief returns the input poll handle corresponding to the socket, - * creates it if it does not exist. - * @returns allocated and initialized ssh_poll_handle object - */ -ssh_poll_handle ssh_socket_get_poll_handle_in(ssh_socket s){ - if(s->poll_in) - return s->poll_in; - s->poll_in=ssh_poll_new(s->fd_in,0,ssh_socket_pollcallback,s); - if(s->fd_in == s->fd_out && s->poll_out == NULL) - s->poll_out=s->poll_in; - return s->poll_in; -} - -/** @internal - * @brief returns the output poll handle corresponding to the socket, - * creates it if it does not exist. - * @returns allocated and initialized ssh_poll_handle object - */ -ssh_poll_handle ssh_socket_get_poll_handle_out(ssh_socket s){ - if(s->poll_out) - return s->poll_out; - s->poll_out=ssh_poll_new(s->fd_out,0,ssh_socket_pollcallback,s); - if(s->fd_in == s->fd_out && s->poll_in == NULL) - s->poll_in=s->poll_out; - return s->poll_out; -} - -/** \internal - * \brief Deletes a socket object - */ -void ssh_socket_free(ssh_socket s){ - if (s == NULL) { - return; - } - ssh_socket_close(s); - ssh_buffer_free(s->in_buffer); - ssh_buffer_free(s->out_buffer); - SAFE_FREE(s); -} - -#ifndef _WIN32 -int ssh_socket_unix(ssh_socket s, const char *path) { - struct sockaddr_un sunaddr; - socket_t fd; - sunaddr.sun_family = AF_UNIX; - snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path); - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd == SSH_INVALID_SOCKET) { - ssh_set_error(s->session, SSH_FATAL, - "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s", - strerror(errno)); - return -1; - } - - if (fcntl(fd, F_SETFD, 1) == -1) { - ssh_set_error(s->session, SSH_FATAL, - "Error from fcntl(fd, F_SETFD, 1): %s", - strerror(errno)); - close(fd); - return -1; - } - - if (connect(fd, (struct sockaddr *) &sunaddr, - sizeof(sunaddr)) < 0) { - ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s", - strerror(errno)); - close(fd); - return -1; - } - ssh_socket_set_fd(s,fd); - return 0; -} -#endif - -/** \internal - * \brief closes a socket - */ -void ssh_socket_close(ssh_socket s){ - if (ssh_socket_is_open(s)) { -#ifdef _WIN32 - closesocket(s->fd_in); - /* fd_in = fd_out under win32 */ - s->last_errno = WSAGetLastError(); -#else - close(s->fd_in); - if(s->fd_out != s->fd_in && s->fd_out != -1) - close(s->fd_out); - s->last_errno = errno; -#endif - s->fd_in = s->fd_out = SSH_INVALID_SOCKET; - } - if(s->poll_in != NULL){ - if(s->poll_out == s->poll_in) - s->poll_out = NULL; - ssh_poll_free(s->poll_in); - s->poll_in=NULL; - } - if(s->poll_out != NULL){ - ssh_poll_free(s->poll_out); - s->poll_out=NULL; - } - - s->state = SSH_SOCKET_CLOSED; -} - -/** - * @internal - * @brief sets the file descriptor of the socket. - * @param[out] s ssh_socket to update - * @param[in] fd file descriptor to set - * @warning this function updates boths the input and output - * file descriptors - */ -void ssh_socket_set_fd(ssh_socket s, socket_t fd) { - s->fd_in = s->fd_out = fd; - - if (s->poll_in) { - ssh_poll_set_fd(s->poll_in,fd); - } else { - s->state = SSH_SOCKET_CONNECTING; - - /* POLLOUT is the event to wait for in a nonblocking connect */ - ssh_poll_set_events(ssh_socket_get_poll_handle_in(s), POLLOUT); -#ifdef _WIN32 - ssh_poll_add_events(ssh_socket_get_poll_handle_in(s), POLLWRNORM); -#endif - } -} - -/** - * @internal - * @brief sets the input file descriptor of the socket. - * @param[out] s ssh_socket to update - * @param[in] fd file descriptor to set - */ -void ssh_socket_set_fd_in(ssh_socket s, socket_t fd) { - s->fd_in = fd; - if(s->poll_in) - ssh_poll_set_fd(s->poll_in,fd); -} - -/** - * @internal - * @brief sets the output file descriptor of the socket. - * @param[out] s ssh_socket to update - * @param[in] fd file descriptor to set - */ -void ssh_socket_set_fd_out(ssh_socket s, socket_t fd) { - s->fd_out = fd; - if(s->poll_out) - ssh_poll_set_fd(s->poll_out,fd); -} - - - -/** \internal - * \brief returns the input file descriptor of the socket - */ -socket_t ssh_socket_get_fd_in(ssh_socket s) { - return s->fd_in; -} - -/** \internal - * \brief returns nonzero if the socket is open - */ -int ssh_socket_is_open(ssh_socket s) { - return s->fd_in != SSH_INVALID_SOCKET; -} - -/** \internal - * \brief read len bytes from socket into buffer - */ -static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len) { - int rc = -1; - - if (s->data_except) { - return -1; - } - if(s->fd_is_socket) - rc = recv(s->fd_in,buffer, len, 0); - else - rc = read(s->fd_in,buffer, len); -#ifdef _WIN32 - s->last_errno = WSAGetLastError(); -#else - s->last_errno = errno; -#endif - s->read_wontblock = 0; - - if (rc < 0) { - s->data_except = 1; - } - - return rc; -} - -/** \internal - * \brief writes len bytes from buffer to socket - */ -static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, - uint32_t len) { - int w = -1; - - if (s->data_except) { - return -1; - } - if (s->fd_is_socket) - w = send(s->fd_out,buffer, len, 0); - else - w = write(s->fd_out, buffer, len); -#ifdef _WIN32 - s->last_errno = WSAGetLastError(); -#else - s->last_errno = errno; -#endif - s->write_wontblock = 0; - /* Reactive the POLLOUT detector in the poll multiplexer system */ - if(s->poll_out){ - SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket"); - ssh_poll_set_events(s->poll_out,ssh_poll_get_events(s->poll_out) | POLLOUT); - } - if (w < 0) { - s->data_except = 1; - } - - return w; -} - -/** \internal - * \brief returns nonzero if the current socket is in the fd_set - */ -int ssh_socket_fd_isset(ssh_socket s, fd_set *set) { - if(s->fd_in == SSH_INVALID_SOCKET) { - return 0; - } - return FD_ISSET(s->fd_in,set) || FD_ISSET(s->fd_out,set); -} - -/** \internal - * \brief sets the current fd in a fd_set and updates the max_fd - */ - -void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) { - if (s->fd_in == SSH_INVALID_SOCKET) { - return; - } - - FD_SET(s->fd_in,set); - FD_SET(s->fd_out,set); - - if (s->fd_in >= 0 && - s->fd_in >= *max_fd && - s->fd_in != SSH_INVALID_SOCKET) { - *max_fd = s->fd_in + 1; - } - if (s->fd_out >= 0 && - s->fd_out >= *max_fd && - s->fd_out != SSH_INVALID_SOCKET) { - *max_fd = s->fd_out + 1; - } -} - -/** \internal - * \brief buffered write of data - * \returns SSH_OK, or SSH_ERROR - * \warning has no effect on socket before a flush - */ -int ssh_socket_write(ssh_socket s, const void *buffer, int len) { - if(len > 0) { - if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) { - ssh_set_error_oom(s->session); - return SSH_ERROR; - } - ssh_socket_nonblocking_flush(s); - } - - return SSH_OK; -} - - -/** \internal - * \brief starts a nonblocking flush of the output buffer - * - */ -int ssh_socket_nonblocking_flush(ssh_socket s) { - ssh_session session = s->session; - uint32_t len; - int w; - - if (!ssh_socket_is_open(s)) { - session->alive = 0; - /* FIXME use ssh_socket_get_errno */ - ssh_set_error(session, SSH_FATAL, - "Writing packet: error on socket (or connection closed): %s", - strerror(s->last_errno)); - - return SSH_ERROR; - } - - len = buffer_get_rest_len(s->out_buffer); - if (!s->write_wontblock && s->poll_out && len > 0) { - /* force the poll system to catch pollout events */ - ssh_poll_add_events(s->poll_out, POLLOUT); - - return SSH_AGAIN; - } - if (s->write_wontblock && len > 0) { - w = ssh_socket_unbuffered_write(s, buffer_get_rest(s->out_buffer), len); - if (w < 0) { - session->alive = 0; - ssh_socket_close(s); - /* FIXME use ssh_socket_get_errno() */ - /* FIXME use callback for errors */ - ssh_set_error(session, SSH_FATAL, - "Writing packet: error on socket (or connection closed): %s", - strerror(s->last_errno)); - - return SSH_ERROR; - } - buffer_pass_bytes(s->out_buffer, w); - if (s->session->socket_counter != NULL) { - s->session->socket_counter->out_bytes += w; - } - } - - /* Is there some data pending? */ - len = buffer_get_rest_len(s->out_buffer); - if (s->poll_out && len > 0) { - /* force the poll system to catch pollout events */ - ssh_poll_add_events(s->poll_out, POLLOUT); - - return SSH_AGAIN; - } - - /* all data written */ - return SSH_OK; -} - -void ssh_socket_set_write_wontblock(ssh_socket s) { - s->write_wontblock = 1; -} - -void ssh_socket_set_read_wontblock(ssh_socket s) { - s->read_wontblock = 1; -} - -void ssh_socket_set_except(ssh_socket s) { - s->data_except = 1; -} - -int ssh_socket_data_available(ssh_socket s) { - return s->read_wontblock; -} - -int ssh_socket_data_writable(ssh_socket s) { - return s->write_wontblock; -} - -/** @internal - * @brief returns the number of outgoing bytes currently buffered - * @param s the socket - * @returns numbers of bytes buffered, or 0 if the socket isn't connected - */ -int ssh_socket_buffered_write_bytes(ssh_socket s){ - if(s==NULL || s->out_buffer == NULL) - return 0; - return buffer_get_rest_len(s->out_buffer); -} - - -int ssh_socket_get_status(ssh_socket s) { - int r = 0; - - if (ssh_buffer_get_len(s->in_buffer) > 0) { - r |= SSH_READ_PENDING; - } - - if (ssh_buffer_get_len(s->out_buffer) > 0) { - r |= SSH_WRITE_PENDING; - } - - if (s->data_except) { - r |= SSH_CLOSED_ERROR; - } - - return r; -} - -int ssh_socket_get_poll_flags(ssh_socket s) { - int r = 0; - if (s->poll_in != NULL && (ssh_poll_get_events (s->poll_in) & POLLIN) > 0) { - r |= SSH_READ_PENDING; - } - if (s->poll_out != NULL && (ssh_poll_get_events (s->poll_out) & POLLOUT) > 0) { - r |= SSH_WRITE_PENDING; - } - return r; -} - -#ifdef _WIN32 -int ssh_socket_set_nonblocking(socket_t fd) { - u_long nonblocking = 1; - return ioctlsocket(fd, FIONBIO, &nonblocking); -} - -int ssh_socket_set_blocking(socket_t fd) { - u_long nonblocking = 0; - return ioctlsocket(fd, FIONBIO, &nonblocking); -} - -#else /* _WIN32 */ -int ssh_socket_set_nonblocking(socket_t fd) { - return fcntl(fd, F_SETFL, O_NONBLOCK); -} - -int ssh_socket_set_blocking(socket_t fd) { - return fcntl(fd, F_SETFL, 0); -} -#endif /* _WIN32 */ - -/** - * @internal - * @brief Launches a socket connection - * If a the socket connected callback has been defined and - * a poll object exists, this call will be non blocking. - * @param s socket to connect. - * @param host hostname or ip address to connect to. - * @param port port number to connect to. - * @param bind_addr address to bind to, or NULL for default. - * @returns SSH_OK socket is being connected. - * @returns SSH_ERROR error while connecting to remote host. - * @bug It only tries connecting to one of the available AI's - * which is problematic for hosts having DNS fail-over. - */ - -int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr){ - socket_t fd; - - if(s->state != SSH_SOCKET_NONE) { - ssh_set_error(s->session, SSH_FATAL, - "ssh_socket_connect called on socket not unconnected"); - return SSH_ERROR; - } - fd=ssh_connect_host_nonblocking(s->session,host,bind_addr,port); - SSH_LOG(SSH_LOG_PROTOCOL,"Nonblocking connection socket: %d",fd); - if(fd == SSH_INVALID_SOCKET) - return SSH_ERROR; - ssh_socket_set_fd(s,fd); - - return SSH_OK; -} - -#ifndef _WIN32 -/** - * @internal - * @brief executes a command and redirect input and outputs - * @param command command to execute - * @param in input file descriptor - * @param out output file descriptor - */ -void ssh_execute_command(const char *command, socket_t in, socket_t out){ - const char *args[]={"/bin/sh","-c",command,NULL}; - /* redirect in and out to stdin, stdout and stderr */ - dup2(in, 0); - dup2(out,1); - dup2(out,2); - close(in); - close(out); - execv(args[0],(char * const *)args); - exit(1); -} - -/** - * @internal - * @brief Open a socket on a ProxyCommand - * This call will always be nonblocking. - * @param s socket to connect. - * @param command Command to execute. - * @returns SSH_OK socket is being connected. - * @returns SSH_ERROR error while executing the command. - */ - -int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){ - socket_t in_pipe[2]; - socket_t out_pipe[2]; - int pid; - int rc; - - if(s->state != SSH_SOCKET_NONE) - return SSH_ERROR; - - rc = pipe(in_pipe); - if (rc < 0) { - return SSH_ERROR; - } - rc = pipe(out_pipe); - if (rc < 0) { - return SSH_ERROR; - } - - SSH_LOG(SSH_LOG_PROTOCOL,"Executing proxycommand '%s'",command); - pid = fork(); - if(pid == 0){ - ssh_execute_command(command,out_pipe[0],in_pipe[1]); - } - close(in_pipe[1]); - close(out_pipe[0]); - SSH_LOG(SSH_LOG_PROTOCOL,"ProxyCommand connection pipe: [%d,%d]",in_pipe[0],out_pipe[1]); - ssh_socket_set_fd_in(s,in_pipe[0]); - ssh_socket_set_fd_out(s,out_pipe[1]); - s->state=SSH_SOCKET_CONNECTED; - s->fd_is_socket=0; - /* POLLOUT is the event to wait for in a nonblocking connect */ - ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLIN); - ssh_poll_set_events(ssh_socket_get_poll_handle_out(s),POLLOUT); - if(s->callbacks && s->callbacks->connected) - s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); - - return SSH_OK; -} - -#endif /* _WIN32 */ -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/string.c b/libssh/src/string.c deleted file mode 100644 index 9002478f..00000000 --- a/libssh/src/string.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * string.c - ssh string functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2008 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#include -#endif - -#include "libssh/priv.h" -#include "libssh/string.h" - -/** - * @defgroup libssh_string The SSH string functions - * @ingroup libssh - * - * @brief String manipulations used in libssh. - * - * @{ - */ - -/** - * @brief Create a new SSH String object. - * - * @param[in] size The size of the string. - * - * @return The newly allocated string, NULL on error. - */ -struct ssh_string_struct *ssh_string_new(size_t size) { - struct ssh_string_struct *str = NULL; - - if (size > UINT_MAX - sizeof(struct ssh_string_struct)) { - return NULL; - } - - str = malloc(sizeof(struct ssh_string_struct) + size); - if (str == NULL) { - return NULL; - } - - str->size = htonl(size); - str->data[0] = 0; - - return str; -} - -/** - * @brief Fill a string with given data. The string should be big enough. - * - * @param s An allocated string to fill with data. - * - * @param data The data to fill the string with. - * - * @param len Size of data. - * - * @return 0 on success, < 0 on error. - */ -int ssh_string_fill(struct ssh_string_struct *s, const void *data, size_t len) { - if ((s == NULL) || (data == NULL) || - (len == 0) || (len > ssh_string_len(s))) { - return -1; - } - - memcpy(s->data, data, len); - - return 0; -} - -/** - * @brief Create a ssh string using a C string - * - * @param[in] what The source 0-terminated C string. - * - * @return The newly allocated string, NULL on error with errno - * set. - * - * @note The nul byte is not copied nor counted in the ouput string. - */ -struct ssh_string_struct *ssh_string_from_char(const char *what) { - struct ssh_string_struct *ptr; - size_t len; - - if(what == NULL) { - errno = EINVAL; - return NULL; - } - - len = strlen(what); - - ptr = ssh_string_new(len); - if (ptr == NULL) { - return NULL; - } - - memcpy(ptr->data, what, len); - - return ptr; -} - -/** - * @brief Return the size of a SSH string. - * - * @param[in] s The the input SSH string. - * - * @return The size of the content of the string, 0 on error. - */ -size_t ssh_string_len(struct ssh_string_struct *s) { - if (s == NULL) { - return ntohl(0); - } - - return ntohl(s->size); -} - -/** - * @brief Get the the string as a C nul-terminated string. - * - * This is only available as long as the SSH string exists. - * - * @param[in] s The SSH string to get the C string from. - * - * @return The char pointer, NULL on error. - */ -const char *ssh_string_get_char(struct ssh_string_struct *s) -{ - if (s == NULL) { - return NULL; - } - s->data[ssh_string_len(s)] = '\0'; - - return (const char *) s->data; -} - -/** - * @brief Convert a SSH string to a C nul-terminated string. - * - * @param[in] s The SSH input string. - * - * @return An allocated string pointer, NULL on error with errno - * set. - * - * @note If the input SSH string contains zeroes, some parts of the output - * string may not be readable with regular libc functions. - */ -char *ssh_string_to_char(struct ssh_string_struct *s) { - size_t len; - char *new; - - if (s == NULL) { - return NULL; - } - - len = ssh_string_len(s); - if (len + 1 < len) { - return NULL; - } - - new = malloc(len + 1); - if (new == NULL) { - return NULL; - } - memcpy(new, s->data, len); - new[len] = '\0'; - - return new; -} - -/** - * @brief Deallocate a char string object. - * - * @param[in] s The string to delete. - */ -void ssh_string_free_char(char *s) { - SAFE_FREE(s); -} - -/** - * @brief Copy a string, return a newly allocated string. The caller has to - * free the string. - * - * @param[in] s String to copy. - * - * @return Newly allocated copy of the string, NULL on error. - */ -struct ssh_string_struct *ssh_string_copy(struct ssh_string_struct *s) { - struct ssh_string_struct *new; - size_t len; - - if (s == NULL) { - return NULL; - } - - len = ssh_string_len(s); - if (len == 0) { - return NULL; - } - - new = ssh_string_new(len); - if (new == NULL) { - return NULL; - } - - memcpy(new->data, s->data, len); - - return new; -} - -/** - * @brief Destroy the data in a string so it couldn't appear in a core dump. - * - * @param[in] s The string to burn. - */ -void ssh_string_burn(struct ssh_string_struct *s) { - if (s == NULL || s->size == 0) { - return; - } - - BURN_BUFFER(s->data, ssh_string_len(s)); -} - -/** - * @brief Get the payload of the string. - * - * @param s The string to get the data from. - * - * @return Return the data of the string or NULL on error. - */ -void *ssh_string_data(struct ssh_string_struct *s) { - if (s == NULL) { - return NULL; - } - - return s->data; -} - -/** - * @brief Deallocate a SSH string object. - * - * \param[in] s The SSH string to delete. - */ -void ssh_string_free(struct ssh_string_struct *s) { - SAFE_FREE(s); -} - -/** @} */ - -/* vim: set ts=4 sw=4 et cindent: */ diff --git a/libssh/src/threads.c b/libssh/src/threads.c deleted file mode 100644 index 7f3a304e..00000000 --- a/libssh/src/threads.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/** - * @defgroup libssh_threads The SSH threading functions. - * @ingroup libssh - * - * Threading with libssh - * @{ - */ - -#include "config.h" - -#include "libssh/priv.h" -#include "libssh/crypto.h" -#include "libssh/threads.h" - -static int threads_noop (void **lock){ - (void)lock; - return 0; -} - -static unsigned long threads_id_noop (void){ - return 1; -} - -static struct ssh_threads_callbacks_struct ssh_threads_noop = -{ - "threads_noop", - threads_noop, - threads_noop, - threads_noop, - threads_noop, - threads_id_noop -}; - -struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void) { - return &ssh_threads_noop; -} - -static struct ssh_threads_callbacks_struct *user_callbacks =&ssh_threads_noop; - -#ifdef HAVE_LIBGCRYPT -#if (GCRYPT_VERSION_NUMBER >= 0x010600) -/* libgcrypt >= 1.6 does not support custom callbacks */ -GCRY_THREAD_OPTION_PTHREAD_IMPL; - -static int libgcrypt_thread_init(void){ - if(user_callbacks == NULL) - return SSH_ERROR; - if(user_callbacks == &ssh_threads_noop) - return SSH_OK; - if (strcmp(user_callbacks->type, "threads_pthread") == 0){ - gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); - return SSH_OK; - } else { - /* not supported */ - SSH_LOG(SSH_LOG_WARN, "Custom thread handlers not supported with libgcrypt >=1.6, using pthreads"); - gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); - return SSH_OK; - } -} - -#else -/* Libgcrypt < 1.6 specific way of handling thread callbacks */ - -static struct gcry_thread_cbs gcrypt_threads_callbacks; - -static int libgcrypt_thread_init(void){ - if(user_callbacks == NULL) - return SSH_ERROR; - if(user_callbacks == &ssh_threads_noop){ - gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || GCRY_THREAD_OPTION_DEFAULT; - } else { - gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || GCRY_THREAD_OPTION_USER; - } - gcrypt_threads_callbacks.mutex_init=user_callbacks->mutex_init; - gcrypt_threads_callbacks.mutex_destroy=user_callbacks->mutex_destroy; - gcrypt_threads_callbacks.mutex_lock=user_callbacks->mutex_lock; - gcrypt_threads_callbacks.mutex_unlock=user_callbacks->mutex_unlock; - gcry_control(GCRYCTL_SET_THREAD_CBS, &gcrypt_threads_callbacks); - return SSH_OK; -} -#endif /* GCRYPT_VERSION_NUMBER */ -#else /* HAVE_LIBGCRYPT */ - -/* Libcrypto specific stuff */ - -static void **libcrypto_mutexes; - -static void libcrypto_lock_callback(int mode, int i, const char *file, int line){ - (void)file; - (void)line; - if(mode & CRYPTO_LOCK){ - user_callbacks->mutex_lock(&libcrypto_mutexes[i]); - } else { - user_callbacks->mutex_unlock(&libcrypto_mutexes[i]); - } -} - -static int libcrypto_thread_init(void){ - int n=CRYPTO_num_locks(); - int i; - if(user_callbacks == &ssh_threads_noop) - return SSH_OK; - libcrypto_mutexes=malloc(sizeof(void *) * n); - if (libcrypto_mutexes == NULL) - return SSH_ERROR; - for (i=0;imutex_init(&libcrypto_mutexes[i]); - } - CRYPTO_set_id_callback(user_callbacks->thread_id); - CRYPTO_set_locking_callback(libcrypto_lock_callback); - - return SSH_OK; -} - -static void libcrypto_thread_finalize(void){ - int n=CRYPTO_num_locks(); - int i; - if (libcrypto_mutexes==NULL) - return; - for (i=0;imutex_destroy(&libcrypto_mutexes[i]); - } - SAFE_FREE(libcrypto_mutexes); - -} - -#endif - -/** @internal - * @brief inits the threading with the backend cryptographic libraries - */ - -int ssh_threads_init(void){ - static int threads_initialized=0; - int ret; - if(threads_initialized) - return SSH_OK; - /* first initialize the user_callbacks with our default handlers if not - * already the case - */ - if(user_callbacks == NULL){ - user_callbacks=&ssh_threads_noop; - } - - /* Then initialize the crypto libraries threading callbacks */ -#ifdef HAVE_LIBGCRYPT - ret = libgcrypt_thread_init(); -#else /* Libcrypto */ - ret = libcrypto_thread_init(); -#endif - if(ret == SSH_OK) - threads_initialized=1; - return ret; -} - -void ssh_threads_finalize(void){ -#ifdef HAVE_LIBGCRYPT -#else - libcrypto_thread_finalize(); -#endif -} - -int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb){ - user_callbacks=cb; - return SSH_OK; -} - -const char *ssh_threads_get_type(void) { - if(user_callbacks != NULL) - return user_callbacks->type; - return NULL; -} - -/** - * @} - */ diff --git a/libssh/src/threads/CMakeLists.txt b/libssh/src/threads/CMakeLists.txt deleted file mode 100644 index a32d601e..00000000 --- a/libssh/src/threads/CMakeLists.txt +++ /dev/null @@ -1,127 +0,0 @@ -project(libssh-threads C) - -set(LIBSSH_THREADS_PUBLIC_INCLUDE_DIRS - ${CMAKE_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_SOURCE_DIR} - CACHE INTERNAL "libssh public include directories" -) - -set(LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS - ${CMAKE_BINARY_DIR} -) - -set(LIBSSH_THREADS_SHARED_LIBRARY - ssh_threads_shared - CACHE INTERNAL "libssh threads shared library" -) - -if (WITH_STATIC_LIB) - set(LIBSSH_THREADS_STATIC_LIBRARY - ssh_threads_static - CACHE INTERNAL "libssh threads static library" - ) -endif (WITH_STATIC_LIB) - -set(LIBSSH_THREADS_LINK_LIBRARIES - ${LIBSSH_SHARED_LIBRARY} -) - -set(libssh_threads_SRCS -) - -# build and link pthread -if (CMAKE_USE_PTHREADS_INIT) - set(libssh_threads_SRCS - ${libssh_threads_SRCS} - pthread.c - ) - - set(LIBSSH_THREADS_LINK_LIBRARIES - ${LIBSSH_THREADS_LINK_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} - ) -endif (CMAKE_USE_PTHREADS_INIT) - -set(LIBSSH_THREADS_LINK_LIBRARIES - ${LIBSSH_THREADS_LINK_LIBRARIES} - CACHE INTERNAL "libssh threads link libraries" -) - -include_directories( - ${LIBSSH_THREADS_PUBLIC_INCLUDE_DIRS} - ${LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS} -) - -if (libssh_threads_SRCS) - add_library(${LIBSSH_THREADS_SHARED_LIBRARY} SHARED ${libssh_threads_SRCS}) - - target_link_libraries(${LIBSSH_THREADS_SHARED_LIBRARY} ${LIBSSH_THREADS_LINK_LIBRARIES}) - - set_target_properties( - ${LIBSSH_THREADS_SHARED_LIBRARY} - PROPERTIES - VERSION - ${LIBRARY_VERSION} - SOVERSION - ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh_threads - DEFINE_SYMBOL - LIBSSH_EXPORTS - ) - - if (WITH_VISIBILITY_HIDDEN) - set_target_properties(${LIBSSH_THREADS_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") - endif (WITH_VISIBILITY_HIDDEN) - - install( - TARGETS - ${LIBSSH_THREADS_SHARED_LIBRARY} - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - COMPONENT libraries - ) - - if (WITH_STATIC_LIB) - add_library(${LIBSSH_THREADS_STATIC_LIBRARY} STATIC ${libssh_threads_SRCS}) - - if (MSVC) - set(OUTPUT_SUFFIX static) - else (MSVC) - set(OUTPUT_SUFFIX ) - endif (MSVC) - - set_target_properties( - ${LIBSSH_THREADS_STATIC_LIBRARY} - PROPERTIES - VERSION - ${LIBRARY_VERSION} - SOVERSION - ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh_threads - ARCHIVE_OUTPUT_DIRECTORY - ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX} - ) - - if (WIN32) - set_target_properties( - ${LIBSSH_THREADS_STATIC_LIBRARY} - PROPERTIES - COMPILE_FLAGS - "-DLIBSSH_STATIC" - ) - endif (WIN32) - - install( - TARGETS - ${LIBSSH_THREADS_STATIC_LIBRARY} - DESTINATION - ${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX} - COMPONENT - libraries - ) - endif (WITH_STATIC_LIB) -endif (libssh_threads_SRCS) diff --git a/libssh/src/threads/pthread.c b/libssh/src/threads/pthread.c deleted file mode 100644 index 829fa5c6..00000000 --- a/libssh/src/threads/pthread.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" -#include - -#ifdef HAVE_PTHREAD - -#include -#include -#include - -/** @brief Defines the needed callbacks for pthread. Use this if your - * OS supports libpthread and want to use it for threading. - * @code - * #include - * #include - * #include - * SSH_THREADS_PTHREAD(ssh_pthread_callbacks); - * int main(){ - * ssh_init_set_threads_callbacks(&ssh_pthread_callbacks); - * ssh_init(); - * ... - * } - * @endcode - * @param name name of the structure to be declared, containing the - * callbacks for threading - * - */ - -static int ssh_pthread_mutex_init (void **priv){ - int err = 0; - *priv = malloc (sizeof (pthread_mutex_t)); - if (*priv==NULL) - return ENOMEM; - err = pthread_mutex_init (*priv, NULL); - if (err != 0){ - free (*priv); - *priv=NULL; - } - return err; -} - -static int ssh_pthread_mutex_destroy (void **lock) { - int err = pthread_mutex_destroy (*lock); - free (*lock); - *lock=NULL; - return err; -} - -static int ssh_pthread_mutex_lock (void **lock) { - return pthread_mutex_lock (*lock); -} - -static int ssh_pthread_mutex_unlock (void **lock){ - return pthread_mutex_unlock (*lock); -} - -static unsigned long ssh_pthread_thread_id (void){ -#if _WIN32 - return (unsigned long) pthread_self().p; -#else - return (unsigned long) pthread_self(); -#endif -} - -static struct ssh_threads_callbacks_struct ssh_threads_pthread = -{ - .type="threads_pthread", - .mutex_init=ssh_pthread_mutex_init, - .mutex_destroy=ssh_pthread_mutex_destroy, - .mutex_lock=ssh_pthread_mutex_lock, - .mutex_unlock=ssh_pthread_mutex_unlock, - .thread_id=ssh_pthread_thread_id -}; - -struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void) { - return &ssh_threads_pthread; -} - -#endif /* HAVE_PTHREAD */ diff --git a/libssh/src/wrapper.c b/libssh/src/wrapper.c deleted file mode 100644 index bcd941b3..00000000 --- a/libssh/src/wrapper.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * wrapper.c - wrapper for crytpo functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2003-2013 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/* - * Why a wrapper? - * - * Let's say you want to port libssh from libcrypto of openssl to libfoo - * you are going to spend hours to remove every references to SHA1_Update() - * to libfoo_sha1_update after the work is finished, you're going to have - * only this file to modify it's not needed to say that your modifications - * are welcome. - */ - -#include "config.h" - - -#include -#include -#include - -#ifdef WITH_ZLIB -#include -#endif - -#include "libssh/priv.h" -#include "libssh/session.h" -#include "libssh/crypto.h" -#include "libssh/wrapper.h" -#include "libssh/pki.h" - -static struct ssh_hmac_struct ssh_hmac_tab[] = { - { "hmac-sha1", SSH_HMAC_SHA1 }, - { "hmac-sha2-256", SSH_HMAC_SHA256 }, - { "hmac-sha2-384", SSH_HMAC_SHA384 }, - { "hmac-sha2-512", SSH_HMAC_SHA512 }, - { "hmac-md5", SSH_HMAC_MD5 }, - { NULL, 0} -}; - -struct ssh_hmac_struct *ssh_get_hmactab(void) { - return ssh_hmac_tab; -} - -size_t hmac_digest_len(enum ssh_hmac_e type) { - switch(type) { - case SSH_HMAC_SHA1: - return SHA_DIGEST_LEN; - case SSH_HMAC_SHA256: - return SHA256_DIGEST_LEN; - case SSH_HMAC_SHA384: - return SHA384_DIGEST_LEN; - case SSH_HMAC_SHA512: - return SHA512_DIGEST_LEN; - case SSH_HMAC_MD5: - return MD5_DIGEST_LEN; - default: - return 0; - } -} - -const char *ssh_hmac_type_to_string(enum ssh_hmac_e hmac_type) -{ - int i = 0; - struct ssh_hmac_struct *ssh_hmactab = ssh_get_hmactab(); - while (ssh_hmactab[i].name && (ssh_hmactab[i].hmac_type != hmac_type)) { - i++; - } - return ssh_hmactab[i].name; -} - -/* it allocates a new cipher structure based on its offset into the global table */ -static struct ssh_cipher_struct *cipher_new(int offset) { - struct ssh_cipher_struct *cipher = NULL; - - cipher = malloc(sizeof(struct ssh_cipher_struct)); - if (cipher == NULL) { - return NULL; - } - - /* note the memcpy will copy the pointers : so, you shouldn't free them */ - memcpy(cipher, &ssh_get_ciphertab()[offset], sizeof(*cipher)); - - return cipher; -} - -static void cipher_free(struct ssh_cipher_struct *cipher) { -#ifdef HAVE_LIBGCRYPT - unsigned int i; -#endif - - if (cipher == NULL) { - return; - } - - if(cipher->key) { -#ifdef HAVE_LIBGCRYPT - for (i = 0; i < (cipher->keylen / sizeof(gcry_cipher_hd_t)); i++) { - gcry_cipher_close(cipher->key[i]); - } -#elif defined HAVE_LIBCRYPTO - /* destroy the key */ - memset(cipher->key, 0, cipher->keylen); -#endif - SAFE_FREE(cipher->key); - } - SAFE_FREE(cipher); -} - -struct ssh_crypto_struct *crypto_new(void) { - struct ssh_crypto_struct *crypto; - - crypto = malloc(sizeof(struct ssh_crypto_struct)); - if (crypto == NULL) { - return NULL; - } - ZERO_STRUCTP(crypto); - return crypto; -} - -void crypto_free(struct ssh_crypto_struct *crypto){ - int i; - if (crypto == NULL) { - return; - } - - SAFE_FREE(crypto->server_pubkey); - - cipher_free(crypto->in_cipher); - cipher_free(crypto->out_cipher); - - bignum_free(crypto->e); - bignum_free(crypto->f); - bignum_free(crypto->x); - bignum_free(crypto->y); - bignum_free(crypto->k); -#ifdef HAVE_ECDH - SAFE_FREE(crypto->ecdh_client_pubkey); - SAFE_FREE(crypto->ecdh_server_pubkey); -#endif - if(crypto->session_id != NULL){ - memset(crypto->session_id, '\0', crypto->digest_len); - SAFE_FREE(crypto->session_id); - } - if(crypto->secret_hash != NULL){ - memset(crypto->secret_hash, '\0', crypto->digest_len); - SAFE_FREE(crypto->secret_hash); - } -#ifdef WITH_ZLIB - if (crypto->compress_out_ctx && - (deflateEnd(crypto->compress_out_ctx) != 0)) { - inflateEnd(crypto->compress_out_ctx); - } - SAFE_FREE(crypto->compress_out_ctx); - - if (crypto->compress_in_ctx && - (deflateEnd(crypto->compress_in_ctx) != 0)) { - inflateEnd(crypto->compress_in_ctx); - } - SAFE_FREE(crypto->compress_in_ctx); -#endif /* WITH_ZLIB */ - if(crypto->encryptIV) - SAFE_FREE(crypto->encryptIV); - if(crypto->decryptIV) - SAFE_FREE(crypto->decryptIV); - if(crypto->encryptMAC) - SAFE_FREE(crypto->encryptMAC); - if(crypto->decryptMAC) - SAFE_FREE(crypto->decryptMAC); - if(crypto->encryptkey){ - memset(crypto->encryptkey, 0, crypto->digest_len); - SAFE_FREE(crypto->encryptkey); - } - if(crypto->decryptkey){ - memset(crypto->decryptkey, 0, crypto->digest_len); - SAFE_FREE(crypto->decryptkey); - } - - for (i = 0; i < SSH_KEX_METHODS; i++) { - SAFE_FREE(crypto->client_kex.methods[i]); - SAFE_FREE(crypto->server_kex.methods[i]); - SAFE_FREE(crypto->kex_methods[i]); - } - - BURN_BUFFER(crypto, sizeof(struct ssh_crypto_struct)); - - SAFE_FREE(crypto); -} - -static int crypt_set_algorithms2(ssh_session session){ - const char *wanted; - int i = 0; - struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); - struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab(); - - /* we must scan the kex entries to find crypto algorithms and set their appropriate structure */ - /* out */ - wanted = session->next_crypto->kex_methods[SSH_CRYPT_C_S]; - while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) { - i++; - } - - if (ssh_ciphertab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, - "crypt_set_algorithms2: no crypto algorithm function found for %s", - wanted); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET, "Set output algorithm to %s", wanted); - - session->next_crypto->out_cipher = cipher_new(i); - if (session->next_crypto->out_cipher == NULL) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - i = 0; - - /* we must scan the kex entries to find hmac algorithms and set their appropriate structure */ - /* out */ - wanted = session->next_crypto->kex_methods[SSH_MAC_C_S]; - while (ssh_hmactab[i].name && strcmp(wanted, ssh_hmactab[i].name)) { - i++; - } - - if (ssh_hmactab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, - "crypt_set_algorithms2: no hmac algorithm function found for %s", - wanted); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET, "Set HMAC output algorithm to %s", wanted); - - session->next_crypto->out_hmac = ssh_hmactab[i].hmac_type; - i = 0; - - /* in */ - wanted = session->next_crypto->kex_methods[SSH_CRYPT_S_C]; - while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) { - i++; - } - - if (ssh_ciphertab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, - "Crypt_set_algorithms: no crypto algorithm function found for %s", - wanted); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET, "Set input algorithm to %s", wanted); - - session->next_crypto->in_cipher = cipher_new(i); - if (session->next_crypto->in_cipher == NULL) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - i = 0; - - /* we must scan the kex entries to find hmac algorithms and set their appropriate structure */ - wanted = session->next_crypto->kex_methods[SSH_MAC_S_C]; - while (ssh_hmactab[i].name && strcmp(wanted, ssh_hmactab[i].name)) { - i++; - } - - if (ssh_hmactab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, - "crypt_set_algorithms2: no hmac algorithm function found for %s", - wanted); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET, "Set HMAC output algorithm to %s", wanted); - - session->next_crypto->in_hmac = ssh_hmactab[i].hmac_type; - i = 0; - - /* compression */ - if (strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib") == 0) { - session->next_crypto->do_compress_out = 1; - } - if (strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib") == 0) { - session->next_crypto->do_compress_in = 1; - } - if (strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib@openssh.com") == 0) { - session->next_crypto->delayed_compress_out = 1; - } - if (strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib@openssh.com") == 0) { - session->next_crypto->delayed_compress_in = 1; - } - - return SSH_OK; -} - -static int crypt_set_algorithms1(ssh_session session, enum ssh_des_e des_type) { - int i = 0; - struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); - - /* right now, we force 3des-cbc to be taken */ - while (ssh_ciphertab[i].name && strcmp(ssh_ciphertab[i].name, - des_type == SSH_DES ? "des-cbc-ssh1" : "3des-cbc-ssh1")) { - i++; - } - - if (ssh_ciphertab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, "cipher 3des-cbc-ssh1 or des-cbc-ssh1 not found!"); - return SSH_ERROR; - } - - session->next_crypto->out_cipher = cipher_new(i); - if (session->next_crypto->out_cipher == NULL) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - - session->next_crypto->in_cipher = cipher_new(i); - if (session->next_crypto->in_cipher == NULL) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - - return SSH_OK; -} - -int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type) { - return (session->version == 1) ? crypt_set_algorithms1(session, des_type) : - crypt_set_algorithms2(session); -} - -#ifdef WITH_SERVER -int crypt_set_algorithms_server(ssh_session session){ - char *method = NULL; - int i = 0; - struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); - struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab(); - - if (session == NULL) { - return SSH_ERROR; - } - - /* - * We must scan the kex entries to find crypto algorithms and set their - * appropriate structure - */ - /* out */ - method = session->next_crypto->kex_methods[SSH_CRYPT_S_C]; - while(ssh_ciphertab[i].name && strcmp(method,ssh_ciphertab[i].name)) - i++; - if(!ssh_ciphertab[i].name){ - ssh_set_error(session,SSH_FATAL,"crypt_set_algorithms_server : " - "no crypto algorithm function found for %s",method); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET,"Set output algorithm %s",method); - - session->next_crypto->out_cipher = cipher_new(i); - if (session->next_crypto->out_cipher == NULL) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - i=0; - /* in */ - method = session->next_crypto->kex_methods[SSH_CRYPT_C_S]; - while(ssh_ciphertab[i].name && strcmp(method,ssh_ciphertab[i].name)) - i++; - if(!ssh_ciphertab[i].name){ - ssh_set_error(session,SSH_FATAL,"Crypt_set_algorithms_server :" - "no crypto algorithm function found for %s",method); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET,"Set input algorithm %s",method); - - session->next_crypto->in_cipher = cipher_new(i); - if (session->next_crypto->in_cipher == NULL) { - ssh_set_error_oom(session); - return SSH_ERROR; - } - i=0; - - /* HMAC algorithm selection */ - method = session->next_crypto->kex_methods[SSH_MAC_S_C]; - while (ssh_hmactab[i].name && strcmp(method, ssh_hmactab[i].name)) { - i++; - } - - if (ssh_hmactab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, - "crypt_set_algorithms_server: no hmac algorithm function found for %s", - method); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET, "Set HMAC output algorithm to %s", method); - - session->next_crypto->out_hmac = ssh_hmactab[i].hmac_type; - i=0; - - method = session->next_crypto->kex_methods[SSH_MAC_C_S]; - while (ssh_hmactab[i].name && strcmp(method, ssh_hmactab[i].name)) { - i++; - } - - if (ssh_hmactab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, - "crypt_set_algorithms_server: no hmac algorithm function found for %s", - method); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET, "Set HMAC input algorithm to %s", method); - - session->next_crypto->in_hmac = ssh_hmactab[i].hmac_type; - i=0; - - /* compression */ - method = session->next_crypto->kex_methods[SSH_COMP_C_S]; - if(strcmp(method,"zlib") == 0){ - SSH_LOG(SSH_LOG_PACKET,"enabling C->S compression"); - session->next_crypto->do_compress_in=1; - } - if(strcmp(method,"zlib@openssh.com") == 0){ - SSH_LOG(SSH_LOG_PACKET,"enabling C->S delayed compression"); - - if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { - session->next_crypto->do_compress_in = 1; - } else { - session->next_crypto->delayed_compress_in = 1; - } - } - - method = session->next_crypto->kex_methods[SSH_COMP_S_C]; - if(strcmp(method,"zlib") == 0){ - SSH_LOG(SSH_LOG_PACKET, "enabling S->C compression\n"); - session->next_crypto->do_compress_out=1; - } - if(strcmp(method,"zlib@openssh.com") == 0){ - SSH_LOG(SSH_LOG_PACKET,"enabling S->C delayed compression\n"); - - if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { - session->next_crypto->do_compress_out = 1; - } else { - session->next_crypto->delayed_compress_out = 1; - } - } - - method = session->next_crypto->kex_methods[SSH_HOSTKEYS]; - session->srv.hostkey = ssh_key_type_from_name(method); - - return SSH_OK; -} - -#endif /* WITH_SERVER */ -/* vim: set ts=2 sw=2 et cindent: */ diff --git a/libssh/tests/CMakeLists.txt b/libssh/tests/CMakeLists.txt deleted file mode 100644 index 7cdb2c48..00000000 --- a/libssh/tests/CMakeLists.txt +++ /dev/null @@ -1,52 +0,0 @@ -project(tests C) - -if (BSD OR SOLARIS OR OSX) - find_package(Argp) -endif (BSD OR SOLARIS OR OSX) - -set(TORTURE_LIBRARY torture) - -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${CMOCKA_INCLUDE_DIR} - ${OPENSSL_INCLUDE_DIRS} - ${GCRYPT_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/src - ${CMAKE_CURRENT_SOURCE_DIR} -) - -# create test library -add_library(${TORTURE_LIBRARY} STATIC cmdline.c torture.c) -target_link_libraries(${TORTURE_LIBRARY} - ${CMOCKA_LIBRARY} - ${LIBSSH_STATIC_LIBRARY} - ${LIBSSH_LINK_LIBRARIES} - ${LIBSSH_THREADS_STATIC_LIBRARY} - ${LIBSSH_THREADS_LINK_LIBRARIES} - ${ARGP_LIBRARIES} -) - -set(TEST_TARGET_LIBRARIES - ${TORTURE_LIBRARY} - ${CMOCKA_LIBRARY} - ${LIBSSH_STATIC_LIBRARY} - ${LIBSSH_LINK_LIBRARIES} - ${LIBSSH_THREADS_STATIC_LIBRARY} - ${LIBSSH_THREADS_LINK_LIBRARIES} -) - -add_subdirectory(unittests) - -if (WITH_CLIENT_TESTING) - add_subdirectory(client) -endif (WITH_CLIENT_TESTING) - -if (WITH_BENCHMARKS) - add_subdirectory(benchmarks) -endif (WITH_BENCHMARKS) - -if (WITH_SERVER) - add_subdirectory(pkd) -endif (WITH_SERVER) diff --git a/libssh/tests/authentication.c b/libssh/tests/authentication.c deleted file mode 100644 index 248b646f..00000000 --- a/libssh/tests/authentication.c +++ /dev/null @@ -1,74 +0,0 @@ -/* -This file is distributed in public domain. You can do whatever you want -with its content. -*/ - - -#include -#include -#include -#include - -#include - -#include "tests.h" -static int auth_kbdint(SSH_SESSION *session){ - int err=ssh_userauth_kbdint(session,NULL,NULL); - char *name,*instruction,*prompt,*ptr; - char buffer[128]; - int i,n; - char echo; - while (err==SSH_AUTH_INFO){ - name=ssh_userauth_kbdint_getname(session); - instruction=ssh_userauth_kbdint_getinstruction(session); - n=ssh_userauth_kbdint_getnprompts(session); - if(strlen(name)>0) - printf("%s\n",name); - if(strlen(instruction)>0) - printf("%s\n",instruction); - for(i=0;i&1`" -echo "Ping latency to $DEST": -ping -q -c 1 -n $DEST -echo "Destination $DEST SSHD vesion : `echo | nc $DEST 22 | head -n1`" -echo "ssh login latency :`(time -f user:%U ssh $DEST 'id > /dev/null') 2>&1`" -./generate.py | dd bs=4096 count=100000 | time ssh -c $CIPHER $DEST "dd bs=4096 of=/dev/null" 2>&1 - diff --git a/libssh/tests/benchmarks/bench2.sh b/libssh/tests/benchmarks/bench2.sh deleted file mode 100755 index 01d67777..00000000 --- a/libssh/tests/benchmarks/bench2.sh +++ /dev/null @@ -1,13 +0,0 @@ -export CIPHER=aes128-cbc -export DEST=localhost - -echo "Upload raw SSH statistics" -echo "local machine: `uname -a`" -echo "Cipher : $CIPHER ; Destination : $DEST (`ssh $DEST uname -a`)" -echo "Local ssh version: `samplessh -V 2>&1`" -echo "Ping latency to $DEST": -ping -q -c 1 -n $DEST -echo "Destination $DEST SSHD vesion : `echo | nc $DEST 22 | head -n1`" -echo "ssh login latency :`(time -f user:%U samplessh $DEST 'id > /dev/null') 2>&1`" -./generate.py | dd bs=4096 count=100000 | strace samplessh -c $CIPHER $DEST "dd bs=4096 of=/dev/null" 2>&1 - diff --git a/libssh/tests/benchmarks/bench_raw.c b/libssh/tests/benchmarks/bench_raw.c deleted file mode 100644 index db1a057c..00000000 --- a/libssh/tests/benchmarks/bench_raw.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "benchmarks.h" -#include -#include -#include - -#define PYTHON_PATH "/usr/bin/python" - -const char python_eater[]= -"#!/usr/bin/python\n" -"import sys\n" -"print 'go'\n" -"sys.stdout.flush()\n" -"toread=XXXXXXXXXX\n" -"read=0\n" -"while(read < toread):\n" -" buffersize=toread-read\n" -" if(buffersize > 4096):\n" -" buffersize=4096\n" -" r=len(sys.stdin.read(buffersize))\n" -" read+=r\n" -" if(r<=0):\n" -" print 'error'\n" -" exit()\n" -"print 'done'\n"; - -static char *get_python_eater(unsigned long bytes){ - char *eater=malloc(sizeof(python_eater)); - char *ptr; - char buf[12]; - - memcpy(eater,python_eater,sizeof(python_eater)); - ptr=strstr(eater,"XXXXXXXXXX"); - if(!ptr){ - free(eater); - return NULL; - } - sprintf(buf,"0x%.8lx",bytes); - memcpy(ptr,buf,10); - return eater; -} - -/** @internal - * @brief uploads a script (python or other) at a specific path on the - * remote host - * @param[in] session an active SSH session - * @param[in] path to copy the file - * @param[in] content of the file to copy - * @return 0 on success, -1 on error - */ -static int upload_script(ssh_session session, const char *path, - const char *script){ - ssh_channel channel; - char cmd[128]; - int err; - - channel=ssh_channel_new(session); - if(!channel) - goto error; - if(ssh_channel_open_session(channel) == SSH_ERROR) - goto error; - snprintf(cmd,sizeof(cmd),"cat > %s",path); - if(ssh_channel_request_exec(channel,cmd) == SSH_ERROR) - goto error; - err=ssh_channel_write(channel,script,strlen(script)); - if(err == SSH_ERROR) - goto error; - if(ssh_channel_send_eof(channel) == SSH_ERROR) - goto error; - if(ssh_channel_close(channel) == SSH_ERROR) - goto error; - ssh_channel_free(channel); - return 0; -error: - fprintf(stderr,"Error while copying script : %s\n",ssh_get_error(session)); - return -1; -} - -/** @internal - * @brief benchmarks a raw upload (simple upload in a SSH channel) using an - * existing SSH session. - * @param[in] session Open SSH session - * @param[in] args Parsed command line arguments - * @param[out] bps The calculated bytes per second obtained via benchmark. - * @return 0 on success, -1 on error. - */ -int benchmarks_raw_up (ssh_session session, struct argument_s *args, - float *bps){ - unsigned long bytes; - char *script; - char cmd[128]; - int err; - ssh_channel channel; - struct timestamp_struct ts; - float ms=0.0; - unsigned long total=0; - - bytes = args->datasize * 1024 * 1024; - script =get_python_eater(bytes); - err=upload_script(session,"/tmp/eater.py",script); - free(script); - if(err<0) - return err; - channel=ssh_channel_new(session); - if(channel == NULL) - goto error; - if(ssh_channel_open_session(channel)==SSH_ERROR) - goto error; - snprintf(cmd,sizeof(cmd),"%s /tmp/eater.py", PYTHON_PATH); - if(ssh_channel_request_exec(channel,cmd)==SSH_ERROR) - goto error; - if((err=ssh_channel_read(channel,buffer,sizeof(buffer)-1,0))==SSH_ERROR) - goto error; - buffer[err]=0; - if(!strstr(buffer,"go")){ - fprintf(stderr,"parse error : %s\n",buffer); - ssh_channel_close(channel); - ssh_channel_free(channel); - return -1; - } - if(args->verbose>0) - fprintf(stdout,"Starting upload of %lu bytes now\n",bytes); - timestamp_init(&ts); - while(total < bytes){ - unsigned long towrite = bytes - total; - int w; - if(towrite > args->chunksize) - towrite = args->chunksize; - w=ssh_channel_write(channel,buffer,towrite); - if(w == SSH_ERROR) - goto error; - total += w; - } - - if(args->verbose>0) - fprintf(stdout,"Finished upload, now waiting the ack\n"); - - if((err=ssh_channel_read(channel,buffer,5,0))==SSH_ERROR) - goto error; - buffer[err]=0; - if(!strstr(buffer,"done")){ - fprintf(stderr,"parse error : %s\n",buffer); - ssh_channel_close(channel); - ssh_channel_free(channel); - return -1; - } - ms=elapsed_time(&ts); - *bps=8000 * (float)bytes / ms; - if(args->verbose > 0) - fprintf(stdout,"Upload took %f ms for %lu bytes, at %f bps\n",ms, - bytes,*bps); - ssh_channel_close(channel); - ssh_channel_free(channel); - return 0; -error: - fprintf(stderr,"Error during raw upload : %s\n",ssh_get_error(session)); - if(channel){ - ssh_channel_close(channel); - ssh_channel_free(channel); - } - return -1; -} - -const char python_giver[] = -"#!/usr/bin/python\n" -"import sys\n" -"r=sys.stdin.read(2)\n" -"towrite=XXXXXXXXXX\n" -"wrote=0\n" -"mtu = 32786\n" -"buf = 'A'*mtu\n" -"while(wrote < towrite):\n" -" buffersize=towrite-wrote\n" -" if(buffersize > mtu):\n" -" buffersize=mtu\n" -" if(buffersize == mtu):\n" -" sys.stdout.write(buf)\n" -" else:\n" -" sys.stdout.write('A'*buffersize)\n" -" wrote+=buffersize\n" -"sys.stdout.flush()\n"; - -static char *get_python_giver(unsigned long bytes){ - char *giver=malloc(sizeof(python_giver)); - char *ptr; - char buf[12]; - - memcpy(giver,python_giver,sizeof(python_giver)); - ptr=strstr(giver,"XXXXXXXXXX"); - if(!ptr){ - free(giver); - return NULL; - } - sprintf(buf,"0x%.8lx",bytes); - memcpy(ptr,buf,10); - return giver; -} - -/** @internal - * @brief benchmarks a raw download (simple upload in a SSH channel) using an - * existing SSH session. - * @param[in] session Open SSH session - * @param[in] args Parsed command line arguments - * @param[out] bps The calculated bytes per second obtained via benchmark. - * @return 0 on success, -1 on error. - */ -int benchmarks_raw_down (ssh_session session, struct argument_s *args, - float *bps){ - unsigned long bytes; - char *script; - char cmd[128]; - int err; - ssh_channel channel; - struct timestamp_struct ts; - float ms=0.0; - unsigned long total=0; - - bytes = args->datasize * 1024 * 1024; - script =get_python_giver(bytes); - err=upload_script(session,"/tmp/giver.py",script); - free(script); - if(err<0) - return err; - channel=ssh_channel_new(session); - if(channel == NULL) - goto error; - if(ssh_channel_open_session(channel)==SSH_ERROR) - goto error; - snprintf(cmd,sizeof(cmd),"%s /tmp/giver.py", PYTHON_PATH); - if(ssh_channel_request_exec(channel,cmd)==SSH_ERROR) - goto error; - if((err=ssh_channel_write(channel,"go",2))==SSH_ERROR) - goto error; - if(args->verbose>0) - fprintf(stdout,"Starting download of %lu bytes now\n",bytes); - timestamp_init(&ts); - while(total < bytes){ - unsigned long toread = bytes - total; - int r; - if(toread > args->chunksize) - toread = args->chunksize; - r=ssh_channel_read(channel,buffer,toread,0); - if(r == SSH_ERROR) - goto error; - total += r; - } - - if(args->verbose>0) - fprintf(stdout,"Finished download\n"); - ms=elapsed_time(&ts); - *bps=8000 * (float)bytes / ms; - if(args->verbose > 0) - fprintf(stdout,"Download took %f ms for %lu bytes, at %f bps\n",ms, - bytes,*bps); - ssh_channel_close(channel); - ssh_channel_free(channel); - return 0; -error: - fprintf(stderr,"Error during raw upload : %s\n",ssh_get_error(session)); - if(channel){ - ssh_channel_close(channel); - ssh_channel_free(channel); - } - return -1; -} diff --git a/libssh/tests/benchmarks/bench_scp.c b/libssh/tests/benchmarks/bench_scp.c deleted file mode 100644 index 340637ba..00000000 --- a/libssh/tests/benchmarks/bench_scp.c +++ /dev/null @@ -1,150 +0,0 @@ -/* bench_scp.c - * - * This file is part of the SSH Library - * - * Copyright (c) 2011 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "benchmarks.h" -#include -#include - -#define SCPDIR "/tmp/" -#define SCPFILE "scpbenchmark" - -/** @internal - * @brief benchmarks a scp upload using an - * existing SSH session. - * @param[in] session Open SSH session - * @param[in] args Parsed command line arguments - * @param[out] bps The calculated bytes per second obtained via benchmark. - * @return 0 on success, -1 on error. - */ -int benchmarks_scp_up (ssh_session session, struct argument_s *args, - float *bps){ - unsigned long bytes; - struct timestamp_struct ts; - float ms=0.0; - unsigned long total=0; - ssh_scp scp; - - bytes = args->datasize * 1024 * 1024; - scp = ssh_scp_new(session,SSH_SCP_WRITE,SCPDIR); - if(scp == NULL) - goto error; - if(ssh_scp_init(scp)==SSH_ERROR) - goto error; - if(ssh_scp_push_file(scp,SCPFILE,bytes,0777) != SSH_OK) - goto error; - if(args->verbose>0) - fprintf(stdout,"Starting upload of %lu bytes now\n",bytes); - timestamp_init(&ts); - while(total < bytes){ - unsigned long towrite = bytes - total; - int w; - if(towrite > args->chunksize) - towrite = args->chunksize; - w=ssh_scp_write(scp,buffer,towrite); - if(w == SSH_ERROR) - goto error; - total += towrite; - } - ms=elapsed_time(&ts); - *bps=8000 * (float)bytes / ms; - if(args->verbose > 0) - fprintf(stdout,"Upload took %f ms for %lu bytes, at %f bps\n",ms, - bytes,*bps); - ssh_scp_close(scp); - ssh_scp_free(scp); - return 0; -error: - fprintf(stderr,"Error during scp upload : %s\n",ssh_get_error(session)); - if(scp){ - ssh_scp_close(scp); - ssh_scp_free(scp); - } - return -1; -} - -/** @internal - * @brief benchmarks a scp download using an - * existing SSH session. - * @param[in] session Open SSH session - * @param[in] args Parsed command line arguments - * @param[out] bps The calculated bytes per second obtained via benchmark. - * @return 0 on success, -1 on error. - */ -int benchmarks_scp_down (ssh_session session, struct argument_s *args, - float *bps){ - unsigned long bytes; - struct timestamp_struct ts; - float ms=0.0; - unsigned long total=0; - ssh_scp scp; - int r; - size_t size; - - bytes = args->datasize * 1024 * 1024; - scp = ssh_scp_new(session,SSH_SCP_READ,SCPDIR SCPFILE); - if(scp == NULL) - goto error; - if(ssh_scp_init(scp)==SSH_ERROR) - goto error; - r=ssh_scp_pull_request(scp); - if(r == SSH_SCP_REQUEST_NEWFILE){ - size=ssh_scp_request_get_size(scp); - if(bytes > size){ - printf("Only %d bytes available (on %lu requested).\n",size,bytes); - bytes = size; - } - if(size > bytes){ - printf("File is %d bytes (on %lu requested). Will cut the end\n",size,bytes); - } - if(args->verbose>0) - fprintf(stdout,"Starting download of %lu bytes now\n",bytes); - timestamp_init(&ts); - ssh_scp_accept_request(scp); - while(total < bytes){ - unsigned long toread = bytes - total; - if(toread > args->chunksize) - toread = args->chunksize; - r=ssh_scp_read(scp,buffer,toread); - if(r == SSH_ERROR || r == 0) - goto error; - total += r; - } - ms=elapsed_time(&ts); - *bps=8000 * (float)bytes / ms; - if(args->verbose > 0) - fprintf(stdout,"download took %f ms for %lu bytes, at %f bps\n",ms, - bytes,*bps); - } else { - fprintf(stderr,"Expected SSH_SCP_REQUEST_NEWFILE, got %d\n",r); - goto error; - } - ssh_scp_close(scp); - ssh_scp_free(scp); - return 0; -error: - fprintf(stderr,"Error during scp download : %s\n",ssh_get_error(session)); - if(scp){ - ssh_scp_close(scp); - ssh_scp_free(scp); - } - return -1; -} diff --git a/libssh/tests/benchmarks/bench_sftp.c b/libssh/tests/benchmarks/bench_sftp.c deleted file mode 100644 index 9e4ab34d..00000000 --- a/libssh/tests/benchmarks/bench_sftp.c +++ /dev/null @@ -1,239 +0,0 @@ -/* bench_sftp.c - * - * This file is part of the SSH Library - * - * Copyright (c) 2011 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "benchmarks.h" -#include -#include -#include -#include -#include - -#define SFTPDIR "/tmp/" -#define SFTPFILE "scpbenchmark" - -/** @internal - * @brief benchmarks a synchronous sftp upload using an - * existing SSH session. - * @param[in] session Open SSH session - * @param[in] args Parsed command line arguments - * @param[out] bps The calculated bytes per second obtained via benchmark. - * @return 0 on success, -1 on error. - */ -int benchmarks_sync_sftp_up (ssh_session session, struct argument_s *args, - float *bps){ - unsigned long bytes; - struct timestamp_struct ts; - float ms=0.0; - unsigned long total=0; - sftp_session sftp; - sftp_file file; - - bytes = args->datasize * 1024 * 1024; - sftp = sftp_new(session); - if(sftp == NULL) - goto error; - if(sftp_init(sftp)==SSH_ERROR) - goto error; - file = sftp_open(sftp,SFTPDIR SFTPFILE,O_RDWR | O_CREAT | O_TRUNC, 0777); - if(!file) - goto error; - if(args->verbose>0) - fprintf(stdout,"Starting upload of %lu bytes now\n",bytes); - timestamp_init(&ts); - while(total < bytes){ - unsigned long towrite = bytes - total; - int w; - if(towrite > args->chunksize) - towrite = args->chunksize; - w=sftp_write(file,buffer,towrite); - if(w == SSH_ERROR) - goto error; - total += w; - } - sftp_close(file); - ms=elapsed_time(&ts); - *bps=8000 * (float)bytes / ms; - if(args->verbose > 0) - fprintf(stdout,"Upload took %f ms for %lu bytes, at %f bps\n",ms, - bytes,*bps); - sftp_free(sftp); - return 0; -error: - fprintf(stderr,"Error during scp upload : %s\n",ssh_get_error(session)); - if(file) - sftp_close(file); - if(sftp) - sftp_free(sftp); - return -1; -} - -/** @internal - * @brief benchmarks a synchronous sftp download using an - * existing SSH session. - * @param[in] session Open SSH session - * @param[in] args Parsed command line arguments - * @param[out] bps The calculated bytes per second obtained via benchmark. - * @return 0 on success, -1 on error. - */ -int benchmarks_sync_sftp_down (ssh_session session, struct argument_s *args, - float *bps){ - unsigned long bytes; - struct timestamp_struct ts; - float ms=0.0; - unsigned long total=0; - sftp_session sftp; - sftp_file file; - int r; - - bytes = args->datasize * 1024 * 1024; - sftp = sftp_new(session); - if(sftp == NULL) - goto error; - if(sftp_init(sftp)==SSH_ERROR) - goto error; - file = sftp_open(sftp,SFTPDIR SFTPFILE,O_RDONLY,0); - if(!file) - goto error; - if(args->verbose>0) - fprintf(stdout,"Starting download of %lu bytes now\n",bytes); - timestamp_init(&ts); - while(total < bytes){ - unsigned long toread = bytes - total; - if(toread > args->chunksize) - toread = args->chunksize; - r=sftp_read(file,buffer,toread); - if(r == SSH_ERROR) - goto error; - total += r; - /* we had a smaller file */ - if(r==0){ - fprintf(stdout,"File smaller than expected : %lu (expected %lu).\n",total,bytes); - bytes = total; - break; - } - } - sftp_close(file); - ms=elapsed_time(&ts); - *bps=8000 * (float)bytes / ms; - if(args->verbose > 0) - fprintf(stdout,"download took %f ms for %lu bytes, at %f bps\n",ms, - bytes,*bps); - sftp_free(sftp); - return 0; -error: - fprintf(stderr,"Error during sftp download : %s\n",ssh_get_error(session)); - if(file) - sftp_close(file); - if(sftp) - sftp_free(sftp); - return -1; -} - -/** @internal - * @brief benchmarks an asynchronous sftp download using an - * existing SSH session. - * @param[in] session Open SSH session - * @param[in] args Parsed command line arguments - * @param[out] bps The calculated bytes per second obtained via benchmark. - * @return 0 on success, -1 on error. - */ -int benchmarks_async_sftp_down (ssh_session session, struct argument_s *args, - float *bps){ - unsigned long bytes; - struct timestamp_struct ts; - float ms=0.0; - unsigned long total=0; - sftp_session sftp; - sftp_file file; - int r,i; - int warned = 0; - unsigned long toread; - int *ids=NULL; - int concurrent_downloads = args->concurrent_requests; - - bytes = args->datasize * 1024 * 1024; - sftp = sftp_new(session); - if(sftp == NULL) - goto error; - if(sftp_init(sftp)==SSH_ERROR) - goto error; - file = sftp_open(sftp,SFTPDIR SFTPFILE,O_RDONLY,0); - if(!file) - goto error; - ids = malloc(concurrent_downloads * sizeof(int)); - if(args->verbose>0) - fprintf(stdout,"Starting download of %lu bytes now, using %d concurrent downloads\n",bytes, - concurrent_downloads); - timestamp_init(&ts); - for (i=0;ichunksize); - if(ids[i]==SSH_ERROR) - goto error; - } - i=0; - while(total < bytes){ - r = sftp_async_read(file, buffer, args->chunksize, ids[i]); - if(r == SSH_ERROR) - goto error; - total += r; - if(r != (int)args->chunksize && total != bytes && !warned){ - fprintf(stderr,"async_sftp_download : receiving short reads (%d, requested %d) " - "the received file will be corrupted and shorted. Adapt chunksize to %d\n", - r, args->chunksize,r); - warned = 1; - } - /* we had a smaller file */ - if(r==0){ - fprintf(stdout,"File smaller than expected : %lu (expected %lu).\n",total,bytes); - bytes = total; - break; - } - toread = bytes - total; - if(toread < args->chunksize * concurrent_downloads){ - /* we've got enough launched downloads */ - ids[i]=-1; - } - if(toread > args->chunksize) - toread = args->chunksize; - ids[i]=sftp_async_read_begin(file,toread); - if(ids[i] == SSH_ERROR) - goto error; - i = (i+1) % concurrent_downloads; - } - sftp_close(file); - ms=elapsed_time(&ts); - *bps=8000 * (float)bytes / ms; - if(args->verbose > 0) - fprintf(stdout,"download took %f ms for %lu bytes, at %f bps\n",ms, - bytes,*bps); - sftp_free(sftp); - free(ids); - return 0; -error: - fprintf(stderr,"Error during sftp download : %s\n",ssh_get_error(session)); - if(file) - sftp_close(file); - if(sftp) - sftp_free(sftp); - free(ids); - return -1; -} diff --git a/libssh/tests/benchmarks/benchmarks.c b/libssh/tests/benchmarks/benchmarks.c deleted file mode 100644 index 5e33dd4b..00000000 --- a/libssh/tests/benchmarks/benchmarks.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" -#include "benchmarks.h" -#include - -#include -#include -#include - -struct benchmark benchmarks[]= { - { - .name="benchmark_raw_upload", - .fct=benchmarks_raw_up, - .enabled=0 - }, - { - .name="benchmark_raw_download", - .fct=benchmarks_raw_down, - .enabled=0 - }, - { - .name="benchmark_scp_upload", - .fct=benchmarks_scp_up, - .enabled=0 - }, - { - .name="benchmark_scp_download", - .fct=benchmarks_scp_down, - .enabled=0 - }, - { - .name="benchmark_sync_sftp_upload", - .fct=benchmarks_sync_sftp_up, - .enabled=0 - }, - { - .name="benchmark_sync_sftp_download", - .fct=benchmarks_sync_sftp_down, - .enabled=0 - }, - { - .name="benchmark_async_sftp_download", - .fct=benchmarks_async_sftp_down, - .enabled=0 - } -}; - -#ifdef HAVE_ARGP_H -#include - -const char *argp_program_version = "libssh benchmarks 2011-08-28"; -const char *argp_program_bug_address = "Aris Adamantiadis "; - -static char **cmdline; - -/* Program documentation. */ -static char doc[] = "libssh benchmarks"; - - -/* The options we understand. */ -static struct argp_option options[] = { - { - .name = "verbose", - .key = 'v', - .arg = NULL, - .flags = 0, - .doc = "Make libssh benchmark more verbose", - .group = 0 - }, - { - .name = "raw-upload", - .key = '1', - .arg = NULL, - .flags = 0, - .doc = "Upload raw data using channel", - .group = 0 - }, - { - .name = "raw-download", - .key = '2', - .arg = NULL, - .flags = 0, - .doc = "Download raw data using channel", - .group = 0 - }, - { - .name = "scp-upload", - .key = '3', - .arg = NULL, - .flags = 0, - .doc = "Upload data using SCP", - .group = 0 - }, - { - .name = "scp-download", - .key = '4', - .arg = NULL, - .flags = 0, - .doc = "Download data using SCP", - .group = 0 - }, - { - .name = "sync-sftp-upload", - .key = '5', - .arg = NULL, - .flags = 0, - .doc = "Upload data using synchronous SFTP", - .group = 0 - - }, - { - .name = "sync-sftp-download", - .key = '6', - .arg = NULL, - .flags = 0, - .doc = "Download data using synchronous SFTP (slow)", - .group = 0 - - }, - { - .name = "async-sftp-download", - .key = '7', - .arg = NULL, - .flags = 0, - .doc = "Download data using asynchronous SFTP (fast)", - .group = 0 - - }, - { - .name = "host", - .key = 'h', - .arg = "HOST", - .flags = 0, - .doc = "Add a host to connect for benchmark (format user@hostname)", - .group = 0 - }, - { - .name = "size", - .key = 's', - .arg = "MBYTES", - .flags = 0, - .doc = "MBytes of data to send/receive per test", - .group = 0 - }, - { - .name = "chunk", - .key = 'c', - .arg = "bytes", - .flags = 0, - .doc = "size of data chunks to send/receive", - .group = 0 - }, - { - .name = "prequests", - .key = 'p', - .arg = "number [20]", - .flags = 0, - .doc = "[async SFTP] number of concurrent requests", - .group = 0 - }, - { - .name = "cipher", - .key = 'C', - .arg = "cipher", - .flags = 0, - .doc = "Cryptographic cipher to be used", - .group = 0 - }, - - {NULL, 0, NULL, 0, NULL, 0} -}; - -/* Parse a single option. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* Get the input argument from argp_parse, which we - * know is a pointer to our arguments structure. - */ - struct argument_s *arguments = state->input; - - /* arg is currently not used */ - (void) arg; - - switch (key) { - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - benchmarks[key - '1'].enabled = 1; - arguments->ntests ++; - break; - case 'v': - arguments->verbose++; - break; - case 's': - arguments->datasize = atoi(arg); - break; - case 'p': - arguments->concurrent_requests = atoi(arg); - break; - case 'c': - arguments->chunksize = atoi(arg); - break; - case 'C': - arguments->cipher = arg; - break; - case 'h': - if(arguments->nhosts >= MAX_HOSTS_CONNECT){ - fprintf(stderr, "Too much hosts\n"); - return ARGP_ERR_UNKNOWN; - } - arguments->hosts[arguments->nhosts]=arg; - arguments->nhosts++; - break; - case ARGP_KEY_ARG: - /* End processing here. */ - cmdline = &state->argv [state->next - 1]; - state->next = state->argc; - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL}; - -#endif /* HAVE_ARGP_H */ - -static void cmdline_parse(int argc, char **argv, struct argument_s *arguments) { - /* - * Parse our arguments; every option seen by parse_opt will - * be reflected in arguments. - */ -#ifdef HAVE_ARGP_H - argp_parse(&argp, argc, argv, 0, 0, arguments); -#else /* HAVE_ARGP_H */ - (void) argc; - (void) argv; - arguments->hosts[0]="localhost"; - arguments->nhosts=1; -#endif /* HAVE_ARGP_H */ -} - -static void arguments_init(struct argument_s *arguments){ - memset(arguments,0,sizeof(*arguments)); - arguments->chunksize=32758; - arguments->concurrent_requests=20; - arguments->datasize = 10; -} - -static ssh_session connect_host(const char *host, int verbose, char *cipher){ - ssh_session session=ssh_new(); - if(session==NULL) - goto error; - if(ssh_options_set(session,SSH_OPTIONS_HOST, host)<0) - goto error; - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbose); - if(cipher != NULL){ - if (ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher) || - ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher)){ - goto error; - } - } - ssh_options_parse_config(session, NULL); - if(ssh_connect(session)==SSH_ERROR) - goto error; - if(ssh_userauth_autopubkey(session,NULL) != SSH_AUTH_SUCCESS) - goto error; - return session; -error: - fprintf(stderr,"Error connecting to \"%s\": %s\n",host,ssh_get_error(session)); - ssh_free(session); - return NULL; -} - -static char *network_speed(float bps){ - static char buf[128]; - if(bps > 1000*1000*1000){ - /* Gbps */ - snprintf(buf,sizeof(buf),"%f Gbps",bps/(1000*1000*1000)); - } else if(bps > 1000*1000){ - /* Mbps */ - snprintf(buf,sizeof(buf),"%f Mbps",bps/(1000*1000)); - } else if(bps > 1000){ - snprintf(buf,sizeof(buf),"%f Kbps",bps/1000); - } else { - snprintf(buf,sizeof(buf),"%f bps",bps); - } - return buf; -} - -static void do_benchmarks(ssh_session session, struct argument_s *arguments, - const char *hostname){ - float ping_rtt=0.0; - float ssh_rtt=0.0; - float bps=0.0; - int i; - int err; - struct benchmark *b; - - if(arguments->verbose>0) - fprintf(stdout,"Testing ICMP RTT\n"); - err=benchmarks_ping_latency(hostname, &ping_rtt); - if(err == 0){ - fprintf(stdout,"ping RTT : %f ms\n",ping_rtt); - } - err=benchmarks_ssh_latency(session, &ssh_rtt); - if(err==0){ - fprintf(stdout, "SSH RTT : %f ms. Theoretical max BW (win=128K) : %s\n",ssh_rtt,network_speed(128000.0/(ssh_rtt / 1000.0))); - } - for (i=0 ; ienabled){ - err=b->fct(session,arguments,&bps); - if(err==0){ - fprintf(stdout, "%s : %s : %s\n",hostname, b->name, network_speed(bps)); - } - } - } -} - -char *buffer; - -int main(int argc, char **argv){ - struct argument_s arguments; - ssh_session session; - int i; - - arguments_init(&arguments); - cmdline_parse(argc, argv, &arguments); - if (arguments.nhosts==0){ - fprintf(stderr,"At least one host (-h) must be specified\n"); - return EXIT_FAILURE; - } - if (arguments.ntests==0){ - for(i=0; i < BENCHMARK_NUMBER ; ++i){ - benchmarks[i].enabled=1; - } - arguments.ntests=BENCHMARK_NUMBER; - } - buffer=malloc(arguments.chunksize > 1024 ? arguments.chunksize : 1024); - if(buffer == NULL){ - fprintf(stderr,"Allocation of chunk buffer failed\n"); - return EXIT_FAILURE; - } - if (arguments.verbose > 0){ - fprintf(stdout, "Will try hosts "); - for(i=0;i 0) - fprintf(stdout,"Connecting to \"%s\"...\n",arguments.hosts[i]); - session=connect_host(arguments.hosts[i], arguments.verbose, arguments.cipher); - if(session != NULL && arguments.verbose > 0) - fprintf(stdout,"Success\n"); - if(session == NULL){ - fprintf(stderr,"Errors occurred, stopping\n"); - return EXIT_FAILURE; - } - do_benchmarks(session, &arguments, arguments.hosts[i]); - ssh_disconnect(session); - ssh_free(session); - } - return EXIT_SUCCESS; -} - diff --git a/libssh/tests/benchmarks/benchmarks.h b/libssh/tests/benchmarks/benchmarks.h deleted file mode 100644 index 26da09bb..00000000 --- a/libssh/tests/benchmarks/benchmarks.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#ifndef BENCHMARKS_H_ -#define BENCHMARKS_H_ - -#include - -/* benchmarks.c */ - -/* maximum number of parallel hosts that may be checked */ -#define MAX_HOSTS_CONNECT 20 - -enum libssh_benchmarks { - BENCHMARK_RAW_UPLOAD=0, - BENCHMARK_RAW_DOWNLOAD, - BENCHMARK_SCP_UPLOAD, - BENCHMARK_SCP_DOWNLOAD, - BENCHMARK_SYNC_SFTP_UPLOAD, - BENCHMARK_SYNC_SFTP_DOWNLOAD, - BENCHMARK_ASYNC_SFTP_DOWNLOAD, - BENCHMARK_NUMBER -}; - -struct argument_s { - const char *hosts[MAX_HOSTS_CONNECT]; - int verbose; - int nhosts; - int ntests; - unsigned int datasize; - unsigned int chunksize; - int concurrent_requests; - char *cipher; -}; - -extern char *buffer; - -typedef int (*bench_fct)(ssh_session session, struct argument_s *args, - float *bps); - -struct benchmark { - const char *name; - bench_fct fct; - int enabled; -}; - -/* latency.c */ - -struct timestamp_struct { - struct timeval timestamp; -}; - -int benchmarks_ping_latency (const char *host, float *average); -int benchmarks_ssh_latency (ssh_session session, float *average); - -void timestamp_init(struct timestamp_struct *ts); -float elapsed_time(struct timestamp_struct *ts); - -/* bench_raw.c */ - -int benchmarks_raw_up (ssh_session session, struct argument_s *args, - float *bps); -int benchmarks_raw_down (ssh_session session, struct argument_s *args, - float *bps); - -/* bench_scp.c */ - -int benchmarks_scp_up (ssh_session session, struct argument_s *args, - float *bps); -int benchmarks_scp_down (ssh_session session, struct argument_s *args, - float *bps); - -/* bench_sftp.c */ - -int benchmarks_sync_sftp_up (ssh_session session, struct argument_s *args, - float *bps); -int benchmarks_sync_sftp_down (ssh_session session, struct argument_s *args, - float *bps); -int benchmarks_async_sftp_down (ssh_session session, struct argument_s *args, - float *bps); -#endif /* BENCHMARKS_H_ */ diff --git a/libssh/tests/benchmarks/latency.c b/libssh/tests/benchmarks/latency.c deleted file mode 100644 index 09c50a04..00000000 --- a/libssh/tests/benchmarks/latency.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "benchmarks.h" -#include - -#include -#include -#include -#include -#include - -#define PING_PROGRAM "/bin/ping" - -/** @internal - * @brief Calculates the RTT of the host with ICMP ping, and returns the - * average of the calculated RTT. - * @param[in] host hostname to ping. - * @param[out] average average RTT in milliseconds. - * @returns 0 on success, -1 if there is an error. - * @warning relies on an external ping program which may not exist on - * certain OS. - */ -int benchmarks_ping_latency (const char *host, float *average){ - const char *ptr; - char cmd[256]; - char line[1024]; - FILE *fd; - int found=0; - - /* strip out the hostname */ - ptr=strchr(host,'@'); - if(ptr) - ptr++; - else - ptr=host; - - snprintf(cmd,sizeof(cmd),"%s -n -q -c3 %s",PING_PROGRAM, ptr); - fd=popen(cmd,"r"); - if(fd==NULL){ - fprintf(stderr,"Error executing command : %s\n", strerror(errno)); - return -1; - } - - while(!found && fgets(line,sizeof(line),fd)!=NULL){ - if(strstr(line,"rtt")){ - ptr=strchr(line,'='); - if(ptr==NULL) - goto parseerror; - ptr=strchr(ptr,'/'); - if(ptr==NULL) - goto parseerror; - *average=strtof(ptr+1,NULL); - found=1; - break; - } - } - if(!found) - goto parseerror; - pclose(fd); - return 0; - -parseerror: - fprintf(stderr,"Parse error : couldn't locate average in %s",line); - pclose(fd); - return -1; -} - -/** @internal - * @brief initialize a timestamp to the current time. - * @param[out] ts A timestamp_struct pointer. - */ -void timestamp_init(struct timestamp_struct *ts){ - gettimeofday(&ts->timestamp,NULL); -} - -/** @internal - * @brief return the elapsed time since now and the moment ts was initialized. - * @param[in] ts An initialized timestamp_struct pointer. - * @return Elapsed time in milliseconds. - */ -float elapsed_time(struct timestamp_struct *ts){ - struct timeval now; - time_t secdiff; - long usecdiff; /* may be negative */ - - gettimeofday(&now,NULL); - secdiff=now.tv_sec - ts->timestamp.tv_sec; - usecdiff=now.tv_usec - ts->timestamp.tv_usec; - //printf("%d sec diff, %d usec diff\n",secdiff, usecdiff); - return (float) (secdiff*1000) + ((float)usecdiff)/1000; -} - -/** @internal - * @brief Calculates the RTT of the host with SSH channel operations, and - * returns the average of the calculated RTT. - * @param[in] session active SSH session to test. - * @param[out] average average RTT in milliseconds. - * @returns 0 on success, -1 if there is an error. - */ -int benchmarks_ssh_latency(ssh_session session, float *average){ - float times[3]; - struct timestamp_struct ts; - int i; - ssh_channel channel; - channel=ssh_channel_new(session); - if(channel==NULL) - goto error; - if(ssh_channel_open_session(channel)==SSH_ERROR) - goto error; - - for(i=0;i<3;++i){ - timestamp_init(&ts); - if(ssh_channel_request_env(channel,"TEST","test")==SSH_ERROR && - ssh_get_error_code(session)==SSH_FATAL) - goto error; - times[i]=elapsed_time(&ts); - } - ssh_channel_close(channel); - ssh_channel_free(channel); - channel=NULL; - printf("SSH request times : %f ms ; %f ms ; %f ms\n", times[0], times[1], times[2]); - *average=(times[0]+times[1]+times[2])/3; - return 0; -error: - fprintf(stderr,"Error calculating SSH latency : %s\n",ssh_get_error(session)); - if(channel) - ssh_channel_free(channel); - return -1; -} diff --git a/libssh/tests/chmodtest.c b/libssh/tests/chmodtest.c deleted file mode 100644 index 1e6f5112..00000000 --- a/libssh/tests/chmodtest.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include -#include "examples_common.h" -#include - -int main(void) { - ssh_session session; - sftp_session sftp; - char buffer[1024*1024]; - int rc; - - session = connect_ssh("localhost", NULL, 0); - if (session == NULL) { - return 1; - } - - sftp=sftp_new(session); - sftp_init(sftp); - rc=sftp_rename(sftp,"/tmp/test","/tmp/test"); - rc=sftp_rename(sftp,"/tmp/test","/tmp/test"); - rc=sftp_chmod(sftp,"/tmp/test",0644); - if (rc < 0) { - printf("error : %s\n",ssh_get_error(sftp)); - - ssh_disconnect(session); - return 1; - } - - ssh_disconnect(session); - - return 0; -} diff --git a/libssh/tests/client/CMakeLists.txt b/libssh/tests/client/CMakeLists.txt deleted file mode 100644 index 616060c3..00000000 --- a/libssh/tests/client/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -project(clienttests C) - -add_cmocka_test(torture_algorithms torture_algorithms.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_auth torture_auth.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_connect torture_connect.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_knownhosts torture_knownhosts.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_proxycommand torture_proxycommand.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_session torture_session.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_forward torture_forward.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_request_env torture_request_env.c ${TORTURE_LIBRARY}) -if (WITH_SFTP) - add_cmocka_test(torture_sftp_static torture_sftp_static.c ${TORTURE_LIBRARY}) - add_cmocka_test(torture_sftp_dir torture_sftp_dir.c ${TORTURE_LIBRARY}) - add_cmocka_test(torture_sftp_read torture_sftp_read.c ${TORTURE_LIBRARY}) -endif (WITH_SFTP) diff --git a/libssh/tests/client/torture_algorithms.c b/libssh/tests/client/torture_algorithms.c deleted file mode 100644 index 8a466380..00000000 --- a/libssh/tests/client/torture_algorithms.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#define LIBSSH_STATIC - -#include "torture.h" -#include "libssh/libssh.h" -#include "libssh/priv.h" - - -static void setup(void **state) { - int verbosity=torture_libssh_verbosity(); - ssh_session session = ssh_new(); - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - *state = session; -} - -static void teardown(void **state) { - ssh_free(*state); -} - -static void test_algorithm(ssh_session session, const char *algo, const char *hmac) { - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, algo); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, algo); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, hmac); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, hmac); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session, NULL); - if (rc != SSH_OK) { - rc = ssh_get_error_code(session); - assert_true(rc == SSH_REQUEST_DENIED); - } - - ssh_disconnect(session); -} - -static void torture_algorithms_aes128_cbc_hmac_sha1(void **state) { - test_algorithm(*state, "aes128-cbc", "hmac-sha1"); -} - -static void torture_algorithms_aes128_cbc_hmac_sha2_256(void **state) { - test_algorithm(*state, "aes128-cbc", "hmac-sha2-256"); -} - -static void torture_algorithms_aes128_cbc_hmac_sha2_512(void **state) { - test_algorithm(*state, "aes128-cbc", "hmac-sha2-512"); -} - -static void torture_algorithms_aes192_cbc_hmac_sha1(void **state) { - test_algorithm(*state, "aes192-cbc", "hmac-sha1"); -} - -static void torture_algorithms_aes192_cbc_hmac_sha2_256(void **state) { - test_algorithm(*state, "aes192-cbc", "hmac-sha2-256"); -} - -static void torture_algorithms_aes192_cbc_hmac_sha2_512(void **state) { - test_algorithm(*state, "aes192-cbc", "hmac-sha2-512"); -} - -static void torture_algorithms_aes256_cbc_hmac_sha1(void **state) { - test_algorithm(*state, "aes256-cbc", "hmac-sha1"); -} - -static void torture_algorithms_aes256_cbc_hmac_sha2_256(void **state) { - test_algorithm(*state, "aes256-cbc", "hmac-sha2-256"); -} - -static void torture_algorithms_aes256_cbc_hmac_sha2_512(void **state) { - test_algorithm(*state, "aes256-cbc", "hmac-sha2-512"); -} - -static void torture_algorithms_aes128_ctr_hmac_sha1(void **state) { - test_algorithm(*state, "aes128-ctr", "hmac-sha1"); -} - -static void torture_algorithms_aes128_ctr_hmac_sha2_256(void **state) { - test_algorithm(*state, "aes128-ctr", "hmac-sha2-256"); -} - -static void torture_algorithms_aes128_ctr_hmac_sha2_512(void **state) { - test_algorithm(*state, "aes128-ctr", "hmac-sha2-512"); -} - -static void torture_algorithms_aes192_ctr_hmac_sha1(void **state) { - test_algorithm(*state, "aes192-ctr", "hmac-sha1"); -} - -static void torture_algorithms_aes192_ctr_hmac_sha2_256(void **state) { - test_algorithm(*state, "aes192-ctr", "hmac-sha2-256"); -} - -static void torture_algorithms_aes192_ctr_hmac_sha2_512(void **state) { - test_algorithm(*state, "aes192-ctr", "hmac-sha2-512"); -} - -static void torture_algorithms_aes256_ctr_hmac_sha1(void **state) { - test_algorithm(*state, "aes256-ctr", "hmac-sha1"); -} - -static void torture_algorithms_aes256_ctr_hmac_sha2_256(void **state) { - test_algorithm(*state, "aes256-ctr", "hmac-sha2-256"); -} - -static void torture_algorithms_aes256_ctr_hmac_sha2_512(void **state) { - test_algorithm(*state, "aes256-ctr", "hmac-sha2-512"); -} - -static void torture_algorithms_3des_cbc_hmac_sha1(void **state) { - test_algorithm(*state, "3des-cbc", "hmac-sha1"); -} - -static void torture_algorithms_3des_cbc_hmac_sha2_256(void **state) { - test_algorithm(*state, "3des-cbc", "hmac-sha2-256"); -} - -static void torture_algorithms_3des_cbc_hmac_sha2_512(void **state) { - test_algorithm(*state, "3des-cbc", "hmac-sha2-512"); -} - -static void torture_algorithms_blowfish_cbc_hmac_sha1(void **state) { - test_algorithm(*state, "blowfish-cbc", "hmac-sha1"); -} - -static void torture_algorithms_blowfish_cbc_hmac_sha2_256(void **state) { - test_algorithm(*state, "blowfish-cbc", "hmac-sha2-256"); -} - -static void torture_algorithms_blowfish_cbc_hmac_sha2_512(void **state) { - test_algorithm(*state, "blowfish-cbc", "hmac-sha2-512"); -} - -static void torture_algorithms_zlib(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session,SSH_OPTIONS_HOST,"localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_C_S, "zlib"); -#ifdef WITH_ZLIB - assert_true(rc == SSH_OK); -#else - assert_true(rc == SSH_ERROR); -#endif - - rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_S_C, "zlib"); -#ifdef WITH_ZLIB - assert_true(rc == SSH_OK); -#else - assert_true(rc == SSH_ERROR); -#endif - - rc = ssh_connect(session); -#ifdef WITH_ZLIB - if (ssh_get_openssh_version(session)) { - assert_false(rc == SSH_OK); - ssh_disconnect(session); - return; - } -#endif - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session, NULL); - if (rc != SSH_OK) { - rc = ssh_get_error_code(session); - assert_true(rc == SSH_REQUEST_DENIED); - } - - ssh_disconnect(session); -} - -static void torture_algorithms_zlib_openssh(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session,SSH_OPTIONS_HOST,"localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_C_S, "zlib@openssh.com"); -#ifdef WITH_ZLIB - assert_true(rc == SSH_OK); -#else - assert_true(rc == SSH_ERROR); -#endif - - rc = ssh_options_set(session, SSH_OPTIONS_COMPRESSION_S_C, "zlib@openssh.com"); -#ifdef WITH_ZLIB - assert_true(rc == SSH_OK); -#else - assert_true(rc == SSH_ERROR); -#endif - - rc = ssh_connect(session); -#ifdef WITH_ZLIB - if (ssh_get_openssh_version(session)) { - assert_true(rc==SSH_OK); - rc = ssh_userauth_none(session, NULL); - if (rc != SSH_OK) { - rc = ssh_get_error_code(session); - assert_true(rc == SSH_REQUEST_DENIED); - } - ssh_disconnect(session); - return; - } - assert_false(rc == SSH_OK); -#else - assert_true(rc == SSH_OK); -#endif - - ssh_disconnect(session); -} - -#if defined(HAVE_LIBCRYPTO) && defined(HAVE_ECC) -static void torture_algorithms_ecdh_sha2_nistp256(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session,SSH_OPTIONS_HOST,"localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "ecdh-sha2-nistp256"); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - rc = ssh_userauth_none(session, NULL); - if (rc != SSH_OK) { - rc = ssh_get_error_code(session); - assert_true(rc == SSH_REQUEST_DENIED); - } - - ssh_disconnect(session); -} -#endif - -static void torture_algorithms_dh_group1(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session,SSH_OPTIONS_HOST,"localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "diffie-hellman-group1-sha1"); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - rc = ssh_userauth_none(session, NULL); - if (rc != SSH_OK) { - rc = ssh_get_error_code(session); - assert_true(rc == SSH_REQUEST_DENIED); - } - - ssh_disconnect(session); -} -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_algorithms_aes128_cbc_hmac_sha1, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes128_cbc_hmac_sha2_256, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes128_cbc_hmac_sha2_512, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes192_cbc_hmac_sha1, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes192_cbc_hmac_sha2_256, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes192_cbc_hmac_sha2_512, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes256_cbc_hmac_sha1, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes256_cbc_hmac_sha2_256, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes256_cbc_hmac_sha2_512, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes128_ctr_hmac_sha1, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes128_ctr_hmac_sha2_256, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes128_ctr_hmac_sha2_512, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes192_ctr_hmac_sha1, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes192_ctr_hmac_sha2_256, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes192_ctr_hmac_sha2_512, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes256_ctr_hmac_sha1, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes256_ctr_hmac_sha2_256, setup, teardown), - unit_test_setup_teardown(torture_algorithms_aes256_ctr_hmac_sha2_512, setup, teardown), - unit_test_setup_teardown(torture_algorithms_3des_cbc_hmac_sha1, setup, teardown), - unit_test_setup_teardown(torture_algorithms_3des_cbc_hmac_sha2_256, setup, teardown), - unit_test_setup_teardown(torture_algorithms_3des_cbc_hmac_sha2_512, setup, teardown), - unit_test_setup_teardown(torture_algorithms_blowfish_cbc_hmac_sha1, setup, teardown), - unit_test_setup_teardown(torture_algorithms_blowfish_cbc_hmac_sha2_256, setup, teardown), - unit_test_setup_teardown(torture_algorithms_blowfish_cbc_hmac_sha2_512, setup, teardown), - unit_test_setup_teardown(torture_algorithms_zlib, setup, teardown), - unit_test_setup_teardown(torture_algorithms_zlib_openssh, setup, teardown), - unit_test_setup_teardown(torture_algorithms_dh_group1,setup,teardown), -#if defined(HAVE_LIBCRYPTO) && defined(HAVE_ECC) - unit_test_setup_teardown(torture_algorithms_ecdh_sha2_nistp256,setup,teardown) -#endif - }; - - ssh_init(); - - rc = run_tests(tests); - ssh_finalize(); - - return rc; -} diff --git a/libssh/tests/client/torture_auth.c b/libssh/tests/client/torture_auth.c deleted file mode 100644 index af36b79f..00000000 --- a/libssh/tests/client/torture_auth.c +++ /dev/null @@ -1,443 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#define LIBSSH_STATIC - -#include "torture.h" -#include "libssh/libssh.h" -#include "libssh/priv.h" -#include "libssh/session.h" -#include "agent.c" - -static void setup(void **state) { - int verbosity = torture_libssh_verbosity(); - ssh_session session = ssh_new(); - - ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - - *state = session; -} - -static void teardown(void **state) { - ssh_disconnect(*state); - ssh_free(*state); -} - -static void torture_auth_autopubkey(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session,NULL); - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); - - rc = ssh_userauth_publickey_auto(session, NULL, NULL); - assert_true(rc == SSH_AUTH_SUCCESS); -} - -static void torture_auth_autopubkey_nonblocking(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - ssh_set_blocking(session,0); - do { - rc = ssh_userauth_none(session, NULL); - } while (rc == SSH_AUTH_AGAIN); - - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); - - do { - rc = ssh_userauth_publickey_auto(session, NULL, NULL); - } while (rc == SSH_AUTH_AGAIN); - assert_true(rc == SSH_AUTH_SUCCESS); -} - -static void torture_auth_kbdint(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - char *password = getenv("TORTURE_PASSWORD"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - - if (password == NULL) { - print_message("*** Please set the environment variable " - "TORTURE_PASSWORD to enable this test!!\n"); - return; - } - - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session,NULL); - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_INTERACTIVE); - - rc = ssh_userauth_kbdint(session, NULL, NULL); - assert_true(rc == SSH_AUTH_INFO); - assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 1); - - rc = ssh_userauth_kbdint_setanswer(session, 0, password); - assert_false(rc < 0); - - rc = ssh_userauth_kbdint(session, NULL, NULL); - /* Sometimes, SSH server send an empty query at the end of exchange */ - if(rc == SSH_AUTH_INFO) { - assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 0); - rc = ssh_userauth_kbdint(session, NULL, NULL); - } - assert_true(rc == SSH_AUTH_SUCCESS); -} - -static void torture_auth_kbdint_nonblocking(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - char *password = getenv("TORTURE_PASSWORD"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - - if (password == NULL) { - print_message("*** Please set the environment variable " - "TORTURE_PASSWORD to enable this test!!\n"); - return; - } - - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - ssh_set_blocking(session,0); - do { - rc = ssh_userauth_none(session, NULL); - } while (rc == SSH_AUTH_AGAIN); - - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_INTERACTIVE); - - do { - rc = ssh_userauth_kbdint(session, NULL, NULL); - } while (rc == SSH_AUTH_AGAIN); - assert_true(rc == SSH_AUTH_INFO); - assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 1); - do { - rc = ssh_userauth_kbdint_setanswer(session, 0, password); - } while (rc == SSH_AUTH_AGAIN); - assert_false(rc < 0); - - do { - rc = ssh_userauth_kbdint(session, NULL, NULL); - } while (rc == SSH_AUTH_AGAIN); - /* Sometimes, SSH server send an empty query at the end of exchange */ - if(rc == SSH_AUTH_INFO) { - assert_int_equal(ssh_userauth_kbdint_getnprompts(session), 0); - do { - rc = ssh_userauth_kbdint(session, NULL, NULL); - } while (rc == SSH_AUTH_AGAIN); - } - assert_true(rc == SSH_AUTH_SUCCESS); -} - -static void torture_auth_password(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - char *password = getenv("TORTURE_PASSWORD"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - - if (password == NULL) { - print_message("*** Please set the environment variable " - "TORTURE_PASSWORD to enable this test!!\n"); - return; - } - - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session, NULL); - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_AUTH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_PASSWORD); - - rc = ssh_userauth_password(session, NULL, password); - assert_true(rc == SSH_AUTH_SUCCESS); -} - -static void torture_auth_password_nonblocking(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - char *password = getenv("TORTURE_PASSWORD"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - - if (password == NULL) { - print_message("*** Please set the environment variable " - "TORTURE_PASSWORD to enable this test!!\n"); - return; - } - - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - ssh_set_blocking(session,0); - do { - rc = ssh_userauth_none(session, NULL); - } while (rc == SSH_AUTH_AGAIN); - - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_AUTH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_PASSWORD); - - do { - rc = ssh_userauth_password(session, NULL, password); - } while(rc==SSH_AUTH_AGAIN); - - assert_true(rc == SSH_AUTH_SUCCESS); -} - -static void torture_auth_agent(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - if (!agent_is_running(session)){ - print_message("*** Agent not running. Test ignored\n"); - return; - } - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session,NULL); - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); - - rc = ssh_userauth_agent(session, NULL); - assert_true(rc == SSH_AUTH_SUCCESS); -} - -static void torture_auth_agent_nonblocking(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - if (!agent_is_running(session)){ - print_message("*** Agent not running. Test ignored\n"); - return; - } - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session,NULL); - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); - - ssh_set_blocking(session,0); - - do { - rc = ssh_userauth_agent(session, NULL); - } while (rc == SSH_AUTH_AGAIN); - assert_true(rc == SSH_AUTH_SUCCESS); -} - - -static void torture_auth_none(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session,NULL); - - assert_true(rc == SSH_AUTH_DENIED); - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } -} - -static void torture_auth_none_nonblocking(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - int rc; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - - ssh_set_blocking(session,0); - - do { - rc = ssh_userauth_none(session,NULL); - } while (rc == SSH_AUTH_AGAIN); - assert_true(rc == SSH_AUTH_DENIED); - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_auth_kbdint, setup, teardown), - unit_test_setup_teardown(torture_auth_kbdint_nonblocking, setup, teardown), - unit_test_setup_teardown(torture_auth_password, setup, teardown), - unit_test_setup_teardown(torture_auth_password_nonblocking, setup, teardown), - unit_test_setup_teardown(torture_auth_autopubkey, setup, teardown), - unit_test_setup_teardown(torture_auth_autopubkey_nonblocking, setup, teardown), - unit_test_setup_teardown(torture_auth_agent, setup, teardown), - unit_test_setup_teardown(torture_auth_agent_nonblocking, setup, teardown), - unit_test_setup_teardown(torture_auth_none, setup, teardown), - unit_test_setup_teardown(torture_auth_none_nonblocking, setup, teardown), - }; - - ssh_init(); - - rc = run_tests(tests); - ssh_finalize(); - - return rc; -} diff --git a/libssh/tests/client/torture_connect.c b/libssh/tests/client/torture_connect.c deleted file mode 100644 index 31573ea3..00000000 --- a/libssh/tests/client/torture_connect.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#define LIBSSH_STATIC - -#include "torture.h" -#include -#include -#include - -#define HOST "localhost" -/* Should work until Apnic decides to assign it :) */ -#define BLACKHOLE "1.1.1.1" - -static void setup(void **state) { - int verbosity=torture_libssh_verbosity(); - ssh_session session = ssh_new(); - - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - - *state = session; -} - -static void teardown(void **state) { - ssh_session session = *state; - ssh_disconnect(session); - ssh_free(session); -} - -static void torture_connect_nonblocking(void **state) { - ssh_session session = *state; - - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, HOST); - assert_true(rc == SSH_OK); - ssh_set_blocking(session,0); - - do { - rc = ssh_connect(session); - assert_true(rc != SSH_ERROR); - } while(rc == SSH_AGAIN); - - assert_true(rc == SSH_OK); -} - -static void torture_connect_timeout(void **state) { - ssh_session session = *state; - struct timeval before, after; - int rc; - long timeout = 2; - time_t sec; - suseconds_t usec; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, BLACKHOLE); - assert_true(rc == SSH_OK); - rc = ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &timeout); - assert_true(rc == SSH_OK); - - rc = gettimeofday(&before, NULL); - assert_true(rc == 0); - rc = ssh_connect(session); - assert_true(rc == SSH_ERROR); - rc = gettimeofday(&after, NULL); - assert_true(rc == 0); - sec = after.tv_sec - before.tv_sec; - usec = after.tv_usec - before.tv_usec; - /* Borrow a second for the missing usecs, but don't bother calculating */ - if (usec < 0) - sec--; - assert_in_range(sec, 1, 3); -} - -static void torture_connect_double(void **state) { - ssh_session session = *state; - - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, HOST); - assert_true(rc == SSH_OK); - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - ssh_disconnect(session); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); -} - -static void torture_connect_failure(void **state) { - /* - * The intent of this test is to check that a fresh - * ssh_new/ssh_disconnect/ssh_free sequence doesn't crash/leak - * and the behavior of a double ssh_disconnect - */ - ssh_session session = *state; - ssh_disconnect(session); -} - -static void torture_connect_socket(void **state) { - ssh_session session = *state; - - int rc; - int sock_fd = 0; - struct sockaddr_in server_addr; - - sock_fd = socket(AF_INET, SOCK_STREAM, 0); - assert_true(sock_fd > 0); - - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(22); - server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - - rc = connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); - assert_true(rc == 0); - - ssh_options_set(session, SSH_OPTIONS_FD, &sock_fd); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_connect_nonblocking, setup, teardown), - unit_test_setup_teardown(torture_connect_double, setup, teardown), - unit_test_setup_teardown(torture_connect_failure, setup, teardown), - unit_test_setup_teardown(torture_connect_timeout, setup, teardown), - unit_test_setup_teardown(torture_connect_socket, setup, teardown), - }; - - ssh_init(); - - rc = run_tests(tests); - - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/client/torture_forward.c b/libssh/tests/client/torture_forward.c deleted file mode 100644 index 5754386f..00000000 --- a/libssh/tests/client/torture_forward.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2013 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#define LIBSSH_STATIC - -#include "torture.h" -#include - -static void setup(void **state) -{ - ssh_session session; - const char *host; - const char *user; - const char *password; - - host = getenv("TORTURE_HOST"); - if (host == NULL) { - host = "localhost"; - } - - user = getenv("TORTURE_USER"); - password = getenv("TORTURE_PASSWORD"); - - session = torture_ssh_session(host, user, password); - - assert_non_null(session); - *state = session; -} - -static void teardown(void **state) -{ - ssh_session session = (ssh_session) *state; - - assert_non_null(session); - - if (ssh_is_connected(session)) { - ssh_disconnect(session); - } - ssh_free(session); -} - -static void torture_ssh_forward(void **state) -{ - ssh_session session = (ssh_session) *state; -#if 0 - ssh_channel c; -#endif - int bound_port; - int rc; - - rc = ssh_channel_listen_forward(session, "127.0.0.1", 8080, &bound_port); - assert_int_equal(rc, SSH_OK); - -#if 0 - c = ssh_forward_accept(session, 60000); - assert_non_null(c); - - ssh_channel_send_eof(c); - ssh_channel_close(c); -#endif -} - -int torture_run_tests(void) { - int rc; - - const UnitTest tests[] = { - unit_test_setup_teardown(torture_ssh_forward, setup, teardown), - }; - - ssh_init(); - - rc = run_tests(tests); - - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/client/torture_knownhosts.c b/libssh/tests/client/torture_knownhosts.c deleted file mode 100644 index df99ae90..00000000 --- a/libssh/tests/client/torture_knownhosts.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2010 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#define LIBSSH_STATIC - -#include "torture.h" -#include "session.c" -#include "known_hosts.c" - -#define KNOWNHOSTFILES "libssh_torture_knownhosts" -#define BADRSA "AAAAB3NzaC1yc2EAAAADAQABAAABAQChm5" \ - "a6Av65O8cKtx5YXOnui3wJnYE6A6J/I4kZSAibbn14Jcl+34VJQwv96f25AxNmo" \ - "NwoiZV93IzdypQmiuieh6s6wB9WhYjU9K/6CkIpNhpCxswA90b3ePjS7LnR9B9J" \ - "slPSbG1H0KC1c5lb7G3utXteXtM+4YvCvpN5VdC4CpghT+p0cwN2Na8Md5vRItz" \ - "YgIytryNn7LLiwYfoSxvWigFrTTZsrVtCOYyNgklmffpGdzuC43wdANvTewfI9G" \ - "o71r8EXmEc228CrYPmb8Scv3mpXFK/BosohSGkPlEHu9lf3YjnknBicDaVtJOYp" \ - "wnXJPjZo2EhG79HxDRpjJHH" -#define BADDSA "AAAAB3NzaC1kc3MAAACBAITDKqGQ5aC5wHySG6ZdL1+BVBY2nLP5vzw3i3pvZfP" \ - "yNUS0UCwrt5pajsMvDRGXXebTJhWVonDnv8tpSgiuIBXMZrma8CU1KCFGRzwb/n8" \ - "cc5tJmIphlOUTrObjBmsRz7u1eZmoaddXC9ask6BNnt0DmhzYi2esL3mbardy8IN" \ - "zAAAAFQDlPFCm410pgQQPb3X5FWjyVEIl+QAAAIAp0vqfir8K8p+zP4dzFG7ppnt" \ - "DjaXf3ge6URF7f5xPDo6CClGo2JQ2REF8NxM7K9cLgR9Ifx2ahO48UMgrXEl/BOp" \ - "IQHpeBqUz26a49O5J0WEW16YSUHxWwMxWVe/SRmyKdTUZJ6fcepH88JNqm3XudNn" \ - "s78grM+yx9mcXnK2AsAAAAIBxpF8ZQIlGrSgwCmCfwjP156bC3Ya6LYf9ZpLJ0dX" \ - "EcxqLVllrNEvd2EGD9p16BYO2yaalYon8im59PtOcul2ay5XQ6rVDQ2T0pgNUpsI" \ - "h0dSi8VJXI1wes5HTyLsv9VBmU1uCXUUvufoQKfF/OcSH0ufcCpnd62g1/adZcy2" \ - "WJg==" - -static void setup(void **state) { - int verbosity=torture_libssh_verbosity(); - ssh_session session = ssh_new(); - - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - - *state = session; -} - -static void teardown(void **state) { - ssh_session session = *state; - - ssh_disconnect(session); - ssh_free(session); - - unlink(KNOWNHOSTFILES); -} - -static void torture_knownhosts_port(void **state) { - ssh_session session = *state; - char buffer[200]; - char *p; - FILE *file; - int rc; - - /* Connect to localhost:22, force the port to 1234 and then write - * the known hosts file. Then check that the entry written is - * [localhost]:1234 - */ - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc==SSH_OK); - - session->opts.port = 1234; - rc = ssh_write_knownhost(session); - assert_true(rc == SSH_OK); - - file = fopen(KNOWNHOSTFILES, "r"); - assert_true(file != NULL); - p = fgets(buffer, sizeof(buffer), file); - assert_false(p == NULL); - fclose(file); - buffer[sizeof(buffer) - 1] = '\0'; - assert_true(strstr(buffer,"[localhost]:1234 ") != NULL); - - ssh_disconnect(session); - ssh_free(session); - - /* Now, connect back to the ssh server and verify the known host line */ - *state = session = ssh_new(); - - ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - session->opts.port = 1234; - rc = ssh_is_server_known(session); - assert_true(rc == SSH_SERVER_KNOWN_OK); -} - -static void torture_knownhosts_fail(void **state) { - ssh_session session = *state; - FILE *file; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa"); - assert_true(rc == SSH_OK); - - file = fopen(KNOWNHOSTFILES, "w"); - assert_true(file != NULL); - fprintf(file, "localhost ssh-rsa %s\n", BADRSA); - fclose(file); - - rc = ssh_connect(session); - assert_true(rc==SSH_OK); - - rc = ssh_is_server_known(session); - assert_true(rc == SSH_SERVER_KNOWN_CHANGED); -} - -static void torture_knownhosts_other(void **state) { - ssh_session session = *state; - FILE *file; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-dss"); - assert_true(rc == SSH_OK); - - file = fopen(KNOWNHOSTFILES, "w"); - assert_true(file != NULL); - fprintf(file, "localhost ssh-rsa %s\n", BADRSA); - fclose(file); - - rc = ssh_connect(session); - assert_true(rc==SSH_OK); - - rc = ssh_is_server_known(session); - assert_true(rc == SSH_SERVER_FOUND_OTHER); -} - -static void torture_knownhosts_other_auto(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-dss"); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc==SSH_OK); - - rc = ssh_is_server_known(session); - assert_true(rc == SSH_SERVER_NOT_KNOWN); - - rc = ssh_write_knownhost(session); - assert_true(rc == SSH_OK); - - ssh_disconnect(session); - ssh_free(session); - - /* connect again and check host key */ - *state = session = ssh_new(); - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc==SSH_OK); - - /* ssh-rsa is the default but libssh should try ssh-dss instead */ - rc = ssh_is_server_known(session); - assert_true(rc == SSH_SERVER_KNOWN_OK); -} - -static void torture_knownhosts_conflict(void **state) { - ssh_session session = *state; - FILE *file; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa"); - assert_true(rc == SSH_OK); - - file = fopen(KNOWNHOSTFILES, "w"); - assert_true(file != NULL); - fprintf(file, "localhost ssh-rsa %s\n", BADRSA); - fprintf(file, "localhost ssh-dss %s\n", BADDSA); - fclose(file); - - rc = ssh_connect(session); - assert_true(rc==SSH_OK); - - rc = ssh_is_server_known(session); - assert_true(rc == SSH_SERVER_KNOWN_CHANGED); - - rc = ssh_write_knownhost(session); - assert_true(rc==SSH_OK); - - ssh_disconnect(session); - ssh_free(session); - - /* connect again and check host key */ - *state = session = ssh_new(); - - ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa"); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_is_server_known(session); - assert_true(rc == SSH_SERVER_KNOWN_OK); -} - -static void torture_knownhosts_precheck(void **state) { - ssh_session session = *state; - FILE *file; - int rc; - char **kex; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == SSH_OK); - - rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES); - assert_true(rc == SSH_OK); - - file = fopen(KNOWNHOSTFILES, "w"); - assert_true(file != NULL); - fprintf(file, "localhost ssh-rsa %s\n", BADRSA); - fprintf(file, "localhost ssh-dss %s\n", BADDSA); - fclose(file); - - kex = ssh_knownhosts_algorithms(session); - assert_true(kex != NULL); - assert_string_equal(kex[0],"ssh-rsa"); - assert_string_equal(kex[1],"ssh-dss"); - assert_true(kex[2]==NULL); - free(kex[1]); - free(kex[0]); - free(kex); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_knownhosts_port, setup, teardown), - unit_test_setup_teardown(torture_knownhosts_fail, setup, teardown), - unit_test_setup_teardown(torture_knownhosts_other, setup, teardown), - unit_test_setup_teardown(torture_knownhosts_other_auto, setup, teardown), - unit_test_setup_teardown(torture_knownhosts_conflict, setup, teardown), - unit_test_setup_teardown(torture_knownhosts_precheck, setup, teardown) - }; - - ssh_init(); - - rc = run_tests(tests); - - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/client/torture_proxycommand.c b/libssh/tests/client/torture_proxycommand.c deleted file mode 100644 index 8cf68685..00000000 --- a/libssh/tests/client/torture_proxycommand.c +++ /dev/null @@ -1,57 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#include -#include "libssh/priv.h" - -static void setup(void **state) { - ssh_session session = ssh_new(); - - *state = session; -} - -static void teardown(void **state) { - ssh_free(*state); -} - -static void torture_options_set_proxycommand(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == 0); - - rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, "nc localhost 22"); - assert_true(rc == 0); - rc = ssh_connect(session); - assert_true(rc == SSH_OK); -} - -static void torture_options_set_proxycommand_notexist(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == 0); - - rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, "this_command_does_not_exist"); - assert_true(rc == SSH_OK); - rc = ssh_connect(session); - assert_true(rc == SSH_ERROR); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_options_set_proxycommand, setup, teardown), - unit_test_setup_teardown(torture_options_set_proxycommand_notexist, setup, teardown), - }; - - - ssh_init(); - - rc = run_tests(tests); - ssh_finalize(); - - return rc; -} diff --git a/libssh/tests/client/torture_request_env.c b/libssh/tests/client/torture_request_env.c deleted file mode 100644 index 7c7338ed..00000000 --- a/libssh/tests/client/torture_request_env.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2013 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#define LIBSSH_STATIC - -#include "torture.h" -#include - -static void setup(void **state) -{ - ssh_session session; - const char *host; - const char *user; - const char *password; - - host = getenv("TORTURE_HOST"); - if (host == NULL) { - host = "localhost"; - } - - user = getenv("TORTURE_USER"); - password = getenv("TORTURE_PASSWORD"); - - session = torture_ssh_session(host, user, password); - - assert_false(session == NULL); - *state = session; -} - -static void teardown(void **state) -{ - ssh_session session = *state; - - assert_false(session == NULL); - - if (ssh_is_connected(session)) { - ssh_disconnect(session); - } - ssh_free(session); -} - -static void torture_request_env(void **state) -{ - ssh_session session = *state; - ssh_channel c; - char buffer[4096] = {0}; - int nbytes; - int rc; - int lang_found = 0; - - c = ssh_channel_new(session); - assert_non_null(c); - - rc = ssh_channel_open_session(c); - assert_int_equal(rc, SSH_OK); - - rc = ssh_channel_request_env(c, "LC_LIBSSH", "LIBSSH"); - assert_int_equal(rc, SSH_OK); - - rc = ssh_channel_request_exec(c, "bash -c export"); - assert_int_equal(rc, SSH_OK); - - nbytes = ssh_channel_read(c, buffer, sizeof(buffer) - 1, 0); - while (nbytes > 0) { -#if 0 - rc = fwrite(buffer, 1, nbytes, stdout); - assert_int_equal(rc, nbytes); -#endif - buffer[nbytes]='\0'; - if (strstr(buffer, "LC_LIBSSH=\"LIBSSH\"")) { - lang_found = 1; - break; - } - - nbytes = ssh_channel_read(c, buffer, sizeof(buffer), 0); - } - assert_int_equal(lang_found, 1); - - ssh_channel_close(c); -} - -int torture_run_tests(void) { - int rc; - - const UnitTest tests[] = { - unit_test_setup_teardown(torture_request_env, setup, teardown), - }; - - ssh_init(); - - rc = run_tests(tests); - - ssh_finalize(); - return rc; -} - diff --git a/libssh/tests/client/torture_session.c b/libssh/tests/client/torture_session.c deleted file mode 100644 index ef9ef653..00000000 --- a/libssh/tests/client/torture_session.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2012 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#define LIBSSH_STATIC - -#include "torture.h" -#include "libssh/libssh.h" -#include "libssh/priv.h" -#include "libssh/session.h" - -#define BUFLEN 4096 -static char buffer[BUFLEN]; - -static void setup(void **state) { - int verbosity = torture_libssh_verbosity(); - ssh_session session = ssh_new(); - - ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); - - *state = session; -} - -static void teardown(void **state) { - ssh_disconnect(*state); - ssh_free(*state); -} - -static void torture_channel_read_error(void **state) { - ssh_session session = *state; - char *user = getenv("TORTURE_USER"); - ssh_channel channel; - int rc; - int i; - - if (user == NULL) { - print_message("*** Please set the environment variable TORTURE_USER" - " to enable this test!!\n"); - return; - } - - rc = ssh_options_set(session, SSH_OPTIONS_USER, user); - assert_true(rc == SSH_OK); - - rc = ssh_connect(session); - assert_true(rc == SSH_OK); - - rc = ssh_userauth_none(session,NULL); - /* This request should return a SSH_REQUEST_DENIED error */ - if (rc == SSH_ERROR) { - assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED); - } - rc = ssh_userauth_list(session, NULL); - assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY); - - rc = ssh_userauth_publickey_auto(session, NULL, NULL); - assert_true(rc == SSH_AUTH_SUCCESS); - - channel = ssh_channel_new(session); - assert_true(channel != NULL); - - rc = ssh_channel_open_session(channel); - assert_true(rc == SSH_OK); - - rc = ssh_channel_request_exec(channel, "hexdump -C /dev/urandom"); - assert_true(rc == SSH_OK); - - /* send crap and for server to send us a disconnect */ - rc = write(ssh_get_fd(session),"AAAA", 4); - assert_int_equal(rc, 4); - - for (i=0;i<20;++i){ - rc = ssh_channel_read(channel,buffer,sizeof(buffer),0); - if (rc == SSH_ERROR) - break; - } - assert_true(rc == SSH_ERROR); - - ssh_channel_free(channel); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_channel_read_error, setup, teardown), - }; - - ssh_init(); - - rc = run_tests(tests); - ssh_finalize(); - - return rc; -} diff --git a/libssh/tests/client/torture_sftp_dir.c b/libssh/tests/client/torture_sftp_dir.c deleted file mode 100644 index 8d2bcda8..00000000 --- a/libssh/tests/client/torture_sftp_dir.c +++ /dev/null @@ -1,74 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#include "sftp.c" - -static void setup(void **state) { - ssh_session session; - struct torture_sftp *t; - const char *host; - const char *user; - const char *password; - - host = getenv("TORTURE_HOST"); - if (host == NULL) { - host = "localhost"; - } - - user = getenv("TORTURE_USER"); - password = getenv("TORTURE_PASSWORD"); - - session = torture_ssh_session(host, user, password); - assert_false(session == NULL); - t = torture_sftp_session(session); - assert_false(t == NULL); - - *state = t; -} - -static void teardown(void **state) { - struct torture_sftp *t = *state; - - assert_false(t == NULL); - - torture_rmdirs(t->testdir); - torture_sftp_close(t); -} - -static void torture_sftp_mkdir(void **state) { - struct torture_sftp *t = *state; - char tmpdir[128] = {0}; - int rc; - - assert_false(t == NULL); - - snprintf(tmpdir, sizeof(tmpdir) - 1, "%s/mkdir_test", t->testdir); - - rc = sftp_mkdir(t->sftp, tmpdir, 0755); - if(rc != SSH_OK) - fprintf(stderr,"error:%s\n",ssh_get_error(t->sftp->session)); - assert_true(rc == 0); - - /* check if it really has been created */ - assert_true(torture_isdir(tmpdir)); - - rc = sftp_rmdir(t->sftp, tmpdir); - assert_true(rc == 0); - - /* check if it has been deleted */ - assert_false(torture_isdir(tmpdir)); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_sftp_mkdir, setup, teardown) - }; - - ssh_init(); - - rc = run_tests(tests); - ssh_finalize(); - - return rc; -} diff --git a/libssh/tests/client/torture_sftp_read.c b/libssh/tests/client/torture_sftp_read.c deleted file mode 100644 index 1e40e2cf..00000000 --- a/libssh/tests/client/torture_sftp_read.c +++ /dev/null @@ -1,83 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#include "sftp.c" - -#define MAX_XFER_BUF_SIZE 16384 - -static void setup(void **state) { - ssh_session session; - struct torture_sftp *t; - const char *host; - const char *user; - const char *password; - - host = getenv("TORTURE_HOST"); - if (host == NULL) { - host = "localhost"; - } - - user = getenv("TORTURE_USER"); - password = getenv("TORTURE_PASSWORD"); - - session = torture_ssh_session(host, user, password); - assert_false(session == NULL); - t = torture_sftp_session(session); - assert_false(t == NULL); - - *state = t; -} - -static void teardown(void **state) { - struct torture_sftp *t = (struct torture_sftp*) *state; - - assert_false(t == NULL); - - torture_rmdirs(t->testdir); - torture_sftp_close(t); -} - -static void torture_sftp_read_blocking(void **state) { - struct torture_sftp *t = (struct torture_sftp*) *state; - char libssh_tmp_file[] = "/tmp/libssh_sftp_test_XXXXXX"; - char buf[MAX_XFER_BUF_SIZE]; - ssize_t bytesread; - ssize_t byteswritten; - int fd; - sftp_file file; - - - file = sftp_open(t->sftp, "/usr/bin/ssh", O_RDONLY, 0); - assert_non_null(file); - - fd = mkstemp(libssh_tmp_file); - unlink(libssh_tmp_file); - - for (;;) { - bytesread = sftp_read(file, buf, MAX_XFER_BUF_SIZE); - if (bytesread == 0) { - break; /* EOF */ - } - assert_false(bytesread < 0); - - byteswritten = write(fd, buf, bytesread); - assert_int_equal(byteswritten, bytesread); - } - - close(fd); - sftp_close(file); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_sftp_read_blocking, setup, teardown) - }; - - ssh_init(); - - rc = run_tests(tests); - ssh_finalize(); - - return rc; -} diff --git a/libssh/tests/client/torture_sftp_static.c b/libssh/tests/client/torture_sftp_static.c deleted file mode 100644 index 7631def0..00000000 --- a/libssh/tests/client/torture_sftp_static.c +++ /dev/null @@ -1,32 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#include "sftp.c" - -static void torture_sftp_ext_new(void **state) { - sftp_ext x; - - (void) state; - - x = sftp_ext_new(); - assert_false(x == NULL); - assert_int_equal(x->count, 0); - assert_true(x->name == NULL); - assert_true(x->data == NULL); - - sftp_ext_free(x); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test(torture_sftp_ext_new), - }; - - ssh_init(); - - rc = run_tests(tests); - ssh_finalize(); - - return rc; -} diff --git a/libssh/tests/cmdline.c b/libssh/tests/cmdline.c deleted file mode 100644 index 4e2a7d02..00000000 --- a/libssh/tests/cmdline.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "config.h" -#include "torture.h" - -#ifdef HAVE_ARGP_H -#include - -const char *argp_program_version = "libssh test 0.2"; -const char *argp_program_bug_address = ""; - -static char **cmdline; - -/* Program documentation. */ -static char doc[] = "libssh test test"; - -/* The options we understand. */ -static struct argp_option options[] = { - { - .name = "verbose", - .key = 'v', - .arg = NULL, - .flags = 0, - .doc = "Make libssh test more verbose", - .group = 0 - }, - {NULL, 0, NULL, 0, NULL, 0} -}; - -/* Parse a single option. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* Get the input argument from argp_parse, which we - * know is a pointer to our arguments structure. - */ - struct argument_s *arguments = state->input; - - /* arg is currently not used */ - (void) arg; - - switch (key) { - case 'v': - arguments->verbose++; - break; - case ARGP_KEY_ARG: - /* End processing here. */ - cmdline = &state->argv [state->next - 1]; - state->next = state->argc; - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -/* Our argp parser. */ -/* static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL}; */ -static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL}; -#endif /* HAVE_ARGP_H */ - -void torture_cmdline_parse(int argc, char **argv, struct argument_s *arguments) { - /* - * Parse our arguments; every option seen by parse_opt will - * be reflected in arguments. - */ -#ifdef HAVE_ARGP_H - argp_parse(&argp, argc, argv, 0, 0, arguments); -#else - (void) argc; - (void) argv; - (void) arguments; -#endif /* HAVE_ARGP_H */ -} diff --git a/libssh/tests/connection.c b/libssh/tests/connection.c deleted file mode 100644 index 889c5117..00000000 --- a/libssh/tests/connection.c +++ /dev/null @@ -1,31 +0,0 @@ -/* -This file is distributed in public domain. You can do whatever you want -with its content. -*/ - -#include -#include -#include "tests.h" -SSH_OPTIONS *set_opts(int argc, char **argv){ - SSH_OPTIONS *options=ssh_options_new(); - char *host=NULL; - if(ssh_options_getopt(options,&argc, argv)){ - fprintf(stderr,"error parsing command line :%s\n",ssh_get_error(options)); - return NULL; - } - int i; - while((i=getopt(argc,argv,""))!=-1){ - switch(i){ - default: - fprintf(stderr,"unknown option %c\n",optopt); - } - } - if(optind < argc) - host=argv[optind++]; - if(host==NULL){ - fprintf(stderr,"must provide an host name\n"); - return NULL; - } - ssh_options_set_host(options,host); - return options; -} diff --git a/libssh/tests/ctest-default.cmake b/libssh/tests/ctest-default.cmake deleted file mode 100644 index a8a3e211..00000000 --- a/libssh/tests/ctest-default.cmake +++ /dev/null @@ -1,71 +0,0 @@ -## The directory to run ctest in. -set(CTEST_DIRECTORY "$ENV{HOME}/workspace/tmp/dashboards/libssh") - -## The hostname of the machine -set(CTEST_SITE "host.libssh.org") -## The buildname -set(CTEST_BUILD_NAME "Linux_2.6-GCC_4.5-x86_64-default") - -## The Makefile generator to use -set(CTEST_CMAKE_GENERATOR "Unix Makefiles") - -## The Build configuration to use. -set(CTEST_BUILD_CONFIGURATION "Debug") - -## The build options for the project -set(CTEST_BUILD_OPTIONS "-DWITH_TESTING=ON -DWITH_SSH1=ON -WITH_SFTP=ON -DWITH_SERVER=ON -DWITH_ZLIB=ON -DWITH_PCAP=ON -DDEBUG_CRYPTO=ON -DWITH_GCRYPT=OFF") - -#set(CTEST_CUSTOM_MEMCHECK_IGNORE torture_rand) - -## The Model to set: Nightly, Continous, Experimental -set(CTEST_MODEL "Experimental") - -## The branch -#set(CTEST_GIT_BRANCH "--branch v0-5") - -## Wether to enable memory checking. -set(WITH_MEMCHECK FALSE) - -## Wether to enable code coverage. -set(WITH_COVERAGE FALSE) - -####################################################################### - -if (WITH_COVERAGE AND NOT WIN32) - set(CTEST_BUILD_CONFIGURATION "Profiling") -endif (WITH_COVERAGE AND NOT WIN32) - -set(CTEST_SOURCE_DIRECTORY "${CTEST_DIRECTORY}/${CTEST_BUILD_NAME}/source") -set(CTEST_BINARY_DIRECTORY "${CTEST_DIRECTORY}/${CTEST_BUILD_NAME}/build") - -set(CTEST_MEMORYCHECK_SUPPRESSIONS_FILE ${CMAKE_SOURCE_DIR}/tests/valgrind.supp) - -find_program(CTEST_GIT_COMMAND NAMES git) -find_program(CTEST_COVERAGE_COMMAND NAMES gcov) -find_program(CTEST_MEMORYCHECK_COMMAND NAMES valgrind) - -if(NOT EXISTS "${CTEST_SOURCE_DIRECTORY}") - set(CTEST_CHECKOUT_COMMAND "${CTEST_GIT_COMMAND} clone ${CTEST_GIT_BRANCH} git://git.libssh.org/projects/libssh.git ${CTEST_SOURCE_DIRECTORY}") -endif() - -set(CTEST_UPDATE_COMMAND "${CTEST_GIT_COMMAND}") - -set(CTEST_CONFIGURE_COMMAND "${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE:STRING=${CTEST_BUILD_CONFIGURATION}") -set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} -DWITH_TESTING:BOOL=ON ${CTEST_BUILD_OPTIONS}") -set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} \"-G${CTEST_CMAKE_GENERATOR}\"") -set(CTEST_CONFIGURE_COMMAND "${CTEST_CONFIGURE_COMMAND} \"${CTEST_SOURCE_DIRECTORY}\"") - -ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY}) - -ctest_start(${CTEST_MODEL} TRACK ${CTEST_MODEL}) -ctest_update(SOURCE ${CTEST_SOURCE_DIRECTORY}) -ctest_configure(BUILD ${CTEST_BINARY_DIRECTORY}) -ctest_build(BUILD ${CTEST_BINARY_DIRECTORY}) -ctest_test(BUILD ${CTEST_BINARY_DIRECTORY}) -if (WITH_MEMCHECK AND CTEST_COVERAGE_COMMAND) - ctest_coverage(BUILD ${CTEST_BINARY_DIRECTORY}) -endif (WITH_MEMCHECK AND CTEST_COVERAGE_COMMAND) -if (WITH_MEMCHECK AND CTEST_MEMORYCHECK_COMMAND) - ctest_memcheck(BUILD ${CTEST_BINARY_DIRECTORY}) -endif (WITH_MEMCHECK AND CTEST_MEMORYCHECK_COMMAND) -ctest_submit() diff --git a/libssh/tests/generate.py b/libssh/tests/generate.py deleted file mode 100755 index 08c2d5b1..00000000 --- a/libssh/tests/generate.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/python -import os -a="" -for i in xrange(4096): - a+=chr(i % 256); -while True: - try: - os.write(1,a) - except: - exit(0) diff --git a/libssh/tests/pkd/CMakeLists.txt b/libssh/tests/pkd/CMakeLists.txt deleted file mode 100644 index 515dae10..00000000 --- a/libssh/tests/pkd/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -project(pkd C) - -if (WITH_SERVER AND UNIX AND NOT WIN32) - -include_directories( - ${LIBSSH_PUBLIC_INCLUDE_DIRS} - ${CMOCKA_INCLUDE_DIR} - ${OPENSSL_INCLUDE_DIRS} - ${GCRYPT_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/src - ${CMAKE_CURRENT_SOURCE_DIR} -) - -set(pkd_hello_src - pkd_daemon.c - pkd_hello.c - pkd_keyutil.c - pkd_util.c -) - -set(pkd_libs - ${CMOCKA_LIBRARY} - ${LIBSSH_STATIC_LIBRARY} - ${LIBSSH_LINK_LIBRARIES} - ${LIBSSH_THREADS_STATIC_LIBRARY} - ${LIBSSH_THREADS_LINK_LIBRARIES} - ${ARGP_LIBRARIES} -) - -add_executable(pkd_hello ${pkd_hello_src}) -target_link_libraries(pkd_hello ${pkd_libs}) - -endif (WITH_SERVER AND UNIX AND NOT WIN32) diff --git a/libssh/tests/pkd/pkd_client.h b/libssh/tests/pkd/pkd_client.h deleted file mode 100644 index c4a8a601..00000000 --- a/libssh/tests/pkd/pkd_client.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * pkd_client.h -- macros for generating client-specific command - * invocations for use with pkd testing - * - * (c) 2014 Jon Simons - */ - -#ifndef __PKD_CLIENT_H__ -#define __PKD_CLIENT_H__ - -/* OpenSSH */ - -#define OPENSSH_BINARY "ssh" -#define OPENSSH_KEYGEN "ssh-keygen" - -#define OPENSSH_CMD_START \ - OPENSSH_BINARY " " \ - "-o UserKnownHostsFile=/dev/null " \ - "-o StrictHostKeyChecking=no " \ - "-i " CLIENT_ID_FILE " " \ - "1> %s.out " \ - "2> %s.err " \ - "-vvv " - -#define OPENSSH_CMD_END "-p 1234 localhost ls" - -#define OPENSSH_CMD \ - OPENSSH_CMD_START OPENSSH_CMD_END - -#define OPENSSH_KEX_CMD(kexalgo) \ - OPENSSH_CMD_START "-o KexAlgorithms=" kexalgo " " OPENSSH_CMD_END - -#define OPENSSH_CIPHER_CMD(ciphers) \ - OPENSSH_CMD_START "-c " ciphers " " OPENSSH_CMD_END - -#define OPENSSH_MAC_CMD(macs) \ - OPENSSH_CMD_START "-o MACs=" macs " " OPENSSH_CMD_END - - -/* Dropbear */ - -#define DROPBEAR_BINARY "dbclient" -#define DROPBEAR_KEYGEN "dropbearkey" - -#define DROPBEAR_CMD_START \ - DROPBEAR_BINARY " " \ - "-y -y " \ - "-i " CLIENT_ID_FILE " " \ - "-v " \ - "1> %s.out " \ - "2> %s.err " - -#define DROPBEAR_CMD_END "-p 1234 localhost ls" - -#define DROPBEAR_CMD \ - DROPBEAR_CMD_START DROPBEAR_CMD_END - -#if 0 /* dbclient does not expose control over kex algo */ -#define DROPBEAR_KEX_CMD(kexalgo) \ - DROPBEAR_CMD -#endif - -#define DROPBEAR_CIPHER_CMD(ciphers) \ - DROPBEAR_CMD_START "-c " ciphers " " DROPBEAR_CMD_END - -#define DROPBEAR_MAC_CMD(macs) \ - DROPBEAR_CMD_START "-m " macs " " DROPBEAR_CMD_END - -#endif /* __PKD_CLIENT_H__ */ diff --git a/libssh/tests/pkd/pkd_daemon.c b/libssh/tests/pkd/pkd_daemon.c deleted file mode 100644 index 07736fa6..00000000 --- a/libssh/tests/pkd/pkd_daemon.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * pkd_daemon.c -- a sample public-key testing daemon using libssh - * - * Uses public key authentication to establish an exec channel and - * echo back payloads to the user. - * - * (c) 2014 Jon Simons - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "pkd_daemon.h" - -#include // for cmocka -#include - -static int pkdout_enabled; -static int pkderr_enabled; - -static void pkdout(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); -static void pkderr(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); - -static void pkdout(const char *fmt, ...) { - va_list vargs; - if (pkdout_enabled) { - va_start(vargs, fmt); - vfprintf(stdout, fmt, vargs); - va_end(vargs); - } -} - -static void pkderr(const char *fmt, ...) { - va_list vargs; - if (pkderr_enabled) { - va_start(vargs, fmt); - vfprintf(stderr, fmt, vargs); - va_end(vargs); - } -} - -/* - * pkd state: only one thread can run pkd at a time --------------------- - */ - -static struct { - int rc; - pthread_t tid; - int keep_going; - int pkd_ready; -} ctx; - -static struct { - int server_fd; - int req_exec_received; - int close_received; - int eof_received; -} pkd_state; - -static void pkd_sighandler(int signum) { - (void) signum; -} - -static int pkd_init_libssh() { - int rc = ssh_threads_set_callbacks(ssh_threads_get_pthread()); - return (rc == SSH_OK) ? 0 : 1; -} - -static int pkd_init_server_fd(short port) { - int rc = 0; - int yes = 1; - struct sockaddr_in addr; - - int server_fd = socket(PF_INET, SOCK_STREAM, 0); - if (server_fd < 0) { - rc = -1; - goto out; - } - - rc = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); - if (rc != 0) { - goto outclose; - } - - memset(&addr, 0x0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = INADDR_ANY; - rc = bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)); - if (rc != 0) { - goto outclose; - } - - rc = listen(server_fd, 128); - if (rc == 0) { - goto out; - } - -outclose: - close(server_fd); - server_fd = -1; -out: - pkd_state.server_fd = server_fd; - return rc; -} - -static int pkd_accept_fd() { - int fd = -1; - struct sockaddr_in addr; - socklen_t len = sizeof(addr); - - do { - fd = accept(pkd_state.server_fd, (struct sockaddr *) &addr, &len); - } while ((ctx.keep_going != 0) && (fd < 0) && (errno == EINTR)); - - return fd; -} - -static void pkd_eof(ssh_session session, - ssh_channel channel, - void *userdata) { - (void) session; - (void) channel; - (void) userdata; - pkdout("pkd_eof\n"); - pkd_state.eof_received = 1; -} - -static void pkd_chan_close(ssh_session session, - ssh_channel channel, - void *userdata) { - (void) session; - (void) channel; - (void) userdata; - pkdout("pkd_chan_close\n"); - pkd_state.close_received = 1; -} - -static int pkd_req_exec(ssh_session s, - ssh_channel c, - const char *cmd, - void *userdata) { - (void) s; - (void) c; - (void) cmd; - (void) userdata; - /* assumes pubkey authentication has already succeeded */ - pkdout("pkd_req_exec\n"); - pkd_state.req_exec_received = 1; - return 0; -} - -/* assumes there is only ever a single channel */ -static struct ssh_channel_callbacks_struct pkd_channel_cb = { - .channel_eof_function = pkd_eof, - .channel_close_function = pkd_chan_close, - .channel_exec_request_function = pkd_req_exec, -}; - -static int pkd_auth_pubkey_cb(ssh_session s, - const char *user, - ssh_key key, - char state, - void *userdata) { - (void) s; - (void) user; - (void) key; - (void) state; - (void) userdata; - pkdout("pkd_auth_pubkey_cb keytype %s, state: %d\n", - ssh_key_type_to_char(ssh_key_type(key)), state); - if ((state == SSH_PUBLICKEY_STATE_NONE) || - (state == SSH_PUBLICKEY_STATE_VALID)) { - return SSH_AUTH_SUCCESS; - } - return SSH_AUTH_DENIED; -} - -static int pkd_service_request_cb(ssh_session session, - const char *service, - void *userdata) { - (void) session; - (void) userdata; - pkdout("pkd_service_request_cb: %s\n", service); - return (0 == (strcmp(service, "ssh-userauth"))) ? 0 : -1; -} - -static ssh_channel pkd_channel_openreq_cb(ssh_session s, - void *userdata) { - ssh_channel c = NULL; - ssh_channel *out = (ssh_channel *) userdata; - - /* assumes pubkey authentication has already succeeded */ - pkdout("pkd_channel_openreq_cb\n"); - - c = ssh_channel_new(s); - if (c == NULL) { - pkderr("ssh_channel_new: %s\n", ssh_get_error(s)); - return NULL; - } - - ssh_callbacks_init(&pkd_channel_cb); - pkd_channel_cb.userdata = userdata; - if (ssh_set_channel_callbacks(c, &pkd_channel_cb) != SSH_OK) { - pkderr("ssh_set_channel_callbacks: %s\n", ssh_get_error(s)); - ssh_channel_free(c); - c = NULL; - } - - *out = c; - - return c; -} - -static struct ssh_server_callbacks_struct pkd_server_cb = { - .auth_pubkey_function = pkd_auth_pubkey_cb, - .service_request_function = pkd_service_request_cb, - .channel_open_request_session_function = pkd_channel_openreq_cb, -}; - -static int pkd_exec_hello(int fd, struct pkd_daemon_args *args) { - int rc = -1; - ssh_bind b = NULL; - ssh_session s = NULL; - ssh_event e = NULL; - ssh_channel c = NULL; - enum ssh_bind_options_e opts = -1; - - int level = args->opts.libssh_log_level; - enum pkd_hostkey_type_e type = args->type; - const char *hostkeypath = args->hostkeypath; - - pkd_state.eof_received = 0; - pkd_state.close_received = 0; - pkd_state.req_exec_received = 0; - - b = ssh_bind_new(); - if (b == NULL) { - pkderr("ssh_bind_new\n"); - goto outclose; - } - - if (type == PKD_RSA) { - opts = SSH_BIND_OPTIONS_RSAKEY; - } else if (type == PKD_DSA) { - opts = SSH_BIND_OPTIONS_DSAKEY; - } else if (type == PKD_ECDSA) { - opts = SSH_BIND_OPTIONS_ECDSAKEY; - } else { - pkderr("unknown kex algorithm: %d\n", type); - rc = -1; - goto outclose; - } - - rc = ssh_bind_options_set(b, opts, hostkeypath); - if (rc != 0) { - pkderr("ssh_bind_options_set: %s\n", ssh_get_error(b)); - goto outclose; - } - - rc = ssh_bind_options_set(b, SSH_BIND_OPTIONS_LOG_VERBOSITY, &level); - if (rc != 0) { - pkderr("ssh_bind_options_set log verbosity: %s\n", ssh_get_error(b)); - goto outclose; - } - - s = ssh_new(); - if (s == NULL) { - pkderr("ssh_new\n"); - goto outclose; - } - - /* - * ssh_bind_accept loads host key as side-effect. If this - * succeeds, the given 'fd' will be closed upon 'ssh_free(s)'. - */ - rc = ssh_bind_accept_fd(b, s, fd); - if (rc != SSH_OK) { - pkderr("ssh_bind_accept_fd: %s\n", ssh_get_error(b)); - goto outclose; - } - - /* accept only publickey-based auth */ - ssh_set_auth_methods(s, SSH_AUTH_METHOD_PUBLICKEY); - - /* initialize callbacks */ - ssh_callbacks_init(&pkd_server_cb); - pkd_server_cb.userdata = &c; - rc = ssh_set_server_callbacks(s, &pkd_server_cb); - if (rc != SSH_OK) { - pkderr("ssh_set_server_callbacks: %s\n", ssh_get_error(s)); - goto out; - } - - /* first do key exchange */ - rc = ssh_handle_key_exchange(s); - if (rc != SSH_OK) { - pkderr("ssh_handle_key_exchange: %s\n", ssh_get_error(s)); - goto out; - } - - /* setup and pump event to carry out exec channel */ - e = ssh_event_new(); - if (e == NULL) { - pkderr("ssh_event_new\n"); - goto out; - } - - rc = ssh_event_add_session(e, s); - if (rc != SSH_OK) { - pkderr("ssh_event_add_session\n"); - goto out; - } - - /* poll until exec channel established */ - while ((ctx.keep_going != 0) && - (rc != SSH_ERROR) && (pkd_state.req_exec_received == 0)) { - rc = ssh_event_dopoll(e, -1 /* infinite timeout */); - } - - if (rc == SSH_ERROR) { - pkderr("ssh_event_dopoll\n"); - goto out; - } else if (c == NULL) { - pkderr("poll loop exited but exec channel not ready\n"); - rc = -1; - goto out; - } - - rc = ssh_channel_write(c, "hello\n", 6); /* XXX: customizable payloads */ - if (rc != 6) { - pkderr("ssh_channel_write partial (%d)\n", rc); - } - - rc = ssh_channel_request_send_exit_status(c, 0); - if (rc != SSH_OK) { - pkderr("ssh_channel_request_send_exit_status: %s\n", - ssh_get_error(s)); - goto out; - } - - rc = ssh_channel_send_eof(c); - if (rc != SSH_OK) { - pkderr("ssh_channel_send_eof: %s\n", ssh_get_error(s)); - goto out; - } - - rc = ssh_channel_close(c); - if (rc != SSH_OK) { - pkderr("ssh_channel_close: %s\n", ssh_get_error(s)); - goto out; - } - - while ((ctx.keep_going != 0) && - (pkd_state.eof_received == 0) && - (pkd_state.close_received == 0) && - (ssh_channel_is_closed(c) == 0)) { - rc = ssh_event_dopoll(e, 1000 /* milliseconds */); - if (rc == SSH_ERROR) { - pkderr("ssh_event_dopoll for eof + close: %s\n", ssh_get_error(s)); - break; - } else { - rc = 0; - } - } - goto out; - -outclose: - close(fd); -out: - if (c != NULL) { - ssh_channel_free(c); - } - if (e != NULL) { - ssh_event_remove_session(e, s); - ssh_event_free(e); - } - if (s != NULL) { - ssh_disconnect(s); - ssh_free(s); - } - if (b != NULL) { - ssh_bind_free(b); - } - return rc; -} - -/* - * main loop ------------------------------------------------------------ - */ - -static void *pkd_main(void *args) { - int rc = -1; - struct pkd_daemon_args *a = (struct pkd_daemon_args *) args; - - struct sigaction act = { .sa_handler = pkd_sighandler, }; - - pkd_state.server_fd = -1; - pkd_state.req_exec_received = 0; - pkd_state.close_received = 0; - pkd_state.eof_received = 0; - - /* SIGUSR1 is used to interrupt 'pkd_accept_fd'. */ - rc = sigaction(SIGUSR1, &act, NULL); - if (rc != 0) { - pkderr("sigaction: %d\n", rc); - goto out; - } - - rc = pkd_init_libssh(); - if (rc != 0) { - pkderr("pkd_init_libssh: %d\n", rc); - goto out; - } - - rc = pkd_init_server_fd(1234); - if (rc != 0) { - pkderr("pkd_init_server_fd: %d\n", rc); - goto out; - } - - ctx.pkd_ready = 1; - - while (ctx.keep_going != 0) { - int fd = pkd_accept_fd(); - if (fd < 0) { - if (ctx.keep_going != 0) { - pkderr("pkd_accept_fd"); - rc = -1; - } else { - rc = 0; - } - break; - } - - rc = pkd_exec_hello(fd, a); - if (rc != 0) { - pkderr("pkd_exec_hello: %d\n", rc); - break; - } - } - - close(pkd_state.server_fd); - pkd_state.server_fd = -1; -out: - ctx.rc = rc; - - return NULL; -} - -/* - * pkd start and stop used by setup/teardown test scaffolding ----------- - */ - -int pkd_start(struct pkd_daemon_args *args) { - int rc = 0; - - pkdout_enabled = args->opts.log_stdout; - pkderr_enabled = args->opts.log_stderr; - - /* Initialize the pkd context. */ - ctx.rc = -1; - ctx.keep_going = 1; - ctx.pkd_ready = 0; - rc = pthread_create(&ctx.tid, NULL, &pkd_main, args); - assert_int_equal(rc, 0); - - /* Busy-spin until pkd thread is ready. */ - while (ctx.pkd_ready == 0); - - return rc; -} - -void pkd_stop(struct pkd_result *out) { - int rc = 0; - - ctx.keep_going = 0; - - rc = pthread_kill(ctx.tid, SIGUSR1); - assert_int_equal(rc, 0); - - rc = pthread_join(ctx.tid, NULL); - assert_int_equal(rc, 0); - - assert_non_null(out); - out->ok = (ctx.rc == 0); - - return; -} diff --git a/libssh/tests/pkd/pkd_daemon.h b/libssh/tests/pkd/pkd_daemon.h deleted file mode 100644 index c42573c1..00000000 --- a/libssh/tests/pkd/pkd_daemon.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * pkd_daemon.h -- tests use this interface to start, stop pkd - * instances and get results - * - * (c) 2014 Jon Simons - */ - -#ifndef __PKD_DAEMON_H__ -#define __PKD_DAEMON_H__ - -enum pkd_hostkey_type_e { - PKD_RSA, - PKD_DSA, - PKD_ECDSA -}; - -struct pkd_daemon_args { - enum pkd_hostkey_type_e type; - const char *hostkeypath; - - struct { - int list; - - int log_stdout; - int log_stderr; - int libssh_log_level; - - const char *testname; - unsigned int iterations; - } opts; -}; - -struct pkd_result { - int ok; -}; - -int pkd_start(struct pkd_daemon_args *args); -void pkd_stop(struct pkd_result *out); - -#endif /* __PKD_DAEMON_H__ */ diff --git a/libssh/tests/pkd/pkd_hello.c b/libssh/tests/pkd/pkd_hello.c deleted file mode 100644 index 2183f897..00000000 --- a/libssh/tests/pkd/pkd_hello.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * pkd_hello.c -- - * - * (c) 2014 Jon Simons - */ - -#include // for cmocka -#include // for cmocka -#include -#include -#include // for cmocka -#include - -#include "libssh/priv.h" - -#include "pkd_client.h" -#include "pkd_daemon.h" -#include "pkd_keyutil.h" -#include "pkd_util.h" - -#define DEFAULT_ITERATIONS 10 -static struct pkd_daemon_args pkd_dargs; - -#ifdef HAVE_ARGP_H -#include -#define PROGNAME "pkd_hello" -#define ARGP_PROGNAME "libssh " PROGNAME -const char *argp_program_version = ARGP_PROGNAME " 2014-04-12"; -const char *argp_program_bug_address = "Jon Simons "; -//static char **cmdline; -static char doc[] = \ - "\nExample usage:\n\n" - " " PROGNAME "\n" - " Run all tests with default number of iterations.\n" - " " PROGNAME " --list\n" - " List available individual test names.\n" - " " PROGNAME " -i 1000 -t torture_pkd_rsa_ecdh_sha2_nistp256\n" - " Run only the torture_pkd_rsa_ecdh_sha2_nistp256 testcase 1000 times.\n" - " " PROGNAME " -v -v -v -v -e -o\n" - " Run all tests with maximum libssh and pkd logging.\n" -; - -static struct argp_option options[] = { - { "stderr", 'e', NULL, 0, - "Emit pkd stderr messages", 0 }, - { "list", 'l', NULL, 0, - "List available individual test names", 0 }, - { "iterations", 'i', "number", 0, - "Run each test for the given number of iterations (default is 10)", 0 }, - { "stdout", 'o', NULL, 0, - "Emit pkd stdout messages", 0 }, - { "test", 't', "testname", 0, - "Run tests matching the given testname", 0 }, - { "verbose", 'v', NULL, 0, - "Increase libssh verbosity (can be used multiple times)", 0 }, - { NULL, 0, NULL, 0, - NULL, 0 }, -}; - -static error_t parse_opt(int key, char *arg, struct argp_state *state) { - (void) arg; - (void) state; - - switch(key) { - case 'e': - pkd_dargs.opts.log_stderr = 1; - break; - case 'l': - pkd_dargs.opts.list = 1; - break; - case 'i': - pkd_dargs.opts.iterations = atoi(arg); - break; - case 'o': - pkd_dargs.opts.log_stdout = 1; - break; - case 't': - pkd_dargs.opts.testname = arg; - break; - case 'v': - pkd_dargs.opts.libssh_log_level += 1; - break; - default: - return ARGP_ERR_UNKNOWN; - } - - return 0; -} - -static struct argp parser = { - options, - parse_opt, - NULL, - doc, - NULL, - NULL, - NULL -}; -#endif /* HAVE_ARGP_H */ - -static struct pkd_state *torture_pkd_setup(enum pkd_hostkey_type_e type, - const char *hostkeypath) { - int rc = 0; - - pkd_dargs.type = type; - pkd_dargs.hostkeypath = hostkeypath; - - rc = pkd_start(&pkd_dargs); - assert_int_equal(rc, 0); - - return NULL; -} - -static void torture_pkd_teardown(void **state) { - struct pkd_result result = { .ok = 0 }; - - (void) state; - - pkd_stop(&result); - assert_int_equal(result.ok, 1); -} - -/* - * one setup for each server keytype ------------------------------------ - */ - -static void torture_pkd_setup_noop(void **state) { - *state = (void *) torture_pkd_setup(PKD_RSA, NULL /*path*/); -} - -static void torture_pkd_setup_rsa(void **state) { - setup_rsa_key(); - *state = (void *) torture_pkd_setup(PKD_RSA, LIBSSH_RSA_TESTKEY); -} - -static void torture_pkd_setup_dsa(void **state) { - setup_dsa_key(); - *state = (void *) torture_pkd_setup(PKD_DSA, LIBSSH_DSA_TESTKEY); -} - -static void torture_pkd_setup_ecdsa_256(void **state) { - setup_ecdsa_keys(); - *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_256_TESTKEY); -} - -static void torture_pkd_setup_ecdsa_384(void **state) { - setup_ecdsa_keys(); - *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_384_TESTKEY); -} - -static void torture_pkd_setup_ecdsa_521(void **state) { - setup_ecdsa_keys(); - *state = (void *) torture_pkd_setup(PKD_ECDSA, LIBSSH_ECDSA_521_TESTKEY); -} - -/* - * Test matrices: f(clientname, testname, ssh-command, setup-function, teardown-function). - */ - -#define PKDTESTS_DEFAULT(f, client, cmd) \ - /* Default passes by server key type. */ \ - f(client, rsa_default, cmd, setup_rsa, teardown) \ - f(client, dsa_default, cmd, setup_dsa, teardown) \ - f(client, ecdsa_256_default, cmd, setup_ecdsa_256, teardown) \ - f(client, ecdsa_384_default, cmd, setup_ecdsa_384, teardown) \ - f(client, ecdsa_521_default, cmd, setup_ecdsa_521, teardown) - -#define PKDTESTS_KEX(f, client, kexcmd) \ - /* Kex algorithms. */ \ - f(client, rsa_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_rsa, teardown) \ - f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_rsa, teardown) \ - f(client, rsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_rsa, teardown) \ - f(client, rsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_rsa, teardown) \ - f(client, dsa_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_dsa, teardown) \ - f(client, dsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_dsa, teardown) \ - f(client, dsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_dsa, teardown) \ - f(client, dsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_dsa, teardown) \ - f(client, ecdsa_256_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_384_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_521_curve25519_sha256, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256 "), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_521, teardown) - -#define PKDTESTS_CIPHER(f, client, ciphercmd) \ - /* Ciphers. */ \ - f(client, rsa_3des_cbc, ciphercmd("3des-cbc"), setup_rsa, teardown) \ - f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown) \ - f(client, rsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_rsa, teardown) \ - f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \ - f(client, rsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_rsa, teardown) \ - f(client, rsa_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_rsa, teardown) \ - f(client, dsa_3des_cbc, ciphercmd("3des-cbc"), setup_dsa, teardown) \ - f(client, dsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_dsa, teardown) \ - f(client, dsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_dsa, teardown) \ - f(client, dsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_dsa, teardown) \ - f(client, dsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_dsa, teardown) \ - f(client, dsa_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_dsa, teardown) \ - f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_blowfish_cbc, ciphercmd("blowfish-cbc"), setup_ecdsa_521, teardown) - -#define PKDTESTS_CIPHER_AES192(f, client, ciphercmd) \ - /* Ciphers. */ \ - f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \ - f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \ - f(client, dsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_dsa, teardown) \ - f(client, dsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_dsa, teardown) \ - f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \ - f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown) - -#define PKDTESTS_MAC(f, client, maccmd) \ - /* MACs. */ \ - f(client, rsa_hmac_sha1, maccmd("hmac-sha1"), setup_rsa, teardown) \ - f(client, dsa_hmac_sha1, maccmd("hmac-sha1"), setup_dsa, teardown) \ - f(client, ecdsa_256_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_384_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_521_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_521, teardown) \ - f(client, rsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_rsa, teardown) \ - f(client, dsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_dsa, teardown) \ - f(client, ecdsa_256_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_384_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_521_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_521, teardown) \ - f(client, rsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_rsa, teardown) \ - f(client, dsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_dsa, teardown) \ - f(client, ecdsa_256_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_256, teardown) \ - f(client, ecdsa_384_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_384, teardown) \ - f(client, ecdsa_521_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_521, teardown) - -static void torture_pkd_client_noop(void **state) { - struct pkd_state *pstate = (struct pkd_state *) (*state); - (void) pstate; - return; -} - -static void torture_pkd_runtest(const char *testname, - const char *testcmd) -{ - int i, rc; - char logfile[1024] = { 0 }; - int iterations = - (pkd_dargs.opts.iterations != 0) ? pkd_dargs.opts.iterations - : DEFAULT_ITERATIONS; - - for (i = 0; i < iterations; i++) { - rc = system_checked(testcmd); - assert_int_equal(rc, 0); - } - - /* Asserts did not trip: cleanup logs. */ - snprintf(&logfile[0], sizeof(logfile), "%s.out", testname); - unlink(logfile); - snprintf(&logfile[0], sizeof(logfile), "%s.err", testname); - unlink(logfile); -} - -/* - * Though each keytest function body is the same, separate functions are - * defined here to result in distinct output when running the tests. - */ - -#define emit_keytest(client, testname, sshcmd, setup, teardown) \ - static void torture_pkd_## client ## _ ## testname(void **state) { \ - const char *tname = "torture_pkd_" #client "_" #testname; \ - char testcmd[1024] = { 0 }; \ - (void) state; \ - snprintf(&testcmd[0], sizeof(testcmd), sshcmd, tname, tname); \ - torture_pkd_runtest(tname, testcmd); \ - } - -/* - * Actual test functions are emitted here. - */ - -#define CLIENT_ID_FILE OPENSSH_DSA_TESTKEY -PKDTESTS_DEFAULT(emit_keytest, openssh_dsa, OPENSSH_CMD) -PKDTESTS_KEX(emit_keytest, openssh_dsa, OPENSSH_KEX_CMD) -PKDTESTS_CIPHER(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD) -PKDTESTS_CIPHER_AES192(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD) -PKDTESTS_MAC(emit_keytest, openssh_dsa, OPENSSH_MAC_CMD) -#undef CLIENT_ID_FILE - -#define CLIENT_ID_FILE OPENSSH_RSA_TESTKEY -PKDTESTS_DEFAULT(emit_keytest, openssh_rsa, OPENSSH_CMD) -PKDTESTS_KEX(emit_keytest, openssh_rsa, OPENSSH_KEX_CMD) -PKDTESTS_CIPHER(emit_keytest, openssh_rsa, OPENSSH_CIPHER_CMD) -PKDTESTS_CIPHER_AES192(emit_keytest, openssh_rsa, OPENSSH_CIPHER_CMD) -PKDTESTS_MAC(emit_keytest, openssh_rsa, OPENSSH_MAC_CMD) -#undef CLIENT_ID_FILE - -#define CLIENT_ID_FILE OPENSSH_ECDSA256_TESTKEY -PKDTESTS_DEFAULT(emit_keytest, openssh_e256, OPENSSH_CMD) -PKDTESTS_KEX(emit_keytest, openssh_e256, OPENSSH_KEX_CMD) -PKDTESTS_CIPHER(emit_keytest, openssh_e256, OPENSSH_CIPHER_CMD) -PKDTESTS_CIPHER_AES192(emit_keytest, openssh_e256, OPENSSH_CIPHER_CMD) -PKDTESTS_MAC(emit_keytest, openssh_e256, OPENSSH_MAC_CMD) -#undef CLIENT_ID_FILE - -/* Could add these passes, too: */ -//#define CLIENT_ID_FILE OPENSSH_ECDSA384_TESTKEY -//#define CLIENT_ID_FILE OPENSSH_ECDSA521_TESTKEY - -#define CLIENT_ID_FILE OPENSSH_ED25519_TESTKEY -PKDTESTS_DEFAULT(emit_keytest, openssh_ed, OPENSSH_CMD) -PKDTESTS_KEX(emit_keytest, openssh_ed, OPENSSH_KEX_CMD) -PKDTESTS_CIPHER(emit_keytest, openssh_ed, OPENSSH_CIPHER_CMD) -PKDTESTS_CIPHER_AES192(emit_keytest, openssh_ed, OPENSSH_CIPHER_CMD) -PKDTESTS_MAC(emit_keytest, openssh_ed, OPENSSH_MAC_CMD) -#undef CLIENT_ID_FILE - -#define CLIENT_ID_FILE DROPBEAR_RSA_TESTKEY -PKDTESTS_DEFAULT(emit_keytest, dropbear, DROPBEAR_CMD) -PKDTESTS_CIPHER(emit_keytest, dropbear, DROPBEAR_CIPHER_CMD) -PKDTESTS_MAC(emit_keytest, dropbear, DROPBEAR_MAC_CMD) -#undef CLIENT_ID_FILE - -/* - * Define an array of testname strings mapped to their associated - * test function. Enables running tests individually by name from - * the command line. - */ - -#define emit_testmap(client, testname, sshcmd, setup, teardown) \ - { "torture_pkd_" #client "_" #testname, \ - { emit_unit_test(client, testname, sshcmd, setup, teardown) } }, - -#define emit_unit_test(client, testname, sshcmd, setup, teardown) \ - unit_test_setup_teardown(torture_pkd_ ## client ## _ ## testname, \ - torture_pkd_ ## setup, \ - torture_pkd_ ## teardown) - -#define emit_unit_test_comma(client, testname, sshcmd, setup, teardown) \ - emit_unit_test(client, testname, sshcmd, setup, teardown), - -struct { - const char *testname; - const UnitTest test[3]; /* requires setup + test + teardown */ -} testmap[] = { - /* OpenSSH */ - PKDTESTS_DEFAULT(emit_testmap, openssh_dsa, OPENSSH_CMD) - PKDTESTS_KEX(emit_testmap, openssh_dsa, OPENSSH_KEX_CMD) - PKDTESTS_CIPHER(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD) - PKDTESTS_CIPHER_AES192(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD) - PKDTESTS_MAC(emit_testmap, openssh_dsa, OPENSSH_MAC_CMD) - - PKDTESTS_DEFAULT(emit_testmap, openssh_rsa, OPENSSH_CMD) - PKDTESTS_KEX(emit_testmap, openssh_rsa, OPENSSH_KEX_CMD) - PKDTESTS_CIPHER(emit_testmap, openssh_rsa, OPENSSH_CIPHER_CMD) - PKDTESTS_CIPHER_AES192(emit_testmap, openssh_rsa, OPENSSH_CIPHER_CMD) - PKDTESTS_MAC(emit_testmap, openssh_rsa, OPENSSH_MAC_CMD) - - PKDTESTS_DEFAULT(emit_testmap, openssh_e256, OPENSSH_CMD) - PKDTESTS_KEX(emit_testmap, openssh_e256, OPENSSH_KEX_CMD) - PKDTESTS_CIPHER(emit_testmap, openssh_e256, OPENSSH_CIPHER_CMD) - PKDTESTS_CIPHER_AES192(emit_testmap, openssh_e256, OPENSSH_CIPHER_CMD) - PKDTESTS_MAC(emit_testmap, openssh_e256, OPENSSH_MAC_CMD) - - PKDTESTS_DEFAULT(emit_testmap, openssh_ed, OPENSSH_CMD) - PKDTESTS_KEX(emit_testmap, openssh_ed, OPENSSH_KEX_CMD) - PKDTESTS_CIPHER(emit_testmap, openssh_ed, OPENSSH_CIPHER_CMD) - PKDTESTS_CIPHER_AES192(emit_testmap, openssh_ed, OPENSSH_CIPHER_CMD) - PKDTESTS_MAC(emit_testmap, openssh_ed, OPENSSH_MAC_CMD) - - /* Dropbear */ - PKDTESTS_DEFAULT(emit_testmap, dropbear, DROPBEAR_CMD) - PKDTESTS_CIPHER(emit_testmap, dropbear, DROPBEAR_CIPHER_CMD) - PKDTESTS_MAC(emit_testmap, dropbear, DROPBEAR_MAC_CMD) - - /* Noop */ - emit_testmap(client, noop, "", setup_noop, teardown) - - /* NULL tail entry */ - { NULL, { { NULL, NULL, 0 }, { NULL, NULL, 0 }, { NULL, NULL, 0 } } } -}; - -static int pkd_run_tests(void) { - int rc = -1; - int tindex = 0; - - const UnitTest openssh_tests[] = { - PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_dsa, OPENSSH_CMD) - PKDTESTS_KEX(emit_unit_test_comma, openssh_dsa, OPENSSH_KEX_CMD) - PKDTESTS_CIPHER(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD) - PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD) - PKDTESTS_MAC(emit_unit_test_comma, openssh_dsa, OPENSSH_MAC_CMD) - - PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_rsa, OPENSSH_CMD) - PKDTESTS_KEX(emit_unit_test_comma, openssh_rsa, OPENSSH_KEX_CMD) - PKDTESTS_CIPHER(emit_unit_test_comma, openssh_rsa, OPENSSH_CIPHER_CMD) - PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_rsa, OPENSSH_CIPHER_CMD) - PKDTESTS_MAC(emit_unit_test_comma, openssh_rsa, OPENSSH_MAC_CMD) - - PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_e256, OPENSSH_CMD) - PKDTESTS_KEX(emit_unit_test_comma, openssh_e256, OPENSSH_KEX_CMD) - PKDTESTS_CIPHER(emit_unit_test_comma, openssh_e256, OPENSSH_CIPHER_CMD) - PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_e256, OPENSSH_CIPHER_CMD) - PKDTESTS_MAC(emit_unit_test_comma, openssh_e256, OPENSSH_MAC_CMD) - - PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_ed, OPENSSH_CMD) - PKDTESTS_KEX(emit_unit_test_comma, openssh_ed, OPENSSH_KEX_CMD) - PKDTESTS_CIPHER(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD) - PKDTESTS_CIPHER_AES192(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD) - PKDTESTS_MAC(emit_unit_test_comma, openssh_ed, OPENSSH_MAC_CMD) - }; - - const UnitTest dropbear_tests[] = { - PKDTESTS_DEFAULT(emit_unit_test_comma, dropbear, DROPBEAR_CMD) - PKDTESTS_CIPHER(emit_unit_test_comma, dropbear, DROPBEAR_CIPHER_CMD) - PKDTESTS_MAC(emit_unit_test_comma, dropbear, DROPBEAR_MAC_CMD) - }; - - const UnitTest noop_tests[] = { - emit_unit_test(client, noop, "", setup_noop, teardown) - }; - - /* Test list is populated depending on which clients are enabled. */ - UnitTest all_tests[(sizeof(openssh_tests) / sizeof(openssh_tests[0])) + - (sizeof(dropbear_tests) / sizeof(dropbear_tests[0])) + - (sizeof(noop_tests) / sizeof(noop_tests[0]))]; - memset(&all_tests[0], 0x0, sizeof(all_tests)); - - /* Generate client keys and populate test list for each enabled client. */ - if (is_openssh_client_enabled()) { - setup_openssh_client_keys(); - memcpy(&all_tests[tindex], &openssh_tests[0], sizeof(openssh_tests)); - tindex += (sizeof(openssh_tests) / sizeof(openssh_tests[0])); - } - - if (is_dropbear_client_enabled()) { - setup_dropbear_client_rsa_key(); - memcpy(&all_tests[tindex], &dropbear_tests[0], sizeof(dropbear_tests)); - tindex += (sizeof(dropbear_tests) / sizeof(dropbear_tests[0])); - } - - memcpy(&all_tests[tindex], &noop_tests[0], sizeof(noop_tests)); - tindex += (sizeof(noop_tests) / sizeof(noop_tests[0])); - - if (pkd_dargs.opts.testname == NULL) { - rc = _run_tests(all_tests, tindex); - } else { - int i = 0; - const UnitTest *found = NULL; - const char *testname = pkd_dargs.opts.testname; - - while (testmap[i].testname != NULL) { - if (strcmp(testmap[i].testname, testname) == 0) { - found = &testmap[i].test[0]; - break; - } - i += 1; - } - - if (found != NULL) { - rc = _run_tests(found, 3); - } else { - fprintf(stderr, "Did not find test '%s'\n", testname); - } - } - - /* Clean up client keys for each enabled client. */ - if (is_dropbear_client_enabled()) { - cleanup_dropbear_client_rsa_key(); - } - - if (is_openssh_client_enabled()) { - cleanup_openssh_client_keys(); - } - - /* Clean up any server keys that were generated. */ - cleanup_rsa_key(); - cleanup_dsa_key(); - cleanup_ecdsa_keys(); - - return rc; -} - -int main(int argc, char **argv) { - int i = 0; - int rc = 0; - - unsetenv("SSH_AUTH_SOCK"); - - rc = ssh_init(); - if (rc != 0) { - rc = SSH_ERROR; - goto out; - } - -#ifdef HAVE_ARGP_H - argp_parse(&parser, argc, argv, 0, 0, NULL); -#else /* HAVE_ARGP_H */ - (void) argc; (void) argv; -#endif /* HAVE_ARGP_H */ - - if (pkd_dargs.opts.list != 0) { - while (testmap[i].testname != NULL) { - printf("%s\n", testmap[i++].testname); - } - } else { - rc = pkd_run_tests(); - } - - rc = ssh_finalize(); - if (rc != 0) { - fprintf(stderr, "ssh_finalize: %d\n", rc); - } -out: - return rc; -} diff --git a/libssh/tests/pkd/pkd_keyutil.c b/libssh/tests/pkd/pkd_keyutil.c deleted file mode 100644 index e1e1ecb8..00000000 --- a/libssh/tests/pkd/pkd_keyutil.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * pkd_keyutil.c -- pkd test key utilities - * - * (c) 2014 Jon Simons - */ - -#include // for cmocka -#include // for cmocka -#include // for cmocka -#include - -#include -#include -#include -#include - -#include "pkd_client.h" -#include "pkd_keyutil.h" -#include "pkd_util.h" - -void setup_rsa_key() { - int rc = 0; - if (access(LIBSSH_RSA_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f " - LIBSSH_RSA_TESTKEY); - } - assert_int_equal(rc, 0); -} - -void setup_dsa_key() { - int rc = 0; - if (access(LIBSSH_DSA_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f " - LIBSSH_DSA_TESTKEY); - } - assert_int_equal(rc, 0); -} - -void setup_ecdsa_keys() { - int rc = 0; - - if (access(LIBSSH_ECDSA_256_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 256 -q -N \"\" -f " - LIBSSH_ECDSA_256_TESTKEY); - assert_int_equal(rc, 0); - } - if (access(LIBSSH_ECDSA_384_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 384 -q -N \"\" -f " - LIBSSH_ECDSA_384_TESTKEY); - assert_int_equal(rc, 0); - } - if (access(LIBSSH_ECDSA_521_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 521 -q -N \"\" -f " - LIBSSH_ECDSA_521_TESTKEY); - assert_int_equal(rc, 0); - } -} - -static void cleanup_key(const char *privkey, const char *pubkey) { - unlink(privkey); - unlink(pubkey); -} - -void cleanup_rsa_key() { - cleanup_key(LIBSSH_RSA_TESTKEY, LIBSSH_RSA_TESTKEY ".pub"); -} - -void cleanup_dsa_key() { - cleanup_key(LIBSSH_DSA_TESTKEY, LIBSSH_DSA_TESTKEY ".pub"); -} - -void cleanup_ecdsa_keys() { - cleanup_key(LIBSSH_ECDSA_256_TESTKEY, LIBSSH_ECDSA_256_TESTKEY ".pub"); - cleanup_key(LIBSSH_ECDSA_384_TESTKEY, LIBSSH_ECDSA_384_TESTKEY ".pub"); - cleanup_key(LIBSSH_ECDSA_521_TESTKEY, LIBSSH_ECDSA_521_TESTKEY ".pub"); -} - -void setup_openssh_client_keys() { - int rc = 0; - - if (access(OPENSSH_DSA_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t dsa -q -N \"\" -f " - OPENSSH_DSA_TESTKEY); - } - assert_int_equal(rc, 0); - - if (access(OPENSSH_RSA_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f " - OPENSSH_RSA_TESTKEY); - } - assert_int_equal(rc, 0); - - if (access(OPENSSH_ECDSA256_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 256 -q -N \"\" -f " - OPENSSH_ECDSA256_TESTKEY); - } - assert_int_equal(rc, 0); - - if (access(OPENSSH_ECDSA384_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 384 -q -N \"\" -f " - OPENSSH_ECDSA384_TESTKEY); - } - assert_int_equal(rc, 0); - - if (access(OPENSSH_ECDSA521_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 521 -q -N \"\" -f " - OPENSSH_ECDSA521_TESTKEY); - } - assert_int_equal(rc, 0); - - if (access(OPENSSH_ED25519_TESTKEY, F_OK) != 0) { - rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f " - OPENSSH_ED25519_TESTKEY); - } - assert_int_equal(rc, 0); -} - -void cleanup_openssh_client_keys() { - cleanup_key(OPENSSH_DSA_TESTKEY, OPENSSH_DSA_TESTKEY ".pub"); - cleanup_key(OPENSSH_RSA_TESTKEY, OPENSSH_RSA_TESTKEY ".pub"); - cleanup_key(OPENSSH_ECDSA256_TESTKEY, OPENSSH_ECDSA256_TESTKEY ".pub"); - cleanup_key(OPENSSH_ECDSA384_TESTKEY, OPENSSH_ECDSA384_TESTKEY ".pub"); - cleanup_key(OPENSSH_ECDSA521_TESTKEY, OPENSSH_ECDSA521_TESTKEY ".pub"); - cleanup_key(OPENSSH_ED25519_TESTKEY, OPENSSH_ED25519_TESTKEY ".pub"); -} - -void setup_dropbear_client_rsa_key() { - int rc = 0; - if (access(DROPBEAR_RSA_TESTKEY, F_OK) != 0) { - rc = system_checked(DROPBEAR_KEYGEN " -t rsa -f " - DROPBEAR_RSA_TESTKEY " 1>/dev/null 2>/dev/null"); - } - assert_int_equal(rc, 0); -} - -void cleanup_dropbear_client_rsa_key() { - unlink(DROPBEAR_RSA_TESTKEY); -} diff --git a/libssh/tests/pkd/pkd_keyutil.h b/libssh/tests/pkd/pkd_keyutil.h deleted file mode 100644 index 8e9de009..00000000 --- a/libssh/tests/pkd/pkd_keyutil.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * pkd_keyutil.h -- - * - * (c) 2014 Jon Simons - */ - -#ifndef __PKD_KEYUTIL_H__ -#define __PKD_KEYUTIL_H__ - -/* Server keys. */ -#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa" -#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa" -#define LIBSSH_ECDSA_256_TESTKEY "libssh_testkey.id_ecdsa256" -#define LIBSSH_ECDSA_384_TESTKEY "libssh_testkey.id_ecdsa384" -#define LIBSSH_ECDSA_521_TESTKEY "libssh_testkey.id_ecdsa521" - -void setup_dsa_key(void); -void setup_rsa_key(void); -void setup_ecdsa_keys(void); -void cleanup_dsa_key(void); -void cleanup_rsa_key(void); -void cleanup_ecdsa_keys(void); - -/* Client keys. */ -#define OPENSSH_DSA_TESTKEY "openssh_testkey.id_dsa" -#define OPENSSH_RSA_TESTKEY "openssh_testkey.id_rsa" -#define OPENSSH_ECDSA256_TESTKEY "openssh_testkey.id_ecdsa256" -#define OPENSSH_ECDSA384_TESTKEY "openssh_testkey.id_ecdsa384" -#define OPENSSH_ECDSA521_TESTKEY "openssh_testkey.id_ecdsa521" -#define OPENSSH_ED25519_TESTKEY "openssh_testkey.id_ed25519" - -#define DROPBEAR_RSA_TESTKEY "dropbear_testkey.id_rsa" - -void setup_openssh_client_keys(void); -void cleanup_openssh_client_keys(void); - -void setup_dropbear_client_rsa_key(void); -void cleanup_dropbear_client_rsa_key(void); - -#endif /* __PKD_KEYUTIL_H__ */ diff --git a/libssh/tests/pkd/pkd_util.c b/libssh/tests/pkd/pkd_util.c deleted file mode 100644 index 963b58d6..00000000 --- a/libssh/tests/pkd/pkd_util.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * pkd_util.c -- pkd utilities - * - * (c) 2014 Jon Simons - */ - -#include -#include -#include -#include - -#include "pkd_client.h" -#include "pkd_util.h" - -/** - * @brief runs system(3); exits if that is interrupted with SIGINT/QUIT - * @returns 0 upon success, non-zero otherwise - */ -int system_checked(const char *cmd) { - int rc = system(cmd); - - if (WIFSIGNALED(rc) && - ((WTERMSIG(rc) == SIGINT) || (WTERMSIG(rc) == SIGQUIT))) { - exit(1); - } - - if (rc == -1) { - return -1; - } - - return WEXITSTATUS(rc); -} - -static int bin_exists(const char *binary) { - char bin[1024] = { 0 }; - snprintf(&bin[0], sizeof(bin), "type %s 1>/dev/null 2>/dev/null", binary); - return (system_checked(bin) == 0); -} - -int is_openssh_client_enabled(void) { - return (bin_exists(OPENSSH_BINARY) && bin_exists(OPENSSH_KEYGEN)); -} - -int is_dropbear_client_enabled(void) { - return (bin_exists(DROPBEAR_BINARY) && bin_exists(DROPBEAR_KEYGEN)); -} diff --git a/libssh/tests/pkd/pkd_util.h b/libssh/tests/pkd/pkd_util.h deleted file mode 100644 index aedbbe9f..00000000 --- a/libssh/tests/pkd/pkd_util.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * pkd_keyutil.h -- - * - * (c) 2014 Jon Simons - */ - -#ifndef __PKD_UTIL_H__ -#define __PKD_UTIL_H__ - -int system_checked(const char *cmd); - -/* Is client 'X' enabled? */ -int is_openssh_client_enabled(void); -int is_dropbear_client_enabled(void); - -#endif /* __PKD_UTIL_H__ */ diff --git a/libssh/tests/sftp_stress/main.c b/libssh/tests/sftp_stress/main.c deleted file mode 100644 index c9b0ad9f..00000000 --- a/libssh/tests/sftp_stress/main.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * main.c - * - * Created on: 22 juin 2009 - * Author: aris - */ -#include -#include -#include -#include -#include -#include -#include -#include -#define TEST_READ 1 -#define TEST_WRITE 2 -#define NTHREADS 3 -#define FILESIZE 100000 -unsigned char samplefile[FILESIZE]; -volatile int stop=0; - -const char* hosts[]={"localhost","barebone"}; -void signal_stop(){ - stop=1; - printf("Stopping...\n"); -} - -SSH_SESSION *connect_host(const char *hostname); -int sftp_test(SSH_SESSION *session, int test); - -int docycle(const char *host, int test){ - SSH_SESSION *session=connect_host(host); - int ret=SSH_ERROR; - if(!session){ - printf("connect failed\n"); - } else { - printf("Connected\n"); - ret=sftp_test(session,test); - if(ret != SSH_OK){ - printf("Error in sftp\n"); - } - ssh_disconnect(session); - } - return ret; -} - -int thread(){ - while(docycle(hosts[rand()%2],TEST_WRITE) == SSH_OK) - if(stop) - break; - return 0; -} - -int main(int argc, char **argv){ - int i; - pthread_t threads[NTHREADS]; - ssh_init(); - srand(time(NULL)); - for(i=0;i -#include -#include -#include "tests.h" - -void do_connect(SSH_SESSION *session) { - char buf[4096] = {0}; - CHANNEL *channel; - - int error = ssh_connect(session); - if (error != SSH_OK) { - fprintf(stderr,"Error at connection: %s\n", ssh_get_error(session)); - return; - } - printf("Connected\n"); - - ssh_is_server_known(session); - - error = authenticate(session); - if(error != SSH_AUTH_SUCCESS) { - fprintf(stderr,"Error at authentication: %s\n", ssh_get_error(session)); - return; - } - printf("Authenticated\n"); - channel = ssh_channel_new(session); - ssh_channel_open_session(channel); - printf("Execute 'ls' on the channel\n"); - error = ssh_channel_request_exec(channel, "ls"); - if(error != SSH_OK){ - fprintf(stderr, "Error executing command: %s\n", ssh_get_error(session)); - return; - } - printf("--------------------output----------------------\n"); - while (ssh_channel_read(channel, buf, sizeof(buf), 0)) { - printf("%s", buf); - } - printf("\n"); - printf("---------------------end------------------------\n"); - ssh_channel_send_eof(channel); - fprintf(stderr, "Exit status: %d\n", ssh_channel_get_exit_status(channel)); - - printf("\nChannel test finished\n"); - ssh_channel_close(channel); - ssh_channel_free(channel); -} - -int main(int argc, char **argv){ - SSH_OPTIONS *options=set_opts(argc, argv); - SSH_SESSION *session=ssh_new(); - if(options==NULL){ - return 1; - } - ssh_set_options(session,options); - do_connect(session); - ssh_disconnect(session); - ssh_finalize(); - return 0; -} diff --git a/libssh/tests/test_pcap.c b/libssh/tests/test_pcap.c deleted file mode 100644 index 01aa714a..00000000 --- a/libssh/tests/test_pcap.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/* Simple test for the pcap functions */ - -#include -#include -#include - -#include -#include -#include - -int main(int argc, char **argv){ - ssh_pcap_file pcap; - ssh_pcap_context ctx; - ssh_buffer buffer=ssh_buffer_new(); - char *str="Hello, this is a test string to test the capabilities of the" - "pcap file writer."; - printf("Simple pcap tester\n"); - pcap=ssh_pcap_file_new(); - if(ssh_pcap_file_open(pcap,"test.cap") != SSH_OK){ - printf("error happened\n"); - return EXIT_FAILURE; - } - buffer_add_data(buffer,str,strlen(str)); - ctx=ssh_pcap_context_new(NULL); - ssh_pcap_context_set_file(ctx,pcap); - ssh_pcap_context_write(ctx,SSH_PCAP_DIR_OUT,str,strlen(str),strlen(str)); - - return EXIT_SUCCESS; -} diff --git a/libssh/tests/test_socket.c b/libssh/tests/test_socket.c deleted file mode 100644 index 84f7b35e..00000000 --- a/libssh/tests/test_socket.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of the SSH Library - * - * Copyright (c) 2009 by Aris Adamantiadis - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/* Simple test for the socket callbacks */ - -#include -#include -#include -#include - -#include -#include -#include - -int stop=0; -ssh_socket s; - -static int data_rcv(const void *data, size_t len, void *user){ - printf("Received data: '"); - fwrite(data,1,len,stdout); - printf("'\n"); - ssh_socket_write(s,"Hello you !\n",12); - ssh_socket_nonblocking_flush(s); - return len; -} - -static void controlflow(int code,void *user){ - printf("Control flow: %x\n",code); -} - -static void exception(int code, int errno_code,void *user){ - printf("Exception: %d (%d)\n",code,errno_code); - stop=1; -} - -static void connected(int code, int errno_code,void *user){ - if(code == SSH_SOCKET_CONNECTED_OK) - printf("Connected: %d (%d)\n",code, errno_code); - else { - printf("Error while connecting:(%d, %d:%s)\n",code,errno_code,strerror(errno_code)); - stop=1; - } -} - -struct ssh_socket_callbacks_struct callbacks={ - data_rcv, - controlflow, - exception, - connected, - NULL -}; -int main(int argc, char **argv){ - ssh_session session; - ssh_poll_ctx ctx; - int verbosity=SSH_LOG_FUNCTIONS; - if(argc < 3){ - printf("Usage : %s host port\n", argv[0]); - return EXIT_FAILURE; - } - session=ssh_new(); - ssh_options_set(session,SSH_OPTIONS_LOG_VERBOSITY,&verbosity); - ssh_init(); - s=ssh_socket_new(session); - ctx=ssh_poll_ctx_new(2); - ssh_socket_set_callbacks(s, &callbacks); - ssh_poll_ctx_add_socket(ctx,s); - if(ssh_socket_connect(s,argv[1],atoi(argv[2]),NULL) != SSH_OK){ - printf("ssh_socket_connect: %s\n",ssh_get_error(session)); - return EXIT_FAILURE; - } - while(!stop) - ssh_poll_ctx_dopoll(ctx,-1); - printf("finished\n"); - return EXIT_SUCCESS; -} diff --git a/libssh/tests/test_ssh_bind_accept_fd.c b/libssh/tests/test_ssh_bind_accept_fd.c deleted file mode 100644 index 7611cf4c..00000000 --- a/libssh/tests/test_ssh_bind_accept_fd.c +++ /dev/null @@ -1,139 +0,0 @@ -/* Test the ability to use ssh_bind_accept_fd. - * - * Expected behavior: Prints "SUCCESS!" - * - * Faulty behavior observed before change: Connection timeout - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct options { - const char *server_keyfile; -} options; - -const char HOST[] = "127.0.0.1"; -const int PORT = 3333; - -int get_connection() { - int rc, server_socket, client_conn = -1; - struct sockaddr_in server_socket_addr; - struct sockaddr_storage client_conn_addr; - socklen_t client_conn_addr_size = sizeof(client_conn_addr); - - server_socket = socket(PF_INET, SOCK_STREAM, 0); - if (server_socket < 0) { - goto out; - } - - server_socket_addr.sin_family = AF_INET; - server_socket_addr.sin_port = htons(PORT); - if (inet_pton(AF_INET, HOST, &server_socket_addr.sin_addr) != 1) { - goto out; - } - - rc = bind(server_socket, (struct sockaddr *)&server_socket_addr, - sizeof(server_socket_addr)); - if (rc < 0) { - goto out; - } - - if (listen(server_socket, 0) < 0) { - goto out; - } - - client_conn = accept(server_socket, - (struct sockaddr *)&client_conn_addr, - &client_conn_addr_size); - - out: - return client_conn; -} - -void ssh_server() { - ssh_bind bind; - ssh_session session; - - int client_conn = get_connection(); - if (client_conn < 0) { - err(1, "get_connection"); - } - - bind = ssh_bind_new(); - if (!bind) { - errx(1, "ssh_bind_new"); - } - - if (ssh_bind_options_set(bind, SSH_BIND_OPTIONS_DSAKEY, - options.server_keyfile) != SSH_OK) { - errx(1, "ssh_bind_options_set(SSH_BIND_OPTIONS_DSAKEY"); - } - - session = ssh_new(); - if (!session) { - errx(1, "ssh_new"); - } - - if (ssh_bind_accept_fd(bind, session, client_conn) != SSH_OK) { - errx(1, "ssh_bind_accept: %s", ssh_get_error(bind)); - } - - if (ssh_handle_key_exchange(session) != SSH_OK) { - errx(1, "ssh_handle_key_exchange: %s", ssh_get_error(session)); - } - - printf("SUCCESS!\n"); -} - -void ssh_client() { - ssh_session session; - - session = ssh_new(); - if (!session) { - errx(1, "ssh_new"); - } - - if (ssh_options_set(session, SSH_OPTIONS_HOST, HOST) < 0) { - errx(1, "ssh_options_set(SSH_OPTIONS_HOST)"); - } - if (ssh_options_set(session, SSH_OPTIONS_PORT, &PORT) < 0) { - errx(1, "ssh_options_set(SSH_OPTIONS_PORT)"); - } - - if (ssh_connect(session) != SSH_OK) { - errx(1, "ssh_connect: %s", ssh_get_error(session)); - } -} - -int main(int argc, const char *argv[]) { - if (argc != 2) { - printf("Usage: %s \n", argv[0]); - exit(1); - } - - options.server_keyfile = argv[1]; - - pid_t pid = fork(); - if (pid < 0) { - errx(1, "fork"); - } - if (pid == 0) { - /* Allow the server to get set up */ - sleep(3); - - ssh_client(); - } else { - ssh_server(); - } - - return 0; -} diff --git a/libssh/tests/test_tunnel.c b/libssh/tests/test_tunnel.c deleted file mode 100644 index 27f667b7..00000000 --- a/libssh/tests/test_tunnel.c +++ /dev/null @@ -1,76 +0,0 @@ -/* -This file is distributed in public domain. You can do whatever you want -with its content. -*/ -#include -#include -#include -#include "tests.h" -#define ECHO_PORT 7 -void do_connect(SSH_SESSION *session){ - int error=ssh_connect(session); - if(error != SSH_OK){ - fprintf(stderr,"Error at connection :%s\n",ssh_get_error(session)); - return; - } - printf("Connected\n"); - ssh_is_server_known(session); - // we don't care what happens here - error=authenticate(session); - if(error != SSH_AUTH_SUCCESS){ - fprintf(stderr,"Error at authentication :%s\n",ssh_get_error(session)); - return; - } - printf("Authenticated\n"); - CHANNEL *channel=ssh_channel_new(session); - error=ssh_channel_open_forward(channel,"localhost",ECHO_PORT,"localhost",42); - if(error!=SSH_OK){ - fprintf(stderr,"Error when opening forward:%s\n",ssh_get_error(session)); - return; - } - printf("Forward opened\n"); - int i=0; - char string[20]; - char buffer[20]; - for(i=0;i<2000;++i){ - sprintf(string,"%d\n",i); - ssh_channel_write(channel,string,strlen(string)); - do { - error=ssh_channel_poll(channel,0); - //if(error < strlen(string)) - //usleep(10); - } while(error < strlen(string) && error >= 0); - if(error>0){ - error=ssh_channel_read_nonblocking(channel,buffer,strlen(string),0); - if(error>=0){ - if(memcmp(buffer,string,strlen(string))!=0){ - fprintf(stderr,"Problem with answer: wanted %s got %s\n",string,buffer); - } else { - printf("."); - fflush(stdout); - } - } - - } - if(error==-1){ - fprintf(stderr,"Channel reading error : %s\n",ssh_get_error(session)); - break; - } - } - printf("\nChannel test finished\n"); - ssh_channel_close(channel); - ssh_channel_free(channel); -} - -int main(int argc, char **argv){ - SSH_OPTIONS *options=set_opts(argc, argv); - SSH_SESSION *session=ssh_new(); - if(options==NULL){ - return 1; - } - ssh_set_options(session,options); - do_connect(session); - ssh_disconnect(session); - ssh_finalize(); - return 0; -} diff --git a/libssh/tests/tests.h b/libssh/tests/tests.h deleted file mode 100644 index dd001f1f..00000000 --- a/libssh/tests/tests.h +++ /dev/null @@ -1,8 +0,0 @@ -/* -This file is distributed in public domain. You can do whatever you want -with its content. -*/ -#include -int authenticate (SSH_SESSION *session); -SSH_OPTIONS *set_opts(int argc, char **argv); - diff --git a/libssh/tests/torture.c b/libssh/tests/torture.c deleted file mode 100644 index 0b8eb3da..00000000 --- a/libssh/tests/torture.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * torture.c - torture library for testing libssh - * - * This file is part of the SSH Library - * - * Copyright (c) 2008-2009 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#ifndef _WIN32 -# include -# include -#endif - -#ifdef HAVE_UNISTD_H -#include -#endif - -#include "torture.h" - -#define TORTURE_TESTKEY_PASSWORD "libssh-rocks" - -static const char torture_rsa_testkey[] = - "-----BEGIN RSA PRIVATE KEY-----\n" - "MIIEowIBAAKCAQEArAOREUWlBXJAKZ5hABYyxnRayDZP1bJeLbPVK+npxemrhHyZ\n" - "gjdbY3ADot+JRyWjvll2w2GI+3blt0j+x/ZWwjMKu/QYcycYp5HL01goxOxuusZb\n" - "i+KiHRGB6z0EMdXM7U82U7lA/j//HyZppyDjUDniWabXQJge8ksGXGTiFeAJ/687\n" - "uV+JJcjGPxAGFQxzyjitf/FrL9S0WGKZbyqeGDzyeBZ1NLIuaiOORyLGSW4duHLD\n" - "N78EmsJnwqg2gJQmRSaD4BNZMjtbfiFcSL9Uw4XQFTsWugUDEY1AU4c5g11nhzHz\n" - "Bi9qMOt5DzrZQpD4j0gA2LOHpHhoOdg1ZuHrGQIDAQABAoIBAFJTaqy/jllq8vZ4\n" - "TKiD900wBvrns5HtSlHJTe80hqQoT+Sa1cWSxPR0eekL32Hjy9igbMzZ83uWzh7I\n" - "mtgNODy9vRdznfgO8CfTCaBfAzQsjFpr8QikMT6EUI/LpiRL1UaGsNOlSEvnSS0Z\n" - "b1uDzAdrjL+nsEHEDJud+K9jwSkCRifVMy7fLfaum+YKpdeEz7K2Mgm5pJ/Vg+9s\n" - "vI2V1q7HAOI4eUVTgJNHXy5ediRJlajQHf/lNUzHKqn7iH+JRl01gt62X8roG62b\n" - "TbFylbheqMm9awuSF2ucOcx+guuwhkPir8BEMb08j3hiK+TfwPdY0F6QH4OhiKK7\n" - "MTqTVgECgYEA0vmmu5GOBtwRmq6gVNCHhdLDQWaxAZqQRmRbzxVhFpbv0GjbQEF7\n" - "tttq3fjDrzDf6CE9RtZWw2BUSXVq+IXB/bXb1kgWU2xWywm+OFDk9OXQs8ui+MY7\n" - "FiP3yuq3YJob2g5CCsVQWl2CHvWGmTLhE1ODll39t7Y1uwdcDobJN+ECgYEA0LlR\n" - "hfMjydWmwqooU9TDjXNBmwufyYlNFTH351amYgFUDpNf35SMCP4hDosUw/zCTDpc\n" - "+1w04BJJfkH1SNvXSOilpdaYRTYuryDvGmWC66K2KX1nLErhlhs17CwzV997nYgD\n" - "H3OOU4HfqIKmdGbjvWlkmY+mLHyG10bbpOTbujkCgYAc68xHejSWDCT9p2KjPdLW\n" - "LYZGuOUa6y1L+QX85Vlh118Ymsczj8Z90qZbt3Zb1b9b+vKDe255agMj7syzNOLa\n" - "/MseHNOyq+9Z9gP1hGFekQKDIy88GzCOYG/fiT2KKJYY1kuHXnUdbiQgSlghODBS\n" - "jehD/K6DOJ80/FVKSH/dAQKBgQDJ+apTzpZhJ2f5k6L2jDq3VEK2ACedZEm9Kt9T\n" - "c1wKFnL6r83kkuB3i0L9ycRMavixvwBfFDjuY4POs5Dh8ip/mPFCa0hqISZHvbzi\n" - "dDyePJO9zmXaTJPDJ42kfpkofVAnfohXFQEy+cguTk848J+MmMIKfyE0h0QMabr9\n" - "86BUsQKBgEVgoi4RXwmtGovtMew01ORPV9MOX3v+VnsCgD4/56URKOAngiS70xEP\n" - "ONwNbTCWuuv43HGzJoVFiAMGnQP1BAJ7gkHkjSegOGKkiw12EPUWhFcMg+GkgPhc\n" - "pOqNt/VMBPjJ/ysHJqmLfQK9A35JV6Cmdphe+OIl28bcKhAOz8Dw\n" - "-----END RSA PRIVATE KEY-----\n"; - -static const char torture_rsa_testkey_pub[] = - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsA5ERRaUFckApnmEAFjLGdFrIN" - "k/Vsl4ts9Ur6enF6auEfJmCN1tjcAOi34lHJaO+WXbDYYj7duW3SP7H9lbCMwq79B" - "hzJxinkcvTWCjE7G66xluL4qIdEYHrPQQx1cztTzZTuUD+P/8fJmmnIONQOeJZptd" - "AmB7ySwZcZOIV4An/rzu5X4klyMY/EAYVDHPKOK1/8Wsv1LRYYplvKp4YPPJ4FnU0" - "si5qI45HIsZJbh24csM3vwSawmfCqDaAlCZFJoPgE1kyO1t+IVxIv1TDhdAVOxa6B" - "QMRjUBThzmDXWeHMfMGL2ow63kPOtlCkPiPSADYs4ekeGg52DVm4esZ " - "aris@aris-air\n"; - -static const char torture_dsa_testkey[] = - "-----BEGIN DSA PRIVATE KEY-----\n" - "MIIBuwIBAAKBgQCUyvVPEkn3UnZDjzCzSzSHpTltzr0Ec+1mz/JACjHMBJ9C/W/P\n" - "wvH3yjkfoFhhREvoY7IPnwAu5bcxw8TkISq7YROQ409PqwwPvy0N3GUp/+kKS268\n" - "BIJ+VKN513XRf7eL1e4aHUJ+al9x1JxTmc6T0GBq1lyu+CTUUyh25aNDFwIVAK84\n" - "j20GmU+zewjQwsIXuVb6C/PHAoGAXhuIVsJxUQJ5nWQRLf7o3XEGQ+EcVmHOzMB1\n" - "xCsHjYnpEhhco+r/HDZSD31kzDeAZUycz31WqGL8yXr+OZRLqEsGC7dwEAzPiXDu\n" - "l0zHcl0yiKPrRrLgNJHeKcT6JflBngK7jQRIVUg3F3104fbVa2rwaniLl4GSBZPX\n" - "MpUdng8CgYB4roDQBfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9RbUIVS7\n" - "n2mw3iqZG0xnG3iv1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM7b2u\n" - "ADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIgIVAI1Hd8/i\n" - "Pzsg7bTzoNvjQL+Noyiy\n" - "-----END DSA PRIVATE KEY-----\n"; - -static const char torture_dsa_testkey_pub[] = - "ssh-dss AAAAB3NzaC1kc3MAAACBAJTK9U8SSfdSdkOPMLNLNIelOW3OvQRz7WbP8k" - "AKMcwEn0L9b8/C8ffKOR+gWGFES+hjsg+fAC7ltzHDxOQhKrthE5DjT0+rDA+/LQ3c" - "ZSn/6QpLbrwEgn5Uo3nXddF/t4vV7hodQn5qX3HUnFOZzpPQYGrWXK74JNRTKHblo0" - "MXAAAAFQCvOI9tBplPs3sI0MLCF7lW+gvzxwAAAIBeG4hWwnFRAnmdZBEt/ujdcQZD" - "4RxWYc7MwHXEKweNiekSGFyj6v8cNlIPfWTMN4BlTJzPfVaoYvzJev45lEuoSwYLt3" - "AQDM+JcO6XTMdyXTKIo+tGsuA0kd4pxPol+UGeAruNBEhVSDcXfXTh9tVravBqeIuX" - "gZIFk9cylR2eDwAAAIB4roDQBfgf8AoSAJAb7y8OVvxt5cT7iqaRMQX2XgtW09Nu9R" - "bUIVS7n2mw3iqZG0xnG3iv1oL9gwNXMLlf+gLmsqU3788jaEZ9IhZ8VdgHAoHm6UWM" - "7b2uADmhirI6dRZUVO+/iMGUvDxa66OI4hDV055pbwQhtxupUatThyDzIg== " - "aris@aris-air\n"; - -static const char torture_rsa_testkey_pp[] = - "-----BEGIN RSA PRIVATE KEY-----\n" - "Proc-Type: 4,ENCRYPTED\n" - "DEK-Info: AES-128-CBC,5375534F40903DD66B3851A0DA03F6FA\n" - "\n" - "m5YYTNOMd1xCKfifwCX4R1iLJoAc4cn1aFiL7f2kBbfE2jF1LTQBJV1h1CqYZfAB\n" - "WtM/7FkQPnKXqsMndP+v+1Xc+PYigE3AezJj/0g7xn/zIBwGjkLAp435AdL5i6Fg\n" - "OhOL8LyolRrcGn17jE4S4iGbzw8PVyfzNzdj0Emwql5F6M7pgLbInRNKM/TF4z2h\n" - "b6Pi9Bw43dwaJ7wiiy/vo/v4MyXsJBoeKbc4VCmxiYFvAYCvVFlDkyIw/QnR3MKQ\n" - "g/Zsk7Pw3aOioxk6LJpZ5x0tO23nXDG1aOZHWykI0BpJV+LIpD2oSYOHJyVO83XT\n" - "RQUMSTXc2K2+ejs0XQoLt/GxDDHe+8W8fWQK3C7Lyvl9oKjmb5sTWi3mdSv0C+zR\n" - "n5KSVbUKNXrjix7qPKkv5rWqb84CKVnCMb7tWaPLR19nQqKVYBIs6v0OTTvS6Le7\n" - "lz4lxBkcUy6vi0tWH9MvLuT+ugdHLJZ4UXBthCgV58pM1o+L+WMIl+SZXckiCAO3\n" - "7ercA57695IA6iHskmr3eazJsYFEVFdR/cm+IDy2FPkKmJMjXeIWuh3yASBk7LBR\n" - "EQq3CC7AioO+Vj8m/fEIiNZJSQ6p0NmgnPoO3rTYT/IobmE99/Ht6oNLmFX4Pr7e\n" - "F4CGWKzwxWpCnw2vVolCFByASmZycbJvrIonZBKY1toU28lRm4tCM6eCNISVLMeE\n" - "VtQ+1PH9/2KZspZl+SX/kjV3egggy0TFKRU8EcYPJFC3Vpy+shEai35KBVo44Z18\n" - "apza7exm3igNEqOqe07hLs3Bjhvk1oS+WhMbAG9ARTOKuyBOJh/ZV9tFMNZ6v+q5\n" - "TofgNcIhNYNascymU1io18xTW9c3RRcmRKqIWnj4EH8o7Aojv/l+zvdV7/GVlR4W\n" - "pR9cuJEiyiEjS46axoc6dSOtdnvag+BpFQb+lGY97F9nNGyBdtLD5ASVh5OVG4fu\n" - "Pf0O7Bdj1kIuBhV8axE/slf6UHANiodeqkR9B24+0Cy+miPiHazzUkbdSJ4r03g5\n" - "J1Y5S8qbl9++sqhQMLMUkeK4pDWh1aocA9bDA2RcBNuXGiZeRFUiqxcBS+iO418n\n" - "DFyWz4UfI/m1IRSjoo/PEpgu5GmosUzs3Dl4nAcf/REBEX6M/kKKxHTLjE8DxDsz\n" - "fn/vfsXV3s0tbN7YyJdP8aU+ApZntw1OF2TS2qS8CPWHTcCGGTab5WEGC3xFXKp0\n" - "uyonCxV7vNLOiIiHdQX+1bLu7ps7GBH92xGkPg7FrNNcMc07soP7jjjB578n9Gpl\n" - "cIDBdgovTRFHiWu3yRspVt0zPfMJB/hqn+IAp98wfvjl8OZM1ZZkejnwXnQil5ZU\n" - "wjEBEtx+nX56vdxipzKoHh5yDXmPbNajBYkg3rXJrLFh3Tsf0CzHcLdHNz/qJ9LO\n" - "wH16grjR1Q0CzCW3FAv0Q0euqkXac+TfuIg3HiTPrBPnJQW1uivrx1F5tpO/uboG\n" - "h28LwqJLYh+1T0V//uiy3SMATpYKvzg2byGct9VUib8QVop8LvVF/n42RaxtTCfw\n" - "JSvUyxoaZUjQkT7iF94HsF+FVVJdI55UjgnMiZ0d5vKffWyTHYcYHkFYaSloAMWN\n" - "-----END RSA PRIVATE KEY-----\n"; - -static const char torture_dsa_testkey_pp[] = - "-----BEGIN DSA PRIVATE KEY-----\n" - "Proc-Type: 4,ENCRYPTED\n" - "DEK-Info: AES-128-CBC,266023B64B1B814BCD0D0E477257F06D\n" - "\n" - "QJQErZrvYsfeMNMnU+6yVHH5Zze/zUFdPip7Bon4T1wCGlVasn4x/GQcMm1+mgmb\n" - "PCK/qJ5qw9nCepLYJq2xh8gohbwF/XKxeaNGcRA2+ancTooDUjeRTlk1WRtS1+bq\n" - "LBkwhxLXW26lIuQUHzfi93rRqQI2LC4McngY7L7WVJer7sH7hk5//4Gf6zHtPEl+\n" - "Tr2ub1zNrVbh6e1Bitw7DaGZNX6XEWpyTTsAd42sQWh6o23MC6GyfS1YFsPGHzGe\n" - "WYQbWn2AZ1mK32z2mLZfVg41qu9RKG20iCyaczZ2YmuYyOkoLHijOAHC8vZbHwYC\n" - "+lN9Yc8/BoMuMMwDTMDaJD0TsBX02hi9YI7Gu88PMCJO+SRe5400MonUMXTwCa91\n" - "Tt3RhYpBzx2XGOq5199+oLdTJAaXHJcuB6viKNdSLBuhx6RAEJXZnVexchaHs4Q6\n" - "HweIv6Et8MjVoqwkaQDmcIGA73qZ0lbUJFZAu2YDJ6TpHc1lHZes763HoMYfuvkX\n" - "HTSuHZ7edjoWqwnl/vkc3+nG//IEj8LqAacx0i4krDcQpGuQ6BnPfwPFco2NQQpw\n" - "wHBOL6HrOnD+gGs6DUFwzA==\n" - "-----END DSA PRIVATE KEY-----\n"; - -static const char torture_ecdsa256_testkey[] = - "-----BEGIN EC PRIVATE KEY-----\n" - "MHcCAQEEIBCDeeYYAtX3EnsP0ratwVpNTaA/4K1N6VvHMiUZlVdhoAoGCCqGSM49\n" - "AwEHoUQDQgAEx+9ud88Q5GWtLd+yMtYaapC85g+2ZLp7VtFHA0EbNHqBUQxoh+Ik\n" - "89Mlr7AUxcFPd+kCo+NE6yq/mNQcL7E6iQ==\n" - "-----END EC PRIVATE KEY-----\n"; - -static const char torture_ecdsa256_testkey_pub[] = - "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNT" - "YAAABBBMfvbnfPEORlrS3fsjLWGmqQvOYPtmS6e1bRRwNBGzR6gVEMaIfiJPPTJa+w" - "FMXBT3fpAqPjROsqv5jUHC+xOok= aris@kalix86\n"; - -static const char torture_ecdsa384_testkey[] = - "-----BEGIN EC PRIVATE KEY-----\n" - "MIGkAgEBBDBY8jEa5DtRy4AVeTWhPJ/TK257behiC3uafEi6YA2oHORibqX55EDN\n" - "wz29MT40mQSgBwYFK4EEACKhZANiAARXc4BN6BrVo1QMi3+i/B85Lu7SMuzBi+1P\n" - "bJti8xz+Szgq64gaBGOK9o+WOdLAd/w7p7DJLdztJ0bYoyT4V3B3ZqR9RyGq6mYC\n" - "jkXlc5YbYHjueBbp0oeNXqsXHNAWQZo=\n" - "-----END EC PRIVATE KEY-----\n"; - -static const char torture_ecdsa384_testkey_pub[] = - "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzOD" - "QAAABhBFdzgE3oGtWjVAyLf6L8Hzku7tIy7MGL7U9sm2LzHP5LOCrriBoEY4r2j5Y5" - "0sB3/DunsMkt3O0nRtijJPhXcHdmpH1HIarqZgKOReVzlhtgeO54FunSh41eqxcc0B" - "ZBmg== aris@kalix86"; - -static const char torture_ecdsa521_testkey[] = - "-----BEGIN EC PRIVATE KEY-----\n" - "MIHbAgEBBEG83nSJ2SLoiBvEku1JteQKWx/Xt6THksgC7rrIaTUmNzk+60f0sCCm\n" - "Gll0dgrZLmeIw+TtnG1E20VZflCKq+IdkaAHBgUrgQQAI6GBiQOBhgAEAc6D728d\n" - "baQkHnSPtztaRwJw63CBl15cykB4SXXuwWdNOtPzBijUULMTTvBXbra8gL4ATd9d\n" - "Qnuwn8KQUh2T/z+BARjWPKhcHcGx57XpXCEkawzMYaHUUnRdeFEmNRsbXypsf0mJ\n" - "KATU3h8gzTMkbrx8DJTFHEIjXBShs44HsSYVl3Xy\n" - "-----END EC PRIVATE KEY-----\n"; - -static const char torture_ecdsa521_testkey_pub[] = - "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1Mj" - "EAAACFBAHOg+9vHW2kJB50j7c7WkcCcOtwgZdeXMpAeEl17sFnTTrT8wYo1FCzE07w" - "V262vIC+AE3fXUJ7sJ/CkFIdk/8/gQEY1jyoXB3Bsee16VwhJGsMzGGh1FJ0XXhRJj" - "UbG18qbH9JiSgE1N4fIM0zJG68fAyUxRxCI1wUobOOB7EmFZd18g== aris@kalix86"; - -static int verbosity = 0; - -#ifndef _WIN32 -static int _torture_auth_kbdint(ssh_session session, - const char *password) { - const char *prompt; - char echo; - int err; - - if (session == NULL || password == NULL) { - return SSH_AUTH_ERROR; - } - - err = ssh_userauth_kbdint(session, NULL, NULL); - if (err == SSH_AUTH_ERROR) { - return err; - } - - if (ssh_userauth_kbdint_getnprompts(session) != 1) { - return SSH_AUTH_ERROR; - } - - prompt = ssh_userauth_kbdint_getprompt(session, 0, &echo); - if (prompt == NULL) { - return SSH_AUTH_ERROR; - } - - if (ssh_userauth_kbdint_setanswer(session, 0, password) < 0) { - return SSH_AUTH_ERROR; - } - err = ssh_userauth_kbdint(session, NULL, NULL); - if (err == SSH_AUTH_INFO) { - if (ssh_userauth_kbdint_getnprompts(session) != 0) { - return SSH_AUTH_ERROR; - } - err = ssh_userauth_kbdint(session, NULL, NULL); - } - - return err; -} - -int torture_rmdirs(const char *path) { - DIR *d; - struct dirent *dp; - struct stat sb; - char *fname; - - if ((d = opendir(path)) != NULL) { - while(stat(path, &sb) == 0) { - /* if we can remove the directory we're done */ - if (rmdir(path) == 0) { - break; - } - switch (errno) { - case ENOTEMPTY: - case EEXIST: - case EBADF: - break; /* continue */ - default: - closedir(d); - return 0; - } - - while ((dp = readdir(d)) != NULL) { - size_t len; - /* skip '.' and '..' */ - if (dp->d_name[0] == '.' && - (dp->d_name[1] == '\0' || - (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) { - continue; - } - - len = strlen(path) + strlen(dp->d_name) + 2; - fname = malloc(len); - if (fname == NULL) { - closedir(d); - return -1; - } - snprintf(fname, len, "%s/%s", path, dp->d_name); - - /* stat the file */ - if (lstat(fname, &sb) != -1) { - if (S_ISDIR(sb.st_mode) && !S_ISLNK(sb.st_mode)) { - if (rmdir(fname) < 0) { /* can't be deleted */ - if (errno == EACCES) { - closedir(d); - SAFE_FREE(fname); - return -1; - } - torture_rmdirs(fname); - } - } else { - unlink(fname); - } - } /* lstat */ - SAFE_FREE(fname); - } /* readdir */ - - rewinddir(d); - } - } else { - return -1; - } - - closedir(d); - return 0; -} - -int torture_isdir(const char *path) { - struct stat sb; - - if (lstat (path, &sb) == 0 && S_ISDIR(sb.st_mode)) { - return 1; - } - - return 0; -} - -ssh_session torture_ssh_session(const char *host, - const char *user, - const char *password) { - ssh_session session; - int method; - int rc; - - if (host == NULL) { - return NULL; - } - - session = ssh_new(); - if (session == NULL) { - return NULL; - } - - if (ssh_options_set(session, SSH_OPTIONS_HOST, host) < 0) { - goto failed; - } - - if (user != NULL) { - if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) { - goto failed; - } - } - - if (ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity) < 0) { - goto failed; - } - - if (ssh_connect(session)) { - goto failed; - } - - /* We are in testing mode, so consinder the hostkey as verified ;) */ - - /* This request should return a SSH_REQUEST_DENIED error */ - rc = ssh_userauth_none(session, NULL); - if (rc == SSH_ERROR) { - goto failed; - } - method = ssh_userauth_list(session, NULL); - if (method == 0) { - goto failed; - } - - if (password != NULL) { - if (method & SSH_AUTH_METHOD_INTERACTIVE) { - rc = _torture_auth_kbdint(session, password); - } else if (method & SSH_AUTH_METHOD_PASSWORD) { - rc = ssh_userauth_password(session, NULL, password); - } - } else { - rc = ssh_userauth_publickey_auto(session, NULL, NULL); - if (rc == SSH_AUTH_ERROR) { - goto failed; - } - } - if (rc != SSH_AUTH_SUCCESS) { - goto failed; - } - - return session; -failed: - if (ssh_is_connected(session)) { - ssh_disconnect(session); - } - ssh_free(session); - - return NULL; -} - -#ifdef WITH_SFTP - -struct torture_sftp *torture_sftp_session(ssh_session session) { - struct torture_sftp *t; - char template[] = "/tmp/ssh_torture_XXXXXX"; - char *p; - int rc; - - if (session == NULL) { - return NULL; - } - - t = malloc(sizeof(struct torture_sftp)); - if (t == NULL) { - return NULL; - } - - t->ssh = session; - t->sftp = sftp_new(session); - if (t->sftp == NULL) { - goto failed; - } - - rc = sftp_init(t->sftp); - if (rc < 0) { - goto failed; - } - - p = mkdtemp(template); - if (p == NULL) { - goto failed; - } - /* useful if TESTUSER is not the local user */ - chmod(template,0777); - t->testdir = strdup(p); - if (t->testdir == NULL) { - goto failed; - } - - return t; -failed: - if (t->sftp != NULL) { - sftp_free(t->sftp); - } - ssh_disconnect(t->ssh); - ssh_free(t->ssh); - free(t); - - return NULL; -} - -void torture_sftp_close(struct torture_sftp *t) { - if (t == NULL) { - return; - } - - if (t->sftp != NULL) { - sftp_free(t->sftp); - } - - if (t->ssh != NULL) { - if (ssh_is_connected(t->ssh)) { - ssh_disconnect(t->ssh); - } - ssh_free(t->ssh); - } - - free(t->testdir); - free(t); -} -#endif /* WITH_SFTP */ - -#endif /* _WIN32 */ - -void torture_write_file(const char *filename, const char *data){ - int fd; - int rc; - - assert_non_null(filename); - assert_true(filename[0] != '\0'); - assert_non_null(data); - - fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0755); - assert_true(fd >= 0); - - rc = write(fd, data, strlen(data)); - assert_int_equal(rc, strlen(data)); - - close(fd); -} - -static const char *torture_get_testkey_internal(enum ssh_keytypes_e type, - int bits, - int with_passphrase, - int pubkey) -{ - switch (type) { - case SSH_KEYTYPE_DSS: - if (pubkey) { - return torture_dsa_testkey_pub; - } else if (with_passphrase) { - return torture_dsa_testkey_pp; - } - return torture_dsa_testkey; - case SSH_KEYTYPE_RSA: - if (pubkey) { - return torture_rsa_testkey_pub; - } else if (with_passphrase) { - return torture_rsa_testkey_pp; - } - return torture_rsa_testkey; - case SSH_KEYTYPE_ECDSA: - if (bits == 521) { - if (pubkey) { - return torture_ecdsa521_testkey_pub; - } - return torture_ecdsa521_testkey; - } else if (bits == 384) { - if (pubkey) { - return torture_ecdsa384_testkey_pub; - } - return torture_ecdsa384_testkey; - } - - if (pubkey) { - return torture_ecdsa256_testkey_pub; - } - return torture_ecdsa256_testkey; - case SSH_KEYTYPE_RSA1: - case SSH_KEYTYPE_UNKNOWN: - return NULL; - } - - return NULL; -} - -const char *torture_get_testkey(enum ssh_keytypes_e type, - int ecda_bits, - int with_passphrase) -{ - return torture_get_testkey_internal(type, ecda_bits, with_passphrase, 0); -} - -const char *torture_get_testkey_pub(enum ssh_keytypes_e type, int ecda_bits) -{ - return torture_get_testkey_internal(type, ecda_bits, 0, 1); -} - -const char *torture_get_testkey_passphrase(void) -{ - return TORTURE_TESTKEY_PASSWORD; -} - -int torture_libssh_verbosity(void){ - return verbosity; -} - -int main(int argc, char **argv) { - struct argument_s arguments; - - arguments.verbose=0; - torture_cmdline_parse(argc, argv, &arguments); - verbosity=arguments.verbose; - - return torture_run_tests(); -} - -/* vim: set ts=4 sw=4 et cindent syntax=c.doxygen: */ diff --git a/libssh/tests/torture.h b/libssh/tests/torture.h deleted file mode 100644 index ffcea8bb..00000000 --- a/libssh/tests/torture.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * torture.c - torture library for testing libssh - * - * This file is part of the SSH Library - * - * Copyright (c) 2008-2009 by Andreas Schneider - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -#ifndef _TORTURE_H -#define _TORTURE_H - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include - -#include "libssh/priv.h" -#include "libssh/sftp.h" - -#include - -/* Used by main to communicate with parse_opt. */ -struct argument_s { - char *args[2]; - int verbose; -}; - -struct torture_sftp { - ssh_session ssh; - sftp_session sftp; - char *testdir; -}; - -void torture_cmdline_parse(int argc, char **argv, struct argument_s *arguments); - -int torture_rmdirs(const char *path); -int torture_isdir(const char *path); - -/* - * Returns the verbosity level asked by user - */ -int torture_libssh_verbosity(void); - -ssh_session torture_ssh_session(const char *host, - const char *user, - const char *password); - -struct torture_sftp *torture_sftp_session(ssh_session session); -void torture_sftp_close(struct torture_sftp *t); - -const char *torture_get_testkey(enum ssh_keytypes_e type, - int ecdsa_bits, - int with_passphrase); -const char *torture_get_testkey_pub(enum ssh_keytypes_e type, int ecdsa_bits); -const char *torture_get_testkey_passphrase(void); - -void torture_write_file(const char *filename, const char *data); - -/* - * This function must be defined in every unit test file. - */ -int torture_run_tests(void); - -#endif /* _TORTURE_H */ diff --git a/libssh/tests/unittests/CMakeLists.txt b/libssh/tests/unittests/CMakeLists.txt deleted file mode 100644 index 38203991..00000000 --- a/libssh/tests/unittests/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -project(unittests C) - -add_cmocka_test(torture_buffer torture_buffer.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_callbacks torture_callbacks.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_init torture_init.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_list torture_list.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_misc torture_misc.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_options torture_options.c ${TORTURE_LIBRARY}) -add_cmocka_test(torture_isipaddr torture_isipaddr.c ${TORTURE_LIBRARY}) -if (UNIX AND NOT WIN32) - # requires ssh-keygen - add_cmocka_test(torture_keyfiles torture_keyfiles.c ${TORTURE_LIBRARY}) - add_cmocka_test(torture_pki torture_pki.c ${TORTURE_LIBRARY}) - # requires pthread - add_cmocka_test(torture_rand torture_rand.c ${TORTURE_LIBRARY}) - # requires /dev/null - add_cmocka_test(torture_channel torture_channel.c ${TORTURE_LIBRARY}) -endif (UNIX AND NOT WIN32) diff --git a/libssh/tests/unittests/torture_buffer.c b/libssh/tests/unittests/torture_buffer.c deleted file mode 100644 index ec87259d..00000000 --- a/libssh/tests/unittests/torture_buffer.c +++ /dev/null @@ -1,268 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#define DEBUG_BUFFER -#include "buffer.c" - -#define LIMIT (8*1024*1024) - -static void setup(void **state) { - ssh_buffer buffer; - buffer = ssh_buffer_new(); - ssh_buffer_set_secure(buffer); - *state = (void *) buffer; -} - -static void teardown(void **state) { - ssh_buffer_free(*state); -} - -/* - * Test if the continuously growing buffer size never exceeds 2 time its - * real capacity - */ -static void torture_growing_buffer(void **state) { - ssh_buffer buffer = *state; - int i; - - for(i=0;iused >= 128){ - if(buffer_get_rest_len(buffer) * 2 < buffer->allocated){ - assert_true(buffer_get_rest_len(buffer) * 2 >= buffer->allocated); - } - } - } -} - -/* - * Test if the continuously growing buffer size never exceeds 2 time its - * real capacity, when we remove 1 byte after each call (sliding window) - */ -static void torture_growing_buffer_shifting(void **state) { - ssh_buffer buffer = *state; - int i; - unsigned char c; - for(i=0; i<1024;++i){ - ssh_buffer_add_data(buffer,"S",1); - } - for(i=0;iused >= 128){ - if(buffer_get_rest_len(buffer) * 4 < buffer->allocated){ - assert_true(buffer_get_rest_len(buffer) * 4 >= buffer->allocated); - return; - } - } - } -} - -/* - * Test the behavior of buffer_prepend_data - */ -static void torture_buffer_prepend(void **state) { - ssh_buffer buffer = *state; - uint32_t v; - ssh_buffer_add_data(buffer,"abcdef",6); - buffer_prepend_data(buffer,"xyz",3); - assert_int_equal(buffer_get_rest_len(buffer),9); - assert_memory_equal(buffer_get_rest(buffer), "xyzabcdef", 9); - - /* Now remove 4 bytes and see if we can replace them */ - buffer_get_u32(buffer,&v); - assert_int_equal(buffer_get_rest_len(buffer),5); - assert_memory_equal(buffer_get_rest(buffer), "bcdef", 5); - - buffer_prepend_data(buffer,"aris",4); - assert_int_equal(buffer_get_rest_len(buffer),9); - assert_memory_equal(buffer_get_rest(buffer), "arisbcdef", 9); - - /* same thing but we add 5 bytes now */ - buffer_get_u32(buffer,&v); - assert_int_equal(buffer_get_rest_len(buffer),5); - assert_memory_equal(buffer_get_rest(buffer), "bcdef", 5); - - buffer_prepend_data(buffer,"12345",5); - assert_int_equal(buffer_get_rest_len(buffer),10); - assert_memory_equal(buffer_get_rest(buffer), "12345bcdef", 10); -} - -/* - * Test the behavior of buffer_get_ssh_string with invalid data - */ -static void torture_buffer_get_ssh_string(void **state) { - ssh_buffer buffer; - int i,j,k,l, rc; - /* some values that can go wrong */ - uint32_t values[] = {0xffffffff, 0xfffffffe, 0xfffffffc, 0xffffff00, - 0x80000000, 0x80000004, 0x7fffffff}; - char data[128]; - (void)state; - memset(data,'X',sizeof(data)); - for(i=0; i < (int)(sizeof(values)/sizeof(values[0]));++i){ - for(j=0; j< (int)sizeof(data);++j){ - for(k=1;k<5;++k){ - buffer = ssh_buffer_new(); - assert_non_null(buffer); - - for(l=0;l -#include - -static int myauthcallback (const char *prompt, char *buf, size_t len, - int echo, int verify, void *userdata) { - (void) prompt; - (void) buf; - (void) len; - (void) echo; - (void) verify; - (void) userdata; - return 0; -} - -static void setup(void **state) { - struct ssh_callbacks_struct *cb; - - cb = malloc(sizeof(struct ssh_callbacks_struct)); - assert_false(cb == NULL); - ZERO_STRUCTP(cb); - - cb->userdata = (void *) 0x0badc0de; - cb->auth_function = myauthcallback; - - ssh_callbacks_init(cb); - *state = cb; -} - -static void teardown(void **state) { - free(*state); -} - -static void torture_callbacks_size(void **state) { - struct ssh_callbacks_struct *cb = *state;; - - assert_int_not_equal(cb->size, 0); -} - -static void torture_callbacks_exists(void **state) { - struct ssh_callbacks_struct *cb = *state; - - assert_int_not_equal(ssh_callbacks_exists(cb, auth_function), 0); - assert_int_equal(ssh_callbacks_exists(cb, log_function), 0); - - /* - * We redefine size so auth_function is outside the range of - * callbacks->size. - */ - cb->size = (unsigned char *) &cb->auth_function - (unsigned char *) cb; - assert_int_equal(ssh_callbacks_exists(cb, auth_function), 0); - - /* Now make it one pointer bigger so we spill over the auth_function slot */ - cb->size += sizeof(void *); - assert_int_not_equal(ssh_callbacks_exists(cb, auth_function), 0); -} - -struct test_mock_state { - int executed; -}; - -static void test_mock_ssh_logging_callback(int priority, - const char *function, - const char *buffer, - void *userdata) -{ - struct test_mock_state *t = (struct test_mock_state *)userdata; - - check_expected(priority); - check_expected(function); - check_expected(buffer); - - t->executed++; -} - -static void torture_log_callback(void **state) -{ - struct test_mock_state t = { - .executed = 0, - }; - - (void)state; /* unused */ - - ssh_set_log_callback(test_mock_ssh_logging_callback); - ssh_set_log_userdata(&t); - ssh_set_log_level(1); - - expect_value(test_mock_ssh_logging_callback, priority, 1); - expect_string(test_mock_ssh_logging_callback, function, "torture_log_callback"); - expect_string(test_mock_ssh_logging_callback, buffer, "torture_log_callback: test"); - - SSH_LOG(SSH_LOG_WARN, "test"); - - assert_int_equal(t.executed, 1); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_callbacks_size, setup, teardown), - unit_test_setup_teardown(torture_callbacks_exists, setup, teardown), - unit_test(torture_log_callback), - }; - - ssh_init(); - rc=run_tests(tests); - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/unittests/torture_channel.c b/libssh/tests/unittests/torture_channel.c deleted file mode 100644 index 1d928b84..00000000 --- a/libssh/tests/unittests/torture_channel.c +++ /dev/null @@ -1,49 +0,0 @@ -#define LIBSSH_STATIC -#include - -#include -#include -#include - -#include "torture.h" -#include "channels.c" - -static void torture_channel_select(void **state) -{ - fd_set readfds; - int fd; - int rc; - int i; - - (void)state; /* unused */ - - fd = open("/dev/null", 0); - assert_true(fd > 2); - - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - - for (i = 0; i < 10; i++) { - ssh_channel cin[1] = { NULL, }; - ssh_channel cout[1] = { NULL, }; - struct timeval tv = { .tv_sec = 0, .tv_usec = 1000 }; - - rc = ssh_select(cin, cout, fd + 1, &readfds, &tv); - assert_int_equal(rc, SSH_OK); - } - - close(fd); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test(torture_channel_select), - }; - - ssh_init(); - rc = run_tests(tests); - ssh_finalize(); - - return rc; -} diff --git a/libssh/tests/unittests/torture_init.c b/libssh/tests/unittests/torture_init.c deleted file mode 100644 index 85bd95e1..00000000 --- a/libssh/tests/unittests/torture_init.c +++ /dev/null @@ -1,23 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#include "libssh/libssh.h" - -static void torture_ssh_init(void **state) { - int rc; - - (void) state; - - rc = ssh_init(); - assert_int_equal(rc, SSH_OK); - rc = ssh_finalize(); - assert_int_equal(rc, SSH_OK); -} - -int torture_run_tests(void) { - const UnitTest tests[] = { - unit_test(torture_ssh_init), - }; - - return run_tests(tests); -} diff --git a/libssh/tests/unittests/torture_isipaddr.c b/libssh/tests/unittests/torture_isipaddr.c deleted file mode 100644 index c2a1e079..00000000 --- a/libssh/tests/unittests/torture_isipaddr.c +++ /dev/null @@ -1,56 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" - -#include "misc.c" -#include "error.c" - -/* - * Test the behavior of ssh_is_ipaddr() - */ -static void torture_ssh_is_ipaddr(void **state) { - (void)state; - - assert_int_equal(ssh_is_ipaddr("127.0.0.1"),1); - assert_int_equal(ssh_is_ipaddr("0.0.0.0"),1); - assert_int_equal(ssh_is_ipaddr("1.1.1.1"),1); - assert_int_equal(ssh_is_ipaddr("255.255.255.255"),1); - assert_int_equal(ssh_is_ipaddr("128.128.128.128"),1); - assert_int_equal(ssh_is_ipaddr("1.10.100.1"),1); - assert_int_equal(ssh_is_ipaddr("0.1.10.100"),1); - - assert_int_equal(ssh_is_ipaddr("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),1); - assert_int_equal(ssh_is_ipaddr("fe80:0000:0000:0000:0202:b3ff:fe1e:8329"),1); - assert_int_equal(ssh_is_ipaddr("fe80:0:0:0:202:b3ff:fe1e:8329"),1); - assert_int_equal(ssh_is_ipaddr("fe80::202:b3ff:fe1e:8329"),1); - assert_int_equal(ssh_is_ipaddr("::1"),1); - - assert_int_equal(ssh_is_ipaddr("::ffff:192.0.2.128"),1); - - assert_int_equal(ssh_is_ipaddr("0.0.0.0.0"),0); - assert_int_equal(ssh_is_ipaddr("0.0.0.0.a"),0); - assert_int_equal(ssh_is_ipaddr("a.0.0.0"),0); - assert_int_equal(ssh_is_ipaddr("0a.0.0.0.0"),0); - assert_int_equal(ssh_is_ipaddr(""),0); - assert_int_equal(ssh_is_ipaddr("0.0.0."),0); - assert_int_equal(ssh_is_ipaddr("0.0"),0); - assert_int_equal(ssh_is_ipaddr("0"),0); - assert_int_equal(ssh_is_ipaddr("255.255.255"),0); - - assert_int_equal(ssh_is_ipaddr("2001:0db8:85a3:0000:0000:8a2e:0370:7334:1002"), 0); - assert_int_equal(ssh_is_ipaddr("fe80:x:202:b3ff:fe1e:8329"), 0); - assert_int_equal(ssh_is_ipaddr("fe80:x:202:b3ff:fe1e:8329"), 0); - assert_int_equal(ssh_is_ipaddr(":1"), 0); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test(torture_ssh_is_ipaddr) - }; - - ssh_init(); - rc=run_tests(tests); - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/unittests/torture_keyfiles.c b/libssh/tests/unittests/torture_keyfiles.c deleted file mode 100644 index 9446bc6d..00000000 --- a/libssh/tests/unittests/torture_keyfiles.c +++ /dev/null @@ -1,261 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#include "legacy.c" - -#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa" -#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa" -#define LIBSSH_PASSPHRASE "libssh-rocks" - -static void setup_rsa_key(void **state) { - ssh_session session; - int rc; - - unlink(LIBSSH_RSA_TESTKEY); - unlink(LIBSSH_RSA_TESTKEY ".pub"); - - rc = system("ssh-keygen -t rsa -q -N \"\" -f " LIBSSH_RSA_TESTKEY); - assert_true(rc == 0); - - session = ssh_new(); - *state = session; -} - -static void setup_dsa_key(void **state) { - ssh_session session; - int rc; - - unlink(LIBSSH_DSA_TESTKEY); - unlink(LIBSSH_DSA_TESTKEY ".pub"); - - rc = system("ssh-keygen -t dsa -q -N \"\" -f " LIBSSH_DSA_TESTKEY); - assert_true(rc == 0); - - session = ssh_new(); - *state = session; -} - -static void setup_both_keys(void **state) { - setup_rsa_key(state); - ssh_free(*state); - setup_dsa_key(state); -} - -static void setup_both_keys_passphrase(void **state) { - ssh_session session; - int rc; - - rc = system("ssh-keygen -t rsa -N " LIBSSH_PASSPHRASE " -f " LIBSSH_RSA_TESTKEY); - assert_true(rc == 0); - - rc = system("ssh-keygen -t dsa -N " LIBSSH_PASSPHRASE " -f " LIBSSH_DSA_TESTKEY); - assert_true(rc == 0); - - session = ssh_new(); - *state = session; -} -static void teardown(void **state) { - unlink(LIBSSH_DSA_TESTKEY); - unlink(LIBSSH_DSA_TESTKEY ".pub"); - - unlink(LIBSSH_RSA_TESTKEY); - unlink(LIBSSH_RSA_TESTKEY ".pub"); - - ssh_free(*state); -} - -static void torture_pubkey_from_file(void **state) { - ssh_session session = *state; - ssh_string pubkey; - int type, rc; - - rc = ssh_try_publickey_from_file(session, LIBSSH_RSA_TESTKEY, &pubkey, &type); - - assert_true(rc == 0); - - ssh_string_free(pubkey); - - /* test if it returns 1 if pubkey doesn't exist */ - unlink(LIBSSH_RSA_TESTKEY ".pub"); - - rc = ssh_try_publickey_from_file(session, LIBSSH_RSA_TESTKEY, &pubkey, &type); - assert_true(rc == 1); - - /* test if it returns -1 if privkey doesn't exist */ - unlink(LIBSSH_RSA_TESTKEY); - - rc = ssh_try_publickey_from_file(session, LIBSSH_RSA_TESTKEY, &pubkey, &type); - assert_true(rc == -1); -} - -static int torture_read_one_line(const char *filename, char *buffer, size_t len) { - FILE *fp; - size_t rc; - - fp = fopen(filename, "r"); - if (fp == NULL) { - return -1; - } - - rc = fread(buffer, len, 1, fp); - if (rc != 0 || ferror(fp)) { - fclose(fp); - return -1; - } - - fclose(fp); - - return 0; -} - -static void torture_pubkey_generate_from_privkey(void **state) { - ssh_session session = *state; - ssh_private_key privkey = NULL; - ssh_public_key pubkey = NULL; - ssh_string pubkey_orig = NULL; - ssh_string pubkey_new = NULL; - char pubkey_line_orig[512] = {0}; - char pubkey_line_new[512] = {0}; - int type_orig = 0; - int type_new = 0; - int rc; - - /* read the publickey */ - rc = ssh_try_publickey_from_file(session, LIBSSH_RSA_TESTKEY, &pubkey_orig, - &type_orig); - assert_true(rc == 0); - assert_true(pubkey_orig != NULL); - - rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub", pubkey_line_orig, - sizeof(pubkey_line_orig)); - assert_true(rc == 0); - - /* remove the public key, generate it from the private key and write it. */ - unlink(LIBSSH_RSA_TESTKEY ".pub"); - - privkey = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, 0, NULL); - assert_true(privkey != NULL); - - pubkey = publickey_from_privatekey(privkey); - assert_true(pubkey != NULL); - type_new = privkey->type; - privatekey_free(privkey); - - pubkey_new = publickey_to_string(pubkey); - publickey_free(pubkey); - - assert_true(pubkey_new != NULL); - - assert_true(ssh_string_len(pubkey_orig) == ssh_string_len(pubkey_new)); - assert_memory_equal(ssh_string_data(pubkey_orig), - ssh_string_data(pubkey_new), - ssh_string_len(pubkey_orig)); - - rc = ssh_publickey_to_file(session, LIBSSH_RSA_TESTKEY ".pub", pubkey_new, type_new); - assert_true(rc == 0); - - rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub", pubkey_line_new, - sizeof(pubkey_line_new)); - assert_true(rc == 0); - - assert_string_equal(pubkey_line_orig, pubkey_line_new); - - ssh_string_free(pubkey_orig); - ssh_string_free(pubkey_new); -} - -/** - * @brief tests the privatekey_from_file function without passphrase - */ -static void torture_privatekey_from_file(void **state) { - ssh_session session = *state; - ssh_private_key key = NULL; - - key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, SSH_KEYTYPE_RSA, NULL); - assert_true(key != NULL); - if (key != NULL) { - privatekey_free(key); - key = NULL; - } - - key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, SSH_KEYTYPE_DSS, NULL); - assert_true(key != NULL); - if (key != NULL) { - privatekey_free(key); - key = NULL; - } - - /* Test the automatic type discovery */ - key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, 0, NULL); - assert_true(key != NULL); - if (key != NULL) { - privatekey_free(key); - key = NULL; - } - - key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, 0, NULL); - assert_true(key != NULL); - if (key != NULL) { - privatekey_free(key); - key = NULL; - } -} - -/** - * @brief tests the privatekey_from_file function with passphrase - */ -static void torture_privatekey_from_file_passphrase(void **state) { - ssh_session session = *state; - ssh_private_key key = NULL; - - key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, SSH_KEYTYPE_RSA, LIBSSH_PASSPHRASE); - assert_true(key != NULL); - if (key != NULL) { - privatekey_free(key); - key = NULL; - } - - key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, SSH_KEYTYPE_DSS, LIBSSH_PASSPHRASE); - assert_true(key != NULL); - if (key != NULL) { - privatekey_free(key); - key = NULL; - } - - /* Test the automatic type discovery */ - key = privatekey_from_file(session, LIBSSH_RSA_TESTKEY, 0, LIBSSH_PASSPHRASE); - assert_true(key != NULL); - if (key != NULL) { - privatekey_free(key); - key = NULL; - } - - key = privatekey_from_file(session, LIBSSH_DSA_TESTKEY, 0, LIBSSH_PASSPHRASE); - assert_true(key != NULL); - if (key != NULL) { - privatekey_free(key); - key = NULL; - } -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_pubkey_from_file, - setup_rsa_key, - teardown), - unit_test_setup_teardown(torture_pubkey_generate_from_privkey, - setup_rsa_key, teardown), - unit_test_setup_teardown(torture_privatekey_from_file, - setup_both_keys, - teardown), - unit_test_setup_teardown(torture_privatekey_from_file_passphrase, - setup_both_keys_passphrase, teardown), - }; - - - ssh_init(); - rc=run_tests(tests); - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/unittests/torture_list.c b/libssh/tests/unittests/torture_list.c deleted file mode 100644 index 75f41825..00000000 --- a/libssh/tests/unittests/torture_list.c +++ /dev/null @@ -1,91 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#include "error.c" -#include "misc.c" - -static void torture_ssh_list_new(void **state) { - struct ssh_list *xlist; - - (void) state; - - xlist = ssh_list_new(); - - assert_true(xlist != NULL); - assert_true(xlist->root == NULL); - assert_true(xlist->end == NULL); - - ssh_list_free(xlist); -} - -static void torture_ssh_list_append(void **state) { - struct ssh_list *xlist; - int rc; - - (void) state; - - xlist = ssh_list_new(); - assert_true(xlist != NULL); - - rc = ssh_list_append(xlist, "item1"); - assert_true(rc == 0); - assert_string_equal((const char *) xlist->root->data, "item1"); - assert_string_equal((const char *) xlist->end->data, "item1"); - - rc = ssh_list_append(xlist, "item2"); - assert_true(rc == 0); - assert_string_equal((const char *) xlist->root->data, "item1"); - assert_string_equal((const char *) xlist->end->data, "item2"); - - rc = ssh_list_append(xlist, "item3"); - assert_true(rc == 0); - assert_string_equal((const char *) xlist->root->data, "item1"); - assert_string_equal((const char *) xlist->root->next->data, "item2"); - assert_string_equal((const char *) xlist->root->next->next->data, "item3"); - assert_string_equal((const char *) xlist->end->data, "item3"); - - ssh_list_free(xlist); -} - -static void torture_ssh_list_prepend(void **state) { - struct ssh_list *xlist; - int rc; - - (void) state; - - xlist = ssh_list_new(); - assert_true(xlist != NULL); - - rc = ssh_list_prepend(xlist, "item1"); - assert_true(rc == 0); - assert_string_equal((const char *) xlist->root->data, "item1"); - assert_string_equal((const char *) xlist->end->data, "item1"); - - rc = ssh_list_append(xlist, "item2"); - assert_true(rc == 0); - assert_string_equal((const char *) xlist->root->data, "item1"); - assert_string_equal((const char *) xlist->end->data, "item2"); - - rc = ssh_list_prepend(xlist, "item3"); - assert_true(rc == 0); - assert_string_equal((const char *) xlist->root->data, "item3"); - assert_string_equal((const char *) xlist->root->next->data, "item1"); - assert_string_equal((const char *) xlist->root->next->next->data, "item2"); - assert_string_equal((const char *) xlist->end->data, "item2"); - - ssh_list_free(xlist); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test(torture_ssh_list_new), - unit_test(torture_ssh_list_append), - unit_test(torture_ssh_list_prepend), - }; - - ssh_init(); - rc=run_tests(tests); - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/unittests/torture_misc.c b/libssh/tests/unittests/torture_misc.c deleted file mode 100644 index 26770324..00000000 --- a/libssh/tests/unittests/torture_misc.c +++ /dev/null @@ -1,210 +0,0 @@ - -#include -#ifndef _WIN32 - -#define _POSIX_PTHREAD_SEMANTICS -#include -#endif - -#define LIBSSH_STATIC -#include - -#include "torture.h" -#include "misc.c" -#include "error.c" - -#define TORTURE_TEST_DIR "/usr/local/bin/truc/much/.." - - -static void setup(void **state) { - ssh_session session = ssh_new(); - *state = session; -} - -static void teardown(void **state) { - ssh_free(*state); -} - -static void torture_get_user_home_dir(void **state) { -#ifndef _WIN32 - struct passwd *pwd = getpwuid(getuid()); -#endif /* _WIN32 */ - char *user; - - (void) state; - - user = ssh_get_user_home_dir(); - assert_false(user == NULL); -#ifndef _WIN32 - assert_string_equal(user, pwd->pw_dir); -#endif /* _WIN32 */ - - SAFE_FREE(user); -} - -static void torture_basename(void **state) { - char *path; - - (void) state; - - path=ssh_basename(TORTURE_TEST_DIR "/test"); - assert_true(path != NULL); - assert_string_equal(path, "test"); - SAFE_FREE(path); - path=ssh_basename(TORTURE_TEST_DIR "/test/"); - assert_true(path != NULL); - assert_string_equal(path, "test"); - SAFE_FREE(path); -} - -static void torture_dirname(void **state) { - char *path; - - (void) state; - - path=ssh_dirname(TORTURE_TEST_DIR "/test"); - assert_true(path != NULL); - assert_string_equal(path, TORTURE_TEST_DIR ); - SAFE_FREE(path); - path=ssh_dirname(TORTURE_TEST_DIR "/test/"); - assert_true(path != NULL); - assert_string_equal(path, TORTURE_TEST_DIR); - SAFE_FREE(path); -} - -static void torture_ntohll(void **state) { - uint64_t value = 0x0123456789abcdef; - uint32_t sample = 1; - unsigned char *ptr = (unsigned char *) &sample; - uint64_t check; - - (void) state; - - if (ptr[0] == 1){ - /* we're in little endian */ - check = 0xefcdab8967452301; - } else { - /* big endian */ - check = value; - } - value = ntohll(value); - assert_true(value == check); -} - -#ifdef _WIN32 - -static void torture_path_expand_tilde_win(void **state) { - char *d; - - (void) state; - - d = ssh_path_expand_tilde("~\\.ssh"); - assert_false(d == NULL); - print_message("Expanded path: %s\n", d); - free(d); - - d = ssh_path_expand_tilde("/guru/meditation"); - assert_string_equal(d, "/guru/meditation"); - free(d); -} - -#else /* _WIN32 */ - -static void torture_path_expand_tilde_unix(void **state) { - char h[256]; - char *d; - - (void) state; - - snprintf(h, 256 - 1, "%s/.ssh", getenv("HOME")); - - d = ssh_path_expand_tilde("~/.ssh"); - assert_string_equal(d, h); - free(d); - - d = ssh_path_expand_tilde("/guru/meditation"); - assert_string_equal(d, "/guru/meditation"); - free(d); - - snprintf(h, 256 - 1, "~%s/.ssh", getenv("USER")); - d = ssh_path_expand_tilde(h); - - snprintf(h, 256 - 1, "%s/.ssh", getenv("HOME")); - assert_string_equal(d, h); - free(d); -} - -#endif /* _WIN32 */ - -static void torture_path_expand_escape(void **state) { - ssh_session session = *state; - const char *s = "%d/%h/by/%r"; - char *e; - - session->opts.sshdir = strdup("guru"); - session->opts.host = strdup("meditation"); - session->opts.username = strdup("root"); - - e = ssh_path_expand_escape(session, s); - assert_string_equal(e, "guru/meditation/by/root"); - free(e); -} - -static void torture_path_expand_known_hosts(void **state) { - ssh_session session = *state; - char *tmp; - - session->opts.sshdir = strdup("/home/guru/.ssh"); - - tmp = ssh_path_expand_escape(session, "%d/known_hosts"); - assert_string_equal(tmp, "/home/guru/.ssh/known_hosts"); - free(tmp); -} - -static void torture_timeout_elapsed(void **state){ - struct ssh_timestamp ts; - (void) state; - ssh_timestamp_init(&ts); - usleep(50000); - assert_true(ssh_timeout_elapsed(&ts,25)); - assert_false(ssh_timeout_elapsed(&ts,30000)); - assert_false(ssh_timeout_elapsed(&ts,75)); - assert_true(ssh_timeout_elapsed(&ts,0)); - assert_false(ssh_timeout_elapsed(&ts,-1)); -} - -static void torture_timeout_update(void **state){ - struct ssh_timestamp ts; - (void) state; - ssh_timestamp_init(&ts); - usleep(50000); - assert_int_equal(ssh_timeout_update(&ts,25), 0); - assert_in_range(ssh_timeout_update(&ts,30000),29000,29960); - assert_in_range(ssh_timeout_update(&ts,75),1,40); - assert_int_equal(ssh_timeout_update(&ts,0),0); - assert_int_equal(ssh_timeout_update(&ts,-1),-1); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test(torture_get_user_home_dir), - unit_test(torture_basename), - unit_test(torture_dirname), - unit_test(torture_ntohll), -#ifdef _WIN32 - unit_test(torture_path_expand_tilde_win), -#else - unit_test(torture_path_expand_tilde_unix), -#endif - unit_test_setup_teardown(torture_path_expand_escape, setup, teardown), - unit_test_setup_teardown(torture_path_expand_known_hosts, setup, teardown), - unit_test(torture_timeout_elapsed), - unit_test(torture_timeout_update), - }; - - ssh_init(); - rc=run_tests(tests); - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/unittests/torture_options.c b/libssh/tests/unittests/torture_options.c deleted file mode 100644 index 6f5df1bb..00000000 --- a/libssh/tests/unittests/torture_options.c +++ /dev/null @@ -1,215 +0,0 @@ -#define LIBSSH_STATIC - -#ifndef _WIN32 -#define _POSIX_PTHREAD_SEMANTICS -# include -#endif - -#include "torture.h" -#include -#include - -static void setup(void **state) { - ssh_session session = ssh_new(); - *state = session; -} - -static void teardown(void **state) { - ssh_free(*state); -} - -static void torture_options_set_host(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == 0); - assert_string_equal(session->opts.host, "localhost"); - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "guru@meditation"); - assert_true(rc == 0); - assert_string_equal(session->opts.host, "meditation"); - assert_string_equal(session->opts.username, "guru"); -} - -static void torture_options_get_host(void **state) { - ssh_session session = *state; - int rc; - char* host = NULL; - - rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - assert_true(rc == 0); - assert_string_equal(session->opts.host, "localhost"); - - assert_false(ssh_options_get(session, SSH_OPTIONS_HOST, &host)); - - assert_string_equal(host, "localhost"); - free(host); -} - -static void torture_options_set_port(void **state) { - ssh_session session = *state; - int rc; - unsigned int port = 42; - - rc = ssh_options_set(session, SSH_OPTIONS_PORT, &port); - assert_true(rc == 0); - assert_true(session->opts.port == port); - - rc = ssh_options_set(session, SSH_OPTIONS_PORT_STR, "23"); - assert_true(rc == 0); - assert_true(session->opts.port == 23); - - rc = ssh_options_set(session, SSH_OPTIONS_PORT_STR, "five"); - assert_true(rc == -1); - - rc = ssh_options_set(session, SSH_OPTIONS_PORT, NULL); - assert_true(rc == -1); -} - -static void torture_options_get_port(void **state) { - ssh_session session = *state; - unsigned int given_port = 1234; - unsigned int port_container; - int rc; - rc = ssh_options_set(session, SSH_OPTIONS_PORT, &given_port); - assert_true(rc == 0); - rc = ssh_options_get_port(session, &port_container); - assert_true(rc == 0); - assert_int_equal(port_container, 1234); -} - -static void torture_options_get_user(void **state) { - ssh_session session = *state; - char* user = NULL; - int rc; - rc = ssh_options_set(session, SSH_OPTIONS_USER, "magicaltrevor"); - assert_true(rc == SSH_OK); - rc = ssh_options_get(session, SSH_OPTIONS_USER, &user); - assert_string_equal(user, "magicaltrevor"); - free(user); -} - -static void torture_options_set_fd(void **state) { - ssh_session session = *state; - socket_t fd = 42; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_FD, &fd); - assert_true(rc == 0); - assert_true(session->opts.fd == fd); - - rc = ssh_options_set(session, SSH_OPTIONS_FD, NULL); - assert_true(rc == SSH_ERROR); - assert_true(session->opts.fd == SSH_INVALID_SOCKET); -} - -static void torture_options_set_user(void **state) { - ssh_session session = *state; - int rc; -#ifndef _WIN32 -# ifndef NSS_BUFLEN_PASSWD -# define NSS_BUFLEN_PASSWD 4096 -# endif /* NSS_BUFLEN_PASSWD */ - struct passwd pwd; - struct passwd *pwdbuf; - char buf[NSS_BUFLEN_PASSWD]; - - /* get local username */ - rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf); - assert_true(rc == 0); -#endif /* _WIN32 */ - - rc = ssh_options_set(session, SSH_OPTIONS_USER, "guru"); - assert_true(rc == 0); - assert_string_equal(session->opts.username, "guru"); - - - rc = ssh_options_set(session, SSH_OPTIONS_USER, NULL); - assert_true(rc == 0); - -#ifndef _WIN32 - assert_string_equal(session->opts.username, pwd.pw_name); -#endif -} - -/* TODO */ -#if 0 -static voidtorture_options_set_sshdir) -{ -} -END_TEST -#endif - -static void torture_options_set_identity(void **state) { - ssh_session session = *state; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, "identity1"); - assert_true(rc == 0); - assert_string_equal(session->opts.identity->root->data, "identity1"); - - rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "identity2"); - assert_true(rc == 0); - assert_string_equal(session->opts.identity->root->data, "identity2"); - assert_string_equal(session->opts.identity->root->next->data, "identity1"); -} - -static void torture_options_get_identity(void **state) { - ssh_session session = *state; - char *identity = NULL; - int rc; - - rc = ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, "identity1"); - assert_true(rc == 0); - rc = ssh_options_get(session, SSH_OPTIONS_IDENTITY, &identity); - assert_true(rc == SSH_OK); - assert_string_equal(identity, "identity1"); - SAFE_FREE(identity); - - rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "identity2"); - assert_true(rc == 0); - assert_string_equal(session->opts.identity->root->data, "identity2"); - rc = ssh_options_get(session, SSH_OPTIONS_IDENTITY, &identity); - assert_true(rc == SSH_OK); - assert_string_equal(identity, "identity2"); - free(identity); -} - -static void torture_options_proxycommand(void **state) { - ssh_session session = *state; - int rc; - - /* Enable ProxyCommand */ - rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, "ssh -q -A -X -W %h:%p JUMPHOST"); - assert_int_equal(rc, 0); - - assert_string_equal(session->opts.ProxyCommand, "ssh -q -A -X -W %h:%p JUMPHOST"); - - /* Disable ProxyCommand */ - rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, "none"); - assert_int_equal(rc, 0); - - assert_null(session->opts.ProxyCommand); -} - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test_setup_teardown(torture_options_set_host, setup, teardown), - unit_test_setup_teardown(torture_options_get_host, setup, teardown), - unit_test_setup_teardown(torture_options_set_port, setup, teardown), - unit_test_setup_teardown(torture_options_get_port, setup, teardown), - unit_test_setup_teardown(torture_options_set_fd, setup, teardown), - unit_test_setup_teardown(torture_options_set_user, setup, teardown), - unit_test_setup_teardown(torture_options_get_user, setup, teardown), - unit_test_setup_teardown(torture_options_set_identity, setup, teardown), - unit_test_setup_teardown(torture_options_get_identity, setup, teardown), - unit_test_setup_teardown(torture_options_proxycommand, setup, teardown), - }; - - ssh_init(); - rc=run_tests(tests); - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/unittests/torture_pki.c b/libssh/tests/unittests/torture_pki.c deleted file mode 100644 index efcbb01b..00000000 --- a/libssh/tests/unittests/torture_pki.c +++ /dev/null @@ -1,1338 +0,0 @@ -#define LIBSSH_STATIC - -#include "torture.h" -#include "pki.c" -#include -#include - -#define LIBSSH_RSA_TESTKEY "libssh_testkey.id_rsa" -#define LIBSSH_DSA_TESTKEY "libssh_testkey.id_dsa" -#define LIBSSH_ECDSA_TESTKEY "libssh_testkey.id_ecdsa" - -const unsigned char HASH[] = "12345678901234567890"; - -static void setup_rsa_key(void **state) { - (void) state; /* unused */ - - unlink(LIBSSH_RSA_TESTKEY); - unlink(LIBSSH_RSA_TESTKEY ".pub"); - - torture_write_file(LIBSSH_RSA_TESTKEY, - torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0)); - torture_write_file(LIBSSH_RSA_TESTKEY ".pub", - torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0)); -} - -static void setup_dsa_key(void **state) { - (void) state; /* unused */ - - unlink(LIBSSH_DSA_TESTKEY); - unlink(LIBSSH_DSA_TESTKEY ".pub"); - - torture_write_file(LIBSSH_DSA_TESTKEY, - torture_get_testkey(SSH_KEYTYPE_DSS, 0, 0)); - torture_write_file(LIBSSH_DSA_TESTKEY ".pub", - torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0)); -} - -#ifdef HAVE_OPENSSL_ECC -static void setup_ecdsa_key(void **state, int ecdsa_bits) { - - (void) state; /* unused */ - - unlink(LIBSSH_ECDSA_TESTKEY); - unlink(LIBSSH_ECDSA_TESTKEY ".pub"); - - torture_write_file(LIBSSH_ECDSA_TESTKEY, - torture_get_testkey(SSH_KEYTYPE_ECDSA, ecdsa_bits, 0)); - torture_write_file(LIBSSH_ECDSA_TESTKEY ".pub", - torture_get_testkey_pub(SSH_KEYTYPE_ECDSA, ecdsa_bits)); -} - -static void setup_ecdsa_key_521(void **state) { - setup_ecdsa_key(state, 521); -} - -static void setup_ecdsa_key_384(void **state) { - setup_ecdsa_key(state, 384); -} - -static void setup_ecdsa_key_256(void **state) { - setup_ecdsa_key(state, 256); -} -#endif - -static void setup_both_keys(void **state) { - (void) state; /* unused */ - - setup_rsa_key(state); - setup_dsa_key(state); -} - -static void teardown(void **state) { - (void) state; /* unused */ - - unlink(LIBSSH_DSA_TESTKEY); - unlink(LIBSSH_DSA_TESTKEY ".pub"); - - unlink(LIBSSH_RSA_TESTKEY); - unlink(LIBSSH_RSA_TESTKEY ".pub"); - - unlink(LIBSSH_ECDSA_TESTKEY); - unlink(LIBSSH_ECDSA_TESTKEY ".pub"); -} - -static char *read_file(const char *filename) { - char *key; - int fd; - int size; - int rc; - struct stat sb; - - assert_true(filename != NULL); - assert_true(*filename != '\0'); - - fd = open(filename, O_RDONLY); - assert_true(fd >= 0); - - rc = fstat(fd, &sb); - assert_int_equal(rc, 0); - - key = malloc(sb.st_size + 1); - assert_true(key != NULL); - - size = read(fd, key, sb.st_size); - assert_true(size == sb.st_size); - - close(fd); - - key[size] = '\0'; - return key; -} - -static int torture_read_one_line(const char *filename, char *buffer, size_t len) { - FILE *fp; - size_t nmemb; - - fp = fopen(filename, "r"); - if (fp == NULL) { - return -1; - } - - nmemb = fread(buffer, len - 2, 1, fp); - if (nmemb != 0 || ferror(fp)) { - fclose(fp); - return -1; - } - buffer[len - 1] = '\0'; - - fclose(fp); - - return 0; -} - -/** @internal - * returns the character len of a public key string, omitting the comment part - */ -static int torture_pubkey_len(const char *pubkey){ - const char *ptr; - ptr=strchr(pubkey, ' '); - if (ptr != NULL){ - ptr = strchr(ptr + 1, ' '); - if (ptr != NULL){ - return ptr - pubkey; - } - } - return 0; -} - -static void torture_pki_keytype(void **state) { - enum ssh_keytypes_e type; - const char *type_c; - - (void) state; /* unused */ - - type = ssh_key_type(NULL); - assert_true(type == SSH_KEYTYPE_UNKNOWN); - - type = ssh_key_type_from_name(NULL); - assert_true(type == SSH_KEYTYPE_UNKNOWN); - - type = ssh_key_type_from_name("42"); - assert_true(type == SSH_KEYTYPE_UNKNOWN); - - type_c = ssh_key_type_to_char(SSH_KEYTYPE_UNKNOWN); - assert_true(type_c == NULL); - - type_c = ssh_key_type_to_char(42); - assert_true(type_c == NULL); -} - -static void torture_pki_signature(void **state) -{ - ssh_signature sig; - - (void) state; /* unused */ - - sig = ssh_signature_new(); - assert_true(sig != NULL); - - ssh_signature_free(sig); -} - -static void torture_pki_import_privkey_base64_RSA(void **state) { - int rc; - char *key_str; - ssh_key key; - const char *passphrase = torture_get_testkey_passphrase(); - enum ssh_keytypes_e type; - - (void) state; /* unused */ - - key_str = read_file(LIBSSH_RSA_TESTKEY); - assert_true(key_str != NULL); - - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); - assert_true(rc == 0); - - type = ssh_key_type(key); - assert_true(type == SSH_KEYTYPE_RSA); - - rc = ssh_key_is_public(key); - assert_true(rc == 1); - - free(key_str); - ssh_key_free(key); -} - -static void torture_pki_import_privkey_base64_NULL_key(void **state) { - int rc; - const char *passphrase = torture_get_testkey_passphrase(); - - (void) state; /* unused */ - - /* test if it returns -1 if key is NULL */ - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0), - passphrase, - NULL, - NULL, - NULL); - assert_true(rc == -1); - -} - -static void torture_pki_import_privkey_base64_NULL_str(void **state) { - int rc; - ssh_key key = NULL; - const char *passphrase = torture_get_testkey_passphrase(); - - (void) state; /* unused */ - - /* test if it returns -1 if key_str is NULL */ - rc = ssh_pki_import_privkey_base64(NULL, passphrase, NULL, NULL, &key); - assert_true(rc == -1); - - ssh_key_free(key); -} - -static void torture_pki_import_privkey_base64_DSA(void **state) { - int rc; - ssh_key key; - const char *passphrase = torture_get_testkey_passphrase(); - - (void) state; /* unused */ - - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 0), - passphrase, - NULL, - NULL, - &key); - assert_true(rc == 0); - - ssh_key_free(key); -} - -#ifdef HAVE_ECC -static void torture_pki_import_privkey_base64_ECDSA(void **state) { - int rc; - char *key_str; - ssh_key key; - const char *passphrase = torture_get_testkey_passphrase(); - - (void) state; /* unused */ - - key_str = read_file(LIBSSH_ECDSA_TESTKEY); - assert_true(key_str != NULL); - - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); - assert_true(rc == 0); - - free(key_str); - ssh_key_free(key); -} -#endif - -static void torture_pki_import_privkey_base64_passphrase(void **state) { - int rc; - ssh_key key = NULL; - const char *passphrase = torture_get_testkey_passphrase(); - - (void) state; /* unused */ - - - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 1), - passphrase, - NULL, - NULL, - &key); - assert_true(rc == 0); - ssh_key_free(key); - - /* test if it returns -1 if passphrase is wrong */ - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 1), - "wrong passphrase !!", - NULL, - NULL, - &key); - assert_true(rc == -1); - -#ifndef HAVE_LIBCRYPTO - /* test if it returns -1 if passphrase is NULL */ - /* libcrypto asks for a passphrase, so skip this test */ - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 1), - NULL, - NULL, - NULL, - &key); - assert_true(rc == -1); -#endif - - /* same for DSA */ - - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 1), - passphrase, - NULL, - NULL, - &key); - assert_true(rc == 0); - ssh_key_free(key); - - /* test if it returns -1 if passphrase is wrong */ - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 1), - "wrong passphrase !!", - NULL, - NULL, - &key); - assert_true(rc == -1); - -#ifndef HAVE_LIBCRYPTO - /* test if it returns -1 if passphrase is NULL */ - /* libcrypto asks for a passphrase, so skip this test */ - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 1), - NULL, - NULL, - NULL, - &key); - assert_true(rc == -1); -#endif - -} - -static void torture_pki_pki_publickey_from_privatekey_RSA(void **state) { - int rc; - ssh_key key; - ssh_key pubkey; - const char *passphrase = NULL; - - (void) state; /* unused */ - - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0, 0), - passphrase, - NULL, - NULL, - &key); - assert_true(rc == 0); - - rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); - assert_true(rc == SSH_OK); - - ssh_key_free(key); - ssh_key_free(pubkey); -} - -static void torture_pki_pki_publickey_from_privatekey_DSA(void **state) { - int rc; - ssh_key key; - ssh_key pubkey; - const char *passphrase = NULL; - - (void) state; /* unused */ - - rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_DSS, 0, 0), - passphrase, - NULL, - NULL, - &key); - assert_true(rc == 0); - - rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); - assert_true(rc == SSH_OK); - - ssh_key_free(key); - ssh_key_free(pubkey); -} - -#ifdef HAVE_ECC -static void torture_pki_publickey_from_privatekey_ECDSA(void **state) { - int rc; - char *key_str; - ssh_key key; - ssh_key pubkey; - const char *passphrase = NULL; - - (void) state; /* unused */ - - key_str = read_file(LIBSSH_ECDSA_TESTKEY); - assert_true(key_str != NULL); - - rc = ssh_pki_import_privkey_base64(key_str, passphrase, NULL, NULL, &key); - assert_true(rc == 0); - - rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); - assert_true(rc == SSH_OK); - - free(key_str); - ssh_key_free(key); - ssh_key_free(pubkey); -} -#endif - -static void torture_pki_publickey_dsa_base64(void **state) -{ - enum ssh_keytypes_e type; - char *b64_key, *key_buf, *p; - const char *q; - ssh_key key; - int rc; - - (void) state; /* unused */ - - key_buf = strdup(torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0)); - assert_true(key_buf != NULL); - - q = p = key_buf; - while (*p != ' ') p++; - *p = '\0'; - - type = ssh_key_type_from_name(q); - assert_true(type == SSH_KEYTYPE_DSS); - - q = ++p; - while (*p != ' ') p++; - *p = '\0'; - - rc = ssh_pki_import_pubkey_base64(q, type, &key); - assert_true(rc == 0); - - rc = ssh_pki_export_pubkey_base64(key, &b64_key); - assert_true(rc == 0); - - assert_string_equal(q, b64_key); - - free(b64_key); - free(key_buf); - ssh_key_free(key); -} - -#ifdef HAVE_ECC -static void torture_pki_publickey_ecdsa_base64(void **state) -{ - enum ssh_keytypes_e type; - char *b64_key, *key_buf, *p; - const char *q; - ssh_key key; - int rc; - - (void) state; /* unused */ - - key_buf = read_file(LIBSSH_ECDSA_TESTKEY ".pub"); - assert_true(key_buf != NULL); - - q = p = key_buf; - while (*p != ' ') p++; - *p = '\0'; - - type = ssh_key_type_from_name(q); - assert_true(type == SSH_KEYTYPE_ECDSA); - - q = ++p; - while (*p != ' ') p++; - *p = '\0'; - - rc = ssh_pki_import_pubkey_base64(q, type, &key); - assert_true(rc == 0); - - rc = ssh_pki_export_pubkey_base64(key, &b64_key); - assert_true(rc == 0); - - assert_string_equal(q, b64_key); - - free(b64_key); - free(key_buf); - ssh_key_free(key); -} -#endif - -static void torture_pki_publickey_rsa_base64(void **state) -{ - enum ssh_keytypes_e type; - char *b64_key, *key_buf, *p; - const char *q; - ssh_key key; - int rc; - - (void) state; /* unused */ - - key_buf = strdup(torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0)); - assert_true(key_buf != NULL); - - q = p = key_buf; - while (*p != ' ') p++; - *p = '\0'; - - type = ssh_key_type_from_name(q); - assert_true(((type == SSH_KEYTYPE_RSA) || - (type == SSH_KEYTYPE_RSA1))); - - q = ++p; - while (*p != ' ') p++; - *p = '\0'; - - rc = ssh_pki_import_pubkey_base64(q, type, &key); - assert_true(rc == 0); - - rc = ssh_pki_export_pubkey_base64(key, &b64_key); - assert_true(rc == 0); - - assert_string_equal(q, b64_key); - - free(b64_key); - free(key_buf); - ssh_key_free(key); -} - -static void torture_generate_pubkey_from_privkey_rsa(void **state) { - char pubkey_generated[4096] = {0}; - ssh_key privkey; - ssh_key pubkey; - int rc; - int len; - - (void) state; /* unused */ - - /* remove the public key, generate it from the private key and write it. */ - unlink(LIBSSH_RSA_TESTKEY ".pub"); - - rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); - assert_true(rc == SSH_OK); - - rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_RSA_TESTKEY ".pub"); - assert_true(rc == 0); - - rc = torture_read_one_line(LIBSSH_RSA_TESTKEY ".pub", - pubkey_generated, - sizeof(pubkey_generated)); - assert_true(rc == 0); - - len = torture_pubkey_len(torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0)); - assert_memory_equal(torture_get_testkey_pub(SSH_KEYTYPE_RSA, 0), - pubkey_generated, - len); - - ssh_key_free(privkey); - ssh_key_free(pubkey); -} - -static void torture_generate_pubkey_from_privkey_dsa(void **state) { - char pubkey_generated[4096] = {0}; - ssh_key privkey; - ssh_key pubkey; - int len; - int rc; - - (void) state; /* unused */ - - /* remove the public key, generate it from the private key and write it. */ - unlink(LIBSSH_DSA_TESTKEY ".pub"); - - rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); - assert_true(rc == SSH_OK); - - rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_DSA_TESTKEY ".pub"); - assert_true(rc == 0); - - rc = torture_read_one_line(LIBSSH_DSA_TESTKEY ".pub", - pubkey_generated, - sizeof(pubkey_generated)); - assert_true(rc == 0); - - len = torture_pubkey_len(torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0)); - assert_memory_equal(torture_get_testkey_pub(SSH_KEYTYPE_DSS, 0), - pubkey_generated, - len); - - ssh_key_free(privkey); - ssh_key_free(pubkey); -} - -#ifdef HAVE_ECC -static void torture_generate_pubkey_from_privkey_ecdsa(void **state) { - char pubkey_original[4096] = {0}; - char pubkey_generated[4096] = {0}; - ssh_key privkey; - ssh_key pubkey; - int rc; - int len; - - (void) state; /* unused */ - - rc = torture_read_one_line(LIBSSH_ECDSA_TESTKEY ".pub", - pubkey_original, - sizeof(pubkey_original)); - assert_true(rc == 0); - - /* remove the public key, generate it from the private key and write it. */ - unlink(LIBSSH_ECDSA_TESTKEY ".pub"); - - rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); - assert_true(rc == SSH_OK); - - rc = ssh_pki_export_pubkey_file(pubkey, LIBSSH_ECDSA_TESTKEY ".pub"); - assert_true(rc == 0); - - rc = torture_read_one_line(LIBSSH_ECDSA_TESTKEY ".pub", - pubkey_generated, - sizeof(pubkey_generated)); - assert_true(rc == 0); - len = torture_pubkey_len(pubkey_original); - assert_int_equal(strncmp(pubkey_original, pubkey_generated, len), 0); - - ssh_key_free(privkey); - ssh_key_free(pubkey); -} -#endif - -static void torture_pki_duplicate_key_rsa(void **state) -{ - int rc; - char *b64_key; - char *b64_key_gen; - ssh_key pubkey; - ssh_key privkey; - ssh_key privkey_dup; - - (void) state; - - rc = ssh_pki_import_pubkey_file(LIBSSH_RSA_TESTKEY ".pub", &pubkey); - assert_true(rc == 0); - - rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key); - assert_true(rc == 0); - ssh_key_free(pubkey); - - rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - privkey_dup = ssh_key_dup(privkey); - assert_true(privkey_dup != NULL); - - rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); - assert_true(rc == SSH_OK); - - rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key_gen); - assert_true(rc == 0); - - assert_string_equal(b64_key, b64_key_gen); - - rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE); - assert_true(rc == 0); - - ssh_key_free(pubkey); - ssh_key_free(privkey); - ssh_key_free(privkey_dup); - ssh_string_free_char(b64_key); - ssh_string_free_char(b64_key_gen); -} - -static void torture_pki_duplicate_key_dsa(void **state) -{ - int rc; - char *b64_key; - char *b64_key_gen; - ssh_key pubkey; - ssh_key privkey; - ssh_key privkey_dup; - - (void) state; - - rc = ssh_pki_import_pubkey_file(LIBSSH_DSA_TESTKEY ".pub", &pubkey); - assert_true(rc == 0); - - rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key); - assert_true(rc == 0); - ssh_key_free(pubkey); - - rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - privkey_dup = ssh_key_dup(privkey); - assert_true(privkey_dup != NULL); - - rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); - assert_true(rc == SSH_OK); - - rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key_gen); - assert_true(rc == 0); - - assert_string_equal(b64_key, b64_key_gen); - - rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE); - assert_true(rc == 0); - - ssh_key_free(pubkey); - ssh_key_free(privkey); - ssh_key_free(privkey_dup); - ssh_string_free_char(b64_key); - ssh_string_free_char(b64_key_gen); -} - -#ifdef HAVE_ECC -static void torture_pki_duplicate_key_ecdsa(void **state) -{ - int rc; - char *b64_key; - char *b64_key_gen; - ssh_key pubkey; - ssh_key privkey; - ssh_key privkey_dup; - - (void) state; - - rc = ssh_pki_import_pubkey_file(LIBSSH_ECDSA_TESTKEY ".pub", &pubkey); - assert_true(rc == 0); - - rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key); - assert_true(rc == 0); - ssh_key_free(pubkey); - - rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - privkey_dup = ssh_key_dup(privkey); - assert_true(privkey_dup != NULL); - - rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey); - assert_true(rc == SSH_OK); - - rc = ssh_pki_export_pubkey_base64(pubkey, &b64_key_gen); - assert_true(rc == 0); - - assert_string_equal(b64_key, b64_key_gen); - - rc = ssh_key_cmp(privkey, privkey_dup, SSH_KEY_CMP_PRIVATE); - assert_true(rc == 0); - - ssh_key_free(pubkey); - ssh_key_free(privkey); - ssh_key_free(privkey_dup); - ssh_string_free_char(b64_key); - ssh_string_free_char(b64_key_gen); -} - -/* Test case for bug #147: Private ECDSA key duplication did not carry - * over parts of the key that then caused subsequent key demotion to - * fail. - */ -static void torture_pki_ecdsa_duplicate_then_demote(void **state) -{ - ssh_key pubkey; - ssh_key privkey; - ssh_key privkey_dup; - int rc; - - (void) state; - - rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - privkey_dup = ssh_key_dup(privkey); - assert_true(privkey_dup != NULL); - assert_int_equal(privkey->ecdsa_nid, privkey_dup->ecdsa_nid); - - rc = ssh_pki_export_privkey_to_pubkey(privkey_dup, &pubkey); - assert_true(rc == 0); - assert_int_equal(pubkey->ecdsa_nid, privkey->ecdsa_nid); - - ssh_key_free(pubkey); - ssh_key_free(privkey); - ssh_key_free(privkey_dup); -} -#endif - -static void torture_pki_generate_key_rsa(void **state) -{ - int rc; - ssh_key key; - ssh_signature sign; - ssh_session session=ssh_new(); - (void) state; - - rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 4096, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - ssh_free(session); -} - -static void torture_pki_generate_key_rsa1(void **state) -{ - int rc; - ssh_key key; - ssh_signature sign; - ssh_session session=ssh_new(); - (void) state; - - rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 1024, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 2048, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 4096, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - ssh_free(session); -} - -static void torture_pki_generate_key_dsa(void **state) -{ - int rc; - ssh_key key; - ssh_signature sign; - ssh_session session=ssh_new(); - (void) state; - - rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 1024, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 2048, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - rc = ssh_pki_generate(SSH_KEYTYPE_DSS, 3072, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - ssh_free(session); -} - -#ifdef HAVE_ECC -static void torture_pki_generate_key_ecdsa(void **state) -{ - int rc; - ssh_key key; - ssh_signature sign; - enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN; - const char *type_char = NULL; - const char *etype_char = NULL; - ssh_session session=ssh_new(); - (void) state; - - rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 256, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - type = ssh_key_type(key); - assert_true(type == SSH_KEYTYPE_ECDSA); - type_char = ssh_key_type_to_char(type); - assert_true(strcmp(type_char, "ssh-ecdsa") == 0); - etype_char = ssh_pki_key_ecdsa_name(key); - assert_true(strcmp(etype_char, "ecdsa-sha2-nistp256") == 0); - - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 384, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - type = ssh_key_type(key); - assert_true(type == SSH_KEYTYPE_ECDSA); - type_char = ssh_key_type_to_char(type); - assert_true(strcmp(type_char, "ssh-ecdsa") == 0); - etype_char =ssh_pki_key_ecdsa_name(key); - assert_true(strcmp(etype_char, "ecdsa-sha2-nistp384") == 0); - - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - rc = ssh_pki_generate(SSH_KEYTYPE_ECDSA, 512, &key); - assert_true(rc == SSH_OK); - assert_true(key != NULL); - sign = pki_do_sign(key, HASH, 20); - assert_true(sign != NULL); - rc = pki_signature_verify(session,sign,key,HASH,20); - assert_true(rc == SSH_OK); - type = ssh_key_type(key); - assert_true(type == SSH_KEYTYPE_ECDSA); - type_char = ssh_key_type_to_char(type); - assert_true(strcmp(type_char, "ssh-ecdsa") == 0); - etype_char =ssh_pki_key_ecdsa_name(key); - assert_true(strcmp(etype_char, "ecdsa-sha2-nistp521") == 0); - - ssh_signature_free(sign); - ssh_key_free(key); - key=NULL; - - ssh_free(session); -} -#endif - -#ifdef HAVE_LIBCRYPTO -static void torture_pki_write_privkey_rsa(void **state) -{ - ssh_key origkey; - ssh_key privkey; - int rc; - - (void) state; /* unused */ - - ssh_set_log_level(5); - - rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, - NULL, - NULL, - NULL, - &origkey); - assert_true(rc == 0); - - unlink(LIBSSH_RSA_TESTKEY); - - rc = ssh_pki_export_privkey_file(origkey, - "", - NULL, - NULL, - LIBSSH_RSA_TESTKEY); - assert_true(rc == 0); - - rc = ssh_pki_import_privkey_file(LIBSSH_RSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE); - assert_true(rc == 0); - - ssh_key_free(origkey); - ssh_key_free(privkey); -} - -static void torture_pki_write_privkey_dsa(void **state) -{ - ssh_key origkey; - ssh_key privkey; - int rc; - - (void) state; /* unused */ - - ssh_set_log_level(5); - - rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY, - NULL, - NULL, - NULL, - &origkey); - assert_true(rc == 0); - - unlink(LIBSSH_DSA_TESTKEY); - - rc = ssh_pki_export_privkey_file(origkey, - "", - NULL, - NULL, - LIBSSH_DSA_TESTKEY); - assert_true(rc == 0); - - rc = ssh_pki_import_privkey_file(LIBSSH_DSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE); - assert_true(rc == 0); - - ssh_key_free(origkey); - ssh_key_free(privkey); -} - -#ifdef HAVE_ECC -static void torture_pki_write_privkey_ecdsa(void **state) -{ - ssh_key origkey; - ssh_key privkey; - int rc; - - (void) state; /* unused */ - - ssh_set_log_level(5); - - rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, - NULL, - NULL, - NULL, - &origkey); - assert_true(rc == 0); - - unlink(LIBSSH_ECDSA_TESTKEY); - - rc = ssh_pki_export_privkey_file(origkey, - "", - NULL, - NULL, - LIBSSH_ECDSA_TESTKEY); - assert_true(rc == 0); - - rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, - NULL, - NULL, - NULL, - &privkey); - assert_true(rc == 0); - - rc = ssh_key_cmp(origkey, privkey, SSH_KEY_CMP_PRIVATE); - assert_true(rc == 0); - - ssh_key_free(origkey); - ssh_key_free(privkey); -} -#endif -#endif /* HAVE_LIBCRYPTO */ - -#ifdef HAVE_ECC -static void torture_pki_ecdsa_name(void **state, const char *expected_name) -{ - int rc; - ssh_key key; - const char *etype_char = NULL; - - (void) state; /* unused */ - - ssh_set_log_level(5); - - rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY, NULL, NULL, NULL, &key); - assert_true(rc == 0); - - etype_char =ssh_pki_key_ecdsa_name(key); - assert_true(strcmp(etype_char, expected_name) == 0); - - ssh_key_free(key); -} - -static void torture_pki_ecdsa_name256(void **state) -{ - torture_pki_ecdsa_name(state, "ecdsa-sha2-nistp256"); -} - -static void torture_pki_ecdsa_name384(void **state) -{ - torture_pki_ecdsa_name(state, "ecdsa-sha2-nistp384"); -} - -static void torture_pki_ecdsa_name521(void **state) -{ - torture_pki_ecdsa_name(state, "ecdsa-sha2-nistp521"); -} -#endif - -int torture_run_tests(void) { - int rc; - const UnitTest tests[] = { - unit_test(torture_pki_keytype), - - unit_test(torture_pki_signature), - - /* ssh_pki_import_privkey_base64 */ - unit_test_setup_teardown(torture_pki_import_privkey_base64_NULL_key, - setup_rsa_key, - teardown), - unit_test_setup_teardown(torture_pki_import_privkey_base64_NULL_str, - setup_rsa_key, - teardown), - unit_test_setup_teardown(torture_pki_import_privkey_base64_RSA, - setup_rsa_key, - teardown), - unit_test_setup_teardown(torture_pki_import_privkey_base64_DSA, - setup_dsa_key, - teardown), -#ifdef HAVE_ECC - unit_test_setup_teardown(torture_pki_import_privkey_base64_ECDSA, - setup_ecdsa_key_256, - teardown), - unit_test_setup_teardown(torture_pki_import_privkey_base64_ECDSA, - setup_ecdsa_key_384, - teardown), - unit_test_setup_teardown(torture_pki_import_privkey_base64_ECDSA, - setup_ecdsa_key_521, - teardown), -#endif - unit_test(torture_pki_import_privkey_base64_passphrase), - /* ssh_pki_export_privkey_to_pubkey */ - unit_test_setup_teardown(torture_pki_pki_publickey_from_privatekey_RSA, - setup_rsa_key, - teardown), - unit_test_setup_teardown(torture_pki_pki_publickey_from_privatekey_DSA, - setup_dsa_key, - teardown), -#ifdef HAVE_ECC - unit_test_setup_teardown(torture_pki_publickey_from_privatekey_ECDSA, - setup_ecdsa_key_256, - teardown), - unit_test_setup_teardown(torture_pki_publickey_from_privatekey_ECDSA, - setup_ecdsa_key_384, - teardown), - unit_test_setup_teardown(torture_pki_publickey_from_privatekey_ECDSA, - setup_ecdsa_key_521, - teardown), - unit_test_setup_teardown(torture_pki_ecdsa_duplicate_then_demote, - setup_ecdsa_key_256, - teardown), - unit_test_setup_teardown(torture_pki_ecdsa_duplicate_then_demote, - setup_ecdsa_key_384, - teardown), - unit_test_setup_teardown(torture_pki_ecdsa_duplicate_then_demote, - setup_ecdsa_key_521, - teardown), -#endif - /* public key */ - unit_test_setup_teardown(torture_pki_publickey_dsa_base64, - setup_dsa_key, - teardown), - unit_test_setup_teardown(torture_pki_publickey_rsa_base64, - setup_rsa_key, - teardown), -#ifdef HAVE_ECC - unit_test_setup_teardown(torture_pki_publickey_ecdsa_base64, - setup_ecdsa_key_256, - teardown), - unit_test_setup_teardown(torture_pki_publickey_ecdsa_base64, - setup_ecdsa_key_384, - teardown), - unit_test_setup_teardown(torture_pki_publickey_ecdsa_base64, - setup_ecdsa_key_521, - teardown), -#endif - - unit_test_setup_teardown(torture_generate_pubkey_from_privkey_dsa, - setup_dsa_key, - teardown), - unit_test_setup_teardown(torture_generate_pubkey_from_privkey_rsa, - setup_rsa_key, - teardown), -#ifdef HAVE_ECC - unit_test_setup_teardown(torture_generate_pubkey_from_privkey_ecdsa, - setup_ecdsa_key_256, - teardown), - unit_test_setup_teardown(torture_generate_pubkey_from_privkey_ecdsa, - setup_ecdsa_key_384, - teardown), - unit_test_setup_teardown(torture_generate_pubkey_from_privkey_ecdsa, - setup_ecdsa_key_521, - teardown), -#endif - - unit_test_setup_teardown(torture_pki_duplicate_key_rsa, - setup_rsa_key, - teardown), - unit_test_setup_teardown(torture_pki_duplicate_key_dsa, - setup_dsa_key, - teardown), -#ifdef HAVE_ECC - unit_test_setup_teardown(torture_pki_duplicate_key_ecdsa, - setup_ecdsa_key_256, - teardown), - unit_test_setup_teardown(torture_pki_duplicate_key_ecdsa, - setup_ecdsa_key_384, - teardown), - unit_test_setup_teardown(torture_pki_duplicate_key_ecdsa, - setup_ecdsa_key_521, - teardown), -#endif - unit_test(torture_pki_generate_key_rsa), - unit_test(torture_pki_generate_key_rsa1), - unit_test(torture_pki_generate_key_dsa), -#ifdef HAVE_ECC - unit_test(torture_pki_generate_key_ecdsa), -#endif -#ifdef HAVE_LIBCRYPTO - unit_test_setup_teardown(torture_pki_write_privkey_rsa, - setup_rsa_key, - teardown), - unit_test_setup_teardown(torture_pki_write_privkey_dsa, - setup_dsa_key, - teardown), -#ifdef HAVE_ECC - unit_test_setup_teardown(torture_pki_write_privkey_ecdsa, - setup_ecdsa_key_256, - teardown), - unit_test_setup_teardown(torture_pki_write_privkey_ecdsa, - setup_ecdsa_key_384, - teardown), - unit_test_setup_teardown(torture_pki_write_privkey_ecdsa, - setup_ecdsa_key_521, - teardown), -#endif -#endif /* HAVE_LIBCRYPTO */ -#ifdef HAVE_ECC - unit_test_setup_teardown(torture_pki_ecdsa_name256, - setup_ecdsa_key_256, - teardown), - unit_test_setup_teardown(torture_pki_ecdsa_name384, - setup_ecdsa_key_384, - teardown), - unit_test_setup_teardown(torture_pki_ecdsa_name521, - setup_ecdsa_key_521, - teardown), -#endif - }; - - (void)setup_both_keys; - - ssh_init(); - rc=run_tests(tests); - ssh_finalize(); - return rc; -} diff --git a/libssh/tests/unittests/torture_rand.c b/libssh/tests/unittests/torture_rand.c deleted file mode 100644 index 829989b0..00000000 --- a/libssh/tests/unittests/torture_rand.c +++ /dev/null @@ -1,68 +0,0 @@ -#define LIBSSH_STATIC -#include -#include -#include -#include -#include "torture.h" - -#ifdef HAVE_LIBGCRYPT -#define NUM_LOOPS 1000 -#else -/* openssl is much faster */ -#define NUM_LOOPS 20000 -#endif -#define NUM_THREADS 100 - -static void setup(void **state) { - (void) state; - - ssh_threads_set_callbacks(ssh_threads_get_pthread()); - ssh_init(); -} - -static void teardown(void **state) { - (void) state; - - ssh_finalize(); -} - -static void *torture_rand_thread(void *threadid) { - char buffer[12]; - int i; - int r; - - (void) threadid; - - buffer[0] = buffer[1] = buffer[10] = buffer[11] = 'X'; - for(i = 0; i < NUM_LOOPS; ++i) { - r = ssh_get_random(&buffer[2], i % 8 + 1, 0); - assert_true(r == 1); - } - - pthread_exit(NULL); -} - -static void torture_rand_threading(void **state) { - pthread_t threads[NUM_THREADS]; - int i; - int err; - - (void) state; - - for(i = 0; i < NUM_THREADS; ++i) { - err = pthread_create(&threads[i], NULL, torture_rand_thread, NULL); - assert_int_equal(err, 0); - } - for(i = 0; i < NUM_THREADS; ++i) { - err=pthread_join(threads[i], NULL); - assert_int_equal(err, 0); - } -} - -int torture_run_tests(void) { - const UnitTest tests[] = { - unit_test_setup_teardown(torture_rand_threading, setup, teardown), - }; - - return run_tests(tests); -} diff --git a/libssh/tests/valgrind.supp b/libssh/tests/valgrind.supp deleted file mode 100644 index a4276c3b..00000000 --- a/libssh/tests/valgrind.supp +++ /dev/null @@ -1,117 +0,0 @@ -### GLIBC -{ - glibc_regcomp - Memcheck:Leak - fun:*alloc - ... - fun:regcomp -} -{ - glibc_getaddrinfo_leak - Memcheck:Leak - fun:malloc - fun:make_request - fun:__check_pf - fun:getaddrinfo - fun:getai - fun:ssh_connect_host_nonblocking -} - -{ - glibc_dlopen_getdelim_selinux - Memcheck:Leak - fun:malloc - fun:getdelim - obj:/lib64/libselinux.so.1 - fun:call_init - fun:_dl_init - obj:/lib64/ld-2.15.so -} - -### OPENSSL -{ - openssl_crypto_value8 - Memcheck:Value8 - fun:* - obj:/lib*/libcrypto.so* -} - -{ - openssl_crypto_value4 - Memcheck:Value4 - fun:* - obj:/lib*/libcrypto.so* -} - -{ - openssl_crypto_cond - Memcheck:Cond - fun:* - obj:/lib*/libcrypto.so* -} - -{ - openssl_BN_cond - Memcheck:Cond - fun:BN_* -} - -{ - openssl_bn_value8 - Memcheck:Value8 - fun:bn_* -} - -{ - openssl_bn_value4 - Memcheck:Value4 - fun:bn_* -} - -{ - openssl_AES_cond - Memcheck:Cond - fun:AES_* -} - -{ - openssl_DES_cond - Memcheck:Cond - fun:DES_* -} - -{ - openssl_DES_value8 - Memcheck:Value8 - fun:DES_* -} - -{ - openssl_DES_value4 - Memcheck:Value4 - fun:DES_* -} - -{ - openssl_BF_cond - Memcheck:Cond - fun:BF_* -} - -{ - openssl_SHA1_cond - Memcheck:Cond - fun:SHA1_* -} - -{ - openssl_CRYPTO_leak - Memcheck:Leak - fun:*alloc - fun:CRYPTO_* -} -{ - openssl_CRYPTO_leak - Memcheck:Cond - fun:OPENSSL_cleanse -} diff --git a/msgpack/.gitignore b/msgpack/.gitignore deleted file mode 100644 index ca7355a4..00000000 --- a/msgpack/.gitignore +++ /dev/null @@ -1,58 +0,0 @@ -# Files generated by the bootstrap script. -/INSTALL -/NEWS -/README -/ac/ -/aclocal.m4 -/autom4te.cache/ -/config.h.in -/configure -/msgpack_vc2008.sln -/msgpack_vc2008.vcproj -/src/msgpack/pack_define.h -/src/msgpack/pack_template.h -/src/msgpack/sysdep.h -/src/msgpack/type/define.hpp -/src/msgpack/type/tuple.hpp -/src/msgpack/unpack_define.h -/src/msgpack/unpack_template.h -/src/msgpack/zone.hpp -/test/cases.mpac -/test/cases_compact.mpac -Makefile.in - -# Files generated by the configure script. - -/config.h -/config.log -/config.status -/libtool -/msgpack.pc -/src/msgpack/version.h -/stamp-h1 -Makefile -.deps -.libs - -# Files generated by make. -*.o -*.so -*.lo -*.la - -# Files generated by make check. -# TODO: Replace these with something like /test/*_test -/test/buffer -/test/cases -/test/convert -/test/fixint -/test/fixint_c -/test/msgpack_test -/test/msgpackc_test -/test/object -/test/pack_unpack -/test/pack_unpack_c -/test/streaming -/test/streaming_c -/test/version -/test/zone diff --git a/msgpack/AUTHORS b/msgpack/AUTHORS deleted file mode 100644 index ababacb0..00000000 --- a/msgpack/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -FURUHASHI Sadayuki diff --git a/msgpack/COPYING b/msgpack/COPYING deleted file mode 100644 index 4388e8fa..00000000 --- a/msgpack/COPYING +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) 2008-2010 FURUHASHI Sadayuki - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/msgpack/ChangeLog b/msgpack/ChangeLog deleted file mode 100644 index a983051a..00000000 --- a/msgpack/ChangeLog +++ /dev/null @@ -1,51 +0,0 @@ - -2011-08-08 version 0.5.7: - - * fixes compile error problem with llvm-gcc and Mac OS X Lion - -2011-04-24 version 0.5.6: - - * #42 fixes double-free problem on msgpack_unpacker_release_zone - -2011-02-24 version 0.5.5: - - * eliminates dependency of winsock2.h header - * fixes msgpack_vc.postbuild.bat file - * fixes some implicit cast warnings - -2010-08-29 version 0.5.4: - - * includes msgpack_vc2008.vcproj file in source package - * fixes type::fix_int types - -2010-08-27 version 0.5.3: - - * adds type::fix_{u,}int{8,16,32,64} types - * adds msgpack_pack_fix_{u,}int{8,16,32,64} functions - * adds packer::pack_fix_{u,}int{8,16,32,64} functions - * fixes include paths - -2010-07-14 version 0.5.2: - - * type::raw::str(), operator==, operator!=, operator< and operator> are now const - * generates version.h using AC_OUTPUT macro in ./configure - -2010-07-06 version 0.5.1: - - * Add msgpack_vrefbuffer_new and msgpack_vrefbuffer_free - * Add msgpack_sbuffer_new and msgpack_sbuffer_free - * Add msgpack_unpacker_next and msgpack_unpack_next - * msgpack::unpack returns void - * Add MSGPACK_VERSION{,_MAJOR,_MINOR} macros to check header version - * Add msgpack_version{,_major,_minor} functions to check library version - * ./configure supports --disable-cxx option not to build C++ API - -2010-04-29 version 0.5.0: - - * msgpack_object_type is changed. MSGPACK_OBJECT_NIL is now 0x00. - * New safe streaming deserializer API. - * Add object::object(const T&) and object::operator=(const T&) - * Add operator==(object, const T&) - * MSGPACK_DEFINE macro defines msgpack_object(object* obj, zone* z) - * C++ programs doesn't need to link "msgpackc" library. - diff --git a/msgpack/Doxyfile b/msgpack/Doxyfile deleted file mode 100644 index ca772301..00000000 --- a/msgpack/Doxyfile +++ /dev/null @@ -1,1552 +0,0 @@ -# Doxyfile 1.6.2 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = "MessagePack" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = doc - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it parses. -# With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this tag. -# The format is ext=language, where ext is a file extension, and language is one of -# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, -# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), -# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will rougly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = NO - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by -# doxygen. The layout file controls the global structure of the generated output files -# in an output format independent way. The create the layout file that represents -# doxygen's defaults, run doxygen with the -l option. You can optionally specify a -# file name after the option, if omitted DoxygenLayout.xml will be used as the name -# of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 - -FILE_PATTERNS = *.hpp *.h - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -#RECURSIVE = NO -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = NO - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER -# are set, an additional index file will be generated that can be used as input for -# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated -# HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. -# For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's -# filter section matches. -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = YES - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/msgpack/LICENSE b/msgpack/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/msgpack/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/msgpack/Makefile.am b/msgpack/Makefile.am deleted file mode 100644 index bcc24a42..00000000 --- a/msgpack/Makefile.am +++ /dev/null @@ -1,23 +0,0 @@ -SUBDIRS = src test - -DOC_FILES = \ - README.md \ - LICENSE \ - NOTICE \ - msgpack_vc8.vcproj \ - msgpack_vc8.sln \ - msgpack_vc2008.vcproj \ - msgpack_vc2008.sln \ - msgpack_vc.postbuild.bat - -EXTRA_DIST = \ - $(DOC_FILES) - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = msgpack.pc - -doxygen: - ./preprocess clean - cd src && $(MAKE) doxygen - ./preprocess - diff --git a/msgpack/NOTICE b/msgpack/NOTICE deleted file mode 100644 index e706f2ab..00000000 --- a/msgpack/NOTICE +++ /dev/null @@ -1,4 +0,0 @@ -MessagePack is developed by FURUHASHI Sadayuki, licensed under Apache License, -Version 2.0. The original software and related information is available at -http://msgpack.sourceforge.jp/. - diff --git a/msgpack/README.md b/msgpack/README.md deleted file mode 100644 index eac77935..00000000 --- a/msgpack/README.md +++ /dev/null @@ -1,73 +0,0 @@ -MessagePack for C/C++ -===================== -Binary-based efficient object serialization library. - - -## Installation - -Download latest package from [releases of MessagePack](http://sourceforge.net/projects/msgpack/files/) and extract it. - -On UNIX-like platform, run ./configure && make && sudo make install: - - $ ./configure - $ make - $ sudo make install - -On Windows, open msgpack_vc8.vcproj or msgpack_vc2008 file and build it using batch build. DLLs are built on lib folder, -and the headers are built on include folder. - -To use the library in your program, include msgpack.hpp header and link "msgpack" library. - - -## Example - - #include - #include - - int main(void) { - // This is target object. - std::vector target; - target.push_back("Hello,"); - target.push_back("World!"); - - // Serialize it. - msgpack::sbuffer buffer; // simple buffer - msgpack::pack(&buffer, target); - - // Deserialize the serialized data. - msgpack::unpacked msg; // includes memory pool and deserialized object - msgpack::unpack(&msg, sbuf.data(), sbuf.size()); - msgpack::object obj = msg.get(); - - // Print the deserialized object to stdout. - std::cout << obj << std::endl; // ["Hello," "World!"] - - // Convert the deserialized object to staticaly typed object. - std::vector result; - obj.convert(&result); - - // If the type is mismatched, it throws msgpack::type_error. - obj.as(); // type is mismatched, msgpack::type_error is thrown - } - -API documents and other example codes are available at the [wiki.](http://redmine.msgpack.org/projects/msgpack/wiki) - - -## License - - Copyright (C) 2008-2010 FURUHASHI Sadayuki - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -See also NOTICE file. - diff --git a/msgpack/README_crosslang.md b/msgpack/README_crosslang.md deleted file mode 100644 index 80270022..00000000 --- a/msgpack/README_crosslang.md +++ /dev/null @@ -1,38 +0,0 @@ -MessagePack cross-language test cases -===================================== - -## cases - -Valid serialized data are stored in "cases.mpac" and "cases_compact.mpac". -These files describe same objects. And "cases.json" describes an array of the described objects. - -Thus you can verify your implementations as comparing the objects. - - -## crosslang - -The *crosslang* tool reads serialized data from stdin and writes re-serialize data to stdout. - -There are C++ and Ruby implementation of crosslang tool. You can verify your implementation -as comparing that implementations. - -### C++ version - - $ cd ../cpp && ./configure && make && make install - or - $ port install msgpack # MacPorts - - $ g++ -Wall -lmsgpack crosslang.cc -o crosslang - - $ ./crosslang - Usage: ./crosslang [in-file] [out-file] - -### Ruby version - - $ gem install msgpack - or - $ port install rb_msgpack # MacPorts - - $ ruby crosslang.rb - Usage: crosslang.rb [in-file] [out-file] - diff --git a/msgpack/bootstrap b/msgpack/bootstrap deleted file mode 100755 index 1ff6b76e..00000000 --- a/msgpack/bootstrap +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/sh -# vim:ts=4:sw=4 -# Calls autotools to build configure script and Makefile.in. -# Generated automatically using bootstrapper 0.2.1 -# http://bootstrapper.sourceforge.net/ -# -# Copyright (C) 2002 Anthony Ventimiglia -# -# This bootstrap script is free software; you can redistribute -# it and/or modify it under the terms of the GNU General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# -# Calls proper programs to create configure script and Makefile.in files. -# if run with the --clean option, bootstrap removes files it generates. To -# clean all autogenerated files (eg: for cvs imports) first run -# make distclean, then bootstrap --clean -# see bootstrapper(1) for more infor - - -if test x"$1" = x"--help"; then - echo "$0: automatic bootstrapping utility for GNU Autotools" - echo " cleans up old autogenerated files and runs autoconf," - echo " automake and aclocal on local directory" - echo - echo " --clean clean up auto-generated files without" - echo " creating new scripts" - echo - exit 0 -fi - - -mkdir -p ac -test -f AUTHORS || touch AUTHORS -test -f COPYING || touch COPYING -test -f ChangeLog || touch ChangeLog -test -f NEWS || touch NEWS -test -f README || cp -f README.md README - -./preprocess -if [ $? -ne 0 ]; then - exit 1 -fi - - - -ACLOCAL="aclocal" -ACLOCAL_FILES="aclocal.m4" -ALWAYS_CLEAN="config.status config.log config.cache libtool" -AUTOCONF="autoconf" -AUTOCONF_FILES="configure" -AUTOHEADER="autoheader" -AUTOHEADER_FILES="" -AUTOMAKE="automake --add-missing --copy" -AUTOMAKE_FILES="config.sub stamp-h.in ltmain.sh missing mkinstalldirs install-sh config.guess" -CONFIG_AUX_DIR="." -CONFIG_FILES="stamp-h ltconfig" -CONFIG_HEADER="" -if [ x`uname` = x"Darwin" ]; then - LIBTOOLIZE="glibtoolize --force --copy" -else - LIBTOOLIZE="libtoolize --force --copy" -fi -LIBTOOLIZE_FILES="config.sub ltmain.sh config.guess" -RM="rm" -SUBDIRS="[]" - - -# These are files created by configure, so we'll always clean them -for i in $ALWAYS_CLEAN; do - test -f $i && \ - $RM $i -done - -if test x"$1" = x"--clean"; then - # - #Clean Files left by previous bootstrap run - # - if test -n "$CONFIG_AUX_DIR"; - then CONFIG_AUX_DIR="$CONFIG_AUX_DIR/" - fi - # Clean Libtoolize generated files - for cf in $LIBTOOLIZE_FILES; do - cf="$CONFIG_AUX_DIR$cf" - test -f $cf && \ - $RM $cf - done - #aclocal.m4 created by aclocal - test -f $ACLOCAL_FILES && $RM $ACLOCAL_FILES - #Clean Autoheader Generated files - for cf in $AUTOHEADER_FILES; do - cf=$CONFIG_AUX_DIR$cf - test -f $cf && \ - $RM $cf - done - # remove config header (Usaually config.h) - test -n "$CONFIG_HEADER" && test -f $CONFIG_HEADER && $RM $CONFIG_HEADER - #Clean Automake generated files - for cf in $AUTOMAKE_FILES; do - cf=$CONFIG_AUX_DIR$cf - test -f $cf && \ - $RM $cf - done - for i in $SUBDIRS; do - test -f $i/Makefile.in && \ - $RM $i/Makefile.in - done - #Autoconf generated files - for cf in $AUTOCONF_FILES; do - test -f $cf && \ - $RM $cf - done - for cf in $CONFIG_FILES; do - cf="$CONFIG_AUX_DIR$cf" - test -f $cf && \ - $RM $cf - done -else - $LIBTOOLIZE - $ACLOCAL - $AUTOHEADER - $AUTOMAKE - $AUTOCONF -fi - - diff --git a/msgpack/cases.json b/msgpack/cases.json deleted file mode 100644 index fd390d48..00000000 --- a/msgpack/cases.json +++ /dev/null @@ -1 +0,0 @@ -[false,true,null,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,127,127,255,65535,4294967295,-32,-32,-128,-32768,-2147483648,0.0,-0.0,1.0,-1.0,"a","a","a","","","",[0],[0],[0],[],[],[],{},{},{},{"a":97},{"a":97},{"a":97},[[]],[["a"]]] \ No newline at end of file diff --git a/msgpack/cases.mpac b/msgpack/cases.mpac deleted file mode 100644 index 5ec08c6a9af42d9568eb429a209a639616e94711..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213 zcmXYp!4<+V3`3R4n8lOSY|v~#CV>aXw2-v7Qc6c)17ihrkbe}**V_dHM&J(DgGLop zU?R;l%8FI9$y_sy>V|HFdDW~{neAn-roN|Wd+OcH160;F91fo!97} FixMap -de 00 01 a1 61 61 # {"a"=>97} map 16 -df 00 00 00 01 a1 61 61 # {"a"=>97} map 32 -91 90 # [[]] -91 91 a1 61 # [["a"]] -EOF - -source.gsub!(/\#.+$/,'') -bytes = source.strip.split(/\s+/).map {|x| x.to_i(16) }.pack('C*') - -objs = [] -compact_bytes = "" - -pac = MessagePack::Unpacker.new -pac.feed(bytes) -pac.each {|obj| - p obj - objs << obj - compact_bytes << obj.to_msgpack -} - -json = objs.to_json - -# self check -cpac = MessagePack::Unpacker.new -cpac.feed(compact_bytes) -cpac.each {|cobj| - obj = objs.shift - if obj != cobj - puts "** SELF CHECK FAILED **" - puts "expected: #{obj.inspect}" - puts "actual: #{cobj.inspect}" - exit 1 - end -} - -File.open("cases.mpac","w") {|f| f.write(bytes) } -File.open("cases_compact.mpac","w") {|f| f.write(compact_bytes) } -File.open("cases.json","w") {|f| f.write(json) } - diff --git a/msgpack/configure.in b/msgpack/configure.in deleted file mode 100644 index e7540187..00000000 --- a/msgpack/configure.in +++ /dev/null @@ -1,100 +0,0 @@ -AC_INIT(src/object.cpp) -AC_CONFIG_AUX_DIR(ac) -AM_INIT_AUTOMAKE(msgpack, 0.5.7) -AC_CONFIG_HEADER(config.h) - -AC_SUBST(CFLAGS) -CFLAGS="-O3 -Wall $CFLAGS" - -AC_SUBST(CXXFLAGS) -CXXFLAGS="-O3 -Wall $CXXFLAGS" - - -AC_PROG_CC - - -AC_MSG_CHECKING([if C++ API is enabled]) -AC_ARG_ENABLE(cxx, - AS_HELP_STRING([--disable-cxx], - [don't build C++ API]) ) #' -AC_MSG_RESULT([$enable_cxx]) -if test "$enable_cxx" != "no"; then - AC_PROG_CXX - AM_PROG_CC_C_O -fi -AM_CONDITIONAL(ENABLE_CXX, test "$enable_cxx" != "no") - - -AC_PROG_LIBTOOL -AM_PROG_AS - - -AC_MSG_CHECKING([if debug option is enabled]) -AC_ARG_ENABLE(debug, - AS_HELP_STRING([--disable-debug], - [disable assert macros and omit -g option]) ) -AC_MSG_RESULT([$enable_debug]) -if test "$enable_debug" != "no"; then - CXXFLAGS="$CXXFLAGS -g" - CFLAGS="$CFLAGS -g" -else - CXXFLAGS="$CXXFLAGS -DNDEBUG" - CFLAGS="$CFLAGS -DNDEBUG" -fi - - -AC_CACHE_CHECK([for __sync_* atomic operations], msgpack_cv_atomic_ops, [ - AC_TRY_LINK([ - int atomic_sub(int i) { return __sync_sub_and_fetch(&i, 1); } - int atomic_add(int i) { return __sync_add_and_fetch(&i, 1); } - ], [atomic_sub(1); atomic_add(1);], msgpack_cv_atomic_ops="yes") - ]) -if test "$msgpack_cv_atomic_ops" != "yes"; then - if test "$enable_cxx" = "no"; then - AC_MSG_ERROR([__sync_* atomic operations are not found. Try to enable C++ support. -If you are using gcc >= 4.1 and the default target CPU architecture is "i386", try to -add CFLAGS="-march=i686" and CXXFLAGS="-march=i686" options to ./configure as follows: - - $ ./configure CFLAGS="-march=i686" CXXFLAGS="-march=i686" - ]) - fi - - AC_LANG_PUSH([C++]) - AC_CACHE_CHECK([for __gnu_cxx::__exchange_and_add], msgpack_cv_gcc_cxx_atomic_ops, [ - AC_TRY_LINK([ - #include - int atomic_sub(int i) { return __gnu_cxx::__exchange_and_add(&i, -1) - 1; } - int atomic_add(int i) { return __gnu_cxx::__exchange_and_add(&i, 1) + 1; } - ], [atomic_sub(1); atomic_add(1);], msgpack_cv_gcc_cxx_atomic_ops="yes") - ]) - AC_LANG_POP([C++]) - - if test "$msgpack_cv_gcc_cxx_atomic_ops" != "yes"; then - AC_MSG_ERROR([__sync_* atomic operations nor __gnu_cxx::__exchange_and_add are not found. - -If you are using gcc >= 4.1 and the default target CPU architecture is "i386", try to -add CFLAGS="-march=i686" and CXXFLAGS="-march=i686" options to ./configure as follows: - - $ ./configure CFLAGS="-march=i686" CXXFLAGS="-march=i686" -]) - - else - enable_gcc_cxx_atomic=yes - fi -fi - -AM_CONDITIONAL(ENABLE_GCC_CXX_ATOMIC, test "$enable_gcc_cxx_atomic" = "yes") - - -major=`echo $VERSION | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` -minor=`echo $VERSION | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` -AC_SUBST(VERSION_MAJOR, $major) -AC_SUBST(VERSION_MINOR, $minor) - - -AC_OUTPUT([Makefile - msgpack.pc - src/Makefile - src/msgpack/version.h - test/Makefile]) - diff --git a/msgpack/crosslang.cc b/msgpack/crosslang.cc deleted file mode 100644 index be521b36..00000000 --- a/msgpack/crosslang.cc +++ /dev/null @@ -1,133 +0,0 @@ -// -// MessagePack cross-language test tool -// -// $ cd ../cpp && ./configure && make && make install -// or -// $ port install msgpack # MacPorts -// -// $ g++ -Wall -lmsgpack crosslang.cc -// -#include -#include -#include -#include -#include -#include -#include - -static int run(int infd, int outfd) -try { - msgpack::unpacker pac; - - while(true) { - pac.reserve_buffer(32*1024); - - ssize_t count = - read(infd, pac.buffer(), pac.buffer_capacity()); - - if(count <= 0) { - if(count == 0) { - return 0; - } - if(errno == EAGAIN || errno == EINTR) { - continue; - } - return 1; - } - - pac.buffer_consumed(count); - - msgpack::unpacked result; - while(pac.next(&result)) { - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, result.get()); - - const char* p = sbuf.data(); - const char* const pend = p + sbuf.size(); - while(p < pend) { - ssize_t bytes = write(outfd, p, pend-p); - - if(bytes <= 0) { - if(count == 0) { - return 0; - } - if(errno == EAGAIN || errno == EINTR) { - continue; - } - return 1; - } - - p += bytes; - } - - } - } - - return 0; - -} catch (std::exception& e) { - std::cerr << e.what() << std::endl; - return 1; -} - -static void usage(const char* prog) -{ - printf( - "Usage: %s [in-file] [out-file]\n" - "\n" - "This tool is for testing of MessagePack implementation.\n" - "This does following behavior:\n" - "\n" - " 1. Reads objects serialized by MessagePack from (default: stdin)\n" - " 2. Re-serializes the objects using C++ implementation of MessagePack (Note that C++ implementation is considered valid)\n" - " 3. Writes the re-serialized objects into (default: stdout)\n" - "\n" - , prog); - exit(1); -} - -int main(int argc, char* argv[]) -{ - int infd = 0; - int outfd = 1; - - if(argc < 1 || argc > 3) { - usage(argv[0]); - } - - for(int i=1; i < argc; ++i) { - if(strlen(argv[i]) > 1 && argv[i][0] == '-') { - usage(argv[0]); - } - } - - if(argc >= 2) { - const char* fname = argv[1]; - if(strcmp(fname, "-") != 0) { - infd = open(fname, O_RDONLY); - if(infd < 0) { - perror("can't open input file"); - exit(1); - } - } - } - - if(argc >= 3) { - const char* fname = argv[2]; - if(strcmp(fname, "-") != 0) { - outfd = open(fname, O_WRONLY | O_CREAT| O_TRUNC, 0666); - if(outfd < 0) { - perror("can't open output file"); - exit(1); - } - } - } - - int code = run(infd, outfd); - - close(infd); - close(outfd); - - return code; -} - diff --git a/msgpack/crosslang.rb b/msgpack/crosslang.rb deleted file mode 100644 index 7aa44820..00000000 --- a/msgpack/crosslang.rb +++ /dev/null @@ -1,88 +0,0 @@ -# -# MessagePack cross-language test tool -# -# $ gem install msgpack -# or -# $ port install rb_msgpack # MacPorts -# -begin -require 'rubygems' -rescue LoadError -end -require 'msgpack' - -def run(inio, outio) - pac = MessagePack::Unpacker.new(inio) - - begin - pac.each {|obj| - outio.write MessagePack.pack(obj) - outio.flush - } - rescue EOFError - return 0 - rescue - $stderr.puts $! - return 1 - end - - return 0 -end - -def usage - puts < (default: stdin) - 2. Re-serializes the objects using Ruby implementation of MessagePack (Note that Ruby implementation is considered valid) - 3. Writes the re-serialized objects into (default: stdout) - -EOF - exit 1 -end - -inio = $stdin -outio = $stdout - -if ARGV.length > 2 - usage -end - -ARGV.each {|str| - if str.size > 1 && str[0] == ?- - usage - end -} - -if fname = ARGV[0] - unless fname == "-" - begin - inio = File.open(fname) - rescue - puts "can't open output file: #{$!}" - exit 1 - end - end -end - -if fname = ARGV[1] - unless fname == "-" - begin - outio = File.open(fname, "w") - rescue - puts "can't open output file: #{$!}" - exit 1 - end - end -end - -code = run(inio, outio) - -inio.close -outio.close - -exit code - diff --git a/msgpack/example/custom.cc b/msgpack/example/custom.cc deleted file mode 100644 index 835ebed9..00000000 --- a/msgpack/example/custom.cc +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include - -class old_class { -public: - old_class() : value("default") { } - - std::string value; - - MSGPACK_DEFINE(value); -}; - -class new_class { -public: - new_class() : value("default"), flag(-1) { } - - std::string value; - int flag; - - MSGPACK_DEFINE(value, flag); -}; - -int main(void) -{ - { - old_class oc; - new_class nc; - - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, oc); - - msgpack::zone zone; - msgpack::object obj; - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &zone, &obj); - - obj.convert(&nc); - - std::cout << obj << " value=" << nc.value << " flag=" << nc.flag << std::endl; - } - - { - new_class nc; - old_class oc; - - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, nc); - - msgpack::zone zone; - msgpack::object obj; - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &zone, &obj); - - obj.convert(&oc); - - std::cout << obj << " value=" << oc.value << std::endl; - } -} - diff --git a/msgpack/example/protocol.cc b/msgpack/example/protocol.cc deleted file mode 100644 index e7f2a387..00000000 --- a/msgpack/example/protocol.cc +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include -#include -#include - -namespace myprotocol { - using namespace msgpack::type; - using msgpack::define; - - struct Get : define< tuple > { - Get() { } - Get(uint32_t f, const std::string& k) : - define_type(msgpack_type(f, k)) { } - uint32_t& flags() { return get<0>(); } - std::string& key() { return get<1>(); } - }; - - struct Put : define< tuple > { - Put() { } - Put(uint32_t f, const std::string& k, const char* valref, size_t vallen) : - define_type(msgpack_type( f, k, raw_ref(valref,vallen) )) { } - uint32_t& flags() { return get<0>(); } - std::string& key() { return get<1>(); } - raw_ref& value() { return get<2>(); } - }; - - struct MultiGet : define< std::vector > { - }; -} - - -int main(void) -{ - // send Get request - std::stringstream stream; - { - myprotocol::Get req; - req.flags() = 0; - req.key() = "key0"; - msgpack::pack(stream, req); - } - - stream.seekg(0); - - // receive Get request - { - std::string buffer(stream.str()); - - msgpack::zone mempool; - msgpack::object o = - msgpack::unpack(buffer.data(), buffer.size(), mempool); - - myprotocol::Get req; - msgpack::convert(req, o); - std::cout << "received: " << o << std::endl; - } - - - stream.str(""); - - - // send MultiGet request - { - myprotocol::MultiGet req; - req.push_back( myprotocol::Get(1, "key1") ); - req.push_back( myprotocol::Get(2, "key2") ); - req.push_back( myprotocol::Get(3, "key3") ); - msgpack::pack(stream, req); - } - - stream.seekg(0); - - // receive MultiGet request - { - std::string buffer(stream.str()); - - msgpack::zone mempool; - msgpack::object o = - msgpack::unpack(buffer.data(), buffer.size(), mempool); - - myprotocol::MultiGet req; - msgpack::convert(req, o); - std::cout << "received: " << o << std::endl; - } -} - diff --git a/msgpack/example/simple.c b/msgpack/example/simple.c deleted file mode 100644 index 41d8bb70..00000000 --- a/msgpack/example/simple.c +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include - -int main(void) -{ - /* msgpack::sbuffer is a simple buffer implementation. */ - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - - /* serialize values into the buffer using msgpack_sbuffer_write callback function. */ - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - msgpack_pack_array(&pk, 3); - msgpack_pack_int(&pk, 1); - msgpack_pack_true(&pk); - msgpack_pack_raw(&pk, 7); - msgpack_pack_raw_body(&pk, "example", 7); - - /* deserialize the buffer into msgpack_object instance. */ - /* deserialized object is valid during the msgpack_zone instance alive. */ - msgpack_zone mempool; - msgpack_zone_init(&mempool, 2048); - - msgpack_object deserialized; - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - /* print the deserialized object. */ - msgpack_object_print(stdout, deserialized); - puts(""); - - msgpack_zone_destroy(&mempool); - msgpack_sbuffer_destroy(&sbuf); - - return 0; -} - diff --git a/msgpack/example/simple.cc b/msgpack/example/simple.cc deleted file mode 100644 index 55ecdf92..00000000 --- a/msgpack/example/simple.cc +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include -#include -#include - -int main(void) -{ - msgpack::type::tuple src(1, true, "example"); - - // serialize the object into the buffer. - // any classes that implements write(const char*,size_t) can be a buffer. - std::stringstream buffer; - msgpack::pack(buffer, src); - - // send the buffer ... - buffer.seekg(0); - - // deserialize the buffer into msgpack::object instance. - std::string str(buffer.str()); - - // deserialized object is valid during the msgpack::zone instance alive. - msgpack::zone mempool; - - msgpack::object deserialized; - msgpack::unpack(str.data(), str.size(), NULL, &mempool, &deserialized); - - // msgpack::object supports ostream. - std::cout << deserialized << std::endl; - - // convert msgpack::object instance into the original type. - // if the type is mismatched, it throws msgpack::type_error exception. - msgpack::type::tuple dst; - deserialized.convert(&dst); - - return 0; -} - diff --git a/msgpack/example/stream.cc b/msgpack/example/stream.cc deleted file mode 100644 index 2241935c..00000000 --- a/msgpack/example/stream.cc +++ /dev/null @@ -1,136 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -class Server { -public: - Server(int sock) : m_sock(sock) { } - - ~Server() { } - - typedef std::auto_ptr auto_zone; - - void socket_readable() - { - m_pac.reserve_buffer(1024); - - ssize_t count = - read(m_sock, m_pac.buffer(), m_pac.buffer_capacity()); - - if(count <= 0) { - if(count == 0) { - throw std::runtime_error("connection closed"); - } - if(errno == EAGAIN || errno == EINTR) { - return; - } - throw std::runtime_error(strerror(errno)); - } - - m_pac.buffer_consumed(count); - - while(m_pac.execute()) { - msgpack::object msg = m_pac.data(); - - auto_zone life( m_pac.release_zone() ); - - m_pac.reset(); - - process_message(msg, life); - } - - if(m_pac.message_size() > 10*1024*1024) { - throw std::runtime_error("message is too large"); - } - } - -private: - void process_message(msgpack::object msg, auto_zone& life) - { - std::cout << "message reached: " << msg << std::endl; - } - -private: - int m_sock; - msgpack::unpacker m_pac; -}; - - -static void* run_server(void* arg) -try { - Server* srv = reinterpret_cast(arg); - - while(true) { - srv->socket_readable(); - } - return NULL; - -} catch (std::exception& e) { - std::cerr << "error while processing client packet: " - << e.what() << std::endl; - return NULL; - -} catch (...) { - std::cerr << "error while processing client packet: " - << "unknown error" << std::endl; - return NULL; -} - - -struct fwriter { - fwriter(int fd) : m_fp( fdopen(fd, "w") ) { } - - void write(const char* buf, size_t buflen) - { - size_t count = fwrite(buf, buflen, 1, m_fp); - if(count < 1) { - std::cout << buflen << std::endl; - std::cout << count << std::endl; - throw std::runtime_error(strerror(errno)); - } - } - - void flush() { fflush(m_fp); } - - void close() { fclose(m_fp); } - -private: - FILE* m_fp; -}; - - -int main(void) -{ - int pair[2]; - pipe(pair); - - // run server thread - Server srv(pair[0]); - pthread_t thread; - pthread_create(&thread, NULL, - run_server, reinterpret_cast(&srv)); - - // client thread: - fwriter writer(pair[1]); - msgpack::packer pk(writer); - - typedef msgpack::type::tuple put_t; - typedef msgpack::type::tuple get_t; - - put_t req1("put", "apple", "red"); - put_t req2("put", "lemon", "yellow"); - get_t req3("get", "apple"); - pk.pack(req1); - pk.pack(req2); - pk.pack(req3); - writer.flush(); - writer.close(); - - pthread_join(thread, NULL); -} - diff --git a/msgpack/msgpack.pc.in b/msgpack/msgpack.pc.in deleted file mode 100644 index 8c34bef9..00000000 --- a/msgpack/msgpack.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: MessagePack -Description: Binary-based efficient object serialization library -Version: @VERSION@ -Libs: -L${libdir} -lmsgpack -Cflags: -I${includedir} diff --git a/msgpack/msgpack_vc.postbuild.bat b/msgpack/msgpack_vc.postbuild.bat deleted file mode 100644 index c7791e6a..00000000 --- a/msgpack/msgpack_vc.postbuild.bat +++ /dev/null @@ -1,45 +0,0 @@ -IF NOT EXIST include MKDIR include -IF NOT EXIST include\msgpack MKDIR include\msgpack -IF NOT EXIST include\msgpack\type MKDIR include\msgpack\type -IF NOT EXIST include\msgpack\type\tr1 MKDIR include\msgpack\type\tr1 -copy src\msgpack\pack_define.h include\msgpack\ -copy src\msgpack\pack_template.h include\msgpack\ -copy src\msgpack\unpack_define.h include\msgpack\ -copy src\msgpack\unpack_template.h include\msgpack\ -copy src\msgpack\sysdep.h include\msgpack\ -copy src\msgpack.h include\ -copy src\msgpack\sbuffer.h include\msgpack\ -copy src\msgpack\version.h include\msgpack\ -copy src\msgpack\vrefbuffer.h include\msgpack\ -copy src\msgpack\zbuffer.h include\msgpack\ -copy src\msgpack\pack.h include\msgpack\ -copy src\msgpack\unpack.h include\msgpack\ -copy src\msgpack\object.h include\msgpack\ -copy src\msgpack\zone.h include\msgpack\ -copy src\msgpack.hpp include\ -copy src\msgpack\sbuffer.hpp include\msgpack\ -copy src\msgpack\vrefbuffer.hpp include\msgpack\ -copy src\msgpack\zbuffer.hpp include\msgpack\ -copy src\msgpack\pack.hpp include\msgpack\ -copy src\msgpack\unpack.hpp include\msgpack\ -copy src\msgpack\object.hpp include\msgpack\ -copy src\msgpack\zone.hpp include\msgpack\ -copy src\msgpack\type.hpp include\msgpack\ -copy src\msgpack\type\bool.hpp include\msgpack\type\ -copy src\msgpack\type\deque.hpp include\msgpack\type\ -copy src\msgpack\type\fixint.hpp include\msgpack\type\ -copy src\msgpack\type\float.hpp include\msgpack\type\ -copy src\msgpack\type\int.hpp include\msgpack\type\ -copy src\msgpack\type\list.hpp include\msgpack\type\ -copy src\msgpack\type\map.hpp include\msgpack\type\ -copy src\msgpack\type\nil.hpp include\msgpack\type\ -copy src\msgpack\type\pair.hpp include\msgpack\type\ -copy src\msgpack\type\raw.hpp include\msgpack\type\ -copy src\msgpack\type\set.hpp include\msgpack\type\ -copy src\msgpack\type\string.hpp include\msgpack\type\ -copy src\msgpack\type\vector.hpp include\msgpack\type\ -copy src\msgpack\type\tuple.hpp include\msgpack\type\ -copy src\msgpack\type\define.hpp include\msgpack\type\ -copy src\msgpack\type\tr1\unordered_map.hpp include\msgpack\type\tr1\ -copy src\msgpack\type\tr1\unordered_set.hpp include\msgpack\type\tr1\ - diff --git a/msgpack/msgpack_vc8.sln b/msgpack/msgpack_vc8.sln deleted file mode 100644 index 84718afa..00000000 --- a/msgpack/msgpack_vc8.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MessagePack", "msgpack_vc8.vcproj", "{122A2EA4-B283-4241-9655-786DE78283B2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {122A2EA4-B283-4241-9655-786DE78283B2}.Debug|Win32.ActiveCfg = Debug|Win32 - {122A2EA4-B283-4241-9655-786DE78283B2}.Debug|Win32.Build.0 = Debug|Win32 - {122A2EA4-B283-4241-9655-786DE78283B2}.Release|Win32.ActiveCfg = Release|Win32 - {122A2EA4-B283-4241-9655-786DE78283B2}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/msgpack/msgpack_vc8.vcproj b/msgpack/msgpack_vc8.vcproj deleted file mode 100644 index 72d47b6b..00000000 --- a/msgpack/msgpack_vc8.vcproj +++ /dev/null @@ -1,299 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/msgpack/pack_define.h b/msgpack/pack_define.h deleted file mode 100644 index 4845d52e..00000000 --- a/msgpack/pack_define.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * MessagePack unpacking routine template - * - * Copyright (C) 2008-2010 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_PACK_DEFINE_H__ -#define MSGPACK_PACK_DEFINE_H__ - -#include "msgpack/sysdep.h" -#include -#include - -#endif /* msgpack/pack_define.h */ - diff --git a/msgpack/pack_template.h b/msgpack/pack_template.h deleted file mode 100644 index 65c959dd..00000000 --- a/msgpack/pack_template.h +++ /dev/null @@ -1,771 +0,0 @@ -/* - * MessagePack packing routine template - * - * Copyright (C) 2008-2010 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if defined(__LITTLE_ENDIAN__) -#define TAKE8_8(d) ((uint8_t*)&d)[0] -#define TAKE8_16(d) ((uint8_t*)&d)[0] -#define TAKE8_32(d) ((uint8_t*)&d)[0] -#define TAKE8_64(d) ((uint8_t*)&d)[0] -#elif defined(__BIG_ENDIAN__) -#define TAKE8_8(d) ((uint8_t*)&d)[0] -#define TAKE8_16(d) ((uint8_t*)&d)[1] -#define TAKE8_32(d) ((uint8_t*)&d)[3] -#define TAKE8_64(d) ((uint8_t*)&d)[7] -#endif - -#ifndef msgpack_pack_inline_func -#error msgpack_pack_inline_func template is not defined -#endif - -#ifndef msgpack_pack_user -#error msgpack_pack_user type is not defined -#endif - -#ifndef msgpack_pack_append_buffer -#error msgpack_pack_append_buffer callback is not defined -#endif - - -/* - * Integer - */ - -#define msgpack_pack_real_uint8(x, d) \ -do { \ - if(d < (1<<7)) { \ - /* fixnum */ \ - msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \ - } else { \ - /* unsigned 8 */ \ - unsigned char buf[2] = {0xcc, TAKE8_8(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } \ -} while(0) - -#define msgpack_pack_real_uint16(x, d) \ -do { \ - if(d < (1<<7)) { \ - /* fixnum */ \ - msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \ - } else if(d < (1<<8)) { \ - /* unsigned 8 */ \ - unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } else { \ - /* unsigned 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } \ -} while(0) - -#define msgpack_pack_real_uint32(x, d) \ -do { \ - if(d < (1<<8)) { \ - if(d < (1<<7)) { \ - /* fixnum */ \ - msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \ - } else { \ - /* unsigned 8 */ \ - unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } \ - } else { \ - if(d < (1<<16)) { \ - /* unsigned 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } else { \ - /* unsigned 32 */ \ - unsigned char buf[5]; \ - buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ - msgpack_pack_append_buffer(x, buf, 5); \ - } \ - } \ -} while(0) - -#define msgpack_pack_real_uint64(x, d) \ -do { \ - if(d < (1ULL<<8)) { \ - if(d < (1ULL<<7)) { \ - /* fixnum */ \ - msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \ - } else { \ - /* unsigned 8 */ \ - unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } \ - } else { \ - if(d < (1ULL<<16)) { \ - /* unsigned 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } else if(d < (1ULL<<32)) { \ - /* unsigned 32 */ \ - unsigned char buf[5]; \ - buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ - msgpack_pack_append_buffer(x, buf, 5); \ - } else { \ - /* unsigned 64 */ \ - unsigned char buf[9]; \ - buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \ - msgpack_pack_append_buffer(x, buf, 9); \ - } \ - } \ -} while(0) - -#define msgpack_pack_real_int8(x, d) \ -do { \ - if(d < -(1<<5)) { \ - /* signed 8 */ \ - unsigned char buf[2] = {0xd0, TAKE8_8(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } else { \ - /* fixnum */ \ - msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); \ - } \ -} while(0) - -#define msgpack_pack_real_int16(x, d) \ -do { \ - if(d < -(1<<5)) { \ - if(d < -(1<<7)) { \ - /* signed 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } else { \ - /* signed 8 */ \ - unsigned char buf[2] = {0xd0, TAKE8_16(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } \ - } else if(d < (1<<7)) { \ - /* fixnum */ \ - msgpack_pack_append_buffer(x, &TAKE8_16(d), 1); \ - } else { \ - if(d < (1<<8)) { \ - /* unsigned 8 */ \ - unsigned char buf[2] = {0xcc, TAKE8_16(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } else { \ - /* unsigned 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } \ - } \ -} while(0) - -#define msgpack_pack_real_int32(x, d) \ -do { \ - if(d < -(1<<5)) { \ - if(d < -(1<<15)) { \ - /* signed 32 */ \ - unsigned char buf[5]; \ - buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \ - msgpack_pack_append_buffer(x, buf, 5); \ - } else if(d < -(1<<7)) { \ - /* signed 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } else { \ - /* signed 8 */ \ - unsigned char buf[2] = {0xd0, TAKE8_32(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } \ - } else if(d < (1<<7)) { \ - /* fixnum */ \ - msgpack_pack_append_buffer(x, &TAKE8_32(d), 1); \ - } else { \ - if(d < (1<<8)) { \ - /* unsigned 8 */ \ - unsigned char buf[2] = {0xcc, TAKE8_32(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } else if(d < (1<<16)) { \ - /* unsigned 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } else { \ - /* unsigned 32 */ \ - unsigned char buf[5]; \ - buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ - msgpack_pack_append_buffer(x, buf, 5); \ - } \ - } \ -} while(0) - -#define msgpack_pack_real_int64(x, d) \ -do { \ - if(d < -(1LL<<5)) { \ - if(d < -(1LL<<15)) { \ - if(d < -(1LL<<31)) { \ - /* signed 64 */ \ - unsigned char buf[9]; \ - buf[0] = 0xd3; _msgpack_store64(&buf[1], d); \ - msgpack_pack_append_buffer(x, buf, 9); \ - } else { \ - /* signed 32 */ \ - unsigned char buf[5]; \ - buf[0] = 0xd2; _msgpack_store32(&buf[1], (int32_t)d); \ - msgpack_pack_append_buffer(x, buf, 5); \ - } \ - } else { \ - if(d < -(1<<7)) { \ - /* signed 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xd1; _msgpack_store16(&buf[1], (int16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } else { \ - /* signed 8 */ \ - unsigned char buf[2] = {0xd0, TAKE8_64(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } \ - } \ - } else if(d < (1<<7)) { \ - /* fixnum */ \ - msgpack_pack_append_buffer(x, &TAKE8_64(d), 1); \ - } else { \ - if(d < (1LL<<16)) { \ - if(d < (1<<8)) { \ - /* unsigned 8 */ \ - unsigned char buf[2] = {0xcc, TAKE8_64(d)}; \ - msgpack_pack_append_buffer(x, buf, 2); \ - } else { \ - /* unsigned 16 */ \ - unsigned char buf[3]; \ - buf[0] = 0xcd; _msgpack_store16(&buf[1], (uint16_t)d); \ - msgpack_pack_append_buffer(x, buf, 3); \ - } \ - } else { \ - if(d < (1LL<<32)) { \ - /* unsigned 32 */ \ - unsigned char buf[5]; \ - buf[0] = 0xce; _msgpack_store32(&buf[1], (uint32_t)d); \ - msgpack_pack_append_buffer(x, buf, 5); \ - } else { \ - /* unsigned 64 */ \ - unsigned char buf[9]; \ - buf[0] = 0xcf; _msgpack_store64(&buf[1], d); \ - msgpack_pack_append_buffer(x, buf, 9); \ - } \ - } \ - } \ -} while(0) - - -#ifdef msgpack_pack_inline_func_fixint - -msgpack_pack_inline_func_fixint(_uint8)(msgpack_pack_user x, uint8_t d) -{ - unsigned char buf[2] = {0xcc, TAKE8_8(d)}; - msgpack_pack_append_buffer(x, buf, 2); -} - -msgpack_pack_inline_func_fixint(_uint16)(msgpack_pack_user x, uint16_t d) -{ - unsigned char buf[3]; - buf[0] = 0xcd; _msgpack_store16(&buf[1], d); - msgpack_pack_append_buffer(x, buf, 3); -} - -msgpack_pack_inline_func_fixint(_uint32)(msgpack_pack_user x, uint32_t d) -{ - unsigned char buf[5]; - buf[0] = 0xce; _msgpack_store32(&buf[1], d); - msgpack_pack_append_buffer(x, buf, 5); -} - -msgpack_pack_inline_func_fixint(_uint64)(msgpack_pack_user x, uint64_t d) -{ - unsigned char buf[9]; - buf[0] = 0xcf; _msgpack_store64(&buf[1], d); - msgpack_pack_append_buffer(x, buf, 9); -} - -msgpack_pack_inline_func_fixint(_int8)(msgpack_pack_user x, int8_t d) -{ - unsigned char buf[2] = {0xd0, TAKE8_8(d)}; - msgpack_pack_append_buffer(x, buf, 2); -} - -msgpack_pack_inline_func_fixint(_int16)(msgpack_pack_user x, int16_t d) -{ - unsigned char buf[3]; - buf[0] = 0xd1; _msgpack_store16(&buf[1], d); - msgpack_pack_append_buffer(x, buf, 3); -} - -msgpack_pack_inline_func_fixint(_int32)(msgpack_pack_user x, int32_t d) -{ - unsigned char buf[5]; - buf[0] = 0xd2; _msgpack_store32(&buf[1], d); - msgpack_pack_append_buffer(x, buf, 5); -} - -msgpack_pack_inline_func_fixint(_int64)(msgpack_pack_user x, int64_t d) -{ - unsigned char buf[9]; - buf[0] = 0xd3; _msgpack_store64(&buf[1], d); - msgpack_pack_append_buffer(x, buf, 9); -} - -#undef msgpack_pack_inline_func_fixint -#endif - - -msgpack_pack_inline_func(_uint8)(msgpack_pack_user x, uint8_t d) -{ - msgpack_pack_real_uint8(x, d); -} - -msgpack_pack_inline_func(_uint16)(msgpack_pack_user x, uint16_t d) -{ - msgpack_pack_real_uint16(x, d); -} - -msgpack_pack_inline_func(_uint32)(msgpack_pack_user x, uint32_t d) -{ - msgpack_pack_real_uint32(x, d); -} - -msgpack_pack_inline_func(_uint64)(msgpack_pack_user x, uint64_t d) -{ - msgpack_pack_real_uint64(x, d); -} - -msgpack_pack_inline_func(_int8)(msgpack_pack_user x, int8_t d) -{ - msgpack_pack_real_int8(x, d); -} - -msgpack_pack_inline_func(_int16)(msgpack_pack_user x, int16_t d) -{ - msgpack_pack_real_int16(x, d); -} - -msgpack_pack_inline_func(_int32)(msgpack_pack_user x, int32_t d) -{ - msgpack_pack_real_int32(x, d); -} - -msgpack_pack_inline_func(_int64)(msgpack_pack_user x, int64_t d) -{ - msgpack_pack_real_int64(x, d); -} - - -#ifdef msgpack_pack_inline_func_cint - -msgpack_pack_inline_func_cint(_short)(msgpack_pack_user x, short d) -{ -#if defined(SIZEOF_SHORT) -#if SIZEOF_SHORT == 2 - msgpack_pack_real_int16(x, d); -#elif SIZEOF_SHORT == 4 - msgpack_pack_real_int32(x, d); -#else - msgpack_pack_real_int64(x, d); -#endif - -#elif defined(SHRT_MAX) -#if SHRT_MAX == 0x7fff - msgpack_pack_real_int16(x, d); -#elif SHRT_MAX == 0x7fffffff - msgpack_pack_real_int32(x, d); -#else - msgpack_pack_real_int64(x, d); -#endif - -#else -if(sizeof(short) == 2) { - msgpack_pack_real_int16(x, d); -} else if(sizeof(short) == 4) { - msgpack_pack_real_int32(x, d); -} else { - msgpack_pack_real_int64(x, d); -} -#endif -} - -msgpack_pack_inline_func_cint(_int)(msgpack_pack_user x, int d) -{ -#if defined(SIZEOF_INT) -#if SIZEOF_INT == 2 - msgpack_pack_real_int16(x, d); -#elif SIZEOF_INT == 4 - msgpack_pack_real_int32(x, d); -#else - msgpack_pack_real_int64(x, d); -#endif - -#elif defined(INT_MAX) -#if INT_MAX == 0x7fff - msgpack_pack_real_int16(x, d); -#elif INT_MAX == 0x7fffffff - msgpack_pack_real_int32(x, d); -#else - msgpack_pack_real_int64(x, d); -#endif - -#else -if(sizeof(int) == 2) { - msgpack_pack_real_int16(x, d); -} else if(sizeof(int) == 4) { - msgpack_pack_real_int32(x, d); -} else { - msgpack_pack_real_int64(x, d); -} -#endif -} - -msgpack_pack_inline_func_cint(_long)(msgpack_pack_user x, long d) -{ -#if defined(SIZEOF_LONG) -#if SIZEOF_LONG == 2 - msgpack_pack_real_int16(x, d); -#elif SIZEOF_LONG == 4 - msgpack_pack_real_int32(x, d); -#else - msgpack_pack_real_int64(x, d); -#endif - -#elif defined(LONG_MAX) -#if LONG_MAX == 0x7fffL - msgpack_pack_real_int16(x, d); -#elif LONG_MAX == 0x7fffffffL - msgpack_pack_real_int32(x, d); -#else - msgpack_pack_real_int64(x, d); -#endif - -#else -if(sizeof(long) == 2) { - msgpack_pack_real_int16(x, d); -} else if(sizeof(long) == 4) { - msgpack_pack_real_int32(x, d); -} else { - msgpack_pack_real_int64(x, d); -} -#endif -} - -msgpack_pack_inline_func_cint(_long_long)(msgpack_pack_user x, long long d) -{ -#if defined(SIZEOF_LONG_LONG) -#if SIZEOF_LONG_LONG == 2 - msgpack_pack_real_int16(x, d); -#elif SIZEOF_LONG_LONG == 4 - msgpack_pack_real_int32(x, d); -#else - msgpack_pack_real_int64(x, d); -#endif - -#elif defined(LLONG_MAX) -#if LLONG_MAX == 0x7fffL - msgpack_pack_real_int16(x, d); -#elif LLONG_MAX == 0x7fffffffL - msgpack_pack_real_int32(x, d); -#else - msgpack_pack_real_int64(x, d); -#endif - -#else -if(sizeof(long long) == 2) { - msgpack_pack_real_int16(x, d); -} else if(sizeof(long long) == 4) { - msgpack_pack_real_int32(x, d); -} else { - msgpack_pack_real_int64(x, d); -} -#endif -} - -msgpack_pack_inline_func_cint(_unsigned_short)(msgpack_pack_user x, unsigned short d) -{ -#if defined(SIZEOF_SHORT) -#if SIZEOF_SHORT == 2 - msgpack_pack_real_uint16(x, d); -#elif SIZEOF_SHORT == 4 - msgpack_pack_real_uint32(x, d); -#else - msgpack_pack_real_uint64(x, d); -#endif - -#elif defined(USHRT_MAX) -#if USHRT_MAX == 0xffffU - msgpack_pack_real_uint16(x, d); -#elif USHRT_MAX == 0xffffffffU - msgpack_pack_real_uint32(x, d); -#else - msgpack_pack_real_uint64(x, d); -#endif - -#else -if(sizeof(unsigned short) == 2) { - msgpack_pack_real_uint16(x, d); -} else if(sizeof(unsigned short) == 4) { - msgpack_pack_real_uint32(x, d); -} else { - msgpack_pack_real_uint64(x, d); -} -#endif -} - -msgpack_pack_inline_func_cint(_unsigned_int)(msgpack_pack_user x, unsigned int d) -{ -#if defined(SIZEOF_INT) -#if SIZEOF_INT == 2 - msgpack_pack_real_uint16(x, d); -#elif SIZEOF_INT == 4 - msgpack_pack_real_uint32(x, d); -#else - msgpack_pack_real_uint64(x, d); -#endif - -#elif defined(UINT_MAX) -#if UINT_MAX == 0xffffU - msgpack_pack_real_uint16(x, d); -#elif UINT_MAX == 0xffffffffU - msgpack_pack_real_uint32(x, d); -#else - msgpack_pack_real_uint64(x, d); -#endif - -#else -if(sizeof(unsigned int) == 2) { - msgpack_pack_real_uint16(x, d); -} else if(sizeof(unsigned int) == 4) { - msgpack_pack_real_uint32(x, d); -} else { - msgpack_pack_real_uint64(x, d); -} -#endif -} - -msgpack_pack_inline_func_cint(_unsigned_long)(msgpack_pack_user x, unsigned long d) -{ -#if defined(SIZEOF_LONG) -#if SIZEOF_LONG == 2 - msgpack_pack_real_uint16(x, d); -#elif SIZEOF_LONG == 4 - msgpack_pack_real_uint32(x, d); -#else - msgpack_pack_real_uint64(x, d); -#endif - -#elif defined(ULONG_MAX) -#if ULONG_MAX == 0xffffUL - msgpack_pack_real_uint16(x, d); -#elif ULONG_MAX == 0xffffffffUL - msgpack_pack_real_uint32(x, d); -#else - msgpack_pack_real_uint64(x, d); -#endif - -#else -if(sizeof(unsigned long) == 2) { - msgpack_pack_real_uint16(x, d); -} else if(sizeof(unsigned long) == 4) { - msgpack_pack_real_uint32(x, d); -} else { - msgpack_pack_real_uint64(x, d); -} -#endif -} - -msgpack_pack_inline_func_cint(_unsigned_long_long)(msgpack_pack_user x, unsigned long long d) -{ -#if defined(SIZEOF_LONG_LONG) -#if SIZEOF_LONG_LONG == 2 - msgpack_pack_real_uint16(x, d); -#elif SIZEOF_LONG_LONG == 4 - msgpack_pack_real_uint32(x, d); -#else - msgpack_pack_real_uint64(x, d); -#endif - -#elif defined(ULLONG_MAX) -#if ULLONG_MAX == 0xffffUL - msgpack_pack_real_uint16(x, d); -#elif ULLONG_MAX == 0xffffffffUL - msgpack_pack_real_uint32(x, d); -#else - msgpack_pack_real_uint64(x, d); -#endif - -#else -if(sizeof(unsigned long long) == 2) { - msgpack_pack_real_uint16(x, d); -} else if(sizeof(unsigned long long) == 4) { - msgpack_pack_real_uint32(x, d); -} else { - msgpack_pack_real_uint64(x, d); -} -#endif -} - -#undef msgpack_pack_inline_func_cint -#endif - - - -/* - * Float - */ - -msgpack_pack_inline_func(_float)(msgpack_pack_user x, float d) -{ - union { float f; uint32_t i; } mem; - mem.f = d; - unsigned char buf[5]; - buf[0] = 0xca; _msgpack_store32(&buf[1], mem.i); - msgpack_pack_append_buffer(x, buf, 5); -} - -msgpack_pack_inline_func(_double)(msgpack_pack_user x, double d) -{ - union { double f; uint64_t i; } mem; - mem.f = d; - unsigned char buf[9]; - buf[0] = 0xcb; -#if defined(__arm__) && !(__ARM_EABI__) // arm-oabi - // https://github.com/msgpack/msgpack-perl/pull/1 - mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL); -#endif - _msgpack_store64(&buf[1], mem.i); - msgpack_pack_append_buffer(x, buf, 9); -} - - -/* - * Nil - */ - -msgpack_pack_inline_func(_nil)(msgpack_pack_user x) -{ - static const unsigned char d = 0xc0; - msgpack_pack_append_buffer(x, &d, 1); -} - - -/* - * Boolean - */ - -msgpack_pack_inline_func(_true)(msgpack_pack_user x) -{ - static const unsigned char d = 0xc3; - msgpack_pack_append_buffer(x, &d, 1); -} - -msgpack_pack_inline_func(_false)(msgpack_pack_user x) -{ - static const unsigned char d = 0xc2; - msgpack_pack_append_buffer(x, &d, 1); -} - - -/* - * Array - */ - -msgpack_pack_inline_func(_array)(msgpack_pack_user x, unsigned int n) -{ - if(n < 16) { - unsigned char d = 0x90 | n; - msgpack_pack_append_buffer(x, &d, 1); - } else if(n < 65536) { - unsigned char buf[3]; - buf[0] = 0xdc; _msgpack_store16(&buf[1], (uint16_t)n); - msgpack_pack_append_buffer(x, buf, 3); - } else { - unsigned char buf[5]; - buf[0] = 0xdd; _msgpack_store32(&buf[1], (uint32_t)n); - msgpack_pack_append_buffer(x, buf, 5); - } -} - - -/* - * Map - */ - -msgpack_pack_inline_func(_map)(msgpack_pack_user x, unsigned int n) -{ - if(n < 16) { - unsigned char d = 0x80 | n; - msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); - } else if(n < 65536) { - unsigned char buf[3]; - buf[0] = 0xde; _msgpack_store16(&buf[1], (uint16_t)n); - msgpack_pack_append_buffer(x, buf, 3); - } else { - unsigned char buf[5]; - buf[0] = 0xdf; _msgpack_store32(&buf[1], (uint32_t)n); - msgpack_pack_append_buffer(x, buf, 5); - } -} - - -/* - * Raw - */ - -msgpack_pack_inline_func(_raw)(msgpack_pack_user x, size_t l) -{ - if(l < 32) { - unsigned char d = 0xa0 | (uint8_t)l; - msgpack_pack_append_buffer(x, &TAKE8_8(d), 1); - } else if(l < 65536) { - unsigned char buf[3]; - buf[0] = 0xda; _msgpack_store16(&buf[1], (uint16_t)l); - msgpack_pack_append_buffer(x, buf, 3); - } else { - unsigned char buf[5]; - buf[0] = 0xdb; _msgpack_store32(&buf[1], (uint32_t)l); - msgpack_pack_append_buffer(x, buf, 5); - } -} - -msgpack_pack_inline_func(_raw_body)(msgpack_pack_user x, const void* b, size_t l) -{ - msgpack_pack_append_buffer(x, (const unsigned char*)b, l); -} - -#undef msgpack_pack_inline_func -#undef msgpack_pack_user -#undef msgpack_pack_append_buffer - -#undef TAKE8_8 -#undef TAKE8_16 -#undef TAKE8_32 -#undef TAKE8_64 - -#undef msgpack_pack_real_uint8 -#undef msgpack_pack_real_uint16 -#undef msgpack_pack_real_uint32 -#undef msgpack_pack_real_uint64 -#undef msgpack_pack_real_int8 -#undef msgpack_pack_real_int16 -#undef msgpack_pack_real_int32 -#undef msgpack_pack_real_int64 - diff --git a/msgpack/preprocess b/msgpack/preprocess deleted file mode 100755 index 839eb137..00000000 --- a/msgpack/preprocess +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh - -preprocess() { - ruby -r erb -e 'puts ERB.new(ARGF.read).result' $1.erb > $1.tmp - if [ "$?" != 0 ]; then - echo "" - echo "** preprocess failed **" - echo "" - exit 1 - else - mv $1.tmp $1 - fi -} - -if [ "$1" = "clean" ];then - rm -f src/msgpack/type/tuple.hpp - rm -f src/msgpack/type/define.hpp - rm -f src/msgpack/zone.hpp -else - preprocess src/msgpack/type/tuple.hpp - preprocess src/msgpack/type/define.hpp - preprocess src/msgpack/zone.hpp -fi -cp -f sysdep.h src/msgpack/ -cp -f pack_define.h src/msgpack/ -cp -f pack_template.h src/msgpack/ -cp -f unpack_define.h src/msgpack/ -cp -f unpack_template.h src/msgpack/ -cp -f cases.mpac test/ -cp -f cases_compact.mpac test/ - -sed -e 's/8\.00/9.00/' < msgpack_vc8.vcproj > msgpack_vc2008.vcproj -sed -e 's/9\.00/10.00/' -e 's/msgpack_vc8/msgpack_vc2008/' < msgpack_vc8.sln > msgpack_vc2008.sln - diff --git a/msgpack/src/Makefile.am b/msgpack/src/Makefile.am deleted file mode 100644 index 59c65010..00000000 --- a/msgpack/src/Makefile.am +++ /dev/null @@ -1,107 +0,0 @@ - -lib_LTLIBRARIES = libmsgpack.la - -libmsgpack_la_SOURCES = \ - unpack.c \ - objectc.c \ - version.c \ - vrefbuffer.c \ - zone.c - -if ENABLE_CXX -libmsgpack_la_SOURCES += \ - object.cpp -endif - -if ENABLE_GCC_CXX_ATOMIC -libmsgpack_la_SOURCES += \ - gcc_atomic.cpp -endif - - -# -version-info CURRENT:REVISION:AGE -libmsgpack_la_LDFLAGS = -version-info 3:0:0 -no-undefined - - -# backward compatibility -lib_LTLIBRARIES += libmsgpackc.la - -libmsgpackc_la_SOURCES = \ - unpack.c \ - objectc.c \ - version.c \ - vrefbuffer.c \ - zone.c - -libmsgpackc_la_LDFLAGS = -version-info 2:0:0 -no-undefined - - -nobase_include_HEADERS = \ - msgpack/pack_define.h \ - msgpack/pack_template.h \ - msgpack/unpack_define.h \ - msgpack/unpack_template.h \ - msgpack/sysdep.h \ - msgpack.h \ - msgpack/sbuffer.h \ - msgpack/version.h \ - msgpack/vrefbuffer.h \ - msgpack/zbuffer.h \ - msgpack/pack.h \ - msgpack/unpack.h \ - msgpack/object.h \ - msgpack/zone.h - -if ENABLE_CXX -nobase_include_HEADERS += \ - msgpack.hpp \ - msgpack/sbuffer.hpp \ - msgpack/vrefbuffer.hpp \ - msgpack/zbuffer.hpp \ - msgpack/pack.hpp \ - msgpack/unpack.hpp \ - msgpack/object.hpp \ - msgpack/zone.hpp \ - msgpack/type.hpp \ - msgpack/type/bool.hpp \ - msgpack/type/deque.hpp \ - msgpack/type/float.hpp \ - msgpack/type/fixint.hpp \ - msgpack/type/int.hpp \ - msgpack/type/list.hpp \ - msgpack/type/map.hpp \ - msgpack/type/nil.hpp \ - msgpack/type/pair.hpp \ - msgpack/type/raw.hpp \ - msgpack/type/set.hpp \ - msgpack/type/string.hpp \ - msgpack/type/vector.hpp \ - msgpack/type/tuple.hpp \ - msgpack/type/define.hpp \ - msgpack/type/tr1/unordered_map.hpp \ - msgpack/type/tr1/unordered_set.hpp -endif - -EXTRA_DIST = \ - msgpack/version.h.in \ - msgpack/zone.hpp.erb \ - msgpack/type/define.hpp.erb \ - msgpack/type/tuple.hpp.erb - - -doxygen_c: - cat ../Doxyfile > Doxyfile_c - echo "FILE_PATTERNS = *.h" >> Doxyfile_c - echo "OUTPUT_DIRECTORY = doc_c" >> Doxyfile_c - echo "PROJECT_NAME = \"MessagePack for C\"" >> Doxyfile_c - doxygen Doxyfile_c - -doxygen_cpp: - cat ../Doxyfile > Doxyfile_cpp - echo "FILE_PATTERNS = *.hpp" >> Doxyfile_cpp - echo "OUTPUT_DIRECTORY = doc_cpp" >> Doxyfile_cpp - echo "PROJECT_NAME = \"MessagePack for C++\"" >> Doxyfile_cpp - doxygen Doxyfile_cpp - -doxygen: doxygen_c doxygen_cpp - diff --git a/msgpack/src/gcc_atomic.cpp b/msgpack/src/gcc_atomic.cpp deleted file mode 100644 index 25681b67..00000000 --- a/msgpack/src/gcc_atomic.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#if defined(__GNUC__) && ((__GNUC__*10 + __GNUC_MINOR__) < 41) - -#include "gcc_atomic.h" -#include - -int _msgpack_sync_decr_and_fetch(volatile _msgpack_atomic_counter_t* ptr) -{ - return __gnu_cxx::__exchange_and_add(ptr, -1) - 1; -} - -int _msgpack_sync_incr_and_fetch(volatile _msgpack_atomic_counter_t* ptr) -{ - return __gnu_cxx::__exchange_and_add(ptr, 1) + 1; -} - - -#endif // old gcc workaround diff --git a/msgpack/src/gcc_atomic.h b/msgpack/src/gcc_atomic.h deleted file mode 100644 index 842a48fb..00000000 --- a/msgpack/src/gcc_atomic.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MSGPACK_GCC_ATOMIC_H__ -#define MSGPACK_GCC_ATOMIC_H__ - -#if defined(__cplusplus) -extern "C" { -#endif - -typedef int _msgpack_atomic_counter_t; - -int _msgpack_sync_decr_and_fetch(volatile _msgpack_atomic_counter_t* ptr); -int _msgpack_sync_incr_and_fetch(volatile _msgpack_atomic_counter_t* ptr); - - -#if defined(__cplusplus) -} -#endif - - -#endif // MSGPACK_GCC_ATOMIC_H__ diff --git a/msgpack/src/msgpack.h b/msgpack/src/msgpack.h deleted file mode 100644 index 08f27688..00000000 --- a/msgpack/src/msgpack.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * MessagePack for C - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @defgroup msgpack MessagePack C - * @{ - * @} - */ -#include "msgpack/object.h" -#include "msgpack/zone.h" -#include "msgpack/pack.h" -#include "msgpack/unpack.h" -#include "msgpack/sbuffer.h" -#include "msgpack/vrefbuffer.h" -#include "msgpack/version.h" - diff --git a/msgpack/src/msgpack.hpp b/msgpack/src/msgpack.hpp deleted file mode 100644 index e14680dd..00000000 --- a/msgpack/src/msgpack.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// MessagePack for C++ -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#include "msgpack/object.hpp" -#include "msgpack/zone.hpp" -#include "msgpack/pack.hpp" -#include "msgpack/unpack.hpp" -#include "msgpack/sbuffer.hpp" -#include "msgpack/vrefbuffer.hpp" -#include "msgpack.h" diff --git a/msgpack/src/msgpack/object.h b/msgpack/src/msgpack/object.h deleted file mode 100644 index baeeb9b2..00000000 --- a/msgpack/src/msgpack/object.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * MessagePack for C dynamic typing routine - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_OBJECT_H__ -#define MSGPACK_OBJECT_H__ - -#include "zone.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @defgroup msgpack_object Dynamically typed object - * @ingroup msgpack - * @{ - */ - -typedef enum { - MSGPACK_OBJECT_NIL = 0x00, - MSGPACK_OBJECT_BOOLEAN = 0x01, - MSGPACK_OBJECT_POSITIVE_INTEGER = 0x02, - MSGPACK_OBJECT_NEGATIVE_INTEGER = 0x03, - MSGPACK_OBJECT_DOUBLE = 0x04, - MSGPACK_OBJECT_RAW = 0x05, - MSGPACK_OBJECT_ARRAY = 0x06, - MSGPACK_OBJECT_MAP = 0x07, -} msgpack_object_type; - - -struct msgpack_object; -struct msgpack_object_kv; - -typedef struct { - uint32_t size; - struct msgpack_object* ptr; -} msgpack_object_array; - -typedef struct { - uint32_t size; - struct msgpack_object_kv* ptr; -} msgpack_object_map; - -typedef struct { - uint32_t size; - const char* ptr; -} msgpack_object_raw; - -typedef union { - bool boolean; - uint64_t u64; - int64_t i64; - double dec; - msgpack_object_array array; - msgpack_object_map map; - msgpack_object_raw raw; -} msgpack_object_union; - -typedef struct msgpack_object { - msgpack_object_type type; - msgpack_object_union via; -} msgpack_object; - -typedef struct msgpack_object_kv { - msgpack_object key; - msgpack_object val; -} msgpack_object_kv; - - -void msgpack_object_print(FILE* out, msgpack_object o); - -bool msgpack_object_equal(const msgpack_object x, const msgpack_object y); - -/** @} */ - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/object.h */ - diff --git a/msgpack/src/msgpack/object.hpp b/msgpack/src/msgpack/object.hpp deleted file mode 100644 index 97c8d4aa..00000000 --- a/msgpack/src/msgpack/object.hpp +++ /dev/null @@ -1,412 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_OBJECT_HPP__ -#define MSGPACK_OBJECT_HPP__ - -#include "object.h" -#include "pack.hpp" -#include "zone.hpp" -#include -#include -#include -#include -#include - -namespace msgpack { - - -class type_error : public std::bad_cast { }; - - -namespace type { - enum object_type { - NIL = MSGPACK_OBJECT_NIL, - BOOLEAN = MSGPACK_OBJECT_BOOLEAN, - POSITIVE_INTEGER = MSGPACK_OBJECT_POSITIVE_INTEGER, - NEGATIVE_INTEGER = MSGPACK_OBJECT_NEGATIVE_INTEGER, - DOUBLE = MSGPACK_OBJECT_DOUBLE, - RAW = MSGPACK_OBJECT_RAW, - ARRAY = MSGPACK_OBJECT_ARRAY, - MAP = MSGPACK_OBJECT_MAP, - }; -} - - -struct object; -struct object_kv; - -struct object_array { - uint32_t size; - object* ptr; -}; - -struct object_map { - uint32_t size; - object_kv* ptr; -}; - -struct object_raw { - uint32_t size; - const char* ptr; -}; - -struct object { - union union_type { - bool boolean; - uint64_t u64; - int64_t i64; - double dec; - object_array array; - object_map map; - object_raw raw; - object_raw ref; // obsolete - }; - - type::object_type type; - union_type via; - - bool is_nil() const { return type == type::NIL; } - - template - T as() const; - - template - void convert(T* v) const; - - object(); - - object(msgpack_object o); - - template - explicit object(const T& v); - - template - object(const T& v, zone* z); - - template - object& operator=(const T& v); - - operator msgpack_object() const; - - struct with_zone; - -private: - struct implicit_type; - -public: - implicit_type convert() const; -}; - -struct object_kv { - object key; - object val; -}; - -struct object::with_zone : object { - with_zone(msgpack::zone* zone) : zone(zone) { } - msgpack::zone* zone; -private: - with_zone(); -}; - - -bool operator==(const object x, const object y); -bool operator!=(const object x, const object y); - -template -bool operator==(const object x, const T& y); - -template -bool operator==(const T& y, const object x); - -template -bool operator!=(const object x, const T& y); - -template -bool operator!=(const T& y, const object x); - -std::ostream& operator<< (std::ostream& s, const object o); - - -// serialize operator -template -packer& operator<< (packer& o, const T& v); - -// convert operator -template -T& operator>> (object o, T& v); - -// deconvert operator -template -void operator<< (object::with_zone& o, const T& v); - - -struct object::implicit_type { - implicit_type(object o) : obj(o) { } - ~implicit_type() { } - - template - operator T() { return obj.as(); } - -private: - object obj; -}; - - -// obsolete -template -class define : public Type { -public: - typedef Type msgpack_type; - typedef define define_type; - - define() {} - define(const msgpack_type& v) : msgpack_type(v) {} - - template - void msgpack_pack(Packer& o) const - { - o << static_cast(*this); - } - - void msgpack_unpack(object o) - { - o >> static_cast(*this); - } -}; - - -template -template -inline packer& packer::pack(const T& v) -{ - *this << v; - return *this; -} - -inline object& operator>> (object o, object& v) -{ - v = o; - return v; -} - -template -inline T& operator>> (object o, T& v) -{ - v.msgpack_unpack(o.convert()); - return v; -} - -template -inline packer& operator<< (packer& o, const T& v) -{ - v.msgpack_pack(o); - return o; -} - -template -void operator<< (object::with_zone& o, const T& v) -{ - v.msgpack_object(static_cast(&o), o.zone); -} - - -inline bool operator==(const object x, const object y) -{ - return msgpack_object_equal(x, y); -} - -template -inline bool operator==(const object x, const T& y) -try { - return x == object(y); -} catch (msgpack::type_error&) { - return false; -} - -inline bool operator!=(const object x, const object y) -{ return !(x == y); } - -template -inline bool operator==(const T& y, const object x) -{ return x == y; } - -template -inline bool operator!=(const object x, const T& y) -{ return !(x == y); } - -template -inline bool operator!=(const T& y, const object x) -{ return x != y; } - - -inline object::implicit_type object::convert() const -{ - return implicit_type(*this); -} - -template -inline void object::convert(T* v) const -{ - *this >> *v; -} - -template -inline T object::as() const -{ - T v; - convert(&v); - return v; -} - - -inline object::object() -{ - type = type::NIL; -} - -template -inline object::object(const T& v) -{ - *this << v; -} - -template -inline object& object::operator=(const T& v) -{ - *this = object(v); - return *this; -} - -template -object::object(const T& v, zone* z) -{ - with_zone oz(z); - oz << v; - type = oz.type; - via = oz.via; -} - - -inline object::object(msgpack_object o) -{ - // FIXME beter way? - ::memcpy(this, &o, sizeof(o)); -} - -inline void operator<< (object& o, msgpack_object v) -{ - // FIXME beter way? - ::memcpy(&o, &v, sizeof(v)); -} - -inline object::operator msgpack_object() const -{ - // FIXME beter way? - msgpack_object obj; - ::memcpy(&obj, this, sizeof(obj)); - return obj; -} - - -// obsolete -template -inline void convert(T& v, object o) -{ - o.convert(&v); -} - -// obsolete -template -inline void pack(packer& o, const T& v) -{ - o.pack(v); -} - -// obsolete -template -inline void pack_copy(packer& o, T v) -{ - pack(o, v); -} - - -template -packer& operator<< (packer& o, const object& v) -{ - switch(v.type) { - case type::NIL: - o.pack_nil(); - return o; - - case type::BOOLEAN: - if(v.via.boolean) { - o.pack_true(); - } else { - o.pack_false(); - } - return o; - - case type::POSITIVE_INTEGER: - o.pack_uint64(v.via.u64); - return o; - - case type::NEGATIVE_INTEGER: - o.pack_int64(v.via.i64); - return o; - - case type::DOUBLE: - o.pack_double(v.via.dec); - return o; - - case type::RAW: - o.pack_raw(v.via.raw.size); - o.pack_raw_body(v.via.raw.ptr, v.via.raw.size); - return o; - - case type::ARRAY: - o.pack_array(v.via.array.size); - for(object* p(v.via.array.ptr), - * const pend(v.via.array.ptr + v.via.array.size); - p < pend; ++p) { - o << *p; - } - return o; - - case type::MAP: - o.pack_map(v.via.map.size); - for(object_kv* p(v.via.map.ptr), - * const pend(v.via.map.ptr + v.via.map.size); - p < pend; ++p) { - o << p->key; - o << p->val; - } - return o; - - default: - throw type_error(); - } -} - - -} // namespace msgpack - -#include "msgpack/type.hpp" - -#endif /* msgpack/object.hpp */ - diff --git a/msgpack/src/msgpack/pack.h b/msgpack/src/msgpack/pack.h deleted file mode 100644 index f86e9299..00000000 --- a/msgpack/src/msgpack/pack.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * MessagePack for C packing routine - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_PACK_H__ -#define MSGPACK_PACK_H__ - -#include "pack_define.h" -#include "object.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @defgroup msgpack_buffer Buffers - * @ingroup msgpack - * @{ - * @} - */ - -/** - * @defgroup msgpack_pack Serializer - * @ingroup msgpack - * @{ - */ - -typedef int (*msgpack_packer_write)(void* data, const char* buf, unsigned int len); - -typedef struct msgpack_packer { - void* data; - msgpack_packer_write callback; -} msgpack_packer; - -static void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback); - -static msgpack_packer* msgpack_packer_new(void* data, msgpack_packer_write callback); -static void msgpack_packer_free(msgpack_packer* pk); - -static int msgpack_pack_short(msgpack_packer* pk, short d); -static int msgpack_pack_int(msgpack_packer* pk, int d); -static int msgpack_pack_long(msgpack_packer* pk, long d); -static int msgpack_pack_long_long(msgpack_packer* pk, long long d); -static int msgpack_pack_unsigned_short(msgpack_packer* pk, unsigned short d); -static int msgpack_pack_unsigned_int(msgpack_packer* pk, unsigned int d); -static int msgpack_pack_unsigned_long(msgpack_packer* pk, unsigned long d); -static int msgpack_pack_unsigned_long_long(msgpack_packer* pk, unsigned long long d); - -static int msgpack_pack_uint8(msgpack_packer* pk, uint8_t d); -static int msgpack_pack_uint16(msgpack_packer* pk, uint16_t d); -static int msgpack_pack_uint32(msgpack_packer* pk, uint32_t d); -static int msgpack_pack_uint64(msgpack_packer* pk, uint64_t d); -static int msgpack_pack_int8(msgpack_packer* pk, int8_t d); -static int msgpack_pack_int16(msgpack_packer* pk, int16_t d); -static int msgpack_pack_int32(msgpack_packer* pk, int32_t d); -static int msgpack_pack_int64(msgpack_packer* pk, int64_t d); - -static int msgpack_pack_fix_uint8(msgpack_packer* pk, uint8_t d); -static int msgpack_pack_fix_uint16(msgpack_packer* pk, uint16_t d); -static int msgpack_pack_fix_uint32(msgpack_packer* pk, uint32_t d); -static int msgpack_pack_fix_uint64(msgpack_packer* pk, uint64_t d); -static int msgpack_pack_fix_int8(msgpack_packer* pk, int8_t d); -static int msgpack_pack_fix_int16(msgpack_packer* pk, int16_t d); -static int msgpack_pack_fix_int32(msgpack_packer* pk, int32_t d); -static int msgpack_pack_fix_int64(msgpack_packer* pk, int64_t d); - -static int msgpack_pack_float(msgpack_packer* pk, float d); -static int msgpack_pack_double(msgpack_packer* pk, double d); - -static int msgpack_pack_nil(msgpack_packer* pk); -static int msgpack_pack_true(msgpack_packer* pk); -static int msgpack_pack_false(msgpack_packer* pk); - -static int msgpack_pack_array(msgpack_packer* pk, unsigned int n); - -static int msgpack_pack_map(msgpack_packer* pk, unsigned int n); - -static int msgpack_pack_raw(msgpack_packer* pk, size_t l); -static int msgpack_pack_raw_body(msgpack_packer* pk, const void* b, size_t l); - -int msgpack_pack_object(msgpack_packer* pk, msgpack_object d); - - -/** @} */ - - -#define msgpack_pack_inline_func(name) \ - inline int msgpack_pack ## name - -#define msgpack_pack_inline_func_cint(name) \ - inline int msgpack_pack ## name - -#define msgpack_pack_inline_func_fixint(name) \ - inline int msgpack_pack_fix ## name - -#define msgpack_pack_user msgpack_packer* - -#define msgpack_pack_append_buffer(user, buf, len) \ - return (*(user)->callback)((user)->data, (const char*)buf, len) - -#include "pack_template.h" - -inline void msgpack_packer_init(msgpack_packer* pk, void* data, msgpack_packer_write callback) -{ - pk->data = data; - pk->callback = callback; -} - -inline msgpack_packer* msgpack_packer_new(void* data, msgpack_packer_write callback) -{ - msgpack_packer* pk = (msgpack_packer*)calloc(1, sizeof(msgpack_packer)); - if(!pk) { return NULL; } - msgpack_packer_init(pk, data, callback); - return pk; -} - -inline void msgpack_packer_free(msgpack_packer* pk) -{ - free(pk); -} - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/pack.h */ - diff --git a/msgpack/src/msgpack/pack.hpp b/msgpack/src/msgpack/pack.hpp deleted file mode 100644 index 0090b961..00000000 --- a/msgpack/src/msgpack/pack.hpp +++ /dev/null @@ -1,318 +0,0 @@ -// -// MessagePack for C++ serializing routine -// -// Copyright (C) 2008-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_PACK_HPP__ -#define MSGPACK_PACK_HPP__ - -#include "pack_define.h" -#include -#include - -namespace msgpack { - - -template -class packer { -public: - packer(Stream* s); - packer(Stream& s); - ~packer(); - -public: - template - packer& pack(const T& v); - - packer& pack_uint8(uint8_t d); - packer& pack_uint16(uint16_t d); - packer& pack_uint32(uint32_t d); - packer& pack_uint64(uint64_t d); - packer& pack_int8(int8_t d); - packer& pack_int16(int16_t d); - packer& pack_int32(int32_t d); - packer& pack_int64(int64_t d); - - packer& pack_fix_uint8(uint8_t d); - packer& pack_fix_uint16(uint16_t d); - packer& pack_fix_uint32(uint32_t d); - packer& pack_fix_uint64(uint64_t d); - packer& pack_fix_int8(int8_t d); - packer& pack_fix_int16(int16_t d); - packer& pack_fix_int32(int32_t d); - packer& pack_fix_int64(int64_t d); - - packer& pack_short(short d); - packer& pack_int(int d); - packer& pack_long(long d); - packer& pack_long_long(long long d); - packer& pack_unsigned_short(unsigned short d); - packer& pack_unsigned_int(unsigned int d); - packer& pack_unsigned_long(unsigned long d); - packer& pack_unsigned_long_long(unsigned long long d); - - packer& pack_float(float d); - packer& pack_double(double d); - - packer& pack_nil(); - packer& pack_true(); - packer& pack_false(); - - packer& pack_array(unsigned int n); - - packer& pack_map(unsigned int n); - - packer& pack_raw(size_t l); - packer& pack_raw_body(const char* b, size_t l); - -private: - static void _pack_uint8(Stream& x, uint8_t d); - static void _pack_uint16(Stream& x, uint16_t d); - static void _pack_uint32(Stream& x, uint32_t d); - static void _pack_uint64(Stream& x, uint64_t d); - static void _pack_int8(Stream& x, int8_t d); - static void _pack_int16(Stream& x, int16_t d); - static void _pack_int32(Stream& x, int32_t d); - static void _pack_int64(Stream& x, int64_t d); - - static void _pack_fix_uint8(Stream& x, uint8_t d); - static void _pack_fix_uint16(Stream& x, uint16_t d); - static void _pack_fix_uint32(Stream& x, uint32_t d); - static void _pack_fix_uint64(Stream& x, uint64_t d); - static void _pack_fix_int8(Stream& x, int8_t d); - static void _pack_fix_int16(Stream& x, int16_t d); - static void _pack_fix_int32(Stream& x, int32_t d); - static void _pack_fix_int64(Stream& x, int64_t d); - - static void _pack_short(Stream& x, short d); - static void _pack_int(Stream& x, int d); - static void _pack_long(Stream& x, long d); - static void _pack_long_long(Stream& x, long long d); - static void _pack_unsigned_short(Stream& x, unsigned short d); - static void _pack_unsigned_int(Stream& x, unsigned int d); - static void _pack_unsigned_long(Stream& x, unsigned long d); - static void _pack_unsigned_long_long(Stream& x, unsigned long long d); - - static void _pack_float(Stream& x, float d); - static void _pack_double(Stream& x, double d); - - static void _pack_nil(Stream& x); - static void _pack_true(Stream& x); - static void _pack_false(Stream& x); - - static void _pack_array(Stream& x, unsigned int n); - - static void _pack_map(Stream& x, unsigned int n); - - static void _pack_raw(Stream& x, size_t l); - static void _pack_raw_body(Stream& x, const void* b, size_t l); - - static void append_buffer(Stream& x, const unsigned char* buf, unsigned int len) - { x.write((const char*)buf, len); } - -private: - Stream& m_stream; - -private: - packer(); -}; - - -template -inline void pack(Stream* s, const T& v) -{ - packer(s).pack(v); -} - -template -inline void pack(Stream& s, const T& v) -{ - packer(s).pack(v); -} - - -#define msgpack_pack_inline_func(name) \ - template \ - inline void packer::_pack ## name - -#define msgpack_pack_inline_func_cint(name) \ - template \ - inline void packer::_pack ## name - -#define msgpack_pack_inline_func_fixint(name) \ - template \ - inline void packer::_pack_fix ## name - -#define msgpack_pack_user Stream& - -#define msgpack_pack_append_buffer append_buffer - -#include "pack_template.h" - - -template -packer::packer(Stream* s) : m_stream(*s) { } - -template -packer::packer(Stream& s) : m_stream(s) { } - -template -packer::~packer() { } - - -template -inline packer& packer::pack_uint8(uint8_t d) -{ _pack_uint8(m_stream, d); return *this; } - -template -inline packer& packer::pack_uint16(uint16_t d) -{ _pack_uint16(m_stream, d); return *this; } - -template -inline packer& packer::pack_uint32(uint32_t d) -{ _pack_uint32(m_stream, d); return *this; } - -template -inline packer& packer::pack_uint64(uint64_t d) -{ _pack_uint64(m_stream, d); return *this; } - -template -inline packer& packer::pack_int8(int8_t d) -{ _pack_int8(m_stream, d); return *this; } - -template -inline packer& packer::pack_int16(int16_t d) -{ _pack_int16(m_stream, d); return *this; } - -template -inline packer& packer::pack_int32(int32_t d) -{ _pack_int32(m_stream, d); return *this; } - -template -inline packer& packer::pack_int64(int64_t d) -{ _pack_int64(m_stream, d); return *this;} - - -template -inline packer& packer::pack_fix_uint8(uint8_t d) -{ _pack_fix_uint8(m_stream, d); return *this; } - -template -inline packer& packer::pack_fix_uint16(uint16_t d) -{ _pack_fix_uint16(m_stream, d); return *this; } - -template -inline packer& packer::pack_fix_uint32(uint32_t d) -{ _pack_fix_uint32(m_stream, d); return *this; } - -template -inline packer& packer::pack_fix_uint64(uint64_t d) -{ _pack_fix_uint64(m_stream, d); return *this; } - -template -inline packer& packer::pack_fix_int8(int8_t d) -{ _pack_fix_int8(m_stream, d); return *this; } - -template -inline packer& packer::pack_fix_int16(int16_t d) -{ _pack_fix_int16(m_stream, d); return *this; } - -template -inline packer& packer::pack_fix_int32(int32_t d) -{ _pack_fix_int32(m_stream, d); return *this; } - -template -inline packer& packer::pack_fix_int64(int64_t d) -{ _pack_fix_int64(m_stream, d); return *this;} - - -template -inline packer& packer::pack_short(short d) -{ _pack_short(m_stream, d); return *this; } - -template -inline packer& packer::pack_int(int d) -{ _pack_int(m_stream, d); return *this; } - -template -inline packer& packer::pack_long(long d) -{ _pack_long(m_stream, d); return *this; } - -template -inline packer& packer::pack_long_long(long long d) -{ _pack_long_long(m_stream, d); return *this; } - -template -inline packer& packer::pack_unsigned_short(unsigned short d) -{ _pack_unsigned_short(m_stream, d); return *this; } - -template -inline packer& packer::pack_unsigned_int(unsigned int d) -{ _pack_unsigned_int(m_stream, d); return *this; } - -template -inline packer& packer::pack_unsigned_long(unsigned long d) -{ _pack_unsigned_long(m_stream, d); return *this; } - -template -inline packer& packer::pack_unsigned_long_long(unsigned long long d) -{ _pack_unsigned_long_long(m_stream, d); return *this; } - - -template -inline packer& packer::pack_float(float d) -{ _pack_float(m_stream, d); return *this; } - -template -inline packer& packer::pack_double(double d) -{ _pack_double(m_stream, d); return *this; } - - -template -inline packer& packer::pack_nil() -{ _pack_nil(m_stream); return *this; } - -template -inline packer& packer::pack_true() -{ _pack_true(m_stream); return *this; } - -template -inline packer& packer::pack_false() -{ _pack_false(m_stream); return *this; } - - -template -inline packer& packer::pack_array(unsigned int n) -{ _pack_array(m_stream, n); return *this; } - - -template -inline packer& packer::pack_map(unsigned int n) -{ _pack_map(m_stream, n); return *this; } - - -template -inline packer& packer::pack_raw(size_t l) -{ _pack_raw(m_stream, l); return *this; } - -template -inline packer& packer::pack_raw_body(const char* b, size_t l) -{ _pack_raw_body(m_stream, b, l); return *this; } - - -} // namespace msgpack - -#endif /* msgpack/pack.hpp */ - diff --git a/msgpack/src/msgpack/sbuffer.h b/msgpack/src/msgpack/sbuffer.h deleted file mode 100644 index 778dea71..00000000 --- a/msgpack/src/msgpack/sbuffer.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * MessagePack for C simple buffer implementation - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_SBUFFER_H__ -#define MSGPACK_SBUFFER_H__ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @defgroup msgpack_sbuffer Simple buffer - * @ingroup msgpack_buffer - * @{ - */ - -typedef struct msgpack_sbuffer { - size_t size; - char* data; - size_t alloc; -} msgpack_sbuffer; - -static inline void msgpack_sbuffer_init(msgpack_sbuffer* sbuf) -{ - memset(sbuf, 0, sizeof(msgpack_sbuffer)); -} - -static inline void msgpack_sbuffer_destroy(msgpack_sbuffer* sbuf) -{ - free(sbuf->data); -} - -static inline msgpack_sbuffer* msgpack_sbuffer_new(void) -{ - return (msgpack_sbuffer*)calloc(1, sizeof(msgpack_sbuffer)); -} - -static inline void msgpack_sbuffer_free(msgpack_sbuffer* sbuf) -{ - if(sbuf == NULL) { return; } - msgpack_sbuffer_destroy(sbuf); - free(sbuf); -} - -#ifndef MSGPACK_SBUFFER_INIT_SIZE -#define MSGPACK_SBUFFER_INIT_SIZE 8192 -#endif - -static inline int msgpack_sbuffer_write(void* data, const char* buf, unsigned int len) -{ - msgpack_sbuffer* sbuf = (msgpack_sbuffer*)data; - - if(sbuf->alloc - sbuf->size < len) { - size_t nsize = (sbuf->alloc) ? - sbuf->alloc * 2 : MSGPACK_SBUFFER_INIT_SIZE; - - while(nsize < sbuf->size + len) { nsize *= 2; } - - void* tmp = realloc(sbuf->data, nsize); - if(!tmp) { return -1; } - - sbuf->data = (char*)tmp; - sbuf->alloc = nsize; - } - - memcpy(sbuf->data + sbuf->size, buf, len); - sbuf->size += len; - return 0; -} - -static inline char* msgpack_sbuffer_release(msgpack_sbuffer* sbuf) -{ - char* tmp = sbuf->data; - sbuf->size = 0; - sbuf->data = NULL; - sbuf->alloc = 0; - return tmp; -} - -static inline void msgpack_sbuffer_clear(msgpack_sbuffer* sbuf) -{ - sbuf->size = 0; -} - -/** @} */ - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/sbuffer.h */ - diff --git a/msgpack/src/msgpack/sbuffer.hpp b/msgpack/src/msgpack/sbuffer.hpp deleted file mode 100644 index 14c5d2a2..00000000 --- a/msgpack/src/msgpack/sbuffer.hpp +++ /dev/null @@ -1,112 +0,0 @@ -// -// MessagePack for C++ simple buffer implementation -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_SBUFFER_HPP__ -#define MSGPACK_SBUFFER_HPP__ - -#include "sbuffer.h" -#include - -namespace msgpack { - - -class sbuffer : public msgpack_sbuffer { -public: - sbuffer(size_t initsz = MSGPACK_SBUFFER_INIT_SIZE) - { - if(initsz == 0) { - base::data = NULL; - } else { - base::data = (char*)::malloc(initsz); - if(!base::data) { - throw std::bad_alloc(); - } - } - - base::size = 0; - base::alloc = initsz; - } - - ~sbuffer() - { - ::free(base::data); - } - -public: - void write(const char* buf, unsigned int len) - { - if(base::alloc - base::size < len) { - expand_buffer(len); - } - memcpy(base::data + base::size, buf, len); - base::size += len; - } - - char* data() - { - return base::data; - } - - const char* data() const - { - return base::data; - } - - size_t size() const - { - return base::size; - } - - char* release() - { - return msgpack_sbuffer_release(this); - } - - void clear() - { - msgpack_sbuffer_clear(this); - } - -private: - void expand_buffer(size_t len) - { - size_t nsize = (base::alloc > 0) ? - base::alloc * 2 : MSGPACK_SBUFFER_INIT_SIZE; - - while(nsize < base::size + len) { nsize *= 2; } - - void* tmp = realloc(base::data, nsize); - if(!tmp) { - throw std::bad_alloc(); - } - - base::data = (char*)tmp; - base::alloc = nsize; - } - -private: - typedef msgpack_sbuffer base; - -private: - sbuffer(const sbuffer&); -}; - - -} // namespace msgpack - -#endif /* msgpack/sbuffer.hpp */ - diff --git a/msgpack/src/msgpack/type.hpp b/msgpack/src/msgpack/type.hpp deleted file mode 100644 index bca69bfd..00000000 --- a/msgpack/src/msgpack/type.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "type/bool.hpp" -#include "type/deque.hpp" -#include "type/fixint.hpp" -#include "type/float.hpp" -#include "type/int.hpp" -#include "type/list.hpp" -#include "type/map.hpp" -#include "type/nil.hpp" -#include "type/pair.hpp" -#include "type/raw.hpp" -#include "type/set.hpp" -#include "type/string.hpp" -#include "type/vector.hpp" -#include "type/tuple.hpp" -#include "type/define.hpp" - diff --git a/msgpack/src/msgpack/type/bool.hpp b/msgpack/src/msgpack/type/bool.hpp deleted file mode 100644 index 9433a982..00000000 --- a/msgpack/src/msgpack/type/bool.hpp +++ /dev/null @@ -1,55 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_BOOL_HPP__ -#define MSGPACK_TYPE_BOOL_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -inline bool& operator>> (object o, bool& v) -{ - if(o.type != type::BOOLEAN) { throw type_error(); } - v = o.via.boolean; - return v; -} - -template -inline packer& operator<< (packer& o, const bool& v) -{ - if(v) { o.pack_true(); } - else { o.pack_false(); } - return o; -} - -inline void operator<< (object& o, bool v) -{ - o.type = type::BOOLEAN; - o.via.boolean = v; -} - -inline void operator<< (object::with_zone& o, bool v) - { static_cast(o) << v; } - - -} // namespace msgpack - -#endif /* msgpack/type/bool.hpp */ - diff --git a/msgpack/src/msgpack/type/define.hpp.erb b/msgpack/src/msgpack/type/define.hpp.erb deleted file mode 100644 index c8a86512..00000000 --- a/msgpack/src/msgpack/type/define.hpp.erb +++ /dev/null @@ -1,136 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_DEFINE_HPP__ -#define MSGPACK_TYPE_DEFINE_HPP__ - -#define MSGPACK_DEFINE(...) \ - template \ - void msgpack_pack(Packer& pk) const \ - { \ - msgpack::type::make_define(__VA_ARGS__).msgpack_pack(pk); \ - } \ - void msgpack_unpack(msgpack::object o) \ - { \ - msgpack::type::make_define(__VA_ARGS__).msgpack_unpack(o); \ - }\ - template \ - void msgpack_object(MSGPACK_OBJECT* o, msgpack::zone* z) const \ - { \ - msgpack::type::make_define(__VA_ARGS__).msgpack_object(o, z); \ - } - -// MSGPACK_ADD_ENUM must be used in the global namespace. -#define MSGPACK_ADD_ENUM(enum) \ - namespace msgpack { \ - template <> \ - inline enum& operator>> (object o, enum& v) \ - { \ - int tmp; \ - o >> tmp; \ - v = static_cast(tmp); \ - return v; \ - } \ - template <> \ - void operator<< (object::with_zone& o, const enum& v) \ - { \ - int tmp = static_cast(v); \ - o << tmp; \ - } \ - } - -namespace msgpack { -namespace type { - - -<% GENERATION_LIMIT = 31 %> -template , typename A<%=i%> = void<%}%>> -struct define; - - -template <> -struct define<> { - typedef define<> value_type; - typedef tuple<> tuple_type; - template - void msgpack_pack(Packer& pk) const - { - pk.pack_array(0); - } - void msgpack_unpack(msgpack::object o) - { - if(o.type != type::ARRAY) { throw type_error(); } - } - void msgpack_object(msgpack::object* o, msgpack::zone* z) const - { - o->type = type::ARRAY; - o->via.array.ptr = NULL; - o->via.array.size = 0; - } -}; -<%0.upto(GENERATION_LIMIT) {|i|%> -template , typename A<%=j%><%}%>> -struct define, A<%=j%><%}%>> { - typedef define, A<%=j%><%}%>> value_type; - typedef tuple, A<%=j%><%}%>> tuple_type; - define(A0& _a0<%1.upto(i) {|j|%>, A<%=j%>& _a<%=j%><%}%>) : - a0(_a0)<%1.upto(i) {|j|%>, a<%=j%>(_a<%=j%>)<%}%> {} - template - void msgpack_pack(Packer& pk) const - { - pk.pack_array(<%=i+1%>); - <%0.upto(i) {|j|%> - pk.pack(a<%=j%>);<%}%> - } - void msgpack_unpack(msgpack::object o) - { - if(o.type != type::ARRAY) { throw type_error(); } - const size_t size = o.via.array.size; - <%0.upto(i) {|j|%> - if(size <= <%=j%>) { return; } o.via.array.ptr[<%=j%>].convert(&a<%=j%>);<%}%> - } - void msgpack_object(msgpack::object* o, msgpack::zone* z) const - { - o->type = type::ARRAY; - o->via.array.ptr = (object*)z->malloc(sizeof(object)*<%=i+1%>); - o->via.array.size = <%=i+1%>; - <%0.upto(i) {|j|%> - o->via.array.ptr[<%=j%>] = object(a<%=j%>, z);<%}%> - } - <%0.upto(i) {|j|%> - A<%=j%>& a<%=j%>;<%}%> -}; -<%}%> - -inline define<> make_define() -{ - return define<>(); -} -<%0.upto(GENERATION_LIMIT) {|i|%> -template , typename A<%=j%><%}%>> -define, A<%=j%><%}%>> make_define(A0& a0<%1.upto(i) {|j|%>, A<%=j%>& a<%=j%><%}%>) -{ - return define, A<%=j%><%}%>>(a0<%1.upto(i) {|j|%>, a<%=j%><%}%>); -} -<%}%> - -} // namespace type -} // namespace msgpack - - -#endif /* msgpack/type/define.hpp */ - diff --git a/msgpack/src/msgpack/type/deque.hpp b/msgpack/src/msgpack/type/deque.hpp deleted file mode 100644 index d21ceeae..00000000 --- a/msgpack/src/msgpack/type/deque.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_DEQUE_HPP__ -#define MSGPACK_TYPE_DEQUE_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -template -inline std::deque& operator>> (object o, std::deque& v) -{ - if(o.type != type::ARRAY) { throw type_error(); } - v.resize(o.via.array.size); - object* p = o.via.array.ptr; - object* const pend = o.via.array.ptr + o.via.array.size; - typename std::deque::iterator it = v.begin(); - for(; p < pend; ++p, ++it) { - p->convert(&*it); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::deque& v) -{ - o.pack_array(v.size()); - for(typename std::deque::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(*it); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::deque& v) -{ - o.type = type::ARRAY; - if(v.empty()) { - o.via.array.ptr = NULL; - o.via.array.size = 0; - } else { - object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); - object* const pend = p + v.size(); - o.via.array.ptr = p; - o.via.array.size = v.size(); - typename std::deque::const_iterator it(v.begin()); - do { - *p = object(*it, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -} // namespace msgpack - -#endif /* msgpack/type/deque.hpp */ - diff --git a/msgpack/src/msgpack/type/fixint.hpp b/msgpack/src/msgpack/type/fixint.hpp deleted file mode 100644 index ebe2696f..00000000 --- a/msgpack/src/msgpack/type/fixint.hpp +++ /dev/null @@ -1,172 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2020 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_FIXINT_HPP__ -#define MSGPACK_TYPE_FIXINT_HPP__ - -#include "msgpack/object.hpp" -#include "msgpack/type/int.hpp" - -namespace msgpack { - -namespace type { - - -template -struct fix_int { - fix_int() : value(0) { } - fix_int(T value) : value(value) { } - - operator T() const { return value; } - - T get() const { return value; } - -private: - T value; -}; - - -typedef fix_int fix_uint8; -typedef fix_int fix_uint16; -typedef fix_int fix_uint32; -typedef fix_int fix_uint64; - -typedef fix_int fix_int8; -typedef fix_int fix_int16; -typedef fix_int fix_int32; -typedef fix_int fix_int64; - - -} // namespace type - - -inline type::fix_int8& operator>> (object o, type::fix_int8& v) - { v = type::detail::convert_integer(o); return v; } - -inline type::fix_int16& operator>> (object o, type::fix_int16& v) - { v = type::detail::convert_integer(o); return v; } - -inline type::fix_int32& operator>> (object o, type::fix_int32& v) - { v = type::detail::convert_integer(o); return v; } - -inline type::fix_int64& operator>> (object o, type::fix_int64& v) - { v = type::detail::convert_integer(o); return v; } - - -inline type::fix_uint8& operator>> (object o, type::fix_uint8& v) - { v = type::detail::convert_integer(o); return v; } - -inline type::fix_uint16& operator>> (object o, type::fix_uint16& v) - { v = type::detail::convert_integer(o); return v; } - -inline type::fix_uint32& operator>> (object o, type::fix_uint32& v) - { v = type::detail::convert_integer(o); return v; } - -inline type::fix_uint64& operator>> (object o, type::fix_uint64& v) - { v = type::detail::convert_integer(o); return v; } - - -template -inline packer& operator<< (packer& o, const type::fix_int8& v) - { o.pack_fix_int8(v); return o; } - -template -inline packer& operator<< (packer& o, const type::fix_int16& v) - { o.pack_fix_int16(v); return o; } - -template -inline packer& operator<< (packer& o, const type::fix_int32& v) - { o.pack_fix_int32(v); return o; } - -template -inline packer& operator<< (packer& o, const type::fix_int64& v) - { o.pack_fix_int64(v); return o; } - - -template -inline packer& operator<< (packer& o, const type::fix_uint8& v) - { o.pack_fix_uint8(v); return o; } - -template -inline packer& operator<< (packer& o, const type::fix_uint16& v) - { o.pack_fix_uint16(v); return o; } - -template -inline packer& operator<< (packer& o, const type::fix_uint32& v) - { o.pack_fix_uint32(v); return o; } - -template -inline packer& operator<< (packer& o, const type::fix_uint64& v) - { o.pack_fix_uint64(v); return o; } - - -inline void operator<< (object& o, type::fix_int8 v) - { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } - -inline void operator<< (object& o, type::fix_int16 v) - { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } - -inline void operator<< (object& o, type::fix_int32 v) - { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } - -inline void operator<< (object& o, type::fix_int64 v) - { v.get() < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v.get() : o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } - - -inline void operator<< (object& o, type::fix_uint8 v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } - -inline void operator<< (object& o, type::fix_uint16 v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } - -inline void operator<< (object& o, type::fix_uint32 v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } - -inline void operator<< (object& o, type::fix_uint64 v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v.get(); } - - -inline void operator<< (object::with_zone& o, type::fix_int8 v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, type::fix_int16 v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, type::fix_int32 v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, type::fix_int64 v) - { static_cast(o) << v; } - - -inline void operator<< (object::with_zone& o, type::fix_uint8 v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, type::fix_uint16 v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, type::fix_uint32 v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, type::fix_uint64 v) - { static_cast(o) << v; } - - -} // namespace msgpack - -#endif /* msgpack/type/fixint.hpp */ - diff --git a/msgpack/src/msgpack/type/float.hpp b/msgpack/src/msgpack/type/float.hpp deleted file mode 100644 index 11df6b2c..00000000 --- a/msgpack/src/msgpack/type/float.hpp +++ /dev/null @@ -1,82 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_FLOAT_HPP__ -#define MSGPACK_TYPE_FLOAT_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -// FIXME check overflow, underflow - - -inline float& operator>> (object o, float& v) -{ - if(o.type != type::DOUBLE) { throw type_error(); } - v = (float)o.via.dec; - return v; -} - -template -inline packer& operator<< (packer& o, const float& v) -{ - o.pack_float(v); - return o; -} - - -inline double& operator>> (object o, double& v) -{ - if(o.type != type::DOUBLE) { throw type_error(); } - v = o.via.dec; - return v; -} - -template -inline packer& operator<< (packer& o, const double& v) -{ - o.pack_double(v); - return o; -} - - -inline void operator<< (object& o, float v) -{ - o.type = type::DOUBLE; - o.via.dec = (double)v; -} - -inline void operator<< (object& o, double v) -{ - o.type = type::DOUBLE; - o.via.dec = v; -} - -inline void operator<< (object::with_zone& o, float v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, double v) - { static_cast(o) << v; } - - -} // namespace msgpack - -#endif /* msgpack/type/float.hpp */ - diff --git a/msgpack/src/msgpack/type/int.hpp b/msgpack/src/msgpack/type/int.hpp deleted file mode 100644 index e45121df..00000000 --- a/msgpack/src/msgpack/type/int.hpp +++ /dev/null @@ -1,211 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_INT_HPP__ -#define MSGPACK_TYPE_INT_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -namespace type { -namespace detail { - template - struct convert_integer_sign; - - template - struct convert_integer_sign { - static inline T convert(object o) { - if(o.type == type::POSITIVE_INTEGER) { - if(o.via.u64 > (uint64_t)std::numeric_limits::max()) - { throw type_error(); } - return (T)o.via.u64; - } else if(o.type == type::NEGATIVE_INTEGER) { - if(o.via.i64 < (int64_t)std::numeric_limits::min()) - { throw type_error(); } - return (T)o.via.i64; - } - throw type_error(); - } - }; - - template - struct convert_integer_sign { - static inline T convert(object o) { - if(o.type == type::POSITIVE_INTEGER) { - if(o.via.u64 > (uint64_t)std::numeric_limits::max()) - { throw type_error(); } - return (T)o.via.u64; - } - throw type_error(); - } - }; - - template - static inline T convert_integer(object o) - { - return detail::convert_integer_sign::is_signed>::convert(o); - } - -} // namespace detail -} // namespace type - - -inline signed char& operator>> (object o, signed char& v) - { v = type::detail::convert_integer(o); return v; } - -inline signed short& operator>> (object o, signed short& v) - { v = type::detail::convert_integer(o); return v; } - -inline signed int& operator>> (object o, signed int& v) - { v = type::detail::convert_integer(o); return v; } - -inline signed long& operator>> (object o, signed long& v) - { v = type::detail::convert_integer(o); return v; } - -inline signed long long& operator>> (object o, signed long long& v) - { v = type::detail::convert_integer(o); return v; } - - -inline unsigned char& operator>> (object o, unsigned char& v) - { v = type::detail::convert_integer(o); return v; } - -inline unsigned short& operator>> (object o, unsigned short& v) - { v = type::detail::convert_integer(o); return v; } - -inline unsigned int& operator>> (object o, unsigned int& v) - { v = type::detail::convert_integer(o); return v; } - -inline unsigned long& operator>> (object o, unsigned long& v) - { v = type::detail::convert_integer(o); return v; } - -inline unsigned long long& operator>> (object o, unsigned long long& v) - { v = type::detail::convert_integer(o); return v; } - - -template -inline packer& operator<< (packer& o, const signed char& v) - { o.pack_int8(v); return o; } - -template -inline packer& operator<< (packer& o, const signed short& v) - { o.pack_short(v); return o; } - -template -inline packer& operator<< (packer& o, const signed int& v) - { o.pack_int(v); return o; } - -template -inline packer& operator<< (packer& o, const signed long& v) - { o.pack_long(v); return o; } - -template -inline packer& operator<< (packer& o, const signed long long& v) - { o.pack_long_long(v); return o; } - - -template -inline packer& operator<< (packer& o, const unsigned char& v) - { o.pack_uint8(v); return o; } - -template -inline packer& operator<< (packer& o, const unsigned short& v) - { o.pack_unsigned_short(v); return o; } - -template -inline packer& operator<< (packer& o, const unsigned int& v) - { o.pack_unsigned_int(v); return o; } - -template -inline packer& operator<< (packer& o, const unsigned long& v) - { o.pack_unsigned_long(v); return o; } - -template -inline packer& operator<< (packer& o, const unsigned long long& v) - { o.pack_unsigned_long_long(v); return o; } - - -inline void operator<< (object& o, signed char v) - { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - -inline void operator<< (object& o, signed short v) - { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - -inline void operator<< (object& o, signed int v) - { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - -inline void operator<< (object& o, signed long v) - { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - -inline void operator<< (object& o, signed long long v) - { v < 0 ? o.type = type::NEGATIVE_INTEGER, o.via.i64 = v : o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - - -inline void operator<< (object& o, unsigned char v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - -inline void operator<< (object& o, unsigned short v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - -inline void operator<< (object& o, unsigned int v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - -inline void operator<< (object& o, unsigned long v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - -inline void operator<< (object& o, unsigned long long v) - { o.type = type::POSITIVE_INTEGER, o.via.u64 = v; } - - -inline void operator<< (object::with_zone& o, signed char v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, signed short v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, signed int v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, signed long v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, signed long long v) - { static_cast(o) << v; } - - -inline void operator<< (object::with_zone& o, unsigned char v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, unsigned short v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, unsigned int v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, unsigned long v) - { static_cast(o) << v; } - -inline void operator<< (object::with_zone& o, unsigned long long v) - { static_cast(o) << v; } - - -} // namespace msgpack - -#endif /* msgpack/type/int.hpp */ - diff --git a/msgpack/src/msgpack/type/list.hpp b/msgpack/src/msgpack/type/list.hpp deleted file mode 100644 index c0f8ce63..00000000 --- a/msgpack/src/msgpack/type/list.hpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_LIST_HPP__ -#define MSGPACK_TYPE_LIST_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -template -inline std::list& operator>> (object o, std::list& v) -{ - if(o.type != type::ARRAY) { throw type_error(); } - v.resize(o.via.array.size); - object* p = o.via.array.ptr; - object* const pend = o.via.array.ptr + o.via.array.size; - typename std::list::iterator it = v.begin(); - for(; p < pend; ++p, ++it) { - p->convert(&*it); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::list& v) -{ - o.pack_array(v.size()); - for(typename std::list::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(*it); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::list& v) -{ - o.type = type::ARRAY; - if(v.empty()) { - o.via.array.ptr = NULL; - o.via.array.size = 0; - } else { - object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); - object* const pend = p + v.size(); - o.via.array.ptr = p; - o.via.array.size = v.size(); - typename std::list::const_iterator it(v.begin()); - do { - *p = object(*it, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -} // namespace msgpack - -#endif /* msgpack/type/list.hpp */ - diff --git a/msgpack/src/msgpack/type/map.hpp b/msgpack/src/msgpack/type/map.hpp deleted file mode 100644 index 958447d5..00000000 --- a/msgpack/src/msgpack/type/map.hpp +++ /dev/null @@ -1,205 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_MAP_HPP__ -#define MSGPACK_TYPE_MAP_HPP__ - -#include "msgpack/object.hpp" -#include -#include -#include - -namespace msgpack { - - -namespace type { - -template -class assoc_vector : public std::vector< std::pair > {}; - -namespace detail { - template - struct pair_first_less { - bool operator() (const std::pair& x, const std::pair& y) const - { return x.first < y.first; } - }; -} - -} //namespace type - - -template -inline type::assoc_vector& operator>> (object o, type::assoc_vector& v) -{ - if(o.type != type::MAP) { throw type_error(); } - v.resize(o.via.map.size); - object_kv* p = o.via.map.ptr; - object_kv* const pend = o.via.map.ptr + o.via.map.size; - std::pair* it(&v.front()); - for(; p < pend; ++p, ++it) { - p->key.convert(&it->first); - p->val.convert(&it->second); - } - std::sort(v.begin(), v.end(), type::detail::pair_first_less()); - return v; -} - -template -inline packer& operator<< (packer& o, const type::assoc_vector& v) -{ - o.pack_map(v.size()); - for(typename type::assoc_vector::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(it->first); - o.pack(it->second); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const type::assoc_vector& v) -{ - o.type = type::MAP; - if(v.empty()) { - o.via.map.ptr = NULL; - o.via.map.size = 0; - } else { - object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); - object_kv* const pend = p + v.size(); - o.via.map.ptr = p; - o.via.map.size = v.size(); - typename type::assoc_vector::const_iterator it(v.begin()); - do { - p->key = object(it->first, o.zone); - p->val = object(it->second, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -template -inline std::map operator>> (object o, std::map& v) -{ - if(o.type != type::MAP) { throw type_error(); } - object_kv* p(o.via.map.ptr); - object_kv* const pend(o.via.map.ptr + o.via.map.size); - for(; p != pend; ++p) { - K key; - p->key.convert(&key); - typename std::map::iterator it(v.lower_bound(key)); - if(it != v.end() && !(key < it->first)) { - p->val.convert(&it->second); - } else { - V val; - p->val.convert(&val); - v.insert(it, std::pair(key, val)); - } - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::map& v) -{ - o.pack_map(v.size()); - for(typename std::map::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(it->first); - o.pack(it->second); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::map& v) -{ - o.type = type::MAP; - if(v.empty()) { - o.via.map.ptr = NULL; - o.via.map.size = 0; - } else { - object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); - object_kv* const pend = p + v.size(); - o.via.map.ptr = p; - o.via.map.size = v.size(); - typename std::map::const_iterator it(v.begin()); - do { - p->key = object(it->first, o.zone); - p->val = object(it->second, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -template -inline std::multimap operator>> (object o, std::multimap& v) -{ - if(o.type != type::MAP) { throw type_error(); } - object_kv* p(o.via.map.ptr); - object_kv* const pend(o.via.map.ptr + o.via.map.size); - for(; p != pend; ++p) { - std::pair value; - p->key.convert(&value.first); - p->val.convert(&value.second); - v.insert(value); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::multimap& v) -{ - o.pack_map(v.size()); - for(typename std::multimap::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(it->first); - o.pack(it->second); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::multimap& v) -{ - o.type = type::MAP; - if(v.empty()) { - o.via.map.ptr = NULL; - o.via.map.size = 0; - } else { - object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); - object_kv* const pend = p + v.size(); - o.via.map.ptr = p; - o.via.map.size = v.size(); - typename std::multimap::const_iterator it(v.begin()); - do { - p->key = object(it->first, o.zone); - p->val = object(it->second, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -} // namespace msgpack - -#endif /* msgpack/type/map.hpp */ - diff --git a/msgpack/src/msgpack/type/nil.hpp b/msgpack/src/msgpack/type/nil.hpp deleted file mode 100644 index f44e45e4..00000000 --- a/msgpack/src/msgpack/type/nil.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_NIL_HPP__ -#define MSGPACK_TYPE_NIL_HPP__ - -#include "msgpack/object.hpp" - -namespace msgpack { - -namespace type { - -struct nil { }; - -} // namespace type - - -inline type::nil& operator>> (object o, type::nil& v) -{ - if(o.type != type::NIL) { throw type_error(); } - return v; -} - -template -inline packer& operator<< (packer& o, const type::nil& v) -{ - o.pack_nil(); - return o; -} - -inline void operator<< (object& o, type::nil v) -{ - o.type = type::NIL; -} - -inline void operator<< (object::with_zone& o, type::nil v) - { static_cast(o) << v; } - - -template <> -inline void object::as() const -{ - msgpack::type::nil v; - convert(&v); -} - - -} // namespace msgpack - -#endif /* msgpack/type/nil.hpp */ - diff --git a/msgpack/src/msgpack/type/pair.hpp b/msgpack/src/msgpack/type/pair.hpp deleted file mode 100644 index 296a8b64..00000000 --- a/msgpack/src/msgpack/type/pair.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_PAIR_HPP__ -#define MSGPACK_TYPE_PAIR_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -template -inline std::pair& operator>> (object o, std::pair& v) -{ - if(o.type != type::ARRAY) { throw type_error(); } - if(o.via.array.size != 2) { throw type_error(); } - o.via.array.ptr[0].convert(&v.first); - o.via.array.ptr[1].convert(&v.second); - return v; -} - -template -inline packer& operator<< (packer& o, const std::pair& v) -{ - o.pack_array(2); - o.pack(v.first); - o.pack(v.second); - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::pair& v) -{ - o.type = type::ARRAY; - object* p = (object*)o.zone->malloc(sizeof(object)*2); - o.via.array.ptr = p; - o.via.array.size = 2; - p[0] = object(v.first, o.zone); - p[1] = object(v.second, o.zone); -} - - -} // namespace msgpack - -#endif /* msgpack/type/pair.hpp */ - diff --git a/msgpack/src/msgpack/type/raw.hpp b/msgpack/src/msgpack/type/raw.hpp deleted file mode 100644 index 87d188f6..00000000 --- a/msgpack/src/msgpack/type/raw.hpp +++ /dev/null @@ -1,94 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_RAW_HPP__ -#define MSGPACK_TYPE_RAW_HPP__ - -#include "msgpack/object.hpp" -#include -#include - -namespace msgpack { - -namespace type { - -struct raw_ref { - raw_ref() : size(0), ptr(NULL) {} - raw_ref(const char* p, uint32_t s) : size(s), ptr(p) {} - - uint32_t size; - const char* ptr; - - std::string str() const { return std::string(ptr, size); } - - bool operator== (const raw_ref& x) const - { - return size == x.size && memcmp(ptr, x.ptr, size) == 0; - } - - bool operator!= (const raw_ref& x) const - { - return !(*this != x); - } - - bool operator< (const raw_ref& x) const - { - if(size == x.size) { return memcmp(ptr, x.ptr, size) < 0; } - else { return size < x.size; } - } - - bool operator> (const raw_ref& x) const - { - if(size == x.size) { return memcmp(ptr, x.ptr, size) > 0; } - else { return size > x.size; } - } -}; - -} // namespace type - - -inline type::raw_ref& operator>> (object o, type::raw_ref& v) -{ - if(o.type != type::RAW) { throw type_error(); } - v.ptr = o.via.raw.ptr; - v.size = o.via.raw.size; - return v; -} - -template -inline packer& operator<< (packer& o, const type::raw_ref& v) -{ - o.pack_raw(v.size); - o.pack_raw_body(v.ptr, v.size); - return o; -} - -inline void operator<< (object& o, const type::raw_ref& v) -{ - o.type = type::RAW; - o.via.raw.ptr = v.ptr; - o.via.raw.size = v.size; -} - -inline void operator<< (object::with_zone& o, const type::raw_ref& v) - { static_cast(o) << v; } - - -} // namespace msgpack - -#endif /* msgpack/type/raw.hpp */ - diff --git a/msgpack/src/msgpack/type/set.hpp b/msgpack/src/msgpack/type/set.hpp deleted file mode 100644 index bcf1030a..00000000 --- a/msgpack/src/msgpack/type/set.hpp +++ /dev/null @@ -1,122 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_SET_HPP__ -#define MSGPACK_TYPE_SET_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -template -inline std::set& operator>> (object o, std::set& v) -{ - if(o.type != type::ARRAY) { throw type_error(); } - object* p = o.via.array.ptr + o.via.array.size; - object* const pbegin = o.via.array.ptr; - while(p > pbegin) { - --p; - v.insert(p->as()); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::set& v) -{ - o.pack_array(v.size()); - for(typename std::set::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(*it); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::set& v) -{ - o.type = type::ARRAY; - if(v.empty()) { - o.via.array.ptr = NULL; - o.via.array.size = 0; - } else { - object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); - object* const pend = p + v.size(); - o.via.array.ptr = p; - o.via.array.size = v.size(); - typename std::set::const_iterator it(v.begin()); - do { - *p = object(*it, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -template -inline std::multiset& operator>> (object o, std::multiset& v) -{ - if(o.type != type::ARRAY) { throw type_error(); } - object* p = o.via.array.ptr + o.via.array.size; - object* const pbegin = o.via.array.ptr; - while(p > pbegin) { - --p; - v.insert(p->as()); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::multiset& v) -{ - o.pack_array(v.size()); - for(typename std::multiset::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(*it); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::multiset& v) -{ - o.type = type::ARRAY; - if(v.empty()) { - o.via.array.ptr = NULL; - o.via.array.size = 0; - } else { - object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); - object* const pend = p + v.size(); - o.via.array.ptr = p; - o.via.array.size = v.size(); - typename std::multiset::const_iterator it(v.begin()); - do { - *p = object(*it, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -} // namespace msgpack - -#endif /* msgpack/type/set.hpp */ - diff --git a/msgpack/src/msgpack/type/string.hpp b/msgpack/src/msgpack/type/string.hpp deleted file mode 100644 index 37d4a172..00000000 --- a/msgpack/src/msgpack/type/string.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_STRING_HPP__ -#define MSGPACK_TYPE_STRING_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -inline std::string& operator>> (object o, std::string& v) -{ - if(o.type != type::RAW) { throw type_error(); } - v.assign(o.via.raw.ptr, o.via.raw.size); - return v; -} - -template -inline packer& operator<< (packer& o, const std::string& v) -{ - o.pack_raw(v.size()); - o.pack_raw_body(v.data(), v.size()); - return o; -} - -inline void operator<< (object::with_zone& o, const std::string& v) -{ - o.type = type::RAW; - char* ptr = (char*)o.zone->malloc(v.size()); - o.via.raw.ptr = ptr; - o.via.raw.size = (uint32_t)v.size(); - memcpy(ptr, v.data(), v.size()); -} - -inline void operator<< (object& o, const std::string& v) -{ - o.type = type::RAW; - o.via.raw.ptr = v.data(); - o.via.raw.size = (uint32_t)v.size(); -} - - -} // namespace msgpack - -#endif /* msgpack/type/string.hpp */ - diff --git a/msgpack/src/msgpack/type/tr1/unordered_map.hpp b/msgpack/src/msgpack/type/tr1/unordered_map.hpp deleted file mode 100644 index 4b29f0ca..00000000 --- a/msgpack/src/msgpack/type/tr1/unordered_map.hpp +++ /dev/null @@ -1,129 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_TR1_UNORDERED_MAP_HPP__ -#define MSGPACK_TYPE_TR1_UNORDERED_MAP_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -template -inline std::tr1::unordered_map operator>> (object o, std::tr1::unordered_map& v) -{ - if(o.type != type::MAP) { throw type_error(); } - object_kv* p(o.via.map.ptr); - object_kv* const pend(o.via.map.ptr + o.via.map.size); - for(; p != pend; ++p) { - K key; - p->key.convert(&key); - p->val.convert(&v[key]); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::tr1::unordered_map& v) -{ - o.pack_map(v.size()); - for(typename std::tr1::unordered_map::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(it->first); - o.pack(it->second); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::tr1::unordered_map& v) -{ - o.type = type::MAP; - if(v.empty()) { - o.via.map.ptr = NULL; - o.via.map.size = 0; - } else { - object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); - object_kv* const pend = p + v.size(); - o.via.map.ptr = p; - o.via.map.size = v.size(); - typename std::tr1::unordered_map::const_iterator it(v.begin()); - do { - p->key = object(it->first, o.zone); - p->val = object(it->second, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -template -inline std::tr1::unordered_multimap operator>> (object o, std::tr1::unordered_multimap& v) -{ - if(o.type != type::MAP) { throw type_error(); } - object_kv* p(o.via.map.ptr); - object_kv* const pend(o.via.map.ptr + o.via.map.size); - for(; p != pend; ++p) { - std::pair value; - p->key.convert(&value.first); - p->val.convert(&value.second); - v.insert(value); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::tr1::unordered_multimap& v) -{ - o.pack_map(v.size()); - for(typename std::tr1::unordered_multimap::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(it->first); - o.pack(it->second); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::tr1::unordered_multimap& v) -{ - o.type = type::MAP; - if(v.empty()) { - o.via.map.ptr = NULL; - o.via.map.size = 0; - } else { - object_kv* p = (object_kv*)o.zone->malloc(sizeof(object_kv)*v.size()); - object_kv* const pend = p + v.size(); - o.via.map.ptr = p; - o.via.map.size = v.size(); - typename std::tr1::unordered_multimap::const_iterator it(v.begin()); - do { - p->key = object(it->first, o.zone); - p->val = object(it->second, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -} // namespace msgpack - -#endif /* msgpack/type/map.hpp */ - diff --git a/msgpack/src/msgpack/type/tr1/unordered_set.hpp b/msgpack/src/msgpack/type/tr1/unordered_set.hpp deleted file mode 100644 index 4af6801c..00000000 --- a/msgpack/src/msgpack/type/tr1/unordered_set.hpp +++ /dev/null @@ -1,122 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_TR1_UNORDERED_SET_HPP__ -#define MSGPACK_TYPE_TR1_UNORDERED_SET_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -template -inline std::tr1::unordered_set& operator>> (object o, std::tr1::unordered_set& v) -{ - if(o.type != type::ARRAY) { throw type_error(); } - object* p = o.via.array.ptr + o.via.array.size; - object* const pbegin = o.via.array.ptr; - while(p > pbegin) { - --p; - v.insert(p->as()); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::tr1::unordered_set& v) -{ - o.pack_array(v.size()); - for(typename std::tr1::unordered_set::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(*it); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::tr1::unordered_set& v) -{ - o.type = type::ARRAY; - if(v.empty()) { - o.via.array.ptr = NULL; - o.via.array.size = 0; - } else { - object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); - object* const pend = p + v.size(); - o.via.array.ptr = p; - o.via.array.size = v.size(); - typename std::tr1::unordered_set::const_iterator it(v.begin()); - do { - *p = object(*it, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -template -inline std::tr1::unordered_multiset& operator>> (object o, std::tr1::unordered_multiset& v) -{ - if(o.type != type::ARRAY) { throw type_error(); } - object* p = o.via.array.ptr + o.via.array.size; - object* const pbegin = o.via.array.ptr; - while(p > pbegin) { - --p; - v.insert(p->as()); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::tr1::unordered_multiset& v) -{ - o.pack_array(v.size()); - for(typename std::tr1::unordered_multiset::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(*it); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::tr1::unordered_multiset& v) -{ - o.type = type::ARRAY; - if(v.empty()) { - o.via.array.ptr = NULL; - o.via.array.size = 0; - } else { - object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); - object* const pend = p + v.size(); - o.via.array.ptr = p; - o.via.array.size = v.size(); - typename std::tr1::unordered_multiset::const_iterator it(v.begin()); - do { - *p = object(*it, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -} // namespace msgpack - -#endif /* msgpack/type/set.hpp */ - diff --git a/msgpack/src/msgpack/type/tuple.hpp.erb b/msgpack/src/msgpack/type/tuple.hpp.erb deleted file mode 100644 index ebef8163..00000000 --- a/msgpack/src/msgpack/type/tuple.hpp.erb +++ /dev/null @@ -1,206 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_TUPLE_HPP__ -#define MSGPACK_TYPE_TUPLE_HPP__ - -#include "msgpack/object.hpp" - -namespace msgpack { - -namespace type { - -// FIXME operator== -// FIXME operator!= -<% GENERATION_LIMIT = 31 %> - -template , typename A<%=i%> = void<%}%>> -struct tuple; - -template -struct tuple_element; - -template -struct const_tuple_element; - -template -struct tuple_type { - typedef T type; - typedef T value_type; - typedef T& reference; - typedef const T& const_reference; - typedef const T& transparent_reference; -}; - -template -struct tuple_type { - typedef T type; - typedef T& value_type; - typedef T& reference; - typedef const T& const_reference; - typedef T& transparent_reference; -}; - -template -struct tuple_type { - typedef T type; - typedef T& value_type; - typedef T& reference; - typedef const T& const_reference; - typedef const T& transparent_reference; -}; - -<%0.upto(GENERATION_LIMIT) {|i|%> -<%0.upto(i) {|j|%> -template , typename A<%=k%><%}%>> -struct tuple_element, A<%=k%><%}%>>, <%=j%>> : tuple_type> { - tuple_element(tuple, A<%=k%> <%}%>>& x) : _x(x.a<%=j%>) {} - typename tuple_type>::reference get() { return _x; } - typename tuple_type>::const_reference get() const { return _x; } -private: - typename tuple_type>::reference _x; -}; -<%}%> -<%}%> - -<%0.upto(GENERATION_LIMIT) {|i|%> -<%0.upto(i) {|j|%> -template , typename A<%=k%><%}%>> -struct const_tuple_element, A<%=k%><%}%>>, <%=j%>> : tuple_type> { - const_tuple_element(const tuple, A<%=k%><%}%>>& x) : _x(x.a<%=j%>) {} - typename tuple_type>::const_reference get() const { return _x; } -private: - typename tuple_type>::const_reference _x; -}; -<%}%> -<%}%> - -template <> -struct tuple<> { - tuple() {} - tuple(object o) { o.convert(this); } - typedef tuple<> value_type; -}; -<%0.upto(GENERATION_LIMIT) {|i|%> -template , typename A<%=j%><%}%>> -struct tuple, A<%=j%><%}%>> { - typedef tuple, A<%=j%><%}%>> value_type; - tuple() {} - tuple(typename tuple_type::transparent_reference _a0<%1.upto(i) {|j|%>, typename tuple_type>::transparent_reference _a<%=j%><%}%>) : - a0(_a0)<%1.upto(i) {|j|%>, a<%=j%>(_a<%=j%>)<%}%> {} - tuple(object o) { o.convert(this); } - template typename tuple_element::reference get() - { return tuple_element(*this).get(); } - template typename const_tuple_element::const_reference get() const - { return const_tuple_element(*this).get(); } - <%0.upto(i) {|j|%> - A<%=j%> a<%=j%>;<%}%> -}; -<%}%> - -inline tuple<> make_tuple() -{ - return tuple<>(); -} -<%0.upto(GENERATION_LIMIT) {|i|%> -template , typename A<%=j%><%}%>> -tuple, A<%=j%><%}%>> make_tuple(typename tuple_type::transparent_reference a0<%1.upto(i) {|j|%>, typename tuple_type>::transparent_reference a<%=j%><%}%>) -{ - return tuple, A<%=j%><%}%>>(a0<%1.upto(i) {|j|%>, a<%=j%><%}%>); -} -<%}%> - -} // namespace type - - -inline type::tuple<>& operator>> ( - object o, - type::tuple<>& v) { - if(o.type != type::ARRAY) { throw type_error(); } - return v; -} -<%0.upto(GENERATION_LIMIT) {|i|%> -template , typename A<%=j%><%}%>> -type::tuple, A<%=j%><%}%>>& operator>> ( - object o, - type::tuple, A<%=j%><%}%>>& v) { - if(o.type != type::ARRAY) { throw type_error(); } - if(o.via.array.size < <%=i+1%>) { throw type_error(); } - <%0.upto(i) {|j|%> - o.via.array.ptr[<%=j%>].convert>::type>(&v.template get<<%=j%>>());<%}%> - return v; -} -<%}%> - -template -const packer& operator<< ( - packer& o, - const type::tuple<>& v) { - o.pack_array(0); - return o; -} -<%0.upto(GENERATION_LIMIT) {|i|%> -template , typename A<%=j%><%}%>> -const packer& operator<< ( - packer& o, - const type::tuple, A<%=j%><%}%>>& v) { - o.pack_array(<%=i+1%>); - <%0.upto(i) {|j|%> - o.pack(v.template get<<%=j%>>());<%}%> - return o; -} -<%}%> - -inline void operator<< ( - object::with_zone& o, - const type::tuple<>& v) { - o.type = type::ARRAY; - o.via.array.ptr = NULL; - o.via.array.size = 0; -} -<%0.upto(GENERATION_LIMIT) {|i|%> -template , typename A<%=j%><%}%>> -inline void operator<< ( - object::with_zone& o, - const type::tuple, A<%=j%><%}%>>& v) { - o.type = type::ARRAY; - o.via.array.ptr = (object*)o.zone->malloc(sizeof(object)*<%=i+1%>); - o.via.array.size = <%=i+1%>; - <%0.upto(i) {|j|%> - o.via.array.ptr[<%=j%>] = object(v.template get<<%=j%>>(), o.zone);<%}%> -} -<%}%> - -} // namespace msgpack - - -//inline std::ostream& operator<< (std::ostream& o, const msgpack::type::tuple<>& v) { -// return o << "[]"; -//} -//<%0.upto(GENERATION_LIMIT) {|i|%> -//template , typename A<%=j%><%}%>> -//inline std::ostream& operator<< (std::ostream& o, -// const msgpack::type::tuple, A<%=j%><%}%>>& v) { -// return o << "[" -// <%0.upto(i) {|j|%> -// <<<%if j != 0 then%> ", " <<<%end%> v.template get<<%=j%>>()<%}%> -// << "]"; -//} -//<%}%> - -#endif /* msgpack/type/tuple.hpp */ - diff --git a/msgpack/src/msgpack/type/vector.hpp b/msgpack/src/msgpack/type/vector.hpp deleted file mode 100644 index bd073ef8..00000000 --- a/msgpack/src/msgpack/type/vector.hpp +++ /dev/null @@ -1,81 +0,0 @@ -// -// MessagePack for C++ static resolution routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_TYPE_VECTOR_HPP__ -#define MSGPACK_TYPE_VECTOR_HPP__ - -#include "msgpack/object.hpp" -#include - -namespace msgpack { - - -template -inline std::vector& operator>> (object o, std::vector& v) -{ - if(o.type != type::ARRAY) { throw type_error(); } - v.resize(o.via.array.size); - if(o.via.array.size > 0) { - object* p = o.via.array.ptr; - object* const pend = o.via.array.ptr + o.via.array.size; - T* it = &v[0]; - do { - p->convert(it); - ++p; - ++it; - } while(p < pend); - } - return v; -} - -template -inline packer& operator<< (packer& o, const std::vector& v) -{ - o.pack_array(v.size()); - for(typename std::vector::const_iterator it(v.begin()), it_end(v.end()); - it != it_end; ++it) { - o.pack(*it); - } - return o; -} - -template -inline void operator<< (object::with_zone& o, const std::vector& v) -{ - o.type = type::ARRAY; - if(v.empty()) { - o.via.array.ptr = NULL; - o.via.array.size = 0; - } else { - object* p = (object*)o.zone->malloc(sizeof(object)*v.size()); - object* const pend = p + v.size(); - o.via.array.ptr = p; - o.via.array.size = v.size(); - typename std::vector::const_iterator it(v.begin()); - do { - *p = object(*it, o.zone); - ++p; - ++it; - } while(p < pend); - } -} - - -} // namespace msgpack - -#endif /* msgpack/type/vector.hpp */ - diff --git a/msgpack/src/msgpack/unpack.h b/msgpack/src/msgpack/unpack.h deleted file mode 100644 index bea7d922..00000000 --- a/msgpack/src/msgpack/unpack.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * MessagePack for C unpacking routine - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_UNPACKER_H__ -#define MSGPACK_UNPACKER_H__ - -#include "zone.h" -#include "object.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @defgroup msgpack_unpack Deserializer - * @ingroup msgpack - * @{ - */ - -typedef struct msgpack_unpacked { - msgpack_zone* zone; - msgpack_object data; -} msgpack_unpacked; - -bool msgpack_unpack_next(msgpack_unpacked* result, - const char* data, size_t len, size_t* off); - -/** @} */ - - -/** - * @defgroup msgpack_unpacker Streaming deserializer - * @ingroup msgpack - * @{ - */ - -typedef struct msgpack_unpacker { - char* buffer; - size_t used; - size_t free; - size_t off; - size_t parsed; - msgpack_zone* z; - size_t initial_buffer_size; - void* ctx; -} msgpack_unpacker; - - -#ifndef MSGPACK_UNPACKER_INIT_BUFFER_SIZE -#define MSGPACK_UNPACKER_INIT_BUFFER_SIZE (64*1024) -#endif - -/** - * Initializes a streaming deserializer. - * The initialized deserializer must be destroyed by msgpack_unpacker_destroy(msgpack_unpacker*). - */ -bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size); - -/** - * Destroys a streaming deserializer initialized by msgpack_unpacker_init(msgpack_unpacker*, size_t). - */ -void msgpack_unpacker_destroy(msgpack_unpacker* mpac); - - -/** - * Creates a streaming deserializer. - * The created deserializer must be destroyed by msgpack_unpacker_free(msgpack_unpacker*). - */ -msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size); - -/** - * Frees a streaming deserializer created by msgpack_unpacker_new(size_t). - */ -void msgpack_unpacker_free(msgpack_unpacker* mpac); - - -#ifndef MSGPACK_UNPACKER_RESERVE_SIZE -#define MSGPACK_UNPACKER_RESERVE_SIZE (32*1024) -#endif - -/** - * Reserves free space of the internal buffer. - * Use this function to fill the internal buffer with - * msgpack_unpacker_buffer(msgpack_unpacker*), - * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*) and - * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). - */ -static inline bool msgpack_unpacker_reserve_buffer(msgpack_unpacker* mpac, size_t size); - -/** - * Gets pointer to the free space of the internal buffer. - * Use this function to fill the internal buffer with - * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), - * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*) and - * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). - */ -static inline char* msgpack_unpacker_buffer(msgpack_unpacker* mpac); - -/** - * Gets size of the free space of the internal buffer. - * Use this function to fill the internal buffer with - * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), - * msgpack_unpacker_buffer(const msgpack_unpacker*) and - * msgpack_unpacker_buffer_consumed(msgpack_unpacker*). - */ -static inline size_t msgpack_unpacker_buffer_capacity(const msgpack_unpacker* mpac); - -/** - * Notifies the deserializer that the internal buffer filled. - * Use this function to fill the internal buffer with - * msgpack_unpacker_reserve_buffer(msgpack_unpacker*, size_t), - * msgpack_unpacker_buffer(msgpack_unpacker*) and - * msgpack_unpacker_buffer_capacity(const msgpack_unpacker*). - */ -static inline void msgpack_unpacker_buffer_consumed(msgpack_unpacker* mpac, size_t size); - - -/** - * Deserializes one object. - * Returns true if it successes. Otherwise false is returned. - * @param pac pointer to an initialized msgpack_unpacked object. - */ -bool msgpack_unpacker_next(msgpack_unpacker* mpac, msgpack_unpacked* pac); - -/** - * Initializes a msgpack_unpacked object. - * The initialized object must be destroyed by msgpack_unpacked_destroy(msgpack_unpacker*). - * Use the object with msgpack_unpacker_next(msgpack_unpacker*, msgpack_unpacked*) or - * msgpack_unpack_next(msgpack_unpacked*, const char*, size_t, size_t*). - */ -static inline void msgpack_unpacked_init(msgpack_unpacked* result); - -/** - * Destroys a streaming deserializer initialized by msgpack_unpacked(). - */ -static inline void msgpack_unpacked_destroy(msgpack_unpacked* result); - -/** - * Releases the memory zone from msgpack_unpacked object. - * The released zone must be freed by msgpack_zone_free(msgpack_zone*). - */ -static inline msgpack_zone* msgpack_unpacked_release_zone(msgpack_unpacked* result); - - -int msgpack_unpacker_execute(msgpack_unpacker* mpac); - -msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac); - -msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac); - -void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac); - -void msgpack_unpacker_reset(msgpack_unpacker* mpac); - -static inline size_t msgpack_unpacker_message_size(const msgpack_unpacker* mpac); - - -/** @} */ - - -// obsolete -typedef enum { - MSGPACK_UNPACK_SUCCESS = 2, - MSGPACK_UNPACK_EXTRA_BYTES = 1, - MSGPACK_UNPACK_CONTINUE = 0, - MSGPACK_UNPACK_PARSE_ERROR = -1, -} msgpack_unpack_return; - -// obsolete -msgpack_unpack_return -msgpack_unpack(const char* data, size_t len, size_t* off, - msgpack_zone* result_zone, msgpack_object* result); - - -static inline size_t msgpack_unpacker_parsed_size(const msgpack_unpacker* mpac); - -bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac); - -bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size); - -bool msgpack_unpacker_reserve_buffer(msgpack_unpacker* mpac, size_t size) -{ - if(mpac->free >= size) { return true; } - return msgpack_unpacker_expand_buffer(mpac, size); -} - -char* msgpack_unpacker_buffer(msgpack_unpacker* mpac) -{ - return mpac->buffer + mpac->used; -} - -size_t msgpack_unpacker_buffer_capacity(const msgpack_unpacker* mpac) -{ - return mpac->free; -} - -void msgpack_unpacker_buffer_consumed(msgpack_unpacker* mpac, size_t size) -{ - mpac->used += size; - mpac->free -= size; -} - -size_t msgpack_unpacker_message_size(const msgpack_unpacker* mpac) -{ - return mpac->parsed - mpac->off + mpac->used; -} - -size_t msgpack_unpacker_parsed_size(const msgpack_unpacker* mpac) -{ - return mpac->parsed; -} - - -void msgpack_unpacked_init(msgpack_unpacked* result) -{ - memset(result, 0, sizeof(msgpack_unpacked)); -} - -void msgpack_unpacked_destroy(msgpack_unpacked* result) -{ - if(result->zone != NULL) { - msgpack_zone_free(result->zone); - result->zone = NULL; - memset(&result->data, 0, sizeof(msgpack_object)); - } -} - -msgpack_zone* msgpack_unpacked_release_zone(msgpack_unpacked* result) -{ - if(result->zone != NULL) { - msgpack_zone* z = result->zone; - result->zone = NULL; - return z; - } - return NULL; -} - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/unpack.h */ - diff --git a/msgpack/src/msgpack/unpack.hpp b/msgpack/src/msgpack/unpack.hpp deleted file mode 100644 index 4d9de32f..00000000 --- a/msgpack/src/msgpack/unpack.hpp +++ /dev/null @@ -1,374 +0,0 @@ -// -// MessagePack for C++ deserializing routine -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_UNPACK_HPP__ -#define MSGPACK_UNPACK_HPP__ - -#include "unpack.h" -#include "object.hpp" -#include "zone.hpp" -#include -#include - -// backward compatibility -#ifndef MSGPACK_UNPACKER_DEFAULT_INITIAL_BUFFER_SIZE -#define MSGPACK_UNPACKER_DEFAULT_INITIAL_BUFFER_SIZE MSGPACK_UNPACKER_INIT_BUFFER_SIZE -#endif - -namespace msgpack { - - -struct unpack_error : public std::runtime_error { - unpack_error(const std::string& msg) : - std::runtime_error(msg) { } -}; - - -class unpacked { -public: - unpacked() { } - - unpacked(object obj, std::auto_ptr z) : - m_obj(obj), m_zone(z) { } - - object& get() - { return m_obj; } - - const object& get() const - { return m_obj; } - - std::auto_ptr& zone() - { return m_zone; } - - const std::auto_ptr& zone() const - { return m_zone; } - -private: - object m_obj; - std::auto_ptr m_zone; -}; - - -class unpacker : public msgpack_unpacker { -public: - unpacker(size_t init_buffer_size = MSGPACK_UNPACKER_INIT_BUFFER_SIZE); - ~unpacker(); - -public: - /*! 1. reserve buffer. at least `size' bytes of capacity will be ready */ - void reserve_buffer(size_t size = MSGPACK_UNPACKER_RESERVE_SIZE); - - /*! 2. read data to the buffer() up to buffer_capacity() bytes */ - char* buffer(); - size_t buffer_capacity() const; - - /*! 3. specify the number of bytes actually copied */ - void buffer_consumed(size_t size); - - /*! 4. repeat next() until it retunrs false */ - bool next(unpacked* result); - - /*! 5. check if the size of message doesn't exceed assumption. */ - size_t message_size() const; - - // Basic usage of the unpacker is as following: - // - // msgpack::unpacker pac; - // while( /* input is readable */ ) { - // - // // 1. - // pac.reserve_buffer(32*1024); - // - // // 2. - // size_t bytes = input.readsome(pac.buffer(), pac.buffer_capacity()); - // - // // error handling ... - // - // // 3. - // pac.buffer_consumed(bytes); - // - // // 4. - // msgpack::unpacked result; - // while(pac.next(&result)) { - // // do some with the object with the zone. - // msgpack::object obj = result.get(); - // std::auto_ptr z = result.zone(); - // on_message(obj, z); - // - // //// boost::shared_ptr is also usable: - // // boost::shared_ptr life(z.release()); - // // on_message(result.get(), life); - // } - // - // // 5. - // if(pac.message_size() > 10*1024*1024) { - // throw std::runtime_error("message is too large"); - // } - // } - // - - /*! for backward compatibility */ - bool execute(); - - /*! for backward compatibility */ - object data(); - - /*! for backward compatibility */ - zone* release_zone(); - - /*! for backward compatibility */ - void reset_zone(); - - /*! for backward compatibility */ - void reset(); - -public: - // These functions are usable when non-MessagePack message follows after - // MessagePack message. - size_t parsed_size() const; - - /*! get address of the buffer that is not parsed */ - char* nonparsed_buffer(); - size_t nonparsed_size() const; - - /*! skip specified size of non-parsed buffer, leaving the buffer */ - // Note that the `size' argument must be smaller than nonparsed_size() - void skip_nonparsed_buffer(size_t size); - - /*! remove unparsed buffer from unpacker */ - // Note that reset() leaves non-parsed buffer. - void remove_nonparsed_buffer(); - -private: - typedef msgpack_unpacker base; - -private: - unpacker(const unpacker&); -}; - - -static void unpack(unpacked* result, - const char* data, size_t len, size_t* offset = NULL); - - -// obsolete -typedef enum { - UNPACK_SUCCESS = 2, - UNPACK_EXTRA_BYTES = 1, - UNPACK_CONTINUE = 0, - UNPACK_PARSE_ERROR = -1, -} unpack_return; - -// obsolete -static unpack_return unpack(const char* data, size_t len, size_t* off, - zone* z, object* result); - - -// obsolete -static object unpack(const char* data, size_t len, zone& z, size_t* off = NULL); - - -inline unpacker::unpacker(size_t initial_buffer_size) -{ - if(!msgpack_unpacker_init(this, initial_buffer_size)) { - throw std::bad_alloc(); - } -} - -inline unpacker::~unpacker() -{ - msgpack_unpacker_destroy(this); -} - - -inline void unpacker::reserve_buffer(size_t size) -{ - if(!msgpack_unpacker_reserve_buffer(this, size)) { - throw std::bad_alloc(); - } -} - -inline char* unpacker::buffer() -{ - return msgpack_unpacker_buffer(this); -} - -inline size_t unpacker::buffer_capacity() const -{ - return msgpack_unpacker_buffer_capacity(this); -} - -inline void unpacker::buffer_consumed(size_t size) -{ - return msgpack_unpacker_buffer_consumed(this, size); -} - -inline bool unpacker::next(unpacked* result) -{ - int ret = msgpack_unpacker_execute(this); - - if(ret < 0) { - throw unpack_error("parse error"); - } - - if(ret == 0) { - if (result->zone().get() != NULL) result->zone().reset(); - result->get() = object(); - return false; - - } else { - if (result->zone().get() != NULL) result->zone().reset( release_zone() ); - result->get() = data(); - reset(); - return true; - } -} - - -inline bool unpacker::execute() -{ - int ret = msgpack_unpacker_execute(this); - if(ret < 0) { - throw unpack_error("parse error"); - } else if(ret == 0) { - return false; - } else { - return true; - } -} - -inline object unpacker::data() -{ - return msgpack_unpacker_data(this); -} - -inline zone* unpacker::release_zone() -{ - return static_cast(msgpack_unpacker_release_zone(static_cast(this))); -} - -inline void unpacker::reset_zone() -{ - msgpack_unpacker_reset_zone(this); -} - -inline void unpacker::reset() -{ - msgpack_unpacker_reset(this); -} - - -inline size_t unpacker::message_size() const -{ - return msgpack_unpacker_message_size(this); -} - -inline size_t unpacker::parsed_size() const -{ - return msgpack_unpacker_parsed_size(this); -} - -inline char* unpacker::nonparsed_buffer() -{ - return base::buffer + base::off; -} - -inline size_t unpacker::nonparsed_size() const -{ - return base::used - base::off; -} - -inline void unpacker::skip_nonparsed_buffer(size_t size) -{ - base::off += size; -} - -inline void unpacker::remove_nonparsed_buffer() -{ - base::used = base::off; -} - - -inline void unpack(unpacked* result, - const char* data, size_t len, size_t* offset) -{ - msgpack::object obj; - std::auto_ptr z(new zone()); - - unpack_return ret = (unpack_return)msgpack_unpack( - data, len, offset, z.get(), - reinterpret_cast(&obj)); - - switch(ret) { - case UNPACK_SUCCESS: - result->get() = obj; - result->zone() = z; - return; - - case UNPACK_EXTRA_BYTES: - result->get() = obj; - result->zone() = z; - return; - - case UNPACK_CONTINUE: - throw unpack_error("insufficient bytes"); - - case UNPACK_PARSE_ERROR: - default: - throw unpack_error("parse error"); - } -} - - -// obsolete -inline unpack_return unpack(const char* data, size_t len, size_t* off, - zone* z, object* result) -{ - return (unpack_return)msgpack_unpack(data, len, off, - z, reinterpret_cast(result)); -} - -// obsolete -inline object unpack(const char* data, size_t len, zone& z, size_t* off) -{ - object result; - - switch( msgpack::unpack(data, len, off, &z, &result) ) { - case UNPACK_SUCCESS: - return result; - - case UNPACK_EXTRA_BYTES: - if(off) { - return result; - } else { - throw unpack_error("extra bytes"); - } - - case UNPACK_CONTINUE: - throw unpack_error("insufficient bytes"); - - case UNPACK_PARSE_ERROR: - default: - throw unpack_error("parse error"); - } -} - - -} // namespace msgpack - -#endif /* msgpack/unpack.hpp */ - diff --git a/msgpack/src/msgpack/version.h.in b/msgpack/src/msgpack/version.h.in deleted file mode 100644 index f1feb331..00000000 --- a/msgpack/src/msgpack/version.h.in +++ /dev/null @@ -1,40 +0,0 @@ -/* - * MessagePack for C version information - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_VERSION_H__ -#define MSGPACK_VERSION_H__ - -#ifdef __cplusplus -extern "C" { -#endif - - -const char* msgpack_version(void); -int msgpack_version_major(void); -int msgpack_version_minor(void); - -#define MSGPACK_VERSION "@VERSION@" -#define MSGPACK_VERSION_MAJOR @VERSION_MAJOR@ -#define MSGPACK_VERSION_MINOR @VERSION_MINOR@ - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/version.h */ - diff --git a/msgpack/src/msgpack/vrefbuffer.h b/msgpack/src/msgpack/vrefbuffer.h deleted file mode 100644 index 0643927e..00000000 --- a/msgpack/src/msgpack/vrefbuffer.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * MessagePack for C zero-copy buffer implementation - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_VREFBUFFER_H__ -#define MSGPACK_VREFBUFFER_H__ - -#include "zone.h" -#include - -#ifndef _WIN32 -#include -#else -struct iovec { - void *iov_base; - size_t iov_len; -}; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @defgroup msgpack_vrefbuffer Vectored Referencing buffer - * @ingroup msgpack_buffer - * @{ - */ - -struct msgpack_vrefbuffer_chunk; -typedef struct msgpack_vrefbuffer_chunk msgpack_vrefbuffer_chunk; - -typedef struct msgpack_vrefbuffer_inner_buffer { - size_t free; - char* ptr; - msgpack_vrefbuffer_chunk* head; -} msgpack_vrefbuffer_inner_buffer; - -typedef struct msgpack_vrefbuffer { - struct iovec* tail; - struct iovec* end; - struct iovec* array; - - size_t chunk_size; - size_t ref_size; - - msgpack_vrefbuffer_inner_buffer inner_buffer; -} msgpack_vrefbuffer; - - -#ifndef MSGPACK_VREFBUFFER_REF_SIZE -#define MSGPACK_VREFBUFFER_REF_SIZE 32 -#endif - -#ifndef MSGPACK_VREFBUFFER_CHUNK_SIZE -#define MSGPACK_VREFBUFFER_CHUNK_SIZE 8192 -#endif - -bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf, - size_t ref_size, size_t chunk_size); -void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf); - -static inline msgpack_vrefbuffer* msgpack_vrefbuffer_new(size_t ref_size, size_t chunk_size); -static inline void msgpack_vrefbuffer_free(msgpack_vrefbuffer* vbuf); - -static inline int msgpack_vrefbuffer_write(void* data, const char* buf, unsigned int len); - -static inline const struct iovec* msgpack_vrefbuffer_vec(const msgpack_vrefbuffer* vref); -static inline size_t msgpack_vrefbuffer_veclen(const msgpack_vrefbuffer* vref); - -int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf, - const char* buf, unsigned int len); - -int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf, - const char* buf, unsigned int len); - -int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to); - -void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vref); - -/** @} */ - - -msgpack_vrefbuffer* msgpack_vrefbuffer_new(size_t ref_size, size_t chunk_size) -{ - msgpack_vrefbuffer* vbuf = (msgpack_vrefbuffer*)malloc(sizeof(msgpack_vrefbuffer)); - if(!msgpack_vrefbuffer_init(vbuf, ref_size, chunk_size)) { - free(vbuf); - return NULL; - } - return vbuf; -} - -void msgpack_vrefbuffer_free(msgpack_vrefbuffer* vbuf) -{ - if(vbuf == NULL) { return; } - msgpack_vrefbuffer_destroy(vbuf); - free(vbuf); -} - -int msgpack_vrefbuffer_write(void* data, const char* buf, unsigned int len) -{ - msgpack_vrefbuffer* vbuf = (msgpack_vrefbuffer*)data; - - if(len < vbuf->ref_size) { - return msgpack_vrefbuffer_append_copy(vbuf, buf, len); - } else { - return msgpack_vrefbuffer_append_ref(vbuf, buf, len); - } -} - -const struct iovec* msgpack_vrefbuffer_vec(const msgpack_vrefbuffer* vref) -{ - return vref->array; -} - -size_t msgpack_vrefbuffer_veclen(const msgpack_vrefbuffer* vref) -{ - return vref->tail - vref->array; -} - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/vrefbuffer.h */ - diff --git a/msgpack/src/msgpack/vrefbuffer.hpp b/msgpack/src/msgpack/vrefbuffer.hpp deleted file mode 100644 index 82335277..00000000 --- a/msgpack/src/msgpack/vrefbuffer.hpp +++ /dev/null @@ -1,97 +0,0 @@ -// -// MessagePack for C++ zero-copy buffer implementation -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_VREFBUFFER_HPP__ -#define MSGPACK_VREFBUFFER_HPP__ - -#include "vrefbuffer.h" -#include - -namespace msgpack { - - -class vrefbuffer : public msgpack_vrefbuffer { -public: - vrefbuffer(size_t ref_size = MSGPACK_VREFBUFFER_REF_SIZE, - size_t chunk_size = MSGPACK_VREFBUFFER_CHUNK_SIZE) - { - msgpack_vrefbuffer_init(this, ref_size, chunk_size); - } - - ~vrefbuffer() - { - msgpack_vrefbuffer_destroy(this); - } - -public: - void write(const char* buf, unsigned int len) - { - if(len < base::ref_size) { - append_copy(buf, len); - } else { - append_ref(buf, len); - } - } - - void append_ref(const char* buf, size_t len) - { - if(msgpack_vrefbuffer_append_ref(this, buf, len) < 0) { - throw std::bad_alloc(); - } - } - - void append_copy(const char* buf, size_t len) - { - if(msgpack_vrefbuffer_append_copy(this, buf, len) < 0) { - throw std::bad_alloc(); - } - } - - const struct iovec* vector() const - { - return msgpack_vrefbuffer_vec(this); - } - - size_t vector_size() const - { - return msgpack_vrefbuffer_veclen(this); - } - - void migrate(vrefbuffer* to) - { - if(msgpack_vrefbuffer_migrate(this, to) < 0) { - throw std::bad_alloc(); - } - } - - void clear() - { - msgpack_vrefbuffer_clear(this); - } - -private: - typedef msgpack_vrefbuffer base; - -private: - vrefbuffer(const vrefbuffer&); -}; - - -} // namespace msgpack - -#endif /* msgpack/vrefbuffer.hpp */ - diff --git a/msgpack/src/msgpack/zbuffer.h b/msgpack/src/msgpack/zbuffer.h deleted file mode 100644 index efdd3049..00000000 --- a/msgpack/src/msgpack/zbuffer.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * MessagePack for C deflate buffer implementation - * - * Copyright (C) 2010 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_ZBUFFER_H__ -#define MSGPACK_ZBUFFER_H__ - -#include "sysdep.h" -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @defgroup msgpack_zbuffer Compressed buffer - * @ingroup msgpack_buffer - * @{ - */ - -typedef struct msgpack_zbuffer { - z_stream stream; - char* data; - size_t init_size; -} msgpack_zbuffer; - -#ifndef MSGPACK_ZBUFFER_INIT_SIZE -#define MSGPACK_ZBUFFER_INIT_SIZE 8192 -#endif - -static inline bool msgpack_zbuffer_init(msgpack_zbuffer* zbuf, - int level, size_t init_size); -static inline void msgpack_zbuffer_destroy(msgpack_zbuffer* zbuf); - -static inline msgpack_zbuffer* msgpack_zbuffer_new(int level, size_t init_size); -static inline void msgpack_zbuffer_free(msgpack_zbuffer* zbuf); - -static inline char* msgpack_zbuffer_flush(msgpack_zbuffer* zbuf); - -static inline const char* msgpack_zbuffer_data(const msgpack_zbuffer* zbuf); -static inline size_t msgpack_zbuffer_size(const msgpack_zbuffer* zbuf); - -static inline bool msgpack_zbuffer_reset(msgpack_zbuffer* zbuf); -static inline void msgpack_zbuffer_reset_buffer(msgpack_zbuffer* zbuf); -static inline char* msgpack_zbuffer_release_buffer(msgpack_zbuffer* zbuf); - - -#ifndef MSGPACK_ZBUFFER_RESERVE_SIZE -#define MSGPACK_ZBUFFER_RESERVE_SIZE 512 -#endif - -static inline int msgpack_zbuffer_write(void* data, const char* buf, unsigned int len); - -static inline bool msgpack_zbuffer_expand(msgpack_zbuffer* zbuf); - - -bool msgpack_zbuffer_init(msgpack_zbuffer* zbuf, - int level, size_t init_size) -{ - memset(zbuf, 0, sizeof(msgpack_zbuffer)); - zbuf->init_size = init_size; - if(deflateInit(&zbuf->stream, level) != Z_OK) { - free(zbuf->data); - return false; - } - return true; -} - -void msgpack_zbuffer_destroy(msgpack_zbuffer* zbuf) -{ - deflateEnd(&zbuf->stream); - free(zbuf->data); -} - -msgpack_zbuffer* msgpack_zbuffer_new(int level, size_t init_size) -{ - msgpack_zbuffer* zbuf = (msgpack_zbuffer*)malloc(sizeof(msgpack_zbuffer)); - if(!msgpack_zbuffer_init(zbuf, level, init_size)) { - free(zbuf); - return NULL; - } - return zbuf; -} - -void msgpack_zbuffer_free(msgpack_zbuffer* zbuf) -{ - if(zbuf == NULL) { return; } - msgpack_zbuffer_destroy(zbuf); - free(zbuf); -} - -bool msgpack_zbuffer_expand(msgpack_zbuffer* zbuf) -{ - size_t used = (char*)zbuf->stream.next_out - zbuf->data; - size_t csize = used + zbuf->stream.avail_out; - size_t nsize = (csize == 0) ? zbuf->init_size : csize * 2; - - char* tmp = (char*)realloc(zbuf->data, nsize); - if(tmp == NULL) { - return false; - } - - zbuf->data = tmp; - zbuf->stream.next_out = (Bytef*)(tmp + used); - zbuf->stream.avail_out = nsize - used; - - return true; -} - -int msgpack_zbuffer_write(void* data, const char* buf, unsigned int len) -{ - msgpack_zbuffer* zbuf = (msgpack_zbuffer*)data; - - zbuf->stream.next_in = (Bytef*)buf; - zbuf->stream.avail_in = len; - - do { - if(zbuf->stream.avail_out < MSGPACK_ZBUFFER_RESERVE_SIZE) { - if(!msgpack_zbuffer_expand(zbuf)) { - return -1; - } - } - - if(deflate(&zbuf->stream, Z_NO_FLUSH) != Z_OK) { - return -1; - } - } while(zbuf->stream.avail_in > 0); - - return 0; -} - -char* msgpack_zbuffer_flush(msgpack_zbuffer* zbuf) -{ - while(true) { - switch(deflate(&zbuf->stream, Z_FINISH)) { - case Z_STREAM_END: - return zbuf->data; - case Z_OK: - if(!msgpack_zbuffer_expand(zbuf)) { - return NULL; - } - break; - default: - return NULL; - } - } -} - -const char* msgpack_zbuffer_data(const msgpack_zbuffer* zbuf) -{ - return zbuf->data; -} - -size_t msgpack_zbuffer_size(const msgpack_zbuffer* zbuf) -{ - return (char*)zbuf->stream.next_out - zbuf->data; -} - -void msgpack_zbuffer_reset_buffer(msgpack_zbuffer* zbuf) -{ - zbuf->stream.avail_out += (char*)zbuf->stream.next_out - zbuf->data; - zbuf->stream.next_out = (Bytef*)zbuf->data; -} - -bool msgpack_zbuffer_reset(msgpack_zbuffer* zbuf) -{ - if(deflateReset(&zbuf->stream) != Z_OK) { - return false; - } - msgpack_zbuffer_reset_buffer(zbuf); - return true; -} - -char* msgpack_zbuffer_release_buffer(msgpack_zbuffer* zbuf) -{ - char* tmp = zbuf->data; - zbuf->data = NULL; - zbuf->stream.next_out = NULL; - zbuf->stream.avail_out = 0; - return tmp; -} - -/** @} */ - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/zbuffer.h */ - diff --git a/msgpack/src/msgpack/zbuffer.hpp b/msgpack/src/msgpack/zbuffer.hpp deleted file mode 100644 index 08e6fd03..00000000 --- a/msgpack/src/msgpack/zbuffer.hpp +++ /dev/null @@ -1,100 +0,0 @@ -// -// MessagePack for C++ deflate buffer implementation -// -// Copyright (C) 2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_ZBUFFER_HPP__ -#define MSGPACK_ZBUFFER_HPP__ - -#include "zbuffer.h" -#include - -namespace msgpack { - - -class zbuffer : public msgpack_zbuffer { -public: - zbuffer(int level = Z_DEFAULT_COMPRESSION, - size_t init_size = MSGPACK_ZBUFFER_INIT_SIZE) - { - msgpack_zbuffer_init(this, level, init_size); - } - - ~zbuffer() - { - msgpack_zbuffer_destroy(this); - } - -public: - void write(const char* buf, unsigned int len) - { - if(msgpack_zbuffer_write(this, buf, len) < 0) { - throw std::bad_alloc(); - } - } - - char* flush() - { - char* buf = msgpack_zbuffer_flush(this); - if(!buf) { - throw std::bad_alloc(); - } - return buf; - } - - char* data() - { - return base::data; - } - - const char* data() const - { - return base::data; - } - - size_t size() const - { - return msgpack_zbuffer_size(this); - } - - void reset() - { - if(!msgpack_zbuffer_reset(this)) { - throw std::bad_alloc(); - } - } - - void reset_buffer() - { - msgpack_zbuffer_reset_buffer(this); - } - - char* release_buffer() - { - return msgpack_zbuffer_release_buffer(this); - } - -private: - typedef msgpack_zbuffer base; - -private: - zbuffer(const zbuffer&); -}; - - -} // namespace msgpack - -#endif /* msgpack/zbuffer.hpp */ - diff --git a/msgpack/src/msgpack/zone.h b/msgpack/src/msgpack/zone.h deleted file mode 100644 index 0f5817f3..00000000 --- a/msgpack/src/msgpack/zone.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * MessagePack for C memory pool implementation - * - * Copyright (C) 2008-2010 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_ZONE_H__ -#define MSGPACK_ZONE_H__ - -#include "sysdep.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * @defgroup msgpack_zone Memory zone - * @ingroup msgpack - * @{ - */ - -typedef struct msgpack_zone_finalizer { - void (*func)(void* data); - void* data; -} msgpack_zone_finalizer; - -typedef struct msgpack_zone_finalizer_array { - msgpack_zone_finalizer* tail; - msgpack_zone_finalizer* end; - msgpack_zone_finalizer* array; -} msgpack_zone_finalizer_array; - -struct msgpack_zone_chunk; -typedef struct msgpack_zone_chunk msgpack_zone_chunk; - -typedef struct msgpack_zone_chunk_list { - size_t free; - char* ptr; - msgpack_zone_chunk* head; -} msgpack_zone_chunk_list; - -typedef struct msgpack_zone { - msgpack_zone_chunk_list chunk_list; - msgpack_zone_finalizer_array finalizer_array; - size_t chunk_size; -} msgpack_zone; - -#ifndef MSGPACK_ZONE_CHUNK_SIZE -#define MSGPACK_ZONE_CHUNK_SIZE 8192 -#endif - -bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size); -void msgpack_zone_destroy(msgpack_zone* zone); - -msgpack_zone* msgpack_zone_new(size_t chunk_size); -void msgpack_zone_free(msgpack_zone* zone); - -static inline void* msgpack_zone_malloc(msgpack_zone* zone, size_t size); -static inline void* msgpack_zone_malloc_no_align(msgpack_zone* zone, size_t size); - -static inline bool msgpack_zone_push_finalizer(msgpack_zone* zone, - void (*func)(void* data), void* data); - -static inline void msgpack_zone_swap(msgpack_zone* a, msgpack_zone* b); - -bool msgpack_zone_is_empty(msgpack_zone* zone); - -void msgpack_zone_clear(msgpack_zone* zone); - -/** @} */ - - -#ifndef MSGPACK_ZONE_ALIGN -#define MSGPACK_ZONE_ALIGN sizeof(int) -#endif - -void* msgpack_zone_malloc_expand(msgpack_zone* zone, size_t size); - -void* msgpack_zone_malloc_no_align(msgpack_zone* zone, size_t size) -{ - msgpack_zone_chunk_list* cl = &zone->chunk_list; - - if(zone->chunk_list.free < size) { - return msgpack_zone_malloc_expand(zone, size); - } - - char* ptr = cl->ptr; - cl->free -= size; - cl->ptr += size; - - return ptr; -} - -void* msgpack_zone_malloc(msgpack_zone* zone, size_t size) -{ - return msgpack_zone_malloc_no_align(zone, - ((size)+((MSGPACK_ZONE_ALIGN)-1)) & ~((MSGPACK_ZONE_ALIGN)-1)); -} - - -bool msgpack_zone_push_finalizer_expand(msgpack_zone* zone, - void (*func)(void* data), void* data); - -bool msgpack_zone_push_finalizer(msgpack_zone* zone, - void (*func)(void* data), void* data) -{ - msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; - msgpack_zone_finalizer* fin = fa->tail; - - if(fin == fa->end) { - return msgpack_zone_push_finalizer_expand(zone, func, data); - } - - fin->func = func; - fin->data = data; - - ++fa->tail; - - return true; -} - -void msgpack_zone_swap(msgpack_zone* a, msgpack_zone* b) -{ - msgpack_zone tmp = *a; - *a = *b; - *b = tmp; -} - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/zone.h */ - diff --git a/msgpack/src/msgpack/zone.hpp.erb b/msgpack/src/msgpack/zone.hpp.erb deleted file mode 100644 index c6f5481a..00000000 --- a/msgpack/src/msgpack/zone.hpp.erb +++ /dev/null @@ -1,155 +0,0 @@ -// -// MessagePack for C++ memory pool -// -// Copyright (C) 2008-2010 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#ifndef MSGPACK_ZONE_HPP__ -#define MSGPACK_ZONE_HPP__ - -#include "zone.h" -#include -#include -#include - -<% GENERATION_LIMIT = 15 %> -namespace msgpack { - - -class zone : public msgpack_zone { -public: - zone(size_t chunk_size = MSGPACK_ZONE_CHUNK_SIZE); - ~zone(); - -public: - void* malloc(size_t size); - void* malloc_no_align(size_t size); - - void push_finalizer(void (*func)(void*), void* data); - - template - void push_finalizer(std::auto_ptr obj); - - void clear(); - - void swap(zone& o); - - <%0.upto(GENERATION_LIMIT) {|i|%> - template , typename A<%=j%><%}%>> - T* allocate(<%=(1..i).map{|j|"A#{j} a#{j}"}.join(', ')%>); - <%}%> - -private: - void undo_malloc(size_t size); - - template - static void object_destructor(void* obj); - - typedef msgpack_zone base; - -private: - zone(const zone&); -}; - - - -inline zone::zone(size_t chunk_size) -{ - msgpack_zone_init(this, chunk_size); -} - -inline zone::~zone() -{ - msgpack_zone_destroy(this); -} - -inline void* zone::malloc(size_t size) -{ - void* ptr = msgpack_zone_malloc(this, size); - if(!ptr) { - throw std::bad_alloc(); - } - return ptr; -} - -inline void* zone::malloc_no_align(size_t size) -{ - void* ptr = msgpack_zone_malloc_no_align(this, size); - if(!ptr) { - throw std::bad_alloc(); - } - return ptr; -} - -inline void zone::push_finalizer(void (*func)(void*), void* data) -{ - if(!msgpack_zone_push_finalizer(this, func, data)) { - throw std::bad_alloc(); - } -} - -template -inline void zone::push_finalizer(std::auto_ptr obj) -{ - if(!msgpack_zone_push_finalizer(this, &zone::object_destructor, obj.get())) { - throw std::bad_alloc(); - } - obj.release(); -} - -inline void zone::clear() -{ - msgpack_zone_clear(this); -} - -inline void zone::swap(zone& o) -{ - msgpack_zone_swap(this, &o); -} - -template -void zone::object_destructor(void* obj) -{ - reinterpret_cast(obj)->~T(); -} - -inline void zone::undo_malloc(size_t size) -{ - base::chunk_list.ptr -= size; - base::chunk_list.free += size; -} - -<%0.upto(GENERATION_LIMIT) {|i|%> -template , typename A<%=j%><%}%>> -T* zone::allocate(<%=(1..i).map{|j|"A#{j} a#{j}"}.join(', ')%>) -{ - void* x = malloc(sizeof(T)); - if(!msgpack_zone_push_finalizer(this, &zone::object_destructor, x)) { - undo_malloc(sizeof(T)); - throw std::bad_alloc(); - } - try { - return new (x) T(<%=(1..i).map{|j|"a#{j}"}.join(', ')%>); - } catch (...) { - --base::finalizer_array.tail; - undo_malloc(sizeof(T)); - throw; - } -} -<%}%> - -} // namespace msgpack - -#endif /* msgpack/zone.hpp */ - diff --git a/msgpack/src/object.cpp b/msgpack/src/object.cpp deleted file mode 100644 index dfe32bbc..00000000 --- a/msgpack/src/object.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// -// MessagePack for C++ dynamic typed objects -// -// Copyright (C) 2008-2009 FURUHASHI Sadayuki -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#include "msgpack/object.hpp" - -namespace msgpack { - - -std::ostream& operator<< (std::ostream& s, const object o) -{ - switch(o.type) { - case type::NIL: - s << "nil"; - break; - - case type::BOOLEAN: - s << (o.via.boolean ? "true" : "false"); - break; - - case type::POSITIVE_INTEGER: - s << o.via.u64; - break; - - case type::NEGATIVE_INTEGER: - s << o.via.i64; - break; - - case type::DOUBLE: - s << o.via.dec; - break; - - case type::RAW: - (s << '"').write(o.via.raw.ptr, o.via.raw.size) << '"'; - break; - - case type::ARRAY: - s << "["; - if(o.via.array.size != 0) { - object* p(o.via.array.ptr); - s << *p; - ++p; - for(object* const pend(o.via.array.ptr + o.via.array.size); - p < pend; ++p) { - s << ", " << *p; - } - } - s << "]"; - break; - - case type::MAP: - s << "{"; - if(o.via.map.size != 0) { - object_kv* p(o.via.map.ptr); - s << p->key << "=>" << p->val; - ++p; - for(object_kv* const pend(o.via.map.ptr + o.via.map.size); - p < pend; ++p) { - s << ", " << p->key << "=>" << p->val; - } - } - s << "}"; - break; - - default: - // FIXME - s << "#"; - } - return s; -} - - -} // namespace msgpack - diff --git a/msgpack/src/objectc.c b/msgpack/src/objectc.c deleted file mode 100644 index 548e923e..00000000 --- a/msgpack/src/objectc.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * MessagePack for C dynamic typing routine - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "msgpack/object.h" -#include "msgpack/pack.h" -#include -#include - -#ifndef _MSC_VER -#include -#else -#ifndef PRIu64 -#define PRIu64 "I64u" -#endif -#ifndef PRIi64 -#define PRIi64 "I64d" -#endif -#endif - - -int msgpack_pack_object(msgpack_packer* pk, msgpack_object d) -{ - switch(d.type) { - case MSGPACK_OBJECT_NIL: - return msgpack_pack_nil(pk); - - case MSGPACK_OBJECT_BOOLEAN: - if(d.via.boolean) { - return msgpack_pack_true(pk); - } else { - return msgpack_pack_false(pk); - } - - case MSGPACK_OBJECT_POSITIVE_INTEGER: - return msgpack_pack_uint64(pk, d.via.u64); - - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - return msgpack_pack_int64(pk, d.via.i64); - - case MSGPACK_OBJECT_DOUBLE: - return msgpack_pack_double(pk, d.via.dec); - - case MSGPACK_OBJECT_RAW: - { - int ret = msgpack_pack_raw(pk, d.via.raw.size); - if(ret < 0) { return ret; } - return msgpack_pack_raw_body(pk, d.via.raw.ptr, d.via.raw.size); - } - - case MSGPACK_OBJECT_ARRAY: - { - int ret = msgpack_pack_array(pk, d.via.array.size); - if(ret < 0) { return ret; } - - msgpack_object* o = d.via.array.ptr; - msgpack_object* const oend = d.via.array.ptr + d.via.array.size; - for(; o != oend; ++o) { - ret = msgpack_pack_object(pk, *o); - if(ret < 0) { return ret; } - } - - return 0; - } - - case MSGPACK_OBJECT_MAP: - { - int ret = msgpack_pack_map(pk, d.via.map.size); - if(ret < 0) { return ret; } - - msgpack_object_kv* kv = d.via.map.ptr; - msgpack_object_kv* const kvend = d.via.map.ptr + d.via.map.size; - for(; kv != kvend; ++kv) { - ret = msgpack_pack_object(pk, kv->key); - if(ret < 0) { return ret; } - ret = msgpack_pack_object(pk, kv->val); - if(ret < 0) { return ret; } - } - - return 0; - } - - default: - return -1; - } -} - - -void msgpack_object_print(FILE* out, msgpack_object o) -{ - switch(o.type) { - case MSGPACK_OBJECT_NIL: - fprintf(out, "nil"); - break; - - case MSGPACK_OBJECT_BOOLEAN: - fprintf(out, (o.via.boolean ? "true" : "false")); - break; - - case MSGPACK_OBJECT_POSITIVE_INTEGER: - fprintf(out, "%"PRIu64, o.via.u64); - break; - - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - fprintf(out, "%"PRIi64, o.via.i64); - break; - - case MSGPACK_OBJECT_DOUBLE: - fprintf(out, "%f", o.via.dec); - break; - - case MSGPACK_OBJECT_RAW: - fprintf(out, "\""); - fwrite(o.via.raw.ptr, o.via.raw.size, 1, out); - fprintf(out, "\""); - break; - - case MSGPACK_OBJECT_ARRAY: - fprintf(out, "["); - if(o.via.array.size != 0) { - msgpack_object* p = o.via.array.ptr; - msgpack_object_print(out, *p); - ++p; - msgpack_object* const pend = o.via.array.ptr + o.via.array.size; - for(; p < pend; ++p) { - fprintf(out, ", "); - msgpack_object_print(out, *p); - } - } - fprintf(out, "]"); - break; - - case MSGPACK_OBJECT_MAP: - fprintf(out, "{"); - if(o.via.map.size != 0) { - msgpack_object_kv* p = o.via.map.ptr; - msgpack_object_print(out, p->key); - fprintf(out, "=>"); - msgpack_object_print(out, p->val); - ++p; - msgpack_object_kv* const pend = o.via.map.ptr + o.via.map.size; - for(; p < pend; ++p) { - fprintf(out, ", "); - msgpack_object_print(out, p->key); - fprintf(out, "=>"); - msgpack_object_print(out, p->val); - } - } - fprintf(out, "}"); - break; - - default: - // FIXME - fprintf(out, "#", o.type, o.via.u64); - } -} - -bool msgpack_object_equal(const msgpack_object x, const msgpack_object y) -{ - if(x.type != y.type) { return false; } - - switch(x.type) { - case MSGPACK_OBJECT_NIL: - return true; - - case MSGPACK_OBJECT_BOOLEAN: - return x.via.boolean == y.via.boolean; - - case MSGPACK_OBJECT_POSITIVE_INTEGER: - return x.via.u64 == y.via.u64; - - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - return x.via.i64 == y.via.i64; - - case MSGPACK_OBJECT_DOUBLE: - return x.via.dec == y.via.dec; - - case MSGPACK_OBJECT_RAW: - return x.via.raw.size == y.via.raw.size && - memcmp(x.via.raw.ptr, y.via.raw.ptr, x.via.raw.size) == 0; - - case MSGPACK_OBJECT_ARRAY: - if(x.via.array.size != y.via.array.size) { - return false; - } else if(x.via.array.size == 0) { - return true; - } else { - msgpack_object* px = x.via.array.ptr; - msgpack_object* const pxend = x.via.array.ptr + x.via.array.size; - msgpack_object* py = y.via.array.ptr; - do { - if(!msgpack_object_equal(*px, *py)) { - return false; - } - ++px; - ++py; - } while(px < pxend); - return true; - } - - case MSGPACK_OBJECT_MAP: - if(x.via.map.size != y.via.map.size) { - return false; - } else if(x.via.map.size == 0) { - return true; - } else { - msgpack_object_kv* px = x.via.map.ptr; - msgpack_object_kv* const pxend = x.via.map.ptr + x.via.map.size; - msgpack_object_kv* py = y.via.map.ptr; - do { - if(!msgpack_object_equal(px->key, py->key) || !msgpack_object_equal(px->val, py->val)) { - return false; - } - ++px; - ++py; - } while(px < pxend); - return true; - } - - default: - return false; - } -} - diff --git a/msgpack/src/unpack.c b/msgpack/src/unpack.c deleted file mode 100644 index 4afc05e2..00000000 --- a/msgpack/src/unpack.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * MessagePack for C unpacking routine - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "msgpack/unpack.h" -#include "msgpack/unpack_define.h" -#include - -#ifdef _msgpack_atomic_counter_header -#include _msgpack_atomic_counter_header -#endif - - -typedef struct { - msgpack_zone* z; - bool referenced; -} unpack_user; - - -#define msgpack_unpack_struct(name) \ - struct template ## name - -#define msgpack_unpack_func(ret, name) \ - ret template ## name - -#define msgpack_unpack_callback(name) \ - template_callback ## name - -#define msgpack_unpack_object msgpack_object - -#define msgpack_unpack_user unpack_user - - -struct template_context; -typedef struct template_context template_context; - -static void template_init(template_context* ctx); - -static msgpack_object template_data(template_context* ctx); - -static int template_execute(template_context* ctx, - const char* data, size_t len, size_t* off); - - -static inline msgpack_object template_callback_root(unpack_user* u) -{ msgpack_object o = {}; return o; } - -static inline int template_callback_uint8(unpack_user* u, uint8_t d, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } - -static inline int template_callback_uint16(unpack_user* u, uint16_t d, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } - -static inline int template_callback_uint32(unpack_user* u, uint32_t d, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } - -static inline int template_callback_uint64(unpack_user* u, uint64_t d, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } - -static inline int template_callback_int8(unpack_user* u, int8_t d, msgpack_object* o) -{ if(d >= 0) { o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } - else { o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } - -static inline int template_callback_int16(unpack_user* u, int16_t d, msgpack_object* o) -{ if(d >= 0) { o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } - else { o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } - -static inline int template_callback_int32(unpack_user* u, int32_t d, msgpack_object* o) -{ if(d >= 0) { o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } - else { o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } - -static inline int template_callback_int64(unpack_user* u, int64_t d, msgpack_object* o) -{ if(d >= 0) { o->type = MSGPACK_OBJECT_POSITIVE_INTEGER; o->via.u64 = d; return 0; } - else { o->type = MSGPACK_OBJECT_NEGATIVE_INTEGER; o->via.i64 = d; return 0; } } - -static inline int template_callback_float(unpack_user* u, float d, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_DOUBLE; o->via.dec = d; return 0; } - -static inline int template_callback_double(unpack_user* u, double d, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_DOUBLE; o->via.dec = d; return 0; } - -static inline int template_callback_nil(unpack_user* u, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_NIL; return 0; } - -static inline int template_callback_true(unpack_user* u, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_BOOLEAN; o->via.boolean = true; return 0; } - -static inline int template_callback_false(unpack_user* u, msgpack_object* o) -{ o->type = MSGPACK_OBJECT_BOOLEAN; o->via.boolean = false; return 0; } - -static inline int template_callback_array(unpack_user* u, unsigned int n, msgpack_object* o) -{ - o->type = MSGPACK_OBJECT_ARRAY; - o->via.array.size = 0; - o->via.array.ptr = (msgpack_object*)msgpack_zone_malloc(u->z, n*sizeof(msgpack_object)); - if(o->via.array.ptr == NULL) { return -1; } - return 0; -} - -static inline int template_callback_array_item(unpack_user* u, msgpack_object* c, msgpack_object o) -{ c->via.array.ptr[c->via.array.size++] = o; return 0; } - -static inline int template_callback_map(unpack_user* u, unsigned int n, msgpack_object* o) -{ - o->type = MSGPACK_OBJECT_MAP; - o->via.map.size = 0; - o->via.map.ptr = (msgpack_object_kv*)msgpack_zone_malloc(u->z, n*sizeof(msgpack_object_kv)); - if(o->via.map.ptr == NULL) { return -1; } - return 0; -} - -static inline int template_callback_map_item(unpack_user* u, msgpack_object* c, msgpack_object k, msgpack_object v) -{ - c->via.map.ptr[c->via.map.size].key = k; - c->via.map.ptr[c->via.map.size].val = v; - ++c->via.map.size; - return 0; -} - -static inline int template_callback_raw(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_object* o) -{ - o->type = MSGPACK_OBJECT_RAW; - o->via.raw.ptr = p; - o->via.raw.size = l; - u->referenced = true; - return 0; -} - -#include "msgpack/unpack_template.h" - - -#define CTX_CAST(m) ((template_context*)(m)) -#define CTX_REFERENCED(mpac) CTX_CAST((mpac)->ctx)->user.referenced - -#define COUNTER_SIZE (sizeof(_msgpack_atomic_counter_t)) - - -static inline void init_count(void* buffer) -{ - *(volatile _msgpack_atomic_counter_t*)buffer = 1; -} - -static inline void decl_count(void* buffer) -{ - // atomic if(--*(_msgpack_atomic_counter_t*)buffer == 0) { free(buffer); } - if(_msgpack_sync_decr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer) == 0) { - free(buffer); - } -} - -static inline void incr_count(void* buffer) -{ - // atomic ++*(_msgpack_atomic_counter_t*)buffer; - _msgpack_sync_incr_and_fetch((volatile _msgpack_atomic_counter_t*)buffer); -} - -static inline _msgpack_atomic_counter_t get_count(void* buffer) -{ - return *(volatile _msgpack_atomic_counter_t*)buffer; -} - - - -bool msgpack_unpacker_init(msgpack_unpacker* mpac, size_t initial_buffer_size) -{ - if(initial_buffer_size < COUNTER_SIZE) { - initial_buffer_size = COUNTER_SIZE; - } - - char* buffer = (char*)malloc(initial_buffer_size); - if(buffer == NULL) { - return false; - } - - void* ctx = malloc(sizeof(template_context)); - if(ctx == NULL) { - free(buffer); - return false; - } - - msgpack_zone* z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); - if(z == NULL) { - free(ctx); - free(buffer); - return false; - } - - mpac->buffer = buffer; - mpac->used = COUNTER_SIZE; - mpac->free = initial_buffer_size - mpac->used; - mpac->off = COUNTER_SIZE; - mpac->parsed = 0; - mpac->initial_buffer_size = initial_buffer_size; - mpac->z = z; - mpac->ctx = ctx; - - init_count(mpac->buffer); - - template_init(CTX_CAST(mpac->ctx)); - CTX_CAST(mpac->ctx)->user.z = mpac->z; - CTX_CAST(mpac->ctx)->user.referenced = false; - - return true; -} - -void msgpack_unpacker_destroy(msgpack_unpacker* mpac) -{ - msgpack_zone_free(mpac->z); - free(mpac->ctx); - decl_count(mpac->buffer); -} - - -msgpack_unpacker* msgpack_unpacker_new(size_t initial_buffer_size) -{ - msgpack_unpacker* mpac = (msgpack_unpacker*)malloc(sizeof(msgpack_unpacker)); - if(mpac == NULL) { - return NULL; - } - - if(!msgpack_unpacker_init(mpac, initial_buffer_size)) { - free(mpac); - return NULL; - } - - return mpac; -} - -void msgpack_unpacker_free(msgpack_unpacker* mpac) -{ - msgpack_unpacker_destroy(mpac); - free(mpac); -} - -bool msgpack_unpacker_expand_buffer(msgpack_unpacker* mpac, size_t size) -{ - if(mpac->used == mpac->off && get_count(mpac->buffer) == 1 - && !CTX_REFERENCED(mpac)) { - // rewind buffer - mpac->free += mpac->used - COUNTER_SIZE; - mpac->used = COUNTER_SIZE; - mpac->off = COUNTER_SIZE; - - if(mpac->free >= size) { - return true; - } - } - - if(mpac->off == COUNTER_SIZE) { - size_t next_size = (mpac->used + mpac->free) * 2; // include COUNTER_SIZE - while(next_size < size + mpac->used) { - next_size *= 2; - } - - char* tmp = (char*)realloc(mpac->buffer, next_size); - if(tmp == NULL) { - return false; - } - - mpac->buffer = tmp; - mpac->free = next_size - mpac->used; - - } else { - size_t next_size = mpac->initial_buffer_size; // include COUNTER_SIZE - size_t not_parsed = mpac->used - mpac->off; - while(next_size < size + not_parsed + COUNTER_SIZE) { - next_size *= 2; - } - - char* tmp = (char*)malloc(next_size); - if(tmp == NULL) { - return false; - } - - init_count(tmp); - - memcpy(tmp+COUNTER_SIZE, mpac->buffer+mpac->off, not_parsed); - - if(CTX_REFERENCED(mpac)) { - if(!msgpack_zone_push_finalizer(mpac->z, decl_count, mpac->buffer)) { - free(tmp); - return false; - } - CTX_REFERENCED(mpac) = false; - } else { - decl_count(mpac->buffer); - } - - mpac->buffer = tmp; - mpac->used = not_parsed + COUNTER_SIZE; - mpac->free = next_size - mpac->used; - mpac->off = COUNTER_SIZE; - } - - return true; -} - -int msgpack_unpacker_execute(msgpack_unpacker* mpac) -{ - size_t off = mpac->off; - int ret = template_execute(CTX_CAST(mpac->ctx), - mpac->buffer, mpac->used, &mpac->off); - if(mpac->off > off) { - mpac->parsed += mpac->off - off; - } - return ret; -} - -msgpack_object msgpack_unpacker_data(msgpack_unpacker* mpac) -{ - return template_data(CTX_CAST(mpac->ctx)); -} - -msgpack_zone* msgpack_unpacker_release_zone(msgpack_unpacker* mpac) -{ - if(!msgpack_unpacker_flush_zone(mpac)) { - return NULL; - } - - msgpack_zone* r = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); - if(r == NULL) { - return NULL; - } - - msgpack_zone* old = mpac->z; - mpac->z = r; - CTX_CAST(mpac->ctx)->user.z = mpac->z; - - return old; -} - -void msgpack_unpacker_reset_zone(msgpack_unpacker* mpac) -{ - msgpack_zone_clear(mpac->z); -} - -bool msgpack_unpacker_flush_zone(msgpack_unpacker* mpac) -{ - if(CTX_REFERENCED(mpac)) { - if(!msgpack_zone_push_finalizer(mpac->z, decl_count, mpac->buffer)) { - return false; - } - CTX_REFERENCED(mpac) = false; - - incr_count(mpac->buffer); - } - - return true; -} - -void msgpack_unpacker_reset(msgpack_unpacker* mpac) -{ - template_init(CTX_CAST(mpac->ctx)); - // don't reset referenced flag - mpac->parsed = 0; -} - -bool msgpack_unpacker_next(msgpack_unpacker* mpac, msgpack_unpacked* result) -{ - if(result->zone != NULL) { - msgpack_zone_free(result->zone); - } - - int ret = msgpack_unpacker_execute(mpac); - - if(ret <= 0) { - result->zone = NULL; - memset(&result->data, 0, sizeof(msgpack_object)); - return false; - } - - result->zone = msgpack_unpacker_release_zone(mpac); - result->data = msgpack_unpacker_data(mpac); - msgpack_unpacker_reset(mpac); - - return true; -} - - -msgpack_unpack_return -msgpack_unpack(const char* data, size_t len, size_t* off, - msgpack_zone* result_zone, msgpack_object* result) -{ - size_t noff = 0; - if(off != NULL) { noff = *off; } - - if(len <= noff) { - // FIXME - return MSGPACK_UNPACK_CONTINUE; - } - - template_context ctx; - template_init(&ctx); - - ctx.user.z = result_zone; - ctx.user.referenced = false; - - int e = template_execute(&ctx, data, len, &noff); - if(e < 0) { - return MSGPACK_UNPACK_PARSE_ERROR; - } - - if(off != NULL) { *off = noff; } - - if(e == 0) { - return MSGPACK_UNPACK_CONTINUE; - } - - *result = template_data(&ctx); - - if(noff < len) { - return MSGPACK_UNPACK_EXTRA_BYTES; - } - - return MSGPACK_UNPACK_SUCCESS; -} - -bool msgpack_unpack_next(msgpack_unpacked* result, - const char* data, size_t len, size_t* off) -{ - msgpack_unpacked_destroy(result); - - size_t noff = 0; - if(off != NULL) { noff = *off; } - - if(len <= noff) { - return false; - } - - msgpack_zone* z = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE); - - template_context ctx; - template_init(&ctx); - - ctx.user.z = z; - ctx.user.referenced = false; - - int e = template_execute(&ctx, data, len, &noff); - if(e <= 0) { - msgpack_zone_free(z); - return false; - } - - if(off != NULL) { *off = noff; } - - result->zone = z; - result->data = template_data(&ctx); - - return true; -} - -// FIXME: Dirty hack to avoid a bus error caused by OS X's old gcc. -static void dummy_function_to_avoid_bus_error() -{ -} diff --git a/msgpack/src/version.c b/msgpack/src/version.c deleted file mode 100644 index 3d956f18..00000000 --- a/msgpack/src/version.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "msgpack.h" - -const char* msgpack_version(void) -{ - return MSGPACK_VERSION; -} - -int msgpack_version_major(void) -{ - return MSGPACK_VERSION_MAJOR; -} - -int msgpack_version_minor(void) -{ - return MSGPACK_VERSION_MINOR; -} - diff --git a/msgpack/src/vrefbuffer.c b/msgpack/src/vrefbuffer.c deleted file mode 100644 index a27b138e..00000000 --- a/msgpack/src/vrefbuffer.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * MessagePack for C zero-copy buffer implementation - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "msgpack/vrefbuffer.h" -#include -#include - -struct msgpack_vrefbuffer_chunk { - struct msgpack_vrefbuffer_chunk* next; - /* data ... */ -}; - -bool msgpack_vrefbuffer_init(msgpack_vrefbuffer* vbuf, - size_t ref_size, size_t chunk_size) -{ - vbuf->chunk_size = chunk_size; - vbuf->ref_size = ref_size; - - size_t nfirst = (sizeof(struct iovec) < 72/2) ? - 72 / sizeof(struct iovec) : 8; - - struct iovec* array = (struct iovec*)malloc( - sizeof(struct iovec) * nfirst); - if(array == NULL) { - return false; - } - - vbuf->tail = array; - vbuf->end = array + nfirst; - vbuf->array = array; - - msgpack_vrefbuffer_chunk* chunk = (msgpack_vrefbuffer_chunk*)malloc( - sizeof(msgpack_vrefbuffer_chunk) + chunk_size); - if(chunk == NULL) { - free(array); - return false; - } - - msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; - - ib->free = chunk_size; - ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); - ib->head = chunk; - chunk->next = NULL; - - return true; -} - -void msgpack_vrefbuffer_destroy(msgpack_vrefbuffer* vbuf) -{ - msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head; - while(true) { - msgpack_vrefbuffer_chunk* n = c->next; - free(c); - if(n != NULL) { - c = n; - } else { - break; - } - } - free(vbuf->array); -} - -void msgpack_vrefbuffer_clear(msgpack_vrefbuffer* vbuf) -{ - msgpack_vrefbuffer_chunk* c = vbuf->inner_buffer.head->next; - msgpack_vrefbuffer_chunk* n; - while(c != NULL) { - n = c->next; - free(c); - c = n; - } - - msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; - msgpack_vrefbuffer_chunk* chunk = ib->head; - chunk->next = NULL; - ib->free = vbuf->chunk_size; - ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); - - vbuf->tail = vbuf->array; -} - -int msgpack_vrefbuffer_append_ref(msgpack_vrefbuffer* vbuf, - const char* buf, unsigned int len) -{ - if(vbuf->tail == vbuf->end) { - const size_t nused = vbuf->tail - vbuf->array; - const size_t nnext = nused * 2; - - struct iovec* nvec = (struct iovec*)realloc( - vbuf->array, sizeof(struct iovec)*nnext); - if(nvec == NULL) { - return -1; - } - - vbuf->array = nvec; - vbuf->end = nvec + nnext; - vbuf->tail = nvec + nused; - } - - vbuf->tail->iov_base = (char*)buf; - vbuf->tail->iov_len = len; - ++vbuf->tail; - - return 0; -} - -int msgpack_vrefbuffer_append_copy(msgpack_vrefbuffer* vbuf, - const char* buf, unsigned int len) -{ - msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; - - if(ib->free < len) { - size_t sz = vbuf->chunk_size; - if(sz < len) { - sz = len; - } - - msgpack_vrefbuffer_chunk* chunk = (msgpack_vrefbuffer_chunk*)malloc( - sizeof(msgpack_vrefbuffer_chunk) + sz); - if(chunk == NULL) { - return -1; - } - - chunk->next = ib->head; - ib->head = chunk; - ib->free = sz; - ib->ptr = ((char*)chunk) + sizeof(msgpack_vrefbuffer_chunk); - } - - char* m = ib->ptr; - memcpy(m, buf, len); - ib->free -= len; - ib->ptr += len; - - if(vbuf->tail != vbuf->array && m == - (const char*)((vbuf->tail-1)->iov_base) + (vbuf->tail-1)->iov_len) { - (vbuf->tail-1)->iov_len += len; - return 0; - } else { - return msgpack_vrefbuffer_append_ref(vbuf, m, len); - } -} - -int msgpack_vrefbuffer_migrate(msgpack_vrefbuffer* vbuf, msgpack_vrefbuffer* to) -{ - size_t sz = vbuf->chunk_size; - - msgpack_vrefbuffer_chunk* empty = (msgpack_vrefbuffer_chunk*)malloc( - sizeof(msgpack_vrefbuffer_chunk) + sz); - if(empty == NULL) { - return -1; - } - - empty->next = NULL; - - - const size_t nused = vbuf->tail - vbuf->array; - if(to->tail + nused < vbuf->end) { - const size_t tosize = to->tail - to->array; - const size_t reqsize = nused + tosize; - size_t nnext = (to->end - to->array) * 2; - while(nnext < reqsize) { - nnext *= 2; - } - - struct iovec* nvec = (struct iovec*)realloc( - to->array, sizeof(struct iovec)*nnext); - if(nvec == NULL) { - free(empty); - return -1; - } - - to->array = nvec; - to->end = nvec + nnext; - to->tail = nvec + tosize; - } - - memcpy(to->tail, vbuf->array, sizeof(struct iovec)*nused); - - to->tail += nused; - vbuf->tail = vbuf->array; - - - msgpack_vrefbuffer_inner_buffer* const ib = &vbuf->inner_buffer; - msgpack_vrefbuffer_inner_buffer* const toib = &to->inner_buffer; - - msgpack_vrefbuffer_chunk* last = ib->head; - while(last->next != NULL) { - last = last->next; - } - last->next = toib->head; - toib->head = ib->head; - - if(toib->free < ib->free) { - toib->free = ib->free; - toib->ptr = ib->ptr; - } - - ib->head = empty; - ib->free = sz; - ib->ptr = ((char*)empty) + sizeof(msgpack_vrefbuffer_chunk); - - return 0; -} - diff --git a/msgpack/src/zone.c b/msgpack/src/zone.c deleted file mode 100644 index 8cc8b0de..00000000 --- a/msgpack/src/zone.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * MessagePack for C memory pool implementation - * - * Copyright (C) 2008-2009 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "msgpack/zone.h" -#include -#include - -struct msgpack_zone_chunk { - struct msgpack_zone_chunk* next; - /* data ... */ -}; - -static inline bool init_chunk_list(msgpack_zone_chunk_list* cl, size_t chunk_size) -{ - msgpack_zone_chunk* chunk = (msgpack_zone_chunk*)malloc( - sizeof(msgpack_zone_chunk) + chunk_size); - if(chunk == NULL) { - return false; - } - - cl->head = chunk; - cl->free = chunk_size; - cl->ptr = ((char*)chunk) + sizeof(msgpack_zone_chunk); - chunk->next = NULL; - - return true; -} - -static inline void destroy_chunk_list(msgpack_zone_chunk_list* cl) -{ - msgpack_zone_chunk* c = cl->head; - while(true) { - msgpack_zone_chunk* n = c->next; - free(c); - if(n != NULL) { - c = n; - } else { - break; - } - } -} - -static inline void clear_chunk_list(msgpack_zone_chunk_list* cl, size_t chunk_size) -{ - msgpack_zone_chunk* c = cl->head; - while(true) { - msgpack_zone_chunk* n = c->next; - if(n != NULL) { - free(c); - c = n; - } else { - break; - } - } - cl->head->next = NULL; - cl->free = chunk_size; - cl->ptr = ((char*)cl->head) + sizeof(msgpack_zone_chunk); -} - -void* msgpack_zone_malloc_expand(msgpack_zone* zone, size_t size) -{ - msgpack_zone_chunk_list* const cl = &zone->chunk_list; - - size_t sz = zone->chunk_size; - - while(sz < size) { - sz *= 2; - } - - msgpack_zone_chunk* chunk = (msgpack_zone_chunk*)malloc( - sizeof(msgpack_zone_chunk) + sz); - - char* ptr = ((char*)chunk) + sizeof(msgpack_zone_chunk); - - chunk->next = cl->head; - cl->head = chunk; - cl->free = sz - size; - cl->ptr = ptr + size; - - return ptr; -} - - -static inline void init_finalizer_array(msgpack_zone_finalizer_array* fa) -{ - fa->tail = NULL; - fa->end = NULL; - fa->array = NULL; -} - -static inline void call_finalizer_array(msgpack_zone_finalizer_array* fa) -{ - msgpack_zone_finalizer* fin = fa->tail; - for(; fin != fa->array; --fin) { - (*(fin-1)->func)((fin-1)->data); - } -} - -static inline void destroy_finalizer_array(msgpack_zone_finalizer_array* fa) -{ - call_finalizer_array(fa); - free(fa->array); -} - -static inline void clear_finalizer_array(msgpack_zone_finalizer_array* fa) -{ - call_finalizer_array(fa); - fa->tail = fa->array; -} - -bool msgpack_zone_push_finalizer_expand(msgpack_zone* zone, - void (*func)(void* data), void* data) -{ - msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; - - const size_t nused = fa->end - fa->array; - - size_t nnext; - if(nused == 0) { - nnext = (sizeof(msgpack_zone_finalizer) < 72/2) ? - 72 / sizeof(msgpack_zone_finalizer) : 8; - - } else { - nnext = nused * 2; - } - - msgpack_zone_finalizer* tmp = - (msgpack_zone_finalizer*)realloc(fa->array, - sizeof(msgpack_zone_finalizer) * nnext); - if(tmp == NULL) { - return false; - } - - fa->array = tmp; - fa->end = tmp + nnext; - fa->tail = tmp + nused; - - fa->tail->func = func; - fa->tail->data = data; - - ++fa->tail; - - return true; -} - - -bool msgpack_zone_is_empty(msgpack_zone* zone) -{ - msgpack_zone_chunk_list* const cl = &zone->chunk_list; - msgpack_zone_finalizer_array* const fa = &zone->finalizer_array; - return cl->free == zone->chunk_size && cl->head->next == NULL && - fa->tail == fa->array; -} - - -void msgpack_zone_destroy(msgpack_zone* zone) -{ - destroy_finalizer_array(&zone->finalizer_array); - destroy_chunk_list(&zone->chunk_list); -} - -void msgpack_zone_clear(msgpack_zone* zone) -{ - clear_finalizer_array(&zone->finalizer_array); - clear_chunk_list(&zone->chunk_list, zone->chunk_size); -} - -bool msgpack_zone_init(msgpack_zone* zone, size_t chunk_size) -{ - zone->chunk_size = chunk_size; - - if(!init_chunk_list(&zone->chunk_list, chunk_size)) { - return false; - } - - init_finalizer_array(&zone->finalizer_array); - - return true; -} - -msgpack_zone* msgpack_zone_new(size_t chunk_size) -{ - msgpack_zone* zone = (msgpack_zone*)malloc( - sizeof(msgpack_zone) + chunk_size); - if(zone == NULL) { - return NULL; - } - - zone->chunk_size = chunk_size; - - if(!init_chunk_list(&zone->chunk_list, chunk_size)) { - free(zone); - return NULL; - } - - init_finalizer_array(&zone->finalizer_array); - - return zone; -} - -void msgpack_zone_free(msgpack_zone* zone) -{ - if(zone == NULL) { return; } - msgpack_zone_destroy(zone); - free(zone); -} - diff --git a/msgpack/sysdep.h b/msgpack/sysdep.h deleted file mode 100644 index 4fedbd8b..00000000 --- a/msgpack/sysdep.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * MessagePack system dependencies - * - * Copyright (C) 2008-2010 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_SYSDEP_H__ -#define MSGPACK_SYSDEP_H__ - -#include -#include -#if defined(_MSC_VER) && _MSC_VER < 1600 -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#elif defined(_MSC_VER) // && _MSC_VER >= 1600 -#include -#else -#include -#include -#endif - -#ifdef _WIN32 -#define _msgpack_atomic_counter_header -typedef long _msgpack_atomic_counter_t; -#define _msgpack_sync_decr_and_fetch(ptr) InterlockedDecrement(ptr) -#define _msgpack_sync_incr_and_fetch(ptr) InterlockedIncrement(ptr) -#elif defined(__GNUC__) && ((__GNUC__*10 + __GNUC_MINOR__) < 41) -#define _msgpack_atomic_counter_header "gcc_atomic.h" -#else -typedef unsigned int _msgpack_atomic_counter_t; -#define _msgpack_sync_decr_and_fetch(ptr) __sync_sub_and_fetch(ptr, 1) -#define _msgpack_sync_incr_and_fetch(ptr) __sync_add_and_fetch(ptr, 1) -#endif - -#ifdef _WIN32 - -#ifdef __cplusplus -/* numeric_limits::min,max */ -#ifdef max -#undef max -#endif -#ifdef min -#undef min -#endif -#endif - -#else -#include /* __BYTE_ORDER */ -#endif - -#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define __LITTLE_ENDIAN__ -#elif __BYTE_ORDER == __BIG_ENDIAN -#define __BIG_ENDIAN__ -#elif _WIN32 -#define __LITTLE_ENDIAN__ -#endif -#endif - - -#ifdef __LITTLE_ENDIAN__ - -#ifdef _WIN32 -# if defined(ntohs) -# define _msgpack_be16(x) ntohs(x) -# elif defined(_byteswap_ushort) || (defined(_MSC_VER) && _MSC_VER >= 1400) -# define _msgpack_be16(x) ((uint16_t)_byteswap_ushort((unsigned short)x)) -# else -# define _msgpack_be16(x) ( \ - ((((uint16_t)x) << 8) ) | \ - ((((uint16_t)x) >> 8) ) ) -# endif -#else -# define _msgpack_be16(x) ntohs(x) -#endif - -#ifdef _WIN32 -# if defined(ntohl) -# define _msgpack_be32(x) ntohl(x) -# elif defined(_byteswap_ulong) || (defined(_MSC_VER) && _MSC_VER >= 1400) -# define _msgpack_be32(x) ((uint32_t)_byteswap_ulong((unsigned long)x)) -# else -# define _msgpack_be32(x) \ - ( ((((uint32_t)x) << 24) ) | \ - ((((uint32_t)x) << 8) & 0x00ff0000U ) | \ - ((((uint32_t)x) >> 8) & 0x0000ff00U ) | \ - ((((uint32_t)x) >> 24) ) ) -# endif -#else -# define _msgpack_be32(x) ntohl(x) -#endif - -#if defined(_byteswap_uint64) || (defined(_MSC_VER) && _MSC_VER >= 1400) -# define _msgpack_be64(x) (_byteswap_uint64(x)) -#elif defined(bswap_64) -# define _msgpack_be64(x) bswap_64(x) -#elif defined(__DARWIN_OSSwapInt64) -# define _msgpack_be64(x) __DARWIN_OSSwapInt64(x) -#else -#define _msgpack_be64(x) \ - ( ((((uint64_t)x) << 56) ) | \ - ((((uint64_t)x) << 40) & 0x00ff000000000000ULL ) | \ - ((((uint64_t)x) << 24) & 0x0000ff0000000000ULL ) | \ - ((((uint64_t)x) << 8) & 0x000000ff00000000ULL ) | \ - ((((uint64_t)x) >> 8) & 0x00000000ff000000ULL ) | \ - ((((uint64_t)x) >> 24) & 0x0000000000ff0000ULL ) | \ - ((((uint64_t)x) >> 40) & 0x000000000000ff00ULL ) | \ - ((((uint64_t)x) >> 56) ) ) -#endif - -#define _msgpack_load16(cast, from) ((cast)( \ - (((uint16_t)((uint8_t*)(from))[0]) << 8) | \ - (((uint16_t)((uint8_t*)(from))[1]) ) )) - -#define _msgpack_load32(cast, from) ((cast)( \ - (((uint32_t)((uint8_t*)(from))[0]) << 24) | \ - (((uint32_t)((uint8_t*)(from))[1]) << 16) | \ - (((uint32_t)((uint8_t*)(from))[2]) << 8) | \ - (((uint32_t)((uint8_t*)(from))[3]) ) )) - -#define _msgpack_load64(cast, from) ((cast)( \ - (((uint64_t)((uint8_t*)(from))[0]) << 56) | \ - (((uint64_t)((uint8_t*)(from))[1]) << 48) | \ - (((uint64_t)((uint8_t*)(from))[2]) << 40) | \ - (((uint64_t)((uint8_t*)(from))[3]) << 32) | \ - (((uint64_t)((uint8_t*)(from))[4]) << 24) | \ - (((uint64_t)((uint8_t*)(from))[5]) << 16) | \ - (((uint64_t)((uint8_t*)(from))[6]) << 8) | \ - (((uint64_t)((uint8_t*)(from))[7]) ) )) - -#else - -#define _msgpack_be16(x) (x) -#define _msgpack_be32(x) (x) -#define _msgpack_be64(x) (x) - -#define _msgpack_load16(cast, from) ((cast)( \ - (((uint16_t)((uint8_t*)from)[0]) << 8) | \ - (((uint16_t)((uint8_t*)from)[1]) ) )) - -#define _msgpack_load32(cast, from) ((cast)( \ - (((uint32_t)((uint8_t*)from)[0]) << 24) | \ - (((uint32_t)((uint8_t*)from)[1]) << 16) | \ - (((uint32_t)((uint8_t*)from)[2]) << 8) | \ - (((uint32_t)((uint8_t*)from)[3]) ) )) - -#define _msgpack_load64(cast, from) ((cast)( \ - (((uint64_t)((uint8_t*)from)[0]) << 56) | \ - (((uint64_t)((uint8_t*)from)[1]) << 48) | \ - (((uint64_t)((uint8_t*)from)[2]) << 40) | \ - (((uint64_t)((uint8_t*)from)[3]) << 32) | \ - (((uint64_t)((uint8_t*)from)[4]) << 24) | \ - (((uint64_t)((uint8_t*)from)[5]) << 16) | \ - (((uint64_t)((uint8_t*)from)[6]) << 8) | \ - (((uint64_t)((uint8_t*)from)[7]) ) )) -#endif - - -#define _msgpack_store16(to, num) \ - do { uint16_t val = _msgpack_be16(num); memcpy(to, &val, 2); } while(0) -#define _msgpack_store32(to, num) \ - do { uint32_t val = _msgpack_be32(num); memcpy(to, &val, 4); } while(0) -#define _msgpack_store64(to, num) \ - do { uint64_t val = _msgpack_be64(num); memcpy(to, &val, 8); } while(0) - -/* -#define _msgpack_load16(cast, from) \ - ({ cast val; memcpy(&val, (char*)from, 2); _msgpack_be16(val); }) -#define _msgpack_load32(cast, from) \ - ({ cast val; memcpy(&val, (char*)from, 4); _msgpack_be32(val); }) -#define _msgpack_load64(cast, from) \ - ({ cast val; memcpy(&val, (char*)from, 8); _msgpack_be64(val); }) -*/ - - -#endif /* msgpack/sysdep.h */ - diff --git a/msgpack/test/Makefile.am b/msgpack/test/Makefile.am deleted file mode 100644 index af56d3be..00000000 --- a/msgpack/test/Makefile.am +++ /dev/null @@ -1,54 +0,0 @@ - -AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src -AM_C_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src -AM_LDFLAGS = $(top_builddir)/src/libmsgpack.la -lgtest_main -pthread - -check_PROGRAMS = \ - zone \ - pack_unpack \ - pack_unpack_c \ - streaming \ - streaming_c \ - object \ - convert \ - buffer \ - cases \ - fixint \ - fixint_c \ - version \ - msgpackc_test \ - msgpack_test - -TESTS = $(check_PROGRAMS) - -zone_SOURCES = zone.cc - -pack_unpack_SOURCES = pack_unpack.cc - -pack_unpack_c_SOURCES = pack_unpack_c.cc - -streaming_SOURCES = streaming.cc - -streaming_c_SOURCES = streaming_c.cc - -object_SOURCES = object.cc - -convert_SOURCES = convert.cc - -buffer_SOURCES = buffer.cc -buffer_LDADD = -lz - -cases_SOURCES = cases.cc - -fixint_SOURCES = fixint.cc - -fixint_c_SOURCES = fixint_c.cc - -version_SOURCES = version.cc - -msgpackc_test_SOURCES = msgpackc_test.cpp - -msgpack_test_SOURCES = msgpack_test.cpp - -EXTRA_DIST = cases.mpac cases_compact.mpac - diff --git a/msgpack/test/buffer.cc b/msgpack/test/buffer.cc deleted file mode 100644 index 0b66f4e2..00000000 --- a/msgpack/test/buffer.cc +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include -#include - -TEST(buffer, sbuffer) -{ - msgpack::sbuffer sbuf; - sbuf.write("a", 1); - sbuf.write("a", 1); - sbuf.write("a", 1); - - EXPECT_EQ(3, sbuf.size()); - EXPECT_TRUE( memcmp(sbuf.data(), "aaa", 3) == 0 ); - - sbuf.clear(); - sbuf.write("a", 1); - sbuf.write("a", 1); - sbuf.write("a", 1); - - EXPECT_EQ(3, sbuf.size()); - EXPECT_TRUE( memcmp(sbuf.data(), "aaa", 3) == 0 ); -} - - -TEST(buffer, vrefbuffer) -{ - msgpack::vrefbuffer vbuf; - vbuf.write("a", 1); - vbuf.write("a", 1); - vbuf.write("a", 1); - - const struct iovec* vec = vbuf.vector(); - size_t veclen = vbuf.vector_size(); - - msgpack::sbuffer sbuf; - for(size_t i=0; i < veclen; ++i) { - sbuf.write((const char*)vec[i].iov_base, vec[i].iov_len); - } - - EXPECT_EQ(3, sbuf.size()); - EXPECT_TRUE( memcmp(sbuf.data(), "aaa", 3) == 0 ); - - - vbuf.clear(); - vbuf.write("a", 1); - vbuf.write("a", 1); - vbuf.write("a", 1); - - vec = vbuf.vector(); - veclen = vbuf.vector_size(); - - sbuf.clear(); - for(size_t i=0; i < veclen; ++i) { - sbuf.write((const char*)vec[i].iov_base, vec[i].iov_len); - } - - EXPECT_EQ(3, sbuf.size()); - EXPECT_TRUE( memcmp(sbuf.data(), "aaa", 3) == 0 ); -} - - -TEST(buffer, zbuffer) -{ - msgpack::zbuffer zbuf; - zbuf.write("a", 1); - zbuf.write("a", 1); - zbuf.write("a", 1); - - zbuf.flush(); -} - diff --git a/msgpack/test/cases.cc b/msgpack/test/cases.cc deleted file mode 100644 index eb1286c7..00000000 --- a/msgpack/test/cases.cc +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include - -static void feed_file(msgpack::unpacker& pac, const char* path) -{ - std::ifstream fin(path); - while(true) { - pac.reserve_buffer(32*1024); - fin.read(pac.buffer(), pac.buffer_capacity()); - if(fin.bad()) { - throw std::runtime_error("read failed"); - } - pac.buffer_consumed(fin.gcount()); - if(fin.fail()) { - break; - } - } -} - -TEST(cases, format) -{ - msgpack::unpacker pac; - msgpack::unpacker pac_compact; - - feed_file(pac, "cases.mpac"); - feed_file(pac_compact, "cases_compact.mpac"); - - msgpack::unpacked result; - while(pac.next(&result)) { - msgpack::unpacked result_compact; - EXPECT_TRUE( pac_compact.next(&result_compact) ); - EXPECT_EQ(result_compact.get(), result.get()); - } - - EXPECT_FALSE( pac_compact.next(&result) ); -} - diff --git a/msgpack/test/convert.cc b/msgpack/test/convert.cc deleted file mode 100644 index f579f33a..00000000 --- a/msgpack/test/convert.cc +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include - -class compatibility { -public: - compatibility() : str1("default"), str2("default") { } - - std::string str1; - std::string str2; - - MSGPACK_DEFINE(str1, str2); -}; - -TEST(convert, compatibility_less) -{ - std::vector src(1); - src[0] = "kumofs"; - - msgpack::zone z; - msgpack::object obj(src, &z); - - compatibility c; - EXPECT_NO_THROW( obj.convert(&c) ); - - EXPECT_EQ("kumofs", c.str1); - EXPECT_EQ("default", c.str2); -} - -TEST(convert, compatibility_more) -{ - std::vector src(3); - src[0] = "kumofs"; - src[1] = "mpio"; - src[2] = "cloudy"; - - msgpack::zone z; - msgpack::object obj(src, &z); - - compatibility to; - EXPECT_NO_THROW( obj.convert(&to) ); - - EXPECT_EQ("kumofs", to.str1); - EXPECT_EQ("mpio", to.str2); -} - - -class enum_member { -public: - enum_member() : flag(A) { } - - enum flags_t { - A = 0, - B = 1, - }; - - flags_t flag; - - MSGPACK_DEFINE(flag); -}; - -MSGPACK_ADD_ENUM(enum_member::flags_t); - -TEST(convert, enum_member) -{ - enum_member src; - src.flag = enum_member::B; - - msgpack::zone z; - msgpack::object obj(src, &z); - - enum_member to; - EXPECT_NO_THROW( obj.convert(&to) ); - - EXPECT_EQ(enum_member::B, to.flag); -} - diff --git a/msgpack/test/fixint.cc b/msgpack/test/fixint.cc deleted file mode 100644 index 63288a1b..00000000 --- a/msgpack/test/fixint.cc +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include - -template -void check_size(size_t size) { - T v(0); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, v); - EXPECT_EQ(size, sbuf.size()); -} - -TEST(fixint, size) -{ - check_size(2); - check_size(3); - check_size(5); - check_size(9); - - check_size(2); - check_size(3); - check_size(5); - check_size(9); -} - - -template -void check_convert() { - T v1(-11); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, v1); - - msgpack::unpacked msg; - msgpack::unpack(&msg, sbuf.data(), sbuf.size()); - - T v2; - msg.get().convert(&v2); - - EXPECT_EQ(v1.get(), v2.get()); - - EXPECT_EQ(msg.get(), msgpack::object(T(v1.get()))); -} - -TEST(fixint, convert) -{ - check_convert(); - check_convert(); - check_convert(); - check_convert(); - - check_convert(); - check_convert(); - check_convert(); - check_convert(); -} - diff --git a/msgpack/test/fixint_c.cc b/msgpack/test/fixint_c.cc deleted file mode 100644 index caa4d262..00000000 --- a/msgpack/test/fixint_c.cc +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include - -TEST(fixint, size) -{ - msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); - msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); - - size_t sum = 0; - - EXPECT_EQ(0, msgpack_pack_fix_int8(pk, 0)); - EXPECT_EQ(sum+=2, sbuf->size); - EXPECT_EQ(0, msgpack_pack_fix_int16(pk, 0)); - EXPECT_EQ(sum+=3, sbuf->size); - EXPECT_EQ(0, msgpack_pack_fix_int32(pk, 0)); - EXPECT_EQ(sum+=5, sbuf->size); - EXPECT_EQ(0, msgpack_pack_fix_int64(pk, 0)); - EXPECT_EQ(sum+=9, sbuf->size); - - EXPECT_EQ(0, msgpack_pack_fix_uint8(pk, 0)); - EXPECT_EQ(sum+=2, sbuf->size); - EXPECT_EQ(0, msgpack_pack_fix_uint16(pk, 0)); - EXPECT_EQ(sum+=3, sbuf->size); - EXPECT_EQ(0, msgpack_pack_fix_uint32(pk, 0)); - EXPECT_EQ(sum+=5, sbuf->size); - EXPECT_EQ(0, msgpack_pack_fix_uint64(pk, 0)); - EXPECT_EQ(sum+=9, sbuf->size); - - msgpack_sbuffer_free(sbuf); - msgpack_packer_free(pk); -} - diff --git a/msgpack/test/msgpack_test.cpp b/msgpack/test/msgpack_test.cpp deleted file mode 100644 index 0dd0ffc6..00000000 --- a/msgpack/test/msgpack_test.cpp +++ /dev/null @@ -1,982 +0,0 @@ -#include "msgpack.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -using namespace std; - -const unsigned int kLoop = 10000; -const unsigned int kElements = 100; -const double kEPS = 1e-10; - -#define GEN_TEST(test_type) \ - do { \ - vector v; \ - v.push_back(0); \ - v.push_back(1); \ - v.push_back(2); \ - v.push_back(numeric_limits::min()); \ - v.push_back(numeric_limits::max()); \ - for (unsigned int i = 0; i < kLoop; i++) \ - v.push_back(rand()); \ - for (unsigned int i = 0; i < v.size() ; i++) { \ - msgpack::sbuffer sbuf; \ - test_type val1 = v[i]; \ - msgpack::pack(sbuf, val1); \ - msgpack::zone z; \ - msgpack::object obj; \ - msgpack::unpack_return ret = \ - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); \ - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); \ - test_type val2; \ - obj.convert(&val2); \ - EXPECT_EQ(val1, val2); \ - } \ -} while(0) - -TEST(MSGPACK, simple_buffer_short) -{ - GEN_TEST(short); -} - -TEST(MSGPACK, simple_buffer_int) -{ - GEN_TEST(int); -} - -TEST(MSGPACK, simple_buffer_long) -{ - GEN_TEST(long); -} - -TEST(MSGPACK, simple_buffer_long_long) -{ - GEN_TEST(long long); -} - -TEST(MSGPACK, simple_buffer_unsigned_short) -{ - GEN_TEST(unsigned short); -} - -TEST(MSGPACK, simple_buffer_unsigned_int) -{ - GEN_TEST(unsigned int); -} - -TEST(MSGPACK, simple_buffer_unsigned_long) -{ - GEN_TEST(unsigned long); -} - -TEST(MSGPACK, simple_buffer_unsigned_long_long) -{ - GEN_TEST(unsigned long long); -} - -TEST(MSGPACK, simple_buffer_uint8) -{ - GEN_TEST(uint8_t); -} - -TEST(MSGPACK, simple_buffer_uint16) -{ - GEN_TEST(uint16_t); -} - -TEST(MSGPACK, simple_buffer_uint32) -{ - GEN_TEST(uint32_t); -} - -TEST(MSGPACK, simple_buffer_uint64) -{ - GEN_TEST(uint64_t); -} - -TEST(MSGPACK, simple_buffer_int8) -{ - GEN_TEST(int8_t); -} - -TEST(MSGPACK, simple_buffer_int16) -{ - GEN_TEST(int16_t); -} - -TEST(MSGPACK, simple_buffer_int32) -{ - GEN_TEST(int32_t); -} - -TEST(MSGPACK, simple_buffer_int64) -{ - GEN_TEST(int64_t); -} - -TEST(MSGPACK, simple_buffer_float) -{ - vector v; - v.push_back(0.0); - v.push_back(-0.0); - v.push_back(1.0); - v.push_back(-1.0); - v.push_back(numeric_limits::min()); - v.push_back(numeric_limits::max()); - v.push_back(nanf("tag")); - v.push_back(1.0/0.0); // inf - v.push_back(-(1.0/0.0)); // -inf - for (unsigned int i = 0; i < kLoop; i++) { - v.push_back(drand48()); - v.push_back(-drand48()); - } - for (unsigned int i = 0; i < v.size() ; i++) { - msgpack::sbuffer sbuf; - float val1 = v[i]; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - float val2; - obj.convert(&val2); - - if (isnan(val1)) - EXPECT_TRUE(isnan(val2)); - else if (isinf(val1)) - EXPECT_TRUE(isinf(val2)); - else - EXPECT_TRUE(fabs(val2 - val1) <= kEPS); - } -} - -TEST(MSGPACK, simple_buffer_double) -{ - vector v; - v.push_back(0.0); - v.push_back(-0.0); - v.push_back(1.0); - v.push_back(-1.0); - v.push_back(numeric_limits::min()); - v.push_back(numeric_limits::max()); - v.push_back(nanf("tag")); - v.push_back(1.0/0.0); // inf - v.push_back(-(1.0/0.0)); // -inf - for (unsigned int i = 0; i < kLoop; i++) { - v.push_back(drand48()); - v.push_back(-drand48()); - } - for (unsigned int i = 0; i < v.size() ; i++) { - msgpack::sbuffer sbuf; - double val1 = v[i]; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - double val2; - obj.convert(&val2); - - if (isnan(val1)) - EXPECT_TRUE(isnan(val2)); - else if (isinf(val1)) - EXPECT_TRUE(isinf(val2)); - else - EXPECT_TRUE(fabs(val2 - val1) <= kEPS); - } -} - -TEST(MSGPACK, simple_buffer_true) -{ - msgpack::sbuffer sbuf; - bool val1 = true; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - bool val2; - obj.convert(&val2); - EXPECT_EQ(val1, val2); -} - -TEST(MSGPACK, simple_buffer_false) -{ - msgpack::sbuffer sbuf; - bool val1 = false; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - bool val2; - obj.convert(&val2); - EXPECT_EQ(val1, val2); -} - -//----------------------------------------------------------------------------- - -// STL - -TEST(MSGPACK_STL, simple_buffer_string) -{ - for (unsigned int k = 0; k < kLoop; k++) { - string val1; - for (unsigned int i = 0; i < kElements; i++) - val1 += 'a' + rand() % 26; - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - string val2; - obj.convert(&val2); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_EQ(val1, val2); - } -} - -TEST(MSGPACK_STL, simple_buffer_vector) -{ - for (unsigned int k = 0; k < kLoop; k++) { - vector val1; - for (unsigned int i = 0; i < kElements; i++) - val1.push_back(rand()); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - vector val2; - obj.convert(&val2); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); - } -} - -TEST(MSGPACK_STL, simple_buffer_map) -{ - for (unsigned int k = 0; k < kLoop; k++) { - map val1; - for (unsigned int i = 0; i < kElements; i++) - val1[rand()] = rand(); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - map val2; - obj.convert(&val2); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); - } -} - -TEST(MSGPACK_STL, simple_buffer_deque) -{ - for (unsigned int k = 0; k < kLoop; k++) { - deque val1; - for (unsigned int i = 0; i < kElements; i++) - val1.push_back(rand()); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - deque val2; - obj.convert(&val2); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); - } -} - -TEST(MSGPACK_STL, simple_buffer_list) -{ - for (unsigned int k = 0; k < kLoop; k++) { - list val1; - for (unsigned int i = 0; i < kElements; i++) - val1.push_back(rand()); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - list val2; - obj.convert(&val2); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); - } -} - -TEST(MSGPACK_STL, simple_buffer_set) -{ - for (unsigned int k = 0; k < kLoop; k++) { - set val1; - for (unsigned int i = 0; i < kElements; i++) - val1.insert(rand()); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - set val2; - obj.convert(&val2); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_TRUE(equal(val1.begin(), val1.end(), val2.begin())); - } -} - -TEST(MSGPACK_STL, simple_buffer_pair) -{ - for (unsigned int k = 0; k < kLoop; k++) { - pair val1 = make_pair(rand(), rand()); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - pair val2; - obj.convert(&val2); - EXPECT_EQ(val1.first, val2.first); - EXPECT_EQ(val1.second, val2.second); - } -} - -TEST(MSGPACK_STL, simple_buffer_multimap) -{ - for (unsigned int k = 0; k < kLoop; k++) { - multimap val1; - for (unsigned int i = 0; i < kElements; i++) { - int i1 = rand(); - val1.insert(make_pair(i1, rand())); - val1.insert(make_pair(i1, rand())); - } - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - multimap val2; - obj.convert(&val2); - - vector > v1, v2; - multimap::const_iterator it; - for (it = val1.begin(); it != val1.end(); ++it) - v1.push_back(make_pair(it->first, it->second)); - for (it = val2.begin(); it != val2.end(); ++it) - v2.push_back(make_pair(it->first, it->second)); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_EQ(v1.size(), v2.size()); - sort(v1.begin(), v1.end()); - sort(v2.begin(), v2.end()); - EXPECT_TRUE(v1 == v2); - } -} - -TEST(MSGPACK_STL, simple_buffer_multiset) -{ - for (unsigned int k = 0; k < kLoop; k++) { - multiset val1; - for (unsigned int i = 0; i < kElements; i++) - val1.insert(rand()); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - multiset val2; - obj.convert(&val2); - - vector v1, v2; - multiset::const_iterator it; - for (it = val1.begin(); it != val1.end(); ++it) - v1.push_back(*it); - for (it = val2.begin(); it != val2.end(); ++it) - v2.push_back(*it); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_EQ(v1.size(), v2.size()); - sort(v1.begin(), v1.end()); - sort(v2.begin(), v2.end()); - EXPECT_TRUE(v1 == v2); - } -} - -// TR1 - -#ifdef HAVE_TR1_UNORDERED_MAP -#include -#include "msgpack/type/tr1/unordered_map.hpp" -TEST(MSGPACK_TR1, simple_buffer_unordered_map) -{ - for (unsigned int k = 0; k < kLoop; k++) { - tr1::unordered_map val1; - for (unsigned int i = 0; i < kElements; i++) - val1[rand()] = rand(); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - tr1::unordered_map val2; - obj.convert(&val2); - EXPECT_EQ(val1.size(), val2.size()); - tr1::unordered_map::const_iterator it; - for (it = val1.begin(); it != val1.end(); ++it) { - EXPECT_TRUE(val2.find(it->first) != val2.end()); - EXPECT_EQ(it->second, val2.find(it->first)->second); - } - } -} - -TEST(MSGPACK_TR1, simple_buffer_unordered_multimap) -{ - for (unsigned int k = 0; k < kLoop; k++) { - tr1::unordered_multimap val1; - for (unsigned int i = 0; i < kElements; i++) { - int i1 = rand(); - val1.insert(make_pair(i1, rand())); - val1.insert(make_pair(i1, rand())); - } - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - tr1::unordered_multimap val2; - obj.convert(&val2); - - vector > v1, v2; - tr1::unordered_multimap::const_iterator it; - for (it = val1.begin(); it != val1.end(); ++it) - v1.push_back(make_pair(it->first, it->second)); - for (it = val2.begin(); it != val2.end(); ++it) - v2.push_back(make_pair(it->first, it->second)); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_EQ(v1.size(), v2.size()); - sort(v1.begin(), v1.end()); - sort(v2.begin(), v2.end()); - EXPECT_TRUE(v1 == v2); - } -} -#endif - -#ifdef HAVE_TR1_UNORDERED_SET -#include -#include "msgpack/type/tr1/unordered_set.hpp" -TEST(MSGPACK_TR1, simple_buffer_unordered_set) -{ - for (unsigned int k = 0; k < kLoop; k++) { - tr1::unordered_set val1; - for (unsigned int i = 0; i < kElements; i++) - val1.insert(rand()); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - tr1::unordered_set val2; - obj.convert(&val2); - EXPECT_EQ(val1.size(), val2.size()); - tr1::unordered_set::const_iterator it; - for (it = val1.begin(); it != val1.end(); ++it) - EXPECT_TRUE(val2.find(*it) != val2.end()); - } -} - -TEST(MSGPACK_TR1, simple_buffer_unordered_multiset) -{ - for (unsigned int k = 0; k < kLoop; k++) { - tr1::unordered_multiset val1; - for (unsigned int i = 0; i < kElements; i++) - val1.insert(rand()); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - tr1::unordered_multiset val2; - obj.convert(&val2); - - vector v1, v2; - tr1::unordered_multiset::const_iterator it; - for (it = val1.begin(); it != val1.end(); ++it) - v1.push_back(*it); - for (it = val2.begin(); it != val2.end(); ++it) - v2.push_back(*it); - EXPECT_EQ(val1.size(), val2.size()); - EXPECT_EQ(v1.size(), v2.size()); - sort(v1.begin(), v1.end()); - sort(v2.begin(), v2.end()); - EXPECT_TRUE(v1 == v2); - } -} -#endif - -// User-Defined Structures - -class TestClass -{ -public: - TestClass() : i(0), s("kzk") {} - int i; - string s; - MSGPACK_DEFINE(i, s); -}; - -TEST(MSGPACK_USER_DEFINED, simple_buffer_class) -{ - for (unsigned int k = 0; k < kLoop; k++) { - TestClass val1; - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - TestClass val2; - val2.i = -1; - val2.s = ""; - obj.convert(&val2); - EXPECT_EQ(val1.i, val2.i); - EXPECT_EQ(val1.s, val2.s); - } -} - -class TestClass2 -{ -public: - TestClass2() : i(0), s("kzk") { - for (unsigned int i = 0; i < kElements; i++) - v.push_back(rand()); - } - int i; - string s; - vector v; - MSGPACK_DEFINE(i, s, v); -}; - -TEST(MSGPACK_USER_DEFINED, simple_buffer_class_old_to_new) -{ - for (unsigned int k = 0; k < kLoop; k++) { - TestClass val1; - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - TestClass2 val2; - val2.i = -1; - val2.s = ""; - val2.v = vector(); - obj.convert(&val2); - EXPECT_EQ(val1.i, val2.i); - EXPECT_EQ(val1.s, val2.s); - EXPECT_FALSE(val2.s.empty()); - } -} - -TEST(MSGPACK_USER_DEFINED, simple_buffer_class_new_to_old) -{ - for (unsigned int k = 0; k < kLoop; k++) { - TestClass2 val1; - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - TestClass val2; - val2.i = -1; - val2.s = ""; - obj.convert(&val2); - EXPECT_EQ(val1.i, val2.i); - EXPECT_EQ(val1.s, val2.s); - EXPECT_FALSE(val2.s.empty()); - } -} - -class TestEnumMemberClass -{ -public: - TestEnumMemberClass() - : t1(STATE_A), t2(STATE_B), t3(STATE_C) {} - - enum TestEnumType { - STATE_INVALID = 0, - STATE_A = 1, - STATE_B = 2, - STATE_C = 3 - }; - TestEnumType t1; - TestEnumType t2; - TestEnumType t3; - - MSGPACK_DEFINE((int&)t1, (int&)t2, (int&)t3); -}; - -TEST(MSGPACK_USER_DEFINED, simple_buffer_enum_member) -{ - TestEnumMemberClass val1; - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - TestEnumMemberClass val2; - val2.t1 = TestEnumMemberClass::STATE_INVALID; - val2.t2 = TestEnumMemberClass::STATE_INVALID; - val2.t3 = TestEnumMemberClass::STATE_INVALID; - obj.convert(&val2); - EXPECT_EQ(val1.t1, val2.t1); - EXPECT_EQ(val1.t2, val2.t2); - EXPECT_EQ(val1.t3, val2.t3); -} - -class TestUnionMemberClass -{ -public: - TestUnionMemberClass() {} - TestUnionMemberClass(double f) { - is_double = true; - value.f = f; - } - TestUnionMemberClass(int i) { - is_double = false; - value.i = i; - } - - union { - double f; - int i; - } value; - bool is_double; - - template - void msgpack_pack(Packer& pk) const - { - if (is_double) - pk.pack(msgpack::type::tuple(true, value.f)); - else - pk.pack(msgpack::type::tuple(false, value.i)); - } - - void msgpack_unpack(msgpack::object o) - { - msgpack::type::tuple tuple; - o.convert(&tuple); - - is_double = tuple.get<0>(); - if (is_double) - tuple.get<1>().convert(&value.f); - else - tuple.get<1>().convert(&value.i); - } -}; - -TEST(MSGPACK_USER_DEFINED, simple_buffer_union_member) -{ - { - // double - TestUnionMemberClass val1(1.0); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - TestUnionMemberClass val2; - obj.convert(&val2); - EXPECT_EQ(val1.is_double, val2.is_double); - EXPECT_TRUE(fabs(val1.value.f - val2.value.f) < kEPS); - } - { - // int - TestUnionMemberClass val1(1); - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, val1); - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); - TestUnionMemberClass val2; - obj.convert(&val2); - EXPECT_EQ(val1.is_double, val2.is_double); - EXPECT_EQ(val1.value.i, 1); - EXPECT_EQ(val1.value.i, val2.value.i); - } -} - -//----------------------------------------------------------------------------- - -#define GEN_TEST_VREF(test_type) \ - do { \ - vector v; \ - v.push_back(0); \ - for (unsigned int i = 0; i < v.size(); i++) { \ - test_type val1 = v[i]; \ - msgpack::vrefbuffer vbuf; \ - msgpack::pack(vbuf, val1); \ - msgpack::sbuffer sbuf; \ - const struct iovec* cur = vbuf.vector(); \ - const struct iovec* end = cur + vbuf.vector_size(); \ - for(; cur != end; ++cur) \ - sbuf.write((const char*)cur->iov_base, cur->iov_len); \ - msgpack::zone z; \ - msgpack::object obj; \ - msgpack::unpack_return ret = \ - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); \ - EXPECT_EQ(msgpack::UNPACK_SUCCESS, ret); \ - test_type val2; \ - obj.convert(&val2); \ - EXPECT_EQ(val1, val2); \ - } \ - } while(0); - -TEST(MSGPACK, vrefbuffer_short) -{ - GEN_TEST_VREF(short); -} - -TEST(MSGPACK, vrefbuffer_int) -{ - GEN_TEST_VREF(int); -} - -TEST(MSGPACK, vrefbuffer_long) -{ - GEN_TEST_VREF(long); -} - -TEST(MSGPACK, vrefbuffer_long_long) -{ - GEN_TEST_VREF(long long); -} - -TEST(MSGPACK, vrefbuffer_unsigned_short) -{ - GEN_TEST_VREF(unsigned short); -} - -TEST(MSGPACK, vrefbuffer_unsigned_int) -{ - GEN_TEST_VREF(unsigned int); -} - -TEST(MSGPACK, vrefbuffer_unsigned_long) -{ - GEN_TEST_VREF(unsigned long); -} - -TEST(MSGPACK, vrefbuffer_unsigned_long_long) -{ - GEN_TEST_VREF(unsigned long long); -} - -TEST(MSGPACK, vrefbuffer_uint8) -{ - GEN_TEST_VREF(uint8_t); -} - -TEST(MSGPACK, vrefbuffer_uint16) -{ - GEN_TEST_VREF(uint16_t); -} - -TEST(MSGPACK, vrefbuffer_uint32) -{ - GEN_TEST_VREF(uint32_t); -} - -TEST(MSGPACK, vrefbuffer_uint64) -{ - GEN_TEST_VREF(uint64_t); -} - -TEST(MSGPACK, vrefbuffer_int8) -{ - GEN_TEST_VREF(int8_t); -} - -TEST(MSGPACK, vrefbuffer_int16) -{ - GEN_TEST_VREF(int16_t); -} - -TEST(MSGPACK, vrefbuffer_int32) -{ - GEN_TEST_VREF(int32_t); -} - -TEST(MSGPACK, vrefbuffer_int64) -{ - GEN_TEST_VREF(int64_t); -} - -//----------------------------------------------------------------------------- - -#define GEN_TEST_STREAM(test_type) \ - for (unsigned int k = 0; k < kLoop; k++) { \ - msgpack::sbuffer sbuf; \ - msgpack::packer pk(sbuf); \ - typedef std::vector vec_type; \ - vec_type vec; \ - for(unsigned int i = 0; i < rand() % kLoop; ++i) { \ - vec_type::value_type r = rand(); \ - vec.push_back(r); \ - pk.pack(r); \ - } \ - msgpack::unpacker pac; \ - vec_type::const_iterator it = vec.begin(); \ - const char *p = sbuf.data(); \ - const char * const pend = p + sbuf.size(); \ - while (p < pend) { \ - const size_t sz = std::min(pend - p, rand() % 128); \ - pac.reserve_buffer(sz); \ - memcpy(pac.buffer(), p, sz); \ - pac.buffer_consumed(sz); \ - while (pac.execute()) { \ - if (it == vec.end()) goto out; \ - msgpack::object obj = pac.data(); \ - msgpack::zone *life = pac.release_zone(); \ - EXPECT_TRUE(life != NULL); \ - pac.reset(); \ - vec_type::value_type val; \ - obj.convert(&val); \ - EXPECT_EQ(*it, val); \ - ++it; \ - delete life; \ - } \ - p += sz; \ - } \ - out: \ - ; \ - } - -TEST(MSGPACK, stream_short) -{ - GEN_TEST_STREAM(short); -} - -TEST(MSGPACK, stream_int) -{ - GEN_TEST_STREAM(int); -} - -TEST(MSGPACK, stream_long) -{ - GEN_TEST_STREAM(long); -} - -TEST(MSGPACK, stream_long_long) -{ - GEN_TEST_STREAM(long long); -} - -TEST(MSGPACK, stream_unsigned_short) -{ - GEN_TEST_STREAM(unsigned short); -} - -TEST(MSGPACK, stream_unsigned_int) -{ - GEN_TEST_STREAM(unsigned int); -} - -TEST(MSGPACK, stream_unsigned_long) -{ - GEN_TEST_STREAM(unsigned long); -} - -TEST(MSGPACK, stream_unsigned_long_long) -{ - GEN_TEST_STREAM(unsigned long long); -} - -TEST(MSGPACK, stream_uint8) -{ - GEN_TEST_STREAM(uint8_t); -} - -TEST(MSGPACK, stream_uint16) -{ - GEN_TEST_STREAM(uint16_t); -} - -TEST(MSGPACK, stream_uint32) -{ - GEN_TEST_STREAM(uint32_t); -} - -TEST(MSGPACK, stream_uint64) -{ - GEN_TEST_STREAM(uint64_t); -} - -TEST(MSGPACK, stream_int8) -{ - GEN_TEST_STREAM(int8_t); -} - -TEST(MSGPACK, stream_int16) -{ - GEN_TEST_STREAM(int16_t); -} - -TEST(MSGPACK, stream_int32) -{ - GEN_TEST_STREAM(int32_t); -} - -TEST(MSGPACK, stream_int64) -{ - GEN_TEST_STREAM(int64_t); -} diff --git a/msgpack/test/msgpackc_test.cpp b/msgpack/test/msgpackc_test.cpp deleted file mode 100644 index f5646ea7..00000000 --- a/msgpack/test/msgpackc_test.cpp +++ /dev/null @@ -1,424 +0,0 @@ -#include "msgpack.h" - -#include -#include -#include - -#include - -using namespace std; - -const unsigned int kLoop = 10000; -const double kEPS = 1e-10; - -#define GEN_TEST_SIGNED(test_type, func_type) \ - do { \ - vector v; \ - v.push_back(0); \ - v.push_back(1); \ - v.push_back(-1); \ - v.push_back(numeric_limits::min()); \ - v.push_back(numeric_limits::max()); \ - for (unsigned int i = 0; i < kLoop; i++) \ - v.push_back(rand()); \ - for (unsigned int i = 0; i < v.size() ; i++) { \ - test_type val = v[i]; \ - msgpack_sbuffer sbuf; \ - msgpack_sbuffer_init(&sbuf); \ - msgpack_packer pk; \ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); \ - msgpack_pack_##func_type(&pk, val); \ - msgpack_zone z; \ - msgpack_zone_init(&z, 2048); \ - msgpack_object obj; \ - msgpack_unpack_return ret = \ - msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); \ - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); \ - if (val < 0) { \ - EXPECT_EQ(MSGPACK_OBJECT_NEGATIVE_INTEGER, obj.type); \ - EXPECT_EQ(val, obj.via.i64); \ - } else { \ - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); \ - EXPECT_EQ(val, obj.via.u64); \ - } \ - msgpack_zone_destroy(&z); \ - msgpack_sbuffer_destroy(&sbuf); \ - } \ - } while(0) - -#define GEN_TEST_UNSIGNED(test_type, func_type) \ - do { \ - vector v; \ - v.push_back(0); \ - v.push_back(1); \ - v.push_back(2); \ - v.push_back(numeric_limits::min()); \ - v.push_back(numeric_limits::max()); \ - for (unsigned int i = 0; i < kLoop; i++) \ - v.push_back(rand()); \ - for (unsigned int i = 0; i < v.size() ; i++) { \ - test_type val = v[i]; \ - msgpack_sbuffer sbuf; \ - msgpack_sbuffer_init(&sbuf); \ - msgpack_packer pk; \ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); \ - msgpack_pack_##func_type(&pk, val); \ - msgpack_zone z; \ - msgpack_zone_init(&z, 2048); \ - msgpack_object obj; \ - msgpack_unpack_return ret = \ - msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); \ - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); \ - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); \ - EXPECT_EQ(val, obj.via.u64); \ - msgpack_zone_destroy(&z); \ - msgpack_sbuffer_destroy(&sbuf); \ - } \ - } while(0) - -TEST(MSGPACKC, simple_buffer_short) -{ - GEN_TEST_SIGNED(short, short); -} - -TEST(MSGPACKC, simple_buffer_int) -{ - GEN_TEST_SIGNED(int, int); -} - -TEST(MSGPACKC, simple_buffer_long) -{ - GEN_TEST_SIGNED(long, long); -} - -TEST(MSGPACKC, simple_buffer_long_long) -{ - GEN_TEST_SIGNED(long long, long_long); -} - -TEST(MSGPACKC, simple_buffer_unsigned_short) -{ - GEN_TEST_UNSIGNED(unsigned short, unsigned_short); -} - -TEST(MSGPACKC, simple_buffer_unsigned_int) -{ - GEN_TEST_UNSIGNED(unsigned int, unsigned_int); -} - -TEST(MSGPACKC, simple_buffer_unsigned_long) -{ - GEN_TEST_UNSIGNED(unsigned long, unsigned_long); -} - -TEST(MSGPACKC, simple_buffer_unsigned_long_long) -{ - GEN_TEST_UNSIGNED(unsigned long long, unsigned_long_long); -} - -TEST(MSGPACKC, simple_buffer_uint8) -{ - GEN_TEST_UNSIGNED(uint8_t, uint8); -} - -TEST(MSGPACKC, simple_buffer_uint16) -{ - GEN_TEST_UNSIGNED(uint16_t, uint16); -} - -TEST(MSGPACKC, simple_buffer_uint32) -{ - GEN_TEST_UNSIGNED(uint32_t, uint32); -} - -TEST(MSGPACKC, simple_buffer_uint64) -{ - GEN_TEST_UNSIGNED(uint64_t, uint64); -} - -TEST(MSGPACKC, simple_buffer_int8) -{ - GEN_TEST_SIGNED(int8_t, int8); -} - -TEST(MSGPACKC, simple_buffer_int16) -{ - GEN_TEST_SIGNED(int16_t, int16); -} - -TEST(MSGPACKC, simple_buffer_int32) -{ - GEN_TEST_SIGNED(int32_t, int32); -} - -TEST(MSGPACKC, simple_buffer_int64) -{ - GEN_TEST_SIGNED(int64_t, int64); -} - -TEST(MSGPACKC, simple_buffer_float) -{ - vector v; - v.push_back(0.0); - v.push_back(1.0); - v.push_back(-1.0); - v.push_back(numeric_limits::min()); - v.push_back(numeric_limits::max()); - v.push_back(nanf("tag")); - v.push_back(1.0/0.0); // inf - v.push_back(-(1.0/0.0)); // -inf - for (unsigned int i = 0; i < kLoop; i++) { - v.push_back(drand48()); - v.push_back(-drand48()); - } - - for (unsigned int i = 0; i < v.size() ; i++) { - float val = v[i]; - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_float(&pk, val); - msgpack_zone z; - msgpack_zone_init(&z, 2048); - msgpack_object obj; - msgpack_unpack_return ret = - msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); - EXPECT_EQ(MSGPACK_OBJECT_DOUBLE, obj.type); - if (isnan(val)) - EXPECT_TRUE(isnan(obj.via.dec)); - else if (isinf(val)) - EXPECT_TRUE(isinf(obj.via.dec)); - else - EXPECT_TRUE(fabs(obj.via.dec - val) <= kEPS); - msgpack_zone_destroy(&z); - msgpack_sbuffer_destroy(&sbuf); - } -} - -TEST(MSGPACKC, simple_buffer_double) -{ - vector v; - v.push_back(0.0); - v.push_back(-0.0); - v.push_back(1.0); - v.push_back(-1.0); - v.push_back(numeric_limits::min()); - v.push_back(numeric_limits::max()); - v.push_back(nan("tag")); - v.push_back(1.0/0.0); // inf - v.push_back(-(1.0/0.0)); // -inf - for (unsigned int i = 0; i < kLoop; i++) { - v.push_back(drand48()); - v.push_back(-drand48()); - } - - for (unsigned int i = 0; i < v.size() ; i++) { - double val = v[i]; - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_double(&pk, val); - msgpack_zone z; - msgpack_zone_init(&z, 2048); - msgpack_object obj; - msgpack_unpack_return ret = - msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); - EXPECT_EQ(MSGPACK_OBJECT_DOUBLE, obj.type); - if (isnan(val)) - EXPECT_TRUE(isnan(obj.via.dec)); - else if (isinf(val)) - EXPECT_TRUE(isinf(obj.via.dec)); - else - EXPECT_TRUE(fabs(obj.via.dec - val) <= kEPS); - msgpack_zone_destroy(&z); - msgpack_sbuffer_destroy(&sbuf); - } -} - -TEST(MSGPACKC, simple_buffer_nil) -{ - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_nil(&pk); - msgpack_zone z; - msgpack_zone_init(&z, 2048); - msgpack_object obj; - msgpack_unpack_return ret = - msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); - EXPECT_EQ(MSGPACK_OBJECT_NIL, obj.type); - msgpack_zone_destroy(&z); - msgpack_sbuffer_destroy(&sbuf); -} - -TEST(MSGPACKC, simple_buffer_true) -{ - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_true(&pk); - msgpack_zone z; - msgpack_zone_init(&z, 2048); - msgpack_object obj; - msgpack_unpack_return ret = - msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); - EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, obj.type); - EXPECT_EQ(true, obj.via.boolean); - msgpack_zone_destroy(&z); - msgpack_sbuffer_destroy(&sbuf); -} - -TEST(MSGPACKC, simple_buffer_false) -{ - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_false(&pk); - msgpack_zone z; - msgpack_zone_init(&z, 2048); - msgpack_object obj; - msgpack_unpack_return ret = - msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); - EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, obj.type); - EXPECT_EQ(false, obj.via.boolean); - msgpack_zone_destroy(&z); - msgpack_sbuffer_destroy(&sbuf); -} - -TEST(MSGPACKC, simple_buffer_array) -{ - unsigned int array_size = 5; - - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, array_size); - msgpack_pack_nil(&pk); - msgpack_pack_true(&pk); - msgpack_pack_false(&pk); - msgpack_pack_int(&pk, 10); - msgpack_pack_int(&pk, -10); - - msgpack_zone z; - msgpack_zone_init(&z, 2048); - msgpack_object obj; - msgpack_unpack_return ret; - ret = msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); - EXPECT_EQ(MSGPACK_OBJECT_ARRAY, obj.type); - EXPECT_EQ(array_size, obj.via.array.size); - - for (unsigned int i = 0; i < obj.via.array.size; i++) { - msgpack_object o = obj.via.array.ptr[i]; - switch (i) { - case 0: - EXPECT_EQ(MSGPACK_OBJECT_NIL, o.type); - break; - case 1: - EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, o.type); - EXPECT_EQ(true, o.via.boolean); - break; - case 2: - EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, o.type); - EXPECT_EQ(false, o.via.boolean); - break; - case 3: - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, o.type); - EXPECT_EQ(10, o.via.u64); - break; - case 4: - EXPECT_EQ(MSGPACK_OBJECT_NEGATIVE_INTEGER, o.type); - EXPECT_EQ(-10, o.via.i64); - break; - } - } - - msgpack_zone_destroy(&z); - msgpack_sbuffer_destroy(&sbuf); -} - -TEST(MSGPACKC, simple_buffer_map) -{ - unsigned int map_size = 2; - - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_map(&pk, map_size); - msgpack_pack_true(&pk); - msgpack_pack_false(&pk); - msgpack_pack_int(&pk, 10); - msgpack_pack_int(&pk, -10); - - msgpack_zone z; - msgpack_zone_init(&z, 2048); - msgpack_object obj; - msgpack_unpack_return ret; - ret = msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); - EXPECT_EQ(MSGPACK_OBJECT_MAP, obj.type); - EXPECT_EQ(map_size, obj.via.map.size); - - for (unsigned int i = 0; i < map_size; i++) { - msgpack_object key = obj.via.map.ptr[i].key; - msgpack_object val = obj.via.map.ptr[i].val; - switch (i) { - case 0: - EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, key.type); - EXPECT_EQ(true, key.via.boolean); - EXPECT_EQ(MSGPACK_OBJECT_BOOLEAN, val.type); - EXPECT_EQ(false, val.via.boolean); - break; - case 1: - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, key.type); - EXPECT_EQ(10, key.via.u64); - EXPECT_EQ(MSGPACK_OBJECT_NEGATIVE_INTEGER, val.type); - EXPECT_EQ(-10, val.via.i64); - break; - } - } - - msgpack_zone_destroy(&z); - msgpack_sbuffer_destroy(&sbuf); -} - -TEST(MSGPACKC, simple_buffer_raw) -{ - unsigned int raw_size = 7; - - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_raw(&pk, raw_size); - msgpack_pack_raw_body(&pk, "fr", 2); - msgpack_pack_raw_body(&pk, "syuki", 5); - // invalid data - msgpack_pack_raw_body(&pk, "", 0); - msgpack_pack_raw_body(&pk, "kzk", 0); - - msgpack_zone z; - msgpack_zone_init(&z, 2048); - msgpack_object obj; - msgpack_unpack_return ret; - ret = msgpack_unpack(sbuf.data, sbuf.size, NULL, &z, &obj); - EXPECT_EQ(MSGPACK_UNPACK_SUCCESS, ret); - EXPECT_EQ(MSGPACK_OBJECT_RAW, obj.type); - EXPECT_EQ(raw_size, obj.via.raw.size); - EXPECT_EQ(0, memcmp("frsyuki", obj.via.raw.ptr, raw_size)); - - msgpack_zone_destroy(&z); - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/msgpack/test/object.cc b/msgpack/test/object.cc deleted file mode 100644 index 5390c4ae..00000000 --- a/msgpack/test/object.cc +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include - -struct myclass { - myclass() : num(0), str("default") { } - - myclass(int num, const std::string& str) : - num(0), str("default") { } - - ~myclass() { } - - int num; - std::string str; - - MSGPACK_DEFINE(num, str); - - bool operator==(const myclass& o) const - { - return num == o.num && str == o.str; - } -}; - -std::ostream& operator<<(std::ostream& o, const myclass& m) -{ - return o << "myclass("<()); -} - - -TEST(object, print) -{ - msgpack::object obj; - std::cout << obj << std::endl; -} - - -TEST(object, is_nil) -{ - msgpack::object obj; - EXPECT_TRUE(obj.is_nil()); -} - - -TEST(object, type_error) -{ - msgpack::object obj(1); - EXPECT_THROW(obj.as(), msgpack::type_error); - EXPECT_THROW(obj.as >(), msgpack::type_error); - EXPECT_EQ(1, obj.as()); - EXPECT_EQ(1, obj.as()); - EXPECT_EQ(1u, obj.as()); - EXPECT_EQ(1u, obj.as()); -} - - -TEST(object, equal_primitive) -{ - msgpack::object obj_nil; - EXPECT_EQ(obj_nil, msgpack::object()); - - msgpack::object obj_int(1); - EXPECT_EQ(obj_int, msgpack::object(1)); - EXPECT_EQ(obj_int, 1); - - msgpack::object obj_double(1.2); - EXPECT_EQ(obj_double, msgpack::object(1.2)); - EXPECT_EQ(obj_double, 1.2); - - msgpack::object obj_bool(true); - EXPECT_EQ(obj_bool, msgpack::object(true)); - EXPECT_EQ(obj_bool, true); -} - - -TEST(object, construct_primitive) -{ - msgpack::object obj_nil; - EXPECT_EQ(msgpack::type::NIL, obj_nil.type); - - msgpack::object obj_uint(1); - EXPECT_EQ(msgpack::type::POSITIVE_INTEGER, obj_uint.type); - EXPECT_EQ(1u, obj_uint.via.u64); - - msgpack::object obj_int(-1); - EXPECT_EQ(msgpack::type::NEGATIVE_INTEGER, obj_int.type); - EXPECT_EQ(-1, obj_int.via.i64); - - msgpack::object obj_double(1.2); - EXPECT_EQ(msgpack::type::DOUBLE, obj_double.type); - EXPECT_EQ(1.2, obj_double.via.dec); - - msgpack::object obj_bool(true); - EXPECT_EQ(msgpack::type::BOOLEAN, obj_bool.type); - EXPECT_EQ(true, obj_bool.via.boolean); -} - diff --git a/msgpack/test/pack_unpack.cc b/msgpack/test/pack_unpack.cc deleted file mode 100644 index fe4625a5..00000000 --- a/msgpack/test/pack_unpack.cc +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include - -TEST(pack, num) -{ - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, 1); -} - - -TEST(pack, vector) -{ - msgpack::sbuffer sbuf; - std::vector vec; - vec.push_back(1); - vec.push_back(2); - vec.push_back(3); - msgpack::pack(sbuf, vec); -} - - -TEST(pack, to_ostream) -{ - std::ostringstream stream; - msgpack::pack(stream, 1); -} - - -struct myclass { - myclass() : num(0), str("default") { } - - myclass(int num, const std::string& str) : - num(0), str("default") { } - - ~myclass() { } - - int num; - std::string str; - - MSGPACK_DEFINE(num, str); -}; - - -TEST(pack, myclass) -{ - msgpack::sbuffer sbuf; - myclass m(1, "msgpack"); - msgpack::pack(sbuf, m); -} - - -TEST(unpack, myclass) -{ - msgpack::sbuffer sbuf; - myclass m1(1, "phraser"); - msgpack::pack(sbuf, m1); - - msgpack::zone z; - msgpack::object obj; - - msgpack::unpack_return ret = - msgpack::unpack(sbuf.data(), sbuf.size(), NULL, &z, &obj); - - EXPECT_EQ(ret, msgpack::UNPACK_SUCCESS); - - myclass m2 = obj.as(); - EXPECT_EQ(m1.num, m2.num); - EXPECT_EQ(m1.str, m2.str); -} - - -TEST(unpack, sequence) -{ - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, 1); - msgpack::pack(sbuf, 2); - msgpack::pack(sbuf, 3); - - size_t offset = 0; - - msgpack::unpacked msg; - - msgpack::unpack(&msg, sbuf.data(), sbuf.size(), &offset); - EXPECT_EQ(1, msg.get().as()); - - msgpack::unpack(&msg, sbuf.data(), sbuf.size(), &offset); - EXPECT_EQ(2, msg.get().as()); - - msgpack::unpack(&msg, sbuf.data(), sbuf.size(), &offset); - EXPECT_EQ(3, msg.get().as()); -} - - -TEST(unpack, sequence_compat) -{ - msgpack::sbuffer sbuf; - msgpack::pack(sbuf, 1); - msgpack::pack(sbuf, 2); - msgpack::pack(sbuf, 3); - - size_t offset = 0; - - msgpack::zone z; - msgpack::object obj; - msgpack::unpack_return ret; - - ret = msgpack::unpack(sbuf.data(), sbuf.size(), &offset, &z, &obj); - EXPECT_TRUE(ret >= 0); - EXPECT_EQ(ret, msgpack::UNPACK_EXTRA_BYTES); - EXPECT_EQ(1, obj.as()); - - ret = msgpack::unpack(sbuf.data(), sbuf.size(), &offset, &z, &obj); - EXPECT_TRUE(ret >= 0); - EXPECT_EQ(ret, msgpack::UNPACK_EXTRA_BYTES); - EXPECT_EQ(2, obj.as()); - - ret = msgpack::unpack(sbuf.data(), sbuf.size(), &offset, &z, &obj); - EXPECT_TRUE(ret >= 0); - EXPECT_EQ(ret, msgpack::UNPACK_SUCCESS); - EXPECT_EQ(3, obj.as()); -} - diff --git a/msgpack/test/pack_unpack_c.cc b/msgpack/test/pack_unpack_c.cc deleted file mode 100644 index e9a03892..00000000 --- a/msgpack/test/pack_unpack_c.cc +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include - -TEST(pack, num) -{ - msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); - msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); - - EXPECT_EQ(0, msgpack_pack_int(pk, 1)); - - msgpack_sbuffer_free(sbuf); - msgpack_packer_free(pk); -} - - -TEST(pack, array) -{ - msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); - msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); - - EXPECT_EQ(0, msgpack_pack_array(pk, 3)); - EXPECT_EQ(0, msgpack_pack_int(pk, 1)); - EXPECT_EQ(0, msgpack_pack_int(pk, 2)); - EXPECT_EQ(0, msgpack_pack_int(pk, 3)); - - msgpack_sbuffer_free(sbuf); - msgpack_packer_free(pk); -} - - -TEST(unpack, sequence) -{ - msgpack_sbuffer* sbuf = msgpack_sbuffer_new(); - msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write); - - EXPECT_EQ(0, msgpack_pack_int(pk, 1)); - EXPECT_EQ(0, msgpack_pack_int(pk, 2)); - EXPECT_EQ(0, msgpack_pack_int(pk, 3)); - - msgpack_packer_free(pk); - - bool success; - size_t offset = 0; - - msgpack_unpacked msg; - msgpack_unpacked_init(&msg); - - success = msgpack_unpack_next(&msg, sbuf->data, sbuf->size, &offset); - EXPECT_TRUE(success); - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, msg.data.type); - EXPECT_EQ(1, msg.data.via.u64); - - success = msgpack_unpack_next(&msg, sbuf->data, sbuf->size, &offset); - EXPECT_TRUE(success); - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, msg.data.type); - EXPECT_EQ(2, msg.data.via.u64); - - success = msgpack_unpack_next(&msg, sbuf->data, sbuf->size, &offset); - EXPECT_TRUE(success); - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, msg.data.type); - EXPECT_EQ(3, msg.data.via.u64); - - success = msgpack_unpack_next(&msg, sbuf->data, sbuf->size, &offset); - EXPECT_FALSE(success); - - msgpack_sbuffer_free(sbuf); - msgpack_unpacked_destroy(&msg); -} - diff --git a/msgpack/test/streaming.cc b/msgpack/test/streaming.cc deleted file mode 100644 index e80c671b..00000000 --- a/msgpack/test/streaming.cc +++ /dev/null @@ -1,220 +0,0 @@ -#include -#include -#include - -TEST(streaming, basic) -{ - msgpack::sbuffer buffer; - - msgpack::packer pk(&buffer); - pk.pack(1); - pk.pack(2); - pk.pack(3); - - const char* input = buffer.data(); - const char* const eof = input + buffer.size(); - - msgpack::unpacker pac; - msgpack::unpacked result; - - int count = 0; - while(count < 3) { - pac.reserve_buffer(32*1024); - - // read buffer into pac.buffer() upto - // pac.buffer_capacity() bytes. - size_t len = 1; - memcpy(pac.buffer(), input, len); - input += len; - - pac.buffer_consumed(len); - - while(pac.next(&result)) { - msgpack::object obj = result.get(); - switch(count++) { - case 0: - EXPECT_EQ(1, obj.as()); - break; - case 1: - EXPECT_EQ(2, obj.as()); - break; - case 2: - EXPECT_EQ(3, obj.as()); - return; - } - } - - EXPECT_TRUE(input < eof); - } -} - - -class event_handler { -public: - event_handler(std::istream& input) : input(input) { } - ~event_handler() { } - - void on_read() - { - while(true) { - pac.reserve_buffer(32*1024); - - size_t len = input.readsome(pac.buffer(), pac.buffer_capacity()); - - if(len == 0) { - return; - } - - pac.buffer_consumed(len); - - msgpack::unpacked result; - while(pac.next(&result)) { - on_message(result.get(), result.zone()); - } - - if(pac.message_size() > 10*1024*1024) { - throw std::runtime_error("message is too large"); - } - } - } - - void on_message(msgpack::object obj, std::auto_ptr z) - { - EXPECT_EQ(expect, obj.as()); - } - - int expect; - -private: - std::istream& input; - msgpack::unpacker pac; -}; - -TEST(streaming, event) -{ - std::stringstream stream; - msgpack::packer pk(&stream); - - event_handler handler(stream); - - pk.pack(1); - handler.expect = 1; - handler.on_read(); - - pk.pack(2); - handler.expect = 2; - handler.on_read(); - - pk.pack(3); - handler.expect = 3; - handler.on_read(); -} - - -// backward compatibility -TEST(streaming, basic_compat) -{ - std::ostringstream stream; - msgpack::packer pk(&stream); - - pk.pack(1); - pk.pack(2); - pk.pack(3); - - std::istringstream input(stream.str()); - - msgpack::unpacker pac; - - int count = 0; - while(count < 3) { - pac.reserve_buffer(32*1024); - - size_t len = input.readsome(pac.buffer(), pac.buffer_capacity()); - pac.buffer_consumed(len); - - while(pac.execute()) { - std::auto_ptr z(pac.release_zone()); - msgpack::object obj = pac.data(); - pac.reset(); - - switch(count++) { - case 0: - EXPECT_EQ(1, obj.as()); - break; - case 1: - EXPECT_EQ(2, obj.as()); - break; - case 2: - EXPECT_EQ(3, obj.as()); - return; - } - - } - } -} - - -// backward compatibility -class event_handler_compat { -public: - event_handler_compat(std::istream& input) : input(input) { } - ~event_handler_compat() { } - - void on_read() - { - while(true) { - pac.reserve_buffer(32*1024); - - size_t len = input.readsome(pac.buffer(), pac.buffer_capacity()); - - if(len == 0) { - return; - } - - pac.buffer_consumed(len); - - while(pac.execute()) { - std::auto_ptr z(pac.release_zone()); - msgpack::object obj = pac.data(); - pac.reset(); - on_message(obj, z); - } - - if(pac.message_size() > 10*1024*1024) { - throw std::runtime_error("message is too large"); - } - } - } - - void on_message(msgpack::object obj, std::auto_ptr z) - { - EXPECT_EQ(expect, obj.as()); - } - - int expect; - -private: - std::istream& input; - msgpack::unpacker pac; -}; - -TEST(streaming, event_compat) -{ - std::stringstream stream; - msgpack::packer pk(&stream); - - event_handler_compat handler(stream); - - pk.pack(1); - handler.expect = 1; - handler.on_read(); - - pk.pack(2); - handler.expect = 2; - handler.on_read(); - - pk.pack(3); - handler.expect = 3; - handler.on_read(); -} - diff --git a/msgpack/test/streaming_c.cc b/msgpack/test/streaming_c.cc deleted file mode 100644 index 1b7ad8b6..00000000 --- a/msgpack/test/streaming_c.cc +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include - -TEST(streaming, basic) -{ - msgpack_sbuffer* buffer = msgpack_sbuffer_new(); - - msgpack_packer* pk = msgpack_packer_new(buffer, msgpack_sbuffer_write); - - // 1, 2, 3, "raw", ["data"], {0.3: 0.4} - EXPECT_EQ(0, msgpack_pack_int(pk, 1)); - EXPECT_EQ(0, msgpack_pack_int(pk, 2)); - EXPECT_EQ(0, msgpack_pack_int(pk, 3)); - EXPECT_EQ(0, msgpack_pack_raw(pk, 3)); - EXPECT_EQ(0, msgpack_pack_raw_body(pk, "raw", 3)); - EXPECT_EQ(0, msgpack_pack_array(pk, 1)); - EXPECT_EQ(0, msgpack_pack_raw(pk, 4)); - EXPECT_EQ(0, msgpack_pack_raw_body(pk, "data", 4)); - EXPECT_EQ(0, msgpack_pack_map(pk, 1)); - EXPECT_EQ(0, msgpack_pack_float(pk, 0.4)); - EXPECT_EQ(0, msgpack_pack_double(pk, 0.8)); - int max_count = 6; - - msgpack_packer_free(pk); - - const char* input = buffer->data; - const char* const eof = input + buffer->size; - - msgpack_unpacker pac; - msgpack_unpacker_init(&pac, MSGPACK_UNPACKER_INIT_BUFFER_SIZE); - - msgpack_unpacked result; - msgpack_unpacked_init(&result); - - int count = 0; - while(count < max_count) { - bool unpacked = false; - - msgpack_unpacker_reserve_buffer(&pac, 32*1024); - - while(!unpacked) { - /* read buffer into msgpack_unapcker_buffer(&pac) upto - * msgpack_unpacker_buffer_capacity(&pac) bytes. */ - memcpy(msgpack_unpacker_buffer(&pac), input, 1); - input += 1; - - EXPECT_TRUE(input <= eof); - - msgpack_unpacker_buffer_consumed(&pac, 1); - - while(msgpack_unpacker_next(&pac, &result)) { - unpacked = 1; - msgpack_object obj = result.data; - msgpack_object e; - switch(count++) { - case 0: - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); - EXPECT_EQ(1, obj.via.u64); - break; - case 1: - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); - EXPECT_EQ(2, obj.via.u64); - break; - case 2: - EXPECT_EQ(MSGPACK_OBJECT_POSITIVE_INTEGER, obj.type); - EXPECT_EQ(3, obj.via.u64); - break; - case 3: - EXPECT_EQ(MSGPACK_OBJECT_RAW, obj.type); - EXPECT_EQ(std::string("raw",3), std::string(obj.via.raw.ptr, obj.via.raw.size)); - break; - case 4: - EXPECT_EQ(MSGPACK_OBJECT_ARRAY, obj.type); - EXPECT_EQ(1, obj.via.array.size); - e = obj.via.array.ptr[0]; - EXPECT_EQ(MSGPACK_OBJECT_RAW, e.type); - EXPECT_EQ(std::string("data",4), std::string(e.via.raw.ptr, e.via.raw.size)); - break; - case 5: - EXPECT_EQ(MSGPACK_OBJECT_MAP, obj.type); - EXPECT_EQ(1, obj.via.map.size); - e = obj.via.map.ptr[0].key; - EXPECT_EQ(MSGPACK_OBJECT_DOUBLE, e.type); - ASSERT_FLOAT_EQ(0.4, (float)e.via.dec); - e = obj.via.map.ptr[0].val; - EXPECT_EQ(MSGPACK_OBJECT_DOUBLE, e.type); - ASSERT_DOUBLE_EQ(0.8, e.via.dec); - break; - } - } - } - } - - msgpack_unpacker_destroy(&pac); - msgpack_unpacked_destroy(&result); -} - diff --git a/msgpack/test/version.cc b/msgpack/test/version.cc deleted file mode 100644 index 9357271e..00000000 --- a/msgpack/test/version.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -TEST(version, print) -{ - printf("MSGPACK_VERSION : %s\n", MSGPACK_VERSION); - printf("MSGPACK_VERSION_MAJOR : %d\n", MSGPACK_VERSION_MAJOR); - printf("MSGPACK_VERSION_MINOR : %d\n", MSGPACK_VERSION_MINOR); - printf("msgpack_version() : %s\n", msgpack_version()); - printf("msgpack_version_major() : %d\n", msgpack_version_major()); - printf("msgpack_version_minor() : %d\n", msgpack_version_minor()); -} - diff --git a/msgpack/test/zone.cc b/msgpack/test/zone.cc deleted file mode 100644 index 5274e9f0..00000000 --- a/msgpack/test/zone.cc +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include - -TEST(zone, malloc) -{ - msgpack::zone z; - char* buf1 = (char*)z.malloc(4); - memcpy(buf1, "test", 4); - char* buf2 = (char*)z.malloc(4); - memcpy(buf2, "test", 4); -} - - -class myclass { -public: - myclass() : num(0), str("default") { } - - myclass(int num, const std::string& str) : - num(num), str(str) { } - - ~myclass() { } - - int num; - std::string str; - -private: - myclass(const myclass&); -}; - - -TEST(zone, allocate) -{ - msgpack::zone z; - myclass* m = z.allocate(); - EXPECT_EQ(m->num, 0); - EXPECT_EQ(m->str, "default"); -} - - -TEST(zone, allocate_constructor) -{ - msgpack::zone z; - myclass* m = z.allocate(7, "msgpack"); - EXPECT_EQ(m->num, 7); - EXPECT_EQ(m->str, "msgpack"); -} - - -static void custom_finalizer_func(void* user) -{ - myclass* m = (myclass*)user; - delete m; -} - -TEST(zone, push_finalizer) -{ - msgpack::zone z; - myclass* m = new myclass(); - z.push_finalizer(custom_finalizer_func, (void*)m); -} - - -TEST(zone, push_finalizer_auto_ptr) -{ - msgpack::zone z; - std::auto_ptr am(new myclass()); - z.push_finalizer(am); -} - - -TEST(zone, malloc_no_align) -{ - msgpack::zone z; - char* buf1 = (char*)z.malloc_no_align(4); - char* buf2 = (char*)z.malloc_no_align(4); - EXPECT_EQ(buf1+4, buf2); -} - diff --git a/msgpack/unpack_define.h b/msgpack/unpack_define.h deleted file mode 100644 index 959d3519..00000000 --- a/msgpack/unpack_define.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * MessagePack unpacking routine template - * - * Copyright (C) 2008-2010 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MSGPACK_UNPACK_DEFINE_H__ -#define MSGPACK_UNPACK_DEFINE_H__ - -#include "msgpack/sysdep.h" -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -#ifndef MSGPACK_EMBED_STACK_SIZE -#define MSGPACK_EMBED_STACK_SIZE 32 -#endif - - -typedef enum { - CS_HEADER = 0x00, // nil - - //CS_ = 0x01, - //CS_ = 0x02, // false - //CS_ = 0x03, // true - - //CS_ = 0x04, - //CS_ = 0x05, - //CS_ = 0x06, - //CS_ = 0x07, - - //CS_ = 0x08, - //CS_ = 0x09, - CS_FLOAT = 0x0a, - CS_DOUBLE = 0x0b, - CS_UINT_8 = 0x0c, - CS_UINT_16 = 0x0d, - CS_UINT_32 = 0x0e, - CS_UINT_64 = 0x0f, - CS_INT_8 = 0x10, - CS_INT_16 = 0x11, - CS_INT_32 = 0x12, - CS_INT_64 = 0x13, - - //CS_ = 0x14, - //CS_ = 0x15, - //CS_BIG_INT_16 = 0x16, - //CS_BIG_INT_32 = 0x17, - //CS_BIG_FLOAT_16 = 0x18, - //CS_BIG_FLOAT_32 = 0x19, - CS_RAW_16 = 0x1a, - CS_RAW_32 = 0x1b, - CS_ARRAY_16 = 0x1c, - CS_ARRAY_32 = 0x1d, - CS_MAP_16 = 0x1e, - CS_MAP_32 = 0x1f, - - //ACS_BIG_INT_VALUE, - //ACS_BIG_FLOAT_VALUE, - ACS_RAW_VALUE, -} msgpack_unpack_state; - - -typedef enum { - CT_ARRAY_ITEM, - CT_MAP_KEY, - CT_MAP_VALUE, -} msgpack_container_type; - - -#ifdef __cplusplus -} -#endif - -#endif /* msgpack/unpack_define.h */ - diff --git a/msgpack/unpack_template.h b/msgpack/unpack_template.h deleted file mode 100644 index 711b163a..00000000 --- a/msgpack/unpack_template.h +++ /dev/null @@ -1,413 +0,0 @@ -/* - * MessagePack unpacking routine template - * - * Copyright (C) 2008-2010 FURUHASHI Sadayuki - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef msgpack_unpack_func -#error msgpack_unpack_func template is not defined -#endif - -#ifndef msgpack_unpack_callback -#error msgpack_unpack_callback template is not defined -#endif - -#ifndef msgpack_unpack_struct -#error msgpack_unpack_struct template is not defined -#endif - -#ifndef msgpack_unpack_struct_decl -#define msgpack_unpack_struct_decl(name) msgpack_unpack_struct(name) -#endif - -#ifndef msgpack_unpack_object -#error msgpack_unpack_object type is not defined -#endif - -#ifndef msgpack_unpack_user -#error msgpack_unpack_user type is not defined -#endif - -#ifndef USE_CASE_RANGE -#if !defined(_MSC_VER) -#define USE_CASE_RANGE -#endif -#endif - -msgpack_unpack_struct_decl(_stack) { - msgpack_unpack_object obj; - size_t count; - unsigned int ct; - msgpack_unpack_object map_key; -}; - -msgpack_unpack_struct_decl(_context) { - msgpack_unpack_user user; - unsigned int cs; - unsigned int trail; - unsigned int top; - /* - msgpack_unpack_struct(_stack)* stack; - unsigned int stack_size; - msgpack_unpack_struct(_stack) embed_stack[MSGPACK_EMBED_STACK_SIZE]; - */ - msgpack_unpack_struct(_stack) stack[MSGPACK_EMBED_STACK_SIZE]; -}; - - -msgpack_unpack_func(void, _init)(msgpack_unpack_struct(_context)* ctx) -{ - ctx->cs = CS_HEADER; - ctx->trail = 0; - ctx->top = 0; - /* - ctx->stack = ctx->embed_stack; - ctx->stack_size = MSGPACK_EMBED_STACK_SIZE; - */ - ctx->stack[0].obj = msgpack_unpack_callback(_root)(&ctx->user); -} - -/* -msgpack_unpack_func(void, _destroy)(msgpack_unpack_struct(_context)* ctx) -{ - if(ctx->stack_size != MSGPACK_EMBED_STACK_SIZE) { - free(ctx->stack); - } -} -*/ - -msgpack_unpack_func(msgpack_unpack_object, _data)(msgpack_unpack_struct(_context)* ctx) -{ - return (ctx)->stack[0].obj; -} - - -msgpack_unpack_func(int, _execute)(msgpack_unpack_struct(_context)* ctx, const char* data, size_t len, size_t* off) -{ - assert(len >= *off); - - const unsigned char* p = (unsigned char*)data + *off; - const unsigned char* const pe = (unsigned char*)data + len; - const void* n = NULL; - - unsigned int trail = ctx->trail; - unsigned int cs = ctx->cs; - unsigned int top = ctx->top; - msgpack_unpack_struct(_stack)* stack = ctx->stack; - /* - unsigned int stack_size = ctx->stack_size; - */ - msgpack_unpack_user* user = &ctx->user; - - msgpack_unpack_object obj; - msgpack_unpack_struct(_stack)* c = NULL; - - int ret; - -#define push_simple_value(func) \ - if(msgpack_unpack_callback(func)(user, &obj) < 0) { goto _failed; } \ - goto _push -#define push_fixed_value(func, arg) \ - if(msgpack_unpack_callback(func)(user, arg, &obj) < 0) { goto _failed; } \ - goto _push -#define push_variable_value(func, base, pos, len) \ - if(msgpack_unpack_callback(func)(user, \ - (const char*)base, (const char*)pos, len, &obj) < 0) { goto _failed; } \ - goto _push - -#define again_fixed_trail(_cs, trail_len) \ - trail = trail_len; \ - cs = _cs; \ - goto _fixed_trail_again -#define again_fixed_trail_if_zero(_cs, trail_len, ifzero) \ - trail = trail_len; \ - if(trail == 0) { goto ifzero; } \ - cs = _cs; \ - goto _fixed_trail_again - -#define start_container(func, count_, ct_) \ - if(top >= MSGPACK_EMBED_STACK_SIZE) { goto _failed; } /* FIXME */ \ - if(msgpack_unpack_callback(func)(user, count_, &stack[top].obj) < 0) { goto _failed; } \ - if((count_) == 0) { obj = stack[top].obj; goto _push; } \ - stack[top].ct = ct_; \ - stack[top].count = count_; \ - ++top; \ - /*printf("container %d count %d stack %d\n",stack[top].obj,count_,top);*/ \ - /*printf("stack push %d\n", top);*/ \ - /* FIXME \ - if(top >= stack_size) { \ - if(stack_size == MSGPACK_EMBED_STACK_SIZE) { \ - size_t csize = sizeof(msgpack_unpack_struct(_stack)) * MSGPACK_EMBED_STACK_SIZE; \ - size_t nsize = csize * 2; \ - msgpack_unpack_struct(_stack)* tmp = (msgpack_unpack_struct(_stack)*)malloc(nsize); \ - if(tmp == NULL) { goto _failed; } \ - memcpy(tmp, ctx->stack, csize); \ - ctx->stack = stack = tmp; \ - ctx->stack_size = stack_size = MSGPACK_EMBED_STACK_SIZE * 2; \ - } else { \ - size_t nsize = sizeof(msgpack_unpack_struct(_stack)) * ctx->stack_size * 2; \ - msgpack_unpack_struct(_stack)* tmp = (msgpack_unpack_struct(_stack)*)realloc(ctx->stack, nsize); \ - if(tmp == NULL) { goto _failed; } \ - ctx->stack = stack = tmp; \ - ctx->stack_size = stack_size = stack_size * 2; \ - } \ - } \ - */ \ - goto _header_again - -#define NEXT_CS(p) \ - ((unsigned int)*p & 0x1f) - -#ifdef USE_CASE_RANGE -#define SWITCH_RANGE_BEGIN switch(*p) { -#define SWITCH_RANGE(FROM, TO) case FROM ... TO: -#define SWITCH_RANGE_DEFAULT default: -#define SWITCH_RANGE_END } -#else -#define SWITCH_RANGE_BEGIN { if(0) { -#define SWITCH_RANGE(FROM, TO) } else if(FROM <= *p && *p <= TO) { -#define SWITCH_RANGE_DEFAULT } else { -#define SWITCH_RANGE_END } } -#endif - - if(p == pe) { goto _out; } - do { - switch(cs) { - case CS_HEADER: - SWITCH_RANGE_BEGIN - SWITCH_RANGE(0x00, 0x7f) // Positive Fixnum - push_fixed_value(_uint8, *(uint8_t*)p); - SWITCH_RANGE(0xe0, 0xff) // Negative Fixnum - push_fixed_value(_int8, *(int8_t*)p); - SWITCH_RANGE(0xc0, 0xdf) // Variable - switch(*p) { - case 0xc0: // nil - push_simple_value(_nil); - //case 0xc1: // string - // again_terminal_trail(NEXT_CS(p), p+1); - case 0xc2: // false - push_simple_value(_false); - case 0xc3: // true - push_simple_value(_true); - //case 0xc4: - //case 0xc5: - //case 0xc6: - //case 0xc7: - //case 0xc8: - //case 0xc9: - case 0xca: // float - case 0xcb: // double - case 0xcc: // unsigned int 8 - case 0xcd: // unsigned int 16 - case 0xce: // unsigned int 32 - case 0xcf: // unsigned int 64 - case 0xd0: // signed int 8 - case 0xd1: // signed int 16 - case 0xd2: // signed int 32 - case 0xd3: // signed int 64 - again_fixed_trail(NEXT_CS(p), 1 << (((unsigned int)*p) & 0x03)); - //case 0xd4: - //case 0xd5: - //case 0xd6: // big integer 16 - //case 0xd7: // big integer 32 - //case 0xd8: // big float 16 - //case 0xd9: // big float 32 - case 0xda: // raw 16 - case 0xdb: // raw 32 - case 0xdc: // array 16 - case 0xdd: // array 32 - case 0xde: // map 16 - case 0xdf: // map 32 - again_fixed_trail(NEXT_CS(p), 2 << (((unsigned int)*p) & 0x01)); - default: - goto _failed; - } - SWITCH_RANGE(0xa0, 0xbf) // FixRaw - again_fixed_trail_if_zero(ACS_RAW_VALUE, ((unsigned int)*p & 0x1f), _raw_zero); - SWITCH_RANGE(0x90, 0x9f) // FixArray - start_container(_array, ((unsigned int)*p) & 0x0f, CT_ARRAY_ITEM); - SWITCH_RANGE(0x80, 0x8f) // FixMap - start_container(_map, ((unsigned int)*p) & 0x0f, CT_MAP_KEY); - - SWITCH_RANGE_DEFAULT - goto _failed; - SWITCH_RANGE_END - // end CS_HEADER - - - _fixed_trail_again: - ++p; - - default: - if((size_t)(pe - p) < trail) { goto _out; } - n = p; p += trail - 1; - switch(cs) { - //case CS_ - //case CS_ - case CS_FLOAT: { - union { uint32_t i; float f; } mem; - mem.i = _msgpack_load32(uint32_t,n); - push_fixed_value(_float, mem.f); } - case CS_DOUBLE: { - union { uint64_t i; double f; } mem; - mem.i = _msgpack_load64(uint64_t,n); -#if defined(__arm__) && !(__ARM_EABI__) // arm-oabi - // https://github.com/msgpack/msgpack-perl/pull/1 - mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL); -#endif - push_fixed_value(_double, mem.f); } - case CS_UINT_8: - push_fixed_value(_uint8, *(uint8_t*)n); - case CS_UINT_16: - push_fixed_value(_uint16, _msgpack_load16(uint16_t,n)); - case CS_UINT_32: - push_fixed_value(_uint32, _msgpack_load32(uint32_t,n)); - case CS_UINT_64: - push_fixed_value(_uint64, _msgpack_load64(uint64_t,n)); - - case CS_INT_8: - push_fixed_value(_int8, *(int8_t*)n); - case CS_INT_16: - push_fixed_value(_int16, _msgpack_load16(int16_t,n)); - case CS_INT_32: - push_fixed_value(_int32, _msgpack_load32(int32_t,n)); - case CS_INT_64: - push_fixed_value(_int64, _msgpack_load64(int64_t,n)); - - //case CS_ - //case CS_ - //case CS_BIG_INT_16: - // again_fixed_trail_if_zero(ACS_BIG_INT_VALUE, _msgpack_load16(uint16_t,n), _big_int_zero); - //case CS_BIG_INT_32: - // again_fixed_trail_if_zero(ACS_BIG_INT_VALUE, _msgpack_load32(uint32_t,n), _big_int_zero); - //case ACS_BIG_INT_VALUE: - //_big_int_zero: - // // FIXME - // push_variable_value(_big_int, data, n, trail); - - //case CS_BIG_FLOAT_16: - // again_fixed_trail_if_zero(ACS_BIG_FLOAT_VALUE, _msgpack_load16(uint16_t,n), _big_float_zero); - //case CS_BIG_FLOAT_32: - // again_fixed_trail_if_zero(ACS_BIG_FLOAT_VALUE, _msgpack_load32(uint32_t,n), _big_float_zero); - //case ACS_BIG_FLOAT_VALUE: - //_big_float_zero: - // // FIXME - // push_variable_value(_big_float, data, n, trail); - - case CS_RAW_16: - again_fixed_trail_if_zero(ACS_RAW_VALUE, _msgpack_load16(uint16_t,n), _raw_zero); - case CS_RAW_32: - again_fixed_trail_if_zero(ACS_RAW_VALUE, _msgpack_load32(uint32_t,n), _raw_zero); - case ACS_RAW_VALUE: - _raw_zero: - push_variable_value(_raw, data, n, trail); - - case CS_ARRAY_16: - start_container(_array, _msgpack_load16(uint16_t,n), CT_ARRAY_ITEM); - case CS_ARRAY_32: - /* FIXME security guard */ - start_container(_array, _msgpack_load32(uint32_t,n), CT_ARRAY_ITEM); - - case CS_MAP_16: - start_container(_map, _msgpack_load16(uint16_t,n), CT_MAP_KEY); - case CS_MAP_32: - /* FIXME security guard */ - start_container(_map, _msgpack_load32(uint32_t,n), CT_MAP_KEY); - - default: - goto _failed; - } - } - -_push: - if(top == 0) { goto _finish; } - c = &stack[top-1]; - switch(c->ct) { - case CT_ARRAY_ITEM: - if(msgpack_unpack_callback(_array_item)(user, &c->obj, obj) < 0) { goto _failed; } - if(--c->count == 0) { - obj = c->obj; - --top; - /*printf("stack pop %d\n", top);*/ - goto _push; - } - goto _header_again; - case CT_MAP_KEY: - c->map_key = obj; - c->ct = CT_MAP_VALUE; - goto _header_again; - case CT_MAP_VALUE: - if(msgpack_unpack_callback(_map_item)(user, &c->obj, c->map_key, obj) < 0) { goto _failed; } - if(--c->count == 0) { - obj = c->obj; - --top; - /*printf("stack pop %d\n", top);*/ - goto _push; - } - c->ct = CT_MAP_KEY; - goto _header_again; - - default: - goto _failed; - } - -_header_again: - cs = CS_HEADER; - ++p; - } while(p != pe); - goto _out; - - -_finish: - stack[0].obj = obj; - ++p; - ret = 1; - /*printf("-- finish --\n"); */ - goto _end; - -_failed: - /*printf("** FAILED **\n"); */ - ret = -1; - goto _end; - -_out: - ret = 0; - goto _end; - -_end: - ctx->cs = cs; - ctx->trail = trail; - ctx->top = top; - *off = p - (const unsigned char*)data; - - return ret; -} - - -#undef msgpack_unpack_func -#undef msgpack_unpack_callback -#undef msgpack_unpack_struct -#undef msgpack_unpack_object -#undef msgpack_unpack_user - -#undef push_simple_value -#undef push_fixed_value -#undef push_variable_value -#undef again_fixed_trail -#undef again_fixed_trail_if_zero -#undef start_container - -#undef NEXT_CS - From 1670d15e8add1175896e98635e3c79d4f4ddec49 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 8 Dec 2015 17:09:19 -0500 Subject: [PATCH 519/703] detect libssh/msgpack during ./configure --- Makefile.am | 5 ----- configure.ac | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 989d27b0..d0a9cd40 100644 --- a/Makefile.am +++ b/Makefile.am @@ -240,8 +240,3 @@ endif if NO_B64_NTOP nodist_tmate_SOURCES += compat/b64_ntop.c endif - -tmate_LDADD = \ - -lssh \ - -lmsgpackc - diff --git a/configure.ac b/configure.ac index 90d109ff..2941fe92 100644 --- a/configure.ac +++ b/configure.ac @@ -147,6 +147,35 @@ if test "x$found_curses" = xno; then AC_MSG_ERROR("curses not found") fi +PKG_CHECK_MODULES( + MSGPACK, + msgpack, + [ + CPPFLAGS="$MSGPACK_CFLAGS $CPPFLAGS" + LIBS="$MSGPACK_LIBS $LIBS" + found_msgpack=yes + ], + found_msgpack=no +) +if test "x$found_msgpack" = xno; then + AC_MSG_ERROR("msgpack not found") +fi + + +PKG_CHECK_MODULES( + LIBSSH, + libssh, + [ + CPPFLAGS="$LIBSSH_CFLAGS $CPPFLAGS" + LIBS="$LIBSSH_LIBS $LIBS" + found_libssh=yes + ], + found_libssh=no +) +if test "x$found_libssh" = xno; then + AC_MSG_ERROR("libssh not found") +fi + # Check for b64_ntop. AC_MSG_CHECKING(for b64_ntop) AC_TRY_LINK( From f0d60cb1c83480a1da43e498e5b973d1650f7a10 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 8 Dec 2015 18:50:38 -0500 Subject: [PATCH 520/703] update libssh/msgpack APIs --- tmate-decoder.c | 7 ++++--- tmate-encoder.c | 10 +++++----- tmate-ssh-client.c | 8 ++++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/tmate-decoder.c b/tmate-decoder.c index 7489d76f..a0434d79 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -49,11 +49,12 @@ static void unpack_raw(struct tmate_unpacker *uk, if (uk->argc == 0) decoder_error(); - if (uk->argv[0].type != MSGPACK_OBJECT_RAW) + if (uk->argv[0].type != MSGPACK_OBJECT_STR && + uk->argv[0].type != MSGPACK_OBJECT_BIN) decoder_error(); - *len = uk->argv[0].via.raw.size; - *buf = uk->argv[0].via.raw.ptr; + *len = uk->argv[0].via.str.size; + *buf = uk->argv[0].via.str.ptr; uk->argv++; uk->argc--; diff --git a/tmate-encoder.c b/tmate-encoder.c index 4ec59447..94dc2137 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -2,7 +2,7 @@ #define DEFAULT_ENCODER (&tmate_session.encoder) -static int msgpack_write(void *data, const char *buf, unsigned int len) +static int msgpack_write(void *data, const char *buf, size_t len) { struct tmate_encoder *encoder = data; @@ -25,8 +25,8 @@ void tmate_encoder_init(struct tmate_encoder *encoder) #define msgpack_pack_string(pk, str) do { \ int __strlen = strlen(str); \ - msgpack_pack_raw(pk, __strlen); \ - msgpack_pack_raw_body(pk, str, __strlen); \ + msgpack_pack_str(pk, __strlen); \ + msgpack_pack_str_body(pk, str, __strlen); \ } while(0) #define pack(what, ...) msgpack_pack_##what(&DEFAULT_ENCODER->pk, __VA_ARGS__) @@ -125,8 +125,8 @@ void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) pack(array, 3); pack(int, TMATE_PTY_DATA); pack(int, wp->id); - pack(raw, to_write); - pack(raw_body, buf, to_write); + pack(str, to_write); + pack(str_body, buf, to_write); buf += to_write; len -= to_write; diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 1c346e85..8e8db6a1 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -223,7 +223,10 @@ static void on_session_event(struct tmate_ssh_client *client) } case SSH_AUTH_SERVER: - if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) { + if (ssh_get_publickey(session, &pubkey) < 0) + tmate_fatal("ssh_get_publickey"); + + if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hash_len) < 0) { kill_session(client, "Cannot authenticate server"); return; } @@ -232,9 +235,6 @@ static void on_session_event(struct tmate_ssh_client *client) if (!hash_str) tmate_fatal("malloc failed"); - if (ssh_get_publickey(session, &pubkey) < 0) - tmate_fatal("ssh_get_publickey"); - key_type = ssh_key_type(pubkey); switch (key_type) { From 834fbfed0ae7a0f15fd42aa3fd7f06d1829a83e0 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 05:57:43 -0500 Subject: [PATCH 521/703] change log files prefix tmux- to tmate- --- log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log.c b/log.c index d7f6fe4b..4473ca71 100644 --- a/log.c +++ b/log.c @@ -65,7 +65,7 @@ log_open(const char *name) if (log_file != NULL) fclose(log_file); - xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid()); + xasprintf(&path, "tmate-%s-%ld.log", name, (long)getpid()); log_file = fopen(path, "w"); free(path); if (log_file == NULL) From 16e1b82cb8d36989e35765e8f36f2f7569552537 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 05:58:09 -0500 Subject: [PATCH 522/703] fix options scope --- options-table.c | 22 +++++++++++++++------- tmate-ssh-client.c | 8 ++++---- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/options-table.c b/options-table.c index 0a602ecc..12e32f8a 100644 --- a/options-table.c +++ b/options-table.c @@ -892,25 +892,21 @@ const struct options_table_entry options_table[] = { }, #ifdef TMATE - { .name = "tmate-display-time", - .type = OPTIONS_TABLE_NUMBER, - .minimum = 1, - .maximum = INT_MAX, - .default_num = 30000 - }, - { .name = "tmate-identity", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-server-host", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "master.tmate.io" }, { .name = "tmate-server-port", .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = 65535, .default_num = 22 @@ -918,18 +914,30 @@ const struct options_table_entry options_table[] = { { .name = "tmate-server-dsa-fingerprint", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "obsolete" }, { .name = "tmate-server-rsa-fingerprint", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "af:2d:81:c1:fe:49:70:2d:7f:09:a9:d7:4b:32:e3:be" }, { .name = "tmate-server-ecdsa-fingerprint", .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, .default_str = "c7:a1:51:36:d2:bb:35:4b:0a:1a:c0:43:97:74:ea:42" }, + + { .name = "tmate-display-time", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SESSION, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 30000 + }, + #endif { .name = NULL } diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 1fd474a4..00c8a199 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -83,7 +83,7 @@ static char *get_identity(void) { char *identity; - identity = options_get_string(global_s_options, "tmate-identity"); + identity = options_get_string(global_options, "tmate-identity"); if (!strlen(identity)) return NULL; @@ -167,7 +167,7 @@ static void on_session_event(struct tmate_ssh_client *client) int match; int verbosity = SSH_LOG_NOLOG + log_get_level(); - int port = options_get_number(global_s_options, "tmate-server-port"); + int port = options_get_number(global_options, "tmate-server-port"); ssh_session session = client->session; ssh_channel channel = client->channel; @@ -241,11 +241,11 @@ static void on_session_event(struct tmate_ssh_client *client) switch (key_type) { case SSH_KEYTYPE_RSA: - server_hash_str = options_get_string(global_s_options, + server_hash_str = options_get_string(global_options, "tmate-server-rsa-fingerprint"); break; case SSH_KEYTYPE_ECDSA: - server_hash_str = options_get_string(global_s_options, + server_hash_str = options_get_string(global_options, "tmate-server-ecdsa-fingerprint"); break; default: From 47340d3203c4918123504f8fd9df9ed366892630 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 22:41:41 -0500 Subject: [PATCH 523/703] Make sure tmate can't nest when ran under an older tmux --- client.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/client.c b/client.c index e24d07e8..1c22fa9f 100644 --- a/client.c +++ b/client.c @@ -210,6 +210,11 @@ client_exit_message(void) return ("unknown reason"); } +#ifdef TMATE +extern const struct cmd_entry cmd_attach_session_entry; +extern const struct cmd_entry cmd_new_session_entry; +#endif + /* Client main loop. */ int client_main(struct event_base *base, int argc, char **argv, int flags, @@ -225,6 +230,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags, char *cause, path[PATH_MAX]; struct termios tio, saved_tio; size_t size; +#ifdef TMATE + int cant_nest = 0; +#endif /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); @@ -240,6 +248,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags, } else if (argc == 0) { msg = MSG_COMMAND; cmdflags = CMD_STARTSERVER; +#ifdef TMATE + cant_nest = 1; +#endif } else { msg = MSG_COMMAND; @@ -257,10 +268,24 @@ client_main(struct event_base *base, int argc, char **argv, int flags, TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) cmdflags |= CMD_STARTSERVER; + +#ifdef TMATE + if (cmd->entry == &cmd_attach_session_entry || + cmd->entry == &cmd_new_session_entry) + cant_nest = 1; +#endif } cmd_list_free(cmdlist); } +#ifdef TMATE + if (cant_nest && getenv("TMUX")) { + fprintf(stderr, "sessions should be nested with care, " + "unset $TMUX to force\n"); + return (1); + } +#endif + /* Create client process structure (starts logging). */ client_proc = proc_start("client", base, 0, client_signal); From c04755361c617a95895bfd99758ccbc797d9ad58 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 22:56:29 -0500 Subject: [PATCH 524/703] randomize the socket name (single session for now) --- tmux.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tmux.c b/tmux.c index 528990a2..be299d62 100644 --- a/tmux.c +++ b/tmux.c @@ -115,6 +115,9 @@ make_label(const char *label) struct stat sb; u_int uid; int saved_errno; +#ifdef TMATE + int do_random_label = label == NULL; +#endif if (label == NULL) label = "default"; @@ -122,9 +125,9 @@ make_label(const char *label) uid = getuid(); if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') - xasprintf(&base, "%s/tmux-%u", s, uid); + xasprintf(&base, "%s/tmate-%u", s, uid); else - xasprintf(&base, "%s/tmux-%u", _PATH_TMP, uid); + xasprintf(&base, "%s/tmate-%u", _PATH_TMP, uid); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) goto fail; @@ -140,9 +143,18 @@ make_label(const char *label) goto fail; } +#ifdef TMATE + if (do_random_label) + label = "XXXXXX"; +#endif + if (realpath(base, resolved) == NULL) strlcpy(resolved, base, sizeof resolved); xasprintf(&path, "%s/%s", resolved, label); +#ifdef TMATE + if (do_random_label) + mktemp(path); +#endif return (path); fail: From 42ac90ca490c89f718f18a139c828bc7a0d5fc8c Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 22:58:22 -0500 Subject: [PATCH 525/703] log file rename --- tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty.c b/tty.c index da5eac4d..de1adb6b 100644 --- a/tty.c +++ b/tty.c @@ -66,7 +66,7 @@ tty_create_log(void) { char name[64]; - xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid()); + xsnprintf(name, sizeof name, "tmate-out-%ld.log", (long)getpid()); tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) From 1b4ab580f3940f2b1619d443a1df9cbde052c5e1 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 23:04:56 -0500 Subject: [PATCH 526/703] early SIGSEGV --- tmate-session.c | 1 - tmux.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tmate-session.c b/tmate-session.c index 802c9a9c..37038a16 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -110,7 +110,6 @@ void tmate_session_init(struct event_base *base) tmate_session.ev_base = base; ssh_set_log_callback(ssh_log_function); - tmate_catch_sigsegv(); tmate_encoder_init(&tmate_session.encoder); tmate_decoder_init(&tmate_session.decoder); diff --git a/tmux.c b/tmux.c index be299d62..73a80360 100644 --- a/tmux.c +++ b/tmux.c @@ -31,6 +31,7 @@ #include #include "tmux.h" +#include "tmate.h" #if defined(DEBUG) && defined(__OpenBSD__) extern char *malloc_options; @@ -219,6 +220,7 @@ main(int argc, char **argv) flags = 0; #ifdef TMATE + tmate_catch_sigsegv(); flags |= CLIENT_256COLOURS | CLIENT_UTF8; #endif From 2680e4fa04e0dd356117ee8bf2b84cc53fad62b8 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 23:17:35 -0500 Subject: [PATCH 527/703] master.tmate.io -> ssh.tmate.io --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 12e32f8a..e67d23d7 100644 --- a/options-table.c +++ b/options-table.c @@ -901,7 +901,7 @@ const struct options_table_entry options_table[] = { { .name = "tmate-server-host", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "master.tmate.io" + .default_str = "ssh.tmate.io" }, { .name = "tmate-server-port", From 75cb217c22a38406ec9fff68ee22e1bee1203e9d Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 23:18:35 -0500 Subject: [PATCH 528/703] Early tmate init for the key bindings --- server.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server.c b/server.c index ce8a7cfe..94255975 100644 --- a/server.c +++ b/server.c @@ -162,6 +162,11 @@ server_start(struct event_base *base, int lockfd, char *lockfile) RB_INIT(&sessions); TAILQ_INIT(&session_groups); mode_key_init_trees(); + +#ifdef TMATE + tmate_session_init(base); +#endif + key_bindings_init(); gettimeofday(&start_time, NULL); @@ -178,10 +183,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) close(lockfd); } -#ifdef TMATE - tmate_session_init(base); -#endif - start_cfg(); status_prompt_load_history(); From 76ab267a35d346afbdabdf03c7d22d8b3d395a23 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 23:19:03 -0500 Subject: [PATCH 529/703] fix options --- tmate-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-session.c b/tmate-session.c index 37038a16..a05d2044 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -92,7 +92,7 @@ static void lookup_and_connect(void) hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - tmate_server_host = options_get_string(global_s_options, + tmate_server_host = options_get_string(global_options, "tmate-server-host"); tmate_info("Looking up %s...", tmate_server_host); (void)evdns_getaddrinfo(tmate_session.ev_dnsbase, tmate_server_host, NULL, From 49f07dca50daf848baaf74fcbc0de0328b905b86 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 23 Dec 2015 23:19:55 -0500 Subject: [PATCH 530/703] fix options --- tmate-msg.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/tmate-msg.c b/tmate-msg.c index 6f19b798..f2566895 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -8,11 +8,13 @@ void status_message_callback(int, short, void *); static void tmate_status_message_client(struct client *c, const char *message) { struct timeval tv; - struct session *s = c->session; struct message_entry *msg, *msg1; int delay; u_int first, limit; + limit = options_get_number(global_options, "message-limit"); + delay = c->session ? options_get_number(c->session->options, "tmate-display-time") : 30000; + status_prompt_clear(c); status_message_clear(c); @@ -24,15 +26,6 @@ static void tmate_status_message_client(struct client *c, const char *message) msg->msg = xstrdup(c->message_string); TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - if (s) { - limit = options_get_number(s->options, "message-limit"); - delay = options_get_number(s->options, "tmate-display-time"); - } else { - /* Very early in the connection process we won't have a session */ - limit = options_get_number(global_s_options, "message-limit"); - delay = options_get_number(global_s_options, "tmate-display-time"); - } - first = c->message_next - limit; TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { if (msg->msg_num >= first) From e15a8a7c4681e4f975d80fc26b9f7c6686c94a9c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 24 Dec 2015 16:59:12 +0000 Subject: [PATCH 531/703] Keith Smiley is going to maintain the vim syntax file. --- README | 8 +- examples/tmux.vim | 314 ---------------------------------------------- 2 files changed, 6 insertions(+), 316 deletions(-) delete mode 100644 examples/tmux.vim diff --git a/README b/README index acf15632..6a78fa2b 100644 --- a/README +++ b/README @@ -33,8 +33,12 @@ the source tree with: Some common questions are answered in the FAQ file and a more extensive (but slightly out of date) guide is available in the OpenBSD FAQ at http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO -file and some example configurations and a Vim syntax file are in the examples -directory. +file and some example configurations are in the examples directory. + +A vim(1) syntax file is available at: + + https://github.com/keith/tmux.vim + https://raw.githubusercontent.com/keith/tmux.vim/master/syntax/tmux.vim For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. diff --git a/examples/tmux.vim b/examples/tmux.vim deleted file mode 100644 index d9b60408..00000000 --- a/examples/tmux.vim +++ /dev/null @@ -1,314 +0,0 @@ -" Vim syntax file -" Language: tmux(1) configuration file -" Maintainer: Tiago Cunha -" Last Change: $Date: 2010-07-27 18:29:07 $ -" License: This file is placed in the public domain. -" -" To install this file: -" -" - Drop the file in the syntax directory into runtimepath (such as -" ~/.vim/syntax/tmux.vim). -" - Make the filetype recognisable by adding the following to filetype.vim -" (~/.vim/filetype.vim): -" -" augroup filetypedetect -" au BufNewFile,BufRead .tmux.conf*,tmux.conf* setf tmux -" augroup END -" -" - Switch on syntax highlighting by adding "syntax enable" to .vimrc. -" - -if version < 600 - syntax clear -elseif exists("b:current_syntax") - finish -endif - -setlocal iskeyword+=- -syntax case match - -syn keyword tmuxAction any current none -syn keyword tmuxBoolean off on - -syn keyword tmuxCmds - \ attach - \ attach-session - \ bind - \ bind-key - \ break-pane - \ breakp - \ capture-pane - \ capturep - \ choose-buffer - \ choose-client - \ choose-session - \ choose-tree - \ choose-window - \ clear-history - \ clearhist - \ clock-mode - \ command-prompt - \ confirm - \ confirm-before - \ copy-mode - \ delete-buffer - \ deleteb - \ detach - \ detach-client - \ display - \ display-message - \ display-panes - \ displayp - \ find-window - \ findw - \ has - \ has-session - \ if - \ if-shell - \ info - \ join-pane - \ joinp - \ kill-pane - \ kill-server - \ kill-session - \ kill-window - \ killp - \ killw - \ last - \ last-pane - \ last-window - \ lastp - \ link-window - \ linkw - \ list-buffers - \ list-clients - \ list-commands - \ list-keys - \ list-panes - \ list-sessions - \ list-windows - \ load-buffer - \ loadb - \ lock - \ lock-client - \ lock-server - \ lock-session - \ lockc - \ locks - \ ls - \ lsb - \ lsc - \ lscm - \ lsk - \ lsp - \ lsw - \ move-pane - \ move-window - \ movep - \ movew - \ new - \ new-session - \ new-window - \ neww - \ next - \ next-layout - \ next-window - \ nextl - \ paste-buffer - \ pasteb - \ path - \ pipe-pane - \ pipep - \ prev - \ previous-layout - \ previous-window - \ prevl - \ refresh - \ refresh-client - \ rename - \ rename-session - \ rename-window - \ renamew - \ resize-pane - \ resizep - \ respawn-pane - \ respawn-window - \ respawnp - \ respawnw - \ rotate-window - \ rotatew - \ run - \ run-shell - \ save-buffer - \ saveb - \ select-layout - \ select-pane - \ select-window - \ selectl - \ selectp - \ selectw - \ send - \ send-keys - \ send-prefix - \ server-info - \ set - \ set-buffer - \ set-environment - \ set-option - \ set-window-option - \ setb - \ setenv - \ setw - \ show - \ show-buffer - \ show-environment - \ show-messages - \ show-options - \ show-window-options - \ showb - \ showenv - \ showmsgs - \ showw - \ source - \ source-file - \ split-window - \ splitw - \ start - \ start-server - \ suspend-client - \ suspendc - \ swap-pane - \ swap-window - \ swapp - \ swapw - \ switch-client - \ switchc - \ unbind - \ unbind-key - \ unlink-window - \ unlinkw - \ wait - \ wait-for - -syn keyword tmuxOptsSet - \ assume-paste-time - \ base-index - \ bell-action - \ bell-on-alert - \ buffer-limit - \ default-command - \ default-shell - \ default-terminal - \ destroy-unattached - \ detach-on-destroy - \ display-panes-active-colour - \ display-panes-colour - \ display-panes-time - \ display-time - \ escape-time - \ exit-unattached - \ focus-events - \ history-file - \ history-limit - \ lock-after-time - \ lock-command - \ message-command-style - \ message-limit - \ message-style - \ mouse - \ mouse-utf8 - \ prefix - \ prefix2 - \ quiet - \ renumber-windows - \ repeat-time - \ set-clipboard - \ set-remain-on-exit - \ set-titles - \ set-titles-string - \ status - \ status-interval - \ status-justify - \ status-keys - \ status-left - \ status-left-length - \ status-left-style - \ status-position - \ status-right - \ status-right-length - \ status-right-style - \ status-style - \ status-utf8 - \ terminal-overrides - \ update-environment - \ visual-activity - \ visual-bell - \ visual-silence - \ word-separators - -syn keyword tmuxOptsSetw - \ aggressive-resize - \ allow-rename - \ alternate-screen - \ automatic-rename - \ automatic-rename-format - \ clock-mode-colour - \ clock-mode-style - \ force-height - \ force-width - \ main-pane-height - \ main-pane-width - \ mode-keys - \ mode-style - \ monitor-activity - \ monitor-silence - \ other-pane-height - \ other-pane-width - \ pane-active-border-style - \ pane-base-index - \ pane-border-style - \ remain-on-exit - \ synchronize-panes - \ utf8 - \ window-active-style - \ window-status-activity-style - \ window-status-bell-style - \ window-status-current-format - \ window-status-current-style - \ window-status-format - \ window-status-last-style - \ window-status-separator - \ window-status-style - \ window-style - \ wrap-search - \ xterm-keys - -syn keyword tmuxTodo FIXME NOTE TODO XXX contained - -syn match tmuxKey /\(C-\|M-\|\^\)\+\S\+/ display -syn match tmuxNumber /\d\+/ display -syn match tmuxOptions /\s-\a\+/ display -syn match tmuxVariable /\w\+=/ display -syn match tmuxVariableExpansion /\${\=\w\+}\=/ display - -" Comments can span multiple lines, when the newline is escaped -" (with a single) backslash at the end. -syn region tmuxComment start=/#/ skip=/\\\@ Date: Sun, 27 Dec 2015 23:31:58 -0500 Subject: [PATCH 532/703] missing AC_DEFINE(IS_LINUX) --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index ed55b42e..e8074373 100644 --- a/configure.ac +++ b/configure.ac @@ -528,6 +528,7 @@ case "$host_os" in *linux*) AC_MSG_RESULT(linux) PLATFORM=linux + AC_DEFINE(IS_LINUX) ;; *freebsd*) AC_MSG_RESULT(freebsd) From 2a1f27eb1af6645209746659b600fc546f725605 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 28 Dec 2015 14:02:52 +0000 Subject: [PATCH 533/703] Couple of trivial style nits. --- screen.c | 4 ++-- tty.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/screen.c b/screen.c index 5551bb93..db9f52a6 100644 --- a/screen.c +++ b/screen.c @@ -94,10 +94,10 @@ screen_set_cursor_style(struct screen *s, u_int style) /* Set screen cursor colour. */ void -screen_set_cursor_colour(struct screen *s, const char *colour_string) +screen_set_cursor_colour(struct screen *s, const char *colour) { free(s->ccolour); - s->ccolour = xstrdup(colour_string); + s->ccolour = xstrdup(colour); } /* Set screen title. */ diff --git a/tty.c b/tty.c index da5eac4d..304e1378 100644 --- a/tty.c +++ b/tty.c @@ -491,7 +491,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) { int changed; - if (s != NULL && strcmp(s->ccolour, tty->ccolour)) + if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) tty_force_cursor_colour(tty, s->ccolour); if (tty->flags & TTY_NOCURSOR) From 862182df4aea3a721e359410e1d2e27ad4e517d6 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 29 Dec 2015 20:17:26 -0500 Subject: [PATCH 534/703] sync emacs/vi mode --- server.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/server.c b/server.c index 94255975..52514794 100644 --- a/server.c +++ b/server.c @@ -131,6 +131,21 @@ server_create_socket(void) return (fd); } +#ifdef TMATE +void tmate_set_editor_mode(void) +{ + switch (options_get_number(global_s_options, "status-keys")) { + case MODEKEY_EMACS: tmate_exec_cmd("set-option -g status-keys emacs"); break; + case MODEKEY_VI: tmate_exec_cmd("set-option -g status-keys vi"); break; + } + + switch (options_get_number(global_w_options, "mode-keys")) { + case MODEKEY_EMACS: tmate_exec_cmd("set-window-option -g mode-keys emacs"); break; + case MODEKEY_VI: tmate_exec_cmd("set-window-option -g mode-keys vi"); break; + } +} +#endif + /* Fork new server. */ int server_start(struct event_base *base, int lockfd, char *lockfile) @@ -183,6 +198,9 @@ server_start(struct event_base *base, int lockfd, char *lockfile) close(lockfd); } +#ifdef TMATE + tmate_set_editor_mode(); +#endif start_cfg(); status_prompt_load_history(); From a104b160b290e7b0f79efee7f6f57a37b3a3cdcc Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 30 Dec 2015 13:40:18 -0500 Subject: [PATCH 535/703] Receive raw tmux keycodes --- tmate-decoder.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ tmate.h | 3 ++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/tmate-decoder.c b/tmate-decoder.c index a3821e0f..79925566 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -105,6 +105,52 @@ static void tmate_client_pane_key(struct tmate_unpacker *uk) window_pane_key(wp, NULL, s, key, NULL); } +static struct window_pane *find_window_pane(struct session *s, unsigned int pane_id) +{ + struct window *w; + struct window_pane *wp; + struct winlink *wl; + + w = s->curw->window; + if (!w) + goto slow_path; + + wp = w->active; + if (!wp) + goto slow_path; + if (wp->id == pane_id) + return wp; + +slow_path: + RB_FOREACH(wl, winlinks, &s->windows) { + TAILQ_FOREACH(wp, &wl->window->panes, entry) { + if (wp->id == pane_id) + return wp; + } + } + + return NULL; +} + +static void tmate_client_pane_tmux_key(struct tmate_unpacker *uk) +{ + struct session *s; + struct window_pane *wp; + + int pane_id = unpack_int(uk); + key_code key = unpack_int(uk); + + s = RB_MIN(sessions, &sessions); + if (!s) + return; + + wp = find_window_pane(s, pane_id); + if (!wp) + return; + + window_pane_key(wp, NULL, s, key, NULL); +} + static void tmate_client_resize(struct tmate_unpacker *uk) { /* TODO This is sad, we might want our own client. */ @@ -187,6 +233,7 @@ static void handle_message(struct tmate_decoder *decoder, msgpack_object obj) case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(uk); break; case TMATE_CLIENT_ENV: tmate_client_env(uk); break; case TMATE_CLIENT_READY: tmate_client_ready(decoder, uk); break; + case TMATE_CLIENT_PANE_TMUX_KEY: tmate_client_pane_tmux_key(uk); break; default: decoder_error(); } } diff --git a/tmate.h b/tmate.h index 77c603cb..217b442a 100644 --- a/tmate.h +++ b/tmate.h @@ -18,7 +18,7 @@ #define TMATE_MAX_MESSAGE_SIZE (16*1024) -#define TMATE_PROTOCOL_VERSION 4 +#define TMATE_PROTOCOL_VERSION 5 enum tmate_commands { TMATE_HEADER, @@ -58,6 +58,7 @@ enum tmate_client_commands { TMATE_CLIENT_EXEC_CMD, TMATE_CLIENT_ENV, TMATE_CLIENT_READY, + TMATE_CLIENT_PANE_TMUX_KEY, }; struct tmate_decoder { From 66e4f554c323e549f39ea49b5e0394933d134141 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 11:35:36 -0500 Subject: [PATCH 536/703] Layout fixes --- server-client.c | 2 ++ tmate-decoder.c | 9 ++++++--- tmate-encoder.c | 17 +++++++++++++++-- tmux.h | 3 +++ window.c | 4 ++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/server-client.c b/server-client.c index 5565e1ab..28c2a348 100644 --- a/server-client.c +++ b/server-client.c @@ -722,6 +722,8 @@ server_client_loop(void) #ifdef TMATE if (w->flags & WINDOW_REDRAW) tmate_should_sync_layout = 1; + if (w->tmate_last_sync_active_pane != w->active) + tmate_should_sync_layout = 1; #endif w->flags &= ~WINDOW_REDRAW; diff --git a/tmate-decoder.c b/tmate-decoder.c index 79925566..7020e64d 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -105,7 +105,7 @@ static void tmate_client_pane_key(struct tmate_unpacker *uk) window_pane_key(wp, NULL, s, key, NULL); } -static struct window_pane *find_window_pane(struct session *s, unsigned int pane_id) +static struct window_pane *find_window_pane(struct session *s, int pane_id) { struct window *w; struct window_pane *wp; @@ -118,13 +118,16 @@ static struct window_pane *find_window_pane(struct session *s, unsigned int pane wp = w->active; if (!wp) goto slow_path; - if (wp->id == pane_id) + if (pane_id == -1 || (int)wp->id == pane_id) return wp; slow_path: + if (pane_id == -1) + return NULL; + RB_FOREACH(wl, winlinks, &s->windows) { TAILQ_FOREACH(wp, &wl->window->panes, entry) { - if (wp->id == pane_id) + if ((int)wp->id == pane_id) return wp; } } diff --git a/tmate-encoder.c b/tmate-encoder.c index 6f9bc0e4..da460515 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -48,9 +48,16 @@ void tmate_sync_layout(void) struct window_pane *wp; int num_panes = 0; int num_windows = 0; - int active_pane_id = -1; + int active_pane_id; int active_window_idx = -1; + /* + * TODO this can get a little heavy. + * We are shipping the full layout whenever a window name changes, + * that is, at every shell command. + * Might be better to do something incremental. + */ + /* * We only allow one session, it makes our lives easier. * Especially when the HTML5 client will come along. @@ -83,6 +90,9 @@ void tmate_sync_layout(void) if (!w) continue; + w->tmate_last_sync_active_pane = NULL; + active_pane_id = -1; + if (active_window_idx == -1) active_window_idx = wl->idx; @@ -103,8 +113,11 @@ void tmate_sync_layout(void) pack(int, wp->xoff); pack(int, wp->yoff); - if (wp == w->active) + if (wp == w->active) { + w->tmate_last_sync_active_pane = wp; active_pane_id = wp->id; + } + } pack(int, active_pane_id); } diff --git a/tmux.h b/tmux.h index 24a7db59..f2a02368 100644 --- a/tmux.h +++ b/tmux.h @@ -915,6 +915,9 @@ struct window { struct timeval activity_time; +#ifdef TMATE + struct window_pane *tmate_last_sync_active_pane; +#endif struct window_pane *active; struct window_pane *last; struct window_panes panes; diff --git a/window.c b/window.c index 6de39d8a..f0e7b53e 100644 --- a/window.c +++ b/window.c @@ -296,6 +296,10 @@ window_create1(u_int sx, u_int sy) TAILQ_INIT(&w->panes); w->active = NULL; +#ifdef TMATE + w->tmate_last_sync_active_pane = NULL; +#endif + w->lastlayout = -1; w->layout_root = NULL; From d5bd2e40f0ae4c52d10ec9b3bf9ec1dc98e89706 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 12:27:14 -0500 Subject: [PATCH 537/703] Default to 500 messages (we care about them) --- options-table.c | 4 ++++ tmate-msg.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index e67d23d7..9bb141b1 100644 --- a/options-table.c +++ b/options-table.c @@ -102,7 +102,11 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, +#ifdef TMATE + .default_num = 500 +#else .default_num = 100 +#endif }, { .name = "quiet", diff --git a/tmate-msg.c b/tmate-msg.c index f2566895..53d24ee8 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -66,7 +66,7 @@ void __tmate_status_message(const char *fmt, va_list ap) free(message); } -void printflike(1, 2) tmate_status_message(const char *fmt, ...) +void tmate_status_message(const char *fmt, ...) { va_list ap; From 5ee65eec5db29c4bb2a3556966503a9df00fc633 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 12:28:26 -0500 Subject: [PATCH 538/703] Remove comments --- status.c | 4 ---- window.c | 6 ------ 2 files changed, 10 deletions(-) diff --git a/status.c b/status.c index c51878c5..828c714e 100644 --- a/status.c +++ b/status.c @@ -581,10 +581,6 @@ status_message_set(struct client *c, const char *fmt, ...) free(msg); } -#ifdef TMATE - /* FIXME tmux: session can be NULL */ -#endif - delay = options_get_number(c->session->options, "display-time"); if (delay > 0) { tv.tv_sec = delay / 1000; diff --git a/window.c b/window.c index f0e7b53e..8dd8243a 100644 --- a/window.c +++ b/window.c @@ -983,12 +983,6 @@ window_pane_read_callback(__unused struct bufferevent *bufev, void *data) new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { -#ifdef TMATE - /* FIXME tmux: - * - new_data - * + new_data + wp->pipe_off; - */ -#endif new_data = EVBUFFER_DATA(evb); bufferevent_write(wp->pipe_event, new_data, new_size); } From f84d32ca5e7b3820134c4fa8296c9c85aa015b9a Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 31 Dec 2015 18:14:13 +0000 Subject: [PATCH 539/703] Use saved pipe buffer offset when writing to pipe, from Nicolas Viennot. --- window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window.c b/window.c index 67d047b9..cfb44ae3 100644 --- a/window.c +++ b/window.c @@ -948,7 +948,7 @@ window_pane_read_callback(__unused struct bufferevent *bufev, void *data) new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(evb); + new_data = EVBUFFER_DATA(evb) + wp->pipe_off; bufferevent_write(wp->pipe_event, new_data, new_size); } From 7e67db79dca50184ee9dec4456ccf38756201a9e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 31 Dec 2015 18:34:47 +0000 Subject: [PATCH 540/703] Remove an extra unzoom call which was probably a merge error. --- cmd-select-pane.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 6ebe753c..02385a41 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -85,12 +85,6 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - server_unzoom_window(wp->window); - if (!window_pane_visible(wp)) { - cmdq_error(cmdq, "pane not visible"); - return (CMD_RETURN_ERROR); - } - if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) return (CMD_RETURN_NORMAL); From e61d3aa18f5df4a1d73a200fa541306fcdaf1df2 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 12:19:57 -0500 Subject: [PATCH 541/703] Fix pipe buffer offset --- window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window.c b/window.c index 8dd8243a..0a1dda21 100644 --- a/window.c +++ b/window.c @@ -983,7 +983,7 @@ window_pane_read_callback(__unused struct bufferevent *bufev, void *data) new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(evb); + new_data = EVBUFFER_DATA(evb) + wp->pipe_off; bufferevent_write(wp->pipe_event, new_data, new_size); } From 058906c24207e74f62a15277e2eaee7a8cdbf3c2 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 12:58:51 -0500 Subject: [PATCH 542/703] nits --- tmate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate.h b/tmate.h index 217b442a..a44afa58 100644 --- a/tmate.h +++ b/tmate.h @@ -12,7 +12,7 @@ #define tmate_debug(...) log_debug("[tmate] D " __VA_ARGS__) #define tmate_warn(...) log_debug("[tmate] W " __VA_ARGS__) #define tmate_info(...) log_debug("[tmate] I " __VA_ARGS__) -#define tmate_fatal(...) fatal("[tmate]" __VA_ARGS__) +#define tmate_fatal(...) fatalx("[tmate] " __VA_ARGS__) /* tmate-encoder.c */ From fc46660c96ac05e6523308815212d43837e0a305 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 13:55:02 -0500 Subject: [PATCH 543/703] make sure ssh passphrase doesn't get synced --- tmate-encoder.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tmate-encoder.c b/tmate-encoder.c index da460515..266e72d5 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -215,7 +215,8 @@ void tmate_sync_copy_mode(struct window_pane *wp) pack(int, wp->id); - if (wp->mode != &window_copy_mode) { + if (wp->mode != &window_copy_mode || + data->inputtype == WINDOW_COPY_PASSWORD) { pack(array, 0); return; } From 55e6a774a8549e4aedbee929b56a94e2efd2beff Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 18:21:02 -0500 Subject: [PATCH 544/703] improve err msg --- tmate-ssh-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 00c8a199..76ebe4a4 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -287,7 +287,7 @@ static void on_session_event(struct tmate_ssh_client *client) !client->tried_passphrase) request_passphrase(client); else - kill_session(client, "Access denied. Check your SSH keys."); + kill_session(client, "SSH keys not found. Run 'ssh-keygen' to create keys and try again."); return; case SSH_AUTH_ERROR: reconnect_session(client, "Auth error: %s", From 4c5522f7fabf086b059dc8cf084d555863020c2f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 18:21:14 -0500 Subject: [PATCH 545/703] ssh key passphrase fix --- tmate-ssh-client.c | 6 ++++-- window-copy.c | 11 +++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 76ebe4a4..d420f27e 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -283,11 +283,13 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: - if (client->tmate_session->need_passphrase && - !client->tried_passphrase) + if (client->tmate_session->need_passphrase) request_passphrase(client); else kill_session(client, "SSH keys not found. Run 'ssh-keygen' to create keys and try again."); + + if (client->tried_passphrase) + tmate_status_message("Can't load SSH key. Try typing passphrase again in case of typo. ctrl-c to abort."); return; case SSH_AUTH_ERROR: reconnect_session(client, "Auth error: %s", diff --git a/window-copy.c b/window-copy.c index 2d6cd77b..c313f50d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -669,6 +669,10 @@ __window_copy_key(struct window_pane *wp, struct client *c, struct session *sess case MODEKEYCOPY_SEARCHAGAIN: case MODEKEYCOPY_SEARCHREVERSE: switch (data->searchtype) { +#ifdef TMATE + case WINDOW_COPY_PASSWORD: + break; +#endif case WINDOW_COPY_OFF: case WINDOW_COPY_GOTOLINE: case WINDOW_COPY_JUMPFORWARD: @@ -677,10 +681,6 @@ __window_copy_key(struct window_pane *wp, struct client *c, struct session *sess case WINDOW_COPY_JUMPTOBACK: case WINDOW_COPY_NAMEDBUFFER: case WINDOW_COPY_NUMERICPREFIX: -#ifdef TMATE - case WINDOW_COPY_PASSWORD: - break; -#endif case WINDOW_COPY_SEARCHUP: ss = data->searchstr; if (cmd == MODEKEYCOPY_SEARCHAGAIN) { @@ -850,9 +850,8 @@ window_copy_key_input(struct window_pane *wp, key_code key) data->password_cb(data->inputstr, data->password_cb_private); } - *data->inputstr = '\0'; - window_copy_copy_selection(wp, NULL); window_pane_reset_mode(wp); + return 0; #endif } data->numprefix = -1; From 2e0d82648f8d5c73808f95c1c5a62c0ccfd78a6e Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 13:14:32 -0500 Subject: [PATCH 546/703] Fix message log appends The following has been improved: 1) Previous code had "u_int first" which is incorrect as "first = c->message_next - limit" would result in negative values, resulting in dropping the first limit messages. 2) Avoid to traverse the entire list of message to prune messages. --- status.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/status.c b/status.c index 828c714e..5b349c4f 100644 --- a/status.c +++ b/status.c @@ -555,7 +555,7 @@ status_message_set(struct client *c, const char *fmt, ...) struct message_entry *msg, *msg1; va_list ap; int delay; - u_int first, limit; + u_int limit; limit = options_get_number(global_options, "message-limit"); @@ -572,10 +572,9 @@ status_message_set(struct client *c, const char *fmt, ...) msg->msg = xstrdup(c->message_string); TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - first = c->message_next - limit; TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { - if (msg->msg_num >= first) - continue; + if (msg->msg_num + limit >= c->message_next) + break; free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); From aef747041813774424aa8a89cf5e046f518c09d7 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 31 Dec 2015 19:55:12 -0500 Subject: [PATCH 547/703] Fix status message --- tmate-msg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tmate-msg.c b/tmate-msg.c index 53d24ee8..665a7034 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -13,7 +13,8 @@ static void tmate_status_message_client(struct client *c, const char *message) u_int first, limit; limit = options_get_number(global_options, "message-limit"); - delay = c->session ? options_get_number(c->session->options, "tmate-display-time") : 30000; + delay = options_get_number(c->session ? c->session->options : global_s_options, + "tmate-display-time"); status_prompt_clear(c); status_message_clear(c); @@ -26,10 +27,9 @@ static void tmate_status_message_client(struct client *c, const char *message) msg->msg = xstrdup(c->message_string); TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - first = c->message_next - limit; TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { - if (msg->msg_num >= first) - continue; + if (msg->msg_num + limit >= c->message_next) + break; free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); From 311be04d6155226b5bbdd8f7616d7df0647c2a0d Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 1 Jan 2016 08:04:20 +0000 Subject: [PATCH 548/703] Don't rely on a calculation wrapping when applying message-limit, and break out of the loop early. From Nicolas Viennot. --- status.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/status.c b/status.c index ca78bd53..cbacfe4c 100644 --- a/status.c +++ b/status.c @@ -547,7 +547,7 @@ status_message_set(struct client *c, const char *fmt, ...) struct message_entry *msg, *msg1; va_list ap; int delay; - u_int first, limit; + u_int limit; limit = options_get_number(global_options, "message-limit"); @@ -564,10 +564,9 @@ status_message_set(struct client *c, const char *fmt, ...) msg->msg = xstrdup(c->message_string); TAILQ_INSERT_TAIL(&c->message_log, msg, entry); - first = c->message_next - limit; TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { - if (msg->msg_num >= first) - continue; + if (msg->msg_num + limit >= c->message_next) + break; free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); From a87d3af3094b6ef02cb42a08f2673e7a17937310 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 1 Jan 2016 16:44:01 -0500 Subject: [PATCH 549/703] Reusing protocol code from tmate-slave --- Makefile.am | 1 + cmd-wait-for.c | 2 +- resize.c | 4 + server.c | 2 +- tmate-debug.c | 4 +- tmate-decoder.c | 175 +++++++------------------------------ tmate-encoder.c | 50 +++-------- tmate-msg.c | 2 +- tmate-msgpack.c | 206 ++++++++++++++++++++++++++++++++++++++++++++ tmate-protocol.h | 89 +++++++++++++++++++ tmate-session.c | 36 ++++---- tmate-ssh-client.c | 210 ++++++++++++++++++++++----------------------- tmate.h | 103 +++++++++++++--------- 13 files changed, 531 insertions(+), 353 deletions(-) create mode 100644 tmate-msgpack.c create mode 100644 tmate-protocol.h diff --git a/Makefile.am b/Makefile.am index a85c3778..630317bc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -177,6 +177,7 @@ dist_tmate_SOURCES = \ tmate-decoder.c \ tmate-env.c \ tmate-msg.c \ + tmate-msgpack.c \ tmate-session.c \ tmux.c \ tty-acs.c \ diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 5e23a1b0..e1694f4b 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -193,7 +193,7 @@ cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct client *c = cmdq->client; #ifdef TMATE - if (!strcmp(name, "tmate-ready") && tmate_session.decoder.ready) + if (!strcmp(name, "tmate-ready") && tmate_session.tmate_env_ready) return (CMD_RETURN_NORMAL); #endif diff --git a/resize.c b/resize.c index 7d9a2551..3bc3b550 100644 --- a/resize.c +++ b/resize.c @@ -52,6 +52,10 @@ recalculate_sizes(void) struct window_pane *wp; u_int ssx, ssy, has, limit; int flag, has_status, is_zoomed, forced; +#ifdef TMATE + int tmate_sx = tmate_session.min_sx; + int tmate_sy = tmate_session.min_sy; +#endif RB_FOREACH(s, sessions, &sessions) { has_status = options_get_number(s->options, "status"); diff --git a/server.c b/server.c index 52514794..0893fab4 100644 --- a/server.c +++ b/server.c @@ -132,7 +132,7 @@ server_create_socket(void) } #ifdef TMATE -void tmate_set_editor_mode(void) +static void tmate_set_editor_mode(void) { switch (options_get_number(global_s_options, "status-keys")) { case MODEKEY_EMACS: tmate_exec_cmd("set-option -g status-keys emacs"); break; diff --git a/tmate-debug.c b/tmate-debug.c index 61a8f676..f1fd971f 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -53,7 +53,7 @@ static int print_resolved_stack_frame(const char *frame) } #endif -void tmate_print_trace(void) +void tmate_print_stack_trace(void) { void *array[20]; size_t size; @@ -80,7 +80,7 @@ static void handle_sigsegv(__unused int sig) { /* TODO send stack trace to server */ tmate_info("CRASH, printing stack trace"); - tmate_print_trace(); + tmate_print_stack_trace(); tmate_fatal("CRASHED"); } diff --git a/tmate-decoder.c b/tmate-decoder.c index 7020e64d..c387b362 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -1,88 +1,16 @@ #include "tmate.h" +#include "tmate-protocol.h" -int tmate_sx = -1; -int tmate_sy = -1; - -struct tmate_unpacker { - msgpack_object *argv; - int argc; -}; - -static void decoder_error(void) -{ - /* TODO Don't kill the session, disconnect */ - tmate_fatal("Received a bad message"); -} - -static void init_unpacker(struct tmate_unpacker *uk, - msgpack_object obj) -{ - if (obj.type != MSGPACK_OBJECT_ARRAY) - decoder_error(); - - uk->argv = obj.via.array.ptr; - uk->argc = obj.via.array.size; -} - -static int64_t unpack_int(struct tmate_unpacker *uk) -{ - int64_t val; - - if (uk->argc == 0) - decoder_error(); - - if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER && - uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER) - decoder_error(); - - val = uk->argv[0].via.i64; - - uk->argv++; - uk->argc--; - - return val; -} - -static void unpack_raw(struct tmate_unpacker *uk, - const char **buf, size_t *len) -{ - if (uk->argc == 0) - decoder_error(); - - if (uk->argv[0].type != MSGPACK_OBJECT_STR && - uk->argv[0].type != MSGPACK_OBJECT_BIN) - decoder_error(); - - *len = uk->argv[0].via.str.size; - *buf = uk->argv[0].via.str.ptr; - - uk->argv++; - uk->argc--; -} - -static char *unpack_string(struct tmate_unpacker *uk) -{ - const char *buf; - char *alloc_buf; - size_t len; - - unpack_raw(uk, &buf, &len); - - alloc_buf = xmalloc(len + 1); - memcpy(alloc_buf, buf, len); - alloc_buf[len] = '\0'; - - return alloc_buf; -} - -static void tmate_notify(struct tmate_unpacker *uk) +static void handle_notify(__unused struct tmate_session *session, + struct tmate_unpacker *uk) { char *msg = unpack_string(uk); tmate_status_message("%s", msg); free(msg); } -static void tmate_client_pane_key(struct tmate_unpacker *uk) +static void handle_legacy_pane_key(__unused struct tmate_session *_session, + struct tmate_unpacker *uk) { struct session *s; struct window *w; @@ -135,7 +63,8 @@ slow_path: return NULL; } -static void tmate_client_pane_tmux_key(struct tmate_unpacker *uk) +static void handle_pane_key(__unused struct tmate_session *_session, + struct tmate_unpacker *uk) { struct session *s; struct window_pane *wp; @@ -154,20 +83,19 @@ static void tmate_client_pane_tmux_key(struct tmate_unpacker *uk) window_pane_key(wp, NULL, s, key, NULL); } -static void tmate_client_resize(struct tmate_unpacker *uk) +static void handle_resize(struct tmate_session *session, + struct tmate_unpacker *uk) { - /* TODO This is sad, we might want our own client. */ - tmate_sx = unpack_int(uk); - tmate_sy = unpack_int(uk); + session->min_sx = unpack_int(uk); + session->min_sy = unpack_int(uk); recalculate_sizes(); - - /* TODO Handle reconnection cases */ } extern char **cfg_causes; extern u_int cfg_ncauses; -static void tmate_client_exec_cmd(struct tmate_unpacker *uk) +static void handle_exec_cmd(__unused struct tmate_session *session, + struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; @@ -202,7 +130,8 @@ out: free(cmd_str); } -static void tmate_client_env(struct tmate_unpacker *uk) +static void handle_set_env(__unused struct tmate_session *session, + struct tmate_unpacker *uk) { char *name = unpack_string(uk); char *value = unpack_string(uk); @@ -213,68 +142,26 @@ static void tmate_client_env(struct tmate_unpacker *uk) free(value); } - -extern void signal_waiting_clients(const char *name); -static void tmate_client_ready(struct tmate_decoder *decoder, - __unused struct tmate_unpacker *uk) +static void handle_ready(__unused struct tmate_session *session, + __unused struct tmate_unpacker *uk) { - decoder->ready = 1; + session->tmate_env_ready = 1; signal_waiting_clients("tmate-ready"); } -static void handle_message(struct tmate_decoder *decoder, msgpack_object obj) +void tmate_dispatch_slave_message(struct tmate_session *session, + struct tmate_unpacker *uk) { - struct tmate_unpacker _uk; - struct tmate_unpacker *uk = &_uk; - - init_unpacker(uk, obj); - - switch (unpack_int(uk)) { - case TMATE_NOTIFY: tmate_notify(uk); break; - case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; - case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; - case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(uk); break; - case TMATE_CLIENT_ENV: tmate_client_env(uk); break; - case TMATE_CLIENT_READY: tmate_client_ready(decoder, uk); break; - case TMATE_CLIENT_PANE_TMUX_KEY: tmate_client_pane_tmux_key(uk); break; - default: decoder_error(); + int cmd = unpack_int(uk); + switch (cmd) { +#define dispatch(c, f) case c: f(session, uk); break + dispatch(TMATE_IN_NOTIFY, handle_notify); + dispatch(TMATE_IN_LEGACY_PANE_KEY, handle_legacy_pane_key); + dispatch(TMATE_IN_RESIZE, handle_resize); + dispatch(TMATE_IN_EXEC_CMD, handle_exec_cmd); + dispatch(TMATE_IN_SET_ENV, handle_set_env); + dispatch(TMATE_IN_READY, handle_ready); + dispatch(TMATE_IN_PANE_KEY, handle_pane_key); + default: tmate_fatal("Bad message type: %d", cmd); } } - -void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) -{ - msgpack_unpacked result; - - msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); - - msgpack_unpacked_init(&result); - while (msgpack_unpacker_next(&decoder->unpacker, &result)) { - handle_message(decoder, result.data); - } - msgpack_unpacked_destroy(&result); - - if (msgpack_unpacker_message_size(&decoder->unpacker) > - TMATE_MAX_MESSAGE_SIZE) { - tmate_fatal("Message too big"); - } -} - -void tmate_decoder_get_buffer(struct tmate_decoder *decoder, - char **buf, size_t *len) -{ - /* rewind the buffer if possible */ - if (msgpack_unpacker_buffer_capacity(&decoder->unpacker) < - TMATE_MAX_MESSAGE_SIZE) { - msgpack_unpacker_expand_buffer(&decoder->unpacker, 0); - } - - *buf = msgpack_unpacker_buffer(&decoder->unpacker); - *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); -} - -void tmate_decoder_init(struct tmate_decoder *decoder) -{ - if (!msgpack_unpacker_init(&decoder->unpacker, 2*TMATE_MAX_MESSAGE_SIZE)) - tmate_fatal("cannot initialize the unpacker"); - decoder->ready = 0; -} diff --git a/tmate-encoder.c b/tmate-encoder.c index 266e72d5..8d47f2ef 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -1,41 +1,13 @@ #include "tmate.h" +#include "tmate-protocol.h" #include "window-copy.h" -#define DEFAULT_ENCODER (&tmate_session.encoder) - -static int msgpack_write(void *data, const char *buf, size_t len) -{ - struct tmate_encoder *encoder = data; - - evbuffer_add(encoder->buffer, buf, len); - - if ((encoder->ev_readable.ev_flags & EVLIST_INSERTED) && - !(encoder->ev_readable.ev_flags & EVLIST_ACTIVE)) { - event_active(&encoder->ev_readable, EV_READ, 0); - } - - return 0; -} - -void tmate_encoder_init(struct tmate_encoder *encoder) -{ - msgpack_packer_init(&encoder->pk, encoder, &msgpack_write); - encoder->ev_readable.ev_flags = 0; - encoder->buffer = evbuffer_new(); -} - -#define msgpack_pack_string(pk, str) do { \ - int __strlen = strlen(str); \ - msgpack_pack_str(pk, __strlen); \ - msgpack_pack_str_body(pk, str, __strlen); \ -} while(0) - -#define pack(what, ...) msgpack_pack_##what(&DEFAULT_ENCODER->pk, __VA_ARGS__) +#define pack(what, ...) _pack(&tmate_session.encoder, what, __VA_ARGS__) void tmate_write_header(void) { pack(array, 3); - pack(int, TMATE_HEADER); + pack(int, TMATE_OUT_HEADER); pack(int, TMATE_PROTOCOL_VERSION); pack(string, VERSION); } @@ -79,7 +51,7 @@ void tmate_sync_layout(void) return; pack(array, 5); - pack(int, TMATE_SYNC_LAYOUT); + pack(int, TMATE_OUT_SYNC_LAYOUT); pack(int, s->sx); pack(int, s->sy); @@ -134,12 +106,12 @@ void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) { size_t max_write, to_write; - max_write = TMATE_MAX_MESSAGE_SIZE - 4; + max_write = TMATE_MAX_MESSAGE_SIZE - 16; while (len > 0) { to_write = len < max_write ? len : max_write; pack(array, 3); - pack(int, TMATE_PTY_DATA); + pack(int, TMATE_OUT_PTY_DATA); pack(int, wp->id); pack(str, to_write); pack(str_body, buf, to_write); @@ -175,14 +147,14 @@ int tmate_should_replicate_cmd(const struct cmd_entry *cmd) void tmate_exec_cmd(const char *cmd) { pack(array, 2); - pack(int, TMATE_EXEC_CMD); + pack(int, TMATE_OUT_EXEC_CMD); pack(string, cmd); } void tmate_failed_cmd(int client_id, const char *cause) { pack(array, 3); - pack(int, TMATE_FAILED_CMD); + pack(int, TMATE_OUT_FAILED_CMD); pack(int, client_id); pack(string, cause); } @@ -196,7 +168,7 @@ void tmate_status(const char *left, const char *right) return; pack(array, 3); - pack(int, TMATE_STATUS); + pack(int, TMATE_OUT_STATUS); pack(string, left); pack(string, right); @@ -211,7 +183,7 @@ void tmate_sync_copy_mode(struct window_pane *wp) struct window_copy_mode_data *data = wp->modedata; pack(array, 3); - pack(int, TMATE_SYNC_COPY_MODE); + pack(int, TMATE_OUT_SYNC_COPY_MODE); pack(int, wp->id); @@ -248,7 +220,7 @@ void tmate_sync_copy_mode(struct window_pane *wp) void tmate_write_copy_mode(struct window_pane *wp, const char *str) { pack(array, 3); - pack(int, TMATE_WRITE_COPY_MODE); + pack(int, TMATE_OUT_WRITE_COPY_MODE); pack(int, wp->id); pack(string, str); } diff --git a/tmate-msg.c b/tmate-msg.c index 665a7034..880e3905 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -10,7 +10,7 @@ static void tmate_status_message_client(struct client *c, const char *message) struct timeval tv; struct message_entry *msg, *msg1; int delay; - u_int first, limit; + u_int limit; limit = options_get_number(global_options, "message-limit"); delay = options_get_number(c->session ? c->session->options : global_s_options, diff --git a/tmate-msgpack.c b/tmate-msgpack.c new file mode 100644 index 00000000..dbf14148 --- /dev/null +++ b/tmate-msgpack.c @@ -0,0 +1,206 @@ +#include "tmate.h" +#include "tmate-protocol.h" + +static void on_encoder_buffer_ready(__unused evutil_socket_t fd, + __unused short what, void *arg) +{ + struct tmate_encoder *encoder = arg; + + encoder->ev_active = false; + if (encoder->ready_callback) + encoder->ready_callback(encoder->userdata, encoder->buffer); +} + +static int on_encoder_write(void *userdata, const char *buf, size_t len) +{ + struct tmate_encoder *encoder = userdata; + + if (evbuffer_add(encoder->buffer, buf, len) < 0) + tmate_fatal("Cannot buffer encoded data"); + + if (!encoder->ev_active) { + event_active(&encoder->ev_buffer, EV_READ, 0); + encoder->ev_active = true; + } + + return 0; +} + +/* Really sad hack, but we can get away with it */ +#define tmate_encoder_from_pk(pk) ((struct tmate_encoder *)pk) + +void msgpack_pack_string(msgpack_packer *pk, const char *str) +{ + size_t len = strlen(str); + + msgpack_pack_str(pk, len); + msgpack_pack_str_body(pk, str, len); +} + +void msgpack_pack_boolean(msgpack_packer *pk, bool value) +{ + if (value) + msgpack_pack_true(pk); + else + msgpack_pack_false(pk); +} + +void tmate_encoder_init(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata) +{ + msgpack_packer_init(&encoder->pk, encoder, &on_encoder_write); + encoder->buffer = evbuffer_new(); + encoder->ready_callback = callback; + encoder->userdata = userdata; + + if (!encoder->buffer) + tmate_fatal("Can't allocate buffer"); + + event_set(&encoder->ev_buffer, -1, + EV_READ | EV_PERSIST, on_encoder_buffer_ready, encoder); + + event_add(&encoder->ev_buffer, NULL); + + encoder->ev_active = false; +} + +void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata) +{ + encoder->ready_callback = callback; + encoder->userdata = userdata; + encoder->ready_callback(encoder->userdata, encoder->buffer); +} + +void tmate_decoder_error(void) +{ + /* TODO Don't kill the session, disconnect */ + tmate_print_stack_trace(); + tmate_fatal("Received a bad message"); +} + +void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj) +{ + if (obj.type != MSGPACK_OBJECT_ARRAY) + tmate_decoder_error(); + + uk->argv = obj.via.array.ptr; + uk->argc = obj.via.array.size; +} + +int64_t unpack_int(struct tmate_unpacker *uk) +{ + int64_t val; + + if (uk->argc == 0) + tmate_decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER && + uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER) + tmate_decoder_error(); + + val = uk->argv[0].via.i64; + + uk->argv++; + uk->argc--; + + return val; +} + +bool unpack_bool(struct tmate_unpacker *uk) +{ + bool val; + + if (uk->argc == 0) + tmate_decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_BOOLEAN) + tmate_decoder_error(); + + val = uk->argv[0].via.boolean; + + uk->argv++; + uk->argc--; + + return val; +} + +void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len) +{ + if (uk->argc == 0) + tmate_decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_STR && + uk->argv[0].type != MSGPACK_OBJECT_BIN) + tmate_decoder_error(); + + *len = uk->argv[0].via.str.size; + *buf = uk->argv[0].via.str.ptr; + + uk->argv++; + uk->argc--; +} + +char *unpack_string(struct tmate_unpacker *uk) +{ + const char *buf; + char *alloc_buf; + size_t len; + + unpack_buffer(uk, &buf, &len); + + alloc_buf = xmalloc(len + 1); + memcpy(alloc_buf, buf, len); + alloc_buf[len] = '\0'; + + return alloc_buf; +} + +void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested) +{ + if (uk->argc == 0) + tmate_decoder_error(); + + init_unpacker(nested, uk->argv[0]); + + uk->argv++; + uk->argc--; +} + +void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, + void *userdata) +{ + if (!msgpack_unpacker_init(&decoder->unpacker, TMATE_MAX_MESSAGE_SIZE)) + tmate_fatal("Cannot initialize the unpacker"); + decoder->reader = reader; + decoder->userdata = userdata; +} + +void tmate_decoder_get_buffer(struct tmate_decoder *decoder, + char **buf, size_t *len) +{ + ssize_t current_size = msgpack_unpacker_message_size(&decoder->unpacker); + if (!msgpack_unpacker_reserve_buffer(&decoder->unpacker, + TMATE_MAX_MESSAGE_SIZE - current_size)) + tmate_fatal("cannot expand decoder buffer"); + + *buf = msgpack_unpacker_buffer(&decoder->unpacker); + *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); +} + +void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) +{ + struct tmate_unpacker _uk, *uk = &_uk; + msgpack_unpacked result; + + msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); + + msgpack_unpacked_init(&result); + while (msgpack_unpacker_next(&decoder->unpacker, &result)) { + init_unpacker(uk, result.data); + decoder->reader(decoder->userdata, uk); + } + msgpack_unpacked_destroy(&result); +} diff --git a/tmate-protocol.h b/tmate-protocol.h new file mode 100644 index 00000000..8611d9a7 --- /dev/null +++ b/tmate-protocol.h @@ -0,0 +1,89 @@ +#ifndef TMATE_PROTOCOL_H +#define TMATE_PROTOCOL_H + +#define TMATE_MAX_MESSAGE_SIZE (16*1024) + +enum tmate_control_out_msg_types { + TMATE_CTL_AUTH, + TMATE_CTL_DEAMON_OUT_MSG, + TMATE_CTL_SNAPSHOT, + TMATE_CTL_CLIENT_JOIN, + TMATE_CTL_CLIENT_LEFT, + TMATE_CTL_EXEC, +}; + +/* +[TMATE_CTL_AUTH, int: ctl_proto_version, string: ip_address, string: pubkey, + string: session_token, string: session_token_ro] +[TMATE_CTL_DEAMON_OUT_MSG, object: msg] +[TMATE_CTL_SNAPSHOT, [[int: pane_id, [int: cur_x, int: cur_y], int: mode, + [[string: line_utf8, [int: char_attr, ...]], ...], ...], ...]] +[TMATE_CTL_CLIENT_JOIN, int: client_id, string: ip_address, string: pubkey, boolean: readonly] +[TMATE_CTL_CLIENT_LEFT, int: client_id] +[TMATE_CTL_EXEC, string: username, string: ip_address, string: pubkey, string: command] +*/ + +enum tmate_control_in_msg_types { + TMATE_CTL_DEAMON_FWD_MSG, + TMATE_CTL_REQUEST_SNAPSHOT, + TMATE_CTL_PANE_KEYS, + TMATE_CTL_RESIZE, + TMATE_CTL_EXEC_RESPONSE, +}; + +/* +[TMATE_CTL_DEAMON_FWD_MSG, object: msg] +[TMATE_CTL_REQUEST_SNAPSHOT, int: max_history_lines] +[TMATE_CTL_PANE_KEYS, int: pane_id, string: keys] +[TMATE_CTL_RESIZE, int: sx, int: sy] // sx == -1: no clients +[TMATE_CTL_EXEC_RESPONSE, int: exit_code, string: message] +*/ + +enum tmate_daemon_out_msg_types { + TMATE_OUT_HEADER, + TMATE_OUT_SYNC_LAYOUT, + TMATE_OUT_PTY_DATA, + TMATE_OUT_EXEC_CMD, + TMATE_OUT_FAILED_CMD, + TMATE_OUT_STATUS, + TMATE_OUT_SYNC_COPY_MODE, + TMATE_OUT_WRITE_COPY_MODE, +}; + +/* +[TMATE_OUT_HEADER, int: proto_version, string: version] +[TMATE_OUT_SYNC_LAYOUT, [int: sx, int: sy, [[int: win_id, string: win_name, + [[int: pane_id, int: sx, int: sy, int: xoff, int: yoff], ...], + int: active_pane_id], ...], int: active_win_id] +[TMATE_OUT_PTY_DATA, int: pane_id, binary: buffer] +[TMATE_OUT_EXEC_CMD, string: cmd] +[TMATE_OUT_FAILED_CMD, int: client_id, string: cause] +[TMATE_OUT_STATUS, string: left, string: right] +[TMATE_OUT_SYNC_COPY_MODE, int: pane_id, [int: backing, int: oy, int: cx, int: cy, + [int: selx, int: sely, int: flags], + [int: type, string: input_prompt, string: input_str]]) + // Any of the array can be [] +[TMATE_OUT_WRITE_COPY_MODE, int: pane_id, string: str] +*/ + +enum tmate_daemon_in_msg_types { + TMATE_IN_NOTIFY, + TMATE_IN_LEGACY_PANE_KEY, + TMATE_IN_RESIZE, + TMATE_IN_EXEC_CMD, + TMATE_IN_SET_ENV, + TMATE_IN_READY, + TMATE_IN_PANE_KEY, +}; + +/* +[TMATE_IN_NOTIFY, string: msg] +[TMATE_IN_PANE_KEY, int: key] +[TMATE_IN_RESIZE, int: sx, int: sy] // sx == -1: no clients +[TMATE_IN_EXEC_CMD, int: client_id, string: cmd] +[TMATE_IN_SET_ENV, string: name, string: value] +[TMATE_IN_READY] +[TMATE_IN_PANE_KEY, int: pane_id, uint64 keycode] // pane_id == -1: active pane +*/ + +#endif diff --git a/tmate-session.c b/tmate-session.c index a05d2044..c96b4fa7 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -99,27 +99,31 @@ static void lookup_and_connect(void) &hints, dns_cb, (void *)tmate_server_host); } -static void ssh_log_function(int priority, const char *function, - const char *buffer, __unused void *userdata) +static void __tmate_session_init(struct tmate_session *session, + struct event_base *base) { - tmate_debug("[%d] [%s] %s", priority, function, buffer); + memset(session, 0, sizeof(*session)); + + session->ev_base = base; + + /* + * Early initialization of encoder because we need to parse + * config files to get the server configs, but while we are parsing + * config files, we need to buffer bind commands and all for the + * slave. + * Decoder is setup later. + */ + tmate_encoder_init(&session->encoder, NULL, &tmate_session); + + session->min_sx = -1; + session->min_sy = -1; + + TAILQ_INIT(&session->clients); } void tmate_session_init(struct event_base *base) { - tmate_session.ev_base = base; - - ssh_set_log_callback(ssh_log_function); - - tmate_encoder_init(&tmate_session.encoder); - tmate_decoder_init(&tmate_session.decoder); - - TAILQ_INIT(&tmate_session.clients); - - tmate_session.need_passphrase = 0; - tmate_session.passphrase = NULL; - - /* The header will be written as soon as the first client connects */ + __tmate_session_init(&tmate_session, base); tmate_write_header(); } diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index d420f27e..2960fe8f 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -7,43 +7,15 @@ #include "tmate.h" #include "window-copy.h" -static void consume_channel(struct tmate_ssh_client *client); -static void flush_input_stream(struct tmate_ssh_client *client); -static void __flush_input_stream(evutil_socket_t fd, short what, void *arg); -static void __on_session_event(evutil_socket_t fd, short what, void *arg); -static void printflike(2, 3) kill_session(struct tmate_ssh_client *client, - const char *fmt, ...); -static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client, - const char *fmt, ...); -static void on_session_event(struct tmate_ssh_client *client); +static void on_ssh_client_event(struct tmate_ssh_client *client); +static void __on_ssh_client_event(evutil_socket_t fd, short what, void *arg); -static void register_session_fd_event(struct tmate_ssh_client *client) -{ - if (!event_initialized(&client->ev_ssh)) { - int flag = 1; - setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, - TCP_NODELAY, &flag, sizeof(flag)); +static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client, + const char *fmt, ...); +static void printflike(2, 3) reconnect_ssh_client(struct tmate_ssh_client *client, + const char *fmt, ...); - event_assign(&client->ev_ssh, client->tmate_session->ev_base, - ssh_get_fd(client->session), - EV_READ | EV_PERSIST, __on_session_event, client); - event_add(&client->ev_ssh, NULL); - } -} - -static void register_input_stream_event(struct tmate_ssh_client *client) -{ - struct tmate_encoder *encoder = &client->tmate_session->encoder; - - if (!event_initialized(&encoder->ev_readable)) { - event_assign(&encoder->ev_readable, client->tmate_session->ev_base, -1, - EV_READ | EV_PERSIST, __flush_input_stream, client); - event_add(&encoder->ev_readable, NULL); - client->has_encoder = 1; - } -} - -static void consume_channel(struct tmate_ssh_client *client) +static void read_channel(struct tmate_ssh_client *client) { struct tmate_decoder *decoder = &client->tmate_session->decoder; char *buf; @@ -53,7 +25,7 @@ static void consume_channel(struct tmate_ssh_client *client) tmate_decoder_get_buffer(decoder, &buf, &len); len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); if (len < 0) { - reconnect_session(client, "Error reading from channel: %s", + reconnect_ssh_client(client, "Error reading from channel: %s", ssh_get_error(client->session)); break; } @@ -65,8 +37,43 @@ static void consume_channel(struct tmate_ssh_client *client) } } -static void connection_complete(struct tmate_ssh_client *connected_client) +static void on_decoder_read(void *userdata, struct tmate_unpacker *uk) { + struct tmate_ssh_client *client = userdata; + tmate_dispatch_slave_message(client->tmate_session, uk); +} + +static void on_encoder_write(void *userdata, struct evbuffer *buffer) +{ + struct tmate_ssh_client *client = userdata; + ssize_t len, written; + unsigned char *buf; + + for(;;) { + len = evbuffer_get_length(buffer); + if (!len) + break; + + buf = evbuffer_pullup(buffer, -1); + + written = ssh_channel_write(client->channel, buf, len); + if (written < 0) { + reconnect_ssh_client(client, "Error writing to channel: %s", + ssh_get_error(client->session)); + break; + } + + evbuffer_drain(buffer, written); + } +} + +static void on_ssh_auth_server_complete(struct tmate_ssh_client *connected_client) +{ + /* + * The first ssh connection succeeded. Hopefully this one offers the + * best latency. We can now kill the other ssh clients that are trying + * to connect. + */ struct tmate_session *session = connected_client->tmate_session; struct tmate_ssh_client *client, *tmp_client; @@ -75,7 +82,7 @@ static void connection_complete(struct tmate_ssh_client *connected_client) continue; assert(!client->has_encoder); - kill_session(client, NULL); + kill_ssh_client(client, NULL); } } @@ -115,7 +122,7 @@ static void on_passphrase_read(const char *passphrase, void *private) struct tmate_ssh_client *client = private; client->tmate_session->passphrase = xstrdup(passphrase); - on_session_event(client); + on_ssh_client_event(client); } static void request_passphrase(struct tmate_ssh_client *client) @@ -155,7 +162,21 @@ static void request_passphrase(struct tmate_ssh_client *client) data->password_cb_private = client; } -static void on_session_event(struct tmate_ssh_client *client) +static void register_session_fd_event(struct tmate_ssh_client *client) +{ + if (!event_initialized(&client->ev_ssh)) { + int flag = 1; + setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, + TCP_NODELAY, &flag, sizeof(flag)); + + event_assign(&client->ev_ssh, client->tmate_session->ev_base, + ssh_get_fd(client->session), + EV_READ | EV_PERSIST, __on_ssh_client_event, client); + event_add(&client->ev_ssh, NULL); + } +} + +static void on_ssh_client_event(struct tmate_ssh_client *client) { char *identity; ssh_key pubkey; @@ -214,7 +235,7 @@ static void on_session_event(struct tmate_ssh_client *client) register_session_fd_event(client); return; case SSH_ERROR: - reconnect_session(client, "Error connecting: %s", + reconnect_ssh_client(client, "Error connecting: %s", ssh_get_error(session)); return; case SSH_OK: @@ -229,7 +250,7 @@ static void on_session_event(struct tmate_ssh_client *client) tmate_fatal("ssh_get_publickey"); if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hash_len) < 0) { - kill_session(client, "Cannot authenticate server"); + kill_ssh_client(client, "Cannot authenticate server"); return; } @@ -259,7 +280,7 @@ static void on_session_event(struct tmate_ssh_client *client) free(hash_str); if (!match) { - kill_session(client, "Cannot authenticate server"); + kill_ssh_client(client, "Cannot authenticate server"); return; } @@ -270,7 +291,7 @@ static void on_session_event(struct tmate_ssh_client *client) * otherwise the speed test would be biased. */ tmate_debug("Connected to %s", client->server_ip); - connection_complete(client); + on_ssh_auth_server_complete(client); client->state = SSH_AUTH_CLIENT; /* fall through */ @@ -286,13 +307,15 @@ static void on_session_event(struct tmate_ssh_client *client) if (client->tmate_session->need_passphrase) request_passphrase(client); else - kill_session(client, "SSH keys not found. Run 'ssh-keygen' to create keys and try again."); + kill_ssh_client(client, "SSH keys not found." + " Run 'ssh-keygen' to create keys and try again."); if (client->tried_passphrase) - tmate_status_message("Can't load SSH key. Try typing passphrase again in case of typo. ctrl-c to abort."); + tmate_status_message("Can't load SSH key." + " Try typing passphrase again in case of typo. ctrl-c to abort."); return; case SSH_AUTH_ERROR: - reconnect_session(client, "Auth error: %s", + reconnect_ssh_client(client, "Auth error: %s", ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: @@ -306,7 +329,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - reconnect_session(client, "Error opening channel: %s", + reconnect_ssh_client(client, "Error opening channel: %s", ssh_get_error(session)); return; case SSH_OK: @@ -320,7 +343,7 @@ static void on_session_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - reconnect_session(client, "Error initializing tmate: %s", + reconnect_ssh_client(client, "Error initializing tmate: %s", ssh_get_error(session)); return; case SSH_OK: @@ -330,63 +353,31 @@ static void on_session_event(struct tmate_ssh_client *client) ssh_set_blocking(session, 1); client->state = SSH_READY; - register_input_stream_event(client); - flush_input_stream(client); + + tmate_encoder_set_ready_callback(&client->tmate_session->encoder, + on_encoder_write, client); + tmate_decoder_init(&client->tmate_session->decoder, + on_decoder_read, client); /* fall through */ } case SSH_READY: - consume_channel(client); + read_channel(client); if (!ssh_is_connected(session)) { - reconnect_session(client, "Disconnected"); + reconnect_ssh_client(client, "Disconnected"); return; } } } -static void flush_input_stream(struct tmate_ssh_client *client) +static void __on_ssh_client_event(__unused evutil_socket_t fd, __unused short what, void *arg) { - struct tmate_encoder *encoder = &client->tmate_session->encoder; - struct evbuffer *evb = encoder->buffer; - ssize_t len, written; - char *buf; - - if (client->state < SSH_READY) - return; - - for (;;) { - len = evbuffer_get_length(evb); - if (!len) - break; - - buf = evbuffer_pullup(evb, -1); - - written = ssh_channel_write(client->channel, buf, len); - if (written < 0) { - reconnect_session(client, "Error writing to channel: %s", - ssh_get_error(client->session)); - return; - } - - evbuffer_drain(evb, written); - } + on_ssh_client_event(arg); } -static void __flush_input_stream(__unused evutil_socket_t fd, __unused short what, void *arg) +static void __kill_ssh_client(struct tmate_ssh_client *client, + const char *fmt, va_list va) { - flush_input_stream(arg); -} - -static void __on_session_event(__unused evutil_socket_t fd, __unused short what, void *arg) -{ - on_session_event(arg); -} - -static void __kill_session(struct tmate_ssh_client *client, - const char *fmt, va_list va) -{ - struct tmate_encoder *encoder; - if (fmt && TAILQ_EMPTY(&client->tmate_session->clients)) __tmate_status_message(fmt, va); else @@ -397,13 +388,6 @@ static void __kill_session(struct tmate_ssh_client *client, client->ev_ssh.ev_flags = 0; } - if (client->has_encoder) { - encoder = &client->tmate_session->encoder; - event_del(&encoder->ev_readable); - encoder->ev_readable.ev_flags = 0; - client->has_encoder = 0; - } - if (client->session) { /* ssh_free() also frees the associated channels. */ ssh_free(client->session); @@ -414,36 +398,36 @@ static void __kill_session(struct tmate_ssh_client *client, client->state = SSH_NONE; } -static void printflike(2, 3) kill_session(struct tmate_ssh_client *client, - const char *fmt, ...) +static void kill_ssh_client(struct tmate_ssh_client *client, + const char *fmt, ...) { va_list ap; TAILQ_REMOVE(&client->tmate_session->clients, client, node); va_start(ap, fmt); - __kill_session(client, fmt, ap); + __kill_ssh_client(client, fmt, ap); va_end(ap); free(client->server_ip); free(client); } -static void connect_session(struct tmate_ssh_client *client) +static void connect_ssh_client(struct tmate_ssh_client *client) { if (!client->session) { client->state = SSH_INIT; - on_session_event(client); + on_ssh_client_event(client); } } static void on_reconnect_timer(__unused evutil_socket_t fd, __unused short what, void *arg) { - connect_session(arg); + connect_ssh_client(arg); } -static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client, - const char *fmt, ...) +static void reconnect_ssh_client(struct tmate_ssh_client *client, + const char *fmt, ...) { /* struct timeval tv; */ va_list ap; @@ -453,7 +437,7 @@ static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client, #endif va_start(ap, fmt); - __kill_session(client, fmt, ap); + __kill_ssh_client(client, fmt, ap); va_end(ap); /* Not yet implemented... */ @@ -464,12 +448,20 @@ static void printflike(2, 3) reconnect_session(struct tmate_ssh_client *client, #endif } +static void ssh_log_function(int priority, const char *function, + const char *buffer, __unused void *userdata) +{ + tmate_debug("[%d] [%s] %s", priority, function, buffer); +} + struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, const char *server_ip) { struct tmate_ssh_client *client; client = xmalloc(sizeof(*client)); + ssh_set_log_callback(ssh_log_function); + memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks)); ssh_callbacks_init(&client->ssh_callbacks); client->ssh_callbacks.userdata = client; @@ -489,7 +481,7 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, evtimer_assign(&client->ev_ssh_reconnect, session->ev_base, on_reconnect_timer, client); - connect_session(client); + connect_ssh_client(client); return client; } diff --git a/tmate.h b/tmate.h index a44afa58..0474e081 100644 --- a/tmate.h +++ b/tmate.h @@ -14,30 +14,66 @@ #define tmate_info(...) log_debug("[tmate] I " __VA_ARGS__) #define tmate_fatal(...) fatalx("[tmate] " __VA_ARGS__) -/* tmate-encoder.c */ +/* tmate-msgpack.c */ -#define TMATE_MAX_MESSAGE_SIZE (16*1024) - -#define TMATE_PROTOCOL_VERSION 5 - -enum tmate_commands { - TMATE_HEADER, - TMATE_SYNC_LAYOUT, - TMATE_PTY_DATA, - TMATE_EXEC_CMD, - TMATE_FAILED_CMD, - TMATE_STATUS, - TMATE_SYNC_COPY_MODE, - TMATE_WRITE_COPY_MODE, -}; +typedef void tmate_encoder_write_cb(void *userdata, struct evbuffer *buffer); struct tmate_encoder { msgpack_packer pk; + tmate_encoder_write_cb *ready_callback; + void *userdata; struct evbuffer *buffer; - struct event ev_readable; + struct event ev_buffer; + bool ev_active; }; -extern void tmate_encoder_init(struct tmate_encoder *encoder); +extern void tmate_encoder_init(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata); +extern void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, + tmate_encoder_write_cb *callback, + void *userdata); + +extern void msgpack_pack_string(msgpack_packer *pk, const char *str); +extern void msgpack_pack_boolean(msgpack_packer *pk, bool value); + +#define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, __VA_ARGS__) + +struct tmate_unpacker; +struct tmate_decoder; +typedef void tmate_decoder_reader(void *userdata, struct tmate_unpacker *uk); + +struct tmate_decoder { + struct msgpack_unpacker unpacker; + tmate_decoder_reader *reader; + void *userdata; +}; + +extern void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, void *userdata); +extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len); +extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); + +struct tmate_unpacker { + int argc; + msgpack_object *argv; +}; + +extern void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj); +extern void tmate_decoder_error(void); +extern int64_t unpack_int(struct tmate_unpacker *uk); +extern bool unpack_bool(struct tmate_unpacker *uk); +extern void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len); +extern char *unpack_string(struct tmate_unpacker *uk); +extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested); + +#define unpack_each(nested_uk, tmp_uk, uk) \ + for (unpack_array(uk, tmp_uk); \ + (tmp_uk)->argc > 0 && (init_unpacker(nested_uk, (tmp_uk)->argv[0]), 1); \ + (tmp_uk)->argv++, (tmp_uk)->argc--) + +/* tmate-encoder.c */ + +#define TMATE_PROTOCOL_VERSION 5 extern void tmate_write_header(void); extern void tmate_sync_layout(void); @@ -51,28 +87,9 @@ extern void tmate_write_copy_mode(struct window_pane *wp, const char *str); /* tmate-decoder.c */ -enum tmate_client_commands { - TMATE_NOTIFY, - TMATE_CLIENT_PANE_KEY, - TMATE_CLIENT_RESIZE, - TMATE_CLIENT_EXEC_CMD, - TMATE_CLIENT_ENV, - TMATE_CLIENT_READY, - TMATE_CLIENT_PANE_TMUX_KEY, -}; - -struct tmate_decoder { - struct msgpack_unpacker unpacker; - int ready; -}; - -extern int tmate_sx; -extern int tmate_sy; - -extern void tmate_decoder_init(struct tmate_decoder *decoder); -extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder, - char **buf, size_t *len); -extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); +struct tmate_session; +extern void tmate_dispatch_slave_message(struct tmate_session *session, + struct tmate_unpacker *uk); /* tmate-ssh-client.c */ @@ -132,6 +149,12 @@ struct tmate_session { struct tmate_encoder encoder; struct tmate_decoder decoder; + /* True when the slave has sent all the environment variables */ + int tmate_env_ready; + + int min_sx; + int min_sy; + /* * This list contains one connection per IP. The first connected * client wins, and saved in *client. When we have a winner, the @@ -147,7 +170,7 @@ extern void tmate_session_init(struct event_base *base); extern void tmate_session_start(void); /* tmate-debug.c */ -extern void tmate_print_trace(void); +extern void tmate_print_stack_trace(void); extern void tmate_catch_sigsegv(void); /* tmate-msg.c */ From 81022ee8c7876c41ff6a86a53c4f2c8cc0d9373e Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 2 Jan 2016 09:45:34 -0500 Subject: [PATCH 550/703] Only use IPv4/IPv6 when we can use them --- tmate-session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-session.c b/tmate-session.c index c96b4fa7..b7001509 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -88,7 +88,7 @@ static void lookup_and_connect(void) memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; - hints.ai_flags = 0; + hints.ai_flags = EVUTIL_AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; From 924eb0129931ab55c3c1070bff8a5e4f91c38346 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 2 Jan 2016 10:04:29 -0500 Subject: [PATCH 551/703] nits --- tmate-ssh-client.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 2960fe8f..eb8a728b 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -26,7 +26,7 @@ static void read_channel(struct tmate_ssh_client *client) len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); if (len < 0) { reconnect_ssh_client(client, "Error reading from channel: %s", - ssh_get_error(client->session)); + ssh_get_error(client->session)); break; } @@ -59,7 +59,7 @@ static void on_encoder_write(void *userdata, struct evbuffer *buffer) written = ssh_channel_write(client->channel, buf, len); if (written < 0) { reconnect_ssh_client(client, "Error writing to channel: %s", - ssh_get_error(client->session)); + ssh_get_error(client->session)); break; } @@ -236,7 +236,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) return; case SSH_ERROR: reconnect_ssh_client(client, "Error connecting: %s", - ssh_get_error(session)); + ssh_get_error(session)); return; case SSH_OK: register_session_fd_event(client); @@ -316,7 +316,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) return; case SSH_AUTH_ERROR: reconnect_ssh_client(client, "Auth error: %s", - ssh_get_error(session)); + ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: tmate_debug("Auth successful"); @@ -330,7 +330,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) return; case SSH_ERROR: reconnect_ssh_client(client, "Error opening channel: %s", - ssh_get_error(session)); + ssh_get_error(session)); return; case SSH_OK: tmate_debug("Session opened, initalizing tmate"); @@ -344,7 +344,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) return; case SSH_ERROR: reconnect_ssh_client(client, "Error initializing tmate: %s", - ssh_get_error(session)); + ssh_get_error(session)); return; case SSH_OK: tmate_debug("Ready"); @@ -427,7 +427,7 @@ static void on_reconnect_timer(__unused evutil_socket_t fd, __unused short what, } static void reconnect_ssh_client(struct tmate_ssh_client *client, - const char *fmt, ...) + const char *fmt, ...) { /* struct timeval tv; */ va_list ap; From 0a3bd83b19c218379232a910dcff6a51ceb732d2 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 2 Jan 2016 11:03:04 -0500 Subject: [PATCH 552/703] Use a session termination message --- session.c | 4 ++++ tmate-encoder.c | 6 ++++++ tmate-protocol.h | 2 ++ tmate.h | 1 + 4 files changed, 13 insertions(+) diff --git a/session.c b/session.c index ab821a81..0fff9ef2 100644 --- a/session.c +++ b/session.c @@ -215,6 +215,10 @@ session_destroy(struct session *s) log_debug("session %s destroyed", s->name); +#ifdef TMATE + tmate_write_fin(); +#endif + RB_REMOVE(sessions, &sessions, s); notify_session_closed(s); diff --git a/tmate-encoder.c b/tmate-encoder.c index 8d47f2ef..ff6924da 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -224,3 +224,9 @@ void tmate_write_copy_mode(struct window_pane *wp, const char *str) pack(int, wp->id); pack(string, str); } + +void tmate_write_fin(void) +{ + pack(array, 1); + pack(int, TMATE_OUT_FIN); +} diff --git a/tmate-protocol.h b/tmate-protocol.h index 8611d9a7..45beaec6 100644 --- a/tmate-protocol.h +++ b/tmate-protocol.h @@ -48,6 +48,7 @@ enum tmate_daemon_out_msg_types { TMATE_OUT_STATUS, TMATE_OUT_SYNC_COPY_MODE, TMATE_OUT_WRITE_COPY_MODE, + TMATE_OUT_FIN, }; /* @@ -64,6 +65,7 @@ enum tmate_daemon_out_msg_types { [int: type, string: input_prompt, string: input_str]]) // Any of the array can be [] [TMATE_OUT_WRITE_COPY_MODE, int: pane_id, string: str] +[TMATE_OUT_FIN] */ enum tmate_daemon_in_msg_types { diff --git a/tmate.h b/tmate.h index 0474e081..2018bbc3 100644 --- a/tmate.h +++ b/tmate.h @@ -84,6 +84,7 @@ extern void tmate_failed_cmd(int client_id, const char *cause); extern void tmate_status(const char *left, const char *right); extern void tmate_sync_copy_mode(struct window_pane *wp); extern void tmate_write_copy_mode(struct window_pane *wp, const char *str); +extern void tmate_write_fin(void); /* tmate-decoder.c */ From cfb78654c2b9a9f0c2f90c22228563d5385c510c Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 2 Jan 2016 17:16:25 +0000 Subject: [PATCH 553/703] clock-mode needs CMD_PANE. --- cmd-copy-mode.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 1a006ebb..49f5b30c 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -46,6 +46,8 @@ const struct cmd_entry cmd_clock_mode_entry = { .args = { "t:", 0, 0 }, .usage = CMD_TARGET_PANE_USAGE, + .tflag = CMD_PANE, + .flags = 0, .exec = cmd_copy_mode_exec }; From a397be388cc3552304fcb1f7195e660eeca6b8ae Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 5 Jan 2016 10:53:28 -0500 Subject: [PATCH 554/703] Simplify find_window_pane() code --- tmate-decoder.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/tmate-decoder.c b/tmate-decoder.c index c387b362..d4646ac7 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -36,31 +36,15 @@ static void handle_legacy_pane_key(__unused struct tmate_session *_session, static struct window_pane *find_window_pane(struct session *s, int pane_id) { struct window *w; - struct window_pane *wp; - struct winlink *wl; + + if (pane_id != -1) + return window_pane_find_by_id(pane_id); w = s->curw->window; if (!w) - goto slow_path; - - wp = w->active; - if (!wp) - goto slow_path; - if (pane_id == -1 || (int)wp->id == pane_id) - return wp; - -slow_path: - if (pane_id == -1) return NULL; - RB_FOREACH(wl, winlinks, &s->windows) { - TAILQ_FOREACH(wp, &wl->window->panes, entry) { - if ((int)wp->id == pane_id) - return wp; - } - } - - return NULL; + return w->active; } static void handle_pane_key(__unused struct tmate_session *_session, From 3a73f1735437ef08d32e3d14966f813a5ed7dfb9 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 9 Jan 2016 21:35:13 -0500 Subject: [PATCH 555/703] Allow static linking --- .gitignore | 4 ++++ Makefile.static-build | 49 +++++++++++++++++++++++++++++++++++++++++++ configure.ac | 14 +++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 Makefile.static-build diff --git a/.gitignore b/.gitignore index 0cd63730..93153ede 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,7 @@ cscope.* ctags *.log tmate.1.* +downloads/ +ext/ +libssh-*/ +msgpack-*/ diff --git a/Makefile.static-build b/Makefile.static-build new file mode 100644 index 00000000..cb16c961 --- /dev/null +++ b/Makefile.static-build @@ -0,0 +1,49 @@ +LIBSSH=libssh-0.7.2 +LIBSSH_URL=https://red.libssh.org/attachments/download/177/$(LIBSSH).tar.xz +LIBSSH_LIB=ext/lib/libssh.a + +MSGPACK=msgpack-1.3.0 +MSGPACK_URL=https://github.com/msgpack/msgpack-c/releases/download/cpp-1.3.0/$(MSGPACK).tar.gz +MSGPACK_LIB=ext/lib/libmsgpack.a + +TMATE_CONFIGURE=PKG_CONFIG_PATH=./ext/lib/pkgconfig + +all: tmate + +dependencies: + apt-get install build-essentials cmake libssl-dev autoconf automake pkg-config libtool libevent-dev libncurses-dev zlib1g-dev + +downloads/$(notdir $(LIBSSH_URL)): + mkdir -p downloads + wget -O $@ $(LIBSSH_URL) + +$(LIBSSH)/.ready: downloads/$(notdir $(LIBSSH_URL)) + tar xf $< + touch $@ + +downloads/$(notdir $(MSGPACK_URL)): + mkdir -p downloads + wget -O $@ $(MSGPACK_URL) + +$(MSGPACK)/.ready: downloads/$(notdir $(MSGPACK_URL)) + tar xf $< + touch $@ + +$(LIBSSH_LIB): $(LIBSSH)/.ready + mkdir -p $(LIBSSH)/build + cd $(LIBSSH)/build; ([ -f Makefile ] || cmake -DCMAKE_INSTALL_PREFIX:PATH=$(shell pwd)/ext .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF) + +make -C $(LIBSSH)/build install + +$(MSGPACK_LIB): $(MSGPACK)/.ready + mkdir -p $(MSGPACK)/build + cd $(MSGPACK)/build; ([ -f Makefile ] || cmake -DCMAKE_INSTALL_PREFIX:PATH=$(shell pwd)/ext ..) + +make -C $(MSGPACK)/build install + +tmate: $(MSGPACK_LIB) $(LIBSSH_LIB) + ./autogen.sh + $(TMATE_CONFIGURE) ./configure --enable-static + +make + +clean: + rm -rf ext $(LIBSSH) $(MSGPACK) + +make clean diff --git a/configure.ac b/configure.ac index e8074373..6c1c5b77 100644 --- a/configure.ac +++ b/configure.ac @@ -49,6 +49,20 @@ AC_ARG_ENABLE( if test "x$found_static" = xyes; then LDFLAGS="$LDFLAGS -static" PKG_CONFIG="pkg-config --static" + + CFLAGS="$CFLAGS -flto" + LDFLAGS="$LDFLAGS -flto" + + PKG_CHECK_MODULES([ZLIB], [zlib], [ + CPPFLAGS="$ZLIB_CFLAGS $CPPFLAGS" + LIBS="$ZLIB_LIBS $LIBS" + ]) + + PKG_CHECK_MODULES([LIBCRYPTO], [libcrypto], [ + CPPFLAGS="$LIBCRYPTO_CFLAGS $CPPFLAGS" + LIBS="$LIBCRYPTO_LIBS $LIBS" + ]) + AC_CHECK_LIB(dl, dlopen) fi # Is this gcc? From a2520da19591599538f8dc26b3bd6bf7a2619300 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Jan 2016 12:39:09 -0800 Subject: [PATCH 556/703] Fix static compilation (libc should be dynamically linked) --- Makefile.static-build | 2 +- configure.ac | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile.static-build b/Makefile.static-build index cb16c961..809ac9db 100644 --- a/Makefile.static-build +++ b/Makefile.static-build @@ -11,7 +11,7 @@ TMATE_CONFIGURE=PKG_CONFIG_PATH=./ext/lib/pkgconfig all: tmate dependencies: - apt-get install build-essentials cmake libssl-dev autoconf automake pkg-config libtool libevent-dev libncurses-dev zlib1g-dev + apt-get install build-essential cmake libssl-dev autoconf automake pkg-config libtool libevent-dev libncurses-dev zlib1g-dev downloads/$(notdir $(LIBSSH_URL)): mkdir -p downloads diff --git a/configure.ac b/configure.ac index 6c1c5b77..663a51c5 100644 --- a/configure.ac +++ b/configure.ac @@ -47,13 +47,12 @@ AC_ARG_ENABLE( found_static=$enable_static ) if test "x$found_static" = xyes; then - LDFLAGS="$LDFLAGS -static" PKG_CONFIG="pkg-config --static" CFLAGS="$CFLAGS -flto" LDFLAGS="$LDFLAGS -flto" - PKG_CHECK_MODULES([ZLIB], [zlib], [ + PKG_CHECK_MODULES([ZLIB], [zlib], [ CPPFLAGS="$ZLIB_CFLAGS $CPPFLAGS" LIBS="$ZLIB_LIBS $LIBS" ]) @@ -62,7 +61,6 @@ if test "x$found_static" = xyes; then CPPFLAGS="$LIBCRYPTO_CFLAGS $CPPFLAGS" LIBS="$LIBCRYPTO_LIBS $LIBS" ]) - AC_CHECK_LIB(dl, dlopen) fi # Is this gcc? @@ -451,6 +449,11 @@ if test "x$found_getopt" != xno; then fi AM_CONDITIONAL(NO_GETOPT, [test "x$found_getopt" = xno]) +if test "x$found_static" = xyes; then + # libc and libdl should be dynamically linked. + LIBS="-Wl,-Bstatic ${LIBS/-ldl/} -Wl,-Bdynamic -ldl" +fi + # Check for BSD-style integer types. AC_MSG_CHECKING(for BSD-style unsigned types) AC_COMPILE_IFELSE([AC_LANG_SOURCE( From 6dc58ab6deef30020953882331c88ca6c80b4f2d Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 11 Jan 2016 02:20:10 -0800 Subject: [PATCH 557/703] Lower the required glib version to 2.8 for static builds --- Makefile.static-build | 15 +++++++++++++-- compat/clock_gettime.c | 9 +++++++++ compat/memcpy.c | 11 +++++++++++ configure.ac | 7 +++++++ 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 compat/clock_gettime.c create mode 100644 compat/memcpy.c diff --git a/Makefile.static-build b/Makefile.static-build index 809ac9db..6db26d76 100644 --- a/Makefile.static-build +++ b/Makefile.static-build @@ -8,6 +8,10 @@ MSGPACK_LIB=ext/lib/libmsgpack.a TMATE_CONFIGURE=PKG_CONFIG_PATH=./ext/lib/pkgconfig +LIBC=$(shell gcc -print-file-name=libc.a) +STATIC_LIBC_OBJECTS=fdelt_chk +STATIC_COMPAT_OBJECTS=memcpy clock_gettime + all: tmate dependencies: @@ -39,11 +43,18 @@ $(MSGPACK_LIB): $(MSGPACK)/.ready cd $(MSGPACK)/build; ([ -f Makefile ] || cmake -DCMAKE_INSTALL_PREFIX:PATH=$(shell pwd)/ext ..) +make -C $(MSGPACK)/build install -tmate: $(MSGPACK_LIB) $(LIBSSH_LIB) +libc/%.o: + mkdir -p libc + cd libc; ar x $(LIBC) $(notdir $@) + +compat/%.o: compat/%.c + gcc -c -o $@ $< + +tmate: $(MSGPACK_LIB) $(LIBSSH_LIB) $(patsubst %,libc/%.o,$(STATIC_LIBC_OBJECTS)) $(patsubst %,compat/%.o,$(STATIC_COMPAT_OBJECTS)) ./autogen.sh $(TMATE_CONFIGURE) ./configure --enable-static +make clean: - rm -rf ext $(LIBSSH) $(MSGPACK) + rm -rf ext libc $(LIBSSH) $(MSGPACK) +make clean diff --git a/compat/clock_gettime.c b/compat/clock_gettime.c new file mode 100644 index 00000000..0b631b79 --- /dev/null +++ b/compat/clock_gettime.c @@ -0,0 +1,9 @@ +#define _GNU_SOURCE +#include +#include +#include + +int clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + return syscall(SYS_clock_gettime, clk_id, tp); +} diff --git a/compat/memcpy.c b/compat/memcpy.c new file mode 100644 index 00000000..37092a33 --- /dev/null +++ b/compat/memcpy.c @@ -0,0 +1,11 @@ +// http://stackoverflow.com/questions/8823267/linking-against-older-symbol-version-in-a-so-file + +#include + +/* some systems do not have newest memcpy@@GLIBC_2.14 - stay with old good one */ +asm (".symver memcpy, memcpy@GLIBC_2.2.5"); + +void *__wrap_memcpy(void *dest, const void *src, size_t n) +{ + return memcpy(dest, src, n); +} diff --git a/configure.ac b/configure.ac index 663a51c5..10d761bc 100644 --- a/configure.ac +++ b/configure.ac @@ -61,6 +61,7 @@ if test "x$found_static" = xyes; then CPPFLAGS="$LIBCRYPTO_CFLAGS $CPPFLAGS" LIBS="$LIBCRYPTO_LIBS $LIBS" ]) + # See more static settings below... (search for found_static) fi # Is this gcc? @@ -451,6 +452,12 @@ AM_CONDITIONAL(NO_GETOPT, [test "x$found_getopt" = xno]) if test "x$found_static" = xyes; then # libc and libdl should be dynamically linked. + # but we want to lower our requirements for libc's version. + # so we extract some symobls and add them here + if test `uname -m` = x86_64; then + LIBS="compat/memcpy.o -Wl,--wrap=memcpy $LIBS" + fi + LIBS="compat/clock_gettime.o libc/fdelt_chk.o $LIBS" LIBS="-Wl,-Bstatic ${LIBS/-ldl/} -Wl,-Bdynamic -ldl" fi From 68d797587ef2acf1464a9390912e4a22e4db6482 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 15 Jan 2016 11:31:47 +0000 Subject: [PATCH 558/703] A couple of missing printflike attributes, from Andrey Starodubtsev. --- input.c | 2 +- tmux.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/input.c b/input.c index ed1ebffc..19fd48b7 100644 --- a/input.c +++ b/input.c @@ -100,7 +100,7 @@ struct input_ctx { struct input_transition; int input_split(struct input_ctx *); int input_get(struct input_ctx *, u_int, int, int); -void input_reply(struct input_ctx *, const char *, ...); +void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...); void input_set_state(struct window_pane *, const struct input_transition *); void input_reset_cell(struct input_ctx *); diff --git a/tmux.h b/tmux.h index 194d01d8..dbea85bf 100644 --- a/tmux.h +++ b/tmux.h @@ -1540,7 +1540,7 @@ extern struct client *cfg_client; void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); void set_cfg_file(const char *); -void cfg_add_cause(const char *, ...); +void printflike(1, 2) cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); From d551ab8e5cfb00fbb7a79e7f0c3f4f2780fc6824 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 15 Jan 2016 11:33:41 +0000 Subject: [PATCH 559/703] Clear the environment properly by looping until it is empty rather than looping over it (which may skip entries), from Brad King. --- environ.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/environ.c b/environ.c index de560896..101dfafd 100644 --- a/environ.c +++ b/environ.c @@ -196,10 +196,10 @@ void environ_push(struct environ *env) { struct environ_entry *envent; - char **vp, *v; + char *v; - for (vp = environ; *vp != NULL; vp++) { - v = xstrdup(*vp); + while (*environ != NULL) { + v = xstrdup(*environ); v[strcspn(v, "=")] = '\0'; unsetenv(v); From c1846177fc64bf52adecfb0e3350b43421564529 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 15 Jan 2016 16:02:42 -0500 Subject: [PATCH 560/703] Cleanup hacks around ev_ssh.ev_flags Fixes #69 Fix suggested by Leon M. George --- tmate-ssh-client.c | 36 +++++++++++++++++++++--------------- tmate.h | 1 + 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index eb8a728b..691a49ce 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -162,18 +162,23 @@ static void request_passphrase(struct tmate_ssh_client *client) data->password_cb_private = client; } -static void register_session_fd_event(struct tmate_ssh_client *client) +static void init_conn_fd(struct tmate_ssh_client *client) { - if (!event_initialized(&client->ev_ssh)) { - int flag = 1; - setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, - TCP_NODELAY, &flag, sizeof(flag)); + if (client->has_init_conn_fd) + return; - event_assign(&client->ev_ssh, client->tmate_session->ev_base, - ssh_get_fd(client->session), - EV_READ | EV_PERSIST, __on_ssh_client_event, client); - event_add(&client->ev_ssh, NULL); - } + if (ssh_get_fd(client->session) < 0) + return; + + int flag = 1; + setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, + TCP_NODELAY, &flag, sizeof(flag)); + + event_set(&client->ev_ssh, ssh_get_fd(client->session), + EV_READ | EV_PERSIST, __on_ssh_client_event, client); + event_add(&client->ev_ssh, NULL); + + client->has_init_conn_fd = true; } static void on_ssh_client_event(struct tmate_ssh_client *client) @@ -232,14 +237,15 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_CONNECT: switch (ssh_connect(session)) { case SSH_AGAIN: - register_session_fd_event(client); + init_conn_fd(client); return; case SSH_ERROR: reconnect_ssh_client(client, "Error connecting: %s", ssh_get_error(session)); return; case SSH_OK: - register_session_fd_event(client); + init_conn_fd(client); + tmate_debug("Establishing connection to %s", client->server_ip); client->state = SSH_AUTH_SERVER; /* fall through */ @@ -383,9 +389,9 @@ static void __kill_ssh_client(struct tmate_ssh_client *client, else tmate_debug("Disconnecting %s", client->server_ip); - if (event_initialized(&client->ev_ssh)) { + if (client->has_init_conn_fd) { event_del(&client->ev_ssh); - client->ev_ssh.ev_flags = 0; + client->has_init_conn_fd = false; } if (client->session) { @@ -476,7 +482,7 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, client->channel = NULL; client->has_encoder = 0; - client->ev_ssh.ev_flags = 0; + client->has_init_conn_fd = false; evtimer_assign(&client->ev_ssh_reconnect, session->ev_base, on_reconnect_timer, client); diff --git a/tmate.h b/tmate.h index 2018bbc3..98b109c4 100644 --- a/tmate.h +++ b/tmate.h @@ -132,6 +132,7 @@ struct tmate_ssh_client { ssh_session session; ssh_channel channel; + bool has_init_conn_fd; struct event ev_ssh; struct event ev_ssh_reconnect; }; From c9815307ebe8b729504d383904ae3ef3b862cf11 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 16 Jan 2016 00:36:53 +0000 Subject: [PATCH 561/703] Add hooks for alerts (bell, silence, activity), from Thomas Adam. --- alerts.c | 23 ++++++++++++++++++++--- cmd-find.c | 16 ++++++++++++++++ tmux.1 | 10 ++++++++++ tmux.h | 2 ++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/alerts.c b/alerts.c index 536ea750..d1fb0fce 100644 --- a/alerts.c +++ b/alerts.c @@ -29,6 +29,7 @@ int alerts_enabled(struct window *, int); void alerts_callback(int, short, void *); void alerts_reset(struct window *); +void alerts_run_hook(struct session *, struct winlink *, int); int alerts_check_all(struct session *, struct winlink *); int alerts_check_bell(struct session *, struct winlink *); int alerts_check_activity(struct session *, struct winlink *); @@ -55,8 +56,6 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg) RB_FOREACH(w, windows, &windows) { RB_FOREACH(s, sessions, &sessions) { - if (s->flags & SESSION_UNATTACHED) - continue; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window != w) continue; @@ -73,6 +72,22 @@ alerts_callback(__unused int fd, __unused short events, __unused void *arg) alerts_fired = 0; } +void +alerts_run_hook(struct session *s, struct winlink *wl, int flags) +{ + struct cmd_find_state fs; + + if (cmd_find_from_winlink(&fs, s, wl) != 0) + return; + + if (flags & WINDOW_BELL) + hooks_run(s->hooks, NULL, &fs, "alert-bell"); + if (flags & WINDOW_SILENCE) + hooks_run(s->hooks, NULL, &fs, "alert-silence"); + if (flags & WINDOW_ACTIVITY) + hooks_run(s->hooks, NULL, &fs, "alert-activity"); +} + int alerts_check_all(struct session *s, struct winlink *wl) { @@ -81,8 +96,10 @@ alerts_check_all(struct session *s, struct winlink *wl) alerts = alerts_check_bell(s, wl); alerts |= alerts_check_activity(s, wl); alerts |= alerts_check_silence(s, wl); - if (alerts != 0) + if (alerts != 0) { + alerts_run_hook(s, wl, alerts); server_status_session(s); + } return (alerts); } diff --git a/cmd-find.c b/cmd-find.c index 9b1ca517..68aaf121 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -879,6 +879,22 @@ cmd_find_from_session(struct cmd_find_state *fs, struct session *s) return (0); } +/* Find state from a winlink. */ +int +cmd_find_from_winlink(struct cmd_find_state *fs, struct session *s, + struct winlink *wl) +{ + cmd_find_clear_state(fs, NULL, 0); + + fs->s = s; + fs->wl = wl; + fs->w = wl->window; + fs->wp = wl->window->active; + + cmd_find_log_state(__func__, fs); + return (0); +} + /* Find state from a window. */ int cmd_find_from_window(struct cmd_find_state *fs, struct window *w) diff --git a/tmux.1 b/tmux.1 index 9017b4e7..159a4bd9 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3224,6 +3224,16 @@ Each hook has a .Em name . The following hooks are available: .Bl -tag -width "XXXXXXXXXXXXXXXX" +.It alert-activity +Run when a window has activity. +See +.Ic monitor-activity . +.It alert-bell +Run when a window has received a bell. +.It alert-silence +Run when a window has been silent. +See +.Ic monitor-silence . .It client-attached Run when a client is attached. .It client-detached diff --git a/tmux.h b/tmux.h index dbea85bf..55fe407f 100644 --- a/tmux.h +++ b/tmux.h @@ -1780,6 +1780,8 @@ void cmd_find_copy_state(struct cmd_find_state *, void cmd_find_log_state(const char *, struct cmd_find_state *); int cmd_find_from_session(struct cmd_find_state *, struct session *); +int cmd_find_from_winlink(struct cmd_find_state *, + struct session *, struct winlink *); int cmd_find_from_window(struct cmd_find_state *, struct window *); int cmd_find_from_pane(struct cmd_find_state *, struct window_pane *); From 995af0e2b72aec54ba3685b1a8ce5e78d279d8ff Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 Jan 2016 15:59:12 +0000 Subject: [PATCH 562/703] I no longer use my SourceForge address so replace it. --- alerts.c | 2 +- arguments.c | 2 +- array.h | 2 +- cfg.c | 2 +- client.c | 2 +- cmd-attach-session.c | 2 +- cmd-bind-key.c | 2 +- cmd-break-pane.c | 2 +- cmd-choose-buffer.c | 2 +- cmd-choose-client.c | 2 +- cmd-clear-history.c | 2 +- cmd-command-prompt.c | 2 +- cmd-copy-mode.c | 2 +- cmd-detach-client.c | 2 +- cmd-display-panes.c | 2 +- cmd-find-window.c | 2 +- cmd-find.c | 2 +- cmd-join-pane.c | 2 +- cmd-kill-pane.c | 2 +- cmd-kill-server.c | 2 +- cmd-kill-session.c | 2 +- cmd-kill-window.c | 2 +- cmd-list-buffers.c | 2 +- cmd-list-clients.c | 2 +- cmd-list-keys.c | 2 +- cmd-list-panes.c | 2 +- cmd-list-sessions.c | 2 +- cmd-list-windows.c | 2 +- cmd-list.c | 2 +- cmd-lock-server.c | 2 +- cmd-move-window.c | 2 +- cmd-new-session.c | 2 +- cmd-new-window.c | 2 +- cmd-paste-buffer.c | 2 +- cmd-pipe-pane.c | 2 +- cmd-queue.c | 2 +- cmd-refresh-client.c | 2 +- cmd-rename-session.c | 2 +- cmd-rename-window.c | 2 +- cmd-resize-pane.c | 2 +- cmd-respawn-pane.c | 2 +- cmd-respawn-window.c | 2 +- cmd-rotate-window.c | 2 +- cmd-select-layout.c | 2 +- cmd-select-pane.c | 2 +- cmd-select-window.c | 2 +- cmd-send-keys.c | 2 +- cmd-set-buffer.c | 2 +- cmd-set-environment.c | 2 +- cmd-set-option.c | 2 +- cmd-show-environment.c | 2 +- cmd-show-messages.c | 2 +- cmd-show-options.c | 2 +- cmd-split-window.c | 2 +- cmd-string.c | 2 +- cmd-swap-pane.c | 2 +- cmd-swap-window.c | 2 +- cmd-switch-client.c | 2 +- cmd-unbind-key.c | 2 +- cmd-wait-for.c | 2 +- cmd.c | 2 +- colour.c | 2 +- control-notify.c | 2 +- control.c | 2 +- environ.c | 2 +- format.c | 2 +- grid-view.c | 2 +- grid.c | 2 +- input-keys.c | 2 +- input.c | 2 +- job.c | 2 +- key-bindings.c | 2 +- key-string.c | 2 +- layout-custom.c | 2 +- layout-set.c | 2 +- layout.c | 2 +- log.c | 2 +- mode-key.c | 2 +- names.c | 2 +- options-table.c | 2 +- options.c | 2 +- paste.c | 2 +- proc.c | 2 +- procname.c | 2 +- resize.c | 2 +- screen-redraw.c | 2 +- screen-write.c | 2 +- screen.c | 2 +- server-client.c | 2 +- server-fn.c | 2 +- server.c | 2 +- session.c | 2 +- signal.c | 2 +- status.c | 2 +- style.c | 2 +- tmux.1 | 4 ++-- tmux.c | 2 +- tmux.h | 2 +- tty-acs.c | 2 +- tty-keys.c | 2 +- tty-term.c | 2 +- tty.c | 2 +- utf8.c | 2 +- window-choose.c | 2 +- window-clock.c | 2 +- window-copy.c | 2 +- window.c | 2 +- xterm-keys.c | 2 +- 108 files changed, 109 insertions(+), 109 deletions(-) diff --git a/alerts.c b/alerts.c index d1fb0fce..cca0d815 100644 --- a/alerts.c +++ b/alerts.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2015 Nicholas Marriott + * Copyright (c) 2015 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/arguments.c b/arguments.c index 0a42cc38..501f38df 100644 --- a/arguments.c +++ b/arguments.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/array.h b/array.h index 671bea42..209de0c5 100644 --- a/array.h +++ b/array.h @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2006 Nicholas Marriott + * Copyright (c) 2006 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cfg.c b/cfg.c index 136c94d1..c5c21b94 100644 --- a/cfg.c +++ b/cfg.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/client.c b/client.c index 516ed3bf..ed15bc80 100644 --- a/client.c +++ b/client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 993f4c75..53c1df31 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-bind-key.c b/cmd-bind-key.c index df3285f7..a829a2c5 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-break-pane.c b/cmd-break-pane.c index c2b021fc..b5a2743f 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c index 1f8fbfb2..90872e90 100644 --- a/cmd-choose-buffer.c +++ b/cmd-choose-buffer.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-choose-client.c b/cmd-choose-client.c index 7d5fc606..b9a24be6 100644 --- a/cmd-choose-client.c +++ b/cmd-choose-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-clear-history.c b/cmd-clear-history.c index 1236e7f1..62683ff6 100644 --- a/cmd-clear-history.c +++ b/cmd-clear-history.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 9200ada1..3ec22865 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index 49f5b30c..beb4d7c8 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-detach-client.c b/cmd-detach-client.c index daf9a5c6..80c6555f 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-display-panes.c b/cmd-display-panes.c index d8db4066..eed3611e 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-find-window.c b/cmd-find-window.c index eb940d8c..6324f26a 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-find.c b/cmd-find.c index 68aaf121..c76dc4a2 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2015 Nicholas Marriott + * Copyright (c) 2015 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 8b4117fa..d630dd45 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2011 George Nachman - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index f843bc58..ebb2b8d6 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 6f84e959..d1940c33 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 4ca4e2c8..c77e45bb 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 7a8e9fa6..9d388ce5 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index a6007c33..238b2776 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-list-clients.c b/cmd-list-clients.c index 75c6f570..f318ac18 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 4abe2473..c3ace8de 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-list-panes.c b/cmd-list-panes.c index da0e0962..76e06a71 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 1fde7f86..27e80dbc 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-list-windows.c b/cmd-list-windows.c index dd05ea85..11a5fddf 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-list.c b/cmd-list.c index 59fc7796..e999c370 100644 --- a/cmd-list.c +++ b/cmd-list.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-lock-server.c b/cmd-lock-server.c index 9cdd816f..01597169 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-move-window.c b/cmd-move-window.c index bb33ab38..e756a638 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-new-session.c b/cmd-new-session.c index f96003c7..b4bb37d5 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-new-window.c b/cmd-new-window.c index 33f68935..2a647b9f 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 0728743a..8025975f 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index a2653dc5..e59fc586 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-queue.c b/cmd-queue.c index 5bc3226a..fcca7eb6 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 444e83fc..79e5aad0 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-rename-session.c b/cmd-rename-session.c index 7fc6193d..b40f44f7 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 36c1bf31..a1f15eef 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index bb29cef9..2b4f1c17 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index bff6c11b..ba2c1cd2 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * Copyright (c) 2011 Marcel P. Partap * * Permission to use, copy, modify, and distribute this software for any diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index da9a365a..95fc0cb4 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index 014c1f2f..94eca37d 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-select-layout.c b/cmd-select-layout.c index e6ede1af..44f01bb7 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 02385a41..14d53d48 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-select-window.c b/cmd-select-window.c index 82acc859..78228067 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 7b0b952c..92c75ec3 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index 1494cf26..1f0cf3d8 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-set-environment.c b/cmd-set-environment.c index f701d7d9..55bdaa9a 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-set-option.c b/cmd-set-option.c index 13de02a3..7fc81286 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 54baafe4..83661c44 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 68ef6674..3131184d 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-show-options.c b/cmd-show-options.c index e99574f8..fec2f1de 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-split-window.c b/cmd-split-window.c index aee323eb..8382d78e 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-string.c b/cmd-string.c index 51554800..757d4cdb 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 84332c2f..aec7753d 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-swap-window.c b/cmd-swap-window.c index e1835820..14907d2d 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-switch-client.c b/cmd-switch-client.c index bc9f5585..6e2ee2a0 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 8e89f21a..7452fd9f 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 59f7dbfb..81b01627 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Nicholas Marriott * Copyright (c) 2013 Thiago de Arruda * * Permission to use, copy, modify, and distribute this software for any diff --git a/cmd.c b/cmd.c index 4f1e1b90..052c095b 100644 --- a/cmd.c +++ b/cmd.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/colour.c b/colour.c index a56ddce9..b349e2a5 100644 --- a/colour.c +++ b/colour.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/control-notify.c b/control-notify.c index a40f8d7c..c28d0fc8 100644 --- a/control-notify.c +++ b/control-notify.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2012 Nicholas Marriott + * Copyright (c) 2012 Nicholas Marriott * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any diff --git a/control.c b/control.c index e799a4cb..f6dedca8 100644 --- a/control.c +++ b/control.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2012 Nicholas Marriott + * Copyright (c) 2012 Nicholas Marriott * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any diff --git a/environ.c b/environ.c index 101dfafd..d855f8b5 100644 --- a/environ.c +++ b/environ.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/format.c b/format.c index 79445936..efa9d1e1 100644 --- a/format.c +++ b/format.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2011 Nicholas Marriott + * Copyright (c) 2011 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/grid-view.c b/grid-view.c index f6708c89..0989f800 100644 --- a/grid-view.c +++ b/grid-view.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/grid.c b/grid.c index 579eb966..c24e6c6a 100644 --- a/grid.c +++ b/grid.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/input-keys.c b/input-keys.c index 3bc1f812..254845cb 100644 --- a/input-keys.c +++ b/input-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/input.c b/input.c index 19fd48b7..1772a4cd 100644 --- a/input.c +++ b/input.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/job.c b/job.c index e9799956..d6541709 100644 --- a/job.c +++ b/job.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/key-bindings.c b/key-bindings.c index 47a7d867..a922eb18 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/key-string.c b/key-string.c index 1ff3ca30..dc211696 100644 --- a/key-string.c +++ b/key-string.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/layout-custom.c b/layout-custom.c index 57503518..99c6c3ce 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/layout-set.c b/layout-set.c index 852ec0f6..bd1304ce 100644 --- a/layout-set.c +++ b/layout-set.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/layout.c b/layout.c index c448814a..31dce95c 100644 --- a/layout.c +++ b/layout.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/log.c b/log.c index 46f1673c..9729a4dd 100644 --- a/log.c +++ b/log.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/mode-key.c b/mode-key.c index a47cda0b..f38ebf66 100644 --- a/mode-key.c +++ b/mode-key.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/names.c b/names.c index 3c25e215..a03f6f5b 100644 --- a/names.c +++ b/names.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/options-table.c b/options-table.c index 63e7ab61..af0f41ed 100644 --- a/options-table.c +++ b/options-table.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2011 Nicholas Marriott + * Copyright (c) 2011 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/options.c b/options.c index 02f0f957..df79ac4b 100644 --- a/options.c +++ b/options.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/paste.c b/paste.c index 5f60914f..f5702438 100644 --- a/paste.c +++ b/paste.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/proc.c b/proc.c index 5f51b0ac..bc27b4ff 100644 --- a/proc.c +++ b/proc.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2015 Nicholas Marriott + * Copyright (c) 2015 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/procname.c b/procname.c index 97a78d71..42f5f473 100644 --- a/procname.c +++ b/procname.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/resize.c b/resize.c index c7805e05..831302b1 100644 --- a/resize.c +++ b/resize.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/screen-redraw.c b/screen-redraw.c index 9958d04a..952a8515 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/screen-write.c b/screen-write.c index e53d3799..05315b3f 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/screen.c b/screen.c index db9f52a6..e002b96e 100644 --- a/screen.c +++ b/screen.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/server-client.c b/server-client.c index 9b88a165..bb54643a 100644 --- a/server-client.c +++ b/server-client.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/server-fn.c b/server-fn.c index 0f35aaaf..39d31f3c 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/server.c b/server.c index 170244c7..890743f6 100644 --- a/server.c +++ b/server.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/session.c b/session.c index b3ae2b42..7f2c3d97 100644 --- a/session.c +++ b/session.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/signal.c b/signal.c index 9a4d58c2..19938638 100644 --- a/signal.c +++ b/signal.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * Copyright (c) 2010 Romain Francoise * * Permission to use, copy, modify, and distribute this software for any diff --git a/status.c b/status.c index cbacfe4c..1bd02c43 100644 --- a/status.c +++ b/status.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/style.c b/style.c index c00b0fee..151c2912 100644 --- a/style.c +++ b/style.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * Copyright (c) 2014 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any diff --git a/tmux.1 b/tmux.1 index 159a4bd9..e41ba798 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,6 +1,6 @@ .\" $OpenBSD$ .\" -.\" Copyright (c) 2007 Nicholas Marriott +.\" Copyright (c) 2007 Nicholas Marriott .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -4248,4 +4248,4 @@ bind-key S command-prompt "new-window -n %1 'ssh %1'" .Sh SEE ALSO .Xr pty 4 .Sh AUTHORS -.An Nicholas Marriott Aq Mt nicm@users.sourceforge.net +.An Nicholas Marriott Aq Mt nicholas.marriott@gmail.com diff --git a/tmux.c b/tmux.c index fe5e54a5..1ee2a269 100644 --- a/tmux.c +++ b/tmux.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/tmux.h b/tmux.h index 55fe407f..941548d0 100644 --- a/tmux.h +++ b/tmux.h @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/tty-acs.c b/tty-acs.c index 5d03c3eb..7fd265d4 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/tty-keys.c b/tty-keys.c index 86839a17..2b998778 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/tty-term.c b/tty-term.c index 27c904a4..8716a1a9 100644 --- a/tty-term.c +++ b/tty-term.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/tty.c b/tty.c index 304e1378..52521be9 100644 --- a/tty.c +++ b/tty.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/utf8.c b/utf8.c index 2210675a..b03c425d 100644 --- a/utf8.c +++ b/utf8.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/window-choose.c b/window-choose.c index fdfc47a0..7a727aac 100644 --- a/window-choose.c +++ b/window-choose.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/window-clock.c b/window-clock.c index e8451f22..4cc58684 100644 --- a/window-clock.c +++ b/window-clock.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/window-copy.c b/window-copy.c index e09a504c..c8e38fd1 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/window.c b/window.c index cfb44ae3..201942a3 100644 --- a/window.c +++ b/window.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/xterm-keys.c b/xterm-keys.c index f1490fcc..7bfefd15 100644 --- a/xterm-keys.c +++ b/xterm-keys.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above From b5b5221c13ded5b141fa35f60c707c9c403b83a6 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 19 Jan 2016 16:01:30 +0000 Subject: [PATCH 563/703] Split out getting the current state from the target search so it can be replaced if we already know the current. --- cmd-find.c | 29 +++++++------ cmd-queue.c | 2 +- cmd.c | 118 ++++++++++++++++++++++++++++++++++++---------------- tmux.h | 10 +++-- 4 files changed, 106 insertions(+), 53 deletions(-) diff --git a/cmd-find.c b/cmd-find.c index c76dc4a2..7ffc5f15 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -923,15 +923,27 @@ cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp) return (0); } +/* Find current state. */ +int +cmd_find_current(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) +{ + cmd_find_clear_state(fs, cmdq, flags); + if (cmd_find_current_session(fs) != 0) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no current session"); + return (-1); + } + return (0); +} + /* * Split target into pieces and resolve for the given type. Fills in the given * state. Returns 0 on success or -1 on error. */ int -cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, - const char *target, enum cmd_find_type type, int flags) +cmd_find_target(struct cmd_find_state *fs, struct cmd_find_state *current, + struct cmd_q *cmdq, const char *target, enum cmd_find_type type, int flags) { - struct cmd_find_state current; struct mouse_event *m; char *colon, *period, *copy = NULL; const char *session, *window, *pane; @@ -951,15 +963,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmd_q *cmdq, fs->current = &marked_pane; else if (cmd_find_valid_state(&cmdq->current)) fs->current = &cmdq->current; - else { - cmd_find_clear_state(¤t, cmdq, flags); - if (cmd_find_current_session(¤t) != 0) { - if (~flags & CMD_FIND_QUIET) - cmdq_error(cmdq, "no current session"); - goto error; - } - fs->current = ¤t; - } + else + fs->current = current; /* An empty or NULL target is the current. */ if (target == NULL || *target == '\0') diff --git a/cmd-queue.c b/cmd-queue.c index fcca7eb6..1c80c59d 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -199,7 +199,7 @@ cmdq_continue_one(struct cmd_q *cmdq) cmdq_guard(cmdq, "begin", flags); - if (cmd_prepare_state(cmd, cmdq) != 0) + if (cmd_prepare_state(cmd, cmdq, NULL) != 0) goto error; retval = cmd->entry->exec(cmd, cmdq); if (retval == CMD_RETURN_ERROR) diff --git a/cmd.c b/cmd.c index 052c095b..005f2a0f 100644 --- a/cmd.c +++ b/cmd.c @@ -389,10 +389,23 @@ usage: } static int -cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, - const char *target, struct cmd_q *cmdq) +cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, + struct cmd_q *cmdq, struct cmd_q *parent) { - int targetflags, error; + int targetflags, error; + struct cmd_find_state *fs = NULL; + struct cmd_find_state *current = NULL; + struct cmd_find_state tmp; + + if (flag == CMD_NONE || + flag == CMD_CLIENT || + flag == CMD_CLIENT_CANFAIL) + return (0); + + if (c == 't') + fs = &cmdq->state.tflag; + else if (c == 's') + fs = &cmdq->state.sflag; if (flag == CMD_SESSION_WITHPANE) { if (target != NULL && target[strcspn(target, ":.")] != '\0') @@ -401,6 +414,55 @@ cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, flag = CMD_SESSION; } + targetflags = 0; + switch (flag) { + case CMD_SESSION: + case CMD_SESSION_CANFAIL: + case CMD_SESSION_PREFERUNATTACHED: + if (flag == CMD_SESSION_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_SESSION_PREFERUNATTACHED) + targetflags |= CMD_FIND_PREFER_UNATTACHED; + break; + case CMD_MOVEW_R: + flag = CMD_WINDOW_INDEX; + /* FALLTHROUGH */ + case CMD_WINDOW: + case CMD_WINDOW_CANFAIL: + case CMD_WINDOW_MARKED: + case CMD_WINDOW_INDEX: + if (flag == CMD_WINDOW_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_WINDOW_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + if (flag == CMD_WINDOW_INDEX) + targetflags |= CMD_FIND_WINDOW_INDEX; + break; + case CMD_PANE: + case CMD_PANE_CANFAIL: + case CMD_PANE_MARKED: + if (flag == CMD_PANE_CANFAIL) + targetflags |= CMD_FIND_QUIET; + if (flag == CMD_PANE_MARKED) + targetflags |= CMD_FIND_DEFAULT_MARKED; + break; + default: + fatalx("unknown %cflag %d", c, flag); + } + + log_debug("%s: flag %c %d %#x", __func__, c, flag, targetflags); + if (parent != NULL) { + if (c == 't') + current = &parent->state.tflag; + else if (c == 's') + current = &parent->state.sflag; + } else { + error = cmd_find_current(&tmp, cmdq, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) + return (-1); + current = &tmp; + } + switch (flag) { case CMD_NONE: case CMD_CLIENT: @@ -410,20 +472,14 @@ cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, case CMD_SESSION_CANFAIL: case CMD_SESSION_PREFERUNATTACHED: case CMD_SESSION_WITHPANE: - targetflags = 0; - if (flag == CMD_SESSION_CANFAIL) - targetflags |= CMD_FIND_QUIET; - if (flag == CMD_SESSION_PREFERUNATTACHED) - targetflags |= CMD_FIND_PREFER_UNATTACHED; - - error = cmd_find_target(fs, cmdq, target, CMD_FIND_SESSION, - targetflags); - if (error != 0 && flag != CMD_SESSION_CANFAIL) + error = cmd_find_target(fs, current, cmdq, target, + CMD_FIND_SESSION, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); break; case CMD_MOVEW_R: - error = cmd_find_target(fs, cmdq, target, CMD_FIND_SESSION, - CMD_FIND_QUIET); + error = cmd_find_target(fs, current, cmdq, target, + CMD_FIND_SESSION, CMD_FIND_QUIET); if (error == 0) break; flag = CMD_WINDOW_INDEX; @@ -432,39 +488,27 @@ cmd_prepare_state_flag(struct cmd_find_state *fs, enum cmd_entry_flag flag, case CMD_WINDOW_CANFAIL: case CMD_WINDOW_MARKED: case CMD_WINDOW_INDEX: - targetflags = 0; - if (flag == CMD_WINDOW_CANFAIL) - targetflags |= CMD_FIND_QUIET; - if (flag == CMD_WINDOW_MARKED) - targetflags |= CMD_FIND_DEFAULT_MARKED; - if (flag == CMD_WINDOW_INDEX) - targetflags |= CMD_FIND_WINDOW_INDEX; - - error = cmd_find_target(fs, cmdq, target, CMD_FIND_WINDOW, - targetflags); - if (error != 0 && flag != CMD_WINDOW_CANFAIL) + error = cmd_find_target(fs, current, cmdq, target, + CMD_FIND_WINDOW, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); break; case CMD_PANE: case CMD_PANE_CANFAIL: case CMD_PANE_MARKED: - targetflags = 0; - if (flag == CMD_PANE_CANFAIL) - targetflags |= CMD_FIND_QUIET; - if (flag == CMD_PANE_MARKED) - targetflags |= CMD_FIND_DEFAULT_MARKED; - - error = cmd_find_target(fs, cmdq, target, CMD_FIND_PANE, - targetflags); - if (error != 0 && flag != CMD_PANE_CANFAIL) + error = cmd_find_target(fs, current, cmdq, target, + CMD_FIND_PANE, targetflags); + if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); break; + default: + fatalx("unknown %cflag %d", c, flag); } return (0); } int -cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) +cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq, struct cmd_q *parent) { const struct cmd_entry *entry = cmd->entry; struct cmd_state *state = &cmdq->state; @@ -504,14 +548,14 @@ cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq) s = args_get(cmd->args, 't'); log_debug("preparing -t state: target %s", s == NULL ? "none" : s); - error = cmd_prepare_state_flag(&state->tflag, entry->tflag, s, cmdq); + error = cmd_prepare_state_flag('t', s, entry->tflag, cmdq, parent); if (error != 0) return (error); s = args_get(cmd->args, 's'); log_debug("preparing -s state: target %s", s == NULL ? "none" : s); - error = cmd_prepare_state_flag(&state->sflag, entry->sflag, s, cmdq); + error = cmd_prepare_state_flag('s', s, entry->sflag, cmdq, parent); if (error != 0) return (error); diff --git a/tmux.h b/tmux.h index 941548d0..eb3373da 100644 --- a/tmux.h +++ b/tmux.h @@ -1769,8 +1769,11 @@ long long args_strtonum(struct args *, u_char, long long, long long, char **); /* cmd-find.c */ -int cmd_find_target(struct cmd_find_state *, struct cmd_q *, - const char *, enum cmd_find_type, int); +int cmd_find_current(struct cmd_find_state *, struct cmd_q *, + int); +int cmd_find_target(struct cmd_find_state *, + struct cmd_find_state *, struct cmd_q *, const char *, + enum cmd_find_type, int); struct client *cmd_find_client(struct cmd_q *, const char *, int); void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); @@ -1793,7 +1796,8 @@ char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); -int cmd_prepare_state(struct cmd *, struct cmd_q *); +int cmd_prepare_state(struct cmd *, struct cmd_q *, + struct cmd_q *); char *cmd_print(struct cmd *); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); From ca29dc9abc5c90da1a56d06ecb692b35a3766bc0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 19 Jan 2016 18:07:25 +0000 Subject: [PATCH 564/703] Update my email address. --- compat.h | 2 +- compat/asprintf.c | 2 +- compat/cfmakeraw.c | 2 +- compat/forkpty-aix.c | 2 +- compat/forkpty-hpux.c | 2 +- compat/forkpty-sunos.c | 2 +- compat/openat.c | 2 +- compat/setenv.c | 2 +- osdep-aix.c | 2 +- osdep-cygwin.c | 2 +- osdep-dragonfly.c | 2 +- osdep-freebsd.c | 2 +- osdep-hpux.c | 2 +- osdep-linux.c | 2 +- osdep-netbsd.c | 2 +- osdep-unknown.c | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/compat.h b/compat.h index 6812e9a5..b2267d37 100644 --- a/compat.h +++ b/compat.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/compat/asprintf.c b/compat/asprintf.c index 09020b35..5ed1c48c 100644 --- a/compat/asprintf.c +++ b/compat/asprintf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 Nicholas Marriott + * Copyright (c) 2006 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/compat/cfmakeraw.c b/compat/cfmakeraw.c index 85b2c9bc..d8794081 100644 --- a/compat/cfmakeraw.c +++ b/compat/cfmakeraw.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013 Dagobert Michelsen - * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/compat/forkpty-aix.c b/compat/forkpty-aix.c index 6894aa44..2557ebf7 100644 --- a/compat/forkpty-aix.c +++ b/compat/forkpty-aix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/compat/forkpty-hpux.c b/compat/forkpty-hpux.c index 59130e1b..09b27d08 100644 --- a/compat/forkpty-hpux.c +++ b/compat/forkpty-hpux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/compat/forkpty-sunos.c b/compat/forkpty-sunos.c index 554e51ac..9abda46c 100644 --- a/compat/forkpty-sunos.c +++ b/compat/forkpty-sunos.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/compat/openat.c b/compat/openat.c index 6b04eedc..d003e53d 100644 --- a/compat/openat.c +++ b/compat/openat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Nicholas Marriott + * Copyright (c) 2013 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/compat/setenv.c b/compat/setenv.c index 6c7d29ec..b16b08cf 100644 --- a/compat/setenv.c +++ b/compat/setenv.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2010 Dagobert Michelsen - * Copyright (c) 2010 Nicholas Marriott + * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/osdep-aix.c b/osdep-aix.c index ef7d6c7e..e1ce4918 100644 --- a/osdep-aix.c +++ b/osdep-aix.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2011 Nicholas Marriott + * Copyright (c) 2011 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/osdep-cygwin.c b/osdep-cygwin.c index 9a3ea408..60630b33 100644 --- a/osdep-cygwin.c +++ b/osdep-cygwin.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/osdep-dragonfly.c b/osdep-dragonfly.c index f9b0efcf..879034e8 100644 --- a/osdep-dragonfly.c +++ b/osdep-dragonfly.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/osdep-freebsd.c b/osdep-freebsd.c index d7f419b8..067ba565 100644 --- a/osdep-freebsd.c +++ b/osdep-freebsd.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/osdep-hpux.c b/osdep-hpux.c index a6d75f94..16993b93 100644 --- a/osdep-hpux.c +++ b/osdep-hpux.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/osdep-linux.c b/osdep-linux.c index 00501efb..42712dea 100644 --- a/osdep-linux.c +++ b/osdep-linux.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/osdep-netbsd.c b/osdep-netbsd.c index 15686860..d8aa41b5 100644 --- a/osdep-netbsd.c +++ b/osdep-netbsd.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/osdep-unknown.c b/osdep-unknown.c index 9465db97..bc59f569 100644 --- a/osdep-unknown.c +++ b/osdep-unknown.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* - * Copyright (c) 2009 Nicholas Marriott + * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above From 64e14eaff5912b592565130ca116251a1028bfe6 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 23 Jan 2016 00:52:35 -0500 Subject: [PATCH 565/703] Avoid crashes when the ssh connction dies We might want to deal with reconnections soon --- tmate-ssh-client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 691a49ce..cd3a71b0 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -49,6 +49,9 @@ static void on_encoder_write(void *userdata, struct evbuffer *buffer) ssize_t len, written; unsigned char *buf; + if (!client->channel) + return; + for(;;) { len = evbuffer_get_length(buffer); if (!len) From 405cd82a8205c77657a586ee10e6ad015d686d25 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 23 Jan 2016 01:07:25 -0500 Subject: [PATCH 566/703] Don't abort on unknown messages --- tmate-decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-decoder.c b/tmate-decoder.c index d4646ac7..bc5141f5 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -146,6 +146,6 @@ void tmate_dispatch_slave_message(struct tmate_session *session, dispatch(TMATE_IN_SET_ENV, handle_set_env); dispatch(TMATE_IN_READY, handle_ready); dispatch(TMATE_IN_PANE_KEY, handle_pane_key); - default: tmate_fatal("Bad message type: %d", cmd); + default: tmate_info("Bad message type: %d", cmd); } } From 1d6bd50343f4395879169868e47bb59f5b2f3811 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 29 Jan 2016 10:58:08 +0000 Subject: [PATCH 567/703] libevent.org URL. --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 6a78fa2b..88d77b5f 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. tmux depends on libevent 2.x. Download it from: - http://www.monkey.org/~provos/libevent/ + http://libevent.org To build tmux from a release tarball, do: From 427b8204268af5548d09b830e101c59daa095df9 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 29 Jan 2016 11:13:56 +0000 Subject: [PATCH 568/703] Support for RGB colour, using the extended cell mechanism to avoid wasting unnecessary space. The 'Tc' flag must be set in the external TERM entry (using terminal-overrides or a custom terminfo entry), if not tmux will map to the closest of the 256 or 16 colour palettes. Mostly from Suraj N Kurapati, based on a diff originally by someone else. --- grid.c | 11 +++- input.c | 35 ++++++---- tmux.1 | 12 ++-- tmux.h | 20 +++++- tty-term.c | 1 + tty.c | 185 ++++++++++++++++++++++++++++++++++++++++++++--------- 6 files changed, 209 insertions(+), 55 deletions(-) diff --git a/grid.c b/grid.c index c24e6c6a..782032f4 100644 --- a/grid.c +++ b/grid.c @@ -37,7 +37,7 @@ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { - 0, 0, 8, 8, { { ' ' }, 0, 1, 1 } + 0, 0, { .fg = 8 }, { .bg = 8 }, { { ' ' }, 0, 1, 1 } }; const struct grid_cell_entry grid_default_entry = { 0, { .data = { 0, 8, 8, ' ' } } @@ -284,6 +284,7 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) struct grid_line *gl; struct grid_cell_entry *gce; struct grid_cell *gcp; + int extended; if (grid_check_y(gd, py) != 0) return; @@ -293,8 +294,12 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) gl = &gd->linedata[py]; gce = &gl->celldata[px]; - if ((gce->flags & GRID_FLAG_EXTENDED) || gc->data.size != 1 || - gc->data.width != 1) { + extended = (gce->flags & GRID_FLAG_EXTENDED); + if (!extended && (gc->data.size != 1 || gc->data.width != 1)) + extended = 1; + if (!extended && (gc->flags & (GRID_FLAG_FGRGB|GRID_FLAG_BGRGB))) + extended = 1; + if (extended) { if (~gce->flags & GRID_FLAG_EXTENDED) { gl->extddata = xreallocarray(gl->extddata, gl->extdsize + 1, sizeof *gl->extddata); diff --git a/input.c b/input.c index 1772a4cd..ae205024 100644 --- a/input.c +++ b/input.c @@ -1629,18 +1629,20 @@ input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) c = input_get(ictx, *i, 0, -1); if (c == -1) { if (fgbg == 38) { - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = 8; } else if (fgbg == 48) { - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = 8; } } else { if (fgbg == 38) { gc->flags |= GRID_FLAG_FG256; + gc->flags &= ~GRID_FLAG_FGRGB; gc->fg = c; } else if (fgbg == 48) { gc->flags |= GRID_FLAG_BG256; + gc->flags &= ~GRID_FLAG_BGRGB; gc->bg = c; } } @@ -1651,7 +1653,7 @@ void input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) { struct grid_cell *gc = &ictx->cell.cell; - int c, r, g, b; + int r, g, b; (*i)++; r = input_get(ictx, *i, 0, -1); @@ -1666,13 +1668,18 @@ input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) if (b == -1 || b > 255) return; - c = colour_find_rgb(r, g, b); if (fgbg == 38) { - gc->flags |= GRID_FLAG_FG256; - gc->fg = c; + gc->flags &= ~GRID_FLAG_FG256; + gc->flags |= GRID_FLAG_FGRGB; + gc->fg_rgb.r = r; + gc->fg_rgb.g = g; + gc->fg_rgb.b = b; } else if (fgbg == 48) { - gc->flags |= GRID_FLAG_BG256; - gc->bg = c; + gc->flags &= ~GRID_FLAG_BG256; + gc->flags |= GRID_FLAG_BGRGB; + gc->bg_rgb.r = r; + gc->bg_rgb.g = g; + gc->bg_rgb.b = b; } } @@ -1754,11 +1761,11 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 35: case 36: case 37: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = n - 30; break; case 39: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = 8; break; case 40: @@ -1769,11 +1776,11 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 45: case 46: case 47: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = n - 40; break; case 49: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = 8; break; case 90: @@ -1784,7 +1791,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 95: case 96: case 97: - gc->flags &= ~GRID_FLAG_FG256; + gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = n; break; case 100: @@ -1795,7 +1802,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) case 105: case 106: case 107: - gc->flags &= ~GRID_FLAG_BG256; + gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = n - 10; break; } diff --git a/tmux.1 b/tmux.1 index e41ba798..e70b07c3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2921,7 +2921,7 @@ and poor for interactive programs such as shells. .Op Ic on | off .Xc Allow programs to change the window name using a terminal escape -sequence (\\033k...\\033\\\\). +sequence (\eek...\ee\e\e). The default is on. .Pp .It Xo Ic alternate-screen @@ -4024,7 +4024,7 @@ This command only works from outside .El .Sh TERMINFO EXTENSIONS .Nm -understands some extensions to +understands some unofficial extensions to .Xr terminfo 5 : .Bl -tag -width Ds .It Em Cs , Cr @@ -4048,10 +4048,12 @@ $ printf '\e033[4 q' If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. +.It Em \&Tc +Indicate that the terminal supports the +.Ql direct colour +RGB escape sequence (for example, \ee[38;2;255;255;255m). .It Em \&Ms -This sequence can be used by -.Nm -to store the current buffer in the host terminal's selection (clipboard). +Store the current buffer in the host terminal's selection (clipboard). See the .Em set-clipboard option above and the diff --git a/tmux.h b/tmux.h index eb3373da..8edb9209 100644 --- a/tmux.h +++ b/tmux.h @@ -385,6 +385,7 @@ enum tty_code_code { TTYC_SMSO, /* enter_standout_mode, so */ TTYC_SMUL, /* enter_underline_mode, us */ TTYC_SS, /* set cursor style, Ss */ + TTYC_TC, /* 24-bit "true" colour, Tc */ TTYC_TSL, /* to_status_line, tsl */ TTYC_VPA, /* row_address, cv */ TTYC_XENL, /* eat_newline_glitch, xn */ @@ -641,16 +642,31 @@ enum utf8_state { #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 #define GRID_FLAG_EXTENDED 0x8 +#define GRID_FLAG_FGRGB 0x10 +#define GRID_FLAG_BGRGB 0x20 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 +/* Grid cell RGB colours. */ +struct grid_cell_rgb { + u_char r; + u_char g; + u_char b; +}; + /* Grid cell data. */ struct grid_cell { u_char flags; u_char attr; - u_char fg; - u_char bg; + union { + u_char fg; + struct grid_cell_rgb fg_rgb; + }; + union { + u_char bg; + struct grid_cell_rgb bg_rgb; + }; struct utf8_data data; }; diff --git a/tty-term.c b/tty-term.c index 8716a1a9..05ef01e7 100644 --- a/tty-term.c +++ b/tty-term.c @@ -251,6 +251,7 @@ const struct tty_term_code_entry tty_term_codes[] = { [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, + [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, diff --git a/tty.c b/tty.c index 52521be9..c6fc2213 100644 --- a/tty.c +++ b/tty.c @@ -36,8 +36,15 @@ static int tty_log_fd = -1; void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); +static int tty_same_fg(const struct grid_cell *, const struct grid_cell *); +static int tty_same_bg(const struct grid_cell *, const struct grid_cell *); +static int tty_same_colours(const struct grid_cell *, const struct grid_cell *); +static int tty_is_fg(const struct grid_cell *, int); +static int tty_is_bg(const struct grid_cell *, int); + void tty_set_italics(struct tty *); int tty_try_256(struct tty *, u_char, const char *); +int tty_try_rgb(struct tty *, const struct grid_cell_rgb *, const char *); void tty_colours(struct tty *, const struct grid_cell *); void tty_check_fg(struct tty *, struct grid_cell *); @@ -61,6 +68,74 @@ void tty_default_colours(struct grid_cell *, const struct window_pane *); #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) +static int +tty_same_fg(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + int flags1, flags2; + + flags1 = (gc1->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); + flags2 = (gc2->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); + + if (flags1 != flags2) + return (0); + + if (flags1 & GRID_FLAG_FGRGB) { + if (gc1->fg_rgb.r != gc2->fg_rgb.r) + return (0); + if (gc1->fg_rgb.g != gc2->fg_rgb.g) + return (0); + if (gc1->fg_rgb.b != gc2->fg_rgb.b) + return (0); + return (1); + } + return (gc1->fg == gc2->fg); +} + +static int +tty_same_bg(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + int flags1, flags2; + + flags1 = (gc1->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); + flags2 = (gc2->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); + + if (flags1 != flags2) + return (0); + + if (flags1 & GRID_FLAG_BGRGB) { + if (gc1->bg_rgb.r != gc2->bg_rgb.r) + return (0); + if (gc1->bg_rgb.g != gc2->bg_rgb.g) + return (0); + if (gc1->bg_rgb.b != gc2->bg_rgb.b) + return (0); + return (1); + } + return (gc1->bg == gc2->bg); +} + +static int +tty_same_colours(const struct grid_cell *gc1, const struct grid_cell *gc2) +{ + return (tty_same_fg(gc1, gc2) && tty_same_bg(gc1, gc2)); +} + +static int +tty_is_fg(const struct grid_cell *gc, int c) +{ + if (gc->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)) + return (0); + return (gc->fg == c); +} + +static int +tty_is_bg(const struct grid_cell *gc, int c) +{ + if (gc->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)) + return (0); + return (gc->bg == c); +} + void tty_create_log(void) { @@ -1423,12 +1498,10 @@ void tty_colours(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; - u_char fg = gc->fg, bg = gc->bg, flags = gc->flags; int have_ax, fg_default, bg_default; /* No changes? Nothing is necessary. */ - if (fg == tc->fg && bg == tc->bg && - ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0) + if (tty_same_colours(gc, tc)) return; /* @@ -1437,8 +1510,8 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) * case if only one is default need to fall onward to set the other * colour. */ - fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256)); - bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256)); + fg_default = tty_is_fg(gc, 8); + bg_default = tty_is_bg(gc, 8); if (fg_default || bg_default) { /* * If don't have AX but do have op, send sgr0 (op can't @@ -1451,48 +1524,52 @@ tty_colours(struct tty *tty, const struct grid_cell *gc) if (!have_ax && tty_term_has(tty->term, TTYC_OP)) tty_reset(tty); else { - if (fg_default && - (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) { + if (fg_default && !tty_is_fg(tc, 8)) { if (have_ax) tty_puts(tty, "\033[39m"); - else if (tc->fg != 7 || - tc->flags & GRID_FLAG_FG256) + else if (!tty_is_fg(tc, 7)) tty_putcode1(tty, TTYC_SETAF, 7); tc->fg = 8; - tc->flags &= ~GRID_FLAG_FG256; + tc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); } - if (bg_default && - (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) { + if (bg_default && !tty_is_bg(tc, 8)) { if (have_ax) tty_puts(tty, "\033[49m"); - else if (tc->bg != 0 || - tc->flags & GRID_FLAG_BG256) + else if (!tty_is_bg(tc, 0)) tty_putcode1(tty, TTYC_SETAB, 0); tc->bg = 8; - tc->flags &= ~GRID_FLAG_BG256; + tc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); } } } /* Set the foreground colour. */ - if (!fg_default && (fg != tc->fg || - ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)))) + if (!fg_default && !tty_same_fg(gc, tc)) tty_colours_fg(tty, gc); /* * Set the background colour. This must come after the foreground as * tty_colour_fg() can call tty_reset(). */ - if (!bg_default && (bg != tc->bg || - ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)))) + if (!bg_default && !tty_same_bg(gc, tc)) tty_colours_bg(tty, gc); } void tty_check_fg(struct tty *tty, struct grid_cell *gc) { - u_int colours; + struct grid_cell_rgb *rgb = &gc->fg_rgb; + u_int colours; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_FGRGB) { + /* Not a 24-bit terminal? Translate to 256-colour palette. */ + if (!tty_term_flag(tty->term, TTYC_TC)) { + gc->flags &= ~GRID_FLAG_FGRGB; + gc->flags |= GRID_FLAG_FG256; + gc->fg = colour_find_rgb(rgb->r, rgb->g, rgb->b); + } + } colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ @@ -1524,8 +1601,18 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) void tty_check_bg(struct tty *tty, struct grid_cell *gc) { - u_int colours; + struct grid_cell_rgb *rgb = &gc->bg_rgb; + u_int colours; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_BGRGB) { + /* Not a 24-bit terminal? Translate to 256-colour palette. */ + if (!tty_term_flag(tty->term, TTYC_TC)) { + gc->flags &= ~GRID_FLAG_BGRGB; + gc->flags |= GRID_FLAG_BG256; + gc->bg = colour_find_rgb(rgb->r, rgb->g, rgb->b); + } + } colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ @@ -1560,12 +1647,21 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) u_char fg = gc->fg; char s[32]; + tc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); + + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_FGRGB) { + if (tty_try_rgb(tty, &gc->fg_rgb, "38") == 0) + goto save_fg; + /* Should not get here, already converted in tty_check_fg. */ + return; + } + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { - /* Try as 256 colours. */ if (tty_try_256(tty, fg, "38") == 0) goto save_fg; - /* Else already handled by tty_check_fg. */ + /* Should not get here, already converted in tty_check_fg. */ return; } @@ -1581,9 +1677,12 @@ tty_colours_fg(struct tty *tty, const struct grid_cell *gc) save_fg: /* Save the new values in the terminal current cell. */ - tc->fg = fg; - tc->flags &= ~GRID_FLAG_FG256; - tc->flags |= gc->flags & GRID_FLAG_FG256; + if (gc->flags & GRID_FLAG_FGRGB) + memcpy(&tc->fg_rgb, &gc->fg_rgb, sizeof tc->fg_rgb); + else + tc->fg = fg; + tc->flags &= ~(GRID_FLAG_FGRGB|GRID_FLAG_FG256); + tc->flags |= (gc->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); } void @@ -1593,12 +1692,19 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) u_char bg = gc->bg; char s[32]; + /* Is this a 24-bit colour? */ + if (gc->flags & GRID_FLAG_BGRGB) { + if (tty_try_rgb(tty, &gc->bg_rgb, "48") == 0) + goto save_bg; + /* Should not get here, already converted in tty_check_bg. */ + return; + } + /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { - /* Try as 256 colours. */ if (tty_try_256(tty, bg, "48") == 0) goto save_bg; - /* Else already handled by tty_check_bg. */ + /* Should not get here, already converted in tty_check_bg. */ return; } @@ -1614,9 +1720,12 @@ tty_colours_bg(struct tty *tty, const struct grid_cell *gc) save_bg: /* Save the new values in the terminal current cell. */ - tc->bg = bg; - tc->flags &= ~GRID_FLAG_BG256; - tc->flags |= gc->flags & GRID_FLAG_BG256; + if (gc->flags & GRID_FLAG_BGRGB) + memcpy(&tc->bg_rgb, &gc->bg_rgb, sizeof tc->bg_rgb); + else + tc->bg = bg; + tc->flags &= ~(GRID_FLAG_BGRGB|GRID_FLAG_BG256); + tc->flags |= (gc->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); } int @@ -1656,6 +1765,20 @@ fallback: return (0); } +int +tty_try_rgb(struct tty *tty, const struct grid_cell_rgb *rgb, const char *type) +{ + char s[32]; + + if (!tty_term_flag(tty->term, TTYC_TC)) + return (-1); + + xsnprintf(s, sizeof s, "\033[%s;2;%hhu;%hhu;%hhum", type, rgb->r, + rgb->g, rgb->b); + tty_puts(tty, s); + return (0); +} + void tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) { From a33bb3e876895ef40ee90e5f89c76184e65c7f10 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 29 Jan 2016 14:40:30 +0000 Subject: [PATCH 569/703] Link to the bash(1) completion file from README rather than including it in examples. --- README | 4 ++ examples/bash_completion_tmux.sh | 105 ------------------------------- 2 files changed, 4 insertions(+), 105 deletions(-) delete mode 100644 examples/bash_completion_tmux.sh diff --git a/README b/README index 88d77b5f..c0102e68 100644 --- a/README +++ b/README @@ -40,6 +40,10 @@ A vim(1) syntax file is available at: https://github.com/keith/tmux.vim https://raw.githubusercontent.com/keith/tmux.vim/master/syntax/tmux.vim +And a bash(1) completion file at: + + https://github.com/przepompownia/tmux-bash-completion + For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. diff --git a/examples/bash_completion_tmux.sh b/examples/bash_completion_tmux.sh deleted file mode 100644 index 74728b91..00000000 --- a/examples/bash_completion_tmux.sh +++ /dev/null @@ -1,105 +0,0 @@ -# START tmux completion -# This file is in the public domain -# See: http://www.debian-administration.org/articles/317 for how to write more. -# Usage: Put "source bash_completion_tmux.sh" into your .bashrc -_tmux() -{ - local cur prev opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - opts=" \ - attach-session \ - bind-key \ - break-pane \ - capture-pane \ - choose-client \ - choose-session \ - choose-window \ - clear-history \ - clock-mode \ - command-prompt \ - confirm-before \ - copy-buffer \ - copy-mode \ - delete-buffer \ - detach-client \ - display-message \ - display-panes \ - down-pane \ - find-window \ - has-session \ - if-shell \ - join-pane \ - kill-pane \ - kill-server \ - kill-session \ - kill-window \ - last-window \ - link-window \ - list-buffers \ - list-clients \ - list-commands \ - list-keys \ - list-panes \ - list-sessions \ - list-windows \ - load-buffer \ - lock-client \ - lock-server \ - lock-session \ - move-window \ - new-session \ - new-window \ - next-layout \ - next-window \ - paste-buffer \ - pipe-pane \ - previous-layout \ - previous-window \ - refresh-client \ - rename-session \ - rename-window \ - resize-pane \ - respawn-window \ - rotate-window \ - run-shell \ - save-buffer \ - select-layout \ - select-pane \ - select-prompt \ - select-window \ - send-keys \ - send-prefix \ - server-info \ - set-buffer \ - set-environment \ - set-option \ - set-window-option \ - show-buffer \ - show-environment \ - show-messages \ - show-options \ - show-window-options \ - source-file \ - split-window \ - start-server \ - suspend-client \ - swap-pane \ - swap-window \ - switch-client \ - unbind-key \ - unlink-window \ - up-pane" - - COMPREPLY=($(compgen -W "${opts}" -- ${cur})) - return 0 - -} -complete -F _tmux tmux - -# END tmux completion - - - From 2a1bb91bf73366b89d27f2ecfacdc770ceecb72e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 29 Jan 2016 14:53:28 +0000 Subject: [PATCH 570/703] Remove old examples in favour of one example configuration file. --- README | 2 +- example_tmux.conf | 66 +++++++++++++++++++++++ examples/h-boetes.conf | 42 --------------- examples/n-marriott.conf | 110 -------------------------------------- examples/screen-keys.conf | 102 ----------------------------------- examples/t-williams.conf | 104 ----------------------------------- examples/vim-keys.conf | 36 ------------- examples/xterm-keys.vim | 49 ----------------- 8 files changed, 67 insertions(+), 444 deletions(-) create mode 100644 example_tmux.conf delete mode 100644 examples/h-boetes.conf delete mode 100644 examples/n-marriott.conf delete mode 100644 examples/screen-keys.conf delete mode 100644 examples/t-williams.conf delete mode 100644 examples/vim-keys.conf delete mode 100644 examples/xterm-keys.vim diff --git a/README b/README index c0102e68..dbc9b173 100644 --- a/README +++ b/README @@ -33,7 +33,7 @@ the source tree with: Some common questions are answered in the FAQ file and a more extensive (but slightly out of date) guide is available in the OpenBSD FAQ at http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO -file and some example configurations are in the examples directory. +file and an example configuration in example_tmux.conf. A vim(1) syntax file is available at: diff --git a/example_tmux.conf b/example_tmux.conf new file mode 100644 index 00000000..f659a3c2 --- /dev/null +++ b/example_tmux.conf @@ -0,0 +1,66 @@ +# +# Example .tmux.conf +# +# By Nicholas Marriott. Public domain. +# + +# Some tweaks to the status line +set -g status-bg green +set -g status-right "%H:%M" +set -g window-status-current-attr "underscore" + +# No bells at all +set -g bell-action none + +# Lock after 15 minutes +set -g lock-after-time 1800 + +# Keep windows around after they exit +set -g remain-on-exit on + +# Turn on xterm-keys so that additional function keys get escape sequences +set -g xterm-keys on + +# Change the prefix key to C-a +set -g prefix C-a +unbind C-b +bind C-a send-prefix + +# Turn the mouse on, but without copy mode dragging +set -g mouse on +unbind -n MouseDrag1Pane +unbind -temacs-copy MouseDrag1Pane + +# Some extra key bindings to select higher numbered windows +bind F1 selectw -t:10 +bind F2 selectw -t:11 +bind F3 selectw -t:12 +bind F4 selectw -t:13 +bind F5 selectw -t:14 +bind F6 selectw -t:15 +bind F7 selectw -t:16 +bind F8 selectw -t:17 +bind F9 selectw -t:18 +bind F10 selectw -t:19 +bind F11 selectw -t:20 +bind F12 selectw -t:21 + +# Keys to toggle monitoring activity in a window, and synchronize-panes +bind m set monitor-activity +bind y set synchronize-panes\; display 'synchronize-panes #{?synchronize-panes,on,off}' + +# Keys to hide and show a window name from the status line +bind '-' set window-status-format '#I'\; set window-status-current-format '#I' +bind '+' set window-status-format '#I:#W#F'\; set window-status-current-format '#I:#W#F' + +# Create a single default session +new -d -s0 -nirssi 'exec irssi' +set -t0:0 monitor-activity on +set -t0:0 aggressive-resize on +neww -d -ntodo 'exec emacs ~/TODO' +setw -t0:1 aggressive-resize on +neww -d -nmutt 'exec mutt' +setw -t0:2 aggressive-resize on +neww -d +neww -d +neww -d diff --git a/examples/h-boetes.conf b/examples/h-boetes.conf deleted file mode 100644 index 2aa86dc5..00000000 --- a/examples/h-boetes.conf +++ /dev/null @@ -1,42 +0,0 @@ -# $Id: h-boetes.conf,v 1.2 2009-10-25 21:45:26 nicm Exp $ -# -# From Han Boetes. - -set -g default-command zsh -set -g status-right "#(uptime|awk '{print $11}') #(date)" - -# Statusbar properties. -set -g display-time 3000 -set -g status-bg black -set -g status-fg cyan -set-window-option -g window-status-current-attr bright,reverse -set-window-option -g window-status-current-bg cyan -set-window-option -g window-status-current-fg black - -# Use c-t instead of c-b as the prefix -unbind C-b -set -g prefix C-t -bind C-t send-prefix -bind t send-prefix - -# Bind function keys. -bind -n F1 select-window -t 1 -bind -n F2 select-window -t 2 -bind -n F3 select-window -t 3 -bind -n F4 select-window -t 4 -bind -n F5 select-window -t 5 -bind -n F6 select-window -t 6 -bind -n F7 select-window -t 7 -bind -n F8 select-window -t 8 - -# All new windows started at startup. -new emacs -neww irssi -neww mutt -neww -neww -neww -neww -neww - -select-window -t 1 diff --git a/examples/n-marriott.conf b/examples/n-marriott.conf deleted file mode 100644 index 6a047ec9..00000000 --- a/examples/n-marriott.conf +++ /dev/null @@ -1,110 +0,0 @@ -# $Id: n-marriott.conf,v 1.11 2009-11-24 19:03:59 nicm Exp $ -# -# By Nicholas Marriott. Public domain. - -# Default global options. -set -g status-bg green -set -g status-right "%H:%M" # %d-%b-%y -set -g bell-action none -set -g lock-after-time 1800 - -# Default global window options. -setw -g remain-on-exit on -setw -g window-status-current-attr "underscore" -#setw -g xterm-keys on - -# Prefix key. -set -g prefix C-a -unbind C-b -bind C-a send-prefix - -# Keys to switch session. -bind Q switchc -t0 -bind W switchc -t1 -bind E switchc -t2 - -# Other key bindings. -bind F1 selectw -t:10 -bind F2 selectw -t:11 -bind F3 selectw -t:12 -bind F4 selectw -t:13 -bind F5 selectw -t:14 -bind F6 selectw -t:15 -bind F7 selectw -t:16 -bind F8 selectw -t:17 -bind F9 selectw -t:18 -bind F10 selectw -t:19 -bind F11 selectw -t:20 -bind F12 selectw -t:21 - -bind m setw monitor-activity - -bind y setw force-width 81 -bind u setw force-width 0 - -bind -n F1 run-shell 'mpc toggle >/dev/null 2>&1' -bind -n F2 run-shell 'mpc' -bind -n F3 run-shell 'mpc prev >/dev/null 2>&1' -bind -n F4 run-shell 'mpc next >/dev/null 2>&1' -bind -n F5 run-shell 'mpc volume -5 >/dev/null 2>&1' -bind -n F6 run-shell 'mpc volume +5 >/dev/null 2>&1' - -# Hide and show window name from status line -bind '-' setw window-status-format '#I'\; setw window-status-current-format '#I' -bind '+' setw window-status-format '#I:#W#F'\; setw window-status-current-format '#I:#W#F' - -# First session. -new -d -s0 -nirssi 'exec ssh -t natalya exec sh ~/bin/tmux-start' -setw -t0:0 monitor-activity on -setw -t0:0 aggressive-resize on -set -t0 status-bg green -neww -d -ntodo 'exec emacs ~/TODO' -setw -t0:1 aggressive-resize on -neww -d -ntodo2 'exec emacs ~/TODO2' -setw -t0:2 aggressive-resize on -neww -d -nncmpc 'exec ncmpc -f ~/.ncmpc.conf' -setw -t0:3 aggressive-resize on -neww -d -nmutt 'exec mutt' -setw -t0:4 aggressive-resize on -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d - -# Second session. -new -d -s1 -set -t1 status-bg cyan -linkw -dk -t0 -s0:0 -linkw -dk -t1 -s0:1 -linkw -dk -t2 -s0:2 -linkw -dk -t3 -s0:3 -linkw -dk -t4 -s0:4 -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d - -# Third session. -new -d -s2 -set -t2 status-bg yellow -linkw -dk -t0 -s0:0 -linkw -dk -t1 -s0:1 -linkw -dk -t2 -s0:2 -linkw -dk -t3 -s0:3 -linkw -dk -t4 -s0:4 -neww -d -neww -d -neww -d -neww -d -neww -d -neww -d diff --git a/examples/screen-keys.conf b/examples/screen-keys.conf deleted file mode 100644 index ce149290..00000000 --- a/examples/screen-keys.conf +++ /dev/null @@ -1,102 +0,0 @@ -# $Id: screen-keys.conf,v 1.7 2010-07-31 11:39:13 nicm Exp $ -# -# By Nicholas Marriott. Public domain. -# -# This configuration file binds many of the common GNU screen key bindings to -# appropriate tmux key bindings. Note that for some key bindings there is no -# tmux analogue and also that this set omits binding some commands available in -# tmux but not in screen. -# -# Note this is only a selection of key bindings and they are in addition to the -# normal tmux key bindings. This is intended as an example not as to be used -# as-is. - -# Set the prefix to ^A. -unbind C-b -set -g prefix ^A -bind a send-prefix - -# Bind appropriate commands similar to screen. -# lockscreen ^X x -unbind ^X -bind ^X lock-server -unbind x -bind x lock-server - -# screen ^C c -unbind ^C -bind ^C new-window -unbind c -bind c new-window - -# detach ^D d -unbind ^D -bind ^D detach - -# displays * -unbind * -bind * list-clients - -# next ^@ ^N sp n -unbind ^@ -bind ^@ next-window -unbind ^N -bind ^N next-window -unbind " " -bind " " next-window -unbind n -bind n next-window - -# title A -unbind A -bind A command-prompt "rename-window %%" - -# other ^A -unbind ^A -bind ^A last-window - -# prev ^H ^P p ^? -unbind ^H -bind ^H previous-window -unbind ^P -bind ^P previous-window -unbind p -bind p previous-window -unbind BSpace -bind BSpace previous-window - -# windows ^W w -unbind ^W -bind ^W list-windows -unbind w -bind w list-windows - -# quit \ -unbind '\' -bind '\' confirm-before "kill-server" - -# kill K k -unbind K -bind K confirm-before "kill-window" -unbind k -bind k confirm-before "kill-window" - -# redisplay ^L l -unbind ^L -bind ^L refresh-client -unbind l -bind l refresh-client - -# split -v | -unbind | -bind | split-window - -# :kB: focus up -unbind Tab -bind Tab select-pane -t:.+ -unbind BTab -bind BTab select-pane -t:.- - -# " windowlist -b -unbind '"' -bind '"' choose-window diff --git a/examples/t-williams.conf b/examples/t-williams.conf deleted file mode 100644 index 0a2cc3f5..00000000 --- a/examples/t-williams.conf +++ /dev/null @@ -1,104 +0,0 @@ -# $Id: t-williams.conf,v 1.1 2009-11-02 18:59:28 nicm Exp $ -# -# ~/.tmux.conf - tmux terminal multiplexer config -# Thayer Williams (http://cinderwick.ca) -# "Feel free to do whatever you like with it." - -# I typically start tmux from ~/.xinitrc with the following: -# -# urxvt -e bash -c "tmux attach -d -t mysession" & -# -# and recall it any time thereafter with xbindkeys (Mod4+s): -# -# "urxvt -e bash -c 'tmux attach -d -t mysession'" -# m:0x50 + c:39 - - -# set prefix key to ctrl+a until I have time to adapt -unbind C-b -set -g prefix C-a - -# send the prefix to client inside window (ala nested sessions) -bind-key a send-prefix - -# toggle last window like screen -bind-key C-a last-window - -# confirm before killing a window or the server -bind-key k confirm kill-window -bind-key K confirm kill-server - -# toggle statusbar -bind-key b set-option status - -# ctrl+left/right cycles thru windows -bind-key -n C-right next -bind-key -n C-left prev - -# open a man page in new window -bind / command-prompt "split-window 'exec man %%'" - -# quick view of processes -bind '~' split-window "exec htop" - -# scrollback buffer n lines -set -g history-limit 5000 - -# listen for activity on all windows -set -g bell-action any - -# on-screen time for display-panes in ms -set -g display-panes-time 2000 - -# start window indexing at one instead of zero -set -g base-index 1 - -# enable wm window titles -set -g set-titles on - -# wm window title string (uses statusbar variables) -set -g set-titles-string "tmux.#I.#W" - -# session initialization -new -s mysession mutt -neww -t 2 -neww -d -t 3 -neww -d -t 5 mocp -neww -d -t 6 rtorrent -selectw -t 1 - -# statusbar -------------------------------------------------------------- - -set -g display-time 2000 - -# default statusbar colors -set -g status-fg white -set -g status-bg default -set -g status-attr default - -# default window title colors -set-window-option -g window-status-fg cyan -set-window-option -g window-status-bg default -set-window-option -g window-status-attr dim - -# active window title colors -set-window-option -g window-status-current-fg white -set-window-option -g window-status-current-bg default -set-window-option -g window-status-current-attr bright - -# command/message line colors -set -g message-fg white -set -g message-bg black -set -g message-attr bright - -# center align the window list -set -g status-justify centre - -# show some useful stats but only when tmux is started -# outside of Xorg, otherwise dwm statusbar shows these already -set -g status-right "" -set -g status-left "" -if '[ -z "$DISPLAY" ]' 'set -g status-left "[#[fg=green] #H #[default]]"' -if '[ -z "$DISPLAY" ]' 'set -g status-right "[ #[fg=magenta]#(cat /proc/loadavg | cut -d \" \" -f 1,2,3)#[default] ][ #[fg=cyan,bright]%a %Y-%m-%d %H:%M #[default]]"' -if '[ -z "$DISPLAY" ]' 'set -g status-right-length 50' - diff --git a/examples/vim-keys.conf b/examples/vim-keys.conf deleted file mode 100644 index d587d0bf..00000000 --- a/examples/vim-keys.conf +++ /dev/null @@ -1,36 +0,0 @@ -# $Id: vim-keys.conf,v 1.2 2010-09-18 09:36:15 nicm Exp $ -# -# vim-keys.conf, v1.2 2010/09/12 -# -# By Daniel Thau. Public domain. -# -# This configuration file binds many vi- and vim-like bindings to the -# appropriate tmux key bindings. Note that for many key bindings there is no -# tmux analogue. This is intended for tmux 1.3, which handles pane selection -# differently from the previous versions - -# split windows like vim -# vim's definition of a horizontal/vertical split is reversed from tmux's -bind s split-window -v -bind v split-window -h - -# move around panes with hjkl, as one would in vim after pressing ctrl-w -bind h select-pane -L -bind j select-pane -D -bind k select-pane -U -bind l select-pane -R - -# resize panes like vim -# feel free to change the "1" to however many lines you want to resize by, only -# one at a time can be slow -bind < resize-pane -L 1 -bind > resize-pane -R 1 -bind - resize-pane -D 1 -bind + resize-pane -U 1 - -# bind : to command-prompt like vim -# this is the default in tmux already -bind : command-prompt - -# vi-style controls for copy mode -setw -g mode-keys vi diff --git a/examples/xterm-keys.vim b/examples/xterm-keys.vim deleted file mode 100644 index 5672c26a..00000000 --- a/examples/xterm-keys.vim +++ /dev/null @@ -1,49 +0,0 @@ -" tmux.vim - Set xterm input codes passed by tmux -" Author: Mark Oteiza -" License: Public domain -" Description: Simple plugin that assigns some xterm(1)-style keys to escape -" sequences passed by tmux when "xterm-keys" is set to "on". Inspired by an -" example given by Chris Johnsen at: -" https://stackoverflow.com/a/15471820 -" -" Documentation: help:xterm-modifier-keys man:tmux(1) - -if exists("g:loaded_tmux") || &cp - finish -endif -let g:loaded_tmux = 1 - -function! s:SetXtermCapabilities() - set ttymouse=sgr - - execute "set =\e[1;*A" - execute "set =\e[1;*B" - execute "set =\e[1;*C" - execute "set =\e[1;*D" - - execute "set =\e[1;*H" - execute "set =\e[1;*F" - - execute "set =\e[2;*~" - execute "set =\e[3;*~" - execute "set =\e[5;*~" - execute "set =\e[6;*~" - - execute "set =\e[1;*P" - execute "set =\e[1;*Q" - execute "set =\e[1;*R" - execute "set =\e[1;*S" - - execute "set =\e[15;*~" - execute "set =\e[17;*~" - execute "set =\e[18;*~" - execute "set =\e[19;*~" - execute "set =\e[20;*~" - execute "set =\e[21;*~" - execute "set =\e[23;*~" - execute "set =\e[24;*~" -endfunction - -if exists('$TMUX') - call s:SetXtermCapabilities() -endif From 404379049a4cb5480c2b1c19634c869e46feb220 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 29 Jan 2016 15:45:32 +0000 Subject: [PATCH 571/703] examples/ has gone, so delete some text about it. --- README | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README b/README index dbc9b173..9141f240 100644 --- a/README +++ b/README @@ -60,10 +60,7 @@ welcome. Please send by email to: tmux-users@googlegroups.com -This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under -the ISC license. Files under examples/ remain copyright their authors unless -otherwise stated in the file but permission has been received to distribute -them with tmux. All other files have a license and copyright notice at their -start. +This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under the +ISC license. All other files have a license and copyright notice at their start. -- Nicholas Marriott From 225a384dbb5ffe5fc91c59ebcaeda1946b80ca69 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 31 Jan 2016 09:52:01 +0000 Subject: [PATCH 572/703] Fix new-session with -t after command flags changes, reported by Michael Graczyk. --- cmd-new-session.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index b4bb37d5..ecbc5983 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -74,7 +74,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) struct environ *env; struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; - const char *path, *cwd, *to_free; + const char *path, *cwd, *to_free = NULL; char **argv, *cmd, *cause, *cp; int detached, already_attached, idx, argc; u_int sx, sy; @@ -118,7 +118,12 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) } } - if ((target = args_get(args, 't')) == NULL) + if ((target = args_get(args, 't')) != NULL) { + if (groupwith == NULL) { + cmdq_error(cmdq, "no such session: %s", target); + goto error; + } + } else groupwith = NULL; /* Set -d if no client. */ @@ -132,7 +137,6 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) already_attached = 1; /* Get the new session working directory. */ - to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); format_defaults(ft, c, NULL, NULL, NULL); @@ -208,7 +212,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (!args_has(args, 't') && args->argc != 0) { argc = args->argc; argv = args->argv; - } else if (target == NULL) { + } else if (groupwith == NULL) { cmd = options_get_string(global_s_options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; @@ -257,7 +261,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) * If a target session is given, this is to be part of a session group, * so add it to the group and synchronize. */ - if (args_has(args, 't')) { + if (groupwith != NULL) { session_group_add(groupwith, s); session_group_synchronize_to(s); session_select(s, RB_MIN(winlinks, &s->windows)->idx); From 8028560f8260aeea6fee1206cf8704a0a5fc25f9 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 31 Jan 2016 09:54:46 +0000 Subject: [PATCH 573/703] Support negative trim values (#{=-10:pane_title}) to trim from the end, suggested by Kevin Brubeck Unhammer. --- format.c | 12 ++++++++---- tmux.1 | 10 +++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/format.c b/format.c index efa9d1e1..78c177cd 100644 --- a/format.c +++ b/format.c @@ -684,7 +684,7 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, char *copy, *copy0, *endptr, *ptr, *found, *new, *value; char *from = NULL, *to = NULL; size_t valuelen, newlen, fromlen, tolen, used; - u_long limit = 0; + long limit = 0; int modifiers = 0, brackets; /* Make a copy of the key. */ @@ -696,8 +696,8 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, switch (copy[0]) { case '=': errno = 0; - limit = strtoul(copy + 1, &endptr, 10); - if (errno == ERANGE && limit == ULONG_MAX) + limit = strtol(copy + 1, &endptr, 10); + if (errno == ERANGE && (limit == LONG_MIN || limit == LONG_MAX)) break; if (*endptr != ':') break; @@ -813,10 +813,14 @@ format_replace(struct format_tree *ft, const char *key, size_t keylen, } /* Truncate the value if needed. */ - if (limit != 0) { + if (limit > 0) { new = utf8_trimcstr(value, limit); free(value); value = new; + } else if (limit < 0) { + new = utf8_rtrimcstr(value, -limit); + free(value); + value = new; } /* Expand the buffer and copy in the value. */ diff --git a/tmux.1 b/tmux.1 index e70b07c3..5dbf6b84 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3377,9 +3377,13 @@ if not. A limit may be placed on the length of the resultant string by prefixing it by an .Ql = , -a number and a colon, so -.Ql #{=10:pane_title} -will include at most the first 10 characters of the pane title. +a number and a colon. +Positive numbers count from the start of the string and negative from the end, +so +.Ql #{=5:pane_title} +will include at most the first 5 characters of the pane title, or +.Ql #{=-5:pane_title} +the last 5 characters. Prefixing a time variable with .Ql t: will convert it to a string, so if From 49e9f937387356a3970fdb2af6bb56818127b433 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 31 Jan 2016 09:57:09 +0000 Subject: [PATCH 574/703] Add RGB escape sequences for capture-pane -e. --- grid.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/grid.c b/grid.c index 782032f4..0be0254f 100644 --- a/grid.c +++ b/grid.c @@ -452,6 +452,12 @@ grid_string_cells_fg(const struct grid_cell *gc, int *values) values[n++] = 38; values[n++] = 5; values[n++] = gc->fg; + } else if (gc->flags & GRID_FLAG_FGRGB) { + values[n++] = 38; + values[n++] = 2; + values[n++] = gc->fg_rgb.r; + values[n++] = gc->fg_rgb.g; + values[n++] = gc->fg_rgb.b; } else { switch (gc->fg) { case 0: @@ -493,6 +499,12 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values) values[n++] = 48; values[n++] = 5; values[n++] = gc->bg; + } else if (gc->flags & GRID_FLAG_BGRGB) { + values[n++] = 48; + values[n++] = 2; + values[n++] = gc->bg_rgb.r; + values[n++] = gc->bg_rgb.g; + values[n++] = gc->bg_rgb.b; } else { switch (gc->bg) { case 0: @@ -532,7 +544,7 @@ void grid_string_cells_code(const struct grid_cell *lastgc, const struct grid_cell *gc, char *buf, size_t len, int escape_c0) { - int oldc[16], newc[16], s[32]; + int oldc[64], newc[64], s[128]; size_t noldc, nnewc, n, i; u_int attr = gc->attr; u_int lastattr = lastgc->attr; From fa64b89ad7af8daf04c50b54fe8b45411750149e Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 31 Jan 2016 09:57:41 +0000 Subject: [PATCH 575/703] Whoops, need this for the previous reverse trim commit too. --- tmux.h | 1 + utf8.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/tmux.h b/tmux.h index 8edb9209..997690fc 100644 --- a/tmux.h +++ b/tmux.h @@ -2324,6 +2324,7 @@ char *utf8_sanitize(const char *); struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); +char *utf8_rtrimcstr(const char *, u_int); char *utf8_trimcstr(const char *, u_int); char *utf8_padcstr(const char *, u_int); diff --git a/utf8.c b/utf8.c index b03c425d..3f6107a7 100644 --- a/utf8.c +++ b/utf8.c @@ -724,6 +724,43 @@ utf8_trimcstr(const char *s, u_int width) return (out); } +/* Trim UTF-8 string to width. Caller frees. */ +char * +utf8_rtrimcstr(const char *s, u_int width) +{ + struct utf8_data *tmp, *next, *end; + char *out; + u_int at; + + tmp = utf8_fromcstr(s); + + for (end = tmp; end->size != 0; end++) + /* nothing */; + if (end == tmp) { + free(tmp); + return (xstrdup("")); + } + next = end - 1; + + at = 0; + for (;;) + { + if (at + next->width > width) { + next++; + break; + } + at += next->width; + + if (next == tmp) + break; + next--; + } + + out = utf8_tocstr(next); + free(tmp); + return (out); +} + /* Pad UTF-8 string to width. Caller frees. */ char * utf8_padcstr(const char *s, u_int width) From 97882f9ce27d615769d3140ef77ddfb47b17a89b Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 31 Jan 2016 14:11:49 +0000 Subject: [PATCH 576/703] Clear RGB flags during selection. --- screen-write.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/screen-write.c b/screen-write.c index 05315b3f..e58d744c 100644 --- a/screen-write.c +++ b/screen-write.c @@ -994,7 +994,9 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) utf8_copy(&tmp_gc.data, &gc->data); tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET; tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET; - tmp_gc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); + tmp_gc.flags = gc->flags; + tmp_gc.flags &= ~(GRID_FLAG_FGRGB|GRID_FLAG_BGRGB); + tmp_gc.flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmp_gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); ttyctx.cell = &tmp_gc; From bdb8bb790ed53c0ea134c690c8f55b449eefbd3f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Feb 2016 14:11:20 +0000 Subject: [PATCH 577/703] Set up -t flag properly when passing new-session -A off to attach-session, GitHub issue 295. --- cmd-new-session.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd-new-session.c b/cmd-new-session.c index ecbc5983..291107bc 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -68,7 +68,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c = cmdq->client; - struct session *s, *attach_sess; + struct session *s, *as; struct session *groupwith = cmdq->state.tflag.s; struct window *w; struct environ *env; @@ -100,7 +100,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) cmdq_error(cmdq, "bad session name: %s", newname); return (CMD_RETURN_ERROR); } - if ((attach_sess = session_find(newname)) != NULL) { + if ((as = session_find(newname)) != NULL) { if (args_has(args, 'A')) { /* * This cmdq is now destined for @@ -108,7 +108,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) * will have already been prepared, copy this * session into its tflag so it can be used. */ - cmdq->state.tflag.s = attach_sess; + cmd_find_from_session(&cmdq->state.tflag, as); return (cmd_attach_session(cmdq, args_has(args, 'D'), 0, NULL, args_has(args, 'E'))); From 2130a07b70db7df8d57b9cad96a6866203daacad Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Feb 2016 10:08:39 +0000 Subject: [PATCH 578/703] Add to TODO. --- TODO | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index b4b8231b..18d3ae61 100644 --- a/TODO +++ b/TODO @@ -62,6 +62,19 @@ * command to toggle selection not to move it in copy-mode * regex searching * copy-pipe should have -x as well + * copy mode key bindings should just be a standard key table, using + something like "copy-mode start-selection"; it could use + command-prompt for search, goto, etc: + + bind -Temacs command-prompt -p'Search Up: ' 'copy-mode search-up %%' + + it'd need a separate lookup, because modes are per-pane, perhaps a + table() cb to give the table name ("vi" or "emacs"). anything in the + table fires the command, anything not in the table is injected as a + key + * searching in copy mode should unwrap lines, so if you seach for "foobar" + then it should be found even if it is now "foo\nbar" (if the WRAP flag + is set on the line) - layout stuff * way to tag a layout as a number/name @@ -123,13 +136,3 @@ * automatic pane logging * BCE? We are halfway there (output side is done for pane backgrounds), just need to change how screen/grid handles erase - * copy mode key bindings should just be a standard key table, using - something like "copy-mode start-selection"; it could use - command-prompt for search, goto, etc: - - bind -Temacs command-prompt -p'Search Up: ' 'copy-mode search-up %%' - - it'd need a separate lookup, because modes are per-pane, perhaps a - table() cb to give the table name ("vi" or "emacs"). anything in the - table fires the command, anything not in the table is injected as a - key From bc0c9c7920349328ed426a735b999d9dc85fe1f1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 5 Feb 2016 10:20:06 +0000 Subject: [PATCH 579/703] Do not wrap cursor at start or end of history, from Michal Mazurek. --- window-copy.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/window-copy.c b/window-copy.c index c8e38fd1..009ed246 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1775,11 +1775,13 @@ void window_copy_cursor_left(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; + u_int py; - if (data->cx == 0) { + py = screen_hsize(data->backing) + data->cy - data->oy; + if (data->cx == 0 && py > 0) { window_copy_cursor_up(wp, 0); window_copy_cursor_end_of_line(wp); - } else { + } else if (data->cx > 0) { window_copy_update_cursor(wp, data->cx - 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); @@ -1790,19 +1792,20 @@ void window_copy_cursor_right(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; - u_int px, py; + u_int px, py, yy; + py = screen_hsize(data->backing) + data->cy - data->oy; + yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; if (data->screen.sel.flag && data->rectflag) px = screen_size_x(&data->screen); else { - py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); } - if (data->cx >= px) { + if (data->cx >= px && py < yy) { window_copy_cursor_start_of_line(wp); window_copy_cursor_down(wp, 0); - } else { + } else if (data->cx < px) { window_copy_update_cursor(wp, data->cx + 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); From ba97ae1737fbbdc7d6cd4ce4f8f0b4a3d77f027e Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sat, 6 Feb 2016 19:04:21 +0000 Subject: [PATCH 580/703] EXTRA_DIST: add example_tmux.conf / xmalloc.h --- Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index d8c92aa9..8678a38e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,8 +6,8 @@ CLEANFILES = tmux.1.mdoc tmux.1.man # Distribution tarball options. EXTRA_DIST = \ - CHANGES FAQ README TODO COPYING examples compat/*.[ch] \ - array.h compat.h tmux.h osdep-*.c mdoc2man.awk tmux.1 + CHANGES FAQ README TODO COPYING example_tmux.conf compat/*.[ch] \ + array.h compat.h tmux.h osdep-*.c xmalloc.h mdoc2man.awk tmux.1 dist-hook: make clean grep "^#found_debug=" configure From f7c8f1ae291b0211734fe2c902a786033a9e0f3f Mon Sep 17 00:00:00 2001 From: Thomas Adam Date: Sun, 7 Feb 2016 00:04:46 +0000 Subject: [PATCH 581/703] xmalloc: define __bounded__ where necessary --- xmalloc.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xmalloc.h b/xmalloc.h index d331ce99..0360b0d9 100644 --- a/xmalloc.h +++ b/xmalloc.h @@ -19,6 +19,10 @@ #ifndef XMALLOC_H #define XMALLOC_H +#if !defined(__bounded__) +# define __bounded__(x, y, z) +#endif + void *xmalloc(size_t); void *xcalloc(size_t, size_t); void *xrealloc(void *, size_t); From 4f6bc0a0a90a652172bdf251d5177476f7e6bdb1 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 12 Feb 2016 12:24:52 +0000 Subject: [PATCH 582/703] Expand client formats in run-shell. --- cmd-run-shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index e857e9c9..0bd8fc59 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -93,7 +93,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) else cwd = NULL; ft = format_create(cmdq, 0); - format_defaults(ft, NULL, s, wl, wp); + format_defaults(ft, cmdq->state.c, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); From 782dd941da97802108628fb068f5462f4c78f431 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 17 Feb 2016 23:21:58 +0000 Subject: [PATCH 583/703] Fire SIGCHLD after utempter_add_record since it probably eats it. --- window.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/window.c b/window.c index a89b1081..a364948f 100644 --- a/window.c +++ b/window.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -915,6 +916,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv, #ifdef HAVE_UTEMPTER xsnprintf(s, sizeof s, "tmux(%lu).%%%u", (long) getpid(), wp->id); utempter_add_record(wp->fd, s); + kill(getpid(), SIGCHLD); #endif setblocking(wp->fd, 0); From fc864529f587fc4c913d1ad40da41eab2e512521 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 19 Feb 2016 13:11:10 +0000 Subject: [PATCH 584/703] Remove malloc_options debug bit (already gone from OpenBSD). --- tmux.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tmux.c b/tmux.c index 700687a5..ff087124 100644 --- a/tmux.c +++ b/tmux.c @@ -32,10 +32,6 @@ #include "tmux.h" -#if defined(DEBUG) && defined(__OpenBSD__) -extern char *malloc_options; -#endif - struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ @@ -194,10 +190,6 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; -#if defined(DEBUG) && defined(__OpenBSD__) - malloc_options = (char *) "AFGJPX"; -#endif - setlocale(LC_TIME, ""); tzset(); From acc1090e778090b8c5d2488220897873266dc368 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 19 Feb 2016 13:14:17 +0000 Subject: [PATCH 585/703] Use system wcwidth() instead of carrying around UTF-8 width tables. --- input-keys.c | 19 ++- tmux.c | 3 + tmux.h | 8 +- utf8.c | 452 ++++----------------------------------------------- 4 files changed, 51 insertions(+), 431 deletions(-) diff --git a/input-keys.c b/input-keys.c index 9a39ba7c..2a22b089 100644 --- a/input-keys.c +++ b/input-keys.c @@ -134,6 +134,19 @@ const struct input_key_ent input_keys[] = { { KEYC_KP_PERIOD, ".", 0 }, }; +/* Split a character into two UTF-8 bytes. */ +static size_t +input_split2(u_int c, u_char *dst) +{ + if (c > 0x7f) { + dst[0] = (c >> 6) | 0xc0; + dst[1] = (c & 0x3f) | 0x80; + return (2); + } + dst[0] = c; + return (1); +} + /* Translate a key code into an output key sequence. */ void input_key(struct window_pane *wp, key_code key, struct mouse_event *m) @@ -250,9 +263,9 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (wp->screen->mode & MODE_MOUSE_UTF8) { len = xsnprintf(buf, sizeof buf, "\033[M"); - len += utf8_split2(m->b + 32, &buf[len]); - len += utf8_split2(x + 33, &buf[len]); - len += utf8_split2(y + 33, &buf[len]); + len += input_split2(m->b + 32, &buf[len]); + len += input_split2(x + 33, &buf[len]); + len += input_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; diff --git a/tmux.c b/tmux.c index ff087124..b1285c3e 100644 --- a/tmux.c +++ b/tmux.c @@ -190,7 +190,10 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; + + setlocale(LC_CTYPE, "en_US.UTF-8"); setlocale(LC_TIME, ""); + tzset(); if (**argv == '-') diff --git a/tmux.h b/tmux.h index be00098d..7c0acc75 100644 --- a/tmux.h +++ b/tmux.h @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef HAVE_UTEMPTER #include @@ -2313,14 +2314,13 @@ void session_group_synchronize1(struct session *, struct session *); void session_renumber_windows(struct session *); /* utf8.c */ -u_int utf8_width(u_int); void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char); -u_int utf8_combine(const struct utf8_data *); -enum utf8_state utf8_split(u_int, struct utf8_data *); -u_int utf8_split2(u_int, u_char *); +u_int utf8_width(wchar_t); +wchar_t utf8_combine(const struct utf8_data *); +enum utf8_state utf8_split(wchar_t, struct utf8_data *); int utf8_strvis(char *, const char *, size_t, int); char *utf8_sanitize(const char *); struct utf8_data *utf8_fromcstr(const char *); diff --git a/utf8.c b/utf8.c index 524ba872..503546af 100644 --- a/utf8.c +++ b/utf8.c @@ -20,333 +20,10 @@ #include #include +#include #include "tmux.h" -struct utf8_width_entry { - u_int first; - u_int last; - - int width; - - struct utf8_width_entry *left; - struct utf8_width_entry *right; -}; - -/* Sorted, then repeatedly split in the middle to balance the tree. */ -static struct utf8_width_entry utf8_width_table[] = { - { 0x00b41, 0x00b44, 0, NULL, NULL }, - { 0x008e4, 0x00902, 0, NULL, NULL }, - { 0x006d6, 0x006dd, 0, NULL, NULL }, - { 0x005c4, 0x005c5, 0, NULL, NULL }, - { 0x00591, 0x005bd, 0, NULL, NULL }, - { 0x00300, 0x0036f, 0, NULL, NULL }, - { 0x00483, 0x00489, 0, NULL, NULL }, - { 0x005bf, 0x005bf, 0, NULL, NULL }, - { 0x005c1, 0x005c2, 0, NULL, NULL }, - { 0x00610, 0x0061a, 0, NULL, NULL }, - { 0x00600, 0x00605, 0, NULL, NULL }, - { 0x005c7, 0x005c7, 0, NULL, NULL }, - { 0x0064b, 0x0065f, 0, NULL, NULL }, - { 0x0061c, 0x0061c, 0, NULL, NULL }, - { 0x00670, 0x00670, 0, NULL, NULL }, - { 0x007a6, 0x007b0, 0, NULL, NULL }, - { 0x006ea, 0x006ed, 0, NULL, NULL }, - { 0x006df, 0x006e4, 0, NULL, NULL }, - { 0x006e7, 0x006e8, 0, NULL, NULL }, - { 0x00711, 0x00711, 0, NULL, NULL }, - { 0x0070f, 0x0070f, 0, NULL, NULL }, - { 0x00730, 0x0074a, 0, NULL, NULL }, - { 0x0081b, 0x00823, 0, NULL, NULL }, - { 0x007eb, 0x007f3, 0, NULL, NULL }, - { 0x00816, 0x00819, 0, NULL, NULL }, - { 0x00829, 0x0082d, 0, NULL, NULL }, - { 0x00825, 0x00827, 0, NULL, NULL }, - { 0x00859, 0x0085b, 0, NULL, NULL }, - { 0x00a41, 0x00a42, 0, NULL, NULL }, - { 0x00981, 0x00981, 0, NULL, NULL }, - { 0x00941, 0x00948, 0, NULL, NULL }, - { 0x0093a, 0x0093a, 0, NULL, NULL }, - { 0x0093c, 0x0093c, 0, NULL, NULL }, - { 0x00951, 0x00957, 0, NULL, NULL }, - { 0x0094d, 0x0094d, 0, NULL, NULL }, - { 0x00962, 0x00963, 0, NULL, NULL }, - { 0x009e2, 0x009e3, 0, NULL, NULL }, - { 0x009c1, 0x009c4, 0, NULL, NULL }, - { 0x009bc, 0x009bc, 0, NULL, NULL }, - { 0x009cd, 0x009cd, 0, NULL, NULL }, - { 0x00a01, 0x00a02, 0, NULL, NULL }, - { 0x00a3c, 0x00a3c, 0, NULL, NULL }, - { 0x00ac1, 0x00ac5, 0, NULL, NULL }, - { 0x00a70, 0x00a71, 0, NULL, NULL }, - { 0x00a4b, 0x00a4d, 0, NULL, NULL }, - { 0x00a47, 0x00a48, 0, NULL, NULL }, - { 0x00a51, 0x00a51, 0, NULL, NULL }, - { 0x00a81, 0x00a82, 0, NULL, NULL }, - { 0x00a75, 0x00a75, 0, NULL, NULL }, - { 0x00abc, 0x00abc, 0, NULL, NULL }, - { 0x00ae2, 0x00ae3, 0, NULL, NULL }, - { 0x00ac7, 0x00ac8, 0, NULL, NULL }, - { 0x00acd, 0x00acd, 0, NULL, NULL }, - { 0x00b3c, 0x00b3c, 0, NULL, NULL }, - { 0x00b01, 0x00b01, 0, NULL, NULL }, - { 0x00b3f, 0x00b3f, 0, NULL, NULL }, - { 0x03190, 0x031ba, 2, NULL, NULL }, - { 0x017c9, 0x017d3, 0, NULL, NULL }, - { 0x00ec8, 0x00ecd, 0, NULL, NULL }, - { 0x00cc6, 0x00cc6, 0, NULL, NULL }, - { 0x00c3e, 0x00c40, 0, NULL, NULL }, - { 0x00b82, 0x00b82, 0, NULL, NULL }, - { 0x00b56, 0x00b56, 0, NULL, NULL }, - { 0x00b4d, 0x00b4d, 0, NULL, NULL }, - { 0x00b62, 0x00b63, 0, NULL, NULL }, - { 0x00bcd, 0x00bcd, 0, NULL, NULL }, - { 0x00bc0, 0x00bc0, 0, NULL, NULL }, - { 0x00c00, 0x00c00, 0, NULL, NULL }, - { 0x00c62, 0x00c63, 0, NULL, NULL }, - { 0x00c4a, 0x00c4d, 0, NULL, NULL }, - { 0x00c46, 0x00c48, 0, NULL, NULL }, - { 0x00c55, 0x00c56, 0, NULL, NULL }, - { 0x00cbc, 0x00cbc, 0, NULL, NULL }, - { 0x00c81, 0x00c81, 0, NULL, NULL }, - { 0x00cbf, 0x00cbf, 0, NULL, NULL }, - { 0x00dd2, 0x00dd4, 0, NULL, NULL }, - { 0x00d41, 0x00d44, 0, NULL, NULL }, - { 0x00ce2, 0x00ce3, 0, NULL, NULL }, - { 0x00ccc, 0x00ccd, 0, NULL, NULL }, - { 0x00d01, 0x00d01, 0, NULL, NULL }, - { 0x00d62, 0x00d63, 0, NULL, NULL }, - { 0x00d4d, 0x00d4d, 0, NULL, NULL }, - { 0x00dca, 0x00dca, 0, NULL, NULL }, - { 0x00e47, 0x00e4e, 0, NULL, NULL }, - { 0x00e31, 0x00e31, 0, NULL, NULL }, - { 0x00dd6, 0x00dd6, 0, NULL, NULL }, - { 0x00e34, 0x00e3a, 0, NULL, NULL }, - { 0x00eb4, 0x00eb9, 0, NULL, NULL }, - { 0x00eb1, 0x00eb1, 0, NULL, NULL }, - { 0x00ebb, 0x00ebc, 0, NULL, NULL }, - { 0x0105e, 0x01060, 0, NULL, NULL }, - { 0x00f8d, 0x00f97, 0, NULL, NULL }, - { 0x00f39, 0x00f39, 0, NULL, NULL }, - { 0x00f35, 0x00f35, 0, NULL, NULL }, - { 0x00f18, 0x00f19, 0, NULL, NULL }, - { 0x00f37, 0x00f37, 0, NULL, NULL }, - { 0x00f80, 0x00f84, 0, NULL, NULL }, - { 0x00f71, 0x00f7e, 0, NULL, NULL }, - { 0x00f86, 0x00f87, 0, NULL, NULL }, - { 0x01032, 0x01037, 0, NULL, NULL }, - { 0x00fc6, 0x00fc6, 0, NULL, NULL }, - { 0x00f99, 0x00fbc, 0, NULL, NULL }, - { 0x0102d, 0x01030, 0, NULL, NULL }, - { 0x0103d, 0x0103e, 0, NULL, NULL }, - { 0x01039, 0x0103a, 0, NULL, NULL }, - { 0x01058, 0x01059, 0, NULL, NULL }, - { 0x0135d, 0x0135f, 0, NULL, NULL }, - { 0x01085, 0x01086, 0, NULL, NULL }, - { 0x01071, 0x01074, 0, NULL, NULL }, - { 0x01082, 0x01082, 0, NULL, NULL }, - { 0x0109d, 0x0109d, 0, NULL, NULL }, - { 0x0108d, 0x0108d, 0, NULL, NULL }, - { 0x01100, 0x011ff, 2, NULL, NULL }, - { 0x01772, 0x01773, 0, NULL, NULL }, - { 0x01732, 0x01734, 0, NULL, NULL }, - { 0x01712, 0x01714, 0, NULL, NULL }, - { 0x01752, 0x01753, 0, NULL, NULL }, - { 0x017b7, 0x017bd, 0, NULL, NULL }, - { 0x017b4, 0x017b5, 0, NULL, NULL }, - { 0x017c6, 0x017c6, 0, NULL, NULL }, - { 0x01c2c, 0x01c33, 0, NULL, NULL }, - { 0x01a7f, 0x01a7f, 0, NULL, NULL }, - { 0x01a17, 0x01a18, 0, NULL, NULL }, - { 0x01920, 0x01922, 0, NULL, NULL }, - { 0x0180b, 0x0180e, 0, NULL, NULL }, - { 0x017dd, 0x017dd, 0, NULL, NULL }, - { 0x018a9, 0x018a9, 0, NULL, NULL }, - { 0x01932, 0x01932, 0, NULL, NULL }, - { 0x01927, 0x01928, 0, NULL, NULL }, - { 0x01939, 0x0193b, 0, NULL, NULL }, - { 0x01a60, 0x01a60, 0, NULL, NULL }, - { 0x01a56, 0x01a56, 0, NULL, NULL }, - { 0x01a1b, 0x01a1b, 0, NULL, NULL }, - { 0x01a58, 0x01a5e, 0, NULL, NULL }, - { 0x01a65, 0x01a6c, 0, NULL, NULL }, - { 0x01a62, 0x01a62, 0, NULL, NULL }, - { 0x01a73, 0x01a7c, 0, NULL, NULL }, - { 0x01b80, 0x01b81, 0, NULL, NULL }, - { 0x01b36, 0x01b3a, 0, NULL, NULL }, - { 0x01b00, 0x01b03, 0, NULL, NULL }, - { 0x01ab0, 0x01abe, 0, NULL, NULL }, - { 0x01b34, 0x01b34, 0, NULL, NULL }, - { 0x01b42, 0x01b42, 0, NULL, NULL }, - { 0x01b3c, 0x01b3c, 0, NULL, NULL }, - { 0x01b6b, 0x01b73, 0, NULL, NULL }, - { 0x01be6, 0x01be6, 0, NULL, NULL }, - { 0x01ba8, 0x01ba9, 0, NULL, NULL }, - { 0x01ba2, 0x01ba5, 0, NULL, NULL }, - { 0x01bab, 0x01bad, 0, NULL, NULL }, - { 0x01bed, 0x01bed, 0, NULL, NULL }, - { 0x01be8, 0x01be9, 0, NULL, NULL }, - { 0x01bef, 0x01bf1, 0, NULL, NULL }, - { 0x02329, 0x0232a, 2, NULL, NULL }, - { 0x01dc0, 0x01df5, 0, NULL, NULL }, - { 0x01ce2, 0x01ce8, 0, NULL, NULL }, - { 0x01cd0, 0x01cd2, 0, NULL, NULL }, - { 0x01c36, 0x01c37, 0, NULL, NULL }, - { 0x01cd4, 0x01ce0, 0, NULL, NULL }, - { 0x01cf4, 0x01cf4, 0, NULL, NULL }, - { 0x01ced, 0x01ced, 0, NULL, NULL }, - { 0x01cf8, 0x01cf9, 0, NULL, NULL }, - { 0x02060, 0x02064, 0, NULL, NULL }, - { 0x0200b, 0x0200f, 0, NULL, NULL }, - { 0x01dfc, 0x01dff, 0, NULL, NULL }, - { 0x0202a, 0x0202e, 0, NULL, NULL }, - { 0x02066, 0x0206f, 0, NULL, NULL }, - { 0x020d0, 0x020f0, 0, NULL, NULL }, - { 0x03001, 0x03029, 2, NULL, NULL }, - { 0x02e80, 0x02e99, 2, NULL, NULL }, - { 0x02d7f, 0x02d7f, 0, NULL, NULL }, - { 0x02cef, 0x02cf1, 0, NULL, NULL }, - { 0x02de0, 0x02dff, 0, NULL, NULL }, - { 0x02f00, 0x02fd5, 2, NULL, NULL }, - { 0x02e9b, 0x02ef3, 2, NULL, NULL }, - { 0x02ff0, 0x02ffb, 2, NULL, NULL }, - { 0x03099, 0x0309a, 0, NULL, NULL }, - { 0x0302e, 0x0303e, 2, NULL, NULL }, - { 0x0302a, 0x0302d, 0, NULL, NULL }, - { 0x03041, 0x03096, 2, NULL, NULL }, - { 0x03105, 0x0312d, 2, NULL, NULL }, - { 0x0309b, 0x030ff, 2, NULL, NULL }, - { 0x03131, 0x0318e, 2, NULL, NULL }, - { 0x10a3f, 0x10a3f, 0, NULL, NULL }, - { 0x0aa4c, 0x0aa4c, 0, NULL, NULL }, - { 0x0a825, 0x0a826, 0, NULL, NULL }, - { 0x0a490, 0x0a4c6, 2, NULL, NULL }, - { 0x03250, 0x032fe, 2, NULL, NULL }, - { 0x031f0, 0x0321e, 2, NULL, NULL }, - { 0x031c0, 0x031e3, 2, NULL, NULL }, - { 0x03220, 0x03247, 2, NULL, NULL }, - { 0x04e00, 0x09fcc, 2, NULL, NULL }, - { 0x03300, 0x04db5, 2, NULL, NULL }, - { 0x0a000, 0x0a48c, 2, NULL, NULL }, - { 0x0a6f0, 0x0a6f1, 0, NULL, NULL }, - { 0x0a674, 0x0a67d, 0, NULL, NULL }, - { 0x0a66f, 0x0a672, 0, NULL, NULL }, - { 0x0a69f, 0x0a69f, 0, NULL, NULL }, - { 0x0a806, 0x0a806, 0, NULL, NULL }, - { 0x0a802, 0x0a802, 0, NULL, NULL }, - { 0x0a80b, 0x0a80b, 0, NULL, NULL }, - { 0x0a9b6, 0x0a9b9, 0, NULL, NULL }, - { 0x0a947, 0x0a951, 0, NULL, NULL }, - { 0x0a8e0, 0x0a8f1, 0, NULL, NULL }, - { 0x0a8c4, 0x0a8c4, 0, NULL, NULL }, - { 0x0a926, 0x0a92d, 0, NULL, NULL }, - { 0x0a980, 0x0a982, 0, NULL, NULL }, - { 0x0a960, 0x0a97c, 2, NULL, NULL }, - { 0x0a9b3, 0x0a9b3, 0, NULL, NULL }, - { 0x0aa29, 0x0aa2e, 0, NULL, NULL }, - { 0x0a9bc, 0x0a9bc, 0, NULL, NULL }, - { 0x0a9e5, 0x0a9e5, 0, NULL, NULL }, - { 0x0aa35, 0x0aa36, 0, NULL, NULL }, - { 0x0aa31, 0x0aa32, 0, NULL, NULL }, - { 0x0aa43, 0x0aa43, 0, NULL, NULL }, - { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, - { 0x0aaf6, 0x0aaf6, 0, NULL, NULL }, - { 0x0aab7, 0x0aab8, 0, NULL, NULL }, - { 0x0aab0, 0x0aab0, 0, NULL, NULL }, - { 0x0aa7c, 0x0aa7c, 0, NULL, NULL }, - { 0x0aab2, 0x0aab4, 0, NULL, NULL }, - { 0x0aac1, 0x0aac1, 0, NULL, NULL }, - { 0x0aabe, 0x0aabf, 0, NULL, NULL }, - { 0x0aaec, 0x0aaed, 0, NULL, NULL }, - { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, - { 0x0abe8, 0x0abe8, 0, NULL, NULL }, - { 0x0abe5, 0x0abe5, 0, NULL, NULL }, - { 0x0abed, 0x0abed, 0, NULL, NULL }, - { 0x0f900, 0x0fa6d, 2, NULL, NULL }, - { 0x0d800, 0x0dfff, 0, NULL, NULL }, - { 0x0fa70, 0x0fad9, 2, NULL, NULL }, - { 0x0fff9, 0x0fffb, 0, NULL, NULL }, - { 0x0fe30, 0x0fe52, 2, NULL, NULL }, - { 0x0fe10, 0x0fe19, 2, NULL, NULL }, - { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, - { 0x0fe20, 0x0fe2d, 0, NULL, NULL }, - { 0x0fe68, 0x0fe6b, 2, NULL, NULL }, - { 0x0fe54, 0x0fe66, 2, NULL, NULL }, - { 0x0feff, 0x0feff, 0, NULL, NULL }, - { 0x10a01, 0x10a03, 0, NULL, NULL }, - { 0x102e0, 0x102e0, 0, NULL, NULL }, - { 0x101fd, 0x101fd, 0, NULL, NULL }, - { 0x10376, 0x1037a, 0, NULL, NULL }, - { 0x10a0c, 0x10a0f, 0, NULL, NULL }, - { 0x10a05, 0x10a06, 0, NULL, NULL }, - { 0x10a38, 0x10a3a, 0, NULL, NULL }, - { 0x11633, 0x1163a, 0, NULL, NULL }, - { 0x11236, 0x11237, 0, NULL, NULL }, - { 0x11100, 0x11102, 0, NULL, NULL }, - { 0x1107f, 0x11081, 0, NULL, NULL }, - { 0x11001, 0x11001, 0, NULL, NULL }, - { 0x10ae5, 0x10ae6, 0, NULL, NULL }, - { 0x11038, 0x11046, 0, NULL, NULL }, - { 0x110b9, 0x110ba, 0, NULL, NULL }, - { 0x110b3, 0x110b6, 0, NULL, NULL }, - { 0x110bd, 0x110bd, 0, NULL, NULL }, - { 0x11180, 0x11181, 0, NULL, NULL }, - { 0x1112d, 0x11134, 0, NULL, NULL }, - { 0x11127, 0x1112b, 0, NULL, NULL }, - { 0x11173, 0x11173, 0, NULL, NULL }, - { 0x1122f, 0x11231, 0, NULL, NULL }, - { 0x111b6, 0x111be, 0, NULL, NULL }, - { 0x11234, 0x11234, 0, NULL, NULL }, - { 0x11370, 0x11374, 0, NULL, NULL }, - { 0x11301, 0x11301, 0, NULL, NULL }, - { 0x112df, 0x112df, 0, NULL, NULL }, - { 0x112e3, 0x112ea, 0, NULL, NULL }, - { 0x11340, 0x11340, 0, NULL, NULL }, - { 0x1133c, 0x1133c, 0, NULL, NULL }, - { 0x11366, 0x1136c, 0, NULL, NULL }, - { 0x114c2, 0x114c3, 0, NULL, NULL }, - { 0x114ba, 0x114ba, 0, NULL, NULL }, - { 0x114b3, 0x114b8, 0, NULL, NULL }, - { 0x114bf, 0x114c0, 0, NULL, NULL }, - { 0x115bc, 0x115bd, 0, NULL, NULL }, - { 0x115b2, 0x115b5, 0, NULL, NULL }, - { 0x115bf, 0x115c0, 0, NULL, NULL }, - { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, - { 0x16b30, 0x16b36, 0, NULL, NULL }, - { 0x116ad, 0x116ad, 0, NULL, NULL }, - { 0x1163f, 0x11640, 0, NULL, NULL }, - { 0x1163d, 0x1163d, 0, NULL, NULL }, - { 0x116ab, 0x116ab, 0, NULL, NULL }, - { 0x116b7, 0x116b7, 0, NULL, NULL }, - { 0x116b0, 0x116b5, 0, NULL, NULL }, - { 0x16af0, 0x16af4, 0, NULL, NULL }, - { 0x1bca0, 0x1bca3, 0, NULL, NULL }, - { 0x1b000, 0x1b001, 2, NULL, NULL }, - { 0x16f8f, 0x16f92, 0, NULL, NULL }, - { 0x1bc9d, 0x1bc9e, 0, NULL, NULL }, - { 0x1d173, 0x1d182, 0, NULL, NULL }, - { 0x1d167, 0x1d169, 0, NULL, NULL }, - { 0x1d185, 0x1d18b, 0, NULL, NULL }, - { 0x2a700, 0x2b734, 2, NULL, NULL }, - { 0x1f210, 0x1f23a, 2, NULL, NULL }, - { 0x1e8d0, 0x1e8d6, 0, NULL, NULL }, - { 0x1d242, 0x1d244, 0, NULL, NULL }, - { 0x1f200, 0x1f202, 2, NULL, NULL }, - { 0x1f250, 0x1f251, 2, NULL, NULL }, - { 0x1f240, 0x1f248, 2, NULL, NULL }, - { 0x20000, 0x2a6d6, 2, NULL, NULL }, - { 0xe0020, 0xe007f, 0, NULL, NULL }, - { 0x2f800, 0x2fa1d, 2, NULL, NULL }, - { 0x2b740, 0x2b81d, 2, NULL, NULL }, - { 0xe0001, 0xe0001, 0, NULL, NULL }, - { 0xf0000, 0xffffd, 0, NULL, NULL }, - { 0xe0100, 0xe01ef, 0, NULL, NULL }, - { 0x100000, 0x10fffd, 0, NULL, NULL }, -}; -static struct utf8_width_entry *utf8_width_root = NULL; - -static void utf8_build(void); - /* Set a single character. */ void utf8_set(struct utf8_data *ud, u_char ch) @@ -420,118 +97,45 @@ utf8_append(struct utf8_data *ud, u_char ch) return (UTF8_DONE); } -/* Build UTF-8 width tree. */ -static void -utf8_build(void) +/* Get width of Unicode character. */ +u_int +utf8_width(wchar_t wc) { - struct utf8_width_entry **ptr, *item, *node; - u_int i; + int width; - for (i = 0; i < nitems(utf8_width_table); i++) { - item = &utf8_width_table[i]; - - ptr = &utf8_width_root; - while (*ptr != NULL) { - node = *ptr; - if (item->last < node->first) - ptr = &node->left; - else if (item->first > node->last) - ptr = &node->right; - } - *ptr = item; - } + width = wcwidth(wc); + if (width < 0) + return (0); + return (width); } -/* Lookup width of UTF-8 data in tree. */ -u_int -utf8_width(u_int uc) -{ - struct utf8_width_entry *item; - - if (utf8_width_root == NULL) - utf8_build(); - - item = utf8_width_root; - while (item != NULL) { - if (uc < item->first) - item = item->left; - else if (uc > item->last) - item = item->right; - else - return (item->width); - } - return (1); -} - -/* Combine UTF-8 into 32-bit Unicode. */ -u_int +/* Combine UTF-8 into Unicode. */ +wchar_t utf8_combine(const struct utf8_data *ud) { - u_int uc; + wchar_t wc; - uc = 0xfffd; - switch (ud->size) { - case 1: - uc = ud->data[0]; - break; - case 2: - uc = ud->data[1] & 0x3f; - uc |= (ud->data[0] & 0x1f) << 6; - break; - case 3: - uc = ud->data[2] & 0x3f; - uc |= (ud->data[1] & 0x3f) << 6; - uc |= (ud->data[0] & 0xf) << 12; - break; - case 4: - uc = ud->data[3] & 0x3f; - uc |= (ud->data[2] & 0x3f) << 6; - uc |= (ud->data[1] & 0x3f) << 12; - uc |= (ud->data[0] & 0x7) << 18; - break; - } - return (uc); + if (mbtowc(&wc, ud->data, ud->size) <= 0) + return (0xfffd); + return (wc); } -/* Split 32-bit Unicode into UTF-8. */ +/* Split Unicode into UTF-8. */ enum utf8_state -utf8_split(u_int uc, struct utf8_data *ud) +utf8_split(wchar_t wc, struct utf8_data *ud) { - if (uc < 0x7f) { - ud->size = 1; - ud->data[0] = uc; - } else if (uc < 0x7ff) { - ud->size = 2; - ud->data[0] = 0xc0 | ((uc >> 6) & 0x1f); - ud->data[1] = 0x80 | (uc & 0x3f); - } else if (uc < 0xffff) { - ud->size = 3; - ud->data[0] = 0xe0 | ((uc >> 12) & 0xf); - ud->data[1] = 0x80 | ((uc >> 6) & 0x3f); - ud->data[2] = 0x80 | (uc & 0x3f); - } else if (uc < 0x1fffff) { - ud->size = 4; - ud->data[0] = 0xf0 | ((uc >> 18) & 0x7); - ud->data[1] = 0x80 | ((uc >> 12) & 0x3f); - ud->data[2] = 0x80 | ((uc >> 6) & 0x3f); - ud->data[3] = 0x80 | (uc & 0x3f); - } else - return (UTF8_ERROR); - ud->width = utf8_width(uc); - return (UTF8_DONE); -} + char s[MB_CUR_MAX]; + int slen; -/* Split a two-byte UTF-8 character. */ -u_int -utf8_split2(u_int uc, u_char *ptr) -{ - if (uc > 0x7f) { - ptr[0] = (uc >> 6) | 0xc0; - ptr[1] = (uc & 0x3f) | 0x80; - return (2); - } - ptr[0] = uc; - return (1); + slen = wctomb(s, wc); + if (slen <= 0 || slen > (int)sizeof ud->data) + return (UTF8_ERROR); + + memcpy(ud->data, s, slen); + ud->size = slen; + + ud->width = utf8_width(wc); + return (UTF8_DONE); } /* From 02753ba9ea89bbeed1a5bba4d5b3a3c6a41e775e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 19 Feb 2016 13:15:22 +0000 Subject: [PATCH 586/703] Remove unused variables, from Michal Mazurek. --- cmd-swap-pane.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index aec7753d..13575e0a 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -45,28 +45,23 @@ const struct cmd_entry cmd_swap_pane_entry = { enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - dst_wl = cmdq->state.tflag.wl; - dst_w = dst_wl->window; + dst_w = cmdq->state.tflag.wl->window; dst_wp = cmdq->state.tflag.wp; - src_wl = cmdq->state.sflag.wl; - src_w = src_wl->window; + src_w = cmdq->state.sflag.wl->window; src_wp = cmdq->state.sflag.wp; server_unzoom_window(dst_w); if (args_has(self->args, 'D')) { - src_wl = dst_wl; src_w = dst_w; src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); } else if (args_has(self->args, 'U')) { - src_wl = dst_wl; src_w = dst_w; src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) From 95adc0e6bacb32108fd6557e2e5ddeaaaa4fd58e Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 19 Feb 2016 13:28:03 +0000 Subject: [PATCH 587/703] When a mouse drag is finished, fire a MouseUp key press, instead of doing the drag end in code. From Stephen Coakley. --- mode-key.c | 2 ++ server-client.c | 40 ++++++++++++++++++++++++++++++++++++++-- window-copy.c | 15 +-------------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/mode-key.c b/mode-key.c index f38ebf66..a9b15bb9 100644 --- a/mode-key.c +++ b/mode-key.c @@ -347,6 +347,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, + { KEYC_MOUSEUP1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 0, -1, 0 } }; @@ -495,6 +496,7 @@ const struct mode_key_entry mode_key_emacs_copy[] = { { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, + { KEYC_MOUSEUP1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 0, -1, 0 } }; diff --git a/server-client.c b/server-client.c index 6e98f062..680350df 100644 --- a/server-client.c +++ b/server-client.c @@ -382,8 +382,42 @@ server_client_check_mouse(struct client *c) c->tty.mouse_drag_update = NULL; c->tty.mouse_drag_release = NULL; + /* + * End a mouse drag by passing a MouseUp key corresponding to + * the button that started the drag. + */ + switch (c->tty.mouse_drag_flag) { + case 1: + if (where == PANE) + key = KEYC_MOUSEUP1_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP1_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEUP2_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP2_BORDER; + break; + case 3: + if (where == PANE) + key = KEYC_MOUSEUP3_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP3_BORDER; + break; + default: + key = KEYC_MOUSE; + break; + } c->tty.mouse_drag_flag = 0; - return (KEYC_MOUSE); /* not a key, but still may want to pass */ + + return (key); } /* Convert to a key binding. */ @@ -423,7 +457,9 @@ server_client_check_mouse(struct client *c) } } - c->tty.mouse_drag_flag = 1; + /* Begin a drag by setting the flag to nonzero, where the value + corresponds to the mouse button doing the dragging. */ + c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1; break; case WHEEL: if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) { diff --git a/window-copy.c b/window-copy.c index 009ed246..d345f246 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2248,7 +2248,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) return; c->tty.mouse_drag_update = window_copy_drag_update; - c->tty.mouse_drag_release = window_copy_drag_release; + c->tty.mouse_drag_release = NULL; /* will fire MouseUp key */ window_copy_update_cursor(wp, x, y); window_copy_start_selection(wp); @@ -2275,16 +2275,3 @@ window_copy_drag_update(__unused struct client *c, struct mouse_event *m) if (window_copy_update_selection(wp, 1)) window_copy_redraw_selection(wp, old_cy); } - -void -window_copy_drag_release(__unused struct client *c, struct mouse_event *m) -{ - struct window_pane *wp; - - wp = cmd_mouse_pane(m, NULL, NULL); - if (wp == NULL || wp->mode != &window_copy_mode) - return; - - window_copy_copy_selection(wp, NULL); - window_pane_reset_mode(wp); -} From 6adf5615075944e87b2a988d5507cca5d9151826 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 19 Feb 2016 13:29:59 +0000 Subject: [PATCH 588/703] Redraw status on mode entry and exit. --- window.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/window.c b/window.c index a364948f..236d2436 100644 --- a/window.c +++ b/window.c @@ -1100,6 +1100,8 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) if ((s = wp->mode->init(wp)) != NULL) wp->screen = s; wp->flags |= (PANE_REDRAW|PANE_CHANGED); + + server_status_window(wp->window); return (0); } @@ -1114,6 +1116,8 @@ window_pane_reset_mode(struct window_pane *wp) wp->screen = &wp->base; wp->flags |= (PANE_REDRAW|PANE_CHANGED); + + server_status_window(wp->window); } void From e9d369a09e48ea8f940958025c8444988d31e840 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 19 Feb 2016 13:35:46 +0000 Subject: [PATCH 589/703] Fixed fgetln(3) implementation (from Joerg Jung) which does not depend on *BSD fgets(3) semantics. --- compat/fgetln.c | 110 +++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/compat/fgetln.c b/compat/fgetln.c index a5c2489d..0ad6378a 100644 --- a/compat/fgetln.c +++ b/compat/fgetln.c @@ -1,43 +1,26 @@ -/* $NetBSD: fgetln.c,v 1.3 2007/08/07 02:06:58 lukem Exp $ */ - -/*- - * Copyright (c) 1998 The NetBSD Foundation, Inc. - * All rights reserved. +/* + * Copyright (c) 2015 Joerg Jung * - * This code is derived from software contributed to The NetBSD Foundation - * by Christos Zoulas. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +/* + * portable fgetln() version, NOT reentrant + */ -#include #include #include -#include +#include #include "tmux.h" @@ -45,41 +28,34 @@ char * fgetln(FILE *fp, size_t *len) { static char *buf = NULL; - static size_t bufsiz = 0; - char *ptr; + static size_t bufsz = 0; + size_t r = 0; + char *p; + int c, e; - - if (buf == NULL) { - bufsiz = BUFSIZ; - if ((buf = malloc(bufsiz)) == NULL) - return NULL; - } - - if (fgets(buf, bufsiz, fp) == NULL) + if (!fp || !len) { + errno = EINVAL; return NULL; - - *len = 0; - while ((ptr = strchr(&buf[*len], '\n')) == NULL) { - size_t nbufsiz = bufsiz + BUFSIZ; - char *nbuf = realloc(buf, nbufsiz); - - if (nbuf == NULL) { - int oerrno = errno; - free(buf); - errno = oerrno; - buf = NULL; - return NULL; - } else - buf = nbuf; - - *len = bufsiz; - if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) - return buf; - - bufsiz = nbufsiz; } - - *len = (ptr - buf) + 1; - return buf; + if (!buf) { + if (!(buf = calloc(1, BUFSIZ))) + return NULL; + bufsz = BUFSIZ; + } + while ((c = getc(fp)) != EOF) { + buf[r++] = c; + if (r == bufsz) { + if (!(p = reallocarray(buf, 2, bufsz))) { + e = errno; + free(buf); + errno = e; + buf = NULL, bufsz = 0; + return NULL; + } + buf = p, bufsz = 2 * bufsz; + } + if (c == '\n') + break; + } + return (*len = r) ? buf : NULL; } - From c3f93e71785290d717ecf60623b99b30d4941850 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 19 Feb 2016 16:45:15 +0000 Subject: [PATCH 590/703] Add to TODO. --- TODO | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO b/TODO index 18d3ae61..4a284220 100644 --- a/TODO +++ b/TODO @@ -131,7 +131,7 @@ comes from config for new sessions and windows. likewise, panes and jobs and run-shell and lock command all start with slightly different environments - * multiline status line? + * multiline status line? separate command prompt and status line? * customizable command aliases * automatic pane logging * BCE? We are halfway there (output side is done for pane backgrounds), From a011b67f56448b38e251418f0af67ff12411a0a0 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 19 Feb 2016 16:45:35 +0000 Subject: [PATCH 591/703] Remove unused variables. --- cmd-if-shell.c | 3 +-- cmd-resize-pane.c | 1 - cmd.c | 1 - key-bindings.c | 4 ++-- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 229289cd..3e2a5251 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -73,14 +73,13 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct format_tree *ft; const char *cwd; - cwd = wp->cwd; - if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else if (s != NULL) cwd = s->cwd; else cwd = NULL; + ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 2b4f1c17..7ec65f10 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -68,7 +68,6 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - w = wl->window; if (args_has(args, 'Z')) { if (w->flags & WINDOW_ZOOMED) window_unzoom(w); diff --git a/cmd.c b/cmd.c index b0517e62..28efa0c5 100644 --- a/cmd.c +++ b/cmd.c @@ -481,7 +481,6 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, CMD_FIND_SESSION, CMD_FIND_QUIET); if (error == 0) break; - flag = CMD_WINDOW_INDEX; /* FALLTHROUGH */ case CMD_WINDOW: case CMD_WINDOW_CANFAIL: diff --git a/key-bindings.c b/key-bindings.c index a922eb18..0d13385d 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -68,12 +68,12 @@ void key_bindings_unref_table(struct key_table *table) { struct key_binding *bd; + struct key_binding *bd1; if (--table->references != 0) return; - while (!RB_EMPTY(&table->key_bindings)) { - bd = RB_ROOT(&table->key_bindings); + RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); From c7851e0ee71e26ee9af67f2523679132369b152f Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2016 11:58:45 +0000 Subject: [PATCH 592/703] Fix break-pane synopsis and some other tmux.1 bits. --- cmd-break-pane.c | 2 +- tmux.1 | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index b5a2743f..85873227 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -35,7 +35,7 @@ const struct cmd_entry cmd_break_pane_entry = { .alias = "breakp", .args = { "dPF:s:t:", 0, 0 }, - .usage = "[-dP] [-F format] " CMD_SRCDST_PANE_USAGE, + .usage = "[-dP] [-F format] [-s src-pane] [-t dst-window]", .sflag = CMD_PANE, .tflag = CMD_WINDOW_INDEX, diff --git a/tmux.1 b/tmux.1 index 5dbf6b84..448673f1 100644 --- a/tmux.1 +++ b/tmux.1 @@ -717,7 +717,7 @@ will set the session working directory (used for new windows) to .Pp If .Fl E -is used, +is used, the .Ic update-environment option will not be applied. .It Xo Ic detach-client @@ -853,13 +853,12 @@ with .Ar target-session . This means they share the same set of windows - all windows from .Ar target-session -are linked to the new session and any subsequent new windows or windows being -closed are applied to both sessions. +are linked to the new session, any new windows are linked to both sessions and +any windows closed removed from both sessions. The current and previous window and any session options remain independent and either session may be killed without affecting the other. -Giving .Fl n -or +and .Ar shell-command are invalid if .Fl t @@ -875,7 +874,7 @@ but a different format may be specified with .Pp If .Fl E -is used, +is used, the .Ic update-environment option will not be applied. .It Xo Ic refresh-client @@ -1250,7 +1249,7 @@ Commands related to windows and panes are as follows: .Op Fl dP .Op Fl F Ar format .Op Fl s Ar src-pane -.Op Fl t Ar dst-pane +.Op Fl t Ar dst-window .Xc .D1 (alias: Ic breakp ) Break From 26945d7956bf1f160fba72677082e1a9c6968e0c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2016 12:02:08 +0000 Subject: [PATCH 593/703] Use system wcwidth() instead of carrying around UTF-8 width tables. --- input-keys.c | 19 ++- tmux.c | 3 + tmux.h | 8 +- utf8.c | 452 ++++----------------------------------------------- 4 files changed, 51 insertions(+), 431 deletions(-) diff --git a/input-keys.c b/input-keys.c index 254845cb..47786d27 100644 --- a/input-keys.c +++ b/input-keys.c @@ -135,6 +135,19 @@ const struct input_key_ent input_keys[] = { { KEYC_KP_PERIOD, ".", 0 }, }; +/* Split a character into two UTF-8 bytes. */ +static size_t +input_split2(u_int c, u_char *dst) +{ + if (c > 0x7f) { + dst[0] = (c >> 6) | 0xc0; + dst[1] = (c & 0x3f) | 0x80; + return (2); + } + dst[0] = c; + return (1); +} + /* Translate a key code into an output key sequence. */ void input_key(struct window_pane *wp, key_code key, struct mouse_event *m) @@ -251,9 +264,9 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (wp->screen->mode & MODE_MOUSE_UTF8) { len = xsnprintf(buf, sizeof buf, "\033[M"); - len += utf8_split2(m->b + 32, &buf[len]); - len += utf8_split2(x + 33, &buf[len]); - len += utf8_split2(y + 33, &buf[len]); + len += input_split2(m->b + 32, &buf[len]); + len += input_split2(x + 33, &buf[len]); + len += input_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; diff --git a/tmux.c b/tmux.c index 1ee2a269..68cd4bb7 100644 --- a/tmux.c +++ b/tmux.c @@ -188,7 +188,10 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; + + setlocale(LC_CTYPE, "en_US.UTF-8"); setlocale(LC_TIME, ""); + tzset(); if (**argv == '-') diff --git a/tmux.h b/tmux.h index 997690fc..b00c7469 100644 --- a/tmux.h +++ b/tmux.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "xmalloc.h" @@ -2311,14 +2312,13 @@ void session_group_synchronize1(struct session *, struct session *); void session_renumber_windows(struct session *); /* utf8.c */ -u_int utf8_width(u_int); void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char); -u_int utf8_combine(const struct utf8_data *); -enum utf8_state utf8_split(u_int, struct utf8_data *); -u_int utf8_split2(u_int, u_char *); +u_int utf8_width(wchar_t); +wchar_t utf8_combine(const struct utf8_data *); +enum utf8_state utf8_split(wchar_t, struct utf8_data *); int utf8_strvis(char *, const char *, size_t, int); char *utf8_sanitize(const char *); struct utf8_data *utf8_fromcstr(const char *); diff --git a/utf8.c b/utf8.c index 3f6107a7..be0915ba 100644 --- a/utf8.c +++ b/utf8.c @@ -21,333 +21,10 @@ #include #include #include +#include #include "tmux.h" -struct utf8_width_entry { - u_int first; - u_int last; - - int width; - - struct utf8_width_entry *left; - struct utf8_width_entry *right; -}; - -/* Sorted, then repeatedly split in the middle to balance the tree. */ -static struct utf8_width_entry utf8_width_table[] = { - { 0x00b41, 0x00b44, 0, NULL, NULL }, - { 0x008e4, 0x00902, 0, NULL, NULL }, - { 0x006d6, 0x006dd, 0, NULL, NULL }, - { 0x005c4, 0x005c5, 0, NULL, NULL }, - { 0x00591, 0x005bd, 0, NULL, NULL }, - { 0x00300, 0x0036f, 0, NULL, NULL }, - { 0x00483, 0x00489, 0, NULL, NULL }, - { 0x005bf, 0x005bf, 0, NULL, NULL }, - { 0x005c1, 0x005c2, 0, NULL, NULL }, - { 0x00610, 0x0061a, 0, NULL, NULL }, - { 0x00600, 0x00605, 0, NULL, NULL }, - { 0x005c7, 0x005c7, 0, NULL, NULL }, - { 0x0064b, 0x0065f, 0, NULL, NULL }, - { 0x0061c, 0x0061c, 0, NULL, NULL }, - { 0x00670, 0x00670, 0, NULL, NULL }, - { 0x007a6, 0x007b0, 0, NULL, NULL }, - { 0x006ea, 0x006ed, 0, NULL, NULL }, - { 0x006df, 0x006e4, 0, NULL, NULL }, - { 0x006e7, 0x006e8, 0, NULL, NULL }, - { 0x00711, 0x00711, 0, NULL, NULL }, - { 0x0070f, 0x0070f, 0, NULL, NULL }, - { 0x00730, 0x0074a, 0, NULL, NULL }, - { 0x0081b, 0x00823, 0, NULL, NULL }, - { 0x007eb, 0x007f3, 0, NULL, NULL }, - { 0x00816, 0x00819, 0, NULL, NULL }, - { 0x00829, 0x0082d, 0, NULL, NULL }, - { 0x00825, 0x00827, 0, NULL, NULL }, - { 0x00859, 0x0085b, 0, NULL, NULL }, - { 0x00a41, 0x00a42, 0, NULL, NULL }, - { 0x00981, 0x00981, 0, NULL, NULL }, - { 0x00941, 0x00948, 0, NULL, NULL }, - { 0x0093a, 0x0093a, 0, NULL, NULL }, - { 0x0093c, 0x0093c, 0, NULL, NULL }, - { 0x00951, 0x00957, 0, NULL, NULL }, - { 0x0094d, 0x0094d, 0, NULL, NULL }, - { 0x00962, 0x00963, 0, NULL, NULL }, - { 0x009e2, 0x009e3, 0, NULL, NULL }, - { 0x009c1, 0x009c4, 0, NULL, NULL }, - { 0x009bc, 0x009bc, 0, NULL, NULL }, - { 0x009cd, 0x009cd, 0, NULL, NULL }, - { 0x00a01, 0x00a02, 0, NULL, NULL }, - { 0x00a3c, 0x00a3c, 0, NULL, NULL }, - { 0x00ac1, 0x00ac5, 0, NULL, NULL }, - { 0x00a70, 0x00a71, 0, NULL, NULL }, - { 0x00a4b, 0x00a4d, 0, NULL, NULL }, - { 0x00a47, 0x00a48, 0, NULL, NULL }, - { 0x00a51, 0x00a51, 0, NULL, NULL }, - { 0x00a81, 0x00a82, 0, NULL, NULL }, - { 0x00a75, 0x00a75, 0, NULL, NULL }, - { 0x00abc, 0x00abc, 0, NULL, NULL }, - { 0x00ae2, 0x00ae3, 0, NULL, NULL }, - { 0x00ac7, 0x00ac8, 0, NULL, NULL }, - { 0x00acd, 0x00acd, 0, NULL, NULL }, - { 0x00b3c, 0x00b3c, 0, NULL, NULL }, - { 0x00b01, 0x00b01, 0, NULL, NULL }, - { 0x00b3f, 0x00b3f, 0, NULL, NULL }, - { 0x03190, 0x031ba, 2, NULL, NULL }, - { 0x017c9, 0x017d3, 0, NULL, NULL }, - { 0x00ec8, 0x00ecd, 0, NULL, NULL }, - { 0x00cc6, 0x00cc6, 0, NULL, NULL }, - { 0x00c3e, 0x00c40, 0, NULL, NULL }, - { 0x00b82, 0x00b82, 0, NULL, NULL }, - { 0x00b56, 0x00b56, 0, NULL, NULL }, - { 0x00b4d, 0x00b4d, 0, NULL, NULL }, - { 0x00b62, 0x00b63, 0, NULL, NULL }, - { 0x00bcd, 0x00bcd, 0, NULL, NULL }, - { 0x00bc0, 0x00bc0, 0, NULL, NULL }, - { 0x00c00, 0x00c00, 0, NULL, NULL }, - { 0x00c62, 0x00c63, 0, NULL, NULL }, - { 0x00c4a, 0x00c4d, 0, NULL, NULL }, - { 0x00c46, 0x00c48, 0, NULL, NULL }, - { 0x00c55, 0x00c56, 0, NULL, NULL }, - { 0x00cbc, 0x00cbc, 0, NULL, NULL }, - { 0x00c81, 0x00c81, 0, NULL, NULL }, - { 0x00cbf, 0x00cbf, 0, NULL, NULL }, - { 0x00dd2, 0x00dd4, 0, NULL, NULL }, - { 0x00d41, 0x00d44, 0, NULL, NULL }, - { 0x00ce2, 0x00ce3, 0, NULL, NULL }, - { 0x00ccc, 0x00ccd, 0, NULL, NULL }, - { 0x00d01, 0x00d01, 0, NULL, NULL }, - { 0x00d62, 0x00d63, 0, NULL, NULL }, - { 0x00d4d, 0x00d4d, 0, NULL, NULL }, - { 0x00dca, 0x00dca, 0, NULL, NULL }, - { 0x00e47, 0x00e4e, 0, NULL, NULL }, - { 0x00e31, 0x00e31, 0, NULL, NULL }, - { 0x00dd6, 0x00dd6, 0, NULL, NULL }, - { 0x00e34, 0x00e3a, 0, NULL, NULL }, - { 0x00eb4, 0x00eb9, 0, NULL, NULL }, - { 0x00eb1, 0x00eb1, 0, NULL, NULL }, - { 0x00ebb, 0x00ebc, 0, NULL, NULL }, - { 0x0105e, 0x01060, 0, NULL, NULL }, - { 0x00f8d, 0x00f97, 0, NULL, NULL }, - { 0x00f39, 0x00f39, 0, NULL, NULL }, - { 0x00f35, 0x00f35, 0, NULL, NULL }, - { 0x00f18, 0x00f19, 0, NULL, NULL }, - { 0x00f37, 0x00f37, 0, NULL, NULL }, - { 0x00f80, 0x00f84, 0, NULL, NULL }, - { 0x00f71, 0x00f7e, 0, NULL, NULL }, - { 0x00f86, 0x00f87, 0, NULL, NULL }, - { 0x01032, 0x01037, 0, NULL, NULL }, - { 0x00fc6, 0x00fc6, 0, NULL, NULL }, - { 0x00f99, 0x00fbc, 0, NULL, NULL }, - { 0x0102d, 0x01030, 0, NULL, NULL }, - { 0x0103d, 0x0103e, 0, NULL, NULL }, - { 0x01039, 0x0103a, 0, NULL, NULL }, - { 0x01058, 0x01059, 0, NULL, NULL }, - { 0x0135d, 0x0135f, 0, NULL, NULL }, - { 0x01085, 0x01086, 0, NULL, NULL }, - { 0x01071, 0x01074, 0, NULL, NULL }, - { 0x01082, 0x01082, 0, NULL, NULL }, - { 0x0109d, 0x0109d, 0, NULL, NULL }, - { 0x0108d, 0x0108d, 0, NULL, NULL }, - { 0x01100, 0x011ff, 2, NULL, NULL }, - { 0x01772, 0x01773, 0, NULL, NULL }, - { 0x01732, 0x01734, 0, NULL, NULL }, - { 0x01712, 0x01714, 0, NULL, NULL }, - { 0x01752, 0x01753, 0, NULL, NULL }, - { 0x017b7, 0x017bd, 0, NULL, NULL }, - { 0x017b4, 0x017b5, 0, NULL, NULL }, - { 0x017c6, 0x017c6, 0, NULL, NULL }, - { 0x01c2c, 0x01c33, 0, NULL, NULL }, - { 0x01a7f, 0x01a7f, 0, NULL, NULL }, - { 0x01a17, 0x01a18, 0, NULL, NULL }, - { 0x01920, 0x01922, 0, NULL, NULL }, - { 0x0180b, 0x0180e, 0, NULL, NULL }, - { 0x017dd, 0x017dd, 0, NULL, NULL }, - { 0x018a9, 0x018a9, 0, NULL, NULL }, - { 0x01932, 0x01932, 0, NULL, NULL }, - { 0x01927, 0x01928, 0, NULL, NULL }, - { 0x01939, 0x0193b, 0, NULL, NULL }, - { 0x01a60, 0x01a60, 0, NULL, NULL }, - { 0x01a56, 0x01a56, 0, NULL, NULL }, - { 0x01a1b, 0x01a1b, 0, NULL, NULL }, - { 0x01a58, 0x01a5e, 0, NULL, NULL }, - { 0x01a65, 0x01a6c, 0, NULL, NULL }, - { 0x01a62, 0x01a62, 0, NULL, NULL }, - { 0x01a73, 0x01a7c, 0, NULL, NULL }, - { 0x01b80, 0x01b81, 0, NULL, NULL }, - { 0x01b36, 0x01b3a, 0, NULL, NULL }, - { 0x01b00, 0x01b03, 0, NULL, NULL }, - { 0x01ab0, 0x01abe, 0, NULL, NULL }, - { 0x01b34, 0x01b34, 0, NULL, NULL }, - { 0x01b42, 0x01b42, 0, NULL, NULL }, - { 0x01b3c, 0x01b3c, 0, NULL, NULL }, - { 0x01b6b, 0x01b73, 0, NULL, NULL }, - { 0x01be6, 0x01be6, 0, NULL, NULL }, - { 0x01ba8, 0x01ba9, 0, NULL, NULL }, - { 0x01ba2, 0x01ba5, 0, NULL, NULL }, - { 0x01bab, 0x01bad, 0, NULL, NULL }, - { 0x01bed, 0x01bed, 0, NULL, NULL }, - { 0x01be8, 0x01be9, 0, NULL, NULL }, - { 0x01bef, 0x01bf1, 0, NULL, NULL }, - { 0x02329, 0x0232a, 2, NULL, NULL }, - { 0x01dc0, 0x01df5, 0, NULL, NULL }, - { 0x01ce2, 0x01ce8, 0, NULL, NULL }, - { 0x01cd0, 0x01cd2, 0, NULL, NULL }, - { 0x01c36, 0x01c37, 0, NULL, NULL }, - { 0x01cd4, 0x01ce0, 0, NULL, NULL }, - { 0x01cf4, 0x01cf4, 0, NULL, NULL }, - { 0x01ced, 0x01ced, 0, NULL, NULL }, - { 0x01cf8, 0x01cf9, 0, NULL, NULL }, - { 0x02060, 0x02064, 0, NULL, NULL }, - { 0x0200b, 0x0200f, 0, NULL, NULL }, - { 0x01dfc, 0x01dff, 0, NULL, NULL }, - { 0x0202a, 0x0202e, 0, NULL, NULL }, - { 0x02066, 0x0206f, 0, NULL, NULL }, - { 0x020d0, 0x020f0, 0, NULL, NULL }, - { 0x03001, 0x03029, 2, NULL, NULL }, - { 0x02e80, 0x02e99, 2, NULL, NULL }, - { 0x02d7f, 0x02d7f, 0, NULL, NULL }, - { 0x02cef, 0x02cf1, 0, NULL, NULL }, - { 0x02de0, 0x02dff, 0, NULL, NULL }, - { 0x02f00, 0x02fd5, 2, NULL, NULL }, - { 0x02e9b, 0x02ef3, 2, NULL, NULL }, - { 0x02ff0, 0x02ffb, 2, NULL, NULL }, - { 0x03099, 0x0309a, 0, NULL, NULL }, - { 0x0302e, 0x0303e, 2, NULL, NULL }, - { 0x0302a, 0x0302d, 0, NULL, NULL }, - { 0x03041, 0x03096, 2, NULL, NULL }, - { 0x03105, 0x0312d, 2, NULL, NULL }, - { 0x0309b, 0x030ff, 2, NULL, NULL }, - { 0x03131, 0x0318e, 2, NULL, NULL }, - { 0x10a3f, 0x10a3f, 0, NULL, NULL }, - { 0x0aa4c, 0x0aa4c, 0, NULL, NULL }, - { 0x0a825, 0x0a826, 0, NULL, NULL }, - { 0x0a490, 0x0a4c6, 2, NULL, NULL }, - { 0x03250, 0x032fe, 2, NULL, NULL }, - { 0x031f0, 0x0321e, 2, NULL, NULL }, - { 0x031c0, 0x031e3, 2, NULL, NULL }, - { 0x03220, 0x03247, 2, NULL, NULL }, - { 0x04e00, 0x09fcc, 2, NULL, NULL }, - { 0x03300, 0x04db5, 2, NULL, NULL }, - { 0x0a000, 0x0a48c, 2, NULL, NULL }, - { 0x0a6f0, 0x0a6f1, 0, NULL, NULL }, - { 0x0a674, 0x0a67d, 0, NULL, NULL }, - { 0x0a66f, 0x0a672, 0, NULL, NULL }, - { 0x0a69f, 0x0a69f, 0, NULL, NULL }, - { 0x0a806, 0x0a806, 0, NULL, NULL }, - { 0x0a802, 0x0a802, 0, NULL, NULL }, - { 0x0a80b, 0x0a80b, 0, NULL, NULL }, - { 0x0a9b6, 0x0a9b9, 0, NULL, NULL }, - { 0x0a947, 0x0a951, 0, NULL, NULL }, - { 0x0a8e0, 0x0a8f1, 0, NULL, NULL }, - { 0x0a8c4, 0x0a8c4, 0, NULL, NULL }, - { 0x0a926, 0x0a92d, 0, NULL, NULL }, - { 0x0a980, 0x0a982, 0, NULL, NULL }, - { 0x0a960, 0x0a97c, 2, NULL, NULL }, - { 0x0a9b3, 0x0a9b3, 0, NULL, NULL }, - { 0x0aa29, 0x0aa2e, 0, NULL, NULL }, - { 0x0a9bc, 0x0a9bc, 0, NULL, NULL }, - { 0x0a9e5, 0x0a9e5, 0, NULL, NULL }, - { 0x0aa35, 0x0aa36, 0, NULL, NULL }, - { 0x0aa31, 0x0aa32, 0, NULL, NULL }, - { 0x0aa43, 0x0aa43, 0, NULL, NULL }, - { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, - { 0x0aaf6, 0x0aaf6, 0, NULL, NULL }, - { 0x0aab7, 0x0aab8, 0, NULL, NULL }, - { 0x0aab0, 0x0aab0, 0, NULL, NULL }, - { 0x0aa7c, 0x0aa7c, 0, NULL, NULL }, - { 0x0aab2, 0x0aab4, 0, NULL, NULL }, - { 0x0aac1, 0x0aac1, 0, NULL, NULL }, - { 0x0aabe, 0x0aabf, 0, NULL, NULL }, - { 0x0aaec, 0x0aaed, 0, NULL, NULL }, - { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, - { 0x0abe8, 0x0abe8, 0, NULL, NULL }, - { 0x0abe5, 0x0abe5, 0, NULL, NULL }, - { 0x0abed, 0x0abed, 0, NULL, NULL }, - { 0x0f900, 0x0fa6d, 2, NULL, NULL }, - { 0x0d800, 0x0dfff, 0, NULL, NULL }, - { 0x0fa70, 0x0fad9, 2, NULL, NULL }, - { 0x0fff9, 0x0fffb, 0, NULL, NULL }, - { 0x0fe30, 0x0fe52, 2, NULL, NULL }, - { 0x0fe10, 0x0fe19, 2, NULL, NULL }, - { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, - { 0x0fe20, 0x0fe2d, 0, NULL, NULL }, - { 0x0fe68, 0x0fe6b, 2, NULL, NULL }, - { 0x0fe54, 0x0fe66, 2, NULL, NULL }, - { 0x0feff, 0x0feff, 0, NULL, NULL }, - { 0x10a01, 0x10a03, 0, NULL, NULL }, - { 0x102e0, 0x102e0, 0, NULL, NULL }, - { 0x101fd, 0x101fd, 0, NULL, NULL }, - { 0x10376, 0x1037a, 0, NULL, NULL }, - { 0x10a0c, 0x10a0f, 0, NULL, NULL }, - { 0x10a05, 0x10a06, 0, NULL, NULL }, - { 0x10a38, 0x10a3a, 0, NULL, NULL }, - { 0x11633, 0x1163a, 0, NULL, NULL }, - { 0x11236, 0x11237, 0, NULL, NULL }, - { 0x11100, 0x11102, 0, NULL, NULL }, - { 0x1107f, 0x11081, 0, NULL, NULL }, - { 0x11001, 0x11001, 0, NULL, NULL }, - { 0x10ae5, 0x10ae6, 0, NULL, NULL }, - { 0x11038, 0x11046, 0, NULL, NULL }, - { 0x110b9, 0x110ba, 0, NULL, NULL }, - { 0x110b3, 0x110b6, 0, NULL, NULL }, - { 0x110bd, 0x110bd, 0, NULL, NULL }, - { 0x11180, 0x11181, 0, NULL, NULL }, - { 0x1112d, 0x11134, 0, NULL, NULL }, - { 0x11127, 0x1112b, 0, NULL, NULL }, - { 0x11173, 0x11173, 0, NULL, NULL }, - { 0x1122f, 0x11231, 0, NULL, NULL }, - { 0x111b6, 0x111be, 0, NULL, NULL }, - { 0x11234, 0x11234, 0, NULL, NULL }, - { 0x11370, 0x11374, 0, NULL, NULL }, - { 0x11301, 0x11301, 0, NULL, NULL }, - { 0x112df, 0x112df, 0, NULL, NULL }, - { 0x112e3, 0x112ea, 0, NULL, NULL }, - { 0x11340, 0x11340, 0, NULL, NULL }, - { 0x1133c, 0x1133c, 0, NULL, NULL }, - { 0x11366, 0x1136c, 0, NULL, NULL }, - { 0x114c2, 0x114c3, 0, NULL, NULL }, - { 0x114ba, 0x114ba, 0, NULL, NULL }, - { 0x114b3, 0x114b8, 0, NULL, NULL }, - { 0x114bf, 0x114c0, 0, NULL, NULL }, - { 0x115bc, 0x115bd, 0, NULL, NULL }, - { 0x115b2, 0x115b5, 0, NULL, NULL }, - { 0x115bf, 0x115c0, 0, NULL, NULL }, - { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, - { 0x16b30, 0x16b36, 0, NULL, NULL }, - { 0x116ad, 0x116ad, 0, NULL, NULL }, - { 0x1163f, 0x11640, 0, NULL, NULL }, - { 0x1163d, 0x1163d, 0, NULL, NULL }, - { 0x116ab, 0x116ab, 0, NULL, NULL }, - { 0x116b7, 0x116b7, 0, NULL, NULL }, - { 0x116b0, 0x116b5, 0, NULL, NULL }, - { 0x16af0, 0x16af4, 0, NULL, NULL }, - { 0x1bca0, 0x1bca3, 0, NULL, NULL }, - { 0x1b000, 0x1b001, 2, NULL, NULL }, - { 0x16f8f, 0x16f92, 0, NULL, NULL }, - { 0x1bc9d, 0x1bc9e, 0, NULL, NULL }, - { 0x1d173, 0x1d182, 0, NULL, NULL }, - { 0x1d167, 0x1d169, 0, NULL, NULL }, - { 0x1d185, 0x1d18b, 0, NULL, NULL }, - { 0x2a700, 0x2b734, 2, NULL, NULL }, - { 0x1f210, 0x1f23a, 2, NULL, NULL }, - { 0x1e8d0, 0x1e8d6, 0, NULL, NULL }, - { 0x1d242, 0x1d244, 0, NULL, NULL }, - { 0x1f200, 0x1f202, 2, NULL, NULL }, - { 0x1f250, 0x1f251, 2, NULL, NULL }, - { 0x1f240, 0x1f248, 2, NULL, NULL }, - { 0x20000, 0x2a6d6, 2, NULL, NULL }, - { 0xe0020, 0xe007f, 0, NULL, NULL }, - { 0x2f800, 0x2fa1d, 2, NULL, NULL }, - { 0x2b740, 0x2b81d, 2, NULL, NULL }, - { 0xe0001, 0xe0001, 0, NULL, NULL }, - { 0xf0000, 0xffffd, 0, NULL, NULL }, - { 0xe0100, 0xe01ef, 0, NULL, NULL }, - { 0x100000, 0x10fffd, 0, NULL, NULL }, -}; -static struct utf8_width_entry *utf8_width_root = NULL; - -static void utf8_build(void); - /* Set a single character. */ void utf8_set(struct utf8_data *ud, u_char ch) @@ -421,118 +98,45 @@ utf8_append(struct utf8_data *ud, u_char ch) return (UTF8_DONE); } -/* Build UTF-8 width tree. */ -static void -utf8_build(void) +/* Get width of Unicode character. */ +u_int +utf8_width(wchar_t wc) { - struct utf8_width_entry **ptr, *item, *node; - u_int i; + int width; - for (i = 0; i < nitems(utf8_width_table); i++) { - item = &utf8_width_table[i]; - - ptr = &utf8_width_root; - while (*ptr != NULL) { - node = *ptr; - if (item->last < node->first) - ptr = &node->left; - else if (item->first > node->last) - ptr = &node->right; - } - *ptr = item; - } + width = wcwidth(wc); + if (width < 0) + return (0); + return (width); } -/* Lookup width of UTF-8 data in tree. */ -u_int -utf8_width(u_int uc) -{ - struct utf8_width_entry *item; - - if (utf8_width_root == NULL) - utf8_build(); - - item = utf8_width_root; - while (item != NULL) { - if (uc < item->first) - item = item->left; - else if (uc > item->last) - item = item->right; - else - return (item->width); - } - return (1); -} - -/* Combine UTF-8 into 32-bit Unicode. */ -u_int +/* Combine UTF-8 into Unicode. */ +wchar_t utf8_combine(const struct utf8_data *ud) { - u_int uc; + wchar_t wc; - uc = 0xfffd; - switch (ud->size) { - case 1: - uc = ud->data[0]; - break; - case 2: - uc = ud->data[1] & 0x3f; - uc |= (ud->data[0] & 0x1f) << 6; - break; - case 3: - uc = ud->data[2] & 0x3f; - uc |= (ud->data[1] & 0x3f) << 6; - uc |= (ud->data[0] & 0xf) << 12; - break; - case 4: - uc = ud->data[3] & 0x3f; - uc |= (ud->data[2] & 0x3f) << 6; - uc |= (ud->data[1] & 0x3f) << 12; - uc |= (ud->data[0] & 0x7) << 18; - break; - } - return (uc); + if (mbtowc(&wc, ud->data, ud->size) <= 0) + return (0xfffd); + return (wc); } -/* Split 32-bit Unicode into UTF-8. */ +/* Split Unicode into UTF-8. */ enum utf8_state -utf8_split(u_int uc, struct utf8_data *ud) +utf8_split(wchar_t wc, struct utf8_data *ud) { - if (uc < 0x7f) { - ud->size = 1; - ud->data[0] = uc; - } else if (uc < 0x7ff) { - ud->size = 2; - ud->data[0] = 0xc0 | ((uc >> 6) & 0x1f); - ud->data[1] = 0x80 | (uc & 0x3f); - } else if (uc < 0xffff) { - ud->size = 3; - ud->data[0] = 0xe0 | ((uc >> 12) & 0xf); - ud->data[1] = 0x80 | ((uc >> 6) & 0x3f); - ud->data[2] = 0x80 | (uc & 0x3f); - } else if (uc < 0x1fffff) { - ud->size = 4; - ud->data[0] = 0xf0 | ((uc >> 18) & 0x7); - ud->data[1] = 0x80 | ((uc >> 12) & 0x3f); - ud->data[2] = 0x80 | ((uc >> 6) & 0x3f); - ud->data[3] = 0x80 | (uc & 0x3f); - } else - return (UTF8_ERROR); - ud->width = utf8_width(uc); - return (UTF8_DONE); -} + char s[MB_CUR_MAX]; + int slen; -/* Split a two-byte UTF-8 character. */ -u_int -utf8_split2(u_int uc, u_char *ptr) -{ - if (uc > 0x7f) { - ptr[0] = (uc >> 6) | 0xc0; - ptr[1] = (uc & 0x3f) | 0x80; - return (2); - } - ptr[0] = uc; - return (1); + slen = wctomb(s, wc); + if (slen <= 0 || slen > (int)sizeof ud->data) + return (UTF8_ERROR); + + memcpy(ud->data, s, slen); + ud->size = slen; + + ud->width = utf8_width(wc); + return (UTF8_DONE); } /* From e647eeb0c92cb5cf9134f77c30dce49f27224304 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2016 12:02:54 +0000 Subject: [PATCH 594/703] Remove unused variables, from Michal Mazurek. --- cmd-swap-pane.c | 9 ++------- tmux.c | 1 - 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index aec7753d..13575e0a 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -45,28 +45,23 @@ const struct cmd_entry cmd_swap_pane_entry = { enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { - struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; - dst_wl = cmdq->state.tflag.wl; - dst_w = dst_wl->window; + dst_w = cmdq->state.tflag.wl->window; dst_wp = cmdq->state.tflag.wp; - src_wl = cmdq->state.sflag.wl; - src_w = src_wl->window; + src_w = cmdq->state.sflag.wl->window; src_wp = cmdq->state.sflag.wp; server_unzoom_window(dst_w); if (args_has(self->args, 'D')) { - src_wl = dst_wl; src_w = dst_w; src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); } else if (args_has(self->args, 'U')) { - src_wl = dst_wl; src_w = dst_w; src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) diff --git a/tmux.c b/tmux.c index 68cd4bb7..93503d2d 100644 --- a/tmux.c +++ b/tmux.c @@ -188,7 +188,6 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; - setlocale(LC_CTYPE, "en_US.UTF-8"); setlocale(LC_TIME, ""); From 54ea8f74ae4ae3bff6df3f09ce8a8cdd148e51e5 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2016 12:04:43 +0000 Subject: [PATCH 595/703] When a mouse drag is finished, fire a MouseUp key press, instead of doing the drag end in code. From Stephen Coakley. --- mode-key.c | 2 ++ server-client.c | 42 ++++++++++++++++++++++++++++++++++++++++-- window-copy.c | 15 +-------------- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/mode-key.c b/mode-key.c index f38ebf66..a9b15bb9 100644 --- a/mode-key.c +++ b/mode-key.c @@ -347,6 +347,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, + { KEYC_MOUSEUP1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 0, -1, 0 } }; @@ -495,6 +496,7 @@ const struct mode_key_entry mode_key_emacs_copy[] = { { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, + { KEYC_MOUSEUP1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 0, -1, 0 } }; diff --git a/server-client.c b/server-client.c index bb54643a..9111eb82 100644 --- a/server-client.c +++ b/server-client.c @@ -384,8 +384,42 @@ server_client_check_mouse(struct client *c) c->tty.mouse_drag_update = NULL; c->tty.mouse_drag_release = NULL; + /* + * End a mouse drag by passing a MouseUp key corresponding to + * the button that started the drag. + */ + switch (c->tty.mouse_drag_flag) { + case 1: + if (where == PANE) + key = KEYC_MOUSEUP1_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP1_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP1_BORDER; + break; + case 2: + if (where == PANE) + key = KEYC_MOUSEUP2_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP2_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP2_BORDER; + break; + case 3: + if (where == PANE) + key = KEYC_MOUSEUP3_PANE; + if (where == STATUS) + key = KEYC_MOUSEUP3_STATUS; + if (where == BORDER) + key = KEYC_MOUSEUP3_BORDER; + break; + default: + key = KEYC_MOUSE; + break; + } c->tty.mouse_drag_flag = 0; - return (KEYC_MOUSE); /* not a key, but still may want to pass */ + + return (key); } /* Convert to a key binding. */ @@ -425,7 +459,11 @@ server_client_check_mouse(struct client *c) } } - c->tty.mouse_drag_flag = 1; + /* + * Begin a drag by setting the flag to a non-zero value that + * corresponds to the mouse button in use. + */ + c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1; break; case WHEEL: if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) { diff --git a/window-copy.c b/window-copy.c index 009ed246..d345f246 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2248,7 +2248,7 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) return; c->tty.mouse_drag_update = window_copy_drag_update; - c->tty.mouse_drag_release = window_copy_drag_release; + c->tty.mouse_drag_release = NULL; /* will fire MouseUp key */ window_copy_update_cursor(wp, x, y); window_copy_start_selection(wp); @@ -2275,16 +2275,3 @@ window_copy_drag_update(__unused struct client *c, struct mouse_event *m) if (window_copy_update_selection(wp, 1)) window_copy_redraw_selection(wp, old_cy); } - -void -window_copy_drag_release(__unused struct client *c, struct mouse_event *m) -{ - struct window_pane *wp; - - wp = cmd_mouse_pane(m, NULL, NULL); - if (wp == NULL || wp->mode != &window_copy_mode) - return; - - window_copy_copy_selection(wp, NULL); - window_pane_reset_mode(wp); -} From 2e4503ad4eec5422e6425480681a261258f7e940 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2016 12:05:15 +0000 Subject: [PATCH 596/703] Redraw status on mode entry and exit. --- window.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/window.c b/window.c index 201942a3..1769552f 100644 --- a/window.c +++ b/window.c @@ -1085,6 +1085,8 @@ window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) if ((s = wp->mode->init(wp)) != NULL) wp->screen = s; wp->flags |= (PANE_REDRAW|PANE_CHANGED); + + server_status_window(wp->window); return (0); } @@ -1099,6 +1101,8 @@ window_pane_reset_mode(struct window_pane *wp) wp->screen = &wp->base; wp->flags |= (PANE_REDRAW|PANE_CHANGED); + + server_status_window(wp->window); } void From f0239a8fe97367ce7ab66d18716c411b424a0e63 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 1 Mar 2016 12:06:07 +0000 Subject: [PATCH 597/703] Remove some more unused variables, and use RB_FOREACH_SAFE in key_bindings_unref_table. --- cmd-if-shell.c | 3 +-- cmd-resize-pane.c | 1 - cmd.c | 1 - key-bindings.c | 4 ++-- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/cmd-if-shell.c b/cmd-if-shell.c index 229289cd..3e2a5251 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -73,14 +73,13 @@ cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) struct format_tree *ft; const char *cwd; - cwd = wp->cwd; - if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else if (s != NULL) cwd = s->cwd; else cwd = NULL; + ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 2b4f1c17..7ec65f10 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -68,7 +68,6 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - w = wl->window; if (args_has(args, 'Z')) { if (w->flags & WINDOW_ZOOMED) window_unzoom(w); diff --git a/cmd.c b/cmd.c index 005f2a0f..c643a346 100644 --- a/cmd.c +++ b/cmd.c @@ -482,7 +482,6 @@ cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, CMD_FIND_SESSION, CMD_FIND_QUIET); if (error == 0) break; - flag = CMD_WINDOW_INDEX; /* FALLTHROUGH */ case CMD_WINDOW: case CMD_WINDOW_CANFAIL: diff --git a/key-bindings.c b/key-bindings.c index a922eb18..0d13385d 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -68,12 +68,12 @@ void key_bindings_unref_table(struct key_table *table) { struct key_binding *bd; + struct key_binding *bd1; if (--table->references != 0) return; - while (!RB_EMPTY(&table->key_bindings)) { - bd = RB_ROOT(&table->key_bindings); + RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); From d980d965ddb6165bd801351892fed2497204a279 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Mar 2016 15:33:36 +0000 Subject: [PATCH 598/703] Limit x, y and b to 0x7ff for UTF-8 mouse input, suggested by schwarze@. --- input-keys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input-keys.c b/input-keys.c index 47786d27..41bd5ab5 100644 --- a/input-keys.c +++ b/input-keys.c @@ -263,6 +263,8 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (wp->screen->mode & MODE_MOUSE_UTF8) { + if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) + return; len = xsnprintf(buf, sizeof buf, "\033[M"); len += input_split2(m->b + 32, &buf[len]); len += input_split2(x + 33, &buf[len]); From b8a102d26f41e57b94359627a4df8f22af10c6fa Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 2 Mar 2016 15:36:02 +0000 Subject: [PATCH 599/703] Handle wcwidth() and mbtowc() failures in better style and drop characters where we can't find the width (wcwidth() fails) on input, the same as we drop invalid UTF-8. Suggested by schwarze@. --- input.c | 10 ++++++++-- key-string.c | 6 ++++-- tmux.h | 3 +-- tty-keys.c | 7 ++++++- utf8.c | 43 +++++++++++++++++++++++++++++-------------- 5 files changed, 48 insertions(+), 21 deletions(-) diff --git a/input.c b/input.c index ae205024..18c8eb9a 100644 --- a/input.c +++ b/input.c @@ -1960,8 +1960,14 @@ input_utf8_close(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; - if (utf8_append(ud, ictx->ch) != UTF8_DONE) - fatalx("UTF-8 close invalid %#x", ictx->ch); + if (utf8_append(ud, ictx->ch) != UTF8_DONE) { + /* + * An error here could be invalid UTF-8 or it could be a + * nonprintable character for which we can't get the + * width. Drop it. + */ + return (0); + } log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); diff --git a/key-string.c b/key-string.c index dc211696..c56681f1 100644 --- a/key-string.c +++ b/key-string.c @@ -149,6 +149,7 @@ key_string_lookup_string(const char *string) struct utf8_data ud; u_int i; enum utf8_state more; + wchar_t wc; /* Is this no key? */ if (strcasecmp(string, "None") == 0) @@ -185,8 +186,9 @@ key_string_lookup_string(const char *string) more = utf8_append(&ud, (u_char)string[i]); if (more != UTF8_DONE) return (KEYC_UNKNOWN); - key = utf8_combine(&ud); - return (key | modifiers); + if (utf8_combine(&ud, &wc) != UTF8_DONE) + return (KEYC_UNKNOWN); + return (wc | modifiers); } /* Otherwise look the key up in the table. */ diff --git a/tmux.h b/tmux.h index b00c7469..ac94d780 100644 --- a/tmux.h +++ b/tmux.h @@ -2316,8 +2316,7 @@ void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char); -u_int utf8_width(wchar_t); -wchar_t utf8_combine(const struct utf8_data *); +enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *); enum utf8_state utf8_split(wchar_t, struct utf8_data *); int utf8_strvis(char *, const char *, size_t, int); char *utf8_sanitize(const char *); diff --git a/tty-keys.c b/tty-keys.c index 2b998778..105f99f7 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -477,6 +477,7 @@ tty_keys_next(struct tty *tty) struct utf8_data ud; enum utf8_state more; u_int i; + wchar_t wc; /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); @@ -552,7 +553,11 @@ first_key: more = utf8_append(&ud, (u_char)buf[i]); if (more != UTF8_DONE) goto discard_key; - key = utf8_combine(&ud); + + if (utf8_combine(&ud, &wc) != UTF8_DONE) + goto discard_key; + key = wc; + log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); goto complete_key; } diff --git a/utf8.c b/utf8.c index be0915ba..114f2b90 100644 --- a/utf8.c +++ b/utf8.c @@ -25,6 +25,8 @@ #include "tmux.h" +static int utf8_width(wchar_t); + /* Set a single character. */ void utf8_set(struct utf8_data *ud, u_char ch) @@ -80,6 +82,9 @@ utf8_open(struct utf8_data *ud, u_char ch) enum utf8_state utf8_append(struct utf8_data *ud, u_char ch) { + wchar_t wc; + int width; + if (ud->have >= ud->size) fatalx("UTF-8 character overflow"); if (ud->size > sizeof ud->data) @@ -94,39 +99,49 @@ utf8_append(struct utf8_data *ud, u_char ch) if (ud->width == 0xff) return (UTF8_ERROR); - ud->width = utf8_width(utf8_combine(ud)); + + if (utf8_combine(ud, &wc) != UTF8_DONE) + return (UTF8_ERROR); + if ((width = utf8_width(wc)) < 0) + return (UTF8_ERROR); + ud->width = width; + return (UTF8_DONE); } /* Get width of Unicode character. */ -u_int +static int utf8_width(wchar_t wc) { - int width; + int width; width = wcwidth(wc); - if (width < 0) - return (0); + if (width < 0 || width > 0xff) + return (-1); return (width); } /* Combine UTF-8 into Unicode. */ -wchar_t -utf8_combine(const struct utf8_data *ud) +enum utf8_state +utf8_combine(const struct utf8_data *ud, wchar_t *wc) { - wchar_t wc; - - if (mbtowc(&wc, ud->data, ud->size) <= 0) - return (0xfffd); - return (wc); + switch (mbtowc(wc, ud->data, ud->size)) { + case -1: + mbtowc(NULL, NULL, MB_CUR_MAX); + return (UTF8_ERROR); + case 0: + return (UTF8_ERROR); + default: + return (UTF8_DONE); + } } /* Split Unicode into UTF-8. */ enum utf8_state utf8_split(wchar_t wc, struct utf8_data *ud) { - char s[MB_CUR_MAX]; - int slen; + char s[MB_LEN_MAX]; + int slen; slen = wctomb(s, wc); if (slen <= 0 || slen > (int)sizeof ud->data) From 9e2fbb31ec34c38d7e3acd42895f11b1a83bcd19 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 2 Mar 2016 18:19:13 +0000 Subject: [PATCH 600/703] +wchar.h --- utf8.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utf8.c b/utf8.c index 6d80266b..c0407576 100644 --- a/utf8.c +++ b/utf8.c @@ -20,6 +20,7 @@ #include #include +#include #include "tmux.h" From bcb41a09b3d3f0c61d2e98c0b91fcea52f745efb Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Mar 2016 12:58:15 +0000 Subject: [PATCH 601/703] RGB colours shouldn't be mixed up with aixterm colours, return before that happens when working out if they are supported. --- tty.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tty.c b/tty.c index c6fc2213..2dff5700 100644 --- a/tty.c +++ b/tty.c @@ -1569,6 +1569,8 @@ tty_check_fg(struct tty *tty, struct grid_cell *gc) gc->flags |= GRID_FLAG_FG256; gc->fg = colour_find_rgb(rgb->r, rgb->g, rgb->b); } + else + return; } colours = tty_term_number(tty->term, TTYC_COLORS); @@ -1612,6 +1614,8 @@ tty_check_bg(struct tty *tty, struct grid_cell *gc) gc->flags |= GRID_FLAG_BG256; gc->bg = colour_find_rgb(rgb->r, rgb->g, rgb->b); } + else + return; } colours = tty_term_number(tty->term, TTYC_COLORS); From fa81d838dacb2dd05d4556db3cbcb3760b7d2c47 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Mar 2016 14:14:46 +0000 Subject: [PATCH 602/703] Accept clients as sessions in cmd_find_get_session. --- cmd-find.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd-find.c b/cmd-find.c index 7ffc5f15..22511201 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -401,6 +401,7 @@ int cmd_find_get_session(struct cmd_find_state *fs, const char *session) { struct session *s, *s_loop; + struct client *c; log_debug("%s: %s", __func__, session); @@ -417,6 +418,13 @@ cmd_find_get_session(struct cmd_find_state *fs, const char *session) if (fs->s != NULL) return (0); + /* Look for as a client. */ + c = cmd_find_client(NULL, session, 1); + if (c != NULL && c->session != NULL) { + fs->s = c->session; + return (0); + } + /* Stop now if exact only. */ if (fs->flags & CMD_FIND_EXACT_SESSION) return (-1); @@ -1209,7 +1217,7 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) const char *path; /* A NULL argument means the current client. */ - if (target == NULL) { + if (cmdq != NULL && target == NULL) { c = cmd_find_current_client(cmdq); if (c == NULL && !quiet) cmdq_error(cmdq, "no current client"); From df0983af39922f2ee747a244c1c718ba7ca28910 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 3 Mar 2016 14:15:22 +0000 Subject: [PATCH 603/703] show-* and set-* need to handle a missing target. --- cmd-set-environment.c | 15 ++++++++++++--- cmd-set-option.c | 38 +++++++++++++++++++------------------- cmd-show-environment.c | 21 +++++++++++++++++++-- cmd-show-options.c | 31 +++++++++++++++++++++++-------- 4 files changed, 73 insertions(+), 32 deletions(-) diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 55bdaa9a..ba295ea6 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -47,7 +47,7 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct environ *env; - const char *name, *value; + const char *name, *value, *target; name = args->argv[0]; if (*name == '\0') { @@ -64,10 +64,19 @@ cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) else value = args->argv[1]; - if (args_has(self->args, 'g') || cmdq->state.tflag.s == NULL) + if (args_has(self->args, 'g')) env = global_environ; - else + else { + if (cmdq->state.tflag.s == NULL) { + target = args_get(args, 't'); + if (target != NULL) + cmdq_error(cmdq, "no such session: %s", target); + else + cmdq_error(cmdq, "no current session"); + return (CMD_RETURN_ERROR); + } env = cmdq->state.tflag.s->environ; + } if (args_has(self->args, 'u')) { if (value != NULL) { diff --git a/cmd-set-option.c b/cmd-set-option.c index 7fc81286..b1771436 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -100,7 +100,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) struct client *c; const struct options_table_entry *oe; struct options *oo; - const char *optstr, *valstr; + const char *optstr, *valstr, *target; /* Get the option name and value. */ optstr = args->argv[0]; @@ -140,29 +140,29 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) else if (oe->scope == OPTIONS_TABLE_WINDOW) { if (args_has(self->args, 'g')) oo = global_w_options; - else { - if (wl == NULL) { - cmdq_error(cmdq, - "couldn't set '%s'%s", optstr, - (!args_has(args, 't') && !args_has(args, - 'g')) ? " need target window or -g" : ""); - return (CMD_RETURN_ERROR); - } + else if (wl == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(cmdq, "no such window: %s", + target); + } else + cmdq_error(cmdq, "no current window"); + return (CMD_RETURN_ERROR); + } else oo = wl->window->options; - } } else if (oe->scope == OPTIONS_TABLE_SESSION) { if (args_has(self->args, 'g')) oo = global_s_options; - else { - if (s == NULL) { - cmdq_error(cmdq, - "couldn't set '%s'%s", optstr, - (!args_has(args, 't') && !args_has(args, - 'g')) ? " need target session or -g" : ""); - return (CMD_RETURN_ERROR); - } + else if (s == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(cmdq, "no such session: %s", + target); + } else + cmdq_error(cmdq, "no current session"); + return (CMD_RETURN_ERROR); + } else oo = s->options; - } } else { cmdq_error(cmdq, "unknown table"); return (CMD_RETURN_ERROR); diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 83661c44..29e89274 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -93,11 +93,28 @@ cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) struct args *args = self->args; struct environ *env; struct environ_entry *envent; + const char *target; - if (args_has(self->args, 'g') || cmdq->state.tflag.s == NULL) + if ((target = args_get(args, 't')) != NULL) { + if (cmdq->state.tflag.s == NULL) { + cmdq_error(cmdq, "no such session: %s", target); + return (CMD_RETURN_ERROR); + } + } + + if (args_has(self->args, 'g')) env = global_environ; - else + else { + if (cmdq->state.tflag.s == NULL) { + target = args_get(args, 't'); + if (target != NULL) + cmdq_error(cmdq, "no such session: %s", target); + else + cmdq_error(cmdq, "no current session"); + return (CMD_RETURN_ERROR); + } env = cmdq->state.tflag.s->environ; + } if (args->argc != 0) { envent = environ_find(env, args->argv[0]); diff --git a/cmd-show-options.c b/cmd-show-options.c index fec2f1de..322f532c 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -63,12 +63,13 @@ const struct cmd_entry cmd_show_window_options_entry = { enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) { - struct args *args = self->args; - struct session *s = cmdq->state.tflag.s; - struct winlink *wl = cmdq->state.tflag.wl; - struct options *oo; - enum options_table_scope scope; - int quiet; + struct args *args = self->args; + struct session *s = cmdq->state.tflag.s; + struct winlink *wl = cmdq->state.tflag.wl; + struct options *oo; + enum options_table_scope scope; + int quiet; + const char *target; if (args_has(self->args, 's')) { oo = global_options; @@ -78,13 +79,27 @@ cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) scope = OPTIONS_TABLE_WINDOW; if (args_has(self->args, 'g')) oo = global_w_options; - else + else if (wl == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(cmdq, "no such window: %s", target); + } else + cmdq_error(cmdq, "no current window"); + return (CMD_RETURN_ERROR); + } else oo = wl->window->options; } else { scope = OPTIONS_TABLE_SESSION; if (args_has(self->args, 'g')) oo = global_s_options; - else + else if (s == NULL) { + target = args_get(args, 't'); + if (target != NULL) { + cmdq_error(cmdq, "no such session: %s", target); + } else + cmdq_error(cmdq, "no current session"); + return (CMD_RETURN_ERROR); + } else oo = s->options; } From 1f0b317088aaeb230d69f13f43ed63b7406c6fd1 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 5 Mar 2016 07:44:31 +0000 Subject: [PATCH 604/703] Although we always have en_US.UTF-8 on OpenBSD, some platforms do not, so fall back to setlocale(LC_CTYPE, ""). tmux requires a UTF-8 locale, so check with wcwidth() on a UTF-8 character after setlocale(). --- tmux.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tmux.c b/tmux.c index 93503d2d..d221c78e 100644 --- a/tmux.c +++ b/tmux.c @@ -188,9 +188,12 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; - setlocale(LC_CTYPE, "en_US.UTF-8"); - setlocale(LC_TIME, ""); + if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) + setlocale(LC_CTYPE, ""); + if (wcwidth(0xfffd) != 1) + errx(1, "no UTF-8 locale; please set LC_CTYPE"); + setlocale(LC_TIME, ""); tzset(); if (**argv == '-') From c38e0a4bbc722865f934db1282ca6f086874f530 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 5 Mar 2016 07:47:52 +0000 Subject: [PATCH 605/703] Do not use c->cwd or s->cwd if it is NULL, found by Ben Boeckel. --- cmd-load-buffer.c | 4 ++-- cmd-new-session.c | 2 +- cmd-save-buffer.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 6fd2a767..de76b855 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -73,9 +73,9 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_WAIT); } - if (c != NULL && c->session == NULL) + if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; - else if ((s = c->session) != NULL) + else if ((s = c->session) != NULL && s->cwd != NULL) cwd = s->cwd; else cwd = "."; diff --git a/cmd-new-session.c b/cmd-new-session.c index 291107bc..357ffed1 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -142,7 +142,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) format_defaults(ft, c, NULL, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); - } else if (c != NULL && c->session == NULL) + } else if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; else cwd = "."; diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index 591390b5..3aaf8159 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -98,9 +98,9 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) goto do_print; } - if (c != NULL && c->session == NULL) + if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; - else if ((s = c->session) != NULL) + else if ((s = c->session) != NULL && s->cwd != NULL) cwd = s->cwd; else cwd = "."; From 0d6de44a37755f0e5046c04e19e4506a6d59e750 Mon Sep 17 00:00:00 2001 From: nicm Date: Sat, 5 Mar 2016 16:08:38 +0000 Subject: [PATCH 606/703] If setlocale("en_US.UTF-8") succeeds, then don't do the check for UTF-8 locale since if it isn't UTF-8 the system is broken anyway. If it fails, try "" and check for UTF-8 with nl_langinfo(CODESET) rather than wcwidth(). Based on a diff from schwarze@, nl_langinfo also suggested by stsp@. --- tmux.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tmux.c b/tmux.c index d221c78e..f8654e2b 100644 --- a/tmux.c +++ b/tmux.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -188,10 +189,14 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; - if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) - setlocale(LC_CTYPE, ""); - if (wcwidth(0xfffd) != 1) - errx(1, "no UTF-8 locale; please set LC_CTYPE"); + if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { + if (setlocale(LC_CTYPE, "") == NULL) + errx(1, "invalid LC_ALL, LC_CTYPE or LANG"); + s = nl_langinfo(CODESET); + if (strcasecmp(s, "UTF-8") != 0 && + strcasecmp(s, "UTF8") != 0) + errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s); + } setlocale(LC_TIME, ""); tzset(); From 0d4aaa6defafee65659b4991050d2633d89c2163 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 11 Mar 2016 13:28:49 -0500 Subject: [PATCH 607/703] Fix compile warning --- tmate-ssh-client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index cd3a71b0..898344f1 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -173,9 +173,11 @@ static void init_conn_fd(struct tmate_ssh_client *client) if (ssh_get_fd(client->session) < 0) return; + { int flag = 1; setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + } event_set(&client->ev_ssh, ssh_get_fd(client->session), EV_READ | EV_PERSIST, __on_ssh_client_event, client); From 9742aeaf9b44028a54c8e09aa63622c4ec5b7cfa Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 11 Mar 2016 14:11:28 -0500 Subject: [PATCH 608/703] Send a ready packet when initialization is done --- server.c | 4 ++++ tmate-encoder.c | 6 ++++++ tmate-protocol.h | 2 ++ tmate.h | 3 ++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/server.c b/server.c index 603dbb46..7461d533 100644 --- a/server.c +++ b/server.c @@ -205,6 +205,10 @@ server_start(struct event_base *base, int lockfd, char *lockfile) status_prompt_load_history(); +#ifdef TMATE + tmate_write_ready(); +#endif + server_add_accept(0); proc_loop(server_proc, server_loop); diff --git a/tmate-encoder.c b/tmate-encoder.c index ff6924da..b6a5384c 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -12,6 +12,12 @@ void tmate_write_header(void) pack(string, VERSION); } +void tmate_write_ready(void) +{ + pack(array, 1); + pack(int, TMATE_OUT_READY); +} + void tmate_sync_layout(void) { struct session *s; diff --git a/tmate-protocol.h b/tmate-protocol.h index 45beaec6..3f3a3614 100644 --- a/tmate-protocol.h +++ b/tmate-protocol.h @@ -49,6 +49,7 @@ enum tmate_daemon_out_msg_types { TMATE_OUT_SYNC_COPY_MODE, TMATE_OUT_WRITE_COPY_MODE, TMATE_OUT_FIN, + TMATE_OUT_READY, }; /* @@ -66,6 +67,7 @@ enum tmate_daemon_out_msg_types { // Any of the array can be [] [TMATE_OUT_WRITE_COPY_MODE, int: pane_id, string: str] [TMATE_OUT_FIN] +[TMATE_OUT_READY] */ enum tmate_daemon_in_msg_types { diff --git a/tmate.h b/tmate.h index 98b109c4..11236360 100644 --- a/tmate.h +++ b/tmate.h @@ -73,9 +73,10 @@ extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *neste /* tmate-encoder.c */ -#define TMATE_PROTOCOL_VERSION 5 +#define TMATE_PROTOCOL_VERSION 6 extern void tmate_write_header(void); +extern void tmate_write_ready(void); extern void tmate_sync_layout(void); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); From cc20e826e0b5552d16cb8d463b60a2691783cd3b Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 11 Mar 2016 14:11:48 -0500 Subject: [PATCH 609/703] Add webhook options --- options-table.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/options-table.c b/options-table.c index ad061a20..eb87d8a9 100644 --- a/options-table.c +++ b/options-table.c @@ -942,6 +942,17 @@ const struct options_table_entry options_table[] = { .default_num = 30000 }, + { .name = "tmate-webhook-userdata", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-webhook-url", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, #endif { .name = NULL } From 3dfc79fb092273b65fd8a24174007d13804d3641 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 17 Mar 2016 15:11:40 +0000 Subject: [PATCH 610/703] Tweak a comment. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 8678a38e..718cfc77 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,7 +54,7 @@ if IS_SUNCC CFLAGS += -erroff=E_EMPTY_DECLARATION endif -# Set _LINUX_SOURCE_COMPAT for AIX for mallocing 0 bytes +# Set _LINUX_SOURCE_COMPAT for AIX for malloc(0). if IS_AIX DEFS += -D_LINUX_SOURCE_COMPAT=1 endif From fa97b0a95b804fec86b03d35d16c270d2866ebd6 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 18 Mar 2016 07:28:27 +0000 Subject: [PATCH 611/703] Instead of reusing MouseUp at the finish of a drag, add a new key MouseDragEnd. It can be useful to bind them separately in copy mode. --- key-string.c | 3 +++ mode-key.c | 4 ++-- server-client.c | 22 +++++++++++----------- tmux.1 | 8 ++++---- tmux.h | 3 +++ 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/key-string.c b/key-string.c index c56681f1..119035a0 100644 --- a/key-string.c +++ b/key-string.c @@ -93,6 +93,9 @@ const struct { KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), + KEYC_MOUSE_STRING(MOUSEDRAGEND1, MouseDragEnd1), + KEYC_MOUSE_STRING(MOUSEDRAGEND2, MouseDragEnd2), + KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), KEYC_MOUSE_STRING(WHEELUP, WheelUp), KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), }; diff --git a/mode-key.c b/mode-key.c index a9b15bb9..aed161bb 100644 --- a/mode-key.c +++ b/mode-key.c @@ -347,7 +347,7 @@ const struct mode_key_entry mode_key_vi_copy[] = { { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, - { KEYC_MOUSEUP1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, + { KEYC_MOUSEDRAGEND1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 0, -1, 0 } }; @@ -496,7 +496,7 @@ const struct mode_key_entry mode_key_emacs_copy[] = { { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, - { KEYC_MOUSEUP1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, + { KEYC_MOUSEDRAGEND1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 0, -1, 0 } }; diff --git a/server-client.c b/server-client.c index 9111eb82..6166eac4 100644 --- a/server-client.c +++ b/server-client.c @@ -385,33 +385,33 @@ server_client_check_mouse(struct client *c) c->tty.mouse_drag_release = NULL; /* - * End a mouse drag by passing a MouseUp key corresponding to - * the button that started the drag. + * End a mouse drag by passing a MouseDragEnd key corresponding + * to the button that started the drag. */ switch (c->tty.mouse_drag_flag) { case 1: if (where == PANE) - key = KEYC_MOUSEUP1_PANE; + key = KEYC_MOUSEDRAGEND1_PANE; if (where == STATUS) - key = KEYC_MOUSEUP1_STATUS; + key = KEYC_MOUSEDRAGEND1_STATUS; if (where == BORDER) - key = KEYC_MOUSEUP1_BORDER; + key = KEYC_MOUSEDRAGEND1_BORDER; break; case 2: if (where == PANE) - key = KEYC_MOUSEUP2_PANE; + key = KEYC_MOUSEDRAGEND2_PANE; if (where == STATUS) - key = KEYC_MOUSEUP2_STATUS; + key = KEYC_MOUSEDRAGEND2_STATUS; if (where == BORDER) - key = KEYC_MOUSEUP2_BORDER; + key = KEYC_MOUSEDRAGEND2_BORDER; break; case 3: if (where == PANE) - key = KEYC_MOUSEUP3_PANE; + key = KEYC_MOUSEDRAGEND3_PANE; if (where == STATUS) - key = KEYC_MOUSEUP3_STATUS; + key = KEYC_MOUSEDRAGEND3_STATUS; if (where == BORDER) - key = KEYC_MOUSEUP3_BORDER; + key = KEYC_MOUSEDRAGEND3_BORDER; break; default: key = KEYC_MOUSE; diff --git a/tmux.1 b/tmux.1 index 448673f1..a304e76b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3294,10 +3294,10 @@ for a pane border or for the status line). The following mouse events are available: .Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent -.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" -.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" -.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" -.It Li "WheelUp" Ta "WheelDown" Ta "" +.It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" +.It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" +.It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" +.It Li "WheelUp" Ta "WheelDown" Ta "" Ta "" .El .Pp Each should be suffixed with a location, for example diff --git a/tmux.h b/tmux.h index ac94d780..b2445fce 100644 --- a/tmux.h +++ b/tmux.h @@ -135,6 +135,9 @@ enum { KEYC_MOUSE_KEY(MOUSEDRAG1), KEYC_MOUSE_KEY(MOUSEDRAG2), KEYC_MOUSE_KEY(MOUSEDRAG3), + KEYC_MOUSE_KEY(MOUSEDRAGEND1), + KEYC_MOUSE_KEY(MOUSEDRAGEND2), + KEYC_MOUSE_KEY(MOUSEDRAGEND3), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), From 312a7a1e629431a71930fc6523f0feb6c595afda Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 18 Mar 2016 14:27:24 +0000 Subject: [PATCH 612/703] Make scrolling behaviour more sensible and maintain cursor position, as if the same had been done line-by-line. From Michal Mazurek. --- window-copy.c | 85 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/window-copy.c b/window-copy.c index d345f246..5c907c3d 100644 --- a/window-copy.c +++ b/window-copy.c @@ -26,6 +26,7 @@ struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); +void window_copy_pagedown(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); void window_copy_key(struct window_pane *, struct client *, struct session *, key_code, struct mouse_event *); @@ -324,15 +325,80 @@ window_copy_pageup(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; - u_int n; + u_int n, ox, oy; + + oy = screen_hsize(data->backing) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + + if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) + window_copy_other_end(wp); + + if (data->cx != ox) { + data->lastcx = data->cx; + data->lastsx = ox; + } + data->cx = data->lastcx; n = 1; if (screen_size_y(s) > 2) n = screen_size_y(s) - 2; + if (data->oy + n > screen_hsize(data->backing)) data->oy = screen_hsize(data->backing); else data->oy += n; + + if (!data->screen.sel.flag || !data->rectflag) { + u_int py = screen_hsize(data->backing) + data->cy - data->oy; + u_int px = window_copy_find_length(wp, py); + if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) + window_copy_cursor_end_of_line(wp); + } + + window_copy_update_selection(wp, 1); + window_copy_redraw_screen(wp); +} + +void +window_copy_pagedown(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int n, ox, oy; + + oy = screen_hsize(data->backing) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + + if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely) + window_copy_other_end(wp); + + if (data->cx != ox) { + data->lastcx = data->cx; + data->lastsx = ox; + } + data->cx = data->lastcx; + + n = 1; + if (screen_size_y(s) > 2) + n = screen_size_y(s) - 2; + + if (data->oy < n) + data->oy = 0; + else + data->oy -= n; + + if (!data->screen.sel.flag || !data->rectflag) { + u_int py = screen_hsize(data->backing) + data->cy - data->oy; + u_int px = window_copy_find_length(wp, py); + if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) + window_copy_cursor_end_of_line(wp); + } + + if (data->scroll_exit && data->oy == 0) { + window_pane_reset_mode(wp); + return; + } + window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } @@ -479,21 +545,8 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, window_copy_pageup(wp); break; case MODEKEYCOPY_NEXTPAGE: - n = 1; - if (screen_size_y(s) > 2) - n = screen_size_y(s) - 2; - for (; np != 0; np--) { - if (data->oy < n) - data->oy = 0; - else - data->oy -= n; - } - if (data->scroll_exit && data->oy == 0) { - window_pane_reset_mode(wp); - return; - } - window_copy_update_selection(wp, 1); - window_copy_redraw_screen(wp); + for (; np != 0; np--) + window_copy_pagedown(wp); break; case MODEKEYCOPY_HALFPAGEUP: n = screen_size_y(s) / 2; From b429a00cce4c150cf8050545f903ecb304691ab9 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 20 Mar 2016 08:14:14 +0000 Subject: [PATCH 613/703] Add to TODO. --- TODO | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO b/TODO index 4a284220..e0e3de6d 100644 --- a/TODO +++ b/TODO @@ -75,6 +75,7 @@ * searching in copy mode should unwrap lines, so if you seach for "foobar" then it should be found even if it is now "foo\nbar" (if the WRAP flag is set on the line) + * {} to go to next/previous blank line in copy mode - layout stuff * way to tag a layout as a number/name From 5658b628b9bf1c1e0bd5856736332ce8b9c51517 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sat, 26 Mar 2016 20:17:17 +0000 Subject: [PATCH 614/703] Look for utempter_add_record to be sure we have the new utempter API, the old utempter API was also using utempter.h. --- configure.ac | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 82e8a452..8ce20632 100644 --- a/configure.ac +++ b/configure.ac @@ -167,10 +167,17 @@ if test "x$found_curses" = xno; then fi # Look for utempter. -AC_CHECK_HEADER(utempter.h, have_utempter=yes, have_utempter=no) -if test "x$have_utempter" = xyes; then - AC_DEFINE(HAVE_UTEMPTER) - LIBS="$LIBS -lutempter" +AC_CHECK_HEADER(utempter.h, found_utempter=yes, found_utempter=no) +if test "x$found_utempter" = xyes; then + AC_SEARCH_LIBS( + utempter_add_record, + utempter, + found_utempter=yes, + found_utempter=no + ) + if test "x$found_utempter" = xyes; then + AC_DEFINE(HAVE_UTEMPTER) + fi fi # Check for b64_ntop. From cdfb6d7ef1122dbb64621c90a4cba88ab5517e53 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 26 Mar 2016 19:00:16 -0400 Subject: [PATCH 615/703] Reconnect wip --- tmate-decoder.c | 14 +++++- tmate-encoder.c | 123 +++++++++++++++++++++++++++++++++++++++++++++ tmate-msgpack.c | 17 ++++++- tmate-protocol.h | 4 ++ tmate-session.c | 46 ++++++++++++++++- tmate-ssh-client.c | 117 ++++++++++++++++++------------------------ tmate.h | 12 ++++- 7 files changed, 259 insertions(+), 74 deletions(-) diff --git a/tmate-decoder.c b/tmate-decoder.c index bc5141f5..9669c4c0 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -114,19 +114,29 @@ out: free(cmd_str); } -static void handle_set_env(__unused struct tmate_session *session, +static void maybe_save_reconnection_data(struct tmate_session *session, + const char *name, const char *value) +{ + if (!strcmp(name, "tmate_reconnection_data")) { + free(session->reconnection_data); + session->reconnection_data = xstrdup(value); + } +} + +static void handle_set_env(struct tmate_session *session, struct tmate_unpacker *uk) { char *name = unpack_string(uk); char *value = unpack_string(uk); tmate_set_env(name, value); + maybe_save_reconnection_data(session, name, value); free(name); free(value); } -static void handle_ready(__unused struct tmate_session *session, +static void handle_ready(struct tmate_session *session, __unused struct tmate_unpacker *uk) { session->tmate_env_ready = 1; diff --git a/tmate-encoder.c b/tmate-encoder.c index b6a5384c..4155237b 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -236,3 +236,126 @@ void tmate_write_fin(void) pack(array, 1); pack(int, TMATE_OUT_FIN); } + +static void do_snapshot(unsigned int max_history_lines, + struct window_pane *pane) +{ + struct screen *screen; + struct grid *grid; + struct grid_line *line; + struct grid_cell gc; + unsigned int line_i, i; + unsigned int max_lines; + size_t str_len; + + screen = &pane->base; + grid = screen->grid; + + pack(array, 4); + pack(int, pane->id); + + pack(array, 2); + pack(int, screen->cx); + pack(int, screen->cy); + + pack(unsigned_int, screen->mode); + + max_lines = max_history_lines + grid->sy; + +#define grid_num_lines(grid) (grid->hsize + grid->sy) + + if (grid_num_lines(grid) > max_lines) + line_i = grid_num_lines(grid) - max_lines; + else + line_i = 0; + + pack(array, grid_num_lines(grid) - line_i); + for (; line_i < grid_num_lines(grid); line_i++) { + line = &grid->linedata[line_i]; + + pack(array, 2); + str_len = 0; + for (i = 0; i < line->cellsize; i++) { + grid_get_cell(grid, i, line_i, &gc); + str_len += gc.data.size; + } + + pack(str, str_len); + for (i = 0; i < line->cellsize; i++) { + grid_get_cell(grid, i, line_i, &gc); + pack(str_body, gc.data.data, gc.data.size); + } + + pack(array, line->cellsize); + for (i = 0; i < line->cellsize; i++) { + grid_get_cell(grid, i, line_i, &gc); + pack(unsigned_int, ((gc.flags << 24) | + (gc.attr << 16) | + (gc.bg << 8) | + gc.fg )); + } + } +} + +static void tmate_send_session_snapshot(unsigned int max_history_lines) +{ + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *pane; + int num_panes; + + pack(array, 2); + pack(int, TMATE_OUT_SNAPSHOT); + + s = RB_MIN(sessions, &sessions); + if (!s) + tmate_fatal("no session?"); + + num_panes = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + if (!w) + continue; + + TAILQ_FOREACH(pane, &w->panes, entry) + num_panes++; + } + + pack(array, num_panes); + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + if (!w) + continue; + + TAILQ_FOREACH(pane, &w->panes, entry) + do_snapshot(max_history_lines, pane); + } +} + +static void tmate_send_reconnection_data(struct tmate_session *session) +{ + if (!session->reconnection_data) + return; + + pack(array, 2); + pack(int, TMATE_OUT_RECONNECT); + pack(string, session->reconnection_data); +} + +#define RECONNECTION_MAX_HISTORY_LINE 300 + +void tmate_send_reconnection_state(struct tmate_session *session) +{ + /* Start with a fresh encoder */ + tmate_encoder_destroy(&session->encoder); + tmate_encoder_init(&session->encoder, NULL, session); + + tmate_write_header(); + tmate_send_reconnection_data(session); + /* TODO send all option variables */ + tmate_write_ready(); + + tmate_sync_layout(); + tmate_send_session_snapshot(RECONNECTION_MAX_HISTORY_LINE); +} diff --git a/tmate-msgpack.c b/tmate-msgpack.c index dbf14148..b7948ef9 100644 --- a/tmate-msgpack.c +++ b/tmate-msgpack.c @@ -65,13 +65,22 @@ void tmate_encoder_init(struct tmate_encoder *encoder, encoder->ev_active = false; } +void tmate_encoder_destroy(struct tmate_encoder *encoder) +{ + /* encoder->pk doesn't need any cleanup */ + evbuffer_free(encoder->buffer); + event_del(&encoder->ev_buffer); + memset(encoder, 0, sizeof(*encoder)); +} + void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, tmate_encoder_write_cb *callback, void *userdata) { encoder->ready_callback = callback; encoder->userdata = userdata; - encoder->ready_callback(encoder->userdata, encoder->buffer); + if (encoder->ready_callback) + encoder->ready_callback(encoder->userdata, encoder->buffer); } void tmate_decoder_error(void) @@ -178,6 +187,12 @@ void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *rea decoder->userdata = userdata; } +void tmate_decoder_destroy(struct tmate_decoder *decoder) +{ + msgpack_unpacker_destroy(&decoder->unpacker); + memset(decoder, 0, sizeof(*decoder)); +} + void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len) { diff --git a/tmate-protocol.h b/tmate-protocol.h index 3f3a3614..93c65ba3 100644 --- a/tmate-protocol.h +++ b/tmate-protocol.h @@ -29,6 +29,7 @@ enum tmate_control_in_msg_types { TMATE_CTL_PANE_KEYS, TMATE_CTL_RESIZE, TMATE_CTL_EXEC_RESPONSE, + TMATE_CTL_RENAME_SESSION, }; /* @@ -37,6 +38,7 @@ enum tmate_control_in_msg_types { [TMATE_CTL_PANE_KEYS, int: pane_id, string: keys] [TMATE_CTL_RESIZE, int: sx, int: sy] // sx == -1: no clients [TMATE_CTL_EXEC_RESPONSE, int: exit_code, string: message] +[TMATE_CTL_RENAME_SESSION, string: stoken, string: stoken_ro] */ enum tmate_daemon_out_msg_types { @@ -50,6 +52,8 @@ enum tmate_daemon_out_msg_types { TMATE_OUT_WRITE_COPY_MODE, TMATE_OUT_FIN, TMATE_OUT_READY, + TMATE_OUT_RECONNECT, + TMATE_OUT_SNAPSHOT, }; /* diff --git a/tmate-session.c b/tmate-session.c index b7001509..6c9fea69 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -11,7 +11,8 @@ #include "tmate.h" -#define TMATE_DNS_RETRY_TIMEOUT 10 +#define TMATE_DNS_RETRY_TIMEOUT 2 +#define TMATE_RECONNECT_RETRY_TIMEOUT 2 struct tmate_session tmate_session; @@ -129,7 +130,8 @@ void tmate_session_init(struct event_base *base) void tmate_session_start(void) { - /* We split init and start because: + /* + * We split init and start because: * - We need to process the tmux config file during the connection as * we are setting up the tmate identity. * - While we are parsing the config file, we need to be able to @@ -137,3 +139,43 @@ void tmate_session_start(void) */ lookup_and_connect(); } + +static void on_reconnect_retry(__unused evutil_socket_t fd, __unused short what, void *arg) +{ + struct tmate_session *session = arg; + + if (session->last_server_ip) { + /* + * We have a previous server ip. Let's try that again first, + * but then connect to any server if it fails again. + */ + (void)tmate_ssh_client_alloc(&tmate_session, session->last_server_ip); + free(session->last_server_ip); + session->last_server_ip = NULL; + } else { + lookup_and_connect(); + } +} + +void tmate_reconnect_session(struct tmate_session *session) +{ + /* + * We no longer have an SSH connection. Time to reconnect. + * We'll reuse some of the session information if we can, + * and we'll try to reconnect to the same server if possible, + * to avoid an SSH connection string change. + */ + struct timeval tv = { .tv_sec = TMATE_RECONNECT_RETRY_TIMEOUT, .tv_usec = 0 }; + + evtimer_assign(&session->ev_connection_retry, session->ev_base, + on_reconnect_retry, session); + evtimer_add(&session->ev_connection_retry, &tv); + + tmate_status_message("Reconnecting..."); + + /* + * This says that we'll need to send a snapshot of the current state. + * Until we have persisted logs... + */ + session->reconnected = true; +} diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 898344f1..6a351483 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -12,7 +12,7 @@ static void __on_ssh_client_event(evutil_socket_t fd, short what, void *arg); static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client, const char *fmt, ...); -static void printflike(2, 3) reconnect_ssh_client(struct tmate_ssh_client *client, +static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client, const char *fmt, ...); static void read_channel(struct tmate_ssh_client *client) @@ -25,8 +25,8 @@ static void read_channel(struct tmate_ssh_client *client) tmate_decoder_get_buffer(decoder, &buf, &len); len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); if (len < 0) { - reconnect_ssh_client(client, "Error reading from channel: %s", - ssh_get_error(client->session)); + kill_ssh_client(client, "Error reading from channel: %s", + ssh_get_error(client->session)); break; } @@ -61,8 +61,8 @@ static void on_encoder_write(void *userdata, struct evbuffer *buffer) written = ssh_channel_write(client->channel, buf, len); if (written < 0) { - reconnect_ssh_client(client, "Error writing to channel: %s", - ssh_get_error(client->session)); + kill_ssh_client(client, "Error writing to channel: %s", + ssh_get_error(client->session)); break; } @@ -245,8 +245,8 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) init_conn_fd(client); return; case SSH_ERROR: - reconnect_ssh_client(client, "Error connecting: %s", - ssh_get_error(session)); + kill_ssh_client(client, "Error connecting: %s", + ssh_get_error(session)); return; case SSH_OK: init_conn_fd(client); @@ -315,19 +315,20 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: - if (client->tmate_session->need_passphrase) + if (client->tmate_session->need_passphrase) { request_passphrase(client); - else + } else { kill_ssh_client(client, "SSH keys not found." " Run 'ssh-keygen' to create keys and try again."); + return; + } if (client->tried_passphrase) tmate_status_message("Can't load SSH key." " Try typing passphrase again in case of typo. ctrl-c to abort."); return; case SSH_AUTH_ERROR: - reconnect_ssh_client(client, "Auth error: %s", - ssh_get_error(session)); + kill_ssh_client(client, "Auth error: %s", ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: tmate_debug("Auth successful"); @@ -340,8 +341,8 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - reconnect_ssh_client(client, "Error opening channel: %s", - ssh_get_error(session)); + kill_ssh_client(client, "Error opening channel: %s", + ssh_get_error(session)); return; case SSH_OK: tmate_debug("Session opened, initalizing tmate"); @@ -354,8 +355,8 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_AGAIN: return; case SSH_ERROR: - reconnect_ssh_client(client, "Error initializing tmate: %s", - ssh_get_error(session)); + kill_ssh_client(client, "Error initializing tmate: %s", + ssh_get_error(session)); return; case SSH_OK: tmate_debug("Ready"); @@ -365,19 +366,22 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) client->state = SSH_READY; + if (client->tmate_session->reconnected) + tmate_send_reconnection_state(client->tmate_session); + tmate_encoder_set_ready_callback(&client->tmate_session->encoder, on_encoder_write, client); tmate_decoder_init(&client->tmate_session->decoder, on_decoder_read, client); + + free(client->tmate_session->last_server_ip); + client->tmate_session->last_server_ip = xstrdup(client->server_ip); + /* fall through */ } case SSH_READY: read_channel(client); - if (!ssh_is_connected(session)) { - reconnect_ssh_client(client, "Disconnected"); - return; - } } } @@ -386,19 +390,37 @@ static void __on_ssh_client_event(__unused evutil_socket_t fd, __unused short wh on_ssh_client_event(arg); } -static void __kill_ssh_client(struct tmate_ssh_client *client, - const char *fmt, va_list va) +static void kill_ssh_client(struct tmate_ssh_client *client, + const char *fmt, ...) { - if (fmt && TAILQ_EMPTY(&client->tmate_session->clients)) - __tmate_status_message(fmt, va); - else - tmate_debug("Disconnecting %s", client->server_ip); + bool last_client; + va_list ap; + + TAILQ_REMOVE(&client->tmate_session->clients, client, node); + last_client = TAILQ_EMPTY(&client->tmate_session->clients); + + if (fmt && last_client) { + va_start(ap, fmt); + __tmate_status_message(fmt, ap); + va_end(ap); + } + + tmate_debug("SSH client killed (%s)", client->server_ip); if (client->has_init_conn_fd) { event_del(&client->ev_ssh); client->has_init_conn_fd = false; } + if (client->state == SSH_READY) { + tmate_encoder_set_ready_callback(&client->tmate_session->encoder, NULL, NULL); + tmate_decoder_destroy(&client->tmate_session->decoder); + + client->tmate_session->min_sx = -1; + client->tmate_session->min_sy = -1; + recalculate_sizes(); + } + if (client->session) { /* ssh_free() also frees the associated channels. */ ssh_free(client->session); @@ -406,19 +428,8 @@ static void __kill_ssh_client(struct tmate_ssh_client *client, client->channel = NULL; } - client->state = SSH_NONE; -} - -static void kill_ssh_client(struct tmate_ssh_client *client, - const char *fmt, ...) -{ - va_list ap; - - TAILQ_REMOVE(&client->tmate_session->clients, client, node); - - va_start(ap, fmt); - __kill_ssh_client(client, fmt, ap); - va_end(ap); + if (last_client) + tmate_reconnect_session(client->tmate_session); free(client->server_ip); free(client); @@ -432,33 +443,6 @@ static void connect_ssh_client(struct tmate_ssh_client *client) } } -static void on_reconnect_timer(__unused evutil_socket_t fd, __unused short what, void *arg) -{ - connect_ssh_client(arg); -} - -static void reconnect_ssh_client(struct tmate_ssh_client *client, - const char *fmt, ...) -{ - /* struct timeval tv; */ - va_list ap; - -#if 1 - TAILQ_REMOVE(&client->tmate_session->clients, client, node); -#endif - - va_start(ap, fmt); - __kill_ssh_client(client, fmt, ap); - va_end(ap); - - /* Not yet implemented... */ -#if 0 - tv.tv_sec = 1; - tv.tv_usec = 0; - evtimer_add(&client->ev_ssh_reconnect, &tv); -#endif -} - static void ssh_log_function(int priority, const char *function, const char *buffer, __unused void *userdata) { @@ -489,9 +473,6 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, client->has_init_conn_fd = false; - evtimer_assign(&client->ev_ssh_reconnect, session->ev_base, - on_reconnect_timer, client); - connect_ssh_client(client); return client; diff --git a/tmate.h b/tmate.h index 11236360..5e799e7e 100644 --- a/tmate.h +++ b/tmate.h @@ -30,6 +30,7 @@ struct tmate_encoder { extern void tmate_encoder_init(struct tmate_encoder *encoder, tmate_encoder_write_cb *callback, void *userdata); +extern void tmate_encoder_destroy(struct tmate_encoder *encoder); extern void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, tmate_encoder_write_cb *callback, void *userdata); @@ -50,6 +51,7 @@ struct tmate_decoder { }; extern void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, void *userdata); +extern void tmate_decoder_destroy(struct tmate_decoder *decoder); extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len); extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); @@ -75,6 +77,8 @@ extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *neste #define TMATE_PROTOCOL_VERSION 6 +struct tmate_session; + extern void tmate_write_header(void); extern void tmate_write_ready(void); extern void tmate_sync_layout(void); @@ -86,6 +90,7 @@ extern void tmate_status(const char *left, const char *right); extern void tmate_sync_copy_mode(struct window_pane *wp); extern void tmate_write_copy_mode(struct window_pane *wp, const char *str); extern void tmate_write_fin(void); +extern void tmate_send_reconnection_state(struct tmate_session *session); /* tmate-decoder.c */ @@ -135,7 +140,6 @@ struct tmate_ssh_client { bool has_init_conn_fd; struct event ev_ssh; - struct event ev_ssh_reconnect; }; TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); @@ -166,11 +170,17 @@ struct tmate_session { struct tmate_ssh_clients clients; int need_passphrase; char *passphrase; + + bool reconnected; + struct event ev_connection_retry; + char *last_server_ip; + char *reconnection_data; }; extern struct tmate_session tmate_session; extern void tmate_session_init(struct event_base *base); extern void tmate_session_start(void); +extern void tmate_reconnect_session(struct tmate_session *session); /* tmate-debug.c */ extern void tmate_print_stack_trace(void); From a7c55074643b06041f24aabf9fcec827ae4906b7 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 27 Mar 2016 00:30:20 -0400 Subject: [PATCH 616/703] snapshot --- tmate-encoder.c | 46 ++++++++++++++++++++++++++++------------------ tmate.h | 2 +- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/tmate-encoder.c b/tmate-encoder.c index 4155237b..10c9e8c8 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -2,7 +2,7 @@ #include "tmate-protocol.h" #include "window-copy.h" -#define pack(what, ...) _pack(&tmate_session.encoder, what, __VA_ARGS__) +#define pack(what, ...) _pack(&tmate_session.encoder, what, ##__VA_ARGS__) void tmate_write_header(void) { @@ -237,29 +237,14 @@ void tmate_write_fin(void) pack(int, TMATE_OUT_FIN); } -static void do_snapshot(unsigned int max_history_lines, - struct window_pane *pane) +static void do_snapshot_grid(struct grid *grid, unsigned int max_history_lines) { - struct screen *screen; - struct grid *grid; struct grid_line *line; struct grid_cell gc; unsigned int line_i, i; unsigned int max_lines; size_t str_len; - screen = &pane->base; - grid = screen->grid; - - pack(array, 4); - pack(int, pane->id); - - pack(array, 2); - pack(int, screen->cx); - pack(int, screen->cy); - - pack(unsigned_int, screen->mode); - max_lines = max_history_lines + grid->sy; #define grid_num_lines(grid) (grid->hsize + grid->sy) @@ -295,6 +280,31 @@ static void do_snapshot(unsigned int max_history_lines, gc.fg )); } } + +} + +static void do_snapshot_pane(struct window_pane *wp, unsigned int max_history_lines) +{ + struct screen *screen = &wp->base; + + pack(array, 4); + pack(int, wp->id); + + pack(unsigned_int, screen->mode); + + pack(array, 3); + pack(int, screen->cx); + pack(int, screen->cy); + do_snapshot_grid(screen->grid, max_history_lines); + + if (wp->saved_grid) { + pack(array, 3); + pack(int, wp->saved_cx); + pack(int, wp->saved_cy); + do_snapshot_grid(wp->saved_grid, max_history_lines); + } else { + pack(nil); + } } static void tmate_send_session_snapshot(unsigned int max_history_lines) @@ -329,7 +339,7 @@ static void tmate_send_session_snapshot(unsigned int max_history_lines) continue; TAILQ_FOREACH(pane, &w->panes, entry) - do_snapshot(max_history_lines, pane); + do_snapshot_pane(pane, max_history_lines); } } diff --git a/tmate.h b/tmate.h index 5e799e7e..0dca56f9 100644 --- a/tmate.h +++ b/tmate.h @@ -38,7 +38,7 @@ extern void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, extern void msgpack_pack_string(msgpack_packer *pk, const char *str); extern void msgpack_pack_boolean(msgpack_packer *pk, bool value); -#define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, __VA_ARGS__) +#define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, ##__VA_ARGS__) struct tmate_unpacker; struct tmate_decoder; From 474487c33e864f9cabc91fc627f9d64160c03a8f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 27 Mar 2016 01:10:23 -0400 Subject: [PATCH 617/703] Replay commands --- tmate-encoder.c | 38 +++++++++++++++++++++++++++++++++++++- tmate.h | 11 +++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/tmate-encoder.c b/tmate-encoder.c index 10c9e8c8..1a6c7b19 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -150,13 +150,48 @@ int tmate_should_replicate_cmd(const struct cmd_entry *cmd) return 0; } -void tmate_exec_cmd(const char *cmd) +#define sc (&session->saved_tmux_cmds) +#define SAVED_TMUX_CMD_INITIAL_SIZE 256 +static void __tmate_exec_cmd(const char *cmd); + +static void append_saved_cmd(struct tmate_session *session, + const char *cmd) +{ + if (!sc->cmds) { + sc->capacity = SAVED_TMUX_CMD_INITIAL_SIZE; + sc->cmds = xmalloc(sizeof(char *) * sc->capacity); + sc->tail = 0; + } + + if (sc->tail == sc->capacity) { + sc->capacity *= 2; + sc->cmds = xrealloc(sc->cmds, sizeof(char *) * sc->capacity); + } + + sc->cmds[sc->tail++] = xstrdup(cmd); +} + +static void replay_saved_cmd(struct tmate_session *session) +{ + unsigned int i; + for (i = 0; i < sc->tail; i++) + __tmate_exec_cmd(sc->cmds[i]); +} +#undef sc + +static void __tmate_exec_cmd(const char *cmd) { pack(array, 2); pack(int, TMATE_OUT_EXEC_CMD); pack(string, cmd); } +void tmate_exec_cmd(const char *cmd) +{ + __tmate_exec_cmd(cmd); + append_saved_cmd(&tmate_session, cmd); +} + void tmate_failed_cmd(int client_id, const char *cause) { pack(array, 3); @@ -363,6 +398,7 @@ void tmate_send_reconnection_state(struct tmate_session *session) tmate_write_header(); tmate_send_reconnection_data(session); + replay_saved_cmd(session); /* TODO send all option variables */ tmate_write_ready(); diff --git a/tmate.h b/tmate.h index 0dca56f9..e6d3ef09 100644 --- a/tmate.h +++ b/tmate.h @@ -175,6 +175,17 @@ struct tmate_session { struct event ev_connection_retry; char *last_server_ip; char *reconnection_data; + /* + * When we reconnect, instead of serializing the key bindings and + * options, we replay all the tmux commands we replicated. + * It may be a little innacurate to replicate the state, but + * it's much easier. + */ + struct { + unsigned int capacity; + unsigned int tail; + char **cmds; + } saved_tmux_cmds; }; extern struct tmate_session tmate_session; From 37c71cfe1587ffaea76776714bd38abe28697e83 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 27 Mar 2016 02:06:56 -0400 Subject: [PATCH 618/703] Escape sent commands --- arguments.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arguments.c b/arguments.c index f7f8f737..bcd2cd42 100644 --- a/arguments.c +++ b/arguments.c @@ -177,20 +177,34 @@ args_print(struct args *args) args_print_add(&buf, &len, " -%c ", entry->flag); else args_print_add(&buf, &len, "-%c ", entry->flag); +#ifdef TMATE + if (strchr(entry->value, '\'') != NULL) + args_print_add(&buf, &len, "\"%s\"", entry->value); + else + args_print_add(&buf, &len, "'%s'", entry->value); +#else if (strchr(entry->value, ' ') != NULL) args_print_add(&buf, &len, "\"%s\"", entry->value); else args_print_add(&buf, &len, "%s", entry->value); +#endif } /* And finally the argument vector. */ for (i = 0; i < args->argc; i++) { if (*buf != '\0') args_print_add(&buf, &len, " "); +#ifdef TMATE + if (strchr(args->argv[i], '\'') != NULL) + args_print_add(&buf, &len, "\"%s\"", args->argv[i]); + else + args_print_add(&buf, &len, "'%s'", args->argv[i]); +#else if (strchr(args->argv[i], ' ') != NULL) args_print_add(&buf, &len, "\"%s\"", args->argv[i]); else args_print_add(&buf, &len, "%s", args->argv[i]); +#endif } return (buf); From 9b5bb8390cd5318a29ce6249b9f16a06d11b78f3 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 27 Mar 2016 02:29:43 -0400 Subject: [PATCH 619/703] better error message --- client.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client.c b/client.c index 3db8783f..164abdd3 100644 --- a/client.c +++ b/client.c @@ -296,6 +296,13 @@ client_main(struct event_base *base, int argc, char **argv, int flags, fprintf(stderr, "no server running on %s\n", socket_path); } else { +#ifdef TMATE + if (errno == ENOENT) + fprintf(stderr, "You must specify a socket name with -S. For example: \n" + " tmate -S /tmp/tmate.sock new-session -d\n" + " tmate -S /tmp/tmate.sock wait tmate-ready\n"); + else +#endif fprintf(stderr, "error connecting to %s (%s)\n", socket_path, strerror(errno)); } From 1ade196fb2005cef09409827ad743abbe97a90ed Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 27 Mar 2016 02:35:11 -0400 Subject: [PATCH 620/703] Default tmate messages to 15s --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index eb87d8a9..6b0c248b 100644 --- a/options-table.c +++ b/options-table.c @@ -939,7 +939,7 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, - .default_num = 30000 + .default_num = 15000 }, { .name = "tmate-webhook-userdata", From 78305a7077adf6b3b9143308e68afcab4d9cd94f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 27 Mar 2016 10:39:19 -0400 Subject: [PATCH 621/703] Cleanup socket --- server.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server.c b/server.c index 7461d533..7313b711 100644 --- a/server.c +++ b/server.c @@ -213,6 +213,9 @@ server_start(struct event_base *base, int lockfd, char *lockfile) proc_loop(server_proc, server_loop); status_prompt_save_history(); +#ifdef TMATE + unlink(socket_path); +#endif exit(0); } From c88870b0a38acdfb598057ec344cc3d5e3991933 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 27 Mar 2016 23:33:39 -0400 Subject: [PATCH 622/703] Revert "Escape sent commands" This reverts commit 37c71cfe1587ffaea76776714bd38abe28697e83. --- arguments.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/arguments.c b/arguments.c index bcd2cd42..f7f8f737 100644 --- a/arguments.c +++ b/arguments.c @@ -177,34 +177,20 @@ args_print(struct args *args) args_print_add(&buf, &len, " -%c ", entry->flag); else args_print_add(&buf, &len, "-%c ", entry->flag); -#ifdef TMATE - if (strchr(entry->value, '\'') != NULL) - args_print_add(&buf, &len, "\"%s\"", entry->value); - else - args_print_add(&buf, &len, "'%s'", entry->value); -#else if (strchr(entry->value, ' ') != NULL) args_print_add(&buf, &len, "\"%s\"", entry->value); else args_print_add(&buf, &len, "%s", entry->value); -#endif } /* And finally the argument vector. */ for (i = 0; i < args->argc; i++) { if (*buf != '\0') args_print_add(&buf, &len, " "); -#ifdef TMATE - if (strchr(args->argv[i], '\'') != NULL) - args_print_add(&buf, &len, "\"%s\"", args->argv[i]); - else - args_print_add(&buf, &len, "'%s'", args->argv[i]); -#else if (strchr(args->argv[i], ' ') != NULL) args_print_add(&buf, &len, "\"%s\"", args->argv[i]); else args_print_add(&buf, &len, "%s", args->argv[i]); -#endif } return (buf); From 02694d2a966b5d1e3a5d2cbd1b76c627eb4b6ec2 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 27 Mar 2016 23:32:32 -0400 Subject: [PATCH 623/703] Send commands with their arguments in an array --- cmd-queue.c | 7 +++-- server.c | 8 ++--- tmate-decoder.c | 56 +++++++++++++++++++++++++++++++-- tmate-encoder.c | 81 ++++++++++++++++++++++++++++++++++++++++-------- tmate-protocol.h | 14 ++++++--- tmate.h | 8 +++-- 6 files changed, 145 insertions(+), 29 deletions(-) diff --git a/cmd-queue.c b/cmd-queue.c index fad9760b..f08a724e 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -201,12 +201,13 @@ cmdq_continue_one(struct cmd_q *cmdq) char *tmp; int flags = !!(cmd->flags & CMD_CONTROL); - tmp = cmd_print(cmd); - log_debug("cmdq %p: %s", cmdq, tmp); #ifdef TMATE if (tmate_should_replicate_cmd(cmd->entry)) - tmate_exec_cmd(tmp); + tmate_exec_cmd(cmd); #endif + + tmp = cmd_print(cmd); + log_debug("cmdq %p: %s", cmdq, tmp); free(tmp); cmdq->time = time(NULL); diff --git a/server.c b/server.c index 7313b711..81334f1e 100644 --- a/server.c +++ b/server.c @@ -135,13 +135,13 @@ server_create_socket(void) static void tmate_set_editor_mode(void) { switch (options_get_number(global_s_options, "status-keys")) { - case MODEKEY_EMACS: tmate_exec_cmd("set-option -g status-keys emacs"); break; - case MODEKEY_VI: tmate_exec_cmd("set-option -g status-keys vi"); break; + case MODEKEY_EMACS: tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "status-keys", "emacs"}); break; + case MODEKEY_VI: tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "status-keys", "vi"}); break; } switch (options_get_number(global_w_options, "mode-keys")) { - case MODEKEY_EMACS: tmate_exec_cmd("set-window-option -g mode-keys emacs"); break; - case MODEKEY_VI: tmate_exec_cmd("set-window-option -g mode-keys vi"); break; + case MODEKEY_EMACS: tmate_exec_cmd_args(4, (const char *[]){"set-window-option", "-g", "status-keys", "emacs"}); break; + case MODEKEY_VI: tmate_exec_cmd_args(4, (const char *[]){"set-window-option", "-g", "status-keys", "vi"}); break; } } #endif diff --git a/tmate-decoder.c b/tmate-decoder.c index 9669c4c0..3b1e31af 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -78,8 +78,8 @@ static void handle_resize(struct tmate_session *session, extern char **cfg_causes; extern u_int cfg_ncauses; -static void handle_exec_cmd(__unused struct tmate_session *session, - struct tmate_unpacker *uk) +static void handle_exec_cmd_str(__unused struct tmate_session *session, + struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; @@ -114,6 +114,55 @@ out: free(cmd_str); } +static void handle_exec_cmd(__unused struct tmate_session *session, + struct tmate_unpacker *uk) +{ + struct cmd_q *cmd_q; + struct cmd_list *cmdlist; + struct cmd *cmd; + char *cause; + u_int i; + unsigned int argc; + char **argv; + + int client_id = unpack_int(uk); + + argc = uk->argc; + argv = xmalloc(sizeof(char *) * argc); + for (i = 0; i < argc; i++) + argv[i] = unpack_string(uk); + + cmd = cmd_parse(argc, argv, NULL, 0, &cause); + if (!cmd) { + tmate_failed_cmd(client_id, cause); + free(cause); + goto out; + } + + cmdlist = xcalloc(1, sizeof *cmdlist); + cmdlist->references = 1; + TAILQ_INIT(&cmdlist->list); + TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); + + cmd_q = cmdq_new(NULL); + cmdq_run(cmd_q, cmdlist, NULL); + cmd_list_free(cmdlist); + cmdq_free(cmd_q); + + /* error messages land in cfg_causes */ + for (i = 0; i < cfg_ncauses; i++) { + tmate_failed_cmd(client_id, cfg_causes[i]); + free(cfg_causes[i]); + } + + free(cfg_causes); + cfg_causes = NULL; + cfg_ncauses = 0; + +out: + cmd_free_argv(argc, argv); +} + static void maybe_save_reconnection_data(struct tmate_session *session, const char *name, const char *value) { @@ -152,10 +201,11 @@ void tmate_dispatch_slave_message(struct tmate_session *session, dispatch(TMATE_IN_NOTIFY, handle_notify); dispatch(TMATE_IN_LEGACY_PANE_KEY, handle_legacy_pane_key); dispatch(TMATE_IN_RESIZE, handle_resize); - dispatch(TMATE_IN_EXEC_CMD, handle_exec_cmd); + dispatch(TMATE_IN_EXEC_CMD_STR, handle_exec_cmd_str); dispatch(TMATE_IN_SET_ENV, handle_set_env); dispatch(TMATE_IN_READY, handle_ready); dispatch(TMATE_IN_PANE_KEY, handle_pane_key); + dispatch(TMATE_IN_EXEC_CMD, handle_exec_cmd); default: tmate_info("Bad message type: %d", cmd); } } diff --git a/tmate-encoder.c b/tmate-encoder.c index 1a6c7b19..d4be7c6e 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -152,44 +152,99 @@ int tmate_should_replicate_cmd(const struct cmd_entry *cmd) #define sc (&session->saved_tmux_cmds) #define SAVED_TMUX_CMD_INITIAL_SIZE 256 -static void __tmate_exec_cmd(const char *cmd); +static void __tmate_exec_cmd_args(int argc, const char **argv); static void append_saved_cmd(struct tmate_session *session, - const char *cmd) + int argc, const char **argv) { if (!sc->cmds) { sc->capacity = SAVED_TMUX_CMD_INITIAL_SIZE; - sc->cmds = xmalloc(sizeof(char *) * sc->capacity); + sc->cmds = xmalloc(sizeof(*sc->cmds) * sc->capacity); sc->tail = 0; } if (sc->tail == sc->capacity) { sc->capacity *= 2; - sc->cmds = xrealloc(sc->cmds, sizeof(char *) * sc->capacity); + sc->cmds = xrealloc(sc->cmds, sizeof(*sc->cmds) * sc->capacity); } - sc->cmds[sc->tail++] = xstrdup(cmd); + sc->cmds[sc->tail].argc = argc; + sc->cmds[sc->tail].argv = cmd_copy_argv(argc, (char **)argv); + + sc->tail++; } static void replay_saved_cmd(struct tmate_session *session) { unsigned int i; for (i = 0; i < sc->tail; i++) - __tmate_exec_cmd(sc->cmds[i]); + __tmate_exec_cmd_args(sc->cmds[i].argc, (const char **)sc->cmds[i].argv); } #undef sc -static void __tmate_exec_cmd(const char *cmd) +struct args_entry { + u_char flag; + char *value; + RB_ENTRY(args_entry) entry; +}; + +static void extract_cmd(struct cmd *cmd, int *_argc, char ***_argv) { - pack(array, 2); - pack(int, TMATE_OUT_EXEC_CMD); - pack(string, cmd); + struct args_entry *entry; + struct args* args = cmd->args; + int argc = 0; + char **argv; + int next = 0, i; + + argc++; /* cmd name */ + RB_FOREACH(entry, args_tree, &args->tree) { + argc++; + if (entry->value != NULL) + argc++; + } + argc += args->argc; + argv = xmalloc(sizeof(char *) * argc); + + argv[next++] = xstrdup(cmd->entry->name); + + RB_FOREACH(entry, args_tree, &args->tree) { + xasprintf(&argv[next++], "-%c", entry->flag); + if (entry->value != NULL) + argv[next++] = xstrdup(entry->value); + } + + for (i = 0; i < args->argc; i++) + argv[next++] = xstrdup(args->argv[i]); + + *_argc = argc; + *_argv = argv; } -void tmate_exec_cmd(const char *cmd) +static void __tmate_exec_cmd_args(int argc, const char **argv) { - __tmate_exec_cmd(cmd); - append_saved_cmd(&tmate_session, cmd); + int i; + + pack(array, argc + 1); + pack(int, TMATE_OUT_EXEC_CMD); + + for (i = 0; i < argc; i++) + pack(string, argv[i]); +} + +void tmate_exec_cmd_args(int argc, const char **argv) +{ + __tmate_exec_cmd_args(argc, argv); + append_saved_cmd(&tmate_session, argc, argv); +} + +void tmate_exec_cmd(struct cmd *cmd) +{ + int argc; + char **argv; + + extract_cmd(cmd, &argc, &argv); + tmate_exec_cmd_args(argc, (const char **)argv); + cmd_free_argv(argc, argv); } void tmate_failed_cmd(int client_id, const char *cause) diff --git a/tmate-protocol.h b/tmate-protocol.h index 93c65ba3..ebae07a9 100644 --- a/tmate-protocol.h +++ b/tmate-protocol.h @@ -45,7 +45,7 @@ enum tmate_daemon_out_msg_types { TMATE_OUT_HEADER, TMATE_OUT_SYNC_LAYOUT, TMATE_OUT_PTY_DATA, - TMATE_OUT_EXEC_CMD, + TMATE_OUT_EXEC_CMD_STR, TMATE_OUT_FAILED_CMD, TMATE_OUT_STATUS, TMATE_OUT_SYNC_COPY_MODE, @@ -54,6 +54,7 @@ enum tmate_daemon_out_msg_types { TMATE_OUT_READY, TMATE_OUT_RECONNECT, TMATE_OUT_SNAPSHOT, + TMATE_OUT_EXEC_CMD, }; /* @@ -62,7 +63,7 @@ enum tmate_daemon_out_msg_types { [[int: pane_id, int: sx, int: sy, int: xoff, int: yoff], ...], int: active_pane_id], ...], int: active_win_id] [TMATE_OUT_PTY_DATA, int: pane_id, binary: buffer] -[TMATE_OUT_EXEC_CMD, string: cmd] +[TMATE_OUT_EXEC_CMD_STR, string: cmd] [TMATE_OUT_FAILED_CMD, int: client_id, string: cause] [TMATE_OUT_STATUS, string: left, string: right] [TMATE_OUT_SYNC_COPY_MODE, int: pane_id, [int: backing, int: oy, int: cx, int: cy, @@ -72,26 +73,31 @@ enum tmate_daemon_out_msg_types { [TMATE_OUT_WRITE_COPY_MODE, int: pane_id, string: str] [TMATE_OUT_FIN] [TMATE_OUT_READY] +[TMATE_OUT_RECONNECT, string: reconnection_data] +[TMATE_OUT_SNAPSHOT, ...] +[TMATE_OUT_EXEC_CMD, string: cmd_name, ...string: args] */ enum tmate_daemon_in_msg_types { TMATE_IN_NOTIFY, TMATE_IN_LEGACY_PANE_KEY, TMATE_IN_RESIZE, - TMATE_IN_EXEC_CMD, + TMATE_IN_EXEC_CMD_STR, TMATE_IN_SET_ENV, TMATE_IN_READY, TMATE_IN_PANE_KEY, + TMATE_IN_EXEC_CMD, }; /* [TMATE_IN_NOTIFY, string: msg] [TMATE_IN_PANE_KEY, int: key] [TMATE_IN_RESIZE, int: sx, int: sy] // sx == -1: no clients -[TMATE_IN_EXEC_CMD, int: client_id, string: cmd] +[TMATE_IN_EXEC_CMD_STR, int: client_id, string: cmd] [TMATE_IN_SET_ENV, string: name, string: value] [TMATE_IN_READY] [TMATE_IN_PANE_KEY, int: pane_id, uint64 keycode] // pane_id == -1: active pane +[TMATE_IN_EXEC_CMD, int: client_id, ...string: args] */ #endif diff --git a/tmate.h b/tmate.h index e6d3ef09..fb764c54 100644 --- a/tmate.h +++ b/tmate.h @@ -84,7 +84,8 @@ extern void tmate_write_ready(void); extern void tmate_sync_layout(void); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); -extern void tmate_exec_cmd(const char *cmd); +extern void tmate_exec_cmd_args(int argc, const char **argv); +extern void tmate_exec_cmd(struct cmd *cmd); extern void tmate_failed_cmd(int client_id, const char *cause); extern void tmate_status(const char *left, const char *right); extern void tmate_sync_copy_mode(struct window_pane *wp); @@ -184,7 +185,10 @@ struct tmate_session { struct { unsigned int capacity; unsigned int tail; - char **cmds; + struct { + int argc; + char **argv; + } *cmds; } saved_tmux_cmds; }; From 71d31e60e6eef1f79191977eb8c7210174d33187 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 28 Mar 2016 16:27:09 -0400 Subject: [PATCH 624/703] Be less strict over msgpack message size --- tmate-encoder.c | 7 ++++--- tmate-msgpack.c | 8 ++++---- tmate-protocol.h | 2 -- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/tmate-encoder.c b/tmate-encoder.c index d4be7c6e..c15d8921 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -108,13 +108,14 @@ void tmate_sync_layout(void) /* TODO add a buffer for pty_data ? */ +#define TMATE_MAX_PTY_SIZE (16*1024) + void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) { - size_t max_write, to_write; + size_t to_write; - max_write = TMATE_MAX_MESSAGE_SIZE - 16; while (len > 0) { - to_write = len < max_write ? len : max_write; + to_write = len < TMATE_MAX_PTY_SIZE ? len : TMATE_MAX_PTY_SIZE; pack(array, 3); pack(int, TMATE_OUT_PTY_DATA); diff --git a/tmate-msgpack.c b/tmate-msgpack.c index b7948ef9..144cda70 100644 --- a/tmate-msgpack.c +++ b/tmate-msgpack.c @@ -178,10 +178,12 @@ void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested) uk->argc--; } +#define UNPACKER_RESERVE_SIZE 1024 + void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, void *userdata) { - if (!msgpack_unpacker_init(&decoder->unpacker, TMATE_MAX_MESSAGE_SIZE)) + if (!msgpack_unpacker_init(&decoder->unpacker, UNPACKER_RESERVE_SIZE)) tmate_fatal("Cannot initialize the unpacker"); decoder->reader = reader; decoder->userdata = userdata; @@ -196,9 +198,7 @@ void tmate_decoder_destroy(struct tmate_decoder *decoder) void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len) { - ssize_t current_size = msgpack_unpacker_message_size(&decoder->unpacker); - if (!msgpack_unpacker_reserve_buffer(&decoder->unpacker, - TMATE_MAX_MESSAGE_SIZE - current_size)) + if (!msgpack_unpacker_reserve_buffer(&decoder->unpacker, UNPACKER_RESERVE_SIZE)) tmate_fatal("cannot expand decoder buffer"); *buf = msgpack_unpacker_buffer(&decoder->unpacker); diff --git a/tmate-protocol.h b/tmate-protocol.h index ebae07a9..1858b691 100644 --- a/tmate-protocol.h +++ b/tmate-protocol.h @@ -1,8 +1,6 @@ #ifndef TMATE_PROTOCOL_H #define TMATE_PROTOCOL_H -#define TMATE_MAX_MESSAGE_SIZE (16*1024) - enum tmate_control_out_msg_types { TMATE_CTL_AUTH, TMATE_CTL_DEAMON_OUT_MSG, From d41b06dea2c87e02de7bb2a7f85f0ddaf2db7064 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 28 Mar 2016 16:28:11 -0400 Subject: [PATCH 625/703] sync tmate-protocol.h --- tmate-protocol.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tmate-protocol.h b/tmate-protocol.h index 1858b691..595b6271 100644 --- a/tmate-protocol.h +++ b/tmate-protocol.h @@ -2,23 +2,26 @@ #define TMATE_PROTOCOL_H enum tmate_control_out_msg_types { - TMATE_CTL_AUTH, + TMATE_CTL_HEADER, TMATE_CTL_DEAMON_OUT_MSG, TMATE_CTL_SNAPSHOT, TMATE_CTL_CLIENT_JOIN, TMATE_CTL_CLIENT_LEFT, TMATE_CTL_EXEC, + TMATE_CTL_LATENCY, }; /* -[TMATE_CTL_AUTH, int: ctl_proto_version, string: ip_address, string: pubkey, - string: session_token, string: session_token_ro] +[TMATE_CTL_HEADER, int: ctl_proto_version, string: ip_address, string: pubkey, + string: session_token, string: session_token_ro, string: ssh_cmd_fmt] + string: client_version, int: client_protocol_version] [TMATE_CTL_DEAMON_OUT_MSG, object: msg] [TMATE_CTL_SNAPSHOT, [[int: pane_id, [int: cur_x, int: cur_y], int: mode, [[string: line_utf8, [int: char_attr, ...]], ...], ...], ...]] [TMATE_CTL_CLIENT_JOIN, int: client_id, string: ip_address, string: pubkey, boolean: readonly] [TMATE_CTL_CLIENT_LEFT, int: client_id] [TMATE_CTL_EXEC, string: username, string: ip_address, string: pubkey, string: command] +[TMATE_CTL_LATENCY, int: client_id, int: latency_ms] // client_id == -1: tmate host */ enum tmate_control_in_msg_types { From 46a29037d43c774c07c1160994b15bb1a64c20a7 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 28 Mar 2016 23:19:42 -0400 Subject: [PATCH 626/703] strip static builds Fixes #79 --- Makefile.static-build | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.static-build b/Makefile.static-build index 6db26d76..9bc83d92 100644 --- a/Makefile.static-build +++ b/Makefile.static-build @@ -54,6 +54,7 @@ tmate: $(MSGPACK_LIB) $(LIBSSH_LIB) $(patsubst %,libc/%.o,$(STATIC_LIBC_OBJECTS) ./autogen.sh $(TMATE_CONFIGURE) ./configure --enable-static +make + strip tmate clean: rm -rf ext libc $(LIBSSH) $(MSGPACK) From c9813a8c4249f930750ee4793e0972db43048d2c Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 28 Mar 2016 23:29:49 -0400 Subject: [PATCH 627/703] Provide better reconnection error message --- tmate-session.c | 7 +++++-- tmate-ssh-client.c | 6 ++++-- tmate.h | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tmate-session.c b/tmate-session.c index 6c9fea69..b43a261b 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -157,7 +157,7 @@ static void on_reconnect_retry(__unused evutil_socket_t fd, __unused short what, } } -void tmate_reconnect_session(struct tmate_session *session) +void tmate_reconnect_session(struct tmate_session *session, const char *message) { /* * We no longer have an SSH connection. Time to reconnect. @@ -171,7 +171,10 @@ void tmate_reconnect_session(struct tmate_session *session) on_reconnect_retry, session); evtimer_add(&session->ev_connection_retry, &tv); - tmate_status_message("Reconnecting..."); + if (message) + tmate_status_message("Reconnecting... (%s)", message); + else + tmate_status_message("Reconnecting..."); /* * This says that we'll need to send a snapshot of the current state. diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 6a351483..7495d584 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -395,14 +395,16 @@ static void kill_ssh_client(struct tmate_ssh_client *client, { bool last_client; va_list ap; + char *message = NULL; TAILQ_REMOVE(&client->tmate_session->clients, client, node); last_client = TAILQ_EMPTY(&client->tmate_session->clients); if (fmt && last_client) { va_start(ap, fmt); - __tmate_status_message(fmt, ap); + xvasprintf(&message, fmt, ap); va_end(ap); + tmate_status_message("%s", message); } tmate_debug("SSH client killed (%s)", client->server_ip); @@ -429,7 +431,7 @@ static void kill_ssh_client(struct tmate_ssh_client *client, } if (last_client) - tmate_reconnect_session(client->tmate_session); + tmate_reconnect_session(client->tmate_session, message); free(client->server_ip); free(client); diff --git a/tmate.h b/tmate.h index fb764c54..f096a35c 100644 --- a/tmate.h +++ b/tmate.h @@ -195,7 +195,7 @@ struct tmate_session { extern struct tmate_session tmate_session; extern void tmate_session_init(struct event_base *base); extern void tmate_session_start(void); -extern void tmate_reconnect_session(struct tmate_session *session); +extern void tmate_reconnect_session(struct tmate_session *session, const char *message); /* tmate-debug.c */ extern void tmate_print_stack_trace(void); From e840ff7582496386e63d289f2c21dc914088374f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 28 Mar 2016 23:06:24 -0400 Subject: [PATCH 628/703] Version Bump --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 55eaf24a..02d2872b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT(tmate, 2.2.0) +AC_INIT(tmate, 2.2.1) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) From d433fe69565db25b4248b8e9f0316d2cd04fe7e3 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 29 Mar 2016 01:42:07 -0400 Subject: [PATCH 629/703] nits --- tmate-ssh-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 7495d584..b280400d 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -319,7 +319,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) request_passphrase(client); } else { kill_ssh_client(client, "SSH keys not found." - " Run 'ssh-keygen' to create keys and try again."); + " Run 'ssh-keygen' to create keys."); return; } From fe81322cc4801860c33b1e31970b8f0f6bf603c7 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 21 Apr 2016 16:05:52 -0400 Subject: [PATCH 630/703] Keep alive the socket to make reconnections work properly --- tmate-ssh-client.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index b280400d..8b78f5cc 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -165,22 +165,48 @@ static void request_passphrase(struct tmate_ssh_client *client) data->password_cb_private = client; } +#define KEEPALIVE_CNT 3 +#define KEEPALIVE_IDLE 20 +#define KEEPALIVE_INTVL 10 + +static void setup_socket(int fd) +{ +#define SSO(level, optname, val) ({ \ + int _flag = val; \ + if (setsockopt(fd, level, optname, &(_flag), sizeof(int)) < 0) { \ + tmate_warn("setsockopt(" #level ", " #optname ", %d) failed", val); \ + } \ +}) + + SSO(IPPROTO_TCP, TCP_NODELAY, 1); + SSO(SOL_SOCKET, SO_KEEPALIVE, 1); +#ifdef TCP_KEEPALIVE + SSO(IPPROTO_TCP, TCP_KEEPALIVE, KEEPALIVE_IDLE); +#endif +#ifdef TCP_KEEPCNT + SSO(IPPROTO_TCP, TCP_KEEPCNT, KEEPALIVE_CNT); +#endif +#ifdef TCP_KEEPIDLE + SSO(IPPROTO_TCP, TCP_KEEPIDLE, KEEPALIVE_IDLE); +#endif +#ifdef TCP_KEEPINTVL + SSO(IPPROTO_TCP, TCP_KEEPINTVL, KEEPALIVE_INTVL); +#endif +#undef SSO +} + static void init_conn_fd(struct tmate_ssh_client *client) { + int fd; + if (client->has_init_conn_fd) return; - if (ssh_get_fd(client->session) < 0) + if ((fd = ssh_get_fd(client->session)) < 0) return; - { - int flag = 1; - setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, - TCP_NODELAY, &flag, sizeof(flag)); - } - - event_set(&client->ev_ssh, ssh_get_fd(client->session), - EV_READ | EV_PERSIST, __on_ssh_client_event, client); + setup_socket(fd); + event_set(&client->ev_ssh, fd, EV_READ | EV_PERSIST, __on_ssh_client_event, client); event_add(&client->ev_ssh, NULL); client->has_init_conn_fd = true; From 27169b7c076376718abf98f7a1a6b484bd579314 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 5 Jun 2016 12:54:25 -0400 Subject: [PATCH 631/703] Add missing incldues for FreeBSD --- tmate-session.c | 1 + tmate-ssh-client.c | 1 + 2 files changed, 2 insertions(+) diff --git a/tmate-session.c b/tmate-session.c index b43a261b..8d647203 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -2,6 +2,7 @@ #include #include +#include #include #include diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 8b78f5cc..f13db90a 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -1,4 +1,5 @@ #include +#include #include #include #include From b27f3bacc0308a86a5f3e3494bd0383cd7583971 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Fri, 10 Jun 2016 18:00:50 -0400 Subject: [PATCH 632/703] Crash fix in search prev/next match Fixes #87 --- window-copy.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/window-copy.c b/window-copy.c index 3862a688..939d3e19 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1054,6 +1054,11 @@ window_copy_search_up(struct window_pane *wp, const char *searchstr) int n, wrapped, wrapflag, cis; const char *ptr; +#ifdef TMATE + if (!searchstr) + return; +#endif + if (*searchstr == '\0') return; wrapflag = options_get_number(wp->window->options, "wrap-search"); @@ -1120,6 +1125,11 @@ window_copy_search_down(struct window_pane *wp, const char *searchstr) int n, wrapped, wrapflag, cis; const char *ptr; +#ifdef TMATE + if (!searchstr) + return; +#endif + if (*searchstr == '\0') return; wrapflag = options_get_number(wp->window->options, "wrap-search"); From 608763a41a2437a8d3e0e54482d0b5f3d1c1e686 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 14 Jun 2016 13:46:15 -0400 Subject: [PATCH 633/703] Attempt to fix environment related crash Fixes #89 --- cmd-string.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd-string.c b/cmd-string.c index 757d4cdb..fdef4a82 100644 --- a/cmd-string.c +++ b/cmd-string.c @@ -307,6 +307,10 @@ cmd_string_variable(const char *s, size_t *p) free(buf); if (envent == NULL) return (xstrdup("")); +#ifdef TMATE + if (envent->value == NULL) + return (xstrdup("")); +#endif return (xstrdup(envent->value)); error: From 3f6c6d44472a5ccab0403ebf89fc59e7df58b6b2 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Thu, 28 Sep 2017 10:28:42 +0000 Subject: [PATCH 634/703] Fix building with or without backtrace(3) backtrace(3) and execinfo.h are GNU extensions and may or may not be available, and they may be provided via libexecinfo. Fix detection of libexecinfo and allow building without any support of backtrace, in which case we let kernel create core dump. Fixes #116 #117 --- configure.ac | 5 +++++ tmate-debug.c | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/configure.ac b/configure.ac index 02d2872b..05c1c482 100644 --- a/configure.ac +++ b/configure.ac @@ -102,6 +102,7 @@ AC_CHECK_HEADERS( bitstring.h \ curses.h \ dirent.h \ + execinfo.h \ fcntl.h \ inttypes.h \ libutil.h \ @@ -121,9 +122,13 @@ AC_CHECK_HEADERS( # Look for library needed for flock. AC_SEARCH_LIBS(flock, bsd) +# Look for library needed for backtrace +AC_SEARCH_LIBS(backtrace, execinfo) + # Check for some functions that are replaced or omitted. AC_CHECK_FUNCS( [ \ + backtrace \ dirfd \ flock \ setproctitle \ diff --git a/tmate-debug.c b/tmate-debug.c index f1fd971f..f58efe0e 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -1,10 +1,19 @@ +#ifdef HAVE_EXECINFO_H #include +#endif #include #include #include #include #include "tmate.h" +#ifndef HAVE_BACKTRACE + +void tmate_print_stack_trace(void) {} +void tmate_catch_sigsegv(void) {} + +#else + #if DEBUG static int print_resolved_stack_frame(const char *frame) @@ -88,3 +97,4 @@ void tmate_catch_sigsegv(void) { signal(SIGSEGV, handle_sigsegv); } +#endif From 25f6a934cf498e32bd5e13ea9e03b6840ab7064d Mon Sep 17 00:00:00 2001 From: David Bishop Date: Tue, 23 Oct 2018 17:39:10 -0600 Subject: [PATCH 635/703] Update MAN page to properly reference tmate where applicable While there are still plenty of places where tmate uses tmux, including config files and environment variables, I have updated the various examples to use tmate where applicable. --- tmux.1 | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/tmux.1 b/tmux.1 index 776b6faf..b55b3682 100644 --- a/tmux.1 +++ b/tmux.1 @@ -15,13 +15,13 @@ .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: March 25 2013 $ -.Dt TMUX 1 +.Dt TMATE 1 .Os .Sh NAME -.Nm tmux +.Nm tmate .Nd terminal multiplexer .Sh SYNOPSIS -.Nm tmux +.Nm tmate .Bk -words .Op Fl 2CluvV .Op Fl c Ar shell-command @@ -80,7 +80,7 @@ key strokes). .Nm may be reattached using: .Pp -.Dl $ tmux attach +.Dl $ tmate attach .Pp In .Nm , @@ -126,7 +126,9 @@ By default, loads the system configuration file from .Pa @SYSCONFDIR@/tmux.conf , if present, then looks for a user configuration file at -.Pa ~/.tmux.conf . +.Pa ~/.tmux.conf +and +.Pa ~/.tmate.conf . .Pp The configuration file is a set of .Nm @@ -167,7 +169,9 @@ directories are missing). .It Fl l Behave as a login shell. This flag currently has no effect and is for compatibility with other shells -when using tmux as a login shell. +when using +.Nm +as a login shell. .It Fl S Ar socket-path Specify a full alternative path to the server socket. If @@ -597,7 +601,7 @@ to be given as multiple arguments and executed directly (without This can avoid issues with shell quoting. For example: .Bd -literal -offset indent -$ tmux new-window vi /etc/passwd +$ tmate new-window vi /etc/passwd .Ed .Pp Will run @@ -616,7 +620,7 @@ bind-key F1 set-window-option force-width 81 Or if using .Xr sh 1 : .Bd -literal -offset indent -$ tmux bind-key F1 set-window-option force-width 81 +$ tmate bind-key F1 set-window-option force-width 81 .Ed .Pp Multiple commands may be specified together as part of a @@ -648,11 +652,11 @@ bind-key R source-file ~/.tmux.conf \e; \e Or from .Xr sh 1 : .Bd -literal -offset indent -$ tmux kill-window -t :1 +$ tmate kill-window -t :1 -$ tmux new-window \e; split-window -d +$ tmate new-window \e; split-window -d -$ tmux new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach +$ tmate new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach .Ed .Sh CLIENTS AND SESSIONS The @@ -1236,10 +1240,10 @@ command displays the layout of each window in a form suitable for use with .Ic select-layout . For example: .Bd -literal -offset indent -$ tmux list-windows +$ tmate list-windows 0: ksh [159x48] layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} -$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} +$ tmate select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} .Ed .Pp .Nm @@ -2307,8 +2311,8 @@ User options may have any name, so long as they are prefixed with and be set to any string. For example: .Bd -literal -offset indent -$ tmux setw -q @foo "abc123" -$ tmux showw -v @foo +$ tmate setw -q @foo "abc123" +$ tmate showw -v @foo abc123 .Ed .Pp @@ -2394,7 +2398,8 @@ to work correctly, this .Em must be set to .Ql screen , -.Ql tmux +.Ql tmux , +.Ql tmate or a derivative of them. .It Ic escape-time Ar time Set the time in milliseconds for which @@ -4172,18 +4177,18 @@ To create a new session running .Xr vi 1 : .Pp -.Dl $ tmux new-session vi +.Dl $ tmate new-session vi .Pp Most commands have a shorter form, known as an alias. For new-session, this is .Ic new : .Pp -.Dl $ tmux new vi +.Dl $ tmate new vi .Pp Alternatively, the shortest unambiguous form of a command is accepted. If there are several options, they are listed: .Bd -literal -offset indent -$ tmux n +$ tmate n ambiguous command: n, could be: new-session, new-window, next-window .Ed .Pp @@ -4213,7 +4218,7 @@ A session may be detached using .Xr ssh 1 disconnection) and reattached with: .Pp -.Dl $ tmux attach-session +.Dl $ tmate attach-session .Pp Typing .Ql C-b \&? From 2ffcbbd18508d6fdb28660ee2e8826acc5899a12 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 20 Mar 2019 19:05:49 +0100 Subject: [PATCH 636/703] ssh-client: Use SHA256 finger prints Signed-off-by: Andreas Schneider --- options-table.c | 4 ++-- tmate-ssh-client.c | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/options-table.c b/options-table.c index 6b0c248b..90d3297b 100644 --- a/options-table.c +++ b/options-table.c @@ -925,13 +925,13 @@ const struct options_table_entry options_table[] = { { .name = "tmate-server-rsa-fingerprint", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "af:2d:81:c1:fe:49:70:2d:7f:09:a9:d7:4b:32:e3:be" + .default_str = "SHA256:Hthk2T/M/Ivqfk1YYUn5ijC2Att3+UPzD7Rn72P5VWs" }, { .name = "tmate-server-ecdsa-fingerprint", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "c7:a1:51:36:d2:bb:35:4b:0a:1a:c0:43:97:74:ea:42" + .default_str = "SHA256:8GmKHYHEJ6n0TEdciHeEGkKOigQfCFuBULdt6vZIhDc" }, { .name = "tmate-display-time", diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index f13db90a..fbe9a470 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -287,12 +287,14 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) if (ssh_get_publickey(session, &pubkey) < 0) tmate_fatal("ssh_get_publickey"); - if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hash_len) < 0) { + if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, + &hash, &hash_len) < 0) { kill_ssh_client(client, "Cannot authenticate server"); return; } - hash_str = ssh_get_hexa(hash, hash_len); + hash_str = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256, + hash, hash_len); if (!hash_str) tmate_fatal("malloc failed"); From b645ce15cb1b64183a002e82112f2ac4830cd693 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 20 Mar 2019 19:06:18 +0100 Subject: [PATCH 637/703] ssh-client: Add support for ed25519 keys Signed-off-by: Andreas Schneider --- options-table.c | 6 ++++++ tmate-ssh-client.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/options-table.c b/options-table.c index 90d3297b..a44f9445 100644 --- a/options-table.c +++ b/options-table.c @@ -934,6 +934,12 @@ const struct options_table_entry options_table[] = { .default_str = "SHA256:8GmKHYHEJ6n0TEdciHeEGkKOigQfCFuBULdt6vZIhDc" }, + { .name = "tmate-server-ed25519-fingerprint", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + { .name = "tmate-display-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index fbe9a470..bdad138d 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -309,6 +309,10 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) server_hash_str = options_get_string(global_options, "tmate-server-ecdsa-fingerprint"); break; + case SSH_KEYTYPE_ED25519: + server_hash_str = options_get_string(global_options, + "tmate-server-ed25519-fingerprint"); + break; default: server_hash_str = ""; } From b01c6ecebde69726b330c3d2c5ba0fa757dc6447 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 20 Mar 2019 19:12:33 +0100 Subject: [PATCH 638/703] configure: Require libssh >= 0.8.4 Signed-off-by: Andreas Schneider --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 05c1c482..8c6c5b98 100644 --- a/configure.ac +++ b/configure.ac @@ -214,7 +214,7 @@ fi PKG_CHECK_MODULES( LIBSSH, - libssh >= 0.6.0, + libssh >= 0.8.4, [ CPPFLAGS="$LIBSSH_CFLAGS $CPPFLAGS" LIBS="$LIBSSH_LIBS $LIBS" @@ -223,7 +223,7 @@ PKG_CHECK_MODULES( found_libssh=no ) if test "x$found_libssh" = xno; then - AC_MSG_ERROR("libssh >= 0.6.0 not found") + AC_MSG_ERROR("libssh >= 0.8.4 not found") fi # Check for b64_ntop. From 32d48cbc9df07045af43e0e8d76bd7a58647cce1 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 7 Apr 2019 10:20:22 -0400 Subject: [PATCH 639/703] Update ed25519 server key (not yet in production) --- options-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index a44f9445..4966431c 100644 --- a/options-table.c +++ b/options-table.c @@ -937,7 +937,7 @@ const struct options_table_entry options_table[] = { { .name = "tmate-server-ed25519-fingerprint", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, - .default_str = "" + .default_str = "SHA256:jfttvoypkHiQYUqUCwKeqd9d1fJj/ZiQlFOHVl6E9sI" }, { .name = "tmate-display-time", From fd4ac27d59604aae0117ad578b1a7e6dccf97bf7 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 20 Mar 2019 19:17:46 +0100 Subject: [PATCH 640/703] ssh-client: Don't use keys from the ssh-agent Fixes #138 Signed-off-by: Andreas Schneider --- tmate-ssh-client.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index bdad138d..96da14c8 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -260,6 +261,9 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) * regular one doesn't. */ ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity); + + /* Do not use keys from ssh-agent. */ + unsetenv("SSH_AUTH_SOCK"); free(identity); } From 299c7c670c3d56ad6eaf73618dcf0c3c5d6fa2ec Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Tue, 30 Jul 2019 08:15:59 +0200 Subject: [PATCH 641/703] add new channel after authentication With libssh commit 8a885f0b ("channels: Add check if we are authenticated before we create a channel") connection fails if channel is added before successful authentication. So add the channel after authentication. Fixes #154 --- tmate-ssh-client.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 96da14c8..2a9afee0 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -241,12 +241,6 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) ssh_set_callbacks(session, &client->ssh_callbacks); - client->channel = channel = ssh_channel_new(session); - if (!channel) { - tmate_fatal("cannot initialize"); - return; - } - ssh_set_blocking(session, 0); ssh_options_set(session, SSH_OPTIONS_HOST, client->server_ip); ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); @@ -370,6 +364,12 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_AUTH_SUCCESS: tmate_debug("Auth successful"); client->state = SSH_OPEN_CHANNEL; + + client->channel = channel = ssh_channel_new(session); + if (!channel) { + tmate_fatal("cannot initialize"); + return; + } /* fall through */ } From e25ab3cc8bb4d42b137625b376d677707bc4fbe2 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Tue, 30 Jul 2019 14:48:37 +0200 Subject: [PATCH 642/703] ssh-client: Add missing ecdsa keytypes of libssh 0.9 --- tmate-ssh-client.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 2a9afee0..943ebf49 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -218,7 +218,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) { char *identity; ssh_key pubkey; - int key_type; + enum ssh_keytypes_e key_type; unsigned char *hash; ssize_t hash_len; char *hash_str; @@ -304,6 +304,11 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) "tmate-server-rsa-fingerprint"); break; case SSH_KEYTYPE_ECDSA: +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0) + case SSH_KEYTYPE_ECDSA_P256: + case SSH_KEYTYPE_ECDSA_P384: + case SSH_KEYTYPE_ECDSA_P521: +#endif server_hash_str = options_get_string(global_options, "tmate-server-ecdsa-fingerprint"); break; From 4e7caeb536629fc4ddc4d413ca867f754b1257c3 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Tue, 30 Jul 2019 15:37:35 +0200 Subject: [PATCH 643/703] ssh-client: Use ssh_get_server_publickey() if possible --- tmate-ssh-client.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 943ebf49..c5fb4243 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -282,8 +282,13 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) } case SSH_AUTH_SERVER: +#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0) + if (ssh_get_server_publickey(session, &pubkey) < 0) + tmate_fatal("ssh_get_server_publickey"); +#else if (ssh_get_publickey(session, &pubkey) < 0) tmate_fatal("ssh_get_publickey"); +#endif if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, &hash, &hash_len) < 0) { From 3e5d919b14e8f5c6aa703e45092247c4a737194e Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 31 Jul 2019 21:02:18 -0400 Subject: [PATCH 644/703] Bump to version 2.3.0 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 8c6c5b98..647ae333 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT(tmate, 2.2.1) +AC_INIT(tmate, 2.3.0) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) From d654ff22194bde5fc6e02b2f93ae55cb69c4ddcc Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 18 Sep 2019 23:35:07 -0400 Subject: [PATCH 645/703] Send TMATE_READY message when the config files are done loading This fixes bug where webhooks are not registering correctly, leading to a disconnect and reconnect. --- server.c | 4 ---- tmate-session.c | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/server.c b/server.c index 81334f1e..a2378959 100644 --- a/server.c +++ b/server.c @@ -205,10 +205,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) status_prompt_load_history(); -#ifdef TMATE - tmate_write_ready(); -#endif - server_add_accept(0); proc_loop(server_proc, server_loop); diff --git a/tmate-session.c b/tmate-session.c index 8d647203..106687e1 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -138,6 +138,7 @@ void tmate_session_start(void) * - While we are parsing the config file, we need to be able to * serialize it, and so we need a worker encoder. */ + tmate_write_ready(); lookup_and_connect(); } From 44635e752d17993943885c1e77864f5395d740f6 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 12 Oct 2019 13:50:27 -0400 Subject: [PATCH 646/703] Use docker to build static releases Static builds are possible with the musl C library. glibc is capricious when it comes to static linking and DNS support (and other things). --- Dockerfile | 27 +++++++++++++++++++ Makefile.static-build | 61 ------------------------------------------ compat/clock_gettime.c | 9 ------- compat/memcpy.c | 11 -------- configure.ac | 15 ++--------- 5 files changed, 29 insertions(+), 94 deletions(-) create mode 100644 Dockerfile delete mode 100644 Makefile.static-build delete mode 100644 compat/clock_gettime.c delete mode 100644 compat/memcpy.c diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..82d75ef4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM alpine:3.10 + +WORKDIR /build + +RUN apk add --no-cache wget cmake make gcc g++ linux-headers zlib-dev openssl-dev \ + automake autoconf libevent-dev ncurses-dev msgpack-c-dev libexecinfo-dev \ + ncurses-static libexecinfo-static libevent-static msgpack-c ncurses-libs \ + libevent libexecinfo openssl zlib + +RUN set -ex; \ + mkdir -p /src/libssh/build; \ + cd /src; \ + wget -O libssh.tar.xz https://www.libssh.org/files/0.9/libssh-0.9.0.tar.xz; \ + tar -xf libssh.tar.xz -C /src/libssh --strip-components=1; \ + cd /src/libssh/build; \ + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ + -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF \ + -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF ..; \ + make -j $(nproc); \ + make install + +COPY . . + +RUN ./autogen.sh +RUN ./configure --enable-static +RUN make -j $(nproc) +RUN strip tmate diff --git a/Makefile.static-build b/Makefile.static-build deleted file mode 100644 index 9bc83d92..00000000 --- a/Makefile.static-build +++ /dev/null @@ -1,61 +0,0 @@ -LIBSSH=libssh-0.7.2 -LIBSSH_URL=https://red.libssh.org/attachments/download/177/$(LIBSSH).tar.xz -LIBSSH_LIB=ext/lib/libssh.a - -MSGPACK=msgpack-1.3.0 -MSGPACK_URL=https://github.com/msgpack/msgpack-c/releases/download/cpp-1.3.0/$(MSGPACK).tar.gz -MSGPACK_LIB=ext/lib/libmsgpack.a - -TMATE_CONFIGURE=PKG_CONFIG_PATH=./ext/lib/pkgconfig - -LIBC=$(shell gcc -print-file-name=libc.a) -STATIC_LIBC_OBJECTS=fdelt_chk -STATIC_COMPAT_OBJECTS=memcpy clock_gettime - -all: tmate - -dependencies: - apt-get install build-essential cmake libssl-dev autoconf automake pkg-config libtool libevent-dev libncurses-dev zlib1g-dev - -downloads/$(notdir $(LIBSSH_URL)): - mkdir -p downloads - wget -O $@ $(LIBSSH_URL) - -$(LIBSSH)/.ready: downloads/$(notdir $(LIBSSH_URL)) - tar xf $< - touch $@ - -downloads/$(notdir $(MSGPACK_URL)): - mkdir -p downloads - wget -O $@ $(MSGPACK_URL) - -$(MSGPACK)/.ready: downloads/$(notdir $(MSGPACK_URL)) - tar xf $< - touch $@ - -$(LIBSSH_LIB): $(LIBSSH)/.ready - mkdir -p $(LIBSSH)/build - cd $(LIBSSH)/build; ([ -f Makefile ] || cmake -DCMAKE_INSTALL_PREFIX:PATH=$(shell pwd)/ext .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF) - +make -C $(LIBSSH)/build install - -$(MSGPACK_LIB): $(MSGPACK)/.ready - mkdir -p $(MSGPACK)/build - cd $(MSGPACK)/build; ([ -f Makefile ] || cmake -DCMAKE_INSTALL_PREFIX:PATH=$(shell pwd)/ext ..) - +make -C $(MSGPACK)/build install - -libc/%.o: - mkdir -p libc - cd libc; ar x $(LIBC) $(notdir $@) - -compat/%.o: compat/%.c - gcc -c -o $@ $< - -tmate: $(MSGPACK_LIB) $(LIBSSH_LIB) $(patsubst %,libc/%.o,$(STATIC_LIBC_OBJECTS)) $(patsubst %,compat/%.o,$(STATIC_COMPAT_OBJECTS)) - ./autogen.sh - $(TMATE_CONFIGURE) ./configure --enable-static - +make - strip tmate - -clean: - rm -rf ext libc $(LIBSSH) $(MSGPACK) - +make clean diff --git a/compat/clock_gettime.c b/compat/clock_gettime.c deleted file mode 100644 index 0b631b79..00000000 --- a/compat/clock_gettime.c +++ /dev/null @@ -1,9 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include - -int clock_gettime(clockid_t clk_id, struct timespec *tp) -{ - return syscall(SYS_clock_gettime, clk_id, tp); -} diff --git a/compat/memcpy.c b/compat/memcpy.c deleted file mode 100644 index 37092a33..00000000 --- a/compat/memcpy.c +++ /dev/null @@ -1,11 +0,0 @@ -// http://stackoverflow.com/questions/8823267/linking-against-older-symbol-version-in-a-so-file - -#include - -/* some systems do not have newest memcpy@@GLIBC_2.14 - stay with old good one */ -asm (".symver memcpy, memcpy@GLIBC_2.2.5"); - -void *__wrap_memcpy(void *dest, const void *src, size_t n) -{ - return memcpy(dest, src, n); -} diff --git a/configure.ac b/configure.ac index 647ae333..2681300a 100644 --- a/configure.ac +++ b/configure.ac @@ -47,10 +47,11 @@ AC_ARG_ENABLE( found_static=$enable_static ) if test "x$found_static" = xyes; then + # XXX Static build are only doable with the musl library PKG_CONFIG="pkg-config --static" CFLAGS="$CFLAGS -flto" - LDFLAGS="$LDFLAGS -flto" + LDFLAGS="$LDFLAGS -flto -static -no-pie" PKG_CHECK_MODULES([ZLIB], [zlib], [ CPPFLAGS="$ZLIB_CFLAGS $CPPFLAGS" @@ -61,7 +62,6 @@ if test "x$found_static" = xyes; then CPPFLAGS="$LIBCRYPTO_CFLAGS $CPPFLAGS" LIBS="$LIBCRYPTO_LIBS $LIBS" ]) - # See more static settings below... (search for found_static) fi # Is this gcc? @@ -462,17 +462,6 @@ if test "x$found_getopt" != xno; then fi AM_CONDITIONAL(NO_GETOPT, [test "x$found_getopt" = xno]) -if test "x$found_static" = xyes; then - # libc and libdl should be dynamically linked. - # but we want to lower our requirements for libc's version. - # so we extract some symobls and add them here - if test `uname -m` = x86_64; then - LIBS="compat/memcpy.o -Wl,--wrap=memcpy $LIBS" - fi - LIBS="compat/clock_gettime.o libc/fdelt_chk.o $LIBS" - LIBS="-Wl,-Bstatic ${LIBS/-ldl/} -Wl,-Bdynamic -ldl" -fi - # Check for BSD-style integer types. AC_MSG_CHECKING(for BSD-style unsigned types) AC_COMPILE_IFELSE([AC_LANG_SOURCE( From 74ff5229830afcca8cd57976a48632fef6f85c17 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 12 Oct 2019 14:13:12 -0400 Subject: [PATCH 647/703] Build static build releases on travis-ci --- .travis.yml | 42 ++++++++++++++++++++++++++++++++++-------- Dockerfile | 27 ++++++++++++++------------- package_release.sh | 14 ++++++++++++++ 3 files changed, 62 insertions(+), 21 deletions(-) create mode 100755 package_release.sh diff --git a/.travis.yml b/.travis.yml index a1d7e427..a02e89a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,36 @@ language: c +services: +- docker + matrix: - include: - - compiler: gcc - - compiler: clang - env: CFLAGS="-g -O2" -before_install: - - sudo apt-get update -qq - - sudo apt-get -y install debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential -script: (CFLAGS= ./autogen.sh) && ./configure --enable-debug && make + include: + - arch: amd64 + env: PLATFORM=amd64 + - arch: amd64 + env: PLATFORM=i386 + - arch: arm64 + env: PLATFORM=arm32v6 + - arch: arm64 + env: PLATFORM=arm32v7 + - arch: arm64 + env: PLATFORM=arm64v8 + +script: +- 'docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM' +# On arch=arm64, some directories are not setup correctly, and 'ruby -S gem +# install dpl' required by the release push scripts fails. +- 'if [ "$TRAVIS_TAG" ]; then sudo chown -R $USER: /var/lib/gems /usr/local/bin; fi' +- 'if [ "$TRAVIS_TAG" ]; then ./package_release.sh $TRAVIS_TAG $PLATFORM; fi' + +deploy: + provider: releases + api_key: + secure: T2109tjjOsrVLEpJZK/uxmO0AuDGXYFdN4AAsNTmVwu/W5dcX57Kk2TCgqDuLfD21iGGXP0U/OYHM06IfBDODBWCA9P8ASHYsenS7wIiFnvCEMbfzoAFyBMrXN2kNdM2+ho3aqc0xE2lQKOKDLxpGm5FZrzujscXXzxQjWBU5Hk= + skip_cleanup: true + overwrite: true + file_glob: true + file: /tmp/tmate-release/*.tar.* + on: + repo: tmate-io/tmate + branch: master + tags: true diff --git a/Dockerfile b/Dockerfile index 82d75ef4..c7aa2b9f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM alpine:3.10 +ARG PLATFORM=amd64 +FROM ${PLATFORM}/alpine:3.10 WORKDIR /build @@ -8,20 +9,20 @@ RUN apk add --no-cache wget cmake make gcc g++ linux-headers zlib-dev openssl-de libevent libexecinfo openssl zlib RUN set -ex; \ - mkdir -p /src/libssh/build; \ - cd /src; \ - wget -O libssh.tar.xz https://www.libssh.org/files/0.9/libssh-0.9.0.tar.xz; \ - tar -xf libssh.tar.xz -C /src/libssh --strip-components=1; \ - cd /src/libssh/build; \ - cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ - -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF \ - -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF ..; \ - make -j $(nproc); \ - make install + mkdir -p /src/libssh/build; \ + cd /src; \ + wget -O libssh.tar.xz https://www.libssh.org/files/0.9/libssh-0.9.0.tar.xz; \ + tar -xf libssh.tar.xz -C /src/libssh --strip-components=1; \ + cd /src/libssh/build; \ + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ + -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF \ + -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF ..; \ + make -j $(nproc); \ + make install COPY . . -RUN ./autogen.sh -RUN ./configure --enable-static +RUN ./autogen.sh && ./configure --enable-static RUN make -j $(nproc) RUN strip tmate +RUN ./tmate -V diff --git a/package_release.sh b/package_release.sh new file mode 100755 index 00000000..f4186e3c --- /dev/null +++ b/package_release.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -eux +VERSION=$1 +PLATFORM=$2 +RELEASE_NAME=tmate-$VERSION-static-linux-$PLATFORM + +# This assumes the follow command has already been run: +# docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM + +mkdir -p /tmp/tmate-release/$RELEASE_NAME +cd /tmp/tmate-release +docker run --rm local-$PLATFORM/tmate-build cat tmate > $RELEASE_NAME/tmate +chmod +x $RELEASE_NAME/tmate +tar -cf - $RELEASE_NAME | xz > tmate-$VERSION-static-linux-$PLATFORM.tar.xz From 7153958e9908658383438745592d9a8da64b2a3f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 12 Oct 2019 21:09:39 -0400 Subject: [PATCH 648/703] Allow the use of C.UTF-8 locale --- tmux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tmux.c b/tmux.c index d55c2e1d..d7cf8d97 100644 --- a/tmux.c +++ b/tmux.c @@ -205,12 +205,12 @@ main(int argc, char **argv) const char *s; int opt, flags, keys; - if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { + if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && + setlocale(LC_CTYPE, "C.UTF-8") == NULL) { if (setlocale(LC_CTYPE, "") == NULL) errx(1, "invalid LC_ALL, LC_CTYPE or LANG"); s = nl_langinfo(CODESET); - if (strcasecmp(s, "UTF-8") != 0 && - strcasecmp(s, "UTF8") != 0) + if (strcasecmp(s, "UTF-8") != 0 && strcasecmp(s, "UTF8") != 0) errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s); } From 7262aead737af678923ae7b9209ae220b0f915b9 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 12 Oct 2019 19:29:48 -0400 Subject: [PATCH 649/703] Bump to version 2.3.1 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 2681300a..f623cadc 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT(tmate, 2.3.0) +AC_INIT(tmate, 2.3.1) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) From 4efe25d91d5c5de0fcb4df2cf9199a96b13afd66 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 15 Oct 2019 03:04:24 -0400 Subject: [PATCH 650/703] During SSH authentication, try the none auth method first --- tmate-ssh-client.c | 44 ++++++++++++++++++++++++++++++++------------ tmate.h | 4 +++- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index c5fb4243..e33f4b17 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -235,7 +235,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_INIT: client->session = session = ssh_new(); if (!session) { - tmate_fatal("cannot initialize"); + tmate_fatal("cannot ssh_new()"); return; } @@ -344,13 +344,30 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) */ tmate_debug("Connected to %s", client->server_ip); on_ssh_auth_server_complete(client); - client->state = SSH_AUTH_CLIENT; + client->state = SSH_AUTH_CLIENT_NONE; /* fall through */ - case SSH_AUTH_CLIENT: + case SSH_AUTH_CLIENT_NONE: + switch (ssh_userauth_none(session, NULL)) { + case SSH_AUTH_AGAIN: + return; + case SSH_AUTH_ERROR: + kill_ssh_client(client, "Auth error: %s", ssh_get_error(session)); + return; + case SSH_AUTH_SUCCESS: + tmate_debug("Auth successful via none method"); + client->state = SSH_NEW_CHANNEL; + goto SSH_NEW_CHANNEL; + case SSH_AUTH_PARTIAL: + case SSH_AUTH_DENIED: + client->state = SSH_AUTH_CLIENT_PUBKEY; + /* fall through */ + } + + case SSH_AUTH_CLIENT_PUBKEY: client->tried_passphrase = client->tmate_session->passphrase; - switch (ssh_userauth_autopubkey(session, client->tried_passphrase)) { + switch (ssh_userauth_publickey_auto(session, NULL, client->tried_passphrase)) { case SSH_AUTH_AGAIN: return; case SSH_AUTH_PARTIAL: @@ -372,17 +389,20 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) kill_ssh_client(client, "Auth error: %s", ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: - tmate_debug("Auth successful"); - client->state = SSH_OPEN_CHANNEL; - - client->channel = channel = ssh_channel_new(session); - if (!channel) { - tmate_fatal("cannot initialize"); - return; - } + tmate_debug("Auth successful with pubkey"); + client->state = SSH_NEW_CHANNEL; /* fall through */ } +SSH_NEW_CHANNEL: + case SSH_NEW_CHANNEL: + client->channel = channel = ssh_channel_new(session); + if (!channel) { + tmate_fatal("cannot ssh_channel_new()"); + return; + } + client->state = SSH_OPEN_CHANNEL; + case SSH_OPEN_CHANNEL: switch (ssh_channel_open_session(channel)) { case SSH_AGAIN: diff --git a/tmate.h b/tmate.h index f096a35c..7deb0ba6 100644 --- a/tmate.h +++ b/tmate.h @@ -106,7 +106,9 @@ enum tmate_ssh_client_state_types { SSH_INIT, SSH_CONNECT, SSH_AUTH_SERVER, - SSH_AUTH_CLIENT, + SSH_AUTH_CLIENT_NONE, + SSH_AUTH_CLIENT_PUBKEY, + SSH_NEW_CHANNEL, SSH_OPEN_CHANNEL, SSH_BOOTSTRAP, SSH_READY, From 7f693a97aede385aad30db1412c48f0093b72509 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 20 Oct 2019 21:23:48 -0400 Subject: [PATCH 651/703] Add options for customizing session names (WIP) --- options-table.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/options-table.c b/options-table.c index 4966431c..617ba05e 100644 --- a/options-table.c +++ b/options-table.c @@ -959,6 +959,24 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, + + { .name = "tmate-account-key", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-session-name", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-session-name-ro", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, #endif { .name = NULL } From 6e84bab68ca80f90023d4336467a44098d17a06f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 3 Nov 2019 05:11:37 -0500 Subject: [PATCH 652/703] Add foreground mode with -F --- cfg.c | 18 ++++++++++++++++ client.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ cmd-new-session.c | 3 +++ log.c | 49 +++++++++++++++++++++++++++++++------------- proc.c | 10 +++++++-- server.c | 14 ++++++++++--- session.c | 21 +++++++++++++++---- signal.c | 18 ++++++++++++++++ tmate-msg.c | 2 +- tmate-session.c | 2 +- tmate-ssh-client.c | 14 +++++++------ tmate.h | 7 +++---- tmux.c | 13 ++++++++++-- tmux.h | 12 ++++++++++- 14 files changed, 196 insertions(+), 38 deletions(-) diff --git a/cfg.c b/cfg.c index 572f8369..68894cb2 100644 --- a/cfg.c +++ b/cfg.c @@ -149,6 +149,20 @@ load_cfg(const char *path, struct cmd_q *cmdq, char **cause) return (found); } +static void print_cfg_errors(void) +{ + u_int i; + + for (i = 0; i < cfg_ncauses; i++) { + tmate_info("%s", cfg_causes[i]); + free(cfg_causes[i]); + } + + free(cfg_causes); + cfg_causes = NULL; + cfg_ncauses = 0; +} + void cfg_default_done(__unused struct cmd_q *cmdq) { @@ -158,6 +172,10 @@ cfg_default_done(__unused struct cmd_q *cmdq) #ifdef TMATE tmate_session_start(); + if (tmate_foreground && cfg_ncauses) { + print_cfg_errors(); + exit(1); + } #endif if (!RB_EMPTY(&sessions)) diff --git a/client.c b/client.c index 164abdd3..ad349972 100644 --- a/client.c +++ b/client.c @@ -32,6 +32,7 @@ #include #include "tmux.h" +#include "tmate.h" struct tmuxproc *client_proc; struct tmuxpeer *client_peer; @@ -213,8 +214,56 @@ client_exit_message(void) #ifdef TMATE extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_new_session_entry; + +/* For foreground mode */ +static int __argc; +static char **__argv; #endif +static void _run_initial_client_cmd(int argc, char **argv) +{ + struct cmd_q *cmd_q; + struct cmd_list *cmdlist; + char *cause; + const char *default_argv[] = {"new-session"}; + + if (argc == 0) { + argc = 1; + argv = (char **)default_argv; + } + + cmd_q = cmdq_new(NULL); /* No client */ + + if ((cmdlist = cmd_list_parse(argc, (char **)argv, NULL, 0, &cause)) == NULL) { + tmate_fatal("%s", cause); + } + + cmdq_run(cmd_q, cmdlist, NULL); + cmd_list_free(cmdlist); + cmdq_free(cmd_q); + + /* error messages land in cfg_causes */ + extern char **cfg_causes; + extern u_int cfg_ncauses; + int has_error = !!cfg_ncauses; + for (u_int i = 0; i < cfg_ncauses; i++) { + tmate_info("%s", cfg_causes[i]); + free(cfg_causes[i]); + } + + free(cfg_causes); + cfg_causes = NULL; + cfg_ncauses = 0; + + if (has_error) + exit(1); +} + +void run_initial_client_cmd(void) +{ + _run_initial_client_cmd(__argc, __argv); +} + /* Client main loop. */ int client_main(struct event_base *base, int argc, char **argv, int flags, @@ -232,6 +281,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags, size_t size; #ifdef TMATE int cant_nest = 0; + __argc = argc; + __argv = argv; #endif /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ diff --git a/cmd-new-session.c b/cmd-new-session.c index 357ffed1..fb2f7eab 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -131,6 +131,9 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) if (c == NULL) detached = 1; + if (tmate_foreground) + detached = 1; + /* Is this client already attached? */ already_attached = 0; if (c != NULL && c->session != NULL) diff --git a/log.c b/log.c index 018348cf..bc798778 100644 --- a/log.c +++ b/log.c @@ -32,6 +32,11 @@ static int log_level; static void log_event_cb(int, const char *); static void log_vwrite(const char *, va_list); +static int is_log_stdout(void) +{ + return fileno(log_file) <= 2; +} + /* Log callback for libevent. */ static void log_event_cb(__unused int severity, const char *msg) @@ -53,6 +58,18 @@ log_get_level(void) return (log_level); } +void +log_open_fp(FILE *f) +{ + if (log_file != NULL && !is_log_stdout()) + fclose(log_file); + + log_file = f; + + setvbuf(log_file, NULL, _IOLBF, 0); + event_set_log_callback(log_event_cb); +} + /* Open logging to file. */ void log_open(const char *name) @@ -62,24 +79,18 @@ log_open(const char *name) if (log_level == 0) return; - if (log_file != NULL) - fclose(log_file); - xasprintf(&path, "tmate-%s-%ld.log", name, (long)getpid()); - log_file = fopen(path, "w"); + FILE *f = fopen(path, "w"); free(path); - if (log_file == NULL) - return; - - setvbuf(log_file, NULL, _IOLBF, 0); - event_set_log_callback(log_event_cb); + if (f) + log_open_fp(f); } /* Close logging. */ void log_close(void) { - if (log_file != NULL) + if (log_file != NULL && !is_log_stdout()) fclose(log_file); log_file = NULL; @@ -102,9 +113,16 @@ log_vwrite(const char *msg, va_list ap) exit(1); gettimeofday(&tv, NULL); - if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, - (int)tv.tv_usec, out) == -1) - exit(1); + + if (is_log_stdout()) { + if (fprintf(log_file, "%s\n", out) == -1) + exit(1); + } else { + if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, + (int)tv.tv_usec, out) == -1) + exit(1); + } + fflush(log_file); free(out); @@ -113,10 +131,13 @@ log_vwrite(const char *msg, va_list ap) /* Log a debug message. */ void -log_debug(const char *msg, ...) +log_emit(int level, const char *msg, ...) { va_list ap; + if (log_level < level) + return; + va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); diff --git a/proc.c b/proc.c index 27880f57..edd4a6f8 100644 --- a/proc.c +++ b/proc.c @@ -172,7 +172,7 @@ proc_start(const char *name, struct event_base *base, int forkflag, struct tmuxproc *tp; struct utsname u; - if (forkflag) { + if (forkflag && !tmate_foreground) { switch (fork()) { case -1: fatal("fork failed"); @@ -189,7 +189,13 @@ proc_start(const char *name, struct event_base *base, int forkflag, fatalx("event_reinit failed"); } - log_open(name); + if (tmate_foreground) { + if (forkflag) + clear_signals(0); + log_open_fp(stdout); + } else { + log_open(name); + } #ifdef HAVE_SETPROCTITLE setproctitle("%s (%s)", name, socket_path); diff --git a/server.c b/server.c index a2378959..05706769 100644 --- a/server.c +++ b/server.c @@ -152,14 +152,16 @@ server_start(struct event_base *base, int lockfd, char *lockfile) { int pair[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) - fatal("socketpair failed"); + if (!tmate_foreground) + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) + fatal("socketpair failed"); server_proc = proc_start("server", base, 1, server_signal); if (server_proc == NULL) { close(pair[1]); return (pair[0]); } + close(pair[0]); if (log_get_level() > 3) @@ -190,7 +192,8 @@ server_start(struct event_base *base, int lockfd, char *lockfile) if (server_fd == -1) fatal("couldn't create socket"); server_update_socket(); - server_client_create(pair[1]); + if (!tmate_foreground) + server_client_create(pair[1]); if (lockfd >= 0) { unlink(lockfile); @@ -207,11 +210,15 @@ server_start(struct event_base *base, int lockfd, char *lockfile) server_add_accept(0); + if (tmate_foreground) + run_initial_client_cmd(); + proc_loop(server_proc, server_loop); status_prompt_save_history(); #ifdef TMATE unlink(socket_path); #endif + exit(0); } @@ -361,6 +368,7 @@ server_signal(int sig) int fd; switch (sig) { + case SIGINT: case SIGTERM: server_exit = 1; server_send_exit(); diff --git a/session.c b/session.c index 5a3b867a..91fb9c1d 100644 --- a/session.c +++ b/session.c @@ -215,10 +215,6 @@ session_destroy(struct session *s) log_debug("session %s destroyed", s->name); -#ifdef TMATE - tmate_write_fin(); -#endif - RB_REMOVE(sessions, &sessions, s); notify_session_closed(s); @@ -240,6 +236,23 @@ session_destroy(struct session *s) free((void *)s->cwd); session_unref(s); + +#ifdef TMATE + if (tmate_foreground && !server_exit) { + tmate_info("Shell exited, restarting"); + /* + * throttle restarts. This is a blocking sleep. + * It's simpler than using a timer, but fairly harmless + * from a blocking perspective. + */ + usleep(500*1000); + next_session_id = 0; + run_initial_client_cmd(); + } else { + tmate_info("Session terminated"); + tmate_write_fin(); + } +#endif } /* Check a session name is valid: not empty and no colons or periods. */ diff --git a/signal.c b/signal.c index 19938638..f20a0257 100644 --- a/signal.c +++ b/signal.c @@ -24,6 +24,7 @@ #include "tmux.h" +struct event ev_sigint; struct event ev_sighup; struct event ev_sigchld; struct event ev_sigcont; @@ -40,15 +41,23 @@ set_signals(void (*handler)(int, short, void *), void *arg) sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_IGN; +#ifndef TMATE if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); +#endif if (sigaction(SIGPIPE, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR2, &sigact, NULL) != 0) fatal("sigaction failed"); +#ifndef TMATE if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); +#endif +#ifdef TMATE + signal_set(&ev_sigint, SIGINT, handler, arg); + signal_add(&ev_sigint, NULL); +#endif signal_set(&ev_sighup, SIGHUP, handler, arg); signal_add(&ev_sighup, NULL); signal_set(&ev_sigchld, SIGCHLD, handler, arg); @@ -72,16 +81,24 @@ clear_signals(int after_fork) sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_DFL; +#ifndef TMATE if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); +#endif if (sigaction(SIGPIPE, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR2, &sigact, NULL) != 0) fatal("sigaction failed"); +#ifndef TMATE if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); +#endif if (after_fork) { +#ifdef TMATE + if (sigaction(SIGINT, &sigact, NULL) != 0) + fatal("sigaction failed"); +#endif if (sigaction(SIGHUP, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGCHLD, &sigact, NULL) != 0) @@ -95,6 +112,7 @@ clear_signals(int after_fork) if (sigaction(SIGWINCH, &sigact, NULL) != 0) fatal("sigaction failed"); } else { + event_del(&ev_sigint); event_del(&ev_sighup); event_del(&ev_sigchld); event_del(&ev_sigcont); diff --git a/tmate-msg.c b/tmate-msg.c index 880e3905..a47f9e4d 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -56,7 +56,7 @@ void __tmate_status_message(const char *fmt, va_list ap) char *message; xvasprintf(&message, fmt, ap); - tmate_debug("%s", message); + tmate_info("%s", message); TAILQ_FOREACH(c, &clients, entry) { if (c && !(c->flags & CLIENT_READONLY)) diff --git a/tmate-session.c b/tmate-session.c index 106687e1..5ab13511 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -96,7 +96,7 @@ static void lookup_and_connect(void) tmate_server_host = options_get_string(global_options, "tmate-server-host"); - tmate_info("Looking up %s...", tmate_server_host); + tmate_debug("Looking up %s...", tmate_server_host); (void)evdns_getaddrinfo(tmate_session.ev_dnsbase, tmate_server_host, NULL, &hints, dns_cb, (void *)tmate_server_host); } diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index e33f4b17..66eb1716 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -171,12 +171,12 @@ static void request_passphrase(struct tmate_ssh_client *client) #define KEEPALIVE_IDLE 20 #define KEEPALIVE_INTVL 10 -static void setup_socket(int fd) +static void tune_socket_opts(int fd) { #define SSO(level, optname, val) ({ \ int _flag = val; \ if (setsockopt(fd, level, optname, &(_flag), sizeof(int)) < 0) { \ - tmate_warn("setsockopt(" #level ", " #optname ", %d) failed", val); \ + tmate_debug("setsockopt(" #level ", " #optname ", %d) failed", val); \ } \ }) @@ -197,7 +197,7 @@ static void setup_socket(int fd) #undef SSO } -static void init_conn_fd(struct tmate_ssh_client *client) +static void init_conn_fd(struct tmate_ssh_client *client, bool tune_socket) { int fd; @@ -207,7 +207,9 @@ static void init_conn_fd(struct tmate_ssh_client *client) if ((fd = ssh_get_fd(client->session)) < 0) return; - setup_socket(fd); + if (tune_socket) + tune_socket_opts(fd); + event_set(&client->ev_ssh, fd, EV_READ | EV_PERSIST, __on_ssh_client_event, client); event_add(&client->ev_ssh, NULL); @@ -267,14 +269,14 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_CONNECT: switch (ssh_connect(session)) { case SSH_AGAIN: - init_conn_fd(client); + init_conn_fd(client, false); return; case SSH_ERROR: kill_ssh_client(client, "Error connecting: %s", ssh_get_error(session)); return; case SSH_OK: - init_conn_fd(client); + init_conn_fd(client, true); tmate_debug("Establishing connection to %s", client->server_ip); client->state = SSH_AUTH_SERVER; diff --git a/tmate.h b/tmate.h index 7deb0ba6..9bf8e975 100644 --- a/tmate.h +++ b/tmate.h @@ -9,10 +9,9 @@ #include "tmux.h" -#define tmate_debug(...) log_debug("[tmate] D " __VA_ARGS__) -#define tmate_warn(...) log_debug("[tmate] W " __VA_ARGS__) -#define tmate_info(...) log_debug("[tmate] I " __VA_ARGS__) -#define tmate_fatal(...) fatalx("[tmate] " __VA_ARGS__) +#define tmate_debug(...) log_emit(LOG_DEBUG, __VA_ARGS__) +#define tmate_info(...) log_emit(LOG_INFO, __VA_ARGS__) +#define tmate_fatal(...) fatalx( __VA_ARGS__) /* tmate-msgpack.c */ diff --git a/tmux.c b/tmux.c index d7cf8d97..60341f84 100644 --- a/tmux.c +++ b/tmux.c @@ -48,7 +48,11 @@ __dead void usage(void); static char *make_label(const char *); #ifndef HAVE___PROGNAME -char *__progname = (char *) "tmux"; +char *__progname = (char *) "tmate"; +#endif + +#ifdef TMATE +int tmate_foreground; #endif __dead void @@ -228,7 +232,7 @@ main(int argc, char **argv) #endif label = path = NULL; - while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) { + while ((opt = getopt(argc, argv, "2c:CdFf:lL:qS:uUVv")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; @@ -268,6 +272,11 @@ main(int argc, char **argv) case 'v': log_add_level(); break; + case 'F': + tmate_foreground = 1; + log_add_level(); + unsetenv("TMUX"); + break; default: usage(); } diff --git a/tmux.h b/tmux.h index 1ab784b7..3956e240 100644 --- a/tmux.h +++ b/tmux.h @@ -1549,6 +1549,9 @@ extern struct options *global_w_options; extern struct environ *global_environ; extern struct timeval start_time; extern const char *socket_path; +#ifdef TMATE +extern int tmate_foreground; +#endif const char *getshell(void); int checkshell(const char *); int areshell(const char *); @@ -1875,6 +1878,7 @@ void signal_waiting_clients(const char *name); void cmd_wait_for_flush(void); /* client.c */ +void run_initial_client_cmd(void); int client_main(struct event_base *, int, char **, int, const char *); /* key-bindings.c */ @@ -1902,6 +1906,7 @@ void alerts_queue(struct window *, int); void alerts_check_session(struct session *); /* server.c */ +extern int server_exit; extern struct tmuxproc *server_proc; extern struct clients clients; extern struct cmd_find_state marked_pane; @@ -2356,9 +2361,14 @@ struct event_base *osdep_event_init(void); /* log.c */ void log_add_level(void); int log_get_level(void); +void log_open_fp(FILE *f); void log_open(const char *); void log_close(void); -void printflike(1, 2) log_debug(const char *, ...); +#define LOG_ERROR 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 +#define log_debug(...) log_emit(LOG_DEBUG+1, __VA_ARGS__) +void printflike(2, 3) log_emit(int level, const char *, ...); __dead void printflike(1, 2) fatal(const char *, ...); __dead void printflike(1, 2) fatalx(const char *, ...); From c63c8fbf90ffdf7bc58a8f73428600ee29a6dcf5 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 3 Nov 2019 18:15:27 -0500 Subject: [PATCH 653/703] Only use tmate.conf, not .tmux.conf Fixes #108 --- cfg.c | 16 +--------------- session.c | 2 +- tmux.c | 3 +-- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/cfg.c b/cfg.c index 68894cb2..1fcbd0d6 100644 --- a/cfg.c +++ b/cfg.c @@ -71,7 +71,7 @@ start_cfg(void) cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); if (cfg_file == NULL && (home = find_home()) != NULL) { - xasprintf(&cfg_file, "%s/.tmux.conf", home); + xasprintf(&cfg_file, "%s/.tmate.conf", home); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { free(cfg_file); cfg_file = NULL; @@ -81,20 +81,6 @@ start_cfg(void) cfg_add_cause("%s: %s", cfg_file, cause); free(cause); -#ifdef TMATE - cause = NULL; - if ((home = find_home()) != NULL) { - xasprintf(&tmate_cfg_file, "%s/.tmate.conf", home); - if (access(tmate_cfg_file, R_OK) != 0 && errno == ENOENT) { - free(tmate_cfg_file); - tmate_cfg_file = NULL; - } - } - if (tmate_cfg_file != NULL && load_cfg(tmate_cfg_file, cfg_cmd_q, &cause) == -1) - cfg_add_cause("%s: %s", cfg_file, cause); - free(cause); -#endif - cmdq_continue(cfg_cmd_q); } diff --git a/session.c b/session.c index 91fb9c1d..ab58ed9f 100644 --- a/session.c +++ b/session.c @@ -249,7 +249,7 @@ session_destroy(struct session *s) next_session_id = 0; run_initial_client_cmd(); } else { - tmate_info("Session terminated"); + tmate_info("Session closed"); tmate_write_fin(); } #endif diff --git a/tmux.c b/tmux.c index 60341f84..91ee316a 100644 --- a/tmux.c +++ b/tmux.c @@ -59,8 +59,7 @@ __dead void usage(void) { fprintf(stderr, - "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" - " [-S socket-path] [command [flags]]\n", + "usage: %s [-vVF] [-f config-file] [-S socket-path] [command [flags]]\n", __progname); exit(1); } From c78198dc59f49e4998f36e31d289b7dd5450178f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 3 Nov 2019 22:40:41 -0500 Subject: [PATCH 654/703] Add command line arguments to set the account_key/session_names -k account_key -n session_name -r session_name_ro --- server.c | 1 + tmux.c | 35 ++++++++++++++++++++++++++++++++++- tmux.h | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/server.c b/server.c index 05706769..f8f08d12 100644 --- a/server.c +++ b/server.c @@ -203,6 +203,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile) #ifdef TMATE tmate_set_editor_mode(); + tmate_init_boot_options(); #endif start_cfg(); diff --git a/tmux.c b/tmux.c index 91ee316a..72c91d39 100644 --- a/tmux.c +++ b/tmux.c @@ -201,6 +201,30 @@ find_home(void) return (home); } +#ifdef TMATE +static char *account_key; +static char *session_name; +static char *session_name_ro; + +void tmate_init_boot_options(void) +{ + if (account_key) + tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-account-key", account_key}); + if (session_name) + tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-session-name", session_name}); + if (session_name_ro) + tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-session-name-ro", session_name_ro}); + + free(account_key); + free(session_name); + free(session_name_ro); + + account_key = NULL; + session_name = NULL; + session_name_ro = NULL; +} +#endif + int main(int argc, char **argv) { @@ -231,7 +255,7 @@ main(int argc, char **argv) #endif label = path = NULL; - while ((opt = getopt(argc, argv, "2c:CdFf:lL:qS:uUVv")) != -1) { + while ((opt = getopt(argc, argv, "2c:CdFf:lL:qS:uUVvk:n:r:")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; @@ -276,6 +300,15 @@ main(int argc, char **argv) log_add_level(); unsetenv("TMUX"); break; + case 'k': + account_key = xstrdup(optarg); + break; + case 'n': + session_name = xstrdup(optarg); + break; + case 'r': + session_name_ro = xstrdup(optarg); + break; default: usage(); } diff --git a/tmux.h b/tmux.h index 3956e240..83a573c1 100644 --- a/tmux.h +++ b/tmux.h @@ -1551,6 +1551,7 @@ extern struct timeval start_time; extern const char *socket_path; #ifdef TMATE extern int tmate_foreground; +void tmate_init_boot_options(void); #endif const char *getshell(void); int checkshell(const char *); From 19341bc5444e7954da23ac06ac799c0fd80a160e Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 4 Nov 2019 16:43:59 -0500 Subject: [PATCH 655/703] Add authorized_keys option -a --- options-table.c | 12 ++++++++++++ tmate-encoder.c | 8 ++++++++ tmate-session.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ tmate.h | 1 + tmux.c | 10 +++++++++- 5 files changed, 75 insertions(+), 1 deletion(-) diff --git a/options-table.c b/options-table.c index 617ba05e..dd9f696e 100644 --- a/options-table.c +++ b/options-table.c @@ -977,6 +977,18 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, + + { .name = "tmate-authorized-keys", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, + + { .name = "tmate-set", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SERVER, + .default_str = "" + }, #endif { .name = NULL } diff --git a/tmate-encoder.c b/tmate-encoder.c index c15d8921..0040a3bf 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -238,6 +238,14 @@ void tmate_exec_cmd_args(int argc, const char **argv) append_saved_cmd(&tmate_session, argc, argv); } +void tmate_set_val(const char *name, const char *value) +{ + char *buf; + xasprintf(&buf, "%s=%s", name, value); + tmate_exec_cmd_args(3, (const char *[]){"set-option", "tmate-set", buf}); + free(buf); +} + void tmate_exec_cmd(struct cmd *cmd) { int argc; diff --git a/tmate-session.c b/tmate-session.c index 5ab13511..e3eb0c19 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -129,6 +129,50 @@ void tmate_session_init(struct event_base *base) tmate_write_header(); } +static void send_authorized_keys() +{ + char *path; + path = options_get_string(global_options, "tmate-authorized-keys"); + if (strlen(path) == 0) + return; + + path = xstrdup(path); + tmate_info("Using %s for access control", path); + + FILE *f; + char *line; + size_t len; + + if (path[0] == '~' && path[1] == '/') { + const char *home = find_home(); + if (home) { + char *new_path; + xasprintf(&new_path, "%s%s", home, &path[1]); + free(path); + path = new_path; + } + } + + if ((f = fopen(path, "r")) == NULL) { + cfg_add_cause("%s: %s", path, strerror(errno)); + free(path); + return; + } + + while ((line = fparseln(f, &len, NULL, NULL, 0)) != NULL) { + if (len == 0) + continue; + tmate_set_val("authorized_keys", line); + free(line); + } + + if (ferror(f)) + cfg_add_cause("%s: %s", path, strerror(errno)); + + fclose(f); + free(path); +} + void tmate_session_start(void) { /* @@ -138,6 +182,7 @@ void tmate_session_start(void) * - While we are parsing the config file, we need to be able to * serialize it, and so we need a worker encoder. */ + send_authorized_keys(); tmate_write_ready(); lookup_and_connect(); } diff --git a/tmate.h b/tmate.h index 9bf8e975..e1b9af47 100644 --- a/tmate.h +++ b/tmate.h @@ -83,6 +83,7 @@ extern void tmate_write_ready(void); extern void tmate_sync_layout(void); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); +extern void tmate_set_val(const char *name, const char *value); extern void tmate_exec_cmd_args(int argc, const char **argv); extern void tmate_exec_cmd(struct cmd *cmd); extern void tmate_failed_cmd(int client_id, const char *cause); diff --git a/tmux.c b/tmux.c index 72c91d39..90b46403 100644 --- a/tmux.c +++ b/tmux.c @@ -205,6 +205,7 @@ find_home(void) static char *account_key; static char *session_name; static char *session_name_ro; +static char *authorized_keys; void tmate_init_boot_options(void) { @@ -214,14 +215,18 @@ void tmate_init_boot_options(void) tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-session-name", session_name}); if (session_name_ro) tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-session-name-ro", session_name_ro}); + if (authorized_keys) + tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-authorized-keys", authorized_keys}); free(account_key); free(session_name); free(session_name_ro); + free(authorized_keys_file); account_key = NULL; session_name = NULL; session_name_ro = NULL; + authorized_keys = NULL; } #endif @@ -255,7 +260,7 @@ main(int argc, char **argv) #endif label = path = NULL; - while ((opt = getopt(argc, argv, "2c:CdFf:lL:qS:uUVvk:n:r:")) != -1) { + while ((opt = getopt(argc, argv, "2c:CdFf:lL:qS:uUVvk:n:r:a:")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; @@ -309,6 +314,9 @@ main(int argc, char **argv) case 'r': session_name_ro = xstrdup(optarg); break; + case 'a': + authorized_keys = xstrdup(optarg); + break; default: usage(); } From 206c0f38b4c6d865b5294cc9ba66eb6b4a611100 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 4 Nov 2019 17:27:00 -0500 Subject: [PATCH 656/703] Set boot options via tmux commands --- cfg.c | 3 +++ client.c | 46 ++++++++++++++++++++++++++++++---------------- server.c | 1 - tmux.c | 32 +++++++++++++------------------- tmux.h | 4 +++- 5 files changed, 49 insertions(+), 37 deletions(-) diff --git a/cfg.c b/cfg.c index 1fcbd0d6..2410fbf9 100644 --- a/cfg.c +++ b/cfg.c @@ -157,6 +157,9 @@ cfg_default_done(__unused struct cmd_q *cmdq) cfg_finished = 1; #ifdef TMATE + /* We do it this late, this way, CLI options take precedence over cfg file */ + tmate_load_cli_options(); + tmate_session_start(); if (tmate_foreground && cfg_ncauses) { print_cfg_errors(); diff --git a/client.c b/client.c index ad349972..456fe793 100644 --- a/client.c +++ b/client.c @@ -217,37 +217,37 @@ extern const struct cmd_entry cmd_new_session_entry; /* For foreground mode */ static int __argc; -static char **__argv; +static const char **__argv; #endif -static void _run_initial_client_cmd(int argc, char **argv) +int run_headless_command(int argc, const char **argv, int flags, void (*err_callback)(const char *)) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; char *cause; - const char *default_argv[] = {"new-session"}; - - if (argc == 0) { - argc = 1; - argv = (char **)default_argv; - } - cmd_q = cmdq_new(NULL); /* No client */ if ((cmdlist = cmd_list_parse(argc, (char **)argv, NULL, 0, &cause)) == NULL) { - tmate_fatal("%s", cause); + if (err_callback) + err_callback(cause); + return -1; } cmdq_run(cmd_q, cmdlist, NULL); cmd_list_free(cmdlist); cmdq_free(cmd_q); + if (flags & DEFER_ERRORS_CFG) + return 0; + /* error messages land in cfg_causes */ extern char **cfg_causes; extern u_int cfg_ncauses; - int has_error = !!cfg_ncauses; + + int ret = cfg_ncauses ? -1 : 0; for (u_int i = 0; i < cfg_ncauses; i++) { - tmate_info("%s", cfg_causes[i]); + if (err_callback) + err_callback(cfg_causes[i]); free(cfg_causes[i]); } @@ -255,13 +255,27 @@ static void _run_initial_client_cmd(int argc, char **argv) cfg_causes = NULL; cfg_ncauses = 0; - if (has_error) - exit(1); + return ret; +} + +static void initial_client_cmd_err_callback(const char *cause) +{ + tmate_info("%s", cause); } void run_initial_client_cmd(void) { - _run_initial_client_cmd(__argc, __argv); + int argc = __argc; + const char **argv = __argv; + + const char *default_argv[] = {"new-session"}; + if (argc == 0) { + argc = 1; + argv = default_argv; + } + + if (run_headless_command(argc, argv, 0, initial_client_cmd_err_callback) < 0) + exit(1); } /* Client main loop. */ @@ -282,7 +296,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags, #ifdef TMATE int cant_nest = 0; __argc = argc; - __argv = argv; + __argv = (const char **)argv; #endif /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ diff --git a/server.c b/server.c index f8f08d12..05706769 100644 --- a/server.c +++ b/server.c @@ -203,7 +203,6 @@ server_start(struct event_base *base, int lockfd, char *lockfile) #ifdef TMATE tmate_set_editor_mode(); - tmate_init_boot_options(); #endif start_cfg(); diff --git a/tmux.c b/tmux.c index 90b46403..2c790eca 100644 --- a/tmux.c +++ b/tmux.c @@ -207,26 +207,20 @@ static char *session_name; static char *session_name_ro; static char *authorized_keys; -void tmate_init_boot_options(void) +void tmate_load_cli_options(void) { - if (account_key) - tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-account-key", account_key}); - if (session_name) - tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-session-name", session_name}); - if (session_name_ro) - tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-session-name-ro", session_name_ro}); - if (authorized_keys) - tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "tmate-authorized-keys", authorized_keys}); - - free(account_key); - free(session_name); - free(session_name_ro); - free(authorized_keys_file); - - account_key = NULL; - session_name = NULL; - session_name_ro = NULL; - authorized_keys = NULL; +#define SET_OPT(name, val) ({\ + if (val) { \ + run_headless_command(3, (const char *[]){"set-option", name, val}, DEFER_ERRORS_CFG, NULL); \ + free(val); \ + val = NULL; \ + } \ +}) + SET_OPT("tmate-account-key", account_key); + SET_OPT("tmate-account-name", session_name); + SET_OPT("tmate-account-name-ro", session_name_ro); + SET_OPT("tmate-authorized-keys", authorized_keys); +#undef SET_OPT } #endif diff --git a/tmux.h b/tmux.h index 83a573c1..9f3ca7bd 100644 --- a/tmux.h +++ b/tmux.h @@ -1551,7 +1551,7 @@ extern struct timeval start_time; extern const char *socket_path; #ifdef TMATE extern int tmate_foreground; -void tmate_init_boot_options(void); +void tmate_load_cli_options(void); #endif const char *getshell(void); int checkshell(const char *); @@ -1879,6 +1879,8 @@ void signal_waiting_clients(const char *name); void cmd_wait_for_flush(void); /* client.c */ +#define DEFER_ERRORS_CFG 1 +int run_headless_command(int argc, const char **argv, int flags, void (*err_callback)(const char *)); void run_initial_client_cmd(void); int client_main(struct event_base *, int, char **, int, const char *); From fa49dc980d3aad68ffccb01579a1a29a023137f9 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Mon, 4 Nov 2019 19:00:06 -0500 Subject: [PATCH 657/703] Provide a better CLI help (-h) --- tmux.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tmux.c b/tmux.c index 2c790eca..be50adfd 100644 --- a/tmux.c +++ b/tmux.c @@ -59,8 +59,18 @@ __dead void usage(void) { fprintf(stderr, - "usage: %s [-vVF] [-f config-file] [-S socket-path] [command [flags]]\n", - __progname); + "Usage: %s [options] [tmux-command [flags]]\n" + "\n" + "Basic options:\n" + " -n specify the session name instead of getting a random one\n" + " -r same, but for the read-only session name\n" + " -k specify the account-key, necessary for named sessions on tmate.io\n" + " -F set the foreground mode, useful for setting remote access\n" + " -f set the config file path\n" + " -S set the socket path, useful to issue commands to a running tmate instance\n" + " -v set verbosity (can be repeated)\n" + " -V print version\n" + ,__progname); exit(1); } @@ -254,7 +264,7 @@ main(int argc, char **argv) #endif label = path = NULL; - while ((opt = getopt(argc, argv, "2c:CdFf:lL:qS:uUVvk:n:r:a:")) != -1) { + while ((opt = getopt(argc, argv, "h2c:CdFf:lL:qS:uUVvk:n:r:a:")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; @@ -311,6 +321,7 @@ main(int argc, char **argv) case 'a': authorized_keys = xstrdup(optarg); break; + case 'h': default: usage(); } From c71307ed5c9c00c1fdd28add86b5d06a7fceb98e Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 5 Nov 2019 19:29:18 -0500 Subject: [PATCH 658/703] Fix reconnection hanging bugs --- tmate-msgpack.c | 13 +++++--- tmate-session.c | 81 ++++++++++++++++++++++++++++------------------ tmate-ssh-client.c | 24 ++++++++------ tmate.h | 9 +++--- 4 files changed, 76 insertions(+), 51 deletions(-) diff --git a/tmate-msgpack.c b/tmate-msgpack.c index 144cda70..c41b07ee 100644 --- a/tmate-msgpack.c +++ b/tmate-msgpack.c @@ -19,7 +19,7 @@ static int on_encoder_write(void *userdata, const char *buf, size_t len) tmate_fatal("Cannot buffer encoded data"); if (!encoder->ev_active) { - event_active(&encoder->ev_buffer, EV_READ, 0); + event_active(encoder->ev_buffer, EV_READ, 0); encoder->ev_active = true; } @@ -57,10 +57,12 @@ void tmate_encoder_init(struct tmate_encoder *encoder, if (!encoder->buffer) tmate_fatal("Can't allocate buffer"); - event_set(&encoder->ev_buffer, -1, - EV_READ | EV_PERSIST, on_encoder_buffer_ready, encoder); + encoder->ev_buffer = event_new(tmate_session.ev_base, -1, + EV_READ | EV_PERSIST, on_encoder_buffer_ready, encoder); + if (!encoder->ev_buffer) + tmate_fatal("Can't allocate event"); - event_add(&encoder->ev_buffer, NULL); + event_add(encoder->ev_buffer, NULL); encoder->ev_active = false; } @@ -69,7 +71,8 @@ void tmate_encoder_destroy(struct tmate_encoder *encoder) { /* encoder->pk doesn't need any cleanup */ evbuffer_free(encoder->buffer); - event_del(&encoder->ev_buffer); + event_del(encoder->ev_buffer); + event_free(encoder->ev_buffer); memset(encoder, 0, sizeof(*encoder)); } diff --git a/tmate-session.c b/tmate-session.c index e3eb0c19..b9f03a8e 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -20,35 +20,50 @@ struct tmate_session tmate_session; static void lookup_and_connect(void); static void on_dns_retry(__unused evutil_socket_t fd, __unused short what, - __unused void *arg) + void *arg) { + struct tmate_session *session = arg; + + assert(session->ev_dns_retry); + event_free(session->ev_dns_retry); + session->ev_dns_retry = NULL; + lookup_and_connect(); } static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) { struct evutil_addrinfo *ai; - struct timeval tv; const char *host = ptr; if (errcode) { + struct tmate_session *session = &tmate_session; + + if (session->ev_dns_retry) + return; + + struct timeval tv = { .tv_sec = TMATE_DNS_RETRY_TIMEOUT, .tv_usec = 0 }; + + session->ev_dns_retry = evtimer_new(session->ev_base, on_dns_retry, session); + if (!session->ev_dns_retry) + tmate_fatal("out of memory"); + evtimer_add(session->ev_dns_retry, &tv); + tmate_status_message("%s lookup failure. Retrying in %d seconds (%s)", host, TMATE_DNS_RETRY_TIMEOUT, evutil_gai_strerror(errcode)); - - tv.tv_sec = TMATE_DNS_RETRY_TIMEOUT; - tv.tv_usec = 0; - - evtimer_assign(&tmate_session.ev_dns_retry, tmate_session.ev_base, - on_dns_retry, NULL); - evtimer_add(&tmate_session.ev_dns_retry, &tv); - return; } tmate_status_message("Connecting to %s...", host); - for (ai = addr; ai; ai = ai->ai_next) { + int i, num_clients = 0; + for (ai = addr; ai; ai = ai->ai_next) + num_clients++; + + struct tmate_ssh_client *ssh_clients[num_clients]; + + for (ai = addr, i = 0; ai; ai = ai->ai_next, i++) { char buf[128]; const char *ip = NULL; if (ai->ai_family == AF_INET) { @@ -59,23 +74,16 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) ip = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128); } - tmate_debug("Trying server %s", ip); - - /* - * Note: We don't deal with the client list. Clients manage it - * and free client structs when necessary. - */ - (void)tmate_ssh_client_alloc(&tmate_session, ip); + ssh_clients[i] = tmate_ssh_client_alloc(&tmate_session, ip); } + for (i = 0; i < num_clients; i++) + connect_ssh_client(ssh_clients[i]); + evutil_freeaddrinfo(addr); - /* - * XXX For some reason, freeing the DNS resolver makes MacOSX flip out... - * not sure what's going on... - * evdns_base_free(tmate_session.ev_dnsbase, 0); - * tmate_session.ev_dnsbase = NULL; - */ + evdns_base_free(tmate_session.ev_dnsbase, 0); + tmate_session.ev_dnsbase = NULL; } static void lookup_and_connect(void) @@ -83,8 +91,8 @@ static void lookup_and_connect(void) struct evutil_addrinfo hints; const char *tmate_server_host; - if (!tmate_session.ev_dnsbase) - tmate_session.ev_dnsbase = evdns_base_new(tmate_session.ev_base, 1); + assert(!tmate_session.ev_dnsbase); + tmate_session.ev_dnsbase = evdns_base_new(tmate_session.ev_base, 1); if (!tmate_session.ev_dnsbase) tmate_fatal("Cannot initialize the DNS lookup service"); @@ -191,12 +199,18 @@ static void on_reconnect_retry(__unused evutil_socket_t fd, __unused short what, { struct tmate_session *session = arg; + assert(session->ev_connection_retry); + event_free(session->ev_connection_retry); + session->ev_connection_retry = NULL; + if (session->last_server_ip) { /* * We have a previous server ip. Let's try that again first, * but then connect to any server if it fails again. */ - (void)tmate_ssh_client_alloc(&tmate_session, session->last_server_ip); + struct tmate_ssh_client *c = tmate_ssh_client_alloc(session, + session->last_server_ip); + connect_ssh_client(c); free(session->last_server_ip); session->last_server_ip = NULL; } else { @@ -214,18 +228,21 @@ void tmate_reconnect_session(struct tmate_session *session, const char *message) */ struct timeval tv = { .tv_sec = TMATE_RECONNECT_RETRY_TIMEOUT, .tv_usec = 0 }; - evtimer_assign(&session->ev_connection_retry, session->ev_base, - on_reconnect_retry, session); - evtimer_add(&session->ev_connection_retry, &tv); + if (session->ev_connection_retry) + return; - if (message) + session->ev_connection_retry = evtimer_new(session->ev_base, on_reconnect_retry, session); + if (!session->ev_connection_retry) + tmate_fatal("out of memory"); + evtimer_add(session->ev_connection_retry, &tv); + + if (message && !tmate_foreground) tmate_status_message("Reconnecting... (%s)", message); else tmate_status_message("Reconnecting..."); /* * This says that we'll need to send a snapshot of the current state. - * Until we have persisted logs... */ session->reconnected = true; } diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 66eb1716..3f4690df 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -210,8 +210,12 @@ static void init_conn_fd(struct tmate_ssh_client *client, bool tune_socket) if (tune_socket) tune_socket_opts(fd); - event_set(&client->ev_ssh, fd, EV_READ | EV_PERSIST, __on_ssh_client_event, client); - event_add(&client->ev_ssh, NULL); + assert(!client->ev_ssh); + client->ev_ssh = event_new(client->tmate_session->ev_base, + fd, EV_READ | EV_PERSIST, __on_ssh_client_event, client); + if (!client->ev_ssh) + tmate_fatal("out of memory"); + event_add(client->ev_ssh, NULL); client->has_init_conn_fd = true; } @@ -479,7 +483,8 @@ static void kill_ssh_client(struct tmate_ssh_client *client, tmate_debug("SSH client killed (%s)", client->server_ip); if (client->has_init_conn_fd) { - event_del(&client->ev_ssh); + event_del(client->ev_ssh); + event_free(client->ev_ssh); client->has_init_conn_fd = false; } @@ -506,12 +511,11 @@ static void kill_ssh_client(struct tmate_ssh_client *client, free(client); } -static void connect_ssh_client(struct tmate_ssh_client *client) +void connect_ssh_client(struct tmate_ssh_client *client) { - if (!client->session) { - client->state = SSH_INIT; - on_ssh_client_event(client); - } + assert(!client->session); + client->state = SSH_INIT; + on_ssh_client_event(client); } static void ssh_log_function(int priority, const char *function, @@ -522,9 +526,11 @@ static void ssh_log_function(int priority, const char *function, struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, const char *server_ip) + { struct tmate_ssh_client *client; client = xmalloc(sizeof(*client)); + memset(client, 0, sizeof(*client)); ssh_set_log_callback(ssh_log_function); @@ -544,7 +550,5 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, client->has_init_conn_fd = false; - connect_ssh_client(client); - return client; } diff --git a/tmate.h b/tmate.h index e1b9af47..9b177120 100644 --- a/tmate.h +++ b/tmate.h @@ -22,7 +22,7 @@ struct tmate_encoder { tmate_encoder_write_cb *ready_callback; void *userdata; struct evbuffer *buffer; - struct event ev_buffer; + struct event *ev_buffer; bool ev_active; }; @@ -142,10 +142,11 @@ struct tmate_ssh_client { ssh_channel channel; bool has_init_conn_fd; - struct event ev_ssh; + struct event *ev_ssh; }; TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); +extern void connect_ssh_client(struct tmate_ssh_client *client); extern struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, const char *server_ip); @@ -154,7 +155,7 @@ extern struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *ses struct tmate_session { struct event_base *ev_base; struct evdns_base *ev_dnsbase; - struct event ev_dns_retry; + struct event *ev_dns_retry; struct tmate_encoder encoder; struct tmate_decoder decoder; @@ -175,7 +176,7 @@ struct tmate_session { char *passphrase; bool reconnected; - struct event ev_connection_retry; + struct event *ev_connection_retry; char *last_server_ip; char *reconnection_data; /* From 442143cd90bc284b487aa2c723b81c8d33e5e38e Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 5 Nov 2019 21:18:13 -0500 Subject: [PATCH 659/703] Show message when restarting shell --- options-table.c | 8 ++++++++ session.c | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/options-table.c b/options-table.c index dd9f696e..4a20c548 100644 --- a/options-table.c +++ b/options-table.c @@ -989,6 +989,14 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, + + { .name = "tmate-foreground-restart", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_SERVER, + .minimum = 0, + .maximum = 1, + .default_num = 1 + }, #endif { .name = NULL } diff --git a/session.c b/session.c index ab58ed9f..d1a644d5 100644 --- a/session.c +++ b/session.c @@ -207,6 +207,36 @@ session_free(__unused int fd, __unused short events, void *arg) } } +static void maybe_restart_session(void) +{ + int fg_restart = options_get_number(global_options, "tmate-foreground-restart"); + if (!fg_restart) + return; + + /* + * throttle restarts. This is a blocking sleep. It's + * simpler than using a timer, but fairly harmless + * from a blocking perspective. + */ + usleep(500*1000); + next_session_id = 0; + run_initial_client_cmd(); + + tmate_info("Shell exited. Shell restarted"); + + struct session *s; + s = RB_MIN(sessions, &sessions); + if (!s) + return; + + struct window_pane *wp; + wp = s->curw->window->active; + window_pane_set_mode(wp, &window_copy_mode); + window_copy_init_for_output(wp); + window_copy_add(wp, "%s", "Shell exited. Shell restarted."); + window_copy_add(wp, "%s", "Note: press the following sequence to disconnect from SSH: ~."); +} + /* Destroy a session. */ void session_destroy(struct session *s) @@ -239,15 +269,7 @@ session_destroy(struct session *s) #ifdef TMATE if (tmate_foreground && !server_exit) { - tmate_info("Shell exited, restarting"); - /* - * throttle restarts. This is a blocking sleep. - * It's simpler than using a timer, but fairly harmless - * from a blocking perspective. - */ - usleep(500*1000); - next_session_id = 0; - run_initial_client_cmd(); + maybe_restart_session(); } else { tmate_info("Session closed"); tmate_write_fin(); From 9781946a70d0bde07abdeca3221b5cb4a31b6f3c Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 5 Nov 2019 21:32:21 -0500 Subject: [PATCH 660/703] Show initial message in copy mode --- tmate-msg.c | 20 ++++++++++++++++++++ tmate-session.c | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/tmate-msg.c b/tmate-msg.c index a47f9e4d..4ee0a5de 100644 --- a/tmate-msg.c +++ b/tmate-msg.c @@ -50,6 +50,24 @@ static void tmate_status_message_client(struct client *c, const char *message) recalculate_sizes(); } +static void tmate_status_message_session(const char *message) +{ + if (tmate_foreground) + return; + + struct session *s; + s = RB_MIN(sessions, &sessions); + if (!s) { + cfg_add_cause("%s", message); + return; + } + + struct window_pane *wp; + wp = s->curw->window->active; + if (wp->mode == &window_copy_mode) + window_copy_add(wp, "%s", message); +} + void __tmate_status_message(const char *fmt, va_list ap) { struct client *c; @@ -63,6 +81,8 @@ void __tmate_status_message(const char *fmt, va_list ap) tmate_status_message_client(c, message); } + tmate_status_message_session(message); + free(message); } diff --git a/tmate-session.c b/tmate-session.c index b9f03a8e..0c07a758 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -190,6 +190,11 @@ void tmate_session_start(void) * - While we are parsing the config file, we need to be able to * serialize it, and so we need a worker encoder. */ + if (!tmate_foreground) { + cfg_add_cause("%s", "To see these messages again, run: tmate show-messages"); + cfg_add_cause("%s", "-----------------------------------------------------"); + } + send_authorized_keys(); tmate_write_ready(); lookup_and_connect(); From 0272757aa531166acca449f081ea008c263b4485 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 6 Nov 2019 12:34:18 -0500 Subject: [PATCH 661/703] Clarify user message --- tmate-session.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tmate-session.c b/tmate-session.c index 0c07a758..0e6080e8 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -192,6 +192,7 @@ void tmate_session_start(void) */ if (!tmate_foreground) { cfg_add_cause("%s", "To see these messages again, run: tmate show-messages"); + cfg_add_cause("%s", "Press to dismiss"); cfg_add_cause("%s", "-----------------------------------------------------"); } From 1600a81e589e92f3e7bd68a34645797909a32b9a Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 6 Nov 2019 20:34:44 -0500 Subject: [PATCH 662/703] Also add crash info on SIGABRT --- tmate-debug.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tmate-debug.c b/tmate-debug.c index f58efe0e..59e8fe7d 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -85,7 +85,7 @@ void tmate_print_stack_trace(void) } -static void handle_sigsegv(__unused int sig) +static void handle_crash(__unused int sig) { /* TODO send stack trace to server */ tmate_info("CRASH, printing stack trace"); @@ -95,6 +95,7 @@ static void handle_sigsegv(__unused int sig) void tmate_catch_sigsegv(void) { - signal(SIGSEGV, handle_sigsegv); + signal(SIGSEGV, handle_crash); + signal(SIGABRT, handle_crash); } #endif From ba860b8f4580cee8878341a5cd1fb4fe4d440651 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 7 Nov 2019 09:08:17 -0500 Subject: [PATCH 663/703] Cleanup warnings --- Makefile.am | 7 +++++-- client.c | 3 --- compat/b64_ntop.c | 4 +++- configure.ac | 1 + log.c | 9 +++++++-- osdep-darwin.c | 2 ++ screen-redraw.c | 2 +- session.c | 1 + tmate-session.c | 2 +- tmate-ssh-client.c | 18 +++++++++--------- tmux.h | 2 ++ xmalloc.c | 2 ++ 12 files changed, 34 insertions(+), 19 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2551bce3..3da954ae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,8 +33,8 @@ CFLAGS += -g CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare -CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align -CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes +CFLAGS += -Wundef -Wbad-function-cast -Winline +CFLAGS += -Wno-pointer-sign -Wno-attributes CPPFLAGS += -DDEBUG endif if IS_COVERAGE @@ -44,6 +44,9 @@ endif CPPFLAGS += -iquote. endif +CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-null-pointer-arithmetic +CFLAGS += -Wno-deprecated-declarations -Wno-format-nonliteral + # Set flags for Solaris. if IS_SUNOS if IS_GCC diff --git a/client.c b/client.c index 456fe793..c587ec0d 100644 --- a/client.c +++ b/client.c @@ -241,9 +241,6 @@ int run_headless_command(int argc, const char **argv, int flags, void (*err_call return 0; /* error messages land in cfg_causes */ - extern char **cfg_causes; - extern u_int cfg_ncauses; - int ret = cfg_ncauses ? -1 : 0; for (u_int i = 0; i < cfg_ncauses; i++) { if (err_callback) diff --git a/compat/b64_ntop.c b/compat/b64_ntop.c index 2b4dc2d4..fd8930f5 100644 --- a/compat/b64_ntop.c +++ b/compat/b64_ntop.c @@ -52,6 +52,8 @@ #include #include +#include "compat.h" + #define Assert(Cond) if (!(Cond)) abort() static const char Base64[] = @@ -122,7 +124,7 @@ static const char Pad64 = '='; */ int -b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) { +b64_ntop(const char *src, size_t srclength, char *target, size_t targsize) { size_t datalength = 0; uint8_t input[3]; uint8_t output[4]; diff --git a/configure.ac b/configure.ac index f623cadc..8bdb0686 100644 --- a/configure.ac +++ b/configure.ac @@ -2,6 +2,7 @@ AC_INIT(tmate, 2.3.1) +AM_SILENT_RULES([yes]) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) diff --git a/log.c b/log.c index bc798778..12266aa4 100644 --- a/log.c +++ b/log.c @@ -98,6 +98,7 @@ log_close(void) } /* Write a log message. */ +__attribute__((__format__(__printf__, 1, 0))) static void log_vwrite(const char *msg, va_list ap) { @@ -144,6 +145,7 @@ log_emit(int level, const char *msg, ...) } /* Log a critical error with error string and die. */ +__attribute__((__format__(__printf__, 1, 0))) __dead void fatal(const char *msg, ...) { @@ -153,11 +155,13 @@ fatal(const char *msg, ...) va_start(ap, msg); if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) exit(1); - log_vwrite(fmt, ap); + msg = fmt; + log_vwrite(msg, ap); exit(1); } /* Log a critical error and die. */ +__attribute__((__format__(__printf__, 1, 0))) __dead void fatalx(const char *msg, ...) { @@ -167,6 +171,7 @@ fatalx(const char *msg, ...) va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); - log_vwrite(fmt, ap); + msg = fmt; + log_vwrite(msg, ap); exit(1); } diff --git a/osdep-darwin.c b/osdep-darwin.c index 40b18951..53b48138 100644 --- a/osdep-darwin.c +++ b/osdep-darwin.c @@ -28,7 +28,9 @@ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); +#ifndef __unused #define __unused __attribute__ ((__unused__)) +#endif char * osdep_get_name(int fd, __unused char *tty) diff --git a/screen-redraw.c b/screen-redraw.c index 952a8515..4201b022 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -278,7 +278,7 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) struct window *w = s->curw->window; struct options *oo = w->options; struct tty *tty = &c->tty; - struct window_pane *wp; + struct window_pane *wp = NULL; struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; struct grid_cell msg_gc; u_int i, j, type, msgx = 0, msgy = 0; diff --git a/session.c b/session.c index d1a644d5..07ca9acc 100644 --- a/session.c +++ b/session.c @@ -621,6 +621,7 @@ session_group_index(struct session_group *sg) } fatalx("session group not found"); + for(;;); } /* diff --git a/tmate-session.c b/tmate-session.c index 0e6080e8..f3a904bd 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -137,7 +137,7 @@ void tmate_session_init(struct event_base *base) tmate_write_header(); } -static void send_authorized_keys() +static void send_authorized_keys(void) { char *path; path = options_get_string(global_options, "tmate-authorized-keys"); diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 3f4690df..030b330a 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -115,7 +115,7 @@ static int passphrase_callback(__unused const char *prompt, char *buf, size_t le client->tmate_session->need_passphrase = 1; if (client->tmate_session->passphrase) - strncpy(buf, client->tmate_session->passphrase, len); + strlcpy(buf, client->tmate_session->passphrase, len); else strcpy(buf, ""); @@ -268,7 +268,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) } client->state = SSH_CONNECT; - /* fall through */ + // fall through case SSH_CONNECT: switch (ssh_connect(session)) { @@ -284,8 +284,8 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) tmate_debug("Establishing connection to %s", client->server_ip); client->state = SSH_AUTH_SERVER; - /* fall through */ } + // fall through case SSH_AUTH_SERVER: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0) @@ -352,7 +352,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) on_ssh_auth_server_complete(client); client->state = SSH_AUTH_CLIENT_NONE; - /* fall through */ + // fall through case SSH_AUTH_CLIENT_NONE: switch (ssh_userauth_none(session, NULL)) { @@ -368,8 +368,8 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_AUTH_PARTIAL: case SSH_AUTH_DENIED: client->state = SSH_AUTH_CLIENT_PUBKEY; - /* fall through */ } + // fall through case SSH_AUTH_CLIENT_PUBKEY: client->tried_passphrase = client->tmate_session->passphrase; @@ -397,8 +397,8 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_AUTH_SUCCESS: tmate_debug("Auth successful with pubkey"); client->state = SSH_NEW_CHANNEL; - /* fall through */ } + // fall through SSH_NEW_CHANNEL: case SSH_NEW_CHANNEL: @@ -408,6 +408,7 @@ SSH_NEW_CHANNEL: return; } client->state = SSH_OPEN_CHANNEL; + // fall through case SSH_OPEN_CHANNEL: switch (ssh_channel_open_session(channel)) { @@ -420,8 +421,8 @@ SSH_NEW_CHANNEL: case SSH_OK: tmate_debug("Session opened, initalizing tmate"); client->state = SSH_BOOTSTRAP; - /* fall through */ } + // fall through case SSH_BOOTSTRAP: switch (ssh_channel_request_subsystem(channel, "tmate")) { @@ -449,9 +450,8 @@ SSH_NEW_CHANNEL: free(client->tmate_session->last_server_ip); client->tmate_session->last_server_ip = xstrdup(client->server_ip); - - /* fall through */ } + // fall through case SSH_READY: read_channel(client); diff --git a/tmux.h b/tmux.h index 9f3ca7bd..c60a044e 100644 --- a/tmux.h +++ b/tmux.h @@ -1576,6 +1576,8 @@ void proc_kill_peer(struct tmuxpeer *); extern int cfg_finished; extern int cfg_references; extern struct client *cfg_client; +extern char **cfg_causes; +extern u_int cfg_ncauses; void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); void set_cfg_file(const char *); diff --git a/xmalloc.c b/xmalloc.c index afa8e585..90af1e2f 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -94,6 +94,7 @@ xasprintf(char **ret, const char *fmt, ...) return i; } +__attribute__((__format__(__printf__, 2, 0))) int xvasprintf(char **ret, const char *fmt, va_list ap) { @@ -120,6 +121,7 @@ xsnprintf(char *str, size_t len, const char *fmt, ...) return i; } +__attribute__((__format__(__printf__, 3, 0))) int xvsnprintf(char *str, size_t len, const char *fmt, va_list ap) { From 8b62c547486ce031bdefc91a7930e590e6dd5c2b Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 7 Nov 2019 11:29:28 -0500 Subject: [PATCH 664/703] Fix typo --- tmux.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tmux.c b/tmux.c index be50adfd..b6b03346 100644 --- a/tmux.c +++ b/tmux.c @@ -62,8 +62,8 @@ usage(void) "Usage: %s [options] [tmux-command [flags]]\n" "\n" "Basic options:\n" - " -n specify the session name instead of getting a random one\n" - " -r same, but for the read-only session name\n" + " -n specify the session token instead of getting a random one\n" + " -r same, but for the read-only token\n" " -k specify the account-key, necessary for named sessions on tmate.io\n" " -F set the foreground mode, useful for setting remote access\n" " -f set the config file path\n" @@ -227,8 +227,8 @@ void tmate_load_cli_options(void) } \ }) SET_OPT("tmate-account-key", account_key); - SET_OPT("tmate-account-name", session_name); - SET_OPT("tmate-account-name-ro", session_name_ro); + SET_OPT("tmate-session-name", session_name); + SET_OPT("tmate-session-name-ro", session_name_ro); SET_OPT("tmate-authorized-keys", authorized_keys); #undef SET_OPT } From d3c8808b0fddee80cf599257e0e68ab44a96d584 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 7 Nov 2019 11:38:54 -0500 Subject: [PATCH 665/703] Version bump --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 8bdb0686..028d5559 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT(tmate, 2.3.1) +AC_INIT(tmate, 2.4.0) AM_SILENT_RULES([yes]) AC_CONFIG_AUX_DIR(etc) From 2b146115446ca0477d2c3d7155f07f22fdb9c265 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 7 Nov 2019 13:31:28 -0500 Subject: [PATCH 666/703] Polish session messages --- session.c | 4 ++-- tmate-session.c | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/session.c b/session.c index 07ca9acc..879eb657 100644 --- a/session.c +++ b/session.c @@ -222,7 +222,7 @@ static void maybe_restart_session(void) next_session_id = 0; run_initial_client_cmd(); - tmate_info("Shell exited. Shell restarted"); + tmate_info("Session shell restarted"); struct session *s; s = RB_MIN(sessions, &sessions); @@ -233,7 +233,7 @@ static void maybe_restart_session(void) wp = s->curw->window->active; window_pane_set_mode(wp, &window_copy_mode); window_copy_init_for_output(wp); - window_copy_add(wp, "%s", "Shell exited. Shell restarted."); + window_copy_add(wp, "%s", "Session shell restarted"); window_copy_add(wp, "%s", "Note: press the following sequence to disconnect from SSH: ~."); } diff --git a/tmate-session.c b/tmate-session.c index f3a904bd..1863f78c 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -190,7 +190,10 @@ void tmate_session_start(void) * - While we are parsing the config file, we need to be able to * serialize it, and so we need a worker encoder. */ - if (!tmate_foreground) { + if (tmate_foreground) { + tmate_set_val("foreground", "true"); + tmate_info("To connect to the session locally, run: tmate -S %s attach", socket_path); + } else { cfg_add_cause("%s", "To see these messages again, run: tmate show-messages"); cfg_add_cause("%s", "Press to dismiss"); cfg_add_cause("%s", "-----------------------------------------------------"); From 2b860313088465044a596b7604dc184336c93e54 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2019 03:44:37 -0500 Subject: [PATCH 667/703] Fix keepalive bug --- tmate-session.c | 6 ++-- tmate-ssh-client.c | 69 +++++++++++++++++++++++++++++++--------------- tmate.h | 1 - 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/tmate-session.c b/tmate-session.c index 1863f78c..10c6123b 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -36,6 +36,9 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) struct evutil_addrinfo *ai; const char *host = ptr; + evdns_base_free(tmate_session.ev_dnsbase, 0); + tmate_session.ev_dnsbase = NULL; + if (errcode) { struct tmate_session *session = &tmate_session; @@ -81,9 +84,6 @@ static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) connect_ssh_client(ssh_clients[i]); evutil_freeaddrinfo(addr); - - evdns_base_free(tmate_session.ev_dnsbase, 0); - tmate_session.ev_dnsbase = NULL; } static void lookup_and_connect(void) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 030b330a..221d7c9f 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -167,57 +167,84 @@ static void request_passphrase(struct tmate_ssh_client *client) data->password_cb_private = client; } -#define KEEPALIVE_CNT 3 -#define KEEPALIVE_IDLE 20 -#define KEEPALIVE_INTVL 10 +#define KEEPALIVE_IDLE 30 +#define KEEPALIVE_CNT 4 +#define KEEPALIVE_INTVL 11 +#define WRITE_TIMEOUT 80 static void tune_socket_opts(int fd) { #define SSO(level, optname, val) ({ \ int _flag = val; \ if (setsockopt(fd, level, optname, &(_flag), sizeof(int)) < 0) { \ - tmate_debug("setsockopt(" #level ", " #optname ", %d) failed", val); \ + tmate_info("setsockopt(" #level ", " #optname ", %d) failed", val); \ } \ }) SSO(IPPROTO_TCP, TCP_NODELAY, 1); SSO(SOL_SOCKET, SO_KEEPALIVE, 1); #ifdef TCP_KEEPALIVE + /* + * The TCP_KEEPALIVE options enable to specify the amount of time, in + * seconds, that the connection must be idle before keepalive probes + * (if enabled) are sent. + */ SSO(IPPROTO_TCP, TCP_KEEPALIVE, KEEPALIVE_IDLE); #endif -#ifdef TCP_KEEPCNT - SSO(IPPROTO_TCP, TCP_KEEPCNT, KEEPALIVE_CNT); -#endif #ifdef TCP_KEEPIDLE + /* + * Same as TCP_KEEPALIVE, but on different systems + */ SSO(IPPROTO_TCP, TCP_KEEPIDLE, KEEPALIVE_IDLE); #endif +#ifdef TCP_KEEPCNT + /* + * When keepalive probes are enabled, this option will set the number + * of times a keepalive probe should be repeated if the peer is not + * responding. After this many probes, the connection will be closed. + */ + SSO(IPPROTO_TCP, TCP_KEEPCNT, KEEPALIVE_CNT); +#endif #ifdef TCP_KEEPINTVL + /* + * When keepalive probes are enabled, this option will set the amount + * of time in seconds between successive keepalives sent to probe an + * unresponsive peer. + */ SSO(IPPROTO_TCP, TCP_KEEPINTVL, KEEPALIVE_INTVL); #endif +#ifdef TCP_USER_TIMEOUT + /* + * This option takes an unsigned int as an argument. When the + * value is greater than 0, it specifies the maximum amount of + * time in milliseconds that transmitted data may remain + * unacknowledged before TCP will forcibly close the + * corresponding connection and return ETIMEDOUT to the + * application. + */ + SSO(IPPROTO_TCP, TCP_USER_TIMEOUT, 1000*WRITE_TIMEOUT); +#endif #undef SSO } -static void init_conn_fd(struct tmate_ssh_client *client, bool tune_socket) +static void init_conn_fd(struct tmate_ssh_client *client) { int fd; - if (client->has_init_conn_fd) + if (client->ev_ssh) return; if ((fd = ssh_get_fd(client->session)) < 0) return; - if (tune_socket) - tune_socket_opts(fd); + tune_socket_opts(fd); - assert(!client->ev_ssh); - client->ev_ssh = event_new(client->tmate_session->ev_base, - fd, EV_READ | EV_PERSIST, __on_ssh_client_event, client); + client->ev_ssh = event_new(client->tmate_session->ev_base, fd, + EV_READ | EV_PERSIST, + __on_ssh_client_event, client); if (!client->ev_ssh) tmate_fatal("out of memory"); event_add(client->ev_ssh, NULL); - - client->has_init_conn_fd = true; } static void on_ssh_client_event(struct tmate_ssh_client *client) @@ -273,14 +300,14 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) case SSH_CONNECT: switch (ssh_connect(session)) { case SSH_AGAIN: - init_conn_fd(client, false); + init_conn_fd(client); return; case SSH_ERROR: kill_ssh_client(client, "Error connecting: %s", ssh_get_error(session)); return; case SSH_OK: - init_conn_fd(client, true); + init_conn_fd(client); tmate_debug("Establishing connection to %s", client->server_ip); client->state = SSH_AUTH_SERVER; @@ -482,10 +509,10 @@ static void kill_ssh_client(struct tmate_ssh_client *client, tmate_debug("SSH client killed (%s)", client->server_ip); - if (client->has_init_conn_fd) { + if (client->ev_ssh) { event_del(client->ev_ssh); event_free(client->ev_ssh); - client->has_init_conn_fd = false; + client->ev_ssh = NULL; } if (client->state == SSH_READY) { @@ -548,7 +575,5 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, client->channel = NULL; client->has_encoder = 0; - client->has_init_conn_fd = false; - return client; } diff --git a/tmate.h b/tmate.h index 9b177120..5c3af6a0 100644 --- a/tmate.h +++ b/tmate.h @@ -141,7 +141,6 @@ struct tmate_ssh_client { ssh_session session; ssh_channel channel; - bool has_init_conn_fd; struct event *ev_ssh; }; TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); From 86ec8d1ad6f00b28ae9c20c6e3d0d2256d5336d2 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2019 03:59:12 -0500 Subject: [PATCH 668/703] Better crash messages --- tmate-debug.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tmate-debug.c b/tmate-debug.c index 59e8fe7d..8b4ddd5b 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -88,9 +88,13 @@ void tmate_print_stack_trace(void) static void handle_crash(__unused int sig) { /* TODO send stack trace to server */ - tmate_info("CRASH, printing stack trace"); + const char *what = sig == SIGSEGV ? "SIGSEGV" : "SIGABRT"; + tmate_info("%s printing stack trace", what); tmate_print_stack_trace(); - tmate_fatal("CRASHED"); + + /* Reraise */ + signal(sig, NULL); + kill(getpid(), sig); } void tmate_catch_sigsegv(void) From 9fc6e96444bf913c5dc8d1db1c6ff0eed55b9781 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2019 15:31:54 -0500 Subject: [PATCH 669/703] Send uname --- tmate-encoder.c | 19 +++++++++++++++++++ tmate-protocol.h | 3 +++ tmate-session.c | 1 + tmate.h | 1 + 4 files changed, 24 insertions(+) diff --git a/tmate-encoder.c b/tmate-encoder.c index 0040a3bf..e1373451 100644 --- a/tmate-encoder.c +++ b/tmate-encoder.c @@ -1,3 +1,4 @@ +#include #include "tmate.h" #include "tmate-protocol.h" #include "window-copy.h" @@ -12,6 +13,23 @@ void tmate_write_header(void) pack(string, VERSION); } +void tmate_write_uname(void) +{ + struct utsname name; + if (uname(&name) < 0) { + tmate_debug("uname() failed"); + return; + } + + pack(array, 6); + pack(int, TMATE_OUT_UNAME); + pack(string, name.sysname); + pack(string, name.nodename); + pack(string, name.release); + pack(string, name.version); + pack(string, name.machine); +} + void tmate_write_ready(void) { pack(array, 1); @@ -464,6 +482,7 @@ void tmate_send_reconnection_state(struct tmate_session *session) tmate_send_reconnection_data(session); replay_saved_cmd(session); /* TODO send all option variables */ + tmate_write_uname(); tmate_write_ready(); tmate_sync_layout(); diff --git a/tmate-protocol.h b/tmate-protocol.h index 595b6271..81716bb2 100644 --- a/tmate-protocol.h +++ b/tmate-protocol.h @@ -56,6 +56,7 @@ enum tmate_daemon_out_msg_types { TMATE_OUT_RECONNECT, TMATE_OUT_SNAPSHOT, TMATE_OUT_EXEC_CMD, + TMATE_OUT_UNAME, }; /* @@ -77,6 +78,8 @@ enum tmate_daemon_out_msg_types { [TMATE_OUT_RECONNECT, string: reconnection_data] [TMATE_OUT_SNAPSHOT, ...] [TMATE_OUT_EXEC_CMD, string: cmd_name, ...string: args] +[TMATE_OUT_UNAME, string: name.sysname, string: name.nodename, + string: name.release, string: name.version, string: name.machine] */ enum tmate_daemon_in_msg_types { diff --git a/tmate-session.c b/tmate-session.c index 10c6123b..d88ec5a5 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -200,6 +200,7 @@ void tmate_session_start(void) } send_authorized_keys(); + tmate_write_uname(); tmate_write_ready(); lookup_and_connect(); } diff --git a/tmate.h b/tmate.h index 5c3af6a0..1fdda4e0 100644 --- a/tmate.h +++ b/tmate.h @@ -79,6 +79,7 @@ extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *neste struct tmate_session; extern void tmate_write_header(void); +extern void tmate_write_uname(void); extern void tmate_write_ready(void); extern void tmate_sync_layout(void); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); From e5f6e68fadc2ca7a27c84a1a3b2ac9e4dce5079c Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2019 14:05:14 -0500 Subject: [PATCH 670/703] Unify tmate-debug.c with tmate-ssh-server --- tmate-debug.c | 11 +++++++++-- tmate.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tmate-debug.c b/tmate-debug.c index 8b4ddd5b..81850284 100644 --- a/tmate-debug.c +++ b/tmate-debug.c @@ -11,6 +11,7 @@ void tmate_print_stack_trace(void) {} void tmate_catch_sigsegv(void) {} +void tmate_preload_trace_lib(void) {} #else @@ -84,8 +85,7 @@ void tmate_print_stack_trace(void) free (strings); } - -static void handle_crash(__unused int sig) +static void handle_crash(int sig) { /* TODO send stack trace to server */ const char *what = sig == SIGSEGV ? "SIGSEGV" : "SIGABRT"; @@ -102,4 +102,11 @@ void tmate_catch_sigsegv(void) signal(SIGSEGV, handle_crash); signal(SIGABRT, handle_crash); } + +void tmate_preload_trace_lib(void) +{ + void *array[1]; + backtrace(array, 1); +} + #endif diff --git a/tmate.h b/tmate.h index 1fdda4e0..ec6d0f29 100644 --- a/tmate.h +++ b/tmate.h @@ -203,6 +203,7 @@ extern void tmate_reconnect_session(struct tmate_session *session, const char *m /* tmate-debug.c */ extern void tmate_print_stack_trace(void); extern void tmate_catch_sigsegv(void); +extern void tmate_preload_trace_lib(void); /* tmate-msg.c */ From bfa3c104d7aed6300313694585fa676f33ba5e4a Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2019 15:19:32 -0500 Subject: [PATCH 671/703] Refactor static builds --- .gitignore | 1 + .travis.yml | 4 ++-- Dockerfile | 5 +++-- build_static_release.sh | 33 +++++++++++++++++++++++++++++++++ package_release.sh | 14 -------------- 5 files changed, 39 insertions(+), 18 deletions(-) create mode 100755 build_static_release.sh delete mode 100755 package_release.sh diff --git a/.gitignore b/.gitignore index 93153ede..bcbdfe13 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ downloads/ ext/ libssh-*/ msgpack-*/ +releases/ diff --git a/.travis.yml b/.travis.yml index a02e89a8..fc43a83f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ script: # On arch=arm64, some directories are not setup correctly, and 'ruby -S gem # install dpl' required by the release push scripts fails. - 'if [ "$TRAVIS_TAG" ]; then sudo chown -R $USER: /var/lib/gems /usr/local/bin; fi' -- 'if [ "$TRAVIS_TAG" ]; then ./package_release.sh $TRAVIS_TAG $PLATFORM; fi' +- 'if [ "$TRAVIS_TAG" ]; then ./build_static_release.sh $TRAVIS_TAG $PLATFORM; fi' deploy: provider: releases @@ -29,7 +29,7 @@ deploy: skip_cleanup: true overwrite: true file_glob: true - file: /tmp/tmate-release/*.tar.* + file: releases/*.tar.* on: repo: tmate-io/tmate branch: master diff --git a/Dockerfile b/Dockerfile index c7aa2b9f..8b736f63 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,9 +20,10 @@ RUN set -ex; \ make -j $(nproc); \ make install -COPY . . +COPY compat ./compat +COPY *.c *.h autogen.sh Makefile.am configure.ac ./ RUN ./autogen.sh && ./configure --enable-static RUN make -j $(nproc) -RUN strip tmate +RUN objcopy --only-keep-debug tmate tmate.symbols && strip tmate RUN ./tmate -V diff --git a/build_static_release.sh b/build_static_release.sh new file mode 100755 index 00000000..77b5d003 --- /dev/null +++ b/build_static_release.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -eux + +# This is invoked by .travis.yml + +VERSION=$1 +PLATFORM=$2 + +SRC_VERSION=`cat configure.ac | grep AC_INIT | sed -E 's/^AC_INIT\(tmate, (.+)\)$/\1/'` + +if [ $SRC_VERSION != $VERSION ]; then + echo "Version mismatch: $SRC_VERSION != $VERSION" + exit 1 +fi + +RELEASE_NAME=tmate-$VERSION-static-linux-$PLATFORM +echo "Building $RELEASE_NAME" + +docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM + +mkdir -p releases +cd releases + +rm -rf $RELEASE_NAME +mkdir -p $RELEASE_NAME +docker run --rm local-$PLATFORM/tmate-build cat tmate > $RELEASE_NAME/tmate +chmod +x $RELEASE_NAME/tmate +tar -cf - $RELEASE_NAME | xz > tmate-$VERSION-static-linux-$PLATFORM.tar.xz + +rm -rf $RELEASE_NAME-symbols +mkdir -p $RELEASE_NAME-symbols +docker run --rm local-$PLATFORM/tmate-build cat tmate.symbols > $RELEASE_NAME-symbols/tmate.symbols +tar -cf - $RELEASE_NAME-symbols | xz > dbg-symbols-tmate-$VERSION-static-linux-$PLATFORM.tar.xz diff --git a/package_release.sh b/package_release.sh deleted file mode 100755 index f4186e3c..00000000 --- a/package_release.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -eux -VERSION=$1 -PLATFORM=$2 -RELEASE_NAME=tmate-$VERSION-static-linux-$PLATFORM - -# This assumes the follow command has already been run: -# docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM - -mkdir -p /tmp/tmate-release/$RELEASE_NAME -cd /tmp/tmate-release -docker run --rm local-$PLATFORM/tmate-build cat tmate > $RELEASE_NAME/tmate -chmod +x $RELEASE_NAME/tmate -tar -cf - $RELEASE_NAME | xz > tmate-$VERSION-static-linux-$PLATFORM.tar.xz From 7e02dba7efb73aabbf1a4f0cd3c43739d62b82bf Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2019 16:23:00 -0500 Subject: [PATCH 672/703] Minor refactor --- tmate-ssh-client.c | 38 ++++++++++++++++++++------------------ tmate.h | 1 - 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 221d7c9f..c52c166c 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -86,7 +86,6 @@ static void on_ssh_auth_server_complete(struct tmate_ssh_client *connected_clien if (client == connected_client) continue; - assert(!client->has_encoder); kill_ssh_client(client, NULL); } } @@ -177,7 +176,9 @@ static void tune_socket_opts(int fd) #define SSO(level, optname, val) ({ \ int _flag = val; \ if (setsockopt(fd, level, optname, &(_flag), sizeof(int)) < 0) { \ - tmate_info("setsockopt(" #level ", " #optname ", %d) failed", val); \ + /* If the connection has been closed, we'll get EINVAL */ \ + if (errno != EINVAL) \ + tmate_info("setsockopt(" #level ", " #optname ", %d) failed %s", val, strerror(errno)); \ } \ }) @@ -249,23 +250,11 @@ static void init_conn_fd(struct tmate_ssh_client *client) static void on_ssh_client_event(struct tmate_ssh_client *client) { - char *identity; - ssh_key pubkey; - enum ssh_keytypes_e key_type; - unsigned char *hash; - ssize_t hash_len; - char *hash_str; - const char *server_hash_str; - int match; - - int verbosity = SSH_LOG_NOLOG + log_get_level(); - int port = options_get_number(global_options, "tmate-server-port"); - ssh_session session = client->session; ssh_channel channel = client->channel; switch (client->state) { - case SSH_INIT: + case SSH_INIT: { client->session = session = ssh_new(); if (!session) { tmate_fatal("cannot ssh_new()"); @@ -274,6 +263,9 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) ssh_set_callbacks(session, &client->ssh_callbacks); + int verbosity = SSH_LOG_NOLOG + log_get_level(); + int port = options_get_number(global_options, "tmate-server-port"); + ssh_set_blocking(session, 0); ssh_options_set(session, SSH_OPTIONS_HOST, client->server_ip); ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); @@ -281,6 +273,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); + char *identity; if ((identity = get_identity())) { /* * FIXME libssh will continue with the next set of @@ -295,7 +288,8 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) } client->state = SSH_CONNECT; - // fall through + } + // fall through case SSH_CONNECT: switch (ssh_connect(session)) { @@ -314,7 +308,15 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) } // fall through - case SSH_AUTH_SERVER: + case SSH_AUTH_SERVER: { + ssh_key pubkey; + enum ssh_keytypes_e key_type; + unsigned char *hash; + ssize_t hash_len; + char *hash_str; + const char *server_hash_str; + int match; + #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0) if (ssh_get_server_publickey(session, &pubkey) < 0) tmate_fatal("ssh_get_server_publickey"); @@ -379,6 +381,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) on_ssh_auth_server_complete(client); client->state = SSH_AUTH_CLIENT_NONE; + } // fall through case SSH_AUTH_CLIENT_NONE: @@ -573,7 +576,6 @@ struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, client->state = SSH_NONE; client->session = NULL; client->channel = NULL; - client->has_encoder = 0; return client; } diff --git a/tmate.h b/tmate.h index ec6d0f29..f4783dc8 100644 --- a/tmate.h +++ b/tmate.h @@ -130,7 +130,6 @@ struct tmate_ssh_client { char *server_ip; - int has_encoder; int state; /* From 9fe8b32293982bb85eb12b9c55991b6fa0dac400 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2019 16:29:05 -0500 Subject: [PATCH 673/703] Add foreground tip --- tmate-session.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tmate-session.c b/tmate-session.c index d88ec5a5..02f25c14 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -194,7 +194,8 @@ void tmate_session_start(void) tmate_set_val("foreground", "true"); tmate_info("To connect to the session locally, run: tmate -S %s attach", socket_path); } else { - cfg_add_cause("%s", "To see these messages again, run: tmate show-messages"); + cfg_add_cause("%s", "Tip: if you wish to use tmate for remote access, run tmate -F"); + cfg_add_cause("%s", "To see the following messages again, run: tmate show-messages"); cfg_add_cause("%s", "Press to dismiss"); cfg_add_cause("%s", "-----------------------------------------------------"); } From f895fe01b1ddd0f0dbd0d019c5f290c7650b7d3b Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sun, 10 Nov 2019 22:27:23 -0500 Subject: [PATCH 674/703] Rename account-key -> api-key --- options-table.c | 2 +- tmux.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/options-table.c b/options-table.c index 4a20c548..e3362ba8 100644 --- a/options-table.c +++ b/options-table.c @@ -960,7 +960,7 @@ const struct options_table_entry options_table[] = { .default_str = "" }, - { .name = "tmate-account-key", + { .name = "tmate-api-key", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" diff --git a/tmux.c b/tmux.c index b6b03346..53b984d7 100644 --- a/tmux.c +++ b/tmux.c @@ -64,7 +64,7 @@ usage(void) "Basic options:\n" " -n specify the session token instead of getting a random one\n" " -r same, but for the read-only token\n" - " -k specify the account-key, necessary for named sessions on tmate.io\n" + " -k specify an api-key, necessary for using named sessions on tmate.io\n" " -F set the foreground mode, useful for setting remote access\n" " -f set the config file path\n" " -S set the socket path, useful to issue commands to a running tmate instance\n" @@ -212,7 +212,7 @@ find_home(void) } #ifdef TMATE -static char *account_key; +static char *api_key; static char *session_name; static char *session_name_ro; static char *authorized_keys; @@ -226,7 +226,7 @@ void tmate_load_cli_options(void) val = NULL; \ } \ }) - SET_OPT("tmate-account-key", account_key); + SET_OPT("tmate-api-key", api_key); SET_OPT("tmate-session-name", session_name); SET_OPT("tmate-session-name-ro", session_name_ro); SET_OPT("tmate-authorized-keys", authorized_keys); @@ -310,7 +310,7 @@ main(int argc, char **argv) unsetenv("TMUX"); break; case 'k': - account_key = xstrdup(optarg); + api_key = xstrdup(optarg); break; case 'n': session_name = xstrdup(optarg); From 5e00bfa5e137e76c81888727712ced2b3fd99f5b Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Sat, 16 Nov 2019 17:08:46 -0500 Subject: [PATCH 675/703] Rephrase --- tmate-session.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tmate-session.c b/tmate-session.c index 02f25c14..62493278 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -194,10 +194,10 @@ void tmate_session_start(void) tmate_set_val("foreground", "true"); tmate_info("To connect to the session locally, run: tmate -S %s attach", socket_path); } else { - cfg_add_cause("%s", "Tip: if you wish to use tmate for remote access, run tmate -F"); - cfg_add_cause("%s", "To see the following messages again, run: tmate show-messages"); - cfg_add_cause("%s", "Press to dismiss"); - cfg_add_cause("%s", "-----------------------------------------------------"); + cfg_add_cause("%s", "Tip: if you wish to use tmate only for remote access, run: tmate -F"); + cfg_add_cause("%s", "To see the following messages again, run in a tmate session: tmate show-messages"); + cfg_add_cause("%s", "Press or to continue"); + cfg_add_cause("%s", "---------------------------------------------------------------------"); } send_authorized_keys(); From f0a4707ef35b04afb87092f11782f63776b3cdec Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Thu, 28 Nov 2019 16:30:32 -0500 Subject: [PATCH 676/703] Update dockerfile for size --- Dockerfile | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8b736f63..2f6387c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ ARG PLATFORM=amd64 -FROM ${PLATFORM}/alpine:3.10 +FROM ${PLATFORM}/alpine:3.10 AS build WORKDIR /build @@ -25,5 +25,13 @@ COPY *.c *.h autogen.sh Makefile.am configure.ac ./ RUN ./autogen.sh && ./configure --enable-static RUN make -j $(nproc) -RUN objcopy --only-keep-debug tmate tmate.symbols && strip tmate +RUN objcopy --only-keep-debug tmate tmate.symbols && chmod -x tmate.symbols && strip tmate RUN ./tmate -V + +FROM alpine:3.9 + +RUN apk --no-cache add bash +RUN mkdir /build +ENV PATH=/build:$PATH +COPY --from=build /build/tmate.symbols /build +COPY --from=build /build/tmate /build From cc01f3f13a73cba3021c072780a22d1fcbd0dceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20Szyma=C5=84ski?= Date: Fri, 29 Nov 2019 14:08:06 +0100 Subject: [PATCH 677/703] Add build on s390x and ppc64le --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index fc43a83f..5a83e0ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,10 @@ matrix: env: PLATFORM=arm32v7 - arch: arm64 env: PLATFORM=arm64v8 + - arch: s390x + env: PLATFORM=s390x + - arch: ppc64le + env: PLATFORM=ppc64le script: - 'docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM' From ba6ac3a363ae7bdd4af946cc81a19f703d06907f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20Szyma=C5=84ski?= Date: Fri, 6 Dec 2019 12:43:04 +0100 Subject: [PATCH 678/703] Update paths in build_static_release script (#178) --- build_static_release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build_static_release.sh b/build_static_release.sh index 77b5d003..9cb35435 100755 --- a/build_static_release.sh +++ b/build_static_release.sh @@ -23,11 +23,11 @@ cd releases rm -rf $RELEASE_NAME mkdir -p $RELEASE_NAME -docker run --rm local-$PLATFORM/tmate-build cat tmate > $RELEASE_NAME/tmate +docker run --rm local-$PLATFORM/tmate-build cat /build/tmate > $RELEASE_NAME/tmate chmod +x $RELEASE_NAME/tmate tar -cf - $RELEASE_NAME | xz > tmate-$VERSION-static-linux-$PLATFORM.tar.xz rm -rf $RELEASE_NAME-symbols mkdir -p $RELEASE_NAME-symbols -docker run --rm local-$PLATFORM/tmate-build cat tmate.symbols > $RELEASE_NAME-symbols/tmate.symbols +docker run --rm local-$PLATFORM/tmate-build cat /build/tmate.symbols > $RELEASE_NAME-symbols/tmate.symbols tar -cf - $RELEASE_NAME-symbols | xz > dbg-symbols-tmate-$VERSION-static-linux-$PLATFORM.tar.xz From 9e3e39d66ddb4a610720fa37a5683c846d2a1ba6 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 11 Mar 2020 11:30:33 -0400 Subject: [PATCH 679/703] Avoid initializing stdout twice Closes #190 --- log.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/log.c b/log.c index 12266aa4..45aaea46 100644 --- a/log.c +++ b/log.c @@ -61,6 +61,9 @@ log_get_level(void) void log_open_fp(FILE *f) { + if (log_file == f) + return; + if (log_file != NULL && !is_log_stdout()) fclose(log_file); From 339e6c43575244f72831654e5dda41c037febde7 Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Thu, 2 Apr 2020 18:19:57 +0200 Subject: [PATCH 680/703] Include for global errno At least on OpenBSD the symbol `errno` is otherwise not defined; it is used in the `SSO()` macro and `send_authorized_keys()` function. --- tmate-session.c | 1 + tmate-ssh-client.c | 1 + 2 files changed, 2 insertions(+) diff --git a/tmate-session.c b/tmate-session.c index 62493278..5ba0896c 100644 --- a/tmate-session.c +++ b/tmate-session.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index c52c166c..42c4e6a6 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include From cbec43f56dfb48c2fb6e00faa2cb85443d4b7d8f Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Tue, 14 Apr 2020 18:22:04 -0400 Subject: [PATCH 681/703] Better debugging when keys are not matching --- tmate-ssh-client.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 42c4e6a6..995c92a7 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -328,7 +328,7 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, &hash, &hash_len) < 0) { - kill_ssh_client(client, "Cannot authenticate server"); + kill_ssh_client(client, "Failed to get server fingerprint"); return; } @@ -362,15 +362,17 @@ static void on_ssh_client_event(struct tmate_ssh_client *client) } match = !strcmp(hash_str, server_hash_str); + if (!match) { + kill_ssh_client(client, "Server fingerprint not recognized: " + "`%s', expected `%s'", server_hash_str, hash_str); + } ssh_key_free(pubkey); ssh_clean_pubkey_hash(&hash); free(hash_str); - if (!match) { - kill_ssh_client(client, "Cannot authenticate server"); + if (!match) return; - } /* * At this point, we abort other connection attempts to the From f6a4ae6042f4bb2b6db8eda23df31d8b788a7978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Jacke?= Date: Thu, 18 Feb 2021 11:58:52 +0100 Subject: [PATCH 682/703] client: set IPTOS_LOWDELAY on TCP connection this helps edge routers to prioritize our interactive network traffic. --- tmate-ssh-client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 995c92a7..ce8d5a5a 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -183,6 +183,7 @@ static void tune_socket_opts(int fd) } \ }) + SSO(IPPROTO_IP, IP_TOS, 0x10); /* IPTOS_LOWDELAY */ SSO(IPPROTO_TCP, TCP_NODELAY, 1); SSO(SOL_SOCKET, SO_KEEPALIVE, 1); #ifdef TCP_KEEPALIVE From 8123fa34f847c658f7cedf154dd4cf966047e6bf Mon Sep 17 00:00:00 2001 From: hpcbjdic Date: Tue, 24 Mar 2020 14:56:06 +0100 Subject: [PATCH 683/703] enable redefinition of TMUX_CONF Without the added #ifndef / #endif it's not possible to _actually_ redefine TMUX_CONF by means of the compiler flag -DTMUX_CONF=... as done by the build system. The same patch has also been applied to tmux, cf. master at https://github.com/tmux/tmux/blob/master/tmux.h. --- tmux.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tmux.h b/tmux.h index c60a044e..b38615b6 100644 --- a/tmux.h +++ b/tmux.h @@ -53,7 +53,9 @@ struct tmuxpeer; struct tmuxproc; /* Default global configuration file. */ +#ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf" +#endif /* * Minimum layout cell size, NOT including separator line. The scroll region From e5ce3f04d2851b4c825430f87620cc42d1d946d6 Mon Sep 17 00:00:00 2001 From: Sergio de Almeida Cipriano Junior Date: Wed, 18 Nov 2020 10:59:07 -0300 Subject: [PATCH 684/703] Fix typo in ssh client --- tmate-ssh-client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index ce8d5a5a..bed79e95 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -453,7 +453,7 @@ SSH_NEW_CHANNEL: ssh_get_error(session)); return; case SSH_OK: - tmate_debug("Session opened, initalizing tmate"); + tmate_debug("Session opened, initializing tmate"); client->state = SSH_BOOTSTRAP; } // fall through From 808b5645641c82cc09e5488ea0466908a514bba5 Mon Sep 17 00:00:00 2001 From: Sergio de Almeida Cipriano Junior Date: Wed, 18 Nov 2020 11:30:37 -0300 Subject: [PATCH 685/703] Update manpage --- tmux.1 | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/tmux.1 b/tmux.1 index b55b3682..c90b5560 100644 --- a/tmux.1 +++ b/tmux.1 @@ -19,10 +19,11 @@ .Os .Sh NAME .Nm tmate -.Nd terminal multiplexer +.Nd terminal multiplexer with instant terminal sharing .Sh SYNOPSIS .Nm tmate .Bk -words +.Op show-messages .Op Fl 2CluvV .Op Fl c Ar shell-command .Op Fl f Ar file @@ -32,13 +33,24 @@ .Ek .Sh DESCRIPTION .Nm -is a terminal multiplexer: +is a terminal multiplexer with instant terminal sharing: it enables a number of terminals to be created, accessed, and -controlled from a single screen. +controlled from a single screen and be shared with another mates. .Nm may be detached from a screen and continue running in the background, -then later reattached. +then later reattached, like as a daemon. +.Pp +.Nm +provides an instant pairing solution, allowing you to share a terminal +with one or several teammates. Together with a voice call, it's almost like +pairing in person. The terminal sharing works by using SSH connections to +backend servers maintained by tmate upstream developers; teammates need to be +given a randomly-generated token to be able to join a session. +.Pp +.Nm +is a modified version of tmux, and uses the same configurations such as +keybindings, color schemes, etc. .Pp When .Nm @@ -47,9 +59,9 @@ is started it creates a new with a single .Em window and displays it on screen. -A status line at the bottom of the screen -shows information on the current session -and is used to enter interactive commands. +A status line at the bottom of the screen shows information +on the current session, such as ssh command to share with +your mate, and is used to enter interactive commands. .Pp A session is a single collection of .Em pseudo terminals From c9ec7af63238b0a47bf659729d32dde2066ce60f Mon Sep 17 00:00:00 2001 From: n0vember Date: Wed, 27 Jan 2021 15:53:45 +0100 Subject: [PATCH 686/703] add basic information for -a option in man page and help text --- tmux.1 | 4 ++++ tmux.c | 1 + 2 files changed, 5 insertions(+) diff --git a/tmux.1 b/tmux.1 index c90b5560..55f88c98 100644 --- a/tmux.1 +++ b/tmux.1 @@ -110,6 +110,10 @@ The options are as follows: Force .Nm to assume the terminal supports 256 colours. +.It Fl a Ar file +Limit access to the public keys listed in the +.Ar file +given as argument. .It Fl C Start in control mode (see the .Sx CONTROL MODE diff --git a/tmux.c b/tmux.c index 53b984d7..730c2dfc 100644 --- a/tmux.c +++ b/tmux.c @@ -68,6 +68,7 @@ usage(void) " -F set the foreground mode, useful for setting remote access\n" " -f set the config file path\n" " -S set the socket path, useful to issue commands to a running tmate instance\n" + " -a limit access to ssh public keys listed in provided file\n" " -v set verbosity (can be repeated)\n" " -V print version\n" ,__progname); From be4a88507e082e0e8e591e18b08374ff591b3980 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Sat, 5 Mar 2022 01:33:21 +0100 Subject: [PATCH 687/703] chore: add FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..ea4e6439 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: nviennot From 169eb64b99719eadccba1f9a00856b68b589ef35 Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 3 Aug 2022 18:19:32 +0800 Subject: [PATCH 688/703] Add FreeBSD build by https://github.com/vmactions/freebsd-vm --- .github/workflows/FreeBSD.yml | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/FreeBSD.yml diff --git a/.github/workflows/FreeBSD.yml b/.github/workflows/FreeBSD.yml new file mode 100644 index 00000000..3a16af9f --- /dev/null +++ b/.github/workflows/FreeBSD.yml @@ -0,0 +1,40 @@ +name: FreeBSD +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/FreeBSD.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/FreeBSD.yml' + + + +jobs: + FreeBSD: + runs-on: macos-12 + steps: + - uses: actions/checkout@v2 + - uses: vmactions/freebsd-vm@v0 + with: + prepare: | + pkg install -y gmake automake autoconf libtool pkgconf libevent msgpack libssh + usesh: true + run: | + autoupdate + ./autogen.sh + ./configure + make + make install + + + From a258907cf8a8888cbb895aa478b697200081c006 Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 3 Aug 2022 18:20:33 +0800 Subject: [PATCH 689/703] add NetBSD test by https://github.com/vmactions/netbsd-vm --- .github/workflows/NetBSD.yml | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/NetBSD.yml diff --git a/.github/workflows/NetBSD.yml b/.github/workflows/NetBSD.yml new file mode 100644 index 00000000..0566d3b9 --- /dev/null +++ b/.github/workflows/NetBSD.yml @@ -0,0 +1,40 @@ +name: NetBSD +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/NetBSD.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/NetBSD.yml' + + + +jobs: + NetBSD: + runs-on: macos-12 + steps: + - uses: actions/checkout@v2 + - uses: vmactions/netbsd-vm@v0 + with: + prepare: | + pkg_add gmake automake autoconf libtool pkgconf libevent msgpack libssh + usesh: true + run: | + autoupdate + ./autogen.sh + ./configure + make + make install + + + From b1079b43930b42f53f6f1579fd1d6afcf9134aeb Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 3 Aug 2022 18:21:37 +0800 Subject: [PATCH 690/703] add OpenBSD build by https://github.com/vmactions/openbsd-vm --- .github/workflows/OpenBSD.yml | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/OpenBSD.yml diff --git a/.github/workflows/OpenBSD.yml b/.github/workflows/OpenBSD.yml new file mode 100644 index 00000000..fbeda51d --- /dev/null +++ b/.github/workflows/OpenBSD.yml @@ -0,0 +1,44 @@ +name: OpenBSD +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/OpenBSD.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/OpenBSD.yml' + + + +jobs: + OpenBSD: + runs-on: macos-12 + steps: + - uses: actions/checkout@v2 + - uses: vmactions/openbsd-vm@v0 + with: + prepare: | + pkg_add gmake automake-1.16.3 autoconf-2.71 libtool pkgconf libevent msgpack libssh gcc-11.2.0p2 + usesh: true + run: | + ln -s /usr/local/bin/egcc /usr/local/bin/gcc + autoupdate + aclocal --print-ac-dir + export AUTOMAKE_VERSION=1.16 + export AUTOCONF_VERSION=2.71 + ./autogen.sh + ./configure + make + make install + + + From 577e04df6c746acbca0b98e12f6ca143bf031be1 Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 3 Aug 2022 23:33:18 +0800 Subject: [PATCH 691/703] fix --- .github/workflows/OpenBSD.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/OpenBSD.yml b/.github/workflows/OpenBSD.yml index fbeda51d..3c884006 100644 --- a/.github/workflows/OpenBSD.yml +++ b/.github/workflows/OpenBSD.yml @@ -31,10 +31,9 @@ jobs: usesh: true run: | ln -s /usr/local/bin/egcc /usr/local/bin/gcc - autoupdate - aclocal --print-ac-dir export AUTOMAKE_VERSION=1.16 export AUTOCONF_VERSION=2.71 + autoupdate ./autogen.sh ./configure make From ef11a7b30ed743051e703b5745fa41916ae0be9f Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 13:39:23 +0800 Subject: [PATCH 692/703] fix OpenBSD.yml --- .github/workflows/OpenBSD.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/OpenBSD.yml b/.github/workflows/OpenBSD.yml index 3c884006..53f06485 100644 --- a/.github/workflows/OpenBSD.yml +++ b/.github/workflows/OpenBSD.yml @@ -27,10 +27,17 @@ jobs: - uses: vmactions/openbsd-vm@v0 with: prepare: | - pkg_add gmake automake-1.16.3 autoconf-2.71 libtool pkgconf libevent msgpack libssh gcc-11.2.0p2 + pkg_add automake-1.16.3 autoconf-2.71 libtool pkgconf libevent msgpack libssh usesh: true run: | - ln -s /usr/local/bin/egcc /usr/local/bin/gcc + sed -i 's,,,' *.[ch] + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-Makefile_am | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-server_c | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-tmate-debug_c | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-tmate_h | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-tmux_c | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-tmux_h | patch + curl https://raw.githubusercontent.com/openbsd/ports/master/sysutils/tmate/patches/patch-osdep-openbsd_c | patch export AUTOMAKE_VERSION=1.16 export AUTOCONF_VERSION=2.71 autoupdate From 17d4a8a7dfa25bcf3ddb336916010f905f2f4183 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 13:45:53 +0800 Subject: [PATCH 693/703] fix OpenBSD.yml --- .github/workflows/OpenBSD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/OpenBSD.yml b/.github/workflows/OpenBSD.yml index 53f06485..8fb7901b 100644 --- a/.github/workflows/OpenBSD.yml +++ b/.github/workflows/OpenBSD.yml @@ -27,7 +27,7 @@ jobs: - uses: vmactions/openbsd-vm@v0 with: prepare: | - pkg_add automake-1.16.3 autoconf-2.71 libtool pkgconf libevent msgpack libssh + pkg_add automake-1.16.3 autoconf-2.71 libtool pkgconf libevent msgpack libssh curl usesh: true run: | sed -i 's,,,' *.[ch] From fde4b58228cd8226e7128bf101e2fe569eed5882 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 13:46:36 +0800 Subject: [PATCH 694/703] add MacOS.yml --- .github/workflows/MacOS.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/MacOS.yml diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml new file mode 100644 index 00000000..5e118ee7 --- /dev/null +++ b/.github/workflows/MacOS.yml @@ -0,0 +1,37 @@ +name: MacOS +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/MacOS.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/MacOS.yml' + + + +jobs: + NetBSD: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - run: | + brew install automake + autoupdate + ./autogen.sh + ./configure + make + make install + + + + From 05a849ba2b29351419ad2034994111388c0817b7 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 13:47:18 +0800 Subject: [PATCH 695/703] fix MacOS.yml --- .github/workflows/MacOS.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml index 5e118ee7..db1c9ca0 100644 --- a/.github/workflows/MacOS.yml +++ b/.github/workflows/MacOS.yml @@ -20,7 +20,7 @@ on: jobs: - NetBSD: + MacOS: runs-on: macos-latest steps: - uses: actions/checkout@v2 From 7c0bbf3e164d869073012b7fffd896869337b143 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 13:56:06 +0800 Subject: [PATCH 696/703] fix MacOS.yml --- .github/workflows/MacOS.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml index db1c9ca0..37293260 100644 --- a/.github/workflows/MacOS.yml +++ b/.github/workflows/MacOS.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: | - brew install automake + brew install automake msgpack autoupdate ./autogen.sh ./configure From 17fcc8b76d28ae5e59b59a08a642e462eea34221 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 13:57:59 +0800 Subject: [PATCH 697/703] fix MacOS.yml --- .github/workflows/MacOS.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml index 37293260..3dc5247e 100644 --- a/.github/workflows/MacOS.yml +++ b/.github/workflows/MacOS.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: | - brew install automake msgpack + brew install automake msgpack libssh autoupdate ./autogen.sh ./configure From 9a1aa9ae9d9c40b6d583355246089a9468dd03bc Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 14:29:15 +0800 Subject: [PATCH 698/703] add Ubuntu.yml --- .github/workflows/Ubuntu.yml | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/Ubuntu.yml diff --git a/.github/workflows/Ubuntu.yml b/.github/workflows/Ubuntu.yml new file mode 100644 index 00000000..581e616d --- /dev/null +++ b/.github/workflows/Ubuntu.yml @@ -0,0 +1,37 @@ +name: Ubuntu +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/Ubuntu.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/Ubuntu.yml' + + + +jobs: + Ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: | + apt-get install -y build-essential automake libtool libevent-dev libncurses5-dev libmsgpack-dev libssh-dev pkg-config + autoupdate + ACLOCAL_PATH=/usr/share/aclocal ./autogen.sh + ./configure + make + make install + + + + From 2ff8bc134d381c88eba8f382625642e5aec8d5da Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 14:33:12 +0800 Subject: [PATCH 699/703] fix Ubuntu.yml --- .github/workflows/Ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Ubuntu.yml b/.github/workflows/Ubuntu.yml index 581e616d..7c8dea57 100644 --- a/.github/workflows/Ubuntu.yml +++ b/.github/workflows/Ubuntu.yml @@ -25,7 +25,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: | - apt-get install -y build-essential automake libtool libevent-dev libncurses5-dev libmsgpack-dev libssh-dev pkg-config + sudo apt-get install -y build-essential automake libtool libevent-dev libncurses5-dev libmsgpack-dev libssh-dev pkg-config autoupdate ACLOCAL_PATH=/usr/share/aclocal ./autogen.sh ./configure From 3efc8496cc90d55dd9bc44d4b5f529ac9760bf66 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 14:34:47 +0800 Subject: [PATCH 700/703] fix Ubuntu.yml --- .github/workflows/Ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Ubuntu.yml b/.github/workflows/Ubuntu.yml index 7c8dea57..cecf39b9 100644 --- a/.github/workflows/Ubuntu.yml +++ b/.github/workflows/Ubuntu.yml @@ -30,7 +30,7 @@ jobs: ACLOCAL_PATH=/usr/share/aclocal ./autogen.sh ./configure make - make install + sudo make install From 15001bfa7739b144969c1181e66c65f0cb14e614 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 14:57:15 +0800 Subject: [PATCH 701/703] fix --- .github/workflows/FreeBSD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/FreeBSD.yml b/.github/workflows/FreeBSD.yml index 3a16af9f..e879618e 100644 --- a/.github/workflows/FreeBSD.yml +++ b/.github/workflows/FreeBSD.yml @@ -27,7 +27,7 @@ jobs: - uses: vmactions/freebsd-vm@v0 with: prepare: | - pkg install -y gmake automake autoconf libtool pkgconf libevent msgpack libssh + pkg install -y automake autoconf libtool pkgconf libevent msgpack libssh usesh: true run: | autoupdate From 6cb27ead4e77ba8cc15982d909fe57c074239434 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 15:04:07 +0800 Subject: [PATCH 702/703] fix NetBSD.yml --- .github/workflows/NetBSD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/NetBSD.yml b/.github/workflows/NetBSD.yml index 0566d3b9..0bb0924a 100644 --- a/.github/workflows/NetBSD.yml +++ b/.github/workflows/NetBSD.yml @@ -27,7 +27,7 @@ jobs: - uses: vmactions/netbsd-vm@v0 with: prepare: | - pkg_add gmake automake autoconf libtool pkgconf libevent msgpack libssh + pkg_add automake autoconf libtool pkgconf libevent msgpack libssh usesh: true run: | autoupdate From ac919516f4f1b10ec928e20b3a5034d18f609d68 Mon Sep 17 00:00:00 2001 From: neil Date: Sat, 6 Aug 2022 15:05:07 +0800 Subject: [PATCH 703/703] add DragonflyBSD by https://github.com/vmactions/dragonflybsd-vm --- .github/workflows/DragonflyBSD.yml | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/DragonflyBSD.yml diff --git a/.github/workflows/DragonflyBSD.yml b/.github/workflows/DragonflyBSD.yml new file mode 100644 index 00000000..f014da9b --- /dev/null +++ b/.github/workflows/DragonflyBSD.yml @@ -0,0 +1,41 @@ +name: DragonflyBSD +on: + push: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/DragonflyBSD.yml' + pull_request: + branches: + - '*' + paths: + - '**.c' + - '**.h' + - 'compat/*' + - '.github/workflows/DragonflyBSD.yml' + + + +jobs: + DragonflyBSD: + runs-on: macos-12 + steps: + - uses: actions/checkout@v2 + - uses: vmactions/dragonflybsd-vm@v0 + with: + prepare: | + pkg install -y automake autoconf libtool pkgconf libevent msgpack libssh gsed + usesh: true + run: | + gsed -i "s/OK/0/g" tty-term.c + autoupdate + ./autogen.sh + ./configure + make + make install + + +